目录
1.效果展示
2.实现方法
在项目开发中,后端研发定义的规则表达式,由于掺杂字段定义、操作符、具体数值等,对业务运营人员比较晦涩难懂,不易理解,解释成本也比较高,为了更好将规则表达式的含义触达业务运营人员,将规则表达式转译为业务术语是一种比较好的方法,下面针对SpringEL编写的规则表达式如何进行转译提供了一种思路:
@Test
public void filterConvertComplex() {
String srcContent =
"#data['bizRecordSource'] eq 1 and #data['paymentType'] eq 2 and ((#data['documentType'] eq 1 and #data['tradeItemType'] eq 1) or (#data['documentType'] matches '21|24' and #data['tradeItemType'] eq 6))";
String resStr = bizItemManager.convertDisplayContent(srcContent, "");
Assert.assertThat(resStr, is(
"业务来源 = 交易履约 并且 付款方式 = 在线支付 并且 ((单据类型 = 普通销售 并且 商品行类型 = 普通商品) 或者 (单据类型 满足 (退货退款 或 仅退款) 并且 商品行类型 = 售后赠品未退回))"));
}
@Slf4j
public abstract class AbstractConfigConvert {
@Resource
private ConfigTranslateDisplayService configTranslateDisplayService;
private Map mccFieldsMap = MccConfigUtils.getTransformDisplayOperatorFields();
private static final Pattern MATCHES_PATTERN1 = Pattern.compile("(-?\\d{1,5})((\\|)(\\-?\\d{1,5}))+");
private static final Pattern MATCHES_PATTERN2 = Pattern.compile("(\\-?\\d{1,5})(\\-)(\\-?\\d{1,5})");
private static final Pattern NUM_PATTERN = Pattern.compile("(\\-?\\d{1,5})");
// 转译引擎
public List convertDisplayEngine(String templateCode, List objects) {
log.info("开始进行字符串转译,templateCode = {},items = {}", templateCode, objects);
try {
// 寻找转译字段并翻译
if (!CollectionUtils.isEmpty(objects)) {
Class> clazz = objects.get(0).getClass();
List fields = Arrays.stream(clazz.getDeclaredFields())
.filter(field -> !field.isSynthetic() && field.getAnnotation(SwitchDisplay.class) != null)
.collect(Collectors.toList());
objects = objects.stream().peek(o -> {
for (Field field : fields) {
field.setAccessible(true);
SwitchDisplay switchDisplay = field.getAnnotation(SwitchDisplay.class);
Field srcField = ReflectionUtils.findField(clazz, switchDisplay.srcFieldName());
srcField.setAccessible(true);
String srcContent = (String) ReflectionUtils.getField(srcField, o);
if (StringUtils.isNotBlank(srcContent)) {
String resContent = convertDisplayContent(srcContent, templateCode);
log.info("转译结果:{}", resContent);
ReflectionUtils.setField(field, o, resContent);
// 如果不需要可以删掉
ReflectionUtils.setField(srcField, o, resContent);
}
}
}).collect(Collectors.toList());
}
} catch (Exception e) {
log.warn("过滤器转译失败,templateCode:{},objects:{}", templateCode, objects);
}
return objects;
}
/**
* 生成转译后的展示内容
*
* @param srcContent 原始内容
* @param templateCode 模板
* @return 转移后的内容
*/
public String convertDisplayContent(String srcContent, String templateCode) {
ExpressionParser parser = new SpelExpressionParser();
SpelExpression spelExpression = (SpelExpression) parser.parseExpression(srcContent);
SpelNodeImpl root = (SpelNodeImpl) spelExpression.getAST();
return travelAndTranslate(root, templateCode, srcContent);
}
// MCC字段转译
private String mccConfigTranslate(String mccField) {
mccField = StringUtils.isNoneBlank(mccFieldsMap.get(mccField)) ? mccFieldsMap.get(mccField) : mccField;
return mccField;
}
/** 遍历EL表达式语义树,生成转译后的字符串 */
private String travelAndTranslate(SpelNodeImpl root, String templateCode, String srcContent) {
if (root instanceof Operator && root.getChildCount() > 1) {
Operator operator = (Operator) root;
SpelNodeImpl left = operator.getLeftOperand();
SpelNodeImpl right = operator.getRightOperand();
boolean isLeftLeaf = left instanceof CompoundExpression;
boolean isRightLeaf = right instanceof Literal || right instanceof OpMinus;
if (isLeftLeaf && isRightLeaf) {
// 到达叶子节点
String leftValue = (String) ((Literal) left.getChild(1).getChild(0)).getLiteralValue().getValue();
String rightValue =
right instanceof Literal ? String.valueOf(((Literal) right).getLiteralValue().getValue())
: right.toStringAST();
// mcc转译操作符
String operatorValue = mccConfigTranslate(operator.getOperatorName());
String res = translateValue(leftValue, rightValue, templateCode, operatorValue);
return replenishBrackets(res, left, right, srcContent);
}
String leftValue = travelAndTranslate((SpelNodeImpl) root.getChild(0), templateCode, srcContent);
String rightValue = travelAndTranslate((SpelNodeImpl) root.getChild(1), templateCode, srcContent);
return leftValue + " " + mccConfigTranslate(operator.getOperatorName()) + " " + rightValue;
}
return root.toStringAST();
}
/** 转译表达式值 */
private String translateValue(String leftValue, String rightValue, String templateCode, String operatorValue) {
// 获取右边表达式集合
Map rightStringMap = regularRightExpr(rightValue);
List rightValues = (List) rightStringMap.get("rightValues");
String rightOperator = (String) rightStringMap.get("operator");
rightOperator = mccConfigTranslate(rightOperator);
List displays;
if (CollectionUtils.isEmpty(rightValues)) {
displays = configTranslateDisplayService.queryDisplayListByFieldName(leftValue);
} else {
displays = configTranslateDisplayService.queryDisplayListByFieldNameFieldVal(leftValue, rightValues);
}
if (!CollectionUtils.isEmpty(displays)) {
List displayList = displays.stream()
.filter(config -> config.getTemplateCode().equals(templateCode)).collect(Collectors.toList());
// 模版过滤后结果list为空,则过滤默认模版
if (CollectionUtils.isEmpty(displayList)) {
displays =
displays.stream()
.filter(configCenterTranslateDisplay -> StringUtils
.isEmpty(configCenterTranslateDisplay.getTemplateCode()))
.collect(Collectors.toList());
} else {
displays = displayList;
}
if (displays.isEmpty()) {
log.info("未查询到相关转译信息");
return leftValue + " " + operatorValue + " " + rightValue;
}
leftValue = displays.get(0).getFieldDesc();
if (!CollectionUtils.isEmpty(rightValues)) {
if (rightValues.size() == 1) {
rightValue = displays.get(0).getFieldValDesc();
} else if (displays.size() < rightValues.size()) {
// 查到转译结果数量小于要转译的数量
rightValue = processNonCompleteFound(rightValues, displays, rightOperator);
} else {
rightValue = processCompleteFound(displays, rightOperator);
}
}
}
return leftValue + " " + operatorValue + " " + rightValue;
}
/** 补充表达式原有括号 */
private String replenishBrackets(String rawRes, SpelNodeImpl left, SpelNodeImpl right, String srcContent) {
StringBuilder res = new StringBuilder(rawRes);
// 添加左括号
for (int i = left.getStartPosition() - 1; i > -1; i--) {
if (srcContent.charAt(i) != '(') {
break;
}
res.insert(0, '(');
}
// 添加右括号
for (int i = right.getEndPosition(); i < srcContent.length(); i++) {
if (srcContent.charAt(i) != ')') {
break;
}
res.append(')');
}
return res.toString();
}
/** 处理未全部查询到转译数据的情况 */
private String processNonCompleteFound(List values, List displays,
String operator) {
operator = mccConfigTranslate(operator);
if (CollectionUtils.isEmpty(displays)) {
return StringUtils.join(values, " " + operator + " ");
}
List displayValues =
displays.stream().map(ConfigCenterTranslateDisplay::getFieldVal).collect(Collectors.toList());
values.removeAll(displayValues);
String res = joinValue(displays, operator);
return "(" + res + " " + operator + " " + StringUtils.join(values, " " + operator + " ") + ")";
}
/** 处理全部查询到转译数据的情况 */
private String processCompleteFound(List displays, String operator) {
return "(" + joinValue(displays, operator) + ")";
}
/** 统一转换右侧表达式 */
private Map regularRightExpr(String rightValue) {
List rightValues = new ArrayList<>();
String operator = "|";
Matcher m1 = MATCHES_PATTERN1.matcher(rightValue);
Matcher m2 = MATCHES_PATTERN2.matcher(rightValue);
if (m1.find()) {
// 匹配x1|x2|x3格式
Matcher numMatcher = NUM_PATTERN.matcher(rightValue);
while (numMatcher.find()) {
rightValues.add(numMatcher.group());
}
} else if (m2.find()) {
// 匹配[a-b]格式
int leftBoundaryValue = Integer.parseInt(m2.group(1));
int rightBoundaryValue = Integer.parseInt(m2.group(3));
for (int i = leftBoundaryValue; i <= rightBoundaryValue; i++) {
rightValues.add(String.valueOf(i));
}
} else {
rightValues.add(rightValue);
}
Map map = new HashMap<>();
map.put("rightValues", rightValues);
map.put("operator", operator);
return map;
}
/** 拼接转译值及操作符 返回值示例: 货到付款 或 在线支付 或 帐期支付 */
private String joinValue(List displays, String operator) {
return StringUtils.join(
displays.stream().map(ConfigCenterTranslateDisplay::getFieldValDesc).collect(Collectors.toList()),
" " + operator + " ");
}
}
关注2点:
1.操作符转译名称映射配置在配置中心上;
2.字段转译映射配置在Mysql数据库中(包括字段名称描述以及关联的字段枚举值描述)
由上,通过对SpringEL表达式抽象语法树的遍历,完成对规则表达式的转译;