Kaynağa Gözat

修改租户业务逻辑

1811872455@163.com 3 hafta önce
ebeveyn
işleme
60b43a7262

+ 11 - 5
java/storlead-api/src/main/resources/application.yml

@@ -52,7 +52,7 @@ spring:
 # 站内消息 / 邮件模板跳转链接前缀(含协议,末尾不要 /)
 domainname: http://localhost:10010/router/rest
 
-# 多租户:无 tenant_id 列的表须列入忽略,否则 SQL 会拼 tenant_id 导致 Unknown column
+# 多租户:默认所有表不注入 tenant_id;enabled=true 且表在 include-tables 中才做租户隔离
 storlead:
   es:
     # false:使用 Noop 实现,不连接 ES(本地无 ES 时请保持 false)
@@ -63,10 +63,16 @@ storlead:
     username: elastic
     password: ""
   tenant:
-    ignore-tables:
-      - system_wechat_config
-      - system_oss_config
-      - sys_app
+    # 总开关:false 时不拼接 tenant_id、不自动填充 tenant_id
+    enabled: false
+    column-name: tenant_id
+    # 需要租户隔离的表白名单(仅 listed 表会加 tenant_id 条件与插入填充)
+    include-tables: []
+    # 示例(按业务启用后取消注释并设 enabled: true):
+    # include-tables:
+    #   - customer
+    #   - tenant_user
+    #   - tenant_enterprise
   datasource:
     # 子模块默认数据源(未标注 @DS 时生效;标注 @DS 则以注解为准)
     module-default-enabled: true

+ 31 - 30
java/storlead-framework/storlead-mybatis/src/main/java/com/storlead/framework/mybatis/handler/MyMetaObjectHandler.java

@@ -1,44 +1,41 @@
 package com.storlead.framework.mybatis.handler;
 
 import cn.hutool.core.date.DateTime;
+import com.baomidou.mybatisplus.annotation.TableName;
 import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
 import com.storlead.framework.auth.vo.LoginUser;
 import com.storlead.framework.common.constant.UserCacheKeyConstants;
 import com.storlead.framework.core.context.Context;
 import com.storlead.framework.core.tenant.TenantContext;
+import com.storlead.framework.mybatis.tenant.TenantProperties;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.ibatis.reflection.MetaObject;
 
 import java.util.Objects;
 
 /**
- * @program: sp-sales
- * @description:
- * @author: chenkq
- * @create: 2022-07-06 15:03
+ * 公共字段自动填充;tenant_id 仅对白名单表且实体含 tenantId 字段时写入。
  */
 @Slf4j
 public class MyMetaObjectHandler implements MetaObjectHandler {
 
-    /**
-     * 自动更新
-     * 需要  @TableField(fill = FieldFill.INSERT) 否则无效
-     *
-     * @param metaObject 要处理的对象
-     * @author chenkq
-     * @date 2022-2-3 12:25
-     */
+    private final TenantProperties tenantProperties;
+
+    public MyMetaObjectHandler(TenantProperties tenantProperties) {
+        this.tenantProperties = tenantProperties;
+    }
+
     @Override
     public void insertFill(MetaObject metaObject) {
         Object createTime = getFieldValByName("createTime", metaObject);
         if (Objects.isNull(createTime)) {
-            DateTime time= new DateTime();
-            this.setInsertFieldValByName("createTime",time,metaObject);
+            DateTime time = new DateTime();
+            this.setInsertFieldValByName("createTime", time, metaObject);
         }
         Object updateTime = getFieldValByName("updateTime", metaObject);
         if (Objects.isNull(updateTime)) {
-            DateTime time= new DateTime();
-            this.setUpdateFieldValByName( "updateTime",time, metaObject);
+            DateTime time = new DateTime();
+            this.setUpdateFieldValByName("updateTime", time, metaObject);
         }
         if (Objects.isNull(Context.getContext())) {
             return;
@@ -47,7 +44,7 @@ public class MyMetaObjectHandler implements MetaObjectHandler {
         Object createBy = getFieldValByName("createBy", metaObject);
         if (Objects.isNull(createBy)) {
             if (Objects.nonNull(loginUser)) {
-                this.setInsertFieldValByName("createBy",loginUser.getId(),metaObject);
+                this.setInsertFieldValByName("createBy", loginUser.getId(), metaObject);
             }
         }
         Object ownerBy = getFieldValByName("ownerBy", metaObject);
@@ -56,7 +53,7 @@ public class MyMetaObjectHandler implements MetaObjectHandler {
                 this.setInsertFieldValByName("ownerBy", loginUser.getId(), metaObject);
             }
         }
-        if (metaObject.hasSetter("tenantId")) {
+        if (metaObject.hasSetter("tenantId") && shouldFillTenantId(metaObject)) {
             Object tenantIdVal = getFieldValByName("tenantId", metaObject);
             if (Objects.isNull(tenantIdVal)) {
                 Long tid = TenantContext.getTenantId();
@@ -68,23 +65,15 @@ public class MyMetaObjectHandler implements MetaObjectHandler {
         Object updateBy = getFieldValByName("updateBy", metaObject);
         if (Objects.isNull(updateBy)) {
             if (Objects.nonNull(loginUser)) {
-                this.setInsertFieldValByName("updateBy",loginUser.getId(),metaObject);
+                this.setInsertFieldValByName("updateBy", loginUser.getId(), metaObject);
             }
         }
     }
 
-    /**
-     * 自动更新
-     * 需要  @TableField(fill = FieldFill.UPDATE) 否则无效
-     *
-     * @param metaObject 要处理的对象
-     * @author chenkq
-     * @date 2022-2-3 12:25
-     */
     @Override
     public void updateFill(MetaObject metaObject) {
-        DateTime time= new DateTime();
-        this.setUpdateFieldValByName( "updateTime",time, metaObject);
+        DateTime time = new DateTime();
+        this.setUpdateFieldValByName("updateTime", time, metaObject);
         if (Objects.isNull(Context.getContext())) {
             return;
         }
@@ -92,8 +81,20 @@ public class MyMetaObjectHandler implements MetaObjectHandler {
         LoginUser loginUser = Context.getContext().getAttribute(UserCacheKeyConstants.LOGIN_USER_INFO_KEY, LoginUser.class);
         if (Objects.isNull(updateBy)) {
             if (Objects.nonNull(loginUser)) {
-                this.setInsertFieldValByName("updateBy",loginUser.getId(),metaObject);
+                this.setInsertFieldValByName("updateBy", loginUser.getId(), metaObject);
             }
         }
     }
+
+    private boolean shouldFillTenantId(MetaObject metaObject) {
+        Object original = metaObject.getOriginalObject();
+        if (original == null) {
+            return false;
+        }
+        TableName tableName = original.getClass().getAnnotation(TableName.class);
+        if (tableName == null || tableName.value().isEmpty()) {
+            return false;
+        }
+        return tenantProperties.shouldApplyTenantLine(tableName.value());
+    }
 }

+ 3 - 2
java/storlead-framework/storlead-mybatis/src/main/java/com/storlead/framework/mybatis/interceptor/MybatisPlusConfig.java

@@ -69,7 +69,8 @@ public class MybatisPlusConfig {
 
     @Primary
     @Bean
-    public MybatisSqlSessionFactoryBean sqlSessionFactory(DataSource dataSource, ApplicationContext appContext, PaginationInterceptor paginationInterceptor) throws Exception {
+    public MybatisSqlSessionFactoryBean sqlSessionFactory(DataSource dataSource, ApplicationContext appContext,
+            PaginationInterceptor paginationInterceptor, TenantProperties tenantProperties) throws Exception {
 
         log.error("sqlSessionFactory--先走");
 
@@ -90,7 +91,7 @@ public class MybatisPlusConfig {
 //        mybatisPlus.setTypeAliasesPackage("com.storlead..modules.*.pojo");
 //        mybatisPlus.setTypeEnumsPackage("com.storlead.sa.modules.console.enums,com.storlead.tems.modules.perform.enums");
         GlobalConfig globalConfig = new GlobalConfig();
-        globalConfig.setMetaObjectHandler(new MyMetaObjectHandler());
+        globalConfig.setMetaObjectHandler(new MyMetaObjectHandler(tenantProperties));
         mybatisPlus.setGlobalConfig(globalConfig);
 
         return mybatisPlus;

+ 4 - 16
java/storlead-framework/storlead-mybatis/src/main/java/com/storlead/framework/mybatis/tenant/StorleadTenantHandler.java

@@ -5,10 +5,11 @@ import com.storlead.framework.core.tenant.TenantContext;
 import net.sf.jsqlparser.expression.Expression;
 import net.sf.jsqlparser.expression.LongValue;
 
-import java.util.Locale;
-
 /**
  * MyBatis-Plus 3.1.x {@link com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser} 使用的租户解析器。
+ * <p>
+ * {@link TenantHandler#doTableFilter(String)} 返回 true 表示跳过租户条件;仅白名单表返回 false。
+ * </p>
  */
 public class StorleadTenantHandler implements TenantHandler {
 
@@ -37,19 +38,6 @@ public class StorleadTenantHandler implements TenantHandler {
 
     @Override
     public boolean doTableFilter(String tableName) {
-        if (tableName == null) {
-            return true;
-        }
-        String normalized = tableName.replace("`", "").toLowerCase(Locale.ROOT);
-        for (String raw : properties.getIgnoreTables()) {
-            if (raw == null) {
-                continue;
-            }
-            String t = raw.replace("`", "").toLowerCase(Locale.ROOT);
-            if (t.equals(normalized)) {
-                return true;
-            }
-        }
-        return false;
+        return !properties.shouldApplyTenantLine(tableName);
     }
 }

+ 45 - 2
java/storlead-framework/storlead-mybatis/src/main/java/com/storlead/framework/mybatis/tenant/TenantProperties.java

@@ -5,21 +5,64 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 
 /**
- * 多租户行拦截配置:忽略无 tenant_id 列的表等。
+ * 多租户行拦截配置。
+ * <p>
+ * 默认不对任何表注入 {@code tenant_id};仅 {@link #includeTables} 中配置的表在 {@link #enabled}=true 时参与租户隔离。
+ * </p>
  */
 @Data
 @ConfigurationProperties(prefix = "storlead.tenant")
 public class TenantProperties {
 
+    /**
+     * 是否开启多租户 SQL 行拦截与插入填充(false 时所有表均不处理 tenant_id)。
+     */
+    private boolean enabled = false;
+
     /**
      * 数据库列名,需与实体字段映射一致(默认 tenant_id)。
      */
     private String columnName = "tenant_id";
 
     /**
-     * 不参与租户条件注入的表名(小写,不含库名)。
+     * 需要租户隔离的表白名单(小写表名,不含库名)。未列入的表不会拼接 tenant_id 条件。
+     */
+    private List<String> includeTables = new ArrayList<>();
+
+    /**
+     * @deprecated 已改为 {@link #includeTables} 白名单模式;仅当 includeTables 为空且本列表非空时,仍按旧黑名单逻辑兼容。
      */
+    @Deprecated
     private List<String> ignoreTables = new ArrayList<>();
+
+    /**
+     * 当前表是否应注入 / 填充 tenant_id。
+     */
+    public boolean shouldApplyTenantLine(String tableName) {
+        if (!enabled || tableName == null) {
+            return false;
+        }
+        String normalized = normalizeTableName(tableName);
+        if (!includeTables.isEmpty()) {
+            return includeTables.stream().anyMatch(t -> tableNameEquals(normalized, t));
+        }
+        if (!ignoreTables.isEmpty()) {
+            return ignoreTables.stream().noneMatch(t -> tableNameEquals(normalized, t));
+        }
+        return false;
+    }
+
+    public static String normalizeTableName(String tableName) {
+        return tableName.replace("`", "").toLowerCase(Locale.ROOT);
+    }
+
+    private static boolean tableNameEquals(String normalized, String configured) {
+        if (configured == null) {
+            return false;
+        }
+        return normalized.equals(normalizeTableName(configured));
+    }
 }

+ 6 - 0
java/storlead-system/storlead-system-api/src/main/java/com/storlead/system/controller/AgentPromptTemplateApiController.java

@@ -32,6 +32,12 @@ public class AgentPromptTemplateApiController {
         return agentPromptTemplateService.saveTemplate(body);
     }
 
+    @ApiOperation("获取提示词(按 templateCode)")
+    @PostMapping("/getAiPrompt")
+    public Result<?> getAiPrompt(@RequestBody AgentPromptTemplateQueryDTO param) {
+        return agentPromptTemplateService.getAiPrompt(param);
+    }
+
     @ApiOperation("分页查询")
     @PostMapping("/pagelist")
     public Result<?> pagelist(@RequestBody AgentPromptTemplatePageDTO dto) {

+ 19 - 0
java/storlead-system/storlead-system-biz/src/main/java/com/storlead/system/service/impl/AgentPromptTemplateServiceImpl.java

@@ -72,6 +72,25 @@ public class AgentPromptTemplateServiceImpl
         return getOne(w);
     }
 
+    @Override
+    public Result<?> getAiPrompt(AgentPromptTemplateQueryDTO dto) {
+        if (dto == null || StrUtil.isBlank(dto.getTemplateCode())) {
+            return Result.error("参数错误");
+        }
+        LambdaQueryWrapper<AgentPromptTemplateEntity> w = new LambdaQueryWrapper<>();
+        w.eq(AgentPromptTemplateEntity::getIsDelete, CommonConstant.DEL_FLAG_0);
+        w.eq(AgentPromptTemplateEntity::getTemplateCode, dto.getTemplateCode().trim());
+        if (dto.getAppId() != null) {
+            w.eq(AgentPromptTemplateEntity::getAppId, dto.getAppId());
+        }
+        w.last(" limit 1");
+        AgentPromptTemplateEntity entity = getOne(w);
+        if (entity == null) {
+            return Result.error("参数错误,未找到对应的AI提示词,请联系管理员进行配置!");
+        }
+        return Result.result(entity);
+    }
+
     @Override
     public Result<?> saveTemplate(AgentPromptTemplateEntity body) {
         if (body == null || StrUtil.isBlank(body.getTemplateCode())) {

+ 5 - 0
java/storlead-system/storlead-system-spi/src/main/java/com/storlead/system/service/AgentPromptTemplateService.java

@@ -22,6 +22,11 @@ public interface AgentPromptTemplateService extends IService<AgentPromptTemplate
 
     AgentPromptTemplateEntity getByTemplateCode(String templateCode, Long appId);
 
+    /**
+     * 按 templateCode 获取模板(未删除,不限启用状态)
+     */
+    Result<?> getAiPrompt(AgentPromptTemplateQueryDTO dto);
+
     Result<?> saveTemplate(AgentPromptTemplateEntity body);
 
     Result<?> enableTemplate(Long id, Boolean enabled);