瀏覽代碼

fix(kb): 统一知识库启动时自愈回填缺失模块

旧库由8模块版本种子初始化后,种子新增的 元数据/食物营养库/菌-疾病关联/营养素参考
因 importFromClasspath(false) 的 DB 非空跳过逻辑永不补齐,导致食物搜索/营养素/疾病关联/
版本号在生产为空。load() 增加 backfillMissingModules:DB 非空但缺 MODULE_KEYS 时从
classpath 种子逐键补齐(幂等,不动已存在模块)。
Xiaogang Liao 1 周之前
父節點
當前提交
f018a48071
共有 1 個文件被更改,包括 80 次插入 和 0 次删除
  1. 80 0
      cfc-backend/src/main/java/com/etotem/cfc/service/UnifiedKnowledgeBaseService.java

+ 80 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/UnifiedKnowledgeBaseService.java

@@ -119,6 +119,13 @@ public class UnifiedKnowledgeBaseService {
             } catch (Exception e) {
                 log.warn("classpath种子导入失败: {}", e.getMessage());
             }
+        } else if (backfillMissingModules(rows)) {
+            // DB 非空但缺少部分模块(种子新增模块后旧库未补齐),从种子回填后重读
+            try {
+                rows = knowledgeBaseSectionMapper.selectList(null);
+            } catch (Exception ignore) {
+                // 回填后重读失败也不影响:至少新增行已落库,下次启动生效
+            }
         }
 
         if (rows != null) {
@@ -151,6 +158,79 @@ public class UnifiedKnowledgeBaseService {
                 indicatorIndex.size(), nutrientIndex.size(), diseaseIndex.size());
     }
 
+    /**
+     * 回填 DB 中缺失的模块(从 classpath 种子读取),用于种子新增模块后旧库自动补齐(幂等)。
+     * 仅插入缺失键,不触碰已存在的模块。
+     *
+     * @param existingRows 当前 DB 中的行
+     * @return true=有回填(调用方应重读 DB)
+     */
+    private boolean backfillMissingModules(List<KnowledgeBaseSection> existingRows) {
+        if (existingRows == null || existingRows.isEmpty()) {
+            return false;
+        }
+        Set<String> existingKeys = new HashSet<>();
+        for (KnowledgeBaseSection r : existingRows) {
+            if (r.getModuleKey() != null) {
+                existingKeys.add(r.getModuleKey());
+            }
+        }
+        List<String> missing = new ArrayList<>();
+        for (String key : MODULE_KEYS) {
+            if (!existingKeys.contains(key)) {
+                missing.add(key);
+            }
+        }
+        if (missing.isEmpty()) {
+            return false;
+        }
+
+        Map<String, Object> root;
+        try (InputStream is = getClass().getResourceAsStream(SEED_CLASSPATH)) {
+            if (is == null) {
+                log.warn("种子文件不存在,无法回填缺失模块: {}", SEED_CLASSPATH);
+                return false;
+            }
+            root = objectMapper.readValue(is, new TypeReference<Map<String, Object>>() {});
+        } catch (Exception e) {
+            log.warn("读取种子文件失败,无法回填缺失模块: {}", e.getMessage());
+            return false;
+        }
+
+        log.info("检测到知识库缺失模块 {} 个,从classpath种子回填: {}", missing.size(), missing);
+        boolean filled = false;
+        Date now = new Date();
+        for (String key : missing) {
+            Object content = root.get(key);
+            if (content == null) {
+                log.warn("种子文件中缺少模块,跳过回填: {}", key);
+                continue;
+            }
+            try {
+                String moduleVersion = null;
+                if ("元数据".equals(key) && content instanceof Map) {
+                    Object v = ((Map<String, Object>) content).get("版本");
+                    if (v != null) {
+                        moduleVersion = String.valueOf(v);
+                    }
+                }
+                KnowledgeBaseSection row = new KnowledgeBaseSection();
+                row.setModuleKey(key);
+                row.setModuleName(key);
+                row.setContent(objectMapper.writeValueAsString(content));
+                row.setVersion(moduleVersion);
+                row.setCreatedAt(now);
+                row.setUpdatedAt(now);
+                knowledgeBaseSectionMapper.insert(row);
+                filled = true;
+                log.info("回填缺失模块到DB: {}", key);
+            } catch (Exception e) {
+                log.warn("回填模块[{}]失败: {}", key, e.getMessage());
+            }
+        }
+        return filled;
+    }
+
     /**
      * 从 classpath 种子文件导入全部模块到 DB(幂等,存在则更新)
      *