From 7f72c75b43d7b4fa9838d9a93974fefc1933cf9b Mon Sep 17 00:00:00 2001 From: wangfs <15029758498@163.com> Date: Sat, 29 Nov 2025 15:15:04 +0800 Subject: [PATCH] 提交处理sql自动拼接业务线逻辑 --- urbanops-framework/urbanops-spring-boot-starter-mybatis/src/main/java/com/zteits/urbanops/framework/mybatis/config/BusinessLineSqlInterceptor.java | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ urbanops-framework/urbanops-spring-boot-starter-mybatis/src/main/java/com/zteits/urbanops/framework/mybatis/config/UrbanopsMybatisAutoConfiguration.java | 9 +++++++++ urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/inspectionplan/InspectionPlanInterceptMapper.java | 34 ++++++++++++++++++++++++++++++++++ urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/inspectionplan/InspectionPlanMapper.java | 2 +- urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/inspectionplan/InspectionPlanServiceImpl.java | 6 +++++- 5 files changed, 249 insertions(+), 2 deletions(-) create mode 100644 urbanops-framework/urbanops-spring-boot-starter-mybatis/src/main/java/com/zteits/urbanops/framework/mybatis/config/BusinessLineSqlInterceptor.java create mode 100644 urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/inspectionplan/InspectionPlanInterceptMapper.java diff --git a/urbanops-framework/urbanops-spring-boot-starter-mybatis/src/main/java/com/zteits/urbanops/framework/mybatis/config/BusinessLineSqlInterceptor.java b/urbanops-framework/urbanops-spring-boot-starter-mybatis/src/main/java/com/zteits/urbanops/framework/mybatis/config/BusinessLineSqlInterceptor.java new file mode 100644 index 0000000..d090263 --- /dev/null +++ b/urbanops-framework/urbanops-spring-boot-starter-mybatis/src/main/java/com/zteits/urbanops/framework/mybatis/config/BusinessLineSqlInterceptor.java @@ -0,0 +1,200 @@ +package com.zteits.urbanops.framework.mybatis.config; + +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.zteits.urbanops.framework.annotation.ProcessBusinessLine; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.cache.CacheKey; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlCommandType; +import org.apache.ibatis.plugin.*; +import org.apache.ibatis.reflection.MetaObject; +import org.apache.ibatis.reflection.SystemMetaObject; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * MyBatis 插件:拦截 SQL,自动拼接业务线条件 + */ +@Slf4j +@Component +//@Intercepts({ +// @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}) +// //@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}) +//}) +// 关键:同时拦截两个query重载方法 +@Intercepts({ + // 重载1:基础query(你原有的签名) + @Signature( + type = Executor.class, + method = "query", + args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class} + ), + // 重载2:带CacheKey的query(MP/分页常用) + @Signature( + type = Executor.class, + method = "query", + args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class} + ) +}) +public class BusinessLineSqlInterceptor implements Interceptor { + /** + * 缓存:Mapper方法全限定名 → 注解(避免重复反射,提升性能) + */ + private static final java.util.Map ANNOTATION_CACHE = new java.util.concurrent.ConcurrentHashMap<>(); + + @Override + public Object intercept(Invocation invocation) throws Throwable { + // 1. 跳过过滤则直接执行原 SQL + if (!isSkipAnnotation(invocation)) { + return invocation.proceed(); + } + + // 1. 解析拦截参数 + Object[] args = invocation.getArgs(); + MappedStatement ms = (MappedStatement) args[0]; + Object parameter = args[1]; + BoundSql boundSql = null; + + // 2. 仅处理 SELECT 类型的 SQL + if (ms.getSqlCommandType() != SqlCommandType.SELECT) { + return invocation.proceed(); + } + + // 3. 适配两种重载方法,获取 BoundSql + if (args.length == 4) { + boundSql = ms.getBoundSql(parameter); + } else if (args.length == 6) { + boundSql = (BoundSql) args[5]; + } + if (boundSql == null) { + return invocation.proceed(); + } + String originalSql = boundSql.getSql().trim(); + SqlCommandType sqlCommandType = ms.getSqlCommandType(); + // 6. 替换 BoundSql 中的 SQL 语句(关键:反射修改) + MetaObject metaBoundSql = SystemMetaObject.forObject(boundSql); + + // 3. 仅处理 SELECT/UPDATE/DELETE(INSERT 由自动填充处理) + if (SqlCommandType.SELECT.equals(sqlCommandType)) { + Set businessLineSet = new HashSet<>();//UserContextHolder.getBusinessLine(); + businessLineSet.add("yl"); + if (!CollectionUtils.isEmpty(businessLineSet)) { + // 拼接业务线条件 + String newSql = buildSqlWithBusinessLine(originalSql, businessLineSet, sqlCommandType); + log.debug("原始 SQL:{},拼接后 SQL:{}", originalSql, newSql); + // 替换原始 SQL + metaBoundSql.setValue("sql", newSql); + } + } + + // 4. 执行原方法 + return invocation.proceed(); + } + + /** + * 拼接业务线条件到 SQL 中 + */ + private String buildSqlWithBusinessLine(String originalSql, Set businessLineSet, SqlCommandType sqlCommandType) { + String upperSql = originalSql.toUpperCase(); + // 核心转换:每个元素加单引号,逗号分隔 + String result = businessLineSet.stream() + .map(s -> "'" + s + "'") // 为每个元素包裹单引号 + .collect(Collectors.joining(",")); // 用逗号拼接所有元素 + String condition = "busi_line in( " + result + ")"; + + // SELECT 语句:处理 WHERE/ORDER BY/GROUP BY 等 + if (SqlCommandType.SELECT.equals(sqlCommandType)) { + int whereIndex = upperSql.indexOf("WHERE"); + int orderIndex = upperSql.indexOf("ORDER BY"); + int groupIndex = upperSql.indexOf("GROUP BY"); + int havingIndex = upperSql.indexOf("HAVING"); + int insertIndex = -1; + if (whereIndex > 0) { + insertIndex = whereIndex + 5; // WHERE 后开始拼接 + } else { + // 无 WHERE,找 ORDER/GROUP/HAVING 的位置,在其前加 WHERE + insertIndex = originalSql.length(); + if (orderIndex > 0) insertIndex = orderIndex; + else if (groupIndex > 0) insertIndex = groupIndex; + else if (havingIndex > 0) insertIndex = havingIndex; + } + + // 拼接条件 + String prefix = whereIndex > 0 ? " " +condition +" AND" : " WHERE "+ condition; + return new StringBuilder(originalSql) + .insert(insertIndex, prefix) + .toString(); + } + + // UPDATE/DELETE 语句:同理拼接 WHERE 条件 + if (SqlCommandType.UPDATE.equals(sqlCommandType) || SqlCommandType.DELETE.equals(sqlCommandType)) { + int whereIndex = upperSql.indexOf("WHERE"); + if (whereIndex > 0) { + return new StringBuilder(originalSql) + .insert(whereIndex + 5, " AND " + condition) + .toString(); + } else { + return originalSql + " WHERE " + condition; + } + } + + return originalSql; + } + + /** + * 插件代理(固定写法) + */ + @Override + public Object plugin(Object target) { + return Plugin.wrap(target, this); + } + + /** + * 设置插件属性(无需配置则空实现) + */ + @Override + public void setProperties(Properties properties) { + } + + /** + * 判断massper 方式上是否存在注解 @SkipBusinessLine + * 有注解则拼接业务线;没有则不拼接 + * @param invocation 对象 + * @return true:拼接业务线;false不拼接业务线 + */ + public boolean isSkipAnnotation(Invocation invocation){ + // 1. 获取MappedStatement(MP封装的Mapper方法信息) + MappedStatement ms = (MappedStatement) invocation.getArgs()[0]; + String mapperMethodId = ms.getId(); // 格式:com.xxx.mapper.UserMapper.selectByAge + // 2. 解析Mapper全类名和方法名 + String[] idParts = mapperMethodId.split("\\."); + String methodName = idParts[idParts.length - 1]; // 方法名:selectByAge + String mapperClassName = mapperMethodId.substring(0, mapperMethodId.lastIndexOf(".")); + Class mapperClass = null; // 加载Mapper接口 + try { + mapperClass = Class.forName(mapperClassName); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + ProcessBusinessLine skipAnnotation = ANNOTATION_CACHE.get(mapperClassName+methodName); + if (skipAnnotation == null) { + if(mapperClass.isAnnotationPresent(ProcessBusinessLine.class)){ + ProcessBusinessLine processBusinessLine = mapperClass.getAnnotation(ProcessBusinessLine.class); + ANNOTATION_CACHE.put(mapperClassName+methodName, processBusinessLine); + return true; + } + } + + return false; + } + +} \ No newline at end of file diff --git a/urbanops-framework/urbanops-spring-boot-starter-mybatis/src/main/java/com/zteits/urbanops/framework/mybatis/config/UrbanopsMybatisAutoConfiguration.java b/urbanops-framework/urbanops-spring-boot-starter-mybatis/src/main/java/com/zteits/urbanops/framework/mybatis/config/UrbanopsMybatisAutoConfiguration.java index 0a1ed67..bf22588 100644 --- a/urbanops-framework/urbanops-spring-boot-starter-mybatis/src/main/java/com/zteits/urbanops/framework/mybatis/config/UrbanopsMybatisAutoConfiguration.java +++ b/urbanops-framework/urbanops-spring-boot-starter-mybatis/src/main/java/com/zteits/urbanops/framework/mybatis/config/UrbanopsMybatisAutoConfiguration.java @@ -16,6 +16,7 @@ import com.baomidou.mybatisplus.extension.parser.cache.JdkSerialCaffeineJsqlPars import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.Resource; import org.apache.ibatis.annotations.Mapper; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -36,6 +37,14 @@ import java.util.concurrent.TimeUnit; lazyInitialization = "${mybatis.lazy-initialization:false}") // Mapper 懒加载,目前仅用于单元测试 public class UrbanopsMybatisAutoConfiguration { + /** + * 注册自定义业务线 SQL 拦截器 + */ + @Bean + public BusinessLineSqlInterceptor businessLineSqlInterceptor() { + return new BusinessLineSqlInterceptor(); + } + static { // 动态 SQL 智能优化支持本地缓存加速解析,更完善的租户复杂 XML 动态 SQL 支持,静态注入缓存 JsqlParserGlobal.setJsqlParseCache(new JdkSerialCaffeineJsqlParseCache( diff --git a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/inspectionplan/InspectionPlanInterceptMapper.java b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/inspectionplan/InspectionPlanInterceptMapper.java new file mode 100644 index 0000000..777cdcc --- /dev/null +++ b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/inspectionplan/InspectionPlanInterceptMapper.java @@ -0,0 +1,34 @@ +package com.zteits.urbanops.module.garden.dal.mysql.inspectionplan; + + +import com.zteits.urbanops.framework.annotation.ProcessBusinessLine; +import com.zteits.urbanops.framework.common.pojo.PageResult; +import com.zteits.urbanops.framework.mybatis.core.mapper.BaseMapperX; +import com.zteits.urbanops.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.zteits.urbanops.module.garden.controller.admin.inspectionplan.vo.InspectionPlanPageReqVO; +import com.zteits.urbanops.module.garden.dal.dataobject.inspectionplan.InspectionPlanDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 巡检计划汇总 Mapper + * + * @author 超级管理员 + */ +@ProcessBusinessLine +@Mapper +public interface InspectionPlanInterceptMapper extends BaseMapperX { + default PageResult selectPage(InspectionPlanPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(InspectionPlanDO::getBatchNo, reqVO.getBatchNo()) + .likeIfPresent(InspectionPlanDO::getPlanName, reqVO.getPlanName()) + .eqIfPresent(InspectionPlanDO::getPlanAttr, reqVO.getPlanAttr()) + .eqIfPresent(InspectionPlanDO::getPlanTypeId, reqVO.getPlanTypeId()) + .eqIfPresent(InspectionPlanDO::getCompanyId, reqVO.getCompanyId()) + .likeIfPresent(InspectionPlanDO::getRoadName, reqVO.getRoadName()) + .eqIfPresent(InspectionPlanDO::getLevelId, reqVO.getLevelId()) + .betweenIfPresent(InspectionPlanDO::getBeginTime, reqVO.getBeginTime()) + .betweenIfPresent(InspectionPlanDO::getEndTime, reqVO.getEndTime()) + .orderByDesc(InspectionPlanDO::getId)); + } + +} \ No newline at end of file diff --git a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/inspectionplan/InspectionPlanMapper.java b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/inspectionplan/InspectionPlanMapper.java index 12e7f36..3c0c46d 100644 --- a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/inspectionplan/InspectionPlanMapper.java +++ b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/inspectionplan/InspectionPlanMapper.java @@ -1,6 +1,7 @@ package com.zteits.urbanops.module.garden.dal.mysql.inspectionplan; +import com.zteits.urbanops.framework.annotation.ProcessBusinessLine; import com.zteits.urbanops.framework.common.pojo.PageResult; import com.zteits.urbanops.framework.mybatis.core.query.LambdaQueryWrapperX; import com.zteits.urbanops.framework.mybatis.core.mapper.BaseMapperX; @@ -16,7 +17,6 @@ import org.apache.ibatis.annotations.Param; */ @Mapper public interface InspectionPlanMapper extends BaseMapperX { - default PageResult selectPage(InspectionPlanPageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX() .eqIfPresent(InspectionPlanDO::getBatchNo, reqVO.getBatchNo()) diff --git a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/inspectionplan/InspectionPlanServiceImpl.java b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/inspectionplan/InspectionPlanServiceImpl.java index dff9775..9e6b63d 100644 --- a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/inspectionplan/InspectionPlanServiceImpl.java +++ b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/inspectionplan/InspectionPlanServiceImpl.java @@ -10,6 +10,7 @@ import com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils; import com.zteits.urbanops.module.garden.dal.dataobject.inspectionplan.InspectionPlanDetailDO; import com.zteits.urbanops.module.garden.dal.dataobject.inspectionplan.InspectionPlanRoleDO; import com.zteits.urbanops.module.garden.dal.mysql.inspectionplan.InspectionPlanDetailMapper; +import com.zteits.urbanops.module.garden.dal.mysql.inspectionplan.InspectionPlanInterceptMapper; import com.zteits.urbanops.module.garden.dal.mysql.inspectionplan.InspectionPlanRoleMapper; import com.zteits.urbanops.module.garden.enums.FinishStateEnum; import com.zteits.urbanops.module.garden.enums.PlanConstants; @@ -51,6 +52,9 @@ public class InspectionPlanServiceImpl implements InspectionPlanService { private InspectionPlanMapper inspectionPlanMapper; @Resource + private InspectionPlanInterceptMapper inspectionPlanInterceptMapper; + + @Resource private InspectionPlanDetailMapper inspectionPlanDetailMapper; @Resource @@ -218,7 +222,7 @@ public class InspectionPlanServiceImpl implements InspectionPlanService { @Override public PageResult getInspectionPlanPage(InspectionPlanPageReqVO pageReqVO) { - return inspectionPlanMapper.selectPage(pageReqVO); + return inspectionPlanInterceptMapper.selectPage(pageReqVO); } @Override -- libgit2 0.21.4