当前位置: 首页 > news >正文

链式多分支规则树模型的应用

 目录

开始调用

初始化 


欢迎关注我的博客!26届java选手,一起加油💘💦👨‍🎓😄😂

引入

最近在学习一个项目中的链式多分枝规则树模型的使用,模型如下:

如图所示:

这是一种 链式多分支规则树模型 设计模式,核心是通过功能节点自主决策后续流程执行链路,相比责任链模式,它允许更灵活的分支扩展,每个节点像 “决策者” 一样根据规则选择下一步走向。以下是核心组件拆解:

StrategyMapper、StrategyHandler、AbstractStrategyRouter定义在type层,与业务层隔离,然后将DefaultActivityStrategyFactory、AbstractGroupBuyMarketSupport与各节点定义在业务层进行业务逻辑处理。

  • 策略映射器(StrategyMapper)与策略处理器(StrategyHandler)
    实现抽象类 AbstractStrategyRouter,前者负责策略映射(如定义不同场景对应规则),后者处理具体策略逻辑(如执行规则计算)。
  • 策略路由抽象类(AbstractStrategyRouter)
    定义路由规则的抽象框架,规范策略映射、处理的通用逻辑,是整个流程的 “规则模板”。
  • 策略工厂(DefaultActivityStrategyFactory)
    作为 “对象制造工厂”,负责创建拼团活动相关的策略实例,确保策略对象的统一管理与创建。
  • 功能服务支撑类(AbstractGroupBuyMarketSupport)
    提供底层通用服务(如数据校验、基础计算),像 “后勤保障”,供上层节点流程调用。
  • 节点体系(RootNode、SwitchRoot 等)
    • 根节点(RootNode):流程起点,类似 “入口”。
    • 开关节点(SwitchRoot):核心决策点,根据条件(如用户类型、活动规则)选择分支(走向其他节点或默认分支)。
    • 营销节点(MarketNode):处理营销相关逻辑(如优惠计算)。
    • 结尾节点(EndNode):流程终点,标志链路结束。

开始调用

    @Resource
    private DefaultActivityStrategyFactory defaultActivityStrategyFactory;


    @Override
    public TrialBalanceEntity indexMarketTrial(MarketProductEntity marketProductEntity) throws Exception {
        // 获取执行策略
        StrategyHandler<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> strategyHandler = defaultActivityStrategyFactory.strategyHandler();
        // 受理试算操作
        return strategyHandler.apply(marketProductEntity, new DefaultActivityStrategyFactory.DynamicContext());
    }

初始化

起始点为DefaultActivityStrategyFactory 策略工厂 ,返回了rootNode给我们,也就是此时的 strategyHandler是rootNode类型的,如下:

@Service
public class DefaultActivityStrategyFactory {

    private final RootNode rootNode;

    public DefaultActivityStrategyFactory(RootNode rootNode) {
        this.rootNode = rootNode;
    }

    public StrategyHandler<MarketProductEntity, DynamicContext, TrialBalanceEntity> strategyHandler() {
        return rootNode;
    }

    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public static class DynamicContext {
        // 拼团活动营销配置值对象
        private GroupBuyActivityDiscountVO groupBuyActivityDiscountVO;
        // 商品信息
        private SkuVO skuVO;
        // 折扣金额
        private BigDecimal deductionPrice;
        // 支付金额
        private BigDecimal payPrice;
        // 活动可见性限制
        private boolean visible;
        // 活动
        private boolean enable;
    }

}

执行流程

// 受理试算操作
        return strategyHandler.apply(marketProductEntity, new DefaultActivityStrategyFactory.DynamicContext());

rootNode继承自AbstractGroupBuyMarketSupport——功能服务支撑类,AbstractGroupBuyMarketSupport又继承自AbstractMultiThreadStrategyRouter——策略路由抽象类,就会去执行下面这里的apply方法,

public abstract class AbstractMultiThreadStrategyRouter<T, D, R> implements StrategyMapper<T, D, R>, StrategyHandler<T, D, R> {

    @Getter
    @Setter
    protected StrategyHandler<T, D, R> defaultStrategyHandler = StrategyHandler.DEFAULT;

    public R router(T requestParameter, D dynamicContext) throws Exception {
        StrategyHandler<T, D, R> strategyHandler = get(requestParameter, dynamicContext);
        if(null != strategyHandler) return strategyHandler.apply(requestParameter, dynamicContext);
        return defaultStrategyHandler.apply(requestParameter, dynamicContext);
    }

    @Override
    public R apply(T requestParameter, D dynamicContext) throws Exception {
        // 异步加载数据
        multiThread(requestParameter, dynamicContext);
        // 业务流程受理
        return doApply(requestParameter, dynamicContext);
    }

    /**
     * 异步加载数据
     */
    protected abstract void multiThread(T requestParameter, D dynamicContext) throws ExecutionException, InterruptedException, TimeoutException;

    /**
     * 业务流程受理
     */
    protected abstract R doApply(T requestParameter, D dynamicContext) throws Exception;

}

会先去AbstractGroupBuyMarketSupport看看有没有重写multiThread和doApply方法,

public abstract class AbstractGroupBuyMarketSupport<MarketProductEntity, DynamicContext, TrialBalanceEntity> extends AbstractMultiThreadStrategyRouter<cn.bugstack.domain.activity.model.entity.MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, cn.bugstack.domain.activity.model.entity.TrialBalanceEntity> {

    protected long timeout = 500;
    @Resource
    protected IActivityRepository repository;

    @Override
    protected void multiThread(cn.bugstack.domain.activity.model.entity.MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws ExecutionException, InterruptedException, TimeoutException {
        // 缺省的方法
    }

}

当我们的rootNode不想用到多线程加载数据的时候就没有重写这个方法,为空,但是rootNode重写了doApply方法,也就是在这里处理rootNode想要处理的业务,

@Slf4j
@Service
public class RootNode extends AbstractGroupBuyMarketSupport<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> {

    @Resource
    private SwitchNode switchNode;

    @Override
    protected TrialBalanceEntity doApply(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {
        log.info("商品查询试算服务-RootNode userId:{} requestParameter:{}", requestParameter.getUserId(), JSON.toJSONString(requestParameter));
        // 参数判断
        if (StringUtils.isBlank(requestParameter.getUserId()) || StringUtils.isBlank(requestParameter.getGoodsId()) ||
                StringUtils.isBlank(requestParameter.getSource()) || StringUtils.isBlank(requestParameter.getChannel())) {
            throw new AppException(ResponseCode.ILLEGAL_PARAMETER.getCode(), ResponseCode.ILLEGAL_PARAMETER.getInfo());
        }
        return router(requestParameter, dynamicContext);
    }

    @Override
    public StrategyHandler<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> get(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {
        return switchNode;
    }

}

执行完doApply方法,就执行return router()方法,这里带上请求参数和上下文对象,router方法在AbstractMultiThreadStrategyRouter类中,负责流转节点

 public R router(T requestParameter, D dynamicContext) throws Exception {
        StrategyHandler<T, D, R> strategyHandler = get(requestParameter, dynamicContext);
        if(null != strategyHandler) return strategyHandler.apply(requestParameter, dynamicContext);
        return defaultStrategyHandler.apply(requestParameter, dynamicContext);
    }

这里调用的get是StrategyMapper——策略映射器的get方法,因为当前对象是rootNode,如果rootNode实现了get就会回到rootNode的get中

public interface StrategyMapper<T, D, R> {

    /**
     * 获取待执行策略
     *
     * @param requestParameter 入参
     * @param dynamicContext   上下文
     * @return 返参
     * @throws Exception 异常
     */
    StrategyHandler<T, D, R> get(T requestParameter, D dynamicContext) throws Exception;

}

回到rootNode,这里重写了get,也就是返回我们需要从rootNode去往的下一个节点

public class RootNode extends AbstractGroupBuyMarketSupport<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> {

    @Resource
    private SwitchNode switchNode;

    @Override
    protected TrialBalanceEntity doApply(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {
        log.info("拼团商品查询试算服务-RootNode userId:{} requestParameter:{}", requestParameter.getUserId(), JSON.toJSONString(requestParameter));
        // 参数判断
        if (StringUtils.isBlank(requestParameter.getUserId()) || StringUtils.isBlank(requestParameter.getGoodsId()) ||
                StringUtils.isBlank(requestParameter.getSource()) || StringUtils.isBlank(requestParameter.getChannel())) {
            throw new AppException(ResponseCode.ILLEGAL_PARAMETER.getCode(), ResponseCode.ILLEGAL_PARAMETER.getInfo());
        }
        return router(requestParameter, dynamicContext);
    }

    @Override
    public StrategyHandler<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> get(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {
        return switchNode;
    }

}

这里会返回switchNode给AbstractMultiThreadStrategyRouter,此时会执行switchNode的apply方法。

    public R router(T requestParameter, D dynamicContext) throws Exception {
        StrategyHandler<T, D, R> strategyHandler = get(requestParameter, dynamicContext);
        if(null != strategyHandler) return strategyHandler.apply(requestParameter, dynamicContext);
        return defaultStrategyHandler.apply(requestParameter, dynamicContext);
    }

与上述过程一样,如果switchNode实现了apply中的方法,就会执行,如果没有实现,就不会执行。再次执行doApply后就会再执行router,然后执行switch的get,这里返回了market Node,就会继续往下,以此类推的执行下去,

@Slf4j
@Service
public class SwitchNode extends AbstractGroupBuyMarketSupport<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> {

   //业务逻辑
        return router(requestParameter, dynamicContext);
    }

    @Override
    public StrategyHandler<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> get(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {
        return marketNode;
    }

}

MarketNode,在这里我们重写MultiThread方法,使用FutureTask异步查询数据后放入上下文,然后在DoApplay中还可以获取到上下文的数据进行业务处理,处理完毕后在此处还能按照业务进入下一个节点或者返回错误节点。

@Slf4j
@Service
public class MarketNode extends AbstractGroupBuyMarketSupport<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> {

    @Resource
    private ErrorNode errorNode;
    @Resource
    private TagNode tagNode;

    @Override
    protected void multiThread(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws ExecutionException, InterruptedException, TimeoutException {
        // 异步查询活动配置
       

        // 异步查询商品信息 - 在实际生产中,商品有同步库或者调用接口查询。这里暂时使用DB方式查询。
      
        // 写入上下文 - 对于一些复杂场景,获取数据的操作,有时候会在下N个节点获取,这样前置查询数据,可以提高接口响应效率
        
    }

    @Override
    public TrialBalanceEntity doApply(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {
      
        // 获取上面查询得到数据的上下文数据
       
        
        // 执行业务,继续放入上下文
      
     
        return router(requestParameter, dynamicContext);
    }

    @Override
    public StrategyHandler<MarketProductEntity, DefaultActivityStrategyFactory.DynamicContext, TrialBalanceEntity> get(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception {
        //走异常节点
        if (null == dynamicContext.getGroupBuyActivityDiscountVO() || null == dynamicContext.getSkuVO() || null == dynamicContext.getDeductionPrice()) {
            return errorNode;
        }

        return tagNode;
    }

}

相关文章:

  • 【KEIL5】HardFault问题DEBUG排查方式
  • 爱普生FC1610AN5G手机中替代传统晶振的理想之选
  • Hyperlane 文件分块上传服务端
  • 解决java使用easyexcel填充模版后,高度不一致问题
  • 【人工智能】DeepSeek 的上下文窗口扩展:解锁长文本处理的理论与实践
  • 【力扣hot100题】(088)最长有效括号
  • VS Code 的 .S 汇编文件里面的注释不显示绿色
  • 在spark中,窄依赖算子map和filter会组合为一个stage,这种情况下,map和filter是在一个task内进行的吗?
  • 玄机靶场-webshell查杀WP
  • viewmodel协程中执行耗时操作,导致viewmodel创建两次,导致observer失效
  • Linux 网络基础知识总结
  • 供应S620 支持 PD 的多协议双向快充移动电源解决方案
  • 保护PCBA的不同方法:喷三防漆 vs 镀膜
  • Ajax------免刷新地前后端交互
  • 力扣DAY46-50 | 热100 | 二叉树:展开为链表、pre+inorder构建、路径总和、最近公共祖先、最大路径和
  • 英伟达开源253B语言模型:Llama-3.1-Nemotron-Ultra-253B-v1 模型情况
  • #Hash 模式 vs History 模式
  • MCP基础学习四:MCP在AI应用中的集成(MCP在AI应用中的完整架构图)
  • 备赛蓝桥杯-Python-考前突击
  • EPLAN许可证更新教程
  • 成都建设招标网站首页/安徽网络推广和优化
  • 自己怎么做网站首页/被忽悠去做网销了
  • 赣州网站建设策划/做一个app软件大概要多少钱
  • 做电影网站哪个服务器好/百度竞价托管一月多少钱
  • 官渡网站设计制作/昆明seo网站管理
  • wordpress搜索增加条件/海南快速seo排名优化