Kaynağa Gözat

fix(S-cfc-7): dimension.registry.getTree 契约 2 处实现缺陷

commit e0ae1e459 的 DimensionRegistryTreeTest(9 用例契约黑盒测试)暴露两处 src/main 实现缺陷,修复如下:

1) 启动时序缺陷(DimensionCodeMapper.reload):
   @PostConstruct 在 DatabaseInitializer.run()(CommandLineRunner 迁移445 legacy_alias 列)之前执行,
   全新/旧 schema 库上 selectList 全字段查询抛 Unknown column 'legacy_alias' → @SpringBootTest
   上下文全量启动失败(9/9 RED 而非 404)。改为惰性加载:去掉 @PostConstruct,首次
   resolveToUnified/resolveLegacy/isKnownCode 调用时触发 reload()(此时 CommandLineRunner 迁移已完成)。

2) buildTree source_system 过滤缺陷(DimensionRegistryService.getTree):
   旧实现先按 source_system 过滤掉不匹配节点、再只收集 parentId==null 的根;
   sourceSystem=health 时 body 根(source=energy)被排除,7 个健康子维因父节点不在集合内
   而成为孤儿(均非根)→ 返回空数组,与契约(应返回 7 节点)不符。
   现改为:收集所有匹配 source_system 的节点,按 parentId 在匹配集合内构建子树,
   父节点被过滤/缺失/无父 则提升为过滤树根;无过滤模式保持原完整树语义不变。

测试:mvn clean test -Dtest=DimensionRegistryTreeTest -DskipTests=false → 9 用例全绿
(Tests run: 9, Failures: 0, Errors: 0, Skipped: 0)。

改动文件:
- cfc-backend/src/main/java/com/etotem/cfc/service/DimensionCodeMapper.java
- cfc-backend/src/main/java/com/etotem/cfc/service/DimensionRegistryService.java
E2E Test Bot 1 hafta önce
ebeveyn
işleme
db095214ae

+ 11 - 2
cfc-backend/src/main/java/com/etotem/cfc/service/DimensionCodeMapper.java

@@ -6,7 +6,6 @@ import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.springframework.stereotype.Service;
 
-import javax.annotation.PostConstruct;
 import javax.annotation.Resource;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -36,7 +35,17 @@ public class DimensionCodeMapper {
     private volatile Map<String, List<String>> unifiedToLegacy = new HashMap<>();
     private volatile boolean loaded = false;
 
-    @PostConstruct
+    /**
+     * 重建内存索引。
+     *
+     * <p><b>启动时序修复(S-cfc-7 / e0ae1e459 契约测试暴露):</b>本方法不再以
+     * {@code @PostConstruct} 在 Bean 初始化期执行——该时机早于
+     * {@code DatabaseInitializer.run()}(CommandLineRunner 迁移445 legacy_alias 列),
+     * 在全新/旧 schema 库上 selectList 会抛 {@code Unknown column 'legacy_alias'},
+     * 导致 @SpringBootTest 上下文全量启动失败(9/9 RED 而非 404)。
+     * 现改为惰性加载:首次 {@link #resolveToUnified}/{@link #resolveLegacy}/{@link #isKnownCode}
+     * 调用时触发(此时 CommandLineRunner 已完成迁移),保障加载顺序。</p>
+     */
     public synchronized void reload() {
         Map<String, String> legacyMap = new HashMap<>();
         Map<String, List<String>> unifiedMap = new HashMap<>();

+ 35 - 11
cfc-backend/src/main/java/com/etotem/cfc/service/DimensionRegistryService.java

@@ -11,8 +11,10 @@ import org.springframework.util.StringUtils;
 import javax.annotation.Resource;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * 统一维度体系注册中心服务(设计文档 §6)。
@@ -43,19 +45,24 @@ public class DimensionRegistryService {
     @Value("${dimension.registry.dualWrite:false}")
     private boolean dualWrite;
 
-    /** 统一树(维度节点 + 指标节点混排)。sourceSystem 为空返回全部。 */
+    /**
+     * 统一树(维度节点 + 指标节点混排)。sourceSystem 为空返回全部。
+     *
+     * <p>启动/过滤时序修复(S-cfc-7 / e0ae1e459 契约测试暴露):
+     * 旧实现先按 source_system 过滤掉不匹配节点、再只收集 parentId==null 的根;
+     * sourceSystem=health 时 body 根(source=energy)被排除,7 个健康子维因父节点
+     * 不在集合内而成为"孤儿"(均非根)→ 返回空数组。
+     * 现改为:收集<strong>所有</strong>匹配 source_system 的节点,按 parentId 在
+     * 匹配集合内构建子树,父节点被过滤掉(或不存在/无父)的节点提升为过滤树的根。</p>
+     */
     public List<DimensionNode> getTree(String sourceSystem) {
         List<ProductDimensionConfig> dims = productDimensionConfigService.list();
         List<IndicatorDefinition> indicators = indicatorService.listAll();
 
         // 统一转成 DimensionNode(维度节点标识 root/sub;指标节点标识 indicator)
         Map<Long, DimensionNode> idToNode = new HashMap<>();
-        List<DimensionNode> roots = new ArrayList<>();
 
         for (ProductDimensionConfig d : dims) {
-            if (!StringUtils.isEmpty(sourceSystem) && !sourceSystem.equals(d.getSourceSystem())) {
-                continue;
-            }
             DimensionNode n = toNode(d);
             idToNode.put(n.getId(), n);
         }
@@ -76,15 +83,25 @@ public class DimensionRegistryService {
             idToNode.putIfAbsent(n.getId(), n);
         }
 
-        // 按 parentId 组装
+        boolean filtered = !StringUtils.isEmpty(sourceSystem);
+        List<DimensionNode> roots = new ArrayList<>();
+
+        // 按 parentId 组装:过滤模式下收集全部匹配来源节点,父节点同源则挂载为子树,
+        // 父节点被过滤/缺失/无父 则提升为过滤树根(修复 sourceSystem=health 遗漏非根节点)。
         for (DimensionNode n : idToNode.values()) {
+            if (filtered && !sourceSystem.equals(n.getSourceSystem())) {
+                continue;
+            }
             Long parentId = n.getParentId();
-            if (parentId != null && idToNode.containsKey(parentId)) {
-                DimensionNode parent = idToNode.get(parentId);
-                if (parent.getChildren() == null) {
-                    parent.setChildren(new ArrayList<>());
+            DimensionNode parent = parentId != null ? idToNode.get(parentId) : null;
+            if (filtered) {
+                if (parent != null && sourceSystem.equals(parent.getSourceSystem())) {
+                    attachChild(parent, n);
+                } else {
+                    roots.add(n);
                 }
-                parent.getChildren().add(n);
+            } else if (parent != null) {
+                attachChild(parent, n);
             } else if (parentId == null) {
                 roots.add(n);
             }
@@ -92,6 +109,13 @@ public class DimensionRegistryService {
         return roots;
     }
 
+    private void attachChild(DimensionNode parent, DimensionNode child) {
+        if (parent.getChildren() == null) {
+            parent.setChildren(new ArrayList<>());
+        }
+        parent.getChildren().add(child);
+    }
+
     /** 5 根。 */
     public List<DimensionNode> getRoots() {
         return getTree(null).stream()