diff --git a/cpte-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisPlusSaasConfig.java b/cpte-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisPlusSaasConfig.java index ac22025..ccd4743 100644 --- a/cpte-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisPlusSaasConfig.java +++ b/cpte-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisPlusSaasConfig.java @@ -5,16 +5,18 @@ import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; -import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor; -import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; -import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; -import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.*; import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils; import lombok.extern.slf4j.Slf4j; import me.zhyd.oauth.log.Log; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.LongValue; import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; import org.jeecg.common.config.TenantContext; import org.jeecg.common.constant.CommonConstant; import org.jeecg.common.constant.TenantConstant; @@ -27,34 +29,37 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; +import java.lang.reflect.Field; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * 单数据源配置(jeecg.datasource.open = false时生效) - * @Author zhoujf * + * @Author zhoujf */ @Slf4j @Configuration -@MapperScan(value={"org.jeecg.modules.**.mapper*", "org.jeecg.**.mapper*","org.cpte.modules.**.mapper*", "org.cpte.**.mapper*"}) +@MapperScan(value = {"org.jeecg.modules.**.mapper*", "org.jeecg.**.mapper*", "org.cpte.modules.**.mapper*", "org.cpte.**.mapper*"}) public class MybatisPlusSaasConfig { @Autowired private DataSource dataSource; - + /** * 是否开启系统模块的租户隔离 - * 控制范围:用户、角色、部门、我的部门、字典、分类字典、多数据源、职务、通知公告 - * - * 实现功能 - * 1.用户表通过硬编码实现租户ID隔离 - * 2.角色、部门、我的部门、字典、分类字典、多数据源、职务、通知公告除了硬编码还加入的 TENANT_TABLE 配置中,实现租户隔离更安全 - * 3.菜单表、租户表不做租户隔离 - * 4.通过拦截器MybatisInterceptor实现,增删改查数据 自动注入租户ID + * 控制范围:用户、角色、部门、我的部门、字典、分类字典、多数据源、职务、通知公告 + *
+ * 实现功能
+ * 1.用户表通过硬编码实现租户ID隔离
+ * 2.角色、部门、我的部门、字典、分类字典、多数据源、职务、通知公告除了硬编码还加入的 TENANT_TABLE 配置中,实现租户隔离更安全
+ * 3.菜单表、租户表不做租户隔离
+ * 4.通过拦截器MybatisInterceptor实现,增删改查数据 自动注入租户ID
*/
public static final Boolean OPEN_SYSTEM_TENANT_CONTROL = false;
-
+
/**
* 哪些表需要做多租户 表需要添加一个字段 tenant_id
*/
@@ -82,6 +87,22 @@ public class MybatisPlusSaasConfig {
TENANT_TABLE.add("airag_knowledge");
TENANT_TABLE.add("airag_knowledge_doc");
TENANT_TABLE.add("airag_model");
+
+ TENANT_TABLE.add("base_area");
+ TENANT_TABLE.add("base_item");
+ TENANT_TABLE.add("base_point");
+ TENANT_TABLE.add("base_stock");
+
+ TENANT_TABLE.add("data_asn");
+ TENANT_TABLE.add("data_asn_detail");
+ TENANT_TABLE.add("data_receive_record");
+ TENANT_TABLE.add("data_pick");
+ TENANT_TABLE.add("data_pick_detail");
+ TENANT_TABLE.add("data_task");
+ TENANT_TABLE.add("data_agv_task");
+ TENANT_TABLE.add("data_inventory");
+ TENANT_TABLE.add("data_inventory_log");
+
}
//2.示例测试
@@ -94,35 +115,36 @@ public class MybatisPlusSaasConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+
// 先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
@Override
public Expression getTenantId() {
String tenantId = TenantContext.getTenant();
//如果通过线程获取租户ID为空,则通过当前请求的request获取租户(shiro排除拦截器的请求会获取不到租户ID)
- if(oConvertUtils.isEmpty(tenantId)){
+ if (oConvertUtils.isEmpty(tenantId)) {
try {
tenantId = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
} catch (Exception e) {
//e.printStackTrace();
}
}
- if(oConvertUtils.isEmpty(tenantId)){
+ if (oConvertUtils.isEmpty(tenantId)) {
tenantId = "0";
}
return new LongValue(tenantId);
}
@Override
- public String getTenantIdColumn(){
+ public String getTenantIdColumn() {
return TenantConstant.TENANT_ID_TABLE;
}
// 返回 true 表示不走租户逻辑
@Override
public boolean ignoreTable(String tableName) {
- for(String temp: TENANT_TABLE){
- if(temp.equalsIgnoreCase(tableName)){
+ for (String temp : TENANT_TABLE) {
+ if (temp.equalsIgnoreCase(tableName)) {
return false;
}
}
@@ -132,38 +154,38 @@ public class MybatisPlusSaasConfig {
//update-begin-author:zyf date:20220425 for:【VUEN-606】注入动态表名适配拦截器解决多表名问题
interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor());
//update-end-author:zyf date:20220425 for:【VUEN-606】注入动态表名适配拦截器解决多表名问题
-
+
//update-begin---author:scott ---date:2025-08-02 for:【issues/8666】升级mybatisPlus后SqlServer分页使用OFFSET ? ROWS FETCH NEXT ? ROWS ONLY,导致online报表报错---
DbType dbType = null;
try {
- dbType = JdbcUtils.getDbType(dataSource.getConnection().getMetaData().getURL());
- log.info("当前数据库类型: {}", dbType);
+ dbType = JdbcUtils.getDbType(dataSource.getConnection().getMetaData().getURL());
+ log.info("当前数据库类型: {}", dbType);
} catch (SQLException e) {
Log.error(e.getMessage(), e);
}
- if (dbType!=null && (dbType == DbType.SQL_SERVER || dbType == DbType.SQL_SERVER2005)) {
+ if (dbType != null && (dbType == DbType.SQL_SERVER || dbType == DbType.SQL_SERVER2005)) {
// 如果是SQL Server则覆盖为2005分页方式
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.SQL_SERVER2005));
} else {
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
}
//update-end---author:scott ---date::2025-08-02 for:【issues/8666】升级mybatisPlus后SqlServer分页使用OFFSET ? ROWS FETCH NEXT ? ROWS ONLY,导致online报表报错---
-
+
//【jeecg-boot/issues/3847】增加@Version乐观锁支持
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
+
+ interceptor.addInnerInterceptor(fixForUpdateOrderInterceptor());
return interceptor;
}
/**
* 动态表名切换拦截器,用于适配vue2和vue3同一个表有多个的情况,如sys_role_index在vue3情况下表名为sys_role_index_v3
+ *
* @return
*/
private DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor() {
DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
dynamicTableNameInnerInterceptor.setTableNameHandler((sql, tableName) -> {
- /* if (sql.contains("FOR UPDATE")) {
- return tableName;
- }*/
//获取需要动态解析的表名
String dynamicTableName = ThreadLocalDataHelper.get(CommonConstant.DYNAMIC_TABLE_NAME);
//当dynamicTableName不为空时才走动态表名处理逻辑,否则返回原始表名
@@ -180,6 +202,66 @@ public class MybatisPlusSaasConfig {
return dynamicTableNameInnerInterceptor;
}
+ private InnerInterceptor fixForUpdateOrderInterceptor() {
+ return new InnerInterceptor() {
+ @Override
+ public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds,
+ ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
+ String originalSql = boundSql.getSql();
+ String upperSql = originalSql.toUpperCase();
+ if (upperSql.contains("FOR UPDATE") && upperSql.contains("ORDER BY")) {
+ // 检查FOR UPDATE是否在ORDER BY之后
+ String fixedSql = moveForUpdateToEnd(upperSql);
+ try {
+ Field sqlField = BoundSql.class.getDeclaredField("sql");
+ sqlField.setAccessible(true);
+ sqlField.set(boundSql, fixedSql);
+ } catch (Exception e) {
+ log.warn("Failed to fix FOR UPDATE order in SQL: {}", e.getMessage());
+ }
+ }
+ }
+
+ public static String moveForUpdateToEnd(String originalSql) {
+ if (originalSql == null || originalSql.trim().isEmpty()) {
+ return originalSql;
+ }
+
+ // 1. 将 SQL 转换为大写,以简化匹配过程,但保留原始大小写用于最终输出(通过捕获组实现)。
+ // 同时在处理前和处理后去除多余的首尾空格。
+ String sql = originalSql.trim();
+
+ // 2. 正则表达式模式解释:
+ // - (.*?):捕获语句开头到 FOR UPDATE 之前的任何内容 (第1组:SELECT/FROM/WHERE 部分)。
+ // - (\sFOR\sUPDATE\s*):捕获 'FOR UPDATE' (第2组:FOR UPDATE)。
+ // - (\sORDER\sBY\s.*):捕获 'ORDER BY' 及其之后的所有内容 (第3组:ORDER BY 部分)。
+ // - $:确保匹配到字符串末尾。
+ // - 关键字之间使用 \s 匹配空白字符,防止误匹配。
+ // - 使用 (?i) 开启不区分大小写匹配。
+ //
+ // 完整的原始结构: (SELECT/FROM/WHERE...) (FOR UPDATE) (ORDER BY...)
+ // 期望的替换结构: (SELECT/FROM/WHERE...) (ORDER BY...) (FOR UPDATE)
+
+ // 模式: 捕获 (SELECT/FROM/WHERE) + (FOR UPDATE) + (ORDER BY ...)
+ String regex = "(?is)(.*?)(\\sFOR\\sUPDATE\\s*)(\\sORDER\\sBY.*)";
+
+ // 替换表达式: $1 是 SELECT/FROM/WHERE 部分, $3 是 ORDER BY 部分, $2 是 FOR UPDATE 部分
+ String replacement = "$1$3$2";
+
+ Pattern pattern = Pattern.compile(regex);
+ Matcher matcher = pattern.matcher(sql);
+
+ if (matcher.find()) {
+ // 如果找到匹配,则进行替换
+ return matcher.replaceAll(replacement);
+ } else {
+ // 如果没有匹配到包含 FOR UPDATE 和 ORDER BY 的特定顺序,则返回原始语句
+ return originalSql;
+ }
+ }
+ };
+ }
+
// /**
// * 下个版本会删除,现在为了避免缓存出现问题不得不配置
// * @return
diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/constant/enums/AsnStatusEnum.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/constant/enums/AsnStatusEnum.java
index d504a0a..8e41c76 100644
--- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/constant/enums/AsnStatusEnum.java
+++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/constant/enums/AsnStatusEnum.java
@@ -16,7 +16,9 @@ public enum AsnStatusEnum {
RECEIVED(3, "收货完成"),
- CANCELED(4, "已取消"),
+ CLOSED(4, "已关闭"),
+
+ CANCELED(5, "已取消"),
;
diff --git a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/conveyorLine/controller/ConveyorLineController.java b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/conveyorLine/controller/ConveyorLineController.java
index c0fd00e..e5091bf 100644
--- a/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/conveyorLine/controller/ConveyorLineController.java
+++ b/cpte-boot-module/cpte-module-wms/src/main/java/org/cpte/modules/conveyorLine/controller/ConveyorLineController.java
@@ -32,10 +32,18 @@ public class ConveyorLineController {
@AutoLog(value = "输送线扫描")
@Operation(summary = "输送线-扫描托盘")
@PostMapping(value = "/scanTray")
- @IgnoreAuth
public Result