目录
1.效果展示
2.实现方法
在项目开发中,后端研发定义的规则表达式,由于掺杂字段定义、操作符、具体数值等,对业务运营人员比较晦涩难懂,不易理解,解释成本也比较高,为了更好将规则表达式的含义触达业务运营人员,将规则表达式转译为业务术语是一种比较好的方法,下面针对SpringEL编写的规则表达式如何进行转译提供了一种思路:
1.效果展示
@Testpublic 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("业务来源 = 交易履约 并且 付款方式 = 在线支付 并且 ((单据类型 = 普通销售 并且 商品行类型 = 普通商品) 或者 (单据类型 满足 (退货退款 或 仅退款) 并且 商品行类型 = 售后赠品未退回))"));}
2.实现方法
@Slf4j
public abstract class AbstractConfigConvert {@Resourceprivate ConfigTranslateDisplayService configTranslateDisplayService;private Map<String, String> 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 <T> List<T> convertDisplayEngine(String templateCode, List<T> objects) {log.info("开始进行字符串转译,templateCode = {},items = {}", templateCode, objects);try {// 寻找转译字段并翻译if (!CollectionUtils.isEmpty(objects)) {Class<?> clazz = objects.get(0).getClass();List<Field> 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<String, Object> rightStringMap = regularRightExpr(rightValue);List<String> rightValues = (List<String>) rightStringMap.get("rightValues");String rightOperator = (String) rightStringMap.get("operator");rightOperator = mccConfigTranslate(rightOperator);List<ConfigCenterTranslateDisplay> displays;if (CollectionUtils.isEmpty(rightValues)) {displays = configTranslateDisplayService.queryDisplayListByFieldName(leftValue);} else {displays = configTranslateDisplayService.queryDisplayListByFieldNameFieldVal(leftValue, rightValues);}if (!CollectionUtils.isEmpty(displays)) {List<ConfigCenterTranslateDisplay> 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<String> values, List<ConfigCenterTranslateDisplay> displays,String operator) {operator = mccConfigTranslate(operator);if (CollectionUtils.isEmpty(displays)) {return StringUtils.join(values, " " + operator + " ");}List<String> 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<ConfigCenterTranslateDisplay> displays, String operator) {return "(" + joinValue(displays, operator) + ")";}/** 统一转换右侧表达式 */private Map<String, Object> regularRightExpr(String rightValue) {List<String> 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<String, Object> map = new HashMap<>();map.put("rightValues", rightValues);map.put("operator", operator);return map;}/** 拼接转译值及操作符 返回值示例: 货到付款 或 在线支付 或 帐期支付 */private String joinValue(List<ConfigCenterTranslateDisplay> displays, String operator) {return StringUtils.join(displays.stream().map(ConfigCenterTranslateDisplay::getFieldValDesc).collect(Collectors.toList())," " + operator + " ");}
}
关注2点:
1.操作符转译名称映射配置在配置中心上;
2.字段转译映射配置在Mysql数据库中(包括字段名称描述以及关联的字段枚举值描述)
由上,通过对SpringEL表达式抽象语法树的遍历,完成对规则表达式的转译;