目录
在项目开发中,后端研发定义的规则表达式,由于掺杂字段定义、操作符、具体数值等,对业务运营人员比较晦涩难懂,不易理解,解释成本也比较高,为了更好将规则表达式的含义触达业务运营人员,将规则表达式转译为业务术语是一种比较好的方法,下面针对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表达式抽象语法树的遍历,完成对规则表达式的转译;