• SpringEL:SpEL表达式文本转译


    目录

    1.效果展示

    2.实现方法


    在项目开发中,后端研发定义的规则表达式,由于掺杂字段定义、操作符、具体数值等,对业务运营人员比较晦涩难懂,不易理解,解释成本也比较高,为了更好将规则表达式的含义触达业务运营人员,将规则表达式转译为业务术语是一种比较好的方法,下面针对SpringEL编写的规则表达式如何进行转译提供了一种思路:

    1.效果展示

    1. @Test
    2. public void filterConvertComplex() {
    3. String srcContent =
    4. "#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))";
    5. String resStr = bizItemManager.convertDisplayContent(srcContent, "");
    6. Assert.assertThat(resStr, is(
    7. "业务来源 = 交易履约 并且 付款方式 = 在线支付 并且 ((单据类型 = 普通销售 并且 商品行类型 = 普通商品) 或者 (单据类型 满足 (退货退款 或 仅退款) 并且 商品行类型 = 售后赠品未退回))"));
    8. }

    2.实现方法

    1. @Slf4j
    2. public abstract class AbstractConfigConvert {
    3. @Resource
    4. private ConfigTranslateDisplayService configTranslateDisplayService;
    5. private Map mccFieldsMap = MccConfigUtils.getTransformDisplayOperatorFields();
    6. private static final Pattern MATCHES_PATTERN1 = Pattern.compile("(-?\\d{1,5})((\\|)(\\-?\\d{1,5}))+");
    7. private static final Pattern MATCHES_PATTERN2 = Pattern.compile("(\\-?\\d{1,5})(\\-)(\\-?\\d{1,5})");
    8. private static final Pattern NUM_PATTERN = Pattern.compile("(\\-?\\d{1,5})");
    9. // 转译引擎
    10. public List convertDisplayEngine(String templateCode, List objects) {
    11. log.info("开始进行字符串转译,templateCode = {},items = {}", templateCode, objects);
    12. try {
    13. // 寻找转译字段并翻译
    14. if (!CollectionUtils.isEmpty(objects)) {
    15. Class clazz = objects.get(0).getClass();
    16. List fields = Arrays.stream(clazz.getDeclaredFields())
    17. .filter(field -> !field.isSynthetic() && field.getAnnotation(SwitchDisplay.class) != null)
    18. .collect(Collectors.toList());
    19. objects = objects.stream().peek(o -> {
    20. for (Field field : fields) {
    21. field.setAccessible(true);
    22. SwitchDisplay switchDisplay = field.getAnnotation(SwitchDisplay.class);
    23. Field srcField = ReflectionUtils.findField(clazz, switchDisplay.srcFieldName());
    24. srcField.setAccessible(true);
    25. String srcContent = (String) ReflectionUtils.getField(srcField, o);
    26. if (StringUtils.isNotBlank(srcContent)) {
    27. String resContent = convertDisplayContent(srcContent, templateCode);
    28. log.info("转译结果:{}", resContent);
    29. ReflectionUtils.setField(field, o, resContent);
    30. // 如果不需要可以删掉
    31. ReflectionUtils.setField(srcField, o, resContent);
    32. }
    33. }
    34. }).collect(Collectors.toList());
    35. }
    36. } catch (Exception e) {
    37. log.warn("过滤器转译失败,templateCode:{},objects:{}", templateCode, objects);
    38. }
    39. return objects;
    40. }
    41. /**
    42. * 生成转译后的展示内容
    43. *
    44. * @param srcContent 原始内容
    45. * @param templateCode 模板
    46. * @return 转移后的内容
    47. */
    48. public String convertDisplayContent(String srcContent, String templateCode) {
    49. ExpressionParser parser = new SpelExpressionParser();
    50. SpelExpression spelExpression = (SpelExpression) parser.parseExpression(srcContent);
    51. SpelNodeImpl root = (SpelNodeImpl) spelExpression.getAST();
    52. return travelAndTranslate(root, templateCode, srcContent);
    53. }
    54. // MCC字段转译
    55. private String mccConfigTranslate(String mccField) {
    56. mccField = StringUtils.isNoneBlank(mccFieldsMap.get(mccField)) ? mccFieldsMap.get(mccField) : mccField;
    57. return mccField;
    58. }
    59. /** 遍历EL表达式语义树,生成转译后的字符串 */
    60. private String travelAndTranslate(SpelNodeImpl root, String templateCode, String srcContent) {
    61. if (root instanceof Operator && root.getChildCount() > 1) {
    62. Operator operator = (Operator) root;
    63. SpelNodeImpl left = operator.getLeftOperand();
    64. SpelNodeImpl right = operator.getRightOperand();
    65. boolean isLeftLeaf = left instanceof CompoundExpression;
    66. boolean isRightLeaf = right instanceof Literal || right instanceof OpMinus;
    67. if (isLeftLeaf && isRightLeaf) {
    68. // 到达叶子节点
    69. String leftValue = (String) ((Literal) left.getChild(1).getChild(0)).getLiteralValue().getValue();
    70. String rightValue =
    71. right instanceof Literal ? String.valueOf(((Literal) right).getLiteralValue().getValue())
    72. : right.toStringAST();
    73. // mcc转译操作符
    74. String operatorValue = mccConfigTranslate(operator.getOperatorName());
    75. String res = translateValue(leftValue, rightValue, templateCode, operatorValue);
    76. return replenishBrackets(res, left, right, srcContent);
    77. }
    78. String leftValue = travelAndTranslate((SpelNodeImpl) root.getChild(0), templateCode, srcContent);
    79. String rightValue = travelAndTranslate((SpelNodeImpl) root.getChild(1), templateCode, srcContent);
    80. return leftValue + " " + mccConfigTranslate(operator.getOperatorName()) + " " + rightValue;
    81. }
    82. return root.toStringAST();
    83. }
    84. /** 转译表达式值 */
    85. private String translateValue(String leftValue, String rightValue, String templateCode, String operatorValue) {
    86. // 获取右边表达式集合
    87. Map rightStringMap = regularRightExpr(rightValue);
    88. List rightValues = (List) rightStringMap.get("rightValues");
    89. String rightOperator = (String) rightStringMap.get("operator");
    90. rightOperator = mccConfigTranslate(rightOperator);
    91. List displays;
    92. if (CollectionUtils.isEmpty(rightValues)) {
    93. displays = configTranslateDisplayService.queryDisplayListByFieldName(leftValue);
    94. } else {
    95. displays = configTranslateDisplayService.queryDisplayListByFieldNameFieldVal(leftValue, rightValues);
    96. }
    97. if (!CollectionUtils.isEmpty(displays)) {
    98. List displayList = displays.stream()
    99. .filter(config -> config.getTemplateCode().equals(templateCode)).collect(Collectors.toList());
    100. // 模版过滤后结果list为空,则过滤默认模版
    101. if (CollectionUtils.isEmpty(displayList)) {
    102. displays =
    103. displays.stream()
    104. .filter(configCenterTranslateDisplay -> StringUtils
    105. .isEmpty(configCenterTranslateDisplay.getTemplateCode()))
    106. .collect(Collectors.toList());
    107. } else {
    108. displays = displayList;
    109. }
    110. if (displays.isEmpty()) {
    111. log.info("未查询到相关转译信息");
    112. return leftValue + " " + operatorValue + " " + rightValue;
    113. }
    114. leftValue = displays.get(0).getFieldDesc();
    115. if (!CollectionUtils.isEmpty(rightValues)) {
    116. if (rightValues.size() == 1) {
    117. rightValue = displays.get(0).getFieldValDesc();
    118. } else if (displays.size() < rightValues.size()) {
    119. // 查到转译结果数量小于要转译的数量
    120. rightValue = processNonCompleteFound(rightValues, displays, rightOperator);
    121. } else {
    122. rightValue = processCompleteFound(displays, rightOperator);
    123. }
    124. }
    125. }
    126. return leftValue + " " + operatorValue + " " + rightValue;
    127. }
    128. /** 补充表达式原有括号 */
    129. private String replenishBrackets(String rawRes, SpelNodeImpl left, SpelNodeImpl right, String srcContent) {
    130. StringBuilder res = new StringBuilder(rawRes);
    131. // 添加左括号
    132. for (int i = left.getStartPosition() - 1; i > -1; i--) {
    133. if (srcContent.charAt(i) != '(') {
    134. break;
    135. }
    136. res.insert(0, '(');
    137. }
    138. // 添加右括号
    139. for (int i = right.getEndPosition(); i < srcContent.length(); i++) {
    140. if (srcContent.charAt(i) != ')') {
    141. break;
    142. }
    143. res.append(')');
    144. }
    145. return res.toString();
    146. }
    147. /** 处理未全部查询到转译数据的情况 */
    148. private String processNonCompleteFound(List values, List displays,
    149. String operator) {
    150. operator = mccConfigTranslate(operator);
    151. if (CollectionUtils.isEmpty(displays)) {
    152. return StringUtils.join(values, " " + operator + " ");
    153. }
    154. List displayValues =
    155. displays.stream().map(ConfigCenterTranslateDisplay::getFieldVal).collect(Collectors.toList());
    156. values.removeAll(displayValues);
    157. String res = joinValue(displays, operator);
    158. return "(" + res + " " + operator + " " + StringUtils.join(values, " " + operator + " ") + ")";
    159. }
    160. /** 处理全部查询到转译数据的情况 */
    161. private String processCompleteFound(List displays, String operator) {
    162. return "(" + joinValue(displays, operator) + ")";
    163. }
    164. /** 统一转换右侧表达式 */
    165. private Map regularRightExpr(String rightValue) {
    166. List rightValues = new ArrayList<>();
    167. String operator = "|";
    168. Matcher m1 = MATCHES_PATTERN1.matcher(rightValue);
    169. Matcher m2 = MATCHES_PATTERN2.matcher(rightValue);
    170. if (m1.find()) {
    171. // 匹配x1|x2|x3格式
    172. Matcher numMatcher = NUM_PATTERN.matcher(rightValue);
    173. while (numMatcher.find()) {
    174. rightValues.add(numMatcher.group());
    175. }
    176. } else if (m2.find()) {
    177. // 匹配[a-b]格式
    178. int leftBoundaryValue = Integer.parseInt(m2.group(1));
    179. int rightBoundaryValue = Integer.parseInt(m2.group(3));
    180. for (int i = leftBoundaryValue; i <= rightBoundaryValue; i++) {
    181. rightValues.add(String.valueOf(i));
    182. }
    183. } else {
    184. rightValues.add(rightValue);
    185. }
    186. Map map = new HashMap<>();
    187. map.put("rightValues", rightValues);
    188. map.put("operator", operator);
    189. return map;
    190. }
    191. /** 拼接转译值及操作符 返回值示例: 货到付款 或 在线支付 或 帐期支付 */
    192. private String joinValue(List displays, String operator) {
    193. return StringUtils.join(
    194. displays.stream().map(ConfigCenterTranslateDisplay::getFieldValDesc).collect(Collectors.toList()),
    195. " " + operator + " ");
    196. }
    197. }

    关注2点:

    1.操作符转译名称映射配置在配置中心上;

    2.字段转译映射配置在Mysql数据库中(包括字段名称描述以及关联的字段枚举值描述)

    由上,通过对SpringEL表达式抽象语法树的遍历,完成对规则表达式的转译;

  • 相关阅读:
    文件的上传和下载
    一文了解GCC(GNU C)语法
    线路位宽与 CPU 位宽
    Oracle EBS 重新打开库存会期间
    西电软件体系结构CH4:理解质量属性
    [数据可视化] 漏斗图(Funnel Chart)
    07 数据库查询(1) | OushuDB 数据库使用入门
    A tour of gRPC:08 - gRPC 反射 与 Evans 客户端
    面试题:Java中为什么只有值传递?
    Kaggle 专利匹配比赛赛后总结
  • 原文地址:https://blog.csdn.net/supzhili/article/details/133750774