提交 7ac02f13 编写于 作者: H hudingrong

策略树+组合模式提交

上级 2454581b
package cn.bugstack.test.domain;
import cn.bugstack.domain.strategy.model.valobj.*;
import cn.bugstack.domain.strategy.service.rule.tree.factory.engine.IDecisionTreeEngine;
import cn.bugstack.domain.strategy.service.rule.tree.factory.DefaultTreeFactory;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
/**
* @author Fuzhengwei bugstack.cn @小傅哥
* @description 规则树测试
* @create 2024-01-27 13:23
*/
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class LogicTreeTest {
@Resource
private DefaultTreeFactory defaultTreeFactory;
/**
* 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));
}
}
package cn.bugstack.domain.strategy.model.valobj;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @description: 枚举类
* @author: hdr
* @PACKAGE_NAME: cn.bugstack.domain.strategy.model.valobj
* @DATE: 2024/4/23
*/
@Getter
@AllArgsConstructor
public enum RuleLimitTypeVO {
EQUAL(1, "等于"),
GT(2,"大于"),
LT(3,"小于"),
GE(4,"大于&等于"),
LE(5,"小于&等于"),
ENUM(6,"枚举")
;
private final Integer code;
private final String info;
}
package cn.bugstack.domain.strategy.model.valobj;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @description: 规则树节点指向线对象。
* @author: hdr
* @PACKAGE_NAME: cn.bugstack.domain.strategy.model.valobj
* @DATE: 2024/4/23
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class RuleTreeNodeLineVO {
/** 规则树ID */
private Integer treeId;
/** 规则Key节点 From */
private String ruleNodeFrom;
/** 规则Key节点 To */
private String ruleNodeTo;
/** 限定类型;1:=;2:>;3:<;4:>=;5<=;6:enum[枚举范围] */
private RuleLimitTypeVO ruleLimitType;
/** 限定值(到下个节点) */
private RuleLogicCheckTypeVO ruleLimitValue;
}
package cn.bugstack.domain.strategy.model.valobj;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @description: 规则树节点对象
* @author: hdr
* @PACKAGE_NAME: cn.bugstack.domain.strategy.model.valobj
* @DATE: 2024/4/23
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class RuleTreeNodeVO {
/** 规则树ID */
private Integer treeId;
/** 规则Key */
private String ruleKey;
/** 规则描述 */
private String ruleDesc;
/** 规则比值 */
private String ruleValue;
/** 规则连线 */
private List<RuleTreeNodeLineVO> treeNodeLineVOList;
}
package cn.bugstack.domain.strategy.model.valobj;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Map;
/**
* @description: 规则树对象 【注意;不具有唯一ID,不需要改变数据库结果的对象,可以被定义为值对象】
* @author: hdr
* @PACKAGE_NAME: cn.bugstack.domain.strategy.model.valobj
* @DATE: 2024/4/23
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class RuleTreeVO {
/** 规则树ID */
private Integer treeId;
/** 规则树名称 */
private String treeName;
/** 规则树描述 */
private String treeDesc;
/** 规则根节点 */
private String treeRootRuleNode;
/** 规则节点 */
private Map<String, RuleTreeNodeVO> treeNodeMap;
}
......@@ -6,7 +6,7 @@ import cn.bugstack.domain.strategy.model.entity.RuleMatterEntity;
import cn.bugstack.domain.strategy.model.valobj.RuleLogicCheckTypeVO;
import cn.bugstack.domain.strategy.repository.IStrategyRepository;
import cn.bugstack.domain.strategy.service.armory.IStrategyDispatch;
import cn.bugstack.domain.strategy.service.rule.ILogicFilter;
import cn.bugstack.domain.strategy.service.rule.filter.ILogicFilter;
import cn.bugstack.domain.strategy.service.rule.chain.factory.DefaultChainFactory;
import cn.bugstack.domain.strategy.service.rule.filter.factory.DefaultLogicFactory;
import com.alibaba.fastjson.JSON;
......
package cn.bugstack.domain.strategy.service.rule;
package cn.bugstack.domain.strategy.service.rule.filter;
import cn.bugstack.domain.strategy.model.entity.RuleActionEntity;
import cn.bugstack.domain.strategy.model.entity.RuleMatterEntity;
......
......@@ -2,7 +2,7 @@ package cn.bugstack.domain.strategy.service.rule.filter.factory;
import cn.bugstack.domain.strategy.model.entity.RuleActionEntity;
import cn.bugstack.domain.strategy.service.annotation.LogicStrategy;
import cn.bugstack.domain.strategy.service.rule.ILogicFilter;
import cn.bugstack.domain.strategy.service.rule.filter.ILogicFilter;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.core.annotation.AnnotationUtils;
......
......@@ -5,7 +5,7 @@ import cn.bugstack.domain.strategy.model.entity.RuleMatterEntity;
import cn.bugstack.domain.strategy.model.valobj.RuleLogicCheckTypeVO;
import cn.bugstack.domain.strategy.repository.IStrategyRepository;
import cn.bugstack.domain.strategy.service.annotation.LogicStrategy;
import cn.bugstack.domain.strategy.service.rule.ILogicFilter;
import cn.bugstack.domain.strategy.service.rule.filter.ILogicFilter;
import cn.bugstack.domain.strategy.service.rule.filter.factory.DefaultLogicFactory;
import cn.bugstack.types.common.Constants;
import lombok.extern.slf4j.Slf4j;
......
......@@ -5,7 +5,7 @@ import cn.bugstack.domain.strategy.model.entity.RuleMatterEntity;
import cn.bugstack.domain.strategy.model.valobj.RuleLogicCheckTypeVO;
import cn.bugstack.domain.strategy.repository.IStrategyRepository;
import cn.bugstack.domain.strategy.service.annotation.LogicStrategy;
import cn.bugstack.domain.strategy.service.rule.ILogicFilter;
import cn.bugstack.domain.strategy.service.rule.filter.ILogicFilter;
import cn.bugstack.domain.strategy.service.rule.filter.factory.DefaultLogicFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
......
......@@ -5,7 +5,7 @@ import cn.bugstack.domain.strategy.model.entity.RuleMatterEntity;
import cn.bugstack.domain.strategy.model.valobj.RuleLogicCheckTypeVO;
import cn.bugstack.domain.strategy.repository.IStrategyRepository;
import cn.bugstack.domain.strategy.service.annotation.LogicStrategy;
import cn.bugstack.domain.strategy.service.rule.ILogicFilter;
import cn.bugstack.domain.strategy.service.rule.filter.ILogicFilter;
import cn.bugstack.domain.strategy.service.rule.filter.factory.DefaultLogicFactory;
import cn.bugstack.types.common.Constants;
import lombok.extern.slf4j.Slf4j;
......
package cn.bugstack.domain.strategy.service.rule.tree;
import cn.bugstack.domain.strategy.service.rule.tree.factory.DefaultTreeFactory;
/**
* @description: 规则树接口
* @author: hdr
* @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.tree
* @DATE: 2024/4/19
*/
public interface ILogicTreeNode {
DefaultTreeFactory.TreeActionEntity logic(String userId, Long strategyId, Integer awardId);
}
package cn.bugstack.domain.strategy.service.rule.tree.factory;
import cn.bugstack.domain.strategy.model.valobj.RuleLogicCheckTypeVO;
import cn.bugstack.domain.strategy.model.valobj.RuleTreeVO;
import cn.bugstack.domain.strategy.service.rule.tree.ILogicTreeNode;
import cn.bugstack.domain.strategy.service.rule.tree.factory.engine.IDecisionTreeEngine;
import cn.bugstack.domain.strategy.service.rule.tree.factory.engine.impl.DecisionTreeEngine;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @description: 库存节点
* @author: hdr
* @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.tree.factory
* @DATE: 2024/4/22
*/
@Service
public class DefaultTreeFactory {
private final Map<String, ILogicTreeNode> logicTreeNodeGroup;
public DefaultTreeFactory(Map<String, ILogicTreeNode> logicTreeNodeGroup) {
this.logicTreeNodeGroup = logicTreeNodeGroup;
}
public IDecisionTreeEngine openLogicTree(RuleTreeVO ruleTreeVO) {
return new DecisionTreeEngine(logicTreeNodeGroup, ruleTreeVO);
}
/**
* 决策树动作实现
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class TreeActionEntity {
private RuleLogicCheckTypeVO ruleLogicCheckType;
private StrategyAwardData strategyAwardData;
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class StrategyAwardData {
/**
* 抽奖奖品ID - 内部流转
*/
private Integer awardId;
/**
* 抽奖奖品规则
*/
private String awardRuleValue;
}
}
package cn.bugstack.domain.strategy.service.rule.tree.factory.engine;
import cn.bugstack.domain.strategy.service.rule.tree.factory.DefaultTreeFactory;
/**
* @description:
* @author: hdr
* @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.tree.factory.engine
* @DATE: 2024/4/22
*/
public interface IDecisionTreeEngine {
DefaultTreeFactory.StrategyAwardData process(String userId, Long strategyId, Integer awardId);
}
package cn.bugstack.domain.strategy.service.rule.tree.factory.engine.impl;
import cn.bugstack.domain.strategy.model.valobj.RuleLogicCheckTypeVO;
import cn.bugstack.domain.strategy.model.valobj.RuleTreeNodeLineVO;
import cn.bugstack.domain.strategy.model.valobj.RuleTreeNodeVO;
import cn.bugstack.domain.strategy.model.valobj.RuleTreeVO;
import cn.bugstack.domain.strategy.service.rule.tree.ILogicTreeNode;
import cn.bugstack.domain.strategy.service.rule.tree.factory.DefaultTreeFactory;
import cn.bugstack.domain.strategy.service.rule.tree.factory.engine.IDecisionTreeEngine;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.Map;
/**
* @description:
* @author: hdr
* @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.tree.factory.engine.impl
* @DATE: 2024/4/23
*/
@Slf4j
public class DecisionTreeEngine implements IDecisionTreeEngine {
private final Map<String, ILogicTreeNode> logicTreeNodeGroup;
private final RuleTreeVO ruleTreeVO;
public DecisionTreeEngine(Map<String, ILogicTreeNode> logicTreeNodeGroup, RuleTreeVO ruleTreeVO) {
this.logicTreeNodeGroup = logicTreeNodeGroup;
this.ruleTreeVO = ruleTreeVO;
}
@Override
public DefaultTreeFactory.StrategyAwardData process(String userId, Long strategyId, Integer awardId) {
DefaultTreeFactory.StrategyAwardData strategyAwardData = null;
// 获取基础信息
String nextNode = ruleTreeVO.getTreeRootRuleNode();
Map<String, RuleTreeNodeVO> treeNodeVOMap = ruleTreeVO.getTreeNodeMap();
// 获取其实节点 【根节点记录了第一个要执行的规则】
RuleTreeNodeVO ruleTreeNode = treeNodeVOMap.get(nextNode);
while (null != nextNode) {
ILogicTreeNode logicTreeNode = logicTreeNodeGroup.get(ruleTreeNode.getRuleKey());
DefaultTreeFactory.TreeActionEntity logicEntity = logicTreeNode.logic(userId, strategyId, awardId);
RuleLogicCheckTypeVO ruleLogicCheckTypeVO = logicEntity.getRuleLogicCheckType();
strategyAwardData = logicEntity.getStrategyAwardData();
log.info("决策树引擎【{}】treeId:{} node:{} code:{}", ruleTreeVO.getTreeName(), ruleTreeVO.getTreeId(), nextNode, ruleLogicCheckTypeVO.getCode());
// 获取下个节点
nextNode = nextNode(ruleLogicCheckTypeVO.getCode(), ruleTreeNode.getTreeNodeLineVOList());
ruleTreeNode = treeNodeVOMap.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();
}
}
throw new RuntimeException("决策树引擎,nextNode 计算失败,未找到可执行节点!");
}
private boolean decisionLogic(String matterValue, RuleTreeNodeLineVO nodeLine) {
switch (nodeLine.getRuleLimitType()) {
case EQUAL:
return matterValue.equals(nodeLine.getRuleLimitValue().getCode());
// 以下规则暂时不需要实现
case GT:
case LT:
case GE:
case LE:
default:
return false;
}
}
}
package cn.bugstack.domain.strategy.service.rule.tree.impl;
import cn.bugstack.domain.strategy.model.valobj.RuleLogicCheckTypeVO;
import cn.bugstack.domain.strategy.service.rule.tree.ILogicTreeNode;
import cn.bugstack.domain.strategy.service.rule.tree.factory.DefaultTreeFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @description:
* @author: hdr
* @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.tree.impl
* @DATE: 2024/4/22
*/
@Slf4j
@Component("rule_lock")
public class RuleLockLogicTreeNode implements ILogicTreeNode {
@Override
public DefaultTreeFactory.TreeActionEntity logic(String userId, Long strategyId, Integer awardId) {
return DefaultTreeFactory.TreeActionEntity.builder()
.ruleLogicCheckType(RuleLogicCheckTypeVO.ALLOW)
.build();
}
}
package cn.bugstack.domain.strategy.service.rule.tree.impl;
import cn.bugstack.domain.strategy.model.valobj.RuleLogicCheckTypeVO;
import cn.bugstack.domain.strategy.service.rule.tree.ILogicTreeNode;
import cn.bugstack.domain.strategy.service.rule.tree.factory.DefaultTreeFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @description: 兜底奖励节点
* @author: hdr
* @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.tree.impl
* @DATE: 2024/4/23
*/
@Slf4j
@Component("rule_luck_award")
public class RuleLuckAwardLogicTreeNode implements ILogicTreeNode {
@Override
public DefaultTreeFactory.TreeActionEntity logic(String userId, Long strategyId, Integer awardId) {
return DefaultTreeFactory.TreeActionEntity.builder()
.ruleLogicCheckType(RuleLogicCheckTypeVO.TAKE_OVER)
.strategyAwardData(DefaultTreeFactory.StrategyAwardData.builder()
.awardId(101)
.awardRuleValue("1,100")
.build())
.build();
}
}
package cn.bugstack.domain.strategy.service.rule.tree.impl;
import cn.bugstack.domain.strategy.model.valobj.RuleLogicCheckTypeVO;
import cn.bugstack.domain.strategy.service.rule.tree.ILogicTreeNode;
import cn.bugstack.domain.strategy.service.rule.tree.factory.DefaultTreeFactory;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.C;
import org.springframework.stereotype.Component;
/**
* @description: 库存扣减节点
* @author: hdr
* @PACKAGE_NAME: cn.bugstack.domain.strategy.service.rule.tree.impl
* @DATE: 2024/4/23
*/
@Slf4j
@Component("rule_stock")
public class RuleStockLogicTreeNode implements ILogicTreeNode {
@Override
public DefaultTreeFactory.TreeActionEntity logic(String userId, Long strategyId, Integer awardId) {
return DefaultTreeFactory.TreeActionEntity.builder()
.ruleLogicCheckType(RuleLogicCheckTypeVO.TAKE_OVER)
.build();
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册