设计模式-java

设计模式

工厂模式

应用场景

  • DefaultLogicFactory 类中,实现了一个典型的工厂模式。在Spring框架下非常实用,工厂模式用于创建对象,不直接使用 new 操作符实例化对象,而是通过调用一个工厂方法来获取新对象的实例。在这种情况下,工厂方法(构造函数)负责从一组 ILogicFilter 实例中读取元数据并创建一个管理这些过滤器实例的映射。

优点

  • 工厂模式支持编程的抽象层级,并允许系统在不修改现有代码的情况下引入新的逻辑过滤器类型,符合开闭原则

参考资料:https://bugstack.cn/md/develop/design-pattern/2020-05-20-%E9%87%8D%E5%AD%A6Java%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E3%80%8A%E5%AE%9E%E6%88%98%E5%B7%A5%E5%8E%82%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8F%E3%80%8B.html

示例

最近在跟大营销项目,里面的一些设计还是慢巧妙地。这边也梳理一下并加强对系统的理解.

当前在strategy 领域中,结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
.
├── adapter
│   └── repository
│   └── IStrategyRepository.java
├── model
│   ├── entity
│   │   ├── RaffleAwardEntity.java
│   │   ├── RaffleFactorEntity.java
│   │   ├── RuleActionEntity.java
│   │   ├── RuleMatterEntity.java
│   │   ├── StrategyAwardEntity.java
│   │   ├── StrategyEntity.java
│   │   └── StrategyRuleEntity.java
│   └── vo
│   └── RuleLogicCheckTypeVO.java
└── service
├── IRaffleStrategy.java
├── annotation
│   └── LogicStrategy.java
├── armory
│   ├── IStrategyArmory.java
│   ├── IStrategyDispatch.java
│   └── StrategyArmoryDispatch.java
├── raffle
│   ├── AbstractRaffleStrategy.java
│   └── DefaultRaffleStrategy.java
└── rule
├── ILogicFilter.java
├── factory
│   └── DefaultLogicFactory.java
└── impl
├── RuleBlackListLogicFilter.java
└── RuleWeightLogicFilter.java

整个抽奖的逻辑是这样的:

  1. 首先,传入一个RaffleFactorEntity ,包含用户ID和策略ID,然后调用RaffleStrategy来执行查询。
  2. 在执行查询前,需要根据策略ID获取规则模型,再根据规则模型前置过滤,以实现黑名单功能和权重匹配功能
  3. 执行查询,用户的积分不同,奖品范围也不同;如果是黑名单,那么只会返回一个最低档的积分。

在这里我们重点关注以下2部分:

1
2
3
4
5
6
7
8
9
├── annotation
│   └── LogicStrategy.java
└── rule
├── ILogicFilter.java
├── factory
│   └── DefaultLogicFactory.java
└── impl
├── RuleBlackListLogicFilter.java
└── RuleWeightLogicFilter.java

其中,annotation中的LogicStrategy是一个自定义的注解:

1
2
3
4
5
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogicStrategy {
DefaultLogicFactory.LogicModel logicMode();
}
  • @Target({ElementType.TYPE}):这表明 LogicStrategy 注解可以被用于类、接口或枚举声明。这意味着不能将此注解用于方法、字段等,只能标注在类等类型上。

  • @Retention(RetentionPolicy.RUNTIME):这表明 LogicStrategy 注解不仅会被编译器记录在类文件中,而且在运行时通过反射仍然可见。这是实现动态逻辑处理中关键的部分,因为它允许程序在运行时查询这些注解信息。

因此,只要在我实现的过滤器类上,应用了 LogicStrategy注解,就需要指定一个特定的策略模式。此外我还用@Component注解,Spring 容器会在应用启动时自动识别并实例化该类的对象,将其注册为 Spring 管理的 Bean。并注入到需要这个Bean的地方去。

如:

1
2
3
4
5
6
@Slf4j
@Component
@LogicStrategy(logicMode = DefaultLogicFactory.LogicModel.RULE_BLACKLIST)
public class RuleBlackListLogicFilter implements ILogicFilter<RuleActionEntity.RaffleBeforeEntity>{
// 实现细节
}

1
2
3
4
5
6
@Slf4j
@Component
@LogicStrategy(logicMode = DefaultLogicFactory.LogicModel.RULE_WEIGHT)
public class RuleWeightLogicFilter implements ILogicFilter<RuleActionEntity.RaffleBeforeEntity>{
// 实现细节
}

在 factory中的DefaultLogicFactory是一个工厂类,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Service
public class DefaultLogicFactory {
public Map<String, ILogicFilter<?>> logicFilterMap = new ConcurrentHashMap<>();

public DefaultLogicFactory(List<ILogicFilter<?>> logicFilters) {
logicFilters.forEach(logic -> {
LogicStrategy strategy = AnnotationUtils.findAnnotation(logic.getClass(), LogicStrategy.class);
if (null != strategy) {
logicFilterMap.put(strategy.logicMode().getCode(), logic);
}
});
}

public <T extends RuleActionEntity.RaffleEntity> Map<String, ILogicFilter<T>> openLogicFilter() {
return (Map<String, ILogicFilter<T>>) (Map<?, ?>) logicFilterMap;
}

@Getter
@AllArgsConstructor
public enum LogicModel {

RULE_WIGHT("rule_weight","【抽奖前规则】根据抽奖权重返回可抽奖范围KEY"),
RULE_BLACKLIST("rule_blacklist","【抽奖前规则】黑名单规则过滤,命中黑名单则直接返回"),
;

private final String code;
private final String info;

}
}
  • 依赖注入:在 DefaultLogicFactory 的构造函数中,我指定了一个 List<ILogicFilter<?>> 类型的参数。这告诉 Spring,这个构造函数需要注入所有匹配 ILogicFilter<?> 类型的 Beans。
  • 集合注入:Spring 支持集合类型的依赖注入。当我在构造函数中使用如 List<ILogicFilter<?>> 这样的集合类型时,Spring 会自动收集所有可用的 ILogicFilter<?> 实例并作为列表注入。这包括所有单独声明的实现了 ILogicFilter<?> 接口的类,例如RuleBlackListLogicFilter
  • 在构造函数中处理逻辑:一旦 DefaultLogicFactory 获得所有逻辑过滤器的实例,它会遍历这些实例,使用 AnnotationUtils.findAnnotation 检查每个实例是否有 @LogicStrategy 注解。
  • 根据注解分类存储:如果发现 @LogicStrategy 注解,就会根据注解中指定的策略模式代码将对应的过滤器存储在 logicFilterMap 中。

工厂模式+责任链模式

未优化的代码

原来的代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 3. 抽奖前 - 规则过滤
RuleActionEntity<RuleActionEntity.RaffleBeforeEntity> ruleActionBeforeEntity = this.doCheckRaffleBeforeLogic(RaffleFactorEntity.builder()
.userId(userId)
.strategyId(strategyId)
.build(), strategy.ruleModels());

if (RuleLogicCheckTypeVO.TAKE_OVER.getCode().equals(ruleActionBeforeEntity.getCode())) {
if (DefaultLogicFactory.LogicModel.RULE_BLACKLIST.getCode().equals(ruleActionBeforeEntity.getRuleModel())) {
// 黑名单返回固定的奖品ID
return RaffleAwardEntity.builder()
.awardId(ruleActionBeforeEntity.getData().getAwardId())
.build();
} else if (DefaultLogicFactory.LogicModel.RULE_WIGHT.getCode().equals(ruleActionBeforeEntity.getRuleModel())) {
// 权重根据返回的信息进行抽奖
RuleActionEntity.RaffleBeforeEntity raffleBeforeEntity = ruleActionBeforeEntity.getData();
String ruleWeightValueKey = raffleBeforeEntity.getRuleWeightValueKey();
Integer awardId = strategyDispatch.getRandomAwardId(strategyId, ruleWeightValueKey);
return RaffleAwardEntity.builder()
.awardId(awardId)
.build();
}
}

// 4. 默认抽奖流程
Integer awardId = strategyDispatch.getRandomAwardId(strategyId);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@Override
protected RuleActionEntity<RuleActionEntity.RaffleBeforeEntity> doCheckRaffleBeforeLogic(RaffleFactorEntity raffleFactorEntity, String... logics) {
if (logics == null || 0 == logics.length) return RuleActionEntity.<RuleActionEntity.RaffleBeforeEntity>builder()
.code(RuleLogicCheckTypeVO.ALLOW.getCode())
.info(RuleLogicCheckTypeVO.ALLOW.getInfo())
.build();

Map<String, ILogicFilter<RuleActionEntity.RaffleBeforeEntity>> logicFilterGroup = logicFactory.openLogicFilter();

// 黑名单规则优先过滤
String ruleBackList = Arrays.stream(logics)
.filter(str -> str.contains(DefaultLogicFactory.LogicModel.RULE_BLACKLIST.getCode()))
.findFirst()
.orElse(null);

if (StringUtils.isNotBlank(ruleBackList)) {
ILogicFilter<RuleActionEntity.RaffleBeforeEntity> logicFilter = logicFilterGroup.get(DefaultLogicFactory.LogicModel.RULE_BLACKLIST.getCode());
RuleMatterEntity ruleMatterEntity = new RuleMatterEntity();
ruleMatterEntity.setUserId(raffleFactorEntity.getUserId());
ruleMatterEntity.setAwardId(ruleMatterEntity.getAwardId());
ruleMatterEntity.setStrategyId(raffleFactorEntity.getStrategyId());
ruleMatterEntity.setRuleModel(DefaultLogicFactory.LogicModel.RULE_BLACKLIST.getCode());
RuleActionEntity<RuleActionEntity.RaffleBeforeEntity> ruleActionEntity = logicFilter.filter(ruleMatterEntity);
if (!RuleLogicCheckTypeVO.ALLOW.getCode().equals(ruleActionEntity.getCode())) {
return ruleActionEntity;
}
}

// 顺序过滤剩余规则
List<String> ruleList = Arrays.stream(logics)
.filter(s -> !s.equals(DefaultLogicFactory.LogicModel.RULE_BLACKLIST.getCode()))
.collect(Collectors.toList());

RuleActionEntity<RuleActionEntity.RaffleBeforeEntity> ruleActionEntity = null;
for (String ruleModel : ruleList) {
ILogicFilter<RuleActionEntity.RaffleBeforeEntity> logicFilter = logicFilterGroup.get(ruleModel);
RuleMatterEntity ruleMatterEntity = new RuleMatterEntity();
ruleMatterEntity.setUserId(raffleFactorEntity.getUserId());
ruleMatterEntity.setAwardId(raffleFactorEntity.getAwardId());
ruleMatterEntity.setStrategyId(raffleFactorEntity.getStrategyId());
ruleMatterEntity.setRuleModel(ruleModel);
ruleActionEntity = logicFilter.filter(ruleMatterEntity);
// 非放行结果则顺序过滤
log.info("抽奖前规则过滤 userId: {} ruleModel: {} code: {} info: {}", raffleFactorEntity.getUserId(), ruleModel, ruleActionEntity.getCode(), ruleActionEntity.getInfo());
if (!RuleLogicCheckTypeVO.ALLOW.getCode().equals(ruleActionEntity.getCode())) return ruleActionEntity;
}

return ruleActionEntity;
}

现在,我们需要思考,在 doCheckRaffleBeforeLogic 方法中,所有规则的逻辑集中在一个方法内。虽然代码结构相对清晰,但它依然耦合了不同规则的判断逻辑(如黑名单和权重规则),使得方法臃肿且难以单独维护。比如,我们还要实现一个白名单规则,那么就要在这个doCheckRaffleBeforeLogic 方法中确定其位置,这会使得方法不断增大、复杂,切不容易理解各个规则的优先级。

因此,我们可以将每个规则处理器作为一个独立的类,各自实现独立的logic方法。不同规则的逻辑和判断被分离到不同的责任链节点中,代码更加模块化。比如,我们想要创建一个新的责任链节点 WhiteListLogicChain, 我们只需要在工厂中将其加入链条,而不是像原来那样修改doCheckRaffleBeforeLogic 方法。 极大地降低了代码的耦合性和维护成本。

示意图如下所示:

重构-链节点

对于原来的Filter,现在我们将其变成责任链上的一个节点:

1
2
3
4
5
6
public interface ILogicChainArmory {

ILogicChain appendNext(ILogicChain next);

ILogicChain next();
}
1
2
3
4
5
6
7
8
9
10
11
public interface ILogicChain  extends ILogicChainArmory {
/*
* 责任链接口
* @param userId 用户ID
* @param strategyId 策略ID
* @return 奖品ID
* */
Integer logic(String userId, Long Strategy);


}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public abstract class AbstractLogicChain implements ILogicChain {

private ILogicChain next;

@Override
public ILogicChain appendNext(ILogicChain next) {
this.next = next;
return next;
}

@Override
public ILogicChain next() {
return next;
}

protected abstract String ruleModel();
}
  • 这是类似于一个链表的数据结构,每个ILogicChain节点都会指向下一节点。

  • 此外,如果一个节点继承了这个抽象节点类AbstractLogicChain, 那么他还需要实现logic()方法,里面具体实现规则过滤逻辑。

比如,这个责任链上有一个黑名单过滤节点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Slf4j
@Component("rule_blacklist")
public class BlackListLogicChain extends AbstractLogicChain {
@Resource

private IStrategyRepository strategyRepository;


@Override
public Integer logic(String userId, Long strategyId) {
log.info("抽奖责任链-黑名单开始 userId:{} strategyId:{} ruleModel:{} ", userId, strategyId,ruleModel());
String ruleValue = strategyRepository.queryStrategyRuleValue(strategyId, ruleModel());

String[] splitRuleValue = ruleValue.split(Constants.COLON);
Integer awardId = Integer.valueOf(splitRuleValue[0]);
String[] userNames = splitRuleValue[1].split(Constants.SPLIT);

// 100:user001,user002,user003 判断ruleMatterEntity.getUserId()是否在黑名单中
boolean isBlackListed = Arrays.asList(userNames).contains(userId);
if(isBlackListed){
log.info("抽奖责任链-黑名单接管 userId:{} strategyId:{} ruleModel:{} ", userId, strategyId,ruleModel());
return awardId;
}
log.info("抽奖责任链-黑名单放行 userId:{} strategyId:{} ruleModel:{} ", userId, strategyId,ruleModel());
return next().logic(userId, strategyId);
}

@Override
protected String ruleModel() {
return "rule_blacklist";
}
}

该节点根据userIdstrategyId 获取ruleValue,判断当前用户是否在黑名单中。

  • 如果是,那么就接管,并返回一个特定的低保奖品
  • 如果否,就放行,然后调用责任链的下一个节点,交给别人去判断

此外,我们还需要一个默认节点,如果规则对这个用户都不适用,那么就返回一个默认值。在抽奖系统中,就是随便抽。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Slf4j
@Component("default")
public class DefaultLogicChain extends AbstractLogicChain {
@Resource
protected IStrategyDispatch strategyDispatch;


@Override
public Integer logic(String userId, Long strategyId) {
Integer awardId = strategyDispatch.getRandomAwardId(strategyId);
log.info("抽奖责任链-默认处理 userId: {}, strategyId: {} ruleModel: {} awardId: {}", userId, strategyId,ruleModel(), awardId);
return awardId;
}

@Override
protected String ruleModel() {
return "default";
}
}

重构-链工厂

构建了节点之后,相当于我们有了很多乐高碎片,那么需要一个工厂类,将它们组装成一个链,于是需要:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@Service
public class DefaultChainFactory {
private final Map<String, ILogicChain> logicChainGroup;

private final IStrategyRepository strategyRepository;


public DefaultChainFactory(Map<String, ILogicChain> logicChainGroup, IStrategyRepository strategyRepository) {
this.logicChainGroup = logicChainGroup;
this.strategyRepository = strategyRepository;
}

public ILogicChain openLogicChain(Long strategyId){

/*
获取规则列表:openLogicChain 方法根据传入的 strategyId
从 strategyRepository 获取该策略对应的规则列表 ruleModels。
*/
StrategyEntity strategy = strategyRepository.queryStrategyEntityByStrategyId(strategyId);
String[] ruleModels = strategy.ruleModels();

if(null == ruleModels || ruleModels.length == 0){
return logicChainGroup.get("default");
}

/*
动态构建链条:根据 ruleModels 中的规则顺序,
从 logicChainGroup 中获取对应的 ILogicChain 实例。
以链条的方式将每个规则节点按顺序连接起来,最终形成一条完整的责任链。

通过先提取 ruleModels[0] 并将其赋值给 logicChain(作为链条的起始节点)
可以避免在循环中处理第一个节点的特殊逻辑。
*/
ILogicChain logicChain = logicChainGroup.get(ruleModels[0]);
ILogicChain current = logicChain;
for (int i = 1; i < ruleModels.length; i++) {
ILogicChain nextChain = logicChainGroup.get(ruleModels[i]);
current = current.appendNext(nextChain);
}
/*
默认节点:在链条末尾追加一个默认节点("default"),
以保证在规则链中没有匹配规则时,责任链可以安全结束。
*/
current.appendNext(logicChainGroup.get("default"));
return logicChain;

}
}

这个工厂类基于 责任链模式工厂模式 的组合,通过动态组合链条节点来生成完整的责任链,具体实现策略模式的行为选择。以下是该工厂类的关键设计思路:

  1. 工厂模式DefaultChainFactory 是一个工厂类,用于构建 ILogicChain 的责任链。它根据输入的strategyId 查询策略对应的规则,然后按顺序从 logicChainGroup 中取出这些规则节点,将它们组装成一条链。
  2. 责任链模式:工厂类生成的 ILogicChain 实例是由多个链式节点组成的责任链。每个链节点都可以在链条中处理请求或将请求传递给下一个节点。这样,规则在链条中按顺序依次处理,每个规则的逻辑独立且具有优先级(顺序)。
  3. 基于规则的动态链式构建openLogicChain 方法根据策略查询到的规则列表动态构建责任链。这种设计确保了链条的灵活性:可以根据不同的规则列表动态生成不同的链条,而不是依赖固定的逻辑顺序。

最后,我们需要修改一开始的抽奖操作,那一大段规则过滤的代码可以简化为:

1
2
3
//2. 责任链抽奖
ILogicChain logicChain = chainFactory.openLogicChain(strategyId);
Integer randomAwardId = logicChain.logic(userId, strategyId);

构造责任链:比如该策略的ruleModels为rule_blacklist,rule_weight。 那么责任链就是 rule_blacklist -> rule_weight -> default 先过滤黑名单,再过滤权重,如果都放行,那么执行默认抽奖。

规则树构建

解决了前置规则过滤,现在我们来看看抽奖中规则过滤。逻辑是这样的:

这是一个非多分支情况的规则过滤。单独的责任链是不能满足的,如果是拆分开抽奖中规则和抽奖后规则分阶段处理,中间单独写逻辑处理库存操作。那么是可以实现的。但这样的方式始终不够优雅,配置化的内容较低,后续的规则开发仍需要在代码上改造。所以这里可以使用规则树的结构,实现规则过滤。

因此,我们需要把库存处理也当做是一种规则,库存足够,说明满足规则,放行;库存不够,说明不满足规则,接管—>给兜底奖励。流程如下:

规则树结构

  1. 接口 ILogicTreeNode
    • 定义了树节点的基础操作,即 logic 方法。每个节点都有 logic 方法来判断当前节点是否符合条件,并返回相应的决策状态。
  2. 具体实现节点 RuleStockLogicTreeNodeRuleLuckAwardLogicTreeNodeRuleLockLogicTreeNode
    • 每个节点实现了 ILogicTreeNode,并定义了各自的逻辑。例如,RuleStockLogicTreeNodeRuleLuckAwardLogicTreeNode 返回 TAKE_OVER,表示节点接管;RuleLockLogicTreeNode 返回 ALLOW,表示放行到下一个节点。
  3. 工厂类 DefaultTreeFactory
    • 负责管理不同的 ILogicTreeNode 实例。通过构造器注入 Map<String, ILogicTreeNode>,在运行时动态决定使用哪种节点逻辑。
    • 提供 openLogicTree 方法生成决策树引擎 DecisionTreeEngine,并传入节点实例映射和规则树配置(RuleTreeVO)。
  4. 规则树引擎 DecisionTreeEngine
    • 核心逻辑处理类,用于遍历规则树,按顺序检查节点并作出决策。
    • process 方法:从根节点开始,根据每个节点的决策结果决定下一步的执行节点。如果判断为接管,那么返回兜底奖励,如果判断为放行,则执行下一个节点。
    • nextNode 方法:负责判断下一节点的路径,依据 RuleTreeNodeLineVO 中的条件是接管还是放行,决定进入的下一个节点。
  5. 规则数据对象(RuleTreeNodeVORuleTreeNodeLineVORuleTreeVO
    • RuleTreeVO 表示整个决策树结构,包括根节点、各个节点和连线。
    • RuleTreeNodeVO 表示树中的单个节点,包含规则标识、描述、下一步连线等信息。
    • RuleTreeNodeLineVO 表示节点之间的连线条件,包括条件类型(如等于、大于等)、下一节点等信息。
  6. 枚举 RuleLimitTypeVORuleLogicCheckTypeVO
    • RuleLimitTypeVO:定义了节点连线的判断条件(如等于、大于、枚举等),用于 nextNode 判断逻辑。
    • RuleLogicCheckTypeVO:用于标识逻辑节点的检查结果,如 TAKE_OVER 表示节点接管、ALLOW 表示允许通过等。

测试如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/**
* rule_lock --左--> rule_luck_award
* --右--> rule_stock --右--> rule_luck_award
*/
@Test
public void test_tree_rule() {
// 构建参数
RuleTreeNodeVO rule_lock = RuleTreeNodeVO.builder()
.treeId(100000001)
.ruleKey("rule_lock")
.ruleDesc("限定用户已完成N次抽奖后解锁")
.ruleValue("1")
.treeNodeLineVOList(new ArrayList<RuleTreeNodeLineVO>() {{
add(RuleTreeNodeLineVO.builder()
.treeId(100000001)
.ruleNodeFrom("rule_lock")
.ruleNodeTo("rule_luck_award")
.ruleLimitType(RuleLimitTypeVO.EQUAL)
.ruleLimitValue(RuleLogicCheckTypeVO.TAKE_OVER)
.build());

add(RuleTreeNodeLineVO.builder()
.treeId(100000001)
.ruleNodeFrom("rule_lock")
.ruleNodeTo("rule_stock")
.ruleLimitType(RuleLimitTypeVO.EQUAL)
.ruleLimitValue(RuleLogicCheckTypeVO.ALLOW)
.build());
}})
.build();

RuleTreeNodeVO rule_luck_award = RuleTreeNodeVO.builder()
.treeId(100000001)
.ruleKey("rule_luck_award")
.ruleDesc("限定用户已完成N次抽奖后解锁")
.ruleValue("1")
.treeNodeLineVOList(null)
.build();

RuleTreeNodeVO rule_stock = RuleTreeNodeVO.builder()
.treeId(100000001)
.ruleKey("rule_stock")
.ruleDesc("库存处理规则")
.ruleValue(null)
.treeNodeLineVOList(new ArrayList<RuleTreeNodeLineVO>() {{
add(RuleTreeNodeLineVO.builder()
.treeId(100000001)
.ruleNodeFrom("rule_lock")
.ruleNodeTo("rule_luck_award")
.ruleLimitType(RuleLimitTypeVO.EQUAL)
.ruleLimitValue(RuleLogicCheckTypeVO.TAKE_OVER)
.build());
}})
.build();

RuleTreeVO ruleTreeVO = new RuleTreeVO();
ruleTreeVO.setTreeId(100000001);
ruleTreeVO.setTreeName("决策树规则;增加dall-e-3画图模型");
ruleTreeVO.setTreeDesc("决策树规则;增加dall-e-3画图模型");
ruleTreeVO.setTreeRootRuleNode("rule_lock");

ruleTreeVO.setTreeNodeMap(new HashMap<String, RuleTreeNodeVO>() {{
put("rule_lock", rule_lock);
put("rule_stock", rule_stock);
put("rule_luck_award", rule_luck_award);
}});

IDecisionTreeEngine treeEngine = defaultTreeFactory.openLogicTree(ruleTreeVO);

DefaultTreeFactory.StrategyAwardData data = treeEngine.process("xiaofuge", 100001L, 100);
log.info("测试结果:{}", JSON.toJSONString(data));

}

运行时的流程

  1. 初始化决策树引擎
    • DefaultTreeFactory 接收配置对象 RuleTreeVO,并调用 openLogicTree 方法生成 DecisionTreeEngine
  2. 调用 process 方法
    • 决策树引擎的 process 方法接收 userIdstrategyIdawardId,并从 RuleTreeVO 中提取根节点开始执行。
  3. 遍历节点执行逻辑
    • process 方法根据当前节点的 ruleKey 获取相应的 ILogicTreeNode 实现类。
    • 调用节点的 logic 方法执行规则逻辑,返回 TreeActionEntity(包含 ruleLogicCheckTypeVOstrategyAwardData)。
    • 通过ruleLogicCheckTypeVO 检查当前节点的执行结果:
      • TAKE_OVER:表示当前节点已经做出决策,直接返回 strategyAwardData,结束决策流程。
      • ALLOW:表示允许继续执行,根据 nextNode 方法判断下一个节点。
  4. 判断下一节点
    • nextNode 方法根据 RuleTreeNodeLineVO 中的条件值和当前节点返回的状态判断下一步要执行的节点。
    • 遍历 treeNodeLineVOList,调用 decisionLogic 方法根据连线条件决定是否满足要求。
    • 如果满足要求,返回下一个节点的标识 ruleNodeTo,并将该节点作为下一步的处理节点。
  5. 返回决策结果
    • 当流程到达某个 TAKE_OVER 节点或遍历完所有节点时,process 方法返回最终的 StrategyAwardData,包含最终的决策结果,如 awardIdawardRuleValue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Override
public DefaultTreeFactory.StrategyAwardVO process(String userId, Long strategyId, Integer awardId) {
DefaultTreeFactory.StrategyAwardVO strategyAwardData = null;

String nextNode = ruleTreeVO.getTreeRootRuleNode();
Map<String, RuleTreeNodeVO> treeNodeMap = ruleTreeVO.getTreeNodeMap();

RuleTreeNodeVO ruleTreeNodeVO = treeNodeMap.get(nextNode);
while(null != nextNode) {
ILogicTreeNode logicTreeNode = logicTreeNodeGroup.get(ruleTreeNodeVO.getRuleKey());
String ruleValue = ruleTreeNodeVO.getRuleValue();
DefaultTreeFactory.TreeActionEntity logicEntity = logicTreeNode.logic(userId, strategyId, awardId,ruleValue);

RuleLogicCheckTypeVO ruleLogicCheckTypeVO = logicEntity.getRuleLogicCheckTypeVO();

strategyAwardData = logicEntity.getStrategyAwardVO();
log.info("决策树引擎【{}】treeId:{} node:{} code:{}", ruleTreeVO.getTreeName(), ruleTreeVO.getTreeId(), nextNode, ruleLogicCheckTypeVO.getCode());
//判断下一个节点,matterValue是TAKE_OVER还是ALLOW
nextNode = nextNode(ruleLogicCheckTypeVO.getCode(), ruleTreeNodeVO.getTreeNodeLineVOList());
ruleTreeNodeVO = treeNodeMap.get(nextNode);
}
//返回最终结果
return strategyAwardData;
}


public String nextNode(String matterValue, List<RuleTreeNodeLineVO> treeNodeLineVOList) {
if (null == treeNodeLineVOList || treeNodeLineVOList.isEmpty()) return null;
for (RuleTreeNodeLineVO nodeLine : treeNodeLineVOList) {
if (decisionLogic(matterValue, nodeLine)) {
return nodeLine.getRuleNodeTo();
}
}
return null;
}
-------------本文结束,感谢您的阅读-------------