基于规则执行器的活动设计

 我们在做活动相关内容, 第一版脑子没转过来写成面向过程的代码结构, 想再扩展成本高, 难读懂. 于是有人指点一番设计了第二版, 基本结构是树状的, 首先是规则管理器, 负责执行规则以及存放规则执行器, 按照数据中的执行器类型选择执行器, 进入执行器, 并且根据group_index分组, 在同组内为链式结构, parent_id有层级关系的为树状结构.

这样设计可以将活动中的各个判断点拆分成一个个执行器. 所有判断点通过的情况下, 进入RewardExecutor, 进行奖励发放, 奖励发放执行器内部为工厂模式, 根据不同奖励获取不同工厂进行奖励具体发放.


/**
 * 通用规则执行管理器
 */
@Component
public class CommonRuleManager {

    /**
     * 规则上下文
     */
    public static final ThreadLocal<Map<String, Object>> RULE_CONTEXT = new ThreadLocal<>();

    /**
     * 规则执行器集合
     */
    private final Map<String, ICommonRuleExecutor> ruleExecutorMap;


    /**
     * 构造器
     * @param ruleExecutors 规则执行器集合
     */
    public CommonRuleManager(List<ICommonRuleExecutor> ruleExecutors) {
        if (ruleExecutors != null) {
            ruleExecutorMap = ruleExecutors.stream().collect(
                Collectors.toMap(item -> item.getRuleType().getCode(), Function.identity()));
        } else {
            ruleExecutorMap = new HashMap<>();
        }
    }

    /**
     * 执行规则
     * @param rule       规则实体
     * @param context    规则上下文
     * @return 规则执行结果
     */
    protected RuleResult execute(MrPromotionRule rule, Map<String, Object> context) {
        ICommonRuleExecutor executor = ruleExecutorMap.get(rule.getRuleType());
        if (executor == null) {
            throw new RuleTypeException("未找到指定类型的规则执行器:" + rule.getRuleType());
        }
        return executor.execute(rule, context);
    }

    /**
     * 执行规则树
     * @param tree      规则树
     * @param context   规则上下文
     * @return  规则执行结果
     */
    public RuleResult executeTree(List<Tree<Long>> tree, Map<String, Object> context) {
        if (CollUtil.isEmpty(tree)) {
            return RuleResult.of(true, StringUtils.EMPTY);
        }
        RuleResult result = null;
        boolean isRoot = RULE_CONTEXT.get() == null;
        if (isRoot) {
            RULE_CONTEXT.set(context);
        }
        try {
            for (Tree<Long> treeNode : tree) {
                MrPromotionRule rule = (MrPromotionRule) treeNode.get(TreeUtils.SELF);
                if (result == null) {
                    result = executeRuleNode(treeNode);
                } else {
                    if (RuleJoinType.AND.codeEquals(rule.getChainType())) {
                        if (!result.isResult()) {
                            break;
                        }
                        result = result.and(executeRuleNode(treeNode));
                    } else if (RuleJoinType.OR.codeEquals(rule.getChainType())) {
                        result = result.or(executeRuleNode(treeNode));
                    } else {
                        throw new RuleTypeException("不支持的规则类型");
                    }
                }
            }
            return result == null ? RuleResult.of(true, StringUtils.EMPTY) : result;
        } finally {
            if (isRoot) {
                RULE_CONTEXT.remove();
            }
        }
    }

    /**
     * 执行规则节点
     * @param treeNode  规则节点
     * @return  规则执行结果
     */
    private RuleResult executeRuleNode(Tree<Long> treeNode) {
        MrPromotionRule rule = (MrPromotionRule) treeNode.get(TreeUtils.SELF);
        if (CommonRuleType.GROUP.codeEquals(rule.getRuleType())) {
            return executeTree(treeNode.getChildren(), RULE_CONTEXT.get());
        } else {
            return execute(rule, RULE_CONTEXT.get());
        }
    }
}

 其中一个执行器, 判断是否已完成任务, 如果完成任务则返回规则通过/未通过.


/**
 * 历史任务规则执行器
 */
@Component
@Slf4j
public class ActivityTaskHistoryExecutor implements ICommonRuleExecutor {

    /**
     * 活动任务service
     */
    @Resource
    @Lazy
    private MrPromotionActivityTaskRecordService mrPromotionActivityTaskRecordService;

    /**
     * 任务历史规则
     * @return CommonRuleType
     */
    @Override
    public CommonRuleType getRuleType() {
        return CommonRuleType.ACITIVTY_TASK_HISTORY;
    }

    /**
     * 执行规则
     * 例1: [{"cycle": "WEEK", "taskType": 11, "taskCount": 1, "cycleCount": 1}, {"cycle": "WEEK", "joinType": "or", "taskType": 5, "taskCount": 1, "cycleCount": 1}]
     * 例2: {"cycle": "WEEK", "taskType": 13, "taskCount": 1, "cycleCount": 1}
     * @param rule 规则实体
     * @param context 上下文
     * @return 是否通过
     */
    @Override
    public RuleResult doExecute(MrPromotionRule rule, Map<String, Object> context) {
        try {
            String attributes = rule.getAttributes();
            RuleResult result = RuleResult.of(true, "");
            // 区分json的array和object
            if (attributes.startsWith("[")) {
                JSONArray jsonArray = new JSONArray(attributes);
                for (Object obj : jsonArray) {
                    JSONObject jsonObject = new JSONObject(obj.toString());
                    // 任务类型
                    String joinType = jsonObject.getStr("joinType");
                    if (joinType == null || "AND".equalsIgnoreCase(joinType)) {
                        if (!result.isResult()) {
                            break;
                        }
                        result = result.and(execute(jsonObject, context));
                    } else if ("OR".equalsIgnoreCase(joinType)) {
                        result = result.or(execute(jsonObject, context));
                    }
                }
            } else if (attributes.startsWith("{")) {
                JSONObject jsonObject = new JSONObject(attributes);
                result = result.and(execute(jsonObject, context));
            } else {
                throw new RuleTypeException("活动日期规则转化异常,attributes:" + attributes);
            }
            return result;
        } catch (Exception e) {
            log.error("日期规则执行异常", e);
            throw new RuleTypeException("活动日期规则转化异常,RuleId:" + rule.getId());
        }
    }

    /**
     * 执行单个规则
     * @param data
     * @param context
     * @return
     */
    private RuleResult execute(JSONObject data, Map<String, Object> context) {
        MrPromotionActivityTask task = (MrPromotionActivityTask)context.get("task");
        MrPromotionActivityTaskEvent event = (MrPromotionActivityTaskEvent)context.get("taskEvent");
        // 任务类型
        Integer taskType = data.getInt("taskType");
        // 任务数量
        Integer taskCount = data.getInt("taskCount");
        // 任务周期
        String cycle = data.getStr("cycle");
        // 任务周期数量
        Integer cycleCount = data.getInt("cycleCount");
        Date startDate = null;
        Date endDate = null;
        Date now = new Date();
        switch(cycle) {
            case "DAY":
                endDate = DateUtils.addDays(DateUtils.truncate(now, Calendar.DATE), 1);
                startDate = DateUtils.addDays(endDate, -cycleCount);
                break;
            case "WEEK":
                startDate = DateUtils.truncate(DateUtil.getWeekStartDate(now), Calendar.DATE);
                endDate = now;
                break;
            case "MONTH":
                startDate = DateUtils.truncate(now, Calendar.MONTH);
                endDate = now;
                break;
            case "YEAR":
                startDate = DateUtils.truncate(now, Calendar.YEAR);
                endDate = now;
                break;
            default:
                log.info("日期规则执行异常: {}", cycle);
                return RuleResult.of(false, "任务周期类型错误");

        }
        // 获取时间周期的开始/结束时间
        List<MrPromotionActivityTaskRecord> records = mrPromotionActivityTaskRecordService.getAllRecordByMobileAndTimeScope(task.getActivityId(), event.getMobile(), startDate, endDate);
        if (taskType == null) {
            if (records.size() < taskCount) {
                return RuleResult.of(false, "任务完成数量不足");
            }
        } else {
            // 判断对应类型的任务是否完成达到数量
            if (records.stream().filter(recordItem -> recordItem.getTaskType().equals(taskType)).count() < taskCount) {
                return RuleResult.of(false, "任务完成数量不足, 任务类型: " + taskType);
            }
        }
        return RuleResult.of(true, "");
    }
}

如果所有判断规则结束并通过, 则进入奖励执行器.


/**
 * 奖励执行器
 */
@Component
@Slf4j
public class ActivityTaskRewordFactoryExecutor implements ICommonRuleExecutor {

    /**
     * 任务奖励工厂
     */
    @Resource
    private TaskRewardFactoryManager taskRewardFactoryManager;


    /**
     * 奖励工厂规则
     * @return CommonRuleType
     */
    @Override
    public CommonRuleType getRuleType() {
        return CommonRuleType.ACITIVTY_TASK_REWARD_FACTORY;
    }

    /**
     * 执行规则
     * @param rule 规则实体
     * @param context 上下文
     * @return 是否通过
     */
    @Override
    public RuleResult doExecute(MrPromotionRule rule, Map<String, Object> context) {
        try {
            String attributes = rule.getAttributes();
            RuleResult result = RuleResult.of(true, "");
            // 区分json的array和object
            if (attributes.startsWith("[")) {
                JSONArray jsonArray = new JSONArray(attributes);
                for (Object obj : jsonArray) {
                    JSONObject jsonObject = new JSONObject(obj.toString());
                    // 任务类型
                    String joinType = jsonObject.getStr("joinType");
                    if (joinType == null || "AND".equalsIgnoreCase(joinType)) {
                        if (!result.isResult()) {
                            break;
                        }
                        result = result.and(execute(jsonObject, context));
                    } else if ("OR".equalsIgnoreCase(joinType)) {
                        result = result.or(execute(jsonObject, context));
                    }
                }
            } else if (attributes.startsWith("{")) {
                JSONObject jsonObject = new JSONObject(attributes);
                result = result.and(execute(jsonObject, context));
            } else {
                result = execute(new JSONObject(), context);
            }
            return result;
        } catch (Exception e) {
            log.error("日期规则执行异常", e);
            throw new RuleTypeException("活动日期规则转化异常,RuleId:" + rule.getId());
        }
    }

    /**
     * 执行单个规则
     * @param data
     * @param context
     * @return
     */
    private RuleResult execute(JSONObject data, Map<String, Object> context) {
        /*
        {"rewardType": 1, "rewardCount": 1}
         */
        log.info("执行任务奖励规则");
        MrPromotionActivityTask mrPromotionActivityTask = (MrPromotionActivityTask)context.get("task");
        MrPromotionActivityTaskEvent mrPromotionActivityTaskEvent = (MrPromotionActivityTaskEvent)context.get("taskEvent");
        Integer rewardCount = data.getInt("rewardCount");
        Integer rewardType = data.getInt("rewardType");
        if (rewardCount == null) {
            rewardCount = mrPromotionActivityTask.getRewardTime();
        }
        if (rewardType == null) {
            rewardType = mrPromotionActivityTask.getRewardType();
        }
        ITaskRewardFactory factory = taskRewardFactoryManager.get(mrPromotionActivityTask.getRewardType());
        factory.execute(rewardCount, rewardType, context);
        return RuleResult.of(true, "");
    }
}

总结: 将活动任务拆分泛化为一个个点, 连接成树状/链状, 条件通过的情况下进行奖励发放. 这样的情况下对已有的同构/异构活动都能持续清晰扩展. 不会堆叠屎山代码.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值