# field-intercept
**Repository Path**: wangzihaogitee/field-intercept
## Basic Information
- **Project Name**: field-intercept
- **Description**: 适合用于DDD思想, 解决业务系统的胶水逻辑代码,整理业务逻辑
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2022-02-23
- **Last Updated**: 2025-08-22
## Categories & Tags
**Categories**: Uncategorized
**Tags**: entity
## README
# field-intercept
#### 介绍
本项目解决业务系统的胶水逻辑代码,整理业务逻辑。
本项目作为协调者,可以让你将领域对象的组织的胶水代码解脱出来,依赖倒置。
1.业务提供者(定义逻辑),2.业务需求者(注入结果),3.组织胶水代码(由本项目解决)
#### 文档:
- 如果你写业务代码时,将列表查询出来后,经常需要用id再查询一遍换数据,看这个demo [demo1-simple](https://github.com/wangzihaogithub/field-intercept-example/blob/master/demo1-simple/README.md), [demo3-userdefined-selectbyid](https://github.com/wangzihaogithub/field-intercept-example/blob/master/demo3-userdefined/userdefined-selectbyid/README.md)
- 如果你写业务代码时,将列表查询出来后,经常需要再用字典表再查询一遍换数据,看这个demo [demo3-userdefined-datadict](https://github.com/wangzihaogithub/field-intercept-example/blob/master/demo3-userdefined/userdefined-datadict/README.md) , [demo3-userdefined-datadict2](https://github.com/wangzihaogithub/field-intercept-example/blob/master/demo3-userdefined/userdefined-datadict2/README.md)
- 如果你是dubbo微服务项目,看完前两个后,看这个demo [demo2-dubbo](https://github.com/wangzihaogithub/field-intercept-example/blob/master/demo2-dubbo/README.md)
- 如果你想将常用的查询独立一个注解区分出来,看这个demo [demo3-userdefined-annotation](https://github.com/wangzihaogithub/field-intercept-example/blob/master/demo3-userdefined/userdefined-annotation/README.md)
- 如果你需要做查询编排优化, 或更多自定义配置,看这个demo [SpringYML](https://github.com/wangzihaogithub/field-intercept-example/blob/master/SpringYML.md)
#### 软件依赖
1. 只依赖JDK,无其他多余依赖
2. 兼容java8~java21
3. 兼容springboot2.x~springboot3.x
4. 兼容dubbo2.7~dubbo3(兼容dubbo调用方没有提供方的类,会退化为Map)
#### 详细看示例项目
[https://github.com/wangzihaogithub/field-intercept-example](https://github.com/wangzihaogithub/field-intercept-example)
#### 使用概要
1.  添加maven依赖, 在pom.xml中加入 [](https://search.maven.org/search?q=g:com.github.wangzihaogithub%20AND%20a:field-intercept)
```xml
            
            
                com.github.wangzihaogithub
                field-intercept
                1.0.18
            
```
2. 添加配置,写上业务包名, 比如com.ig, 认为com.ig包下都是业务实体类
        application.yaml里
```yaml
   spring:
      fieldintercept:
         beanBasePackages: 'com.xxx'
```
3. 在业务系统增加抽象Service, 类似下面这种
```java
         public abstract class AbstractCrudService<
               REPOSITORY extends AbstractMapper, 
               PO extends AbstractPO, 
               ID extends Number
            > 
            implements CompositeFieldIntercept {
                  @Autowired
                  private REPOSITORY repository;
                  // 加个字段,用户支持注入名称(例:员工表=部门/员工名称)
                  private final KeyNameFieldIntercept keyNameFieldIntercept = new KeyNameFieldIntercept<>(keyClass, this::selectNameMapByKeys);
                   @Override
                   public KeyNameFieldIntercept keyNameFieldIntercept() {
                       return keyNameFieldIntercept;
                   }
                  // 加个字段,用于支持注入实体类Like (例:List, SysUser, SysUserDTO)
                  private final KeyValueFieldIntercept keyValueFieldIntercept = new KeyValueFieldIntercept<>(keyClass, valueClass, this::selectValueMapByKeys);
                   @Override
                   public KeyValueFieldIntercept keyValueFieldIntercept() {
                       return keyValueFieldIntercept;
                   }
                  // 这个方法你可以实现的,因为持久化框架都默认实现了ByIds的查询
                   public Map selectNameMapByKeys(Collection ids) {
                       return convertNames(repository.findByIds(ids));
                   }
                  
                   // 这个方法你可以实现的,因为持久化框架都默认实现了ByIds的查询
                   public Map selectValueMapByKeys(Collection ids) {
                       return repository.findByIds(ids).stream()
                               .collect(Collectors.toMap(AbstractPO::getId, e -> e));
                   }
                     
                  // 显示名称的拼接格式
                   protected Map convertNames(List pos) {
                       return pos.stream().collect(Collectors.toMap(AbstractPO::getId, po -> nameGetter.getReadMethod().invoke(po)));
                   }
         }
         
```
4. 然后你可以使用方式1或方式2暴露你的提供者逻辑,就可以供他人使用了
```java
         // 方式1 (通用的无逻辑的根据id查询)
         @Service("SYS_USER")
         public class SysUserServiceImpl extends AbstractCrudService{
    
         }
```
```java
         // 方式2(自定义逻辑的根据id查询)
         @Service("TALENT_WORK_LAST")
         public class TalentWorkLastServiceImpl 
               extends DefaultCompositeFieldIntercept, Object> {
              public TalentWorkLastServiceImpl(TalentWorkMapper mapper) {
                  super(
                          ids -> {
                              // 查询名称(最近一段工作经历	公司/职位/时间)
                              return mapper.selectNameMapByIds(ids);
                          },
                          ids -> {
                              // 查询对象(最后一段工作经历)
                              return mapper.selectMapByIds(ids);
                          });
              }
       }
```
5. 使用方式:其他使用者在需要你的地方写上你的名字"SYS_USER", 这个StatisticsDetailResp只要遇到触发查询的地方,就会被填充。
```java
         @Data
         public class StatisticsDetailResp {
             private Integer pipelineId;
             private Integer talentId;
             private Integer userId;
             private List userIds;
             @EnumFieldConsumer(value = InterTypeEnum.class, keyField = "interType", valueField = "${color}")
             private String interTypeColor;
             /**
              * 用户
              */
             @FieldConsumer(value = "SYS_USER", keyField = "userId")
             private SysUserVO user;
             /**
              * 用户
              */
             @FieldConsumer(value = "SYS_USER", keyField = "userIds")
             private List userList;
             /**
              * 用户
              */
             @FieldConsumer(value = "SYS_USER", keyField = "userIds")
             private List userNameList;
             /**
              * 用户
              */
             @FieldConsumer(value = "SYS_USER", keyField = "userIds")
             private List userNameList;
             /**
              * 用户
              */
             @FieldConsumer(value = "SYS_USER", keyField = "userIds", joinDelimiter = "、")
             private String userNames;
             /**
              * 最后一段工作经历	公司/职位/时间
              */
             @FieldConsumer(value = "TALENT_WORK_LAST", keyField = "talentId")
             private TalentWork talentWork;
         }
         
```
         触发查询的入口有两种:
```java
          // 1. 方法上标记 @ReturnFieldAop注解。
           @ReturnFieldAop
           @Override
           public List selectHrDetailList(StatisticsHrListDetailReq req) {
               return mapper.selectHrDetailList(req);
           }
           
```
```java
          // 2. 主动触发查询
            @Autowired 
            private ReturnFieldDispatchAop returnFieldDispatchAop;
           @Override
           public List selectHrDetailList(StatisticsHrListDetailReq req) {
               List list = mapper.selectHrDetailList(req);
               // 主动方式1: 并行查询:注:如果在Spring事物中,会导致切出当前事物查询。
               returnFieldDispatchAop.parallelAutowiredFieldValue(list);
               return list;
           }
            
           @Override
           public List selectHrDetailList(StatisticsHrListDetailReq req) {
               List list = mapper.selectHrDetailList(req);
               // 主动方式2: 串行查询:注:如果在Spring事物中,不会切出当前事物查询。
               returnFieldDispatchAop.autowiredFieldValue(list);
               return list;
           }
```
#### 其他高阶用法
- 枚举表或字典表
```java
        // 解锁第一种用法:value为字符串,这种不需要你自定义注解。
        @EnumFieldConsumer(value = "INTER_ROUND", keyField = "interRoundKey")
        private String interRoundName;
        // 解锁第二种用法:value为枚举类,要你自定义注解
        @MyEnumFieldConsumer(value = BizEnumGroupEnum.INTER_ROUND, keyField = "interRoundKey")
        private String interRoundName;
        
        
        // 可选:如果你选择第二种用法,可参考如下自定义注解。如果你用的第一种,value为字符串,可以忽略这一步。
          @Retention(RetentionPolicy.RUNTIME)
          @Target({ElementType.FIELD})
          @EnumDBFieldConsumer.Extends
          public @interface MyEnumFieldConsumer {
         
                String NAME = EnumDBFieldConsumer.NAME;
        
              /**
              * 枚举组
                */
                MyBizEnumGroupEnum[] enumGroup();
        
              /**
              * value解析
              *
              * @return value解析
                */
                Class extends MyEnumFieldConsumer.ValueParser> valueParser() default BaseEnumGroupEnumParser.class;
        
              /**
              * 通常用于告知aop. id字段,或者key字段
              *
              * @return 字段名称
                */
                String[] keyField();
        
              /**
              * 多个拼接间隔符
              *
              * @return
                */
                String joinDelimiter() default ",";
        
                class BaseEnumGroupEnumParser implements EnumDBFieldConsumer.ValueParser {
                        @Override
                        public String[] apply(CField cField) {
                        // 获取字典类型(字典组)
                        // 这个方法是为了可供如果你自定义了,类似下面统一管理的枚举类而写的。如果没有可以不写
                        // public enum MyBizEnumGroupEnum {
                        //     INTER_ROUND("inter_round","面试轮次"),
                        //     USER_LEVEL("user_level","员工级别");
                        //     private String group;
                        // }
                            MyBizEnumGroupEnum annotation = (MyBizEnumGroupEnum) cField.getAnnotation();
                            return Stream.of(annotation.value()).map(SysDictTypeEnum::getGroup).toArray(String[]::new);
                        }
                    }
          }
        // 最终不管你用哪种, 这步查询的实现逻辑都需要你自己写的。
         @Component(EnumDBFieldConsumer.NAME)
         public static class BizEnumDBFieldIntercept extends EnumDBFieldIntercept