我们在做活动相关内容, 第一版脑子没转过来写成面向过程的代码结构, 想再扩展成本高, 难读懂. 于是有人指点一番设计了第二版, 基本结构是树状的, 首先是规则管理器, 负责执行规则以及存放规则执行器, 按照数据中的执行器类型选择执行器, 进入执行器, 并且根据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, "");
}
}
总结: 将活动任务拆分泛化为一个个点, 连接成树状/链状, 条件通过的情况下进行奖励发放. 这样的情况下对已有的同构/异构活动都能持续清晰扩展. 不会堆叠屎山代码.