SpringEL:SpEL表达式文本转译

目录

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表达式抽象语法树的遍历,完成对规则表达式的转译;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/104724.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

多线程并发篇---第十篇

系列文章目录 文章目录 系列文章目录一、Java线程池中队列常用类型有哪些?二、线程安全需要保证几个基本特征?三、说一下线程之间是如何通信的?一、Java线程池中队列常用类型有哪些? ArrayBlockingQueue 是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则…

如何在 Spring Boot 中进行分布式追踪

在 Spring Boot 中进行分布式追踪 分布式系统中的应用程序由多个微服务组成&#xff0c;它们可以位于不同的服务器、容器或云中。当出现问题时&#xff0c;如性能瓶颈、错误或延迟&#xff0c;了解问题的根本原因变得至关重要。分布式追踪是一种用于跟踪和分析分布式应用程序性…

linux安装filebeat并收集日志到elasticsearch

摘要&#xff1a; 通过filebeat收集服务器上各个应用的日志到elasticsearch&#xff0c;通过tags区分不同的应用创建不同的索引保存日志。 官网地址&#xff1a; https://www.elastic.co/cn/downloads/past-releases#filebeat 安装步骤&#xff1a; 1&#xff1a;下载并解…

linux8使用yum安装reids流程

准备安装环境 yum install -y gcc tcl gcc-c make 使用yum进行处理问题 yum install redis -y 安装完成之后会有/etc/redis.conf&#xff0c;我们需要进行一个备份操作 cp /etc/redis.conf /home/redis/ 启动redis服务测试 能否登录 systemctl start redis systemctl st…

如何利用IP地址定位保护网络安全?

通过IP地址定位可以在一定程度上增强网络安全&#xff0c;但它并不是唯一的安全措施。以下是如何利用IP地址定位来保护网络安全的一些方法&#xff1a; 异常检测和入侵检测&#xff1a;监控网络上的IP地址流量&#xff0c;定位异常活动&#xff0c;如大规模的连接尝试、不寻常的…

免费 AI 编程助手 Amazon CodeWhisperer 体验

文章作者&#xff1a;文章作者&#xff1a;米菲爸爸 2022 年 6 月 23 亚马逊云科技就已经推出了 Amazon CodeWhisperer&#xff08;预览版&#xff09;。经过不到一年的测试和 AIGC的飓风在 2023 年 4 月 18 日实时 AI 编程助手 Amazon CodeWhisperer正式可用 Amazon CodeWhis…

微信浏览器大字体模式 按钮文字居中用line-height 显示异常

按钮文字居中用line-height 的css 在微信浏览器大字体模式&#xff0c;会导致显示错误。改成flex 居中就好了

对称加密和非对称加密以及CA证书

对称加密 对称加密只用到了公匙,这个公匙是消息的发送方和消息的接收方共享的,也就是消息发送方使用这个公匙对消息加密,然后接收方使用公匙对消息解密,最典型的例子比如像 encrypt 加密,比如我们有个 springboot 应用,需要对 application.yml 文件里的密码做加密,我们…

【LeetCode高频SQL50题-基础版】打卡第8天:第41~45题

文章目录 【LeetCode高频SQL50题-基础版】打卡第8天&#xff1a;第41~45题⛅前言好友申请II&#x1f512;题目&#x1f511;题解 2016年的投资&#x1f512;题目&#x1f511;题解 部门工资前三高的所有员工&#x1f512;题目&#x1f511;题解 修复表中的名字&#x1f512;题目…

阿里云服务器不能访问网络之安装mysql 提示连接超时

wget -i -c http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm 过了一段时间后提示 fail .......time out 链接超时 有可能你的服务器不能访问网络 因为宽带套餐 我购买的时候没有购 重新购买就行了

登录中获取验证码的节流

一. 验证码框 <el-input placeholder"请输入验证码" prefix-icon"el-icon-lock" v-model"ruleForm.code"><el-button slot"suffix" :disabled"disabled" type"text" size"mini" click"ch…

今年的秋招面试,确实有点难。

不可否认的是&#xff0c;今年秋招确实有点难 从今年的形势来看&#xff0c;好的 offer 都掌握在少数人的手里&#xff0c;想要秋招找到理想的工作&#xff0c;要么学历好&#xff0c;要么技术功底很扎实&#xff0c;这两样都不占的话&#xff0c;就业压力就会比较大。 如何从…

python之PyQt按钮右键菜单功能的实现代码

示例一 我们可以创建一个简单的窗口&#xff0c;然后在窗口中添加一个自定义按钮。 当鼠标右键点击按钮时&#xff0c;弹出菜单&#xff0c;并在菜单中选中某个选项后弹出消息框。 完整代码如下所示&#xff1a; # 导入PyQt库 from PyQt5.QtWidgets import QApplication, QM…

latex伪代码书写进阶(2)

algorithm2e 是一个 LaTeX 宏包&#xff0c;用于在文档中编写算法和伪代码。以下是 algorithm2e 常见的语法和使用方式的示例&#xff1a; 引入 algorithm2e 宏包&#xff1a; \usepackage{algorithm2e}设置算法标题和标签&#xff1a; \begin{algorithm}[h]\caption{Algori…

nodejs+vue+elementui养老院老年人服务系统er809

“养老智慧服务平台”是运用nodejs语言和vue框架&#xff0c;以MySQL数据库为基础而发出来的。为保证我国经济的持续性发展&#xff0c;必须要让互联网信息时代在我国日益壮大&#xff0c;蓬勃发展。伴随着信息社会的飞速发展&#xff0c;养老智慧服务平台所面临的问题也一个接…

C语言水平测试题 过关斩将(3)辗转相除法,前n项求和,整数的正序分解,求最大公约数

我的个人主页&#xff1a;☆光之梦☆的博客_CSDN博客-C语言基础语法&#xff08;超详细&#xff09;领域博主 欢迎各位 &#x1f44d;点赞 ⭐收藏 &#x1f4dd;评论 我的专栏&#xff1a;C语言基础语法&#xff08;超详细&#xff09;_☆光之梦☆的博客-CSDN博客&#xff08;这…

前后端开发环境下载,java web前后端分离项目所有环境下载

前言介绍 配置环境过程枯燥 又麻烦&#xff0c;同时经常设计版本依赖对应关系&#xff0c;所以这里将所有工具集合发出来供大家一次性下载。工具包含如下&#xff1a; 工具介绍&#xff1a; 下面为安装包名&#xff1a; linux环境&#xff1a; jdk-8u45-linux-x64.rpm (jav…

Linux 编写一个 简单进度条

进度条 回车换行理解&#xff1a; 我们要理解&#xff0c;回车换行是两个概念&#xff1a; 换行是把光标移到下一行&#xff0c;是竖直的往下平移&#xff1b;" \n "回车是把光标移到当前行的最开始&#xff1b; " \r " 就和一起打字…

【计网】傻瓜式安装cpolar内网穿透

目录 一、注册账户 二、下载安装包 三、安装 四、查看AuthToken 五、简单使用 如果在本机部署项目外网是访问不到的&#xff0c;通过内网穿透就可以使本机部署的项目可被外网访问 一、注册账户 cpolar - secure introspectable tunnels to localhostcpolar secure intro…

数字技术助力智慧公厕,让公厕变身为全新创新应用

在如今数字化的时代&#xff0c;数字技术的集成应用已经渗透到了生活的方方面面。其中一个令人瞩目的领域就是智慧公厕。以前只是简单的厕所&#xff0c;如今借助数字技术的力量&#xff0c;智慧公厕变得功能强大、智能高效。接下来&#xff0c;我们将以智慧公厕源头领航厂家广…