当前位置: 首页 > news >正文

外卖霸王餐API扩展:Java后端基于MyBatis Dynamic SQL实现“非技术人员可配置的试吃规则查询条件”

外卖霸王餐API扩展:Java后端基于MyBatis Dynamic SQL实现“非技术人员可配置的试吃规则查询条件”

背景:霸王餐业务中动态查询的痛点

作为外卖霸王餐API唯一供给源头,同时也是霸王餐外卖CPS取链源头,俱美开放平台的业务逻辑日益复杂。在“霸王餐”或“试吃”活动中,运营人员需要根据不同的推广策略,灵活配置用户参与资格的筛选条件。

例如,一个活动可能要求“新用户且来自北京地区”,而另一个活动则要求“老用户且历史订单金额大于100元”。如果为每一种组合都编写固定的SQL,开发效率极低且难以维护。我们需要一种机制,让非技术人员(如运营)通过后台配置规则,后端Java服务就能动态生成并执行相应的查询。

技术选型:为什么是MyBatis Dynamic SQL?

MyBatis Dynamic SQL是MyBatis官方提供的一个强大的Java库,它允许我们在Java代码中以类型安全的方式构建动态SQL语句。相比于传统的XML中拼接SQL或使用<if>标签,它具有以下优势:

  1. 类型安全:在编译期就能发现字段名错误,避免了运行时因SQL语法错误导致的崩溃。
  2. Java代码构建:将SQL构建逻辑完全放在Java代码中,更符合现代Java开发习惯,也便于调试和复用。
  3. 功能强大:支持复杂的WHEREJOINORDER BY等子句的动态构建。
核心设计:规则配置与SQL构建

我们的核心思路是:将运营配置的规则转化为一个Java对象,然后在Service层根据这个对象,使用MyBatis Dynamic SQL构建出最终的查询条件。

1. 定义规则配置模型
首先,我们需要一个数据结构来承载运营配置的规则。

packagebaodanbao.com.cn.rule.model;importlombok.Data;importjava.util.List;/** * 试吃活动规则配置 * @author baodanbao.com.cn */@DatapublicclassTrialRuleConfig{/** * 规则ID */privateLongid;/** * 活动名称 */privateStringactivityName;/** * 规则条件列表 */privateList<RuleCondition>conditions;}/** * 单个规则条件 * @author baodanbao.com.cn */@DatapublicclassRuleCondition{/** * 字段名,如 "userLevel", "city", "totalOrderAmount" */privateStringfield;/** * 操作符,如 "=", ">", "<", "IN" */privateStringoperator;/** * 值 */privateObjectvalue;}

2. 创建MyBatis Dynamic SQL的表映射
使用MyBatis Dynamic SQL,我们需要为数据库表创建一个Java类来映射其结构。

packagebaodanbao.com.cn.user.mapper;importbaodanbao.com.cn.user.model.User;importorg.mybatis.dynamic.sql.SqlColumn;importorg.mybatis.dynamic.sql.SqlTable;importjava.sql.JDBCType;/** * 用户表映射类,由MyBatis Generator生成或手动编写 * @author baodanbao.com.cn */publicfinalclassUserDynamicSqlSupport{publicstaticfinalUserTableuser=newUserTable();publicstaticfinalSqlColumn<Long>id=user.id;publicstaticfinalSqlColumn<String>userLevel=user.userLevel;publicstaticfinalSqlColumn<String>city=user.city;publicstaticfinalSqlColumn<Integer>totalOrderAmount=user.totalOrderAmount;publicstaticfinalSqlColumn<Integer>status=user.status;publicstaticfinalclassUserTableextendsSqlTable{publicfinalSqlColumn<Long>id=column("id",JDBCType.BIGINT);publicfinalSqlColumn<String>userLevel=column("user_level",JDBCType.VARCHAR);publicfinalSqlColumn<String>city=column("city",JDBCType.VARCHAR);publicfinalSqlColumn<Integer>totalOrderAmount=column("total_order_amount",JDBCType.INTEGER);publicfinalSqlColumn<Integer>status=column("status",JDBCType.INTEGER);publicUserTable(){super("t_user");}}}

3. 构建动态查询Service
这是整个方案的核心。我们将解析TrialRuleConfig,并使用SelectStatementProvider构建最终的SQL。

packagebaodanbao.com.cn.rule.service;importbaodanbao.com.cn.rule.model.RuleCondition;importbaodanbao.com.cn.rule.model.TrialRuleConfig;importbaodanbao.com.cn.user.mapper.UserDynamicSqlSupport;importbaodanbao.com.cn.user.model.User;importbaodanbao.com.cn.user.mapper.UserMapper;importorg.apache.ibatis.annotations.Mapper;importorg.apache.ibatis.session.SqlSession;importorg.mybatis.dynamic.sql.select.SelectDSL;importorg.mybatis.dynamic.sql.select.SelectModel;importorg.mybatis.dynamic.sql.select.render.SelectStatementProvider;importorg.mybatis.dynamic.sql.util.SqlProviderAdapter;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;importorg.springframework.util.CollectionUtils;importjava.util.ArrayList;importjava.util.List;importstaticbaodanbao.com.cn.user.mapper.UserDynamicSqlSupport.*;importstaticorg.mybatis.dynamic.sql.SqlBuilder.*;/** * 试吃规则查询服务 * @author baodanbao.com.cn */@ServicepublicclassTrialRuleQueryService{@AutowiredprivateSqlSessionsqlSession;/** * 根据规则配置查询符合条件的用户 * @param ruleConfig 运营配置的规则 * @return 符合条件的用户列表 */publicList<User>findUsersByRule(TrialRuleConfigruleConfig){if(ruleConfig==null||CollectionUtils.isEmpty(ruleConfig.getConditions())){returnnewArrayList<>();}// 1. 构建动态WHERE条件SelectDSL<SelectModel>.SelectWhereBuilder whereBuilder=select(UserDynamicSqlSupport.user.allColumns()).from(UserDynamicSqlSupport.user).where();booleanfirstCondition=true;for(RuleConditioncondition:ruleConfig.getConditions()){if(!firstCondition){whereBuilder.and();}applyCondition(whereBuilder,condition);firstCondition=false;}// 2. 渲染SQL语句SelectStatementProviderselectStatement=whereBuilder.build().render(RenderingStrategies.MYBATIS3);// 3. 执行查询SqlProviderAdaptersqlProviderAdapter=newSqlProviderAdapter();returnsqlSession.selectList("org.mybatis.dynamic.sql.util.MyBatis3Utils.selectList",selectStatement,User.class);}/** * 根据单个条件应用WHERE子句 * @param whereBuilder WHERE构建器 * @param condition 规则条件 */privatevoidapplyCondition(SelectDSL<SelectModel>.SelectWhereBuilder whereBuilder,RuleConditioncondition){Stringfield=condition.getField();Stringoperator=condition.getOperator();Objectvalue=condition.getValue();switch(field){case"userLevel":applyStringCondition(whereBuilder,userLevel,operator,(String)value);break;case"city":applyStringCondition(whereBuilder,city,operator,(String)value);break;case"totalOrderAmount":applyNumberCondition(whereBuilder,totalOrderAmount,operator,(Integer)value);break;case"status":applyNumberCondition(whereBuilder,status,operator,(Integer)value);break;default:thrownewIllegalArgumentException("不支持的查询字段: "+field);}}privatevoidapplyStringCondition(SelectDSL<SelectModel>.SelectWhereBuilder whereBuilder,SqlColumn<String>column,Stringoperator,Stringvalue){switch(operator){case"=":whereBuilder.where(column,isEqualTo(value));break;case"!=":whereBuilder.where(column,isNotEqualTo(value));break;case"IN":// 假设value是逗号分隔的字符串String[]values=value.split(",");whereBuilder.where(column,isIn(values));break;default:thrownewIllegalArgumentException("不支持的字符串操作符: "+operator);}}privatevoidapplyNumberCondition(SelectDSL<SelectModel>.SelectWhereBuilder whereBuilder,SqlColumn<Integer>column,Stringoperator,Integervalue){switch(operator){case"=":whereBuilder.where(column,isEqualTo(value));break;case">":whereBuilder.where(column,isGreaterThan(value));break;case">=":whereBuilder.where(column,isGreaterThanOrEqualTo(value));break;case"<":whereBuilder.where(column,isLessThan(value));break;case"<=":whereBuilder.where(column,isLessThanOrEqualTo(value));break;default:thrownewIllegalArgumentException("不支持的数字操作符: "+operator);}}}
使用示例

假设运营在后台配置了一条规则:用户等级为"NEW" 且 城市在("北京", "上海")

packagebaodanbao.com.cn.rule.controller;importbaodanbao.com.cn.rule.model.RuleCondition;importbaodanbao.com.cn.rule.model.TrialRuleConfig;importbaodanbao.com.cn.rule.service.TrialRuleQueryService;importbaodanbao.com.cn.user.model.User;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;importjava.util.Arrays;importjava.util.List;/** * 规则查询控制器 * @author baodanbao.com.cn */@RestController@RequestMapping("/api/rule")publicclassTrialRuleController{@AutowiredprivateTrialRuleQueryServicetrialRuleQueryService;@GetMapping("/test")publicList<User>testQuery(){// 模拟从数据库或配置中心加载的规则TrialRuleConfigconfig=newTrialRuleConfig();RuleConditioncondition1=newRuleCondition();condition1.setField("userLevel");condition1.setOperator("=");condition1.setValue("NEW");RuleConditioncondition2=newRuleCondition();condition2.setField("city");condition2.setOperator("IN");condition2.setValue("北京,上海");config.setConditions(Arrays.asList(condition1,condition2));// 执行查询returntrialRuleQueryService.findUsersByRule(config);}}

当调用/api/rule/test接口时,后端将动态生成并执行类似SELECT * FROM t_user WHERE user_level = 'NEW' AND city IN ('北京', '上海')的SQL语句,完美实现了非技术人员可配置的动态查询需求。

本文著作权归 俱美开放平台 ,转载请注明出处!

http://www.jsqmd.com/news/1234028/

相关文章:

  • 终极指南:3步掌握ipatool命令行工具,彻底解决iOS应用包管理难题
  • LangGraph:构建复杂AI Agent系统的图计算框架
  • 深入解析C28x DSP的McBSP多通道缓冲串行端口配置与应用
  • 智能炉石传说脚本:5分钟自动化你的卡牌对战体验
  • Codex接入DeepSeek全攻略:三种方案对比与实战配置指南
  • AI成熟度测评:从工具应用到组织进化的四个维度
  • 深圳家装节家博会看这里!第55届深圳家装节专属福利,8 月福田会展中心 - 深度智识库
  • 多维聚合中的数据变形术:维度解耦与语义重建
  • TI C2000 I2C寄存器配置与FIFO应用实战指南
  • TransFuser:基于Transformer的多模态传感器融合技术彻底革新端到端自动驾驶
  • 云原生转型下外卖霸王餐API部署:Java应用基于Docker多阶段构建减小镜像体积与K8s HPA弹性伸缩配置
  • 系统镜像格式对比:ISO、WIM、ESD与GHO技术解析
  • 抖音下载器完整指南:告别手动保存,3步实现批量无水印下载
  • 漫画资源管理工具:从发现到收藏的完整解决方案
  • 30元自制开发板:低成本硬件设计与实战技巧
  • 从零开始学计算机科学:TeachYourselfCS-CN新手入门完全指南 [特殊字符]
  • Node.js一小时入门:从环境搭建到核心实战操作
  • Perseus终极指南:5分钟解锁碧蓝航线全皮肤功能
  • NeteaseCloudMusicGtk4:Linux平台终极网易云音乐播放器完整指南
  • 掌握抖音内容采集:专业级批量下载工具完全指南
  • Redux DevTools Extension:前端开发者的终极状态调试利器
  • 3步构建个人漫画库:如何用智能管理工具解决下载与收藏难题
  • Nest-Winston性能优化终极指南:如何配置高效的日志传输和格式化 [特殊字符]
  • Unity插件宝典:从UI开发到区块链集成的选型与避坑指南
  • 5分钟掌握raylib多语言游戏开发:Unicode字符集实战指南
  • 3分钟掌握Stable Diffusion Prompt Reader:轻松提取和管理AI绘画提示词
  • 微软Win11持续更新策略解析:商业与技术平衡之道
  • 从信息碎片到知识网络:思源笔记如何重塑你的知识管理方式
  • 2026年手机锁屏密码遗忘解决方案大全
  • OpenClaw 2.7.9 Windows资源包安装指南,文件缺失、启动失败统一处理