营销系统规则引擎
一、系统介绍
规则引擎是一个用于执行营销规则的模块,其包括营销规则配置、规则校验等功能。规则引擎可以根据预先设定的条件和逻辑,自动化地执行特点的营销策略,帮助企业更好地吸引客户,增加销售和提高客户满意度。
规则引擎功能列表:
1、规则装配 - 根据活动类型组装不同营销规则
2、逻辑设计 - 支持与、或、非等复杂逻辑关系。
3、配置校验 - 支持对活动规则进行自动化校验。
二、技术实现
1.UserCenterRegistryActivityRule - 个人中心注册活动规则
class UserCenterRegistryActivityRule extends ActivityRule{
public UserCenterRegistryActivityRule(){
super("region_rule&channel_rule&(user_group_rule|user_tag_rule)");
}
public String rootRulePath(){
return "usercenter_registry_activity";
}
}
2.ActivityRule - 活动规则抽象
public abstract class ActivityRule implements RuleItem{
/**
* 规则表达式
*/
private String ruleExpression;
/**
* 规则项配置校验
*/
@Autowired
private Map<String, RuleItem> ruleItemMap;
public ActivityRule(String ruleExpression){
this.ruleExpression = ruleExpression;
}
/**
* 规则项校验
*/
public boolean apply(CalculateRequest request, PeContext peContext) {
Stack<Character> opStack = new Stack<>();
Stack<Boolean> resStack = new Stack<>();
Set<Character> opChSet = Set.of('(', ')', '&', '|');
int i = 0;
// 处理右括号
while (i < ruleExpression.length()) {
char ch = ruleExpression.charAt(i);
switch (ch) {
case '(':
opStack.push(ch);
i++;
break;
case ')':
while (opStack.peek() != '(') {
Character opChar = opStack.peek();
if (opChar == '&') {
boolean right = resStack.pop();
boolean left = resStack.pop();
resStack.push(left && right);
} else if (opChar == '|') {
boolean right = resStack.pop();
boolean left = resStack.pop();
resStack.push(left || right);
}
opStack.pop();
}
break;
case '&': // 操作符
opStack.push('&');
i++;
break;
case '|':
opStack.push('|');
i++;
break;
default: // 操作数
int j = i;
while (j < ruleExpression.length() && !opChSet.contains(ruleExpression.charAt(j))) {
j++;
}
String rule = ruleExpression.substring(i, j);
resStack.push(ruleItemMap.get(rule).apply(request, peContext));
i = j;
}
}
// 处理结果
while (!opStack.isEmpty()) {
Character opChar = opStack.peek();
if (opChar == '&') {
boolean right = resStack.pop();
boolean left = resStack.pop();
resStack.push(left && right);
} else if (opChar == '|') {
boolean right = resStack.pop();
boolean left = resStack.pop();
resStack.push(left || right);
}
opStack.pop();
}
return resStack.pop();
}
default String rootRulePath();
/**
* 规则配置校验
*/
public PathError validate(String rulePath) {
if (rulePath == null){
rulePath = rootRulePath();
}
PathError pathError = PathError.of(null, rulePath);
for (Map.Entry<String, RuleItem> entry : ruleItemMap.entrySet()) {
String ruleItemPath = entry.getKey();
RuleItem ruleItem = entry.getValue();
PathError ruleItemError = ruleItem.validate(ruleItemPath);
if (ruleItemError != null) {
return pathError.with(ruleItemError);
}
}
return null;
}
}
3.UserGroupRuleItem - 用户组规则项
class UserGroupRuleItem extends RuleItem{
private List<String> groupsLimit;
@Override
public PathError validate(String rulePath){
PathError pathError = PathError.of(null, rulePath);
if (groupsLimit == null || groupsLimit.size() == 0){
return pathError.withPath("groupsLimit").withError("groupsLimit不能为空");
}
return null;
}
@Overrider
public boolean apply(CalculateRequest req, PeContext context){
UserService userService = SpringUtil.getBean(UserService.class);
// 判断用户是否符合用户组规则
List<String> userGroups = userService.getUserGroups(req.getUserId());
if (groupsLimit.stream().anyMatch(userGroups::contains)){
return true;
}
retrun false;
}
}
4.RuleItem - 规则项定义
interface RuleItem {
// 规则配置校验
default PathError validate(String rulePath) {
PathError pathError = new PathError(null, rulePath);
// 校验规则字段
Field[] fields = FieldUtils.getSpecFields(getClass(), RuleItem.class);
if (fields.length == 0) {
return null;
}
try {
for (Field ruleField : fields) {
RuleItem rule = (Rule) FieldUtils.getField(this, ruleField);
if (rule == null){
continue;
}
String fieldName = ruleField.getName();
// 规则字段校验失败
PathError fieldPathError = rule.validate(fieldName);
// 返回字段校验失败结果
if (fieldPathError != null) {
return pathError.with(fieldPathError);
}
}
} catch (Exception e) {
throw new AppException(ErrorCode.SYSTEM_ERROR, String.format("校验Rule错误: %s", e.getMessage()));
}
return null;
}
// 规则应用
default boolean apply(CalculateRequest request, PeContext peContext){
return true;
};
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class PathError{
private String error;
private List<String> paths;
public PathError with(PathError other){
this.error = other.error;
this.paths.addAll(other.paths);
return this;
}
public PathError withError(String error){
this.error = error;
return this;
}
public PathError withPath(String path){
this.paths.add(path);
return this;
}
}
}