Răsfoiți Sursa

fix(server): 治理配置与人员批量写入改绑案件数据源

- saveGovernCon 的 saveOrUpdateBatch 在 service 层就开启事务并绕过 mapper 代理,@DS("slave") 来不及生效,查询落到主库 PG 报 govern_conf 不存在
- GovernConfService 新增 saveGovernConfBatch,以 @DS + @Transactional 提前绑定案件 DuckDB,DmController 改走该方法
- flushPerson / personOrder 同样存在批量写绕过 mapper 代理的问题,补上 @DS + 事务,避免人员数据静默丢失
cc 6 zile în urmă
părinte
comite
8f7e30b2dd

+ 2 - 1
ai-server/src/main/java/com/zsjz/ai/module/dm/controller/DmController.java

@@ -172,7 +172,8 @@ public class DmController {
 
     @PostMapping("/saveGovernCon")
     public Result<Void> saveOrUpdate(@RequestBody List<GovernConf> governConf) {
-        governConfService.saveOrUpdateBatch(governConf);
+        // 走 service 中显式绑定案件库的方法:saveOrUpdateBatch 的事务必须先拿到 slave 连接
+        governConfService.saveGovernConfBatch(governConf);
         return Result.succeed();
     }
 }

+ 18 - 0
ai-server/src/main/java/com/zsjz/ai/module/govern/serivce/GovernConfService.java

@@ -1,5 +1,6 @@
 package com.zsjz.ai.module.govern.serivce;
 
+import com.baomidou.dynamic.datasource.annotation.DS;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
 import com.zsjz.ai.common.cache.CaseDataCache;
@@ -7,6 +8,9 @@ import com.zsjz.ai.common.constants.StrConsts;
 import com.zsjz.ai.common.model.govern.entity.GovernConf;
 import com.zsjz.ai.module.govern.mapper.GovernConfMapper;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
 
 /**
  * 数据治理配置服务类
@@ -14,6 +18,20 @@ import org.springframework.stereotype.Service;
  */
 @Service
 public class GovernConfService extends ServiceImpl<GovernConfMapper, GovernConf> {
+    /**
+     * 保存 / 更新治理配置(案件 DuckDB 库)。
+     *
+     * <p>不能直接调用继承来的 {@code saveOrUpdateBatch}:它的事务在 service 层就开始了,
+     * 而 mapper 上的 {@code @DS("slave")} 要到 SQL 执行时才生效,此前的连接已经取自主库
+     * (PostgreSQL),查询 {@code govern_conf} 会报「关系不存在」。
+     * 这里把 {@code @DS("slave")} 提到事务外层,确保事务一开始就拿案件库连接。
+     */
+    @DS(StrConsts.DS_KEY_SLAVE)
+    @Transactional(rollbackFor = Exception.class)
+    public void saveGovernConfBatch(List<GovernConf> governConfList) {
+        saveOrUpdateBatch(governConfList);
+    }
+
     /**
      * 获取配置项的值
      * 从治理配置表中根据 key 获取对应的配置值,如果不存在则返回默认值

+ 12 - 2
ai-server/src/main/java/com/zsjz/ai/module/person/service/PersonBasicInfoService.java

@@ -4,10 +4,12 @@ import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.io.FileUtil;
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.StrUtil;
+import com.baomidou.dynamic.datasource.annotation.DS;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
 import com.google.common.collect.Sets;
 import com.zsjz.ai.common.cache.CaseDataCache;
+import com.zsjz.ai.common.constants.StrConsts;
 import com.zsjz.ai.common.exception.ServerException;
 import com.zsjz.ai.common.model.person.dto.PersonBasicInfoDTO;
 import com.zsjz.ai.common.model.person.entity.PersonBasicInfo;
@@ -21,6 +23,7 @@ import org.apache.commons.io.FileUtils;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.io.IOException;
 import java.nio.file.Files;
@@ -156,10 +159,13 @@ public class PersonBasicInfoService extends ServiceImpl<PersonBasicInfoMapper, P
     }
 
     /**
-     * 调整人员排序
+     * 调整人员排序(案件 DuckDB 库)。
      *
-     * @param list 人员基本信息列表
+     * <p>{@code updateBatchById} 内部走 SqlSession 批量执行,不经过 mapper 代理,
+     * 必须在外层先把数据源切到 slave 并开启事务,否则会落到主库 PostgreSQL。
      */
+    @DS(StrConsts.DS_KEY_SLAVE)
+    @Transactional(rollbackFor = Exception.class)
     public void personOrder(List<PersonBasicInfo> list) {
         updateBatchById(list);
     }
@@ -171,11 +177,15 @@ public class PersonBasicInfoService extends ServiceImpl<PersonBasicInfoMapper, P
      * 多人同时清洗时会互相清掉对方缓冲、把其他案件的人员冲进当前案件库。
      * 现在由调用方 {@code CleanCache.drain()} 按 caseId 原子取走快照后传入。
      */
+    @DS(StrConsts.DS_KEY_SLAVE)
+    @Transactional(rollbackFor = Exception.class)
     public void flushPerson(Set<PersonBasicInfo> persons) {
         if (CollUtil.isEmpty(persons)) {
             return;
         }
         try {
+            // saveOrUpdateBatch 是 self-invocation,父类的 @Transactional 不会生效;
+            // 由本方法的事务 + 外层 @DS 保证整批写入案件库
             saveOrUpdateBatch(persons);
         } catch (Exception e) {
             log.error("flushPerson error", e);