https://github.com/wangzihaogithub/field-intercept
适合用于DDD思想,解决业务系统的胶水逻辑代码,整理业务逻辑
https://github.com/wangzihaogithub/field-intercept
ddd entity
Last synced: 5 months ago
JSON representation
适合用于DDD思想,解决业务系统的胶水逻辑代码,整理业务逻辑
- Host: GitHub
- URL: https://github.com/wangzihaogithub/field-intercept
- Owner: wangzihaogithub
- License: apache-2.0
- Created: 2022-02-23T07:19:37.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2025-12-12T07:19:54.000Z (6 months ago)
- Last Synced: 2025-12-13T17:38:42.089Z (6 months ago)
- Topics: ddd, entity
- Language: Java
- Homepage:
- Size: 314 KB
- Stars: 1
- Watchers: 0
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
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 {
@Resource
private BizEnumMapper mapper;
// 根据(字典group,字典key), 获取 Map<字典group, Map<字典key, 字典value>>
@Override
public Map> selectEnumGroupKeyValueMap(Set groups, Collection keys) {
return mapper.selectEnumGroupKeyValueList(groups, keys).stream()
.collect(Collectors.groupingBy(BizEnumPO::getGroup,
Collectors.toMap(BizEnumPO::getKey, e -> e)));
}
}
```
// 结束。可以用了
- 如果业务提供者在其他应用中,不在本应用里,可以借助Dubbo,别的都不用改。 详细配置参考:com.github.fieldintercept.springboot.FieldinterceptProperties
```yaml
# 提供者参考配置
spring:
fieldintercept:
bean-base-packages: 'com.xxx'
cluster:
enabled: true
rpc: dubbo
role: provider
dubbo:
registry: 'myRegistryConfig' # 非必填,参考dubbo注册中心配置
```
```yaml
# 调用者参考配置
spring:
fieldintercept:
bean-base-packages: 'com.xxx'
cluster:
enabled: true
rpc: dubbo
role: consumer
dubbo:
registry: 'myRegistryConfig' # 非必填,参考dubbo注册中心配置
```
- 递归用法
```java
// 这种用法可以让纵向查询,简化为横向查询(如果递归深度为3,则只进行3次查询,不会随着条数增加而增加)
public class FolderParent {
private String name;
private Long parentId;
@FieldConsumer(value = Providers.FOLDER, keyField = "parentId")
private FolderParent parent;
}
```
- 兼容spring的多线程上下文切换组件
```java
@Bean
public org.springframework.core.task.TaskDecorator taskDecorator(){
return new org.springframework.core.task.TaskDecorator() {
@Override
public Runnable decorate(Runnable runnable) {
return null;
}
};
}
```
- 非阻塞用法(取决于底层自动优化:可能为异步,可能为单线程聚合,可能为Dubbo调用)
```java
@ReturnFieldAop
public CompletableFuture> selectList() {
List list = mapper.selectList();
return new FieldCompletableFuture<>(list);
}
```
- 非阻塞链式用法(每次回掉阶段,(user,order,errorCode)都会被注入数据 )
```java
@ReturnFieldAop
public CompletableFuture method1() {
CompletableFuture future = new FieldCompletableFuture<>(user)
.thenApply(user ->{
Order order = ..user// 业务逻辑
return order;
})
.thenApply(order ->{
Invoice invoice = ..order// 业务逻辑
return invoice;
})
.thenApply(order ->{
ErrorCode errorCode = ..order// 业务逻辑
return errorCode;
});
return future;
}
```