Эх сурвалжийг харах

Merge remote-tracking branch 'origin/cfclub' into cfclub

Sisyphus Agent 2 цаг өмнө
parent
commit
1250a2f139

+ 386 - 0
cfc-backend/src/test/java/com/etotem/cfc/unit/AdminKnowledgeBaseManageContractTest.java

@@ -0,0 +1,386 @@
+package com.etotem.cfc.unit;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.nio.charset.StandardCharsets;
+import java.util.Scanner;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * 契约级单测:管理端知识库管理(能力键 {@code admin.knowledgeBase.manage})。
+ *
+ * <p><b>契约来源(设计/需求溯源)</b>:
+ * <ul>
+ *   <li>需求:{@code tests/user-scenarios/应用场景总览.md} <b>SC-A-08 知识库管理</b>(知识条目 CRUD → 向量同步;
+ *       边界:<b>同步失败重试;大批量分批</b>)与 <b>SC-A-22 精准营养知识库管理</b>(CRUD → 同步;边界:<b>同步失败重试</b>)。</li>
+ *   <li>设计:{@code docs/superpowers/api/API_REFERENCE.md} §4.60.8 —— 载体为
+ *       {@code KnowledgeBaseController} 系(非开工包猜测的 {@code AdminKnowledgeController}):
+ *       {@code /api/admin/knowledge-base}(list/get/save/delete/upload-file/fetch-url/bind-tags/bind-dimensions/backfill-tags)
+ *       + {@code KnowledgeBaseVectorController /api/admin/knowledge-base/vector}(stats/documents/sync/delete-source/update-document)
+ *       + {@code KnowledgeTagController /api/admin/knowledge-tag}(list/save/delete)
+ *       + {@code AdminUnifiedKnowledgeController /api/admin/knowledge-base/unified}(overview/section/update/reset)。</li>
+ *   <li>契约核对:{@code docs/superpowers/specs/2026-11-07-admin-ops-contract.md} §1.8/§1.22 —— SC-A-08 载体确认存在,
+ *       写入端点权限粒度「留待测试侧补权限流核验」。</li>
+ * </ul></p>
+ *
+ * <p><b>实现现状(src/main 实测)</b>:
+ * <ul>
+ *   <li>✅ <b>已落地</b>:四个 Controller(CRUD/向量/标签/统一知识库)与 {@code DanKnowledgeBaseService} /
+ *       {@code UnifiedKnowledgeBaseService} 均存在,schema.sql 含 {@code dan_knowledge_base} / {@code dan_knowledge_tag} /
+ *       {@code dan_knowledge_base_tag} / {@code dan_knowledge_base_dimension} / {@code knowledge_base_section} 建表。</li>
+ *   <li>❌ <b>未落地(契约缺口,RED)</b>:① 同步失败重试 —— {@code LanggraphSyncService}(已标 deprecated)与
+ *       {@code KnowledgeBaseVectorController} 同步路径无任何 retry/重试能力(无 {@code maxRetries}、无
+ *       {@code syncWithRetry}、无 {@code @Retryable});② 大批量分批 —— 同步/回填路径无任何 batch 分批能力
+ *       (无 {@code syncBatch}、无 {@code BATCH_SIZE});③ 知识库写入端点角色门禁 —— {@code KnowledgeBaseController} /
+ *       {@code KnowledgeBaseVectorController} 无控制器内平台角色校验({@code AdminUnifiedKnowledgeController} 已有
+ *       {@code allowed} 门禁,故该项仅对 base/vector 缺口红)。</li>
+ * </ul></p>
+ *
+ * <p><b>测试范围</b>:仅用反射 + schema.sql 类路径探测(无 Spring / 无 DB / 无外部服务),
+ * 与同批已合入主链的 {@code TeamProfitHookContractTest} 风格一致。</p>
+ *
+ * <p><b>RED 预期</b>:契约边界行为(同步失败重试 / 大批量分批)与 base/vector 角色门禁未落地,
+ * 对应用例在实现补齐前应失败(红);已落地的契约表面(CRUD/向量/标签/统一端点 + 统一知识库角色门禁)用例为绿,
+ * 作为「已存在」的登记证据。</p>
+ *
+ * <p><b>GREEN 升级指引</b>:实现者在同步路径补「失败重试」({@code maxRetries} 配置 / {@code syncWithRetry} /
+ * {@code @Retryable})、补「大批量分批」({@code syncBatch} / {@code BATCH_SIZE})、并为
+ * {@code KnowledgeBaseController} / {@code KnowledgeBaseVectorController} 补平台角色门禁后,
+ * 对应 RED 用例自动转绿。</p>
+ */
+class AdminKnowledgeBaseManageContractTest {
+
+    /** SC-A-08 载体 Controller 类(§4.60.8) */
+    private static final String KB_CONTROLLER = "com.etotem.cfc.controller.admin.KnowledgeBaseController";
+    private static final String KB_VECTOR_CONTROLLER = "com.etotem.cfc.controller.admin.KnowledgeBaseVectorController";
+    private static final String KB_TAG_CONTROLLER = "com.etotem.cfc.controller.admin.KnowledgeTagController";
+    private static final String KB_UNIFIED_CONTROLLER = "com.etotem.cfc.controller.admin.AdminUnifiedKnowledgeController";
+
+    /** SC-A-08/SC-A-22 Service 载体 */
+    private static final String KB_SERVICE = "com.etotem.cfc.service.DanKnowledgeBaseService";
+    private static final String UNIFIED_KB_SERVICE = "com.etotem.cfc.service.UnifiedKnowledgeBaseService";
+    private static final String SYNC_SERVICE = "com.etotem.cfc.service.LanggraphSyncService";
+
+    /** §4.60.8 端点子路径(Controller 内 {@code @PostMapping} 值) */
+    private static final String[] KB_CRUD_ENDPOINTS = {
+            "/list", "/get", "/save", "/delete", "/upload-file", "/fetch-url",
+            "/bind-tags", "/bind-dimensions", "/backfill-tags"
+    };
+    private static final String[] KB_VECTOR_ENDPOINTS = {
+            "/stats", "/documents", "/sync", "/delete-source", "/update-document"
+    };
+    private static final String[] KB_TAG_ENDPOINTS = {"/list", "/save", "/delete"};
+    private static final String[] KB_UNIFIED_ENDPOINTS = {"/overview", "/section", "/update", "/reset"};
+
+    /** schema.sql 应包含的知识库相关表 */
+    private static final String[] KB_TABLES = {
+            "dan_knowledge_base", "dan_knowledge_tag", "dan_knowledge_base_tag",
+            "dan_knowledge_base_dimension", "knowledge_base_section"
+    };
+
+    // ------------------------------------------------------------------
+    // 正常流:契约表面已落地(登记证据,GREEN)
+    // ------------------------------------------------------------------
+
+    @Test
+    @DisplayName("正常流-1:KnowledgeBaseController 存在并承载知识条目 CRUD 端点(§4.60.8)")
+    void knowledgeBaseController_surfaceExists() {
+        Class<?> c = loadClass(KB_CONTROLLER);
+        assertNotNull(c, "[admin.knowledgeBase.manage] KnowledgeBaseController 类不存在(§4.60.8)。");
+        assertEndpoints(c, KB_CRUD_ENDPOINTS, "KnowledgeBaseController", "/api/admin/knowledge-base");
+    }
+
+    @Test
+    @DisplayName("正常流-2:KnowledgeBaseVectorController 存在并承载向量库端点(§4.60.8)")
+    void knowledgeBaseVectorController_surfaceExists() {
+        Class<?> c = loadClass(KB_VECTOR_CONTROLLER);
+        assertNotNull(c, "[admin.knowledgeBase.manage] KnowledgeBaseVectorController 类不存在(§4.60.8)。");
+        assertEndpoints(c, KB_VECTOR_ENDPOINTS, "KnowledgeBaseVectorController", "/api/admin/knowledge-base/vector");
+    }
+
+    @Test
+    @DisplayName("正常流-3:KnowledgeTagController 存在并承载知识标签字典端点(§4.60.8)")
+    void knowledgeTagController_surfaceExists() {
+        Class<?> c = loadClass(KB_TAG_CONTROLLER);
+        assertNotNull(c, "[admin.knowledgeBase.manage] KnowledgeTagController 类不存在(§4.60.8)。");
+        assertEndpoints(c, KB_TAG_ENDPOINTS, "KnowledgeTagController", "/api/admin/knowledge-tag");
+    }
+
+    @Test
+    @DisplayName("正常流-4:AdminUnifiedKnowledgeController 存在并承载统一知识库端点(§4.60.8)")
+    void adminUnifiedKnowledgeController_surfaceExists() {
+        Class<?> c = loadClass(KB_UNIFIED_CONTROLLER);
+        assertNotNull(c, "[admin.knowledgeBase.manage] AdminUnifiedKnowledgeController 类不存在(§4.60.8)。");
+        assertEndpoints(c, KB_UNIFIED_ENDPOINTS, "AdminUnifiedKnowledgeController", "/api/admin/knowledge-base/unified");
+    }
+
+    @Test
+    @DisplayName("正常流-5:知识库 Service 载体存在(DanKnowledgeBaseService / UnifiedKnowledgeBaseService)")
+    void knowledgeServices_surfaceExists() {
+        Class<?> svc = loadClass(KB_SERVICE);
+        assertNotNull(svc, "[admin.knowledgeBase.manage] DanKnowledgeBaseService 类不存在。");
+        assertTrue(hasMethod(svc, "saveWithAssociations"), "[admin.knowledgeBase.manage] "
+                + "DanKnowledgeBaseService 应提供 saveWithAssociations(保存+关联标签/维度)。");
+        assertTrue(hasMethod(svc, "getWithAssociations"), "[admin.knowledgeBase.manage] "
+                + "DanKnowledgeBaseService 应提供 getWithAssociations(详情+关联)。");
+        assertTrue(hasMethod(svc, "backfillTags"), "[admin.knowledgeBase.manage] "
+                + "DanKnowledgeBaseService 应提供 backfillTags(标签/维度回填)。");
+
+        Class<?> uni = loadClass(UNIFIED_KB_SERVICE);
+        assertNotNull(uni, "[admin.knowledgeBase.manage] UnifiedKnowledgeBaseService 类不存在。");
+        assertTrue(hasMethod(uni, "getModuleOverviewDetailed"), "[admin.knowledgeBase.manage] "
+                + "UnifiedKnowledgeBaseService 应提供 getModuleOverviewDetailed(模块概览)。");
+        assertTrue(hasMethod(uni, "updateModule"), "[admin.knowledgeBase.manage] "
+                + "UnifiedKnowledgeBaseService 应提供 updateModule(写库+热重载)。");
+    }
+
+    @Test
+    @DisplayName("正常流-6:知识库相关表落地 schema.sql(§2.2~2.5 设计)")
+    void schemaSql_hasKnowledgeTables() {
+        String schema = readSchemaSql();
+        for (String table : KB_TABLES) {
+            assertTrue(schema.contains(table),
+                    "[admin.knowledgeBase.manage] schema.sql 缺少 " + table + " 建表。");
+        }
+    }
+
+    // ------------------------------------------------------------------
+    // 边界流:同步失败重试(契约 SC-A-08/A-22 要求,当前未实现 → RED)
+    // ------------------------------------------------------------------
+
+    @Test
+    @DisplayName("边界流-1:知识库同步路径应支持失败重试(SC-A-08/A-22 边界,未实现 → 红)")
+    void syncFailureRetry_syncPathShouldSupportRetry() {
+        // 契约 AC:知识条目 CRUD → 向量同步,边界「同步失败重试」。
+        // 探测同步路径(LanggraphSyncService / KnowledgeBaseVectorController / DanKnowledgeBaseService)
+        // 是否暴露重试能力:syncWithRetry 方法 / maxRetries、retryCount 字段 / @Retryable 注解。
+        assertTrue(hasRetryCapability(SYNC_SERVICE, KB_VECTOR_CONTROLLER, KB_SERVICE),
+                "[admin.knowledgeBase.manage] 同步路径无任何失败重试能力(syncWithRetry / maxRetries / @Retryable):"
+                        + "契约 SC-A-08/A-22 边界要求「同步失败重试」,当前未实现。");
+    }
+
+    @Test
+    @DisplayName("边界流-2:向量同步端点应接受重试配置参数(SC-A-08 边界,未实现 → 红)")
+    void syncFailureRetry_vectorSyncShouldAcceptRetryParam() {
+        Class<?> c = loadClass(KB_VECTOR_CONTROLLER);
+        if (c == null) {
+            fail("[admin.knowledgeBase.manage] KnowledgeBaseVectorController 类不存在,无法验证重试参数。");
+        }
+        Method sync = findMethod(c, "triggerSync");
+        if (sync == null) {
+            fail("[admin.knowledgeBase.manage] KnowledgeBaseVectorController 缺少 triggerSync(/sync)端点。");
+        }
+        // 契约要求同步可传重试/批量参数;现无参(triggerSync())→ 红
+        boolean hasRetryParam = sync.getParameterCount() > 0
+                && (sync.getParameterTypes()[0] == java.util.Map.class);
+        assertTrue(hasRetryParam,
+                "[admin.knowledgeBase.manage] /api/admin/knowledge-base/vector/sync 应接受重试配置参数"
+                        + "(如 maxRetries),支持同步失败重试(SC-A-08 边界);当前为无参 triggerSync()。");
+    }
+
+    // ------------------------------------------------------------------
+    // 边界流:大批量分批(契约 SC-A-08 要求,当前未实现 → RED)
+    // ------------------------------------------------------------------
+
+    @Test
+    @DisplayName("边界流-3:知识库同步/回填应支持大批量分批(SC-A-08 边界,未实现 → 红)")
+    void largeBatch_syncAndBackfillShouldSupportBatching() {
+        // 契约 AC:边界「大批量分批」。探测 DanKnowledgeBaseService / LanggraphSyncService 是否暴露
+        // 分批能力:syncBatch / batchSync 方法 或 BATCH_SIZE 常量(默认分批阈值)。
+        assertTrue(hasBatchCapability(KB_SERVICE, SYNC_SERVICE),
+                "[admin.knowledgeBase.manage] 同步/回填路径无任何分批能力(syncBatch / BATCH_SIZE):"
+                        + "契约 SC-A-08 边界要求「大批量分批」,当前未实现。");
+    }
+
+    @Test
+    @DisplayName("边界流-4:backfill-tags 应分批回填并返回批处理统计(SC-A-08 边界,未实现 → 红)")
+    void largeBatch_backfillShouldReturnBatchStats() {
+        Class<?> svc = loadClass(KB_SERVICE);
+        if (svc == null) {
+            fail("[admin.knowledgeBase.manage] DanKnowledgeBaseService 类不存在,无法验证分批回填。");
+        }
+        Method backfill = findMethod(svc, "backfillTags");
+        if (backfill == null) {
+            fail("[admin.knowledgeBase.manage] DanKnowledgeBaseService 缺少 backfillTags。");
+        }
+        // 契约要求回填按批处理并返回批统计(如 total / processed / batchSize);
+        // 现返回 Map 但无 batchSize 语义探测点(返回类型为 Map<String,Object>,无法静态断言)——
+        // 以是否存在 BATCH_SIZE 常量 / batchSize 字段作为分批哨兵。
+        boolean hasBatchSentinel = false;
+        for (Field f : svc.getDeclaredFields()) {
+            if (f.getName().equalsIgnoreCase("BATCH_SIZE") || f.getName().toLowerCase().contains("batch")) {
+                hasBatchSentinel = true;
+                break;
+            }
+        }
+        assertTrue(hasBatchSentinel,
+                "[admin.knowledgeBase.manage] DanKnowledgeBaseService 无 BATCH_SIZE/batch 分批哨兵:"
+                        + "契约 SC-A-08 边界要求「大批量分批」回填,当前未实现。");
+    }
+
+    // ------------------------------------------------------------------
+    // 权限流:知识库管理应受平台角色门禁(SC-A-08 权限粒度)
+    // ------------------------------------------------------------------
+
+    @Test
+    @DisplayName("权限流-1:统一知识库端点已实现平台角色门禁(allowed,已落地 → 绿)")
+    void permissionFlow_unifiedEnforcesRole() {
+        Class<?> c = loadClass(KB_UNIFIED_CONTROLLER);
+        if (c == null) {
+            fail("[admin.knowledgeBase.manage] AdminUnifiedKnowledgeController 类不存在。");
+        }
+        // 已落地:allowed(request) 门禁 + ALLOWED_ROLES 集合(admin/operator/finance/knowledge_admin/butler)
+        assertTrue(hasMethod(c, "allowed"),
+                "[admin.knowledgeBase.manage] AdminUnifiedKnowledgeController 应含 allowed(request) 角色门禁。");
+    }
+
+    @Test
+    @DisplayName("权限流-2:知识库 CRUD 写入端点应实现平台角色门禁(SC-A-08 权限粒度,未实现 → 红)")
+    void permissionFlow_kbWriteEndpointsShouldEnforceRole() {
+        Class<?> c = loadClass(KB_CONTROLLER);
+        if (c == null) {
+            fail("[admin.knowledgeBase.manage] KnowledgeBaseController 类不存在。");
+        }
+        // 契约(§4.60.8 + 2026-11-07-admin-ops-contract §3#5「写入端点权限粒度留待测试侧核验」):
+        // 知识库写入(save/delete/bind-tags/bind-dimensions/backfill-tags)应受平台/knowledge_admin 角色门禁,
+        // 实现方式为控制器内 @RequestAttribute("role") 校验或 allowed/roles 门禁。
+        boolean hasRoleGate = hasMethod(c, "allowed") || hasMethod(c, "roles")
+                || hasParameterAnnotation(c, "javax.servlet.http.HttpServletRequest");
+        assertTrue(hasRoleGate,
+                "[admin.knowledgeBase.manage] KnowledgeBaseController 写入端点无平台角色门禁"
+                        + "(无 allowed/roles 门禁、无 @RequestAttribute(\"role\")):"
+                        + "知识库写入权限粒度应核验(2026-11-07-admin-ops-contract §3#5)。");
+    }
+
+    @Test
+    @DisplayName("权限流-3:向量库端点应实现平台角色门禁(SC-A-08 权限粒度,未实现 → 红)")
+    void permissionFlow_vectorEndpointsShouldEnforceRole() {
+        Class<?> c = loadClass(KB_VECTOR_CONTROLLER);
+        if (c == null) {
+            fail("[admin.knowledgeBase.manage] KnowledgeBaseVectorController 类不存在。");
+        }
+        boolean hasRoleGate = hasMethod(c, "allowed") || hasMethod(c, "roles")
+                || hasParameterAnnotation(c, "javax.servlet.http.HttpServletRequest");
+        assertTrue(hasRoleGate,
+                "[admin.knowledgeBase.manage] KnowledgeBaseVectorController 无平台角色门禁"
+                        + "(向量库管理应受平台角色保护,与统一知识库一致)。");
+    }
+
+    // ------------------------------------------------------------------
+    // 反射辅助
+    // ------------------------------------------------------------------
+
+    private void assertEndpoints(Class<?> c, String[] endpoints, String label, String mappingPrefix) {
+        for (String ep : endpoints) {
+            boolean found = false;
+            for (Method m : c.getDeclaredMethods()) {
+                if (m.isAnnotationPresent(org.springframework.web.bind.annotation.PostMapping.class)) {
+                    org.springframework.web.bind.annotation.PostMapping pm =
+                            m.getAnnotation(org.springframework.web.bind.annotation.PostMapping.class);
+                    String[] vals = pm.value();
+                    for (String v : vals) {
+                        if (v.equals(ep)) {
+                            found = true;
+                            break;
+                        }
+                    }
+                }
+                if (found) break;
+            }
+            assertTrue(found,
+                    "[admin.knowledgeBase.manage] " + label + " 缺少端点 " + mappingPrefix + ep
+                            + "(§4.60.8)。");
+        }
+    }
+
+    private boolean hasRetryCapability(String... classNames) {
+        for (String cn : classNames) {
+            Class<?> c = loadClass(cn);
+            if (c == null) continue;
+            for (Method m : c.getDeclaredMethods()) {
+                if (m.getName().toLowerCase().contains("retry")) return true;
+            }
+            for (Field f : c.getDeclaredFields()) {
+                if (f.getName().toLowerCase().contains("retry")) return true;
+            }
+            if (hasAnnotationByName(c, "org.springframework.retry.annotation.Retryable")) return true;
+        }
+        return false;
+    }
+
+    private boolean hasBatchCapability(String... classNames) {
+        for (String cn : classNames) {
+            Class<?> c = loadClass(cn);
+            if (c == null) continue;
+            for (Method m : c.getDeclaredMethods()) {
+                if (m.getName().toLowerCase().contains("batch")) return true;
+            }
+            for (Field f : c.getDeclaredFields()) {
+                if (f.getName().equalsIgnoreCase("BATCH_SIZE")
+                        || f.getName().toLowerCase().contains("batch")) return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean hasMethod(Class<?> c, String name) {
+        for (Method m : c.getDeclaredMethods()) {
+            if (m.getName().equals(name)) return true;
+        }
+        return false;
+    }
+
+    private Method findMethod(Class<?> c, String name) {
+        for (Method m : c.getDeclaredMethods()) {
+            if (m.getName().equals(name)) return m;
+        }
+        return null;
+    }
+
+    private boolean hasParameterAnnotation(Class<?> c, String annotationName) {
+        for (Method m : c.getDeclaredMethods()) {
+            for (java.lang.annotation.Annotation[] paramAnns : m.getParameterAnnotations()) {
+                for (java.lang.annotation.Annotation a : paramAnns) {
+                    if (a.annotationType().getName().equals(annotationName)) return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean hasAnnotationByName(Class<?> c, String annotationName) {
+        for (java.lang.annotation.Annotation a : c.getDeclaredAnnotations()) {
+            if (a.annotationType().getName().equals(annotationName)) return true;
+        }
+        return false;
+    }
+
+    private Class<?> loadClass(String name) {
+        try {
+            return Class.forName(name, false, Thread.currentThread().getContextClassLoader());
+        } catch (ClassNotFoundException e) {
+            return null;
+        }
+    }
+
+    private String readSchemaSql() {
+        try (InputStream is = getClass().getClassLoader().getResourceAsStream("schema.sql")) {
+            if (is == null) {
+                return "";
+            }
+            try (Scanner scanner = new Scanner(is, StandardCharsets.UTF_8.name())) {
+                scanner.useDelimiter("\\A");
+                return scanner.hasNext() ? scanner.next() : "";
+            }
+        } catch (Exception e) {
+            return "";
+        }
+    }
+}

+ 1 - 1
cfc-web/.last_build_commit

@@ -1 +1 @@
-181282731c1c1faa17eb7f4fdddc34d583c1e9f4
+f2f6e9235d086118bf496d80f42a459ee116e56a

+ 2 - 2
cfc-web/package-lock.json

@@ -1,12 +1,12 @@
 {
 {
   "name": "cfc-web",
   "name": "cfc-web",
-  "version": "1.0.2434",
+  "version": "1.0.2435",
   "lockfileVersion": 3,
   "lockfileVersion": 3,
   "requires": true,
   "requires": true,
   "packages": {
   "packages": {
     "": {
     "": {
       "name": "cfc-web",
       "name": "cfc-web",
-      "version": "1.0.2434",
+      "version": "1.0.2435",
       "dependencies": {
       "dependencies": {
         "@wangeditor/editor": "^5.1.23",
         "@wangeditor/editor": "^5.1.23",
         "@wangeditor/editor-for-vue": "^1.0.2",
         "@wangeditor/editor-for-vue": "^1.0.2",

+ 1 - 1
cfc-web/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "cfc-web",
   "name": "cfc-web",
-  "version": "1.0.2435",
+  "version": "1.0.2436",
   "private": true,
   "private": true,
   "scripts": {
   "scripts": {
     "dev": "vue-cli-service serve",
     "dev": "vue-cli-service serve",

+ 25 - 0
cfc-web/public/CHANGELOG-v1.0.md

@@ -4,6 +4,31 @@
 
 
 ---
 ---
 
 
+## v1.0.2436 (2026-11-13)
+
+### 文档
+- SC 台账 P 域 C/D 域 32 行回填(report/diet/growth/assessment/plan 16 键入池 S-cfc-267~282)
+
+### 测试
+- 能力键 admin.reportParser.manage 契约级单测(S-cfc-197)
+- RED contract test for SC-A-08/SC-A-22 knowledge base management
+
+### 其他
+- ReportParseAdminController(/api/admin/report-parse) 完整契约面:
+- types/save/toggle/delete、fingerprints/list/save/delete、imports、
+- unknown-clusters/unknown-uploads、dan/reparse、list/retry/backfill-food。
+- 纯 Mockito/反射(无 Spring/DB/外部服务),对齐 TeamProfitHook/StarMultiplier/
+- Pipeline/FamilyJoinValidation 同批 RED 契约测试风格。
+- 实测实现已落地(origin/cfclub 两个 Controller 已存在),测试 37/37 绿,
+- 按 TR-7alescarpenter 契约核对转测试侧三类流证据登记,不私改 src/main。
+- 
+- - Sync failure retry (SC-A-08/A-22 boundary) - no maxRetries/syncWithRetry in sync path
+- - Large batch batching (SC-A-08 boundary) - no BATCH_SIZE/syncBatch in DanKnowledgeBaseService
+- - KB write endpoint role gate (AdminUnifiedKnowledgeController already has allowed; KnowledgeBaseController/KnowledgeBaseVectorController lack it per 2026-11-07-admin-ops-contract section 3#5)
+- Green tests confirm landed surface: 4 controllers, services, schema tables.
+- 
+
+
 ## v1.0.2435 (2026-11-13)
 ## v1.0.2435 (2026-11-13)
 
 
 ### 文档
 ### 文档

+ 26 - 1
cfc-web/public/CHANGELOG.md

@@ -1,6 +1,6 @@
 # 更新日志
 # 更新日志
 
 
-> 当前版本: v1.0.2435
+> 当前版本: v1.0.2436
 
 
 ## 历史版本
 ## 历史版本
 
 
@@ -8,6 +8,31 @@
 
 
 ---
 ---
 
 
+## v1.0.2436 (2026-11-13)
+
+### 文档
+- SC 台账 P 域 C/D 域 32 行回填(report/diet/growth/assessment/plan 16 键入池 S-cfc-267~282)
+
+### 测试
+- 能力键 admin.reportParser.manage 契约级单测(S-cfc-197)
+- RED contract test for SC-A-08/SC-A-22 knowledge base management
+
+### 其他
+- ReportParseAdminController(/api/admin/report-parse) 完整契约面:
+- types/save/toggle/delete、fingerprints/list/save/delete、imports、
+- unknown-clusters/unknown-uploads、dan/reparse、list/retry/backfill-food。
+- 纯 Mockito/反射(无 Spring/DB/外部服务),对齐 TeamProfitHook/StarMultiplier/
+- Pipeline/FamilyJoinValidation 同批 RED 契约测试风格。
+- 实测实现已落地(origin/cfclub 两个 Controller 已存在),测试 37/37 绿,
+- 按 TR-7alescarpenter 契约核对转测试侧三类流证据登记,不私改 src/main。
+- 
+- - Sync failure retry (SC-A-08/A-22 boundary) - no maxRetries/syncWithRetry in sync path
+- - Large batch batching (SC-A-08 boundary) - no BATCH_SIZE/syncBatch in DanKnowledgeBaseService
+- - KB write endpoint role gate (AdminUnifiedKnowledgeController already has allowed; KnowledgeBaseController/KnowledgeBaseVectorController lack it per 2026-11-07-admin-ops-contract section 3#5)
+- Green tests confirm landed surface: 4 controllers, services, schema tables.
+- 
+
+
 ## v1.0.2435 (2026-11-13)
 ## v1.0.2435 (2026-11-13)
 
 
 ### 文档
 ### 文档

+ 69 - 69
docs/tdd/orchestration/sc-key-map.md

@@ -31,7 +31,7 @@ SC-G-02 / SC-G-06 / SC-P-03 / SC-P-10 / SC-P-11 共 5 个 SC 已于 2026-11-06 O
 | SC-A-01 | 平台运营 | 业务域 L:交易延伸 / 内容 / 多端 | 管理端登录 + 全局统计 | admin.auth.login | GREEN | 未跑通 | 键已 GREEN(admin-auth R5);全局统计载体 AdminController /api/admin/users |
 | SC-A-01 | 平台运营 | 业务域 L:交易延伸 / 内容 / 多端 | 管理端登录 + 全局统计 | admin.auth.login | GREEN | 未跑通 | 键已 GREEN(admin-auth R5);全局统计载体 AdminController /api/admin/users |
 | SC-A-02 | 平台运营 | 业务域 L:交易延伸 / 内容 / 多端 | 报告解析中台(类型/聚类) | admin.reportParser.manage | RED | 未跑通 | S-cfc-197;ReportParserAdminController /api/admin/report-parser |
 | SC-A-02 | 平台运营 | 业务域 L:交易延伸 / 内容 / 多端 | 报告解析中台(类型/聚类) | admin.reportParser.manage | RED | 未跑通 | S-cfc-197;ReportParserAdminController /api/admin/report-parser |
 | SC-A-03 | 平台运营 | 业务域 L:交易延伸 / 内容 / 多端 | 文章发布-审核 | admin.article.manage | RED | 未跑通 | S-cfc-201;AdminArticleController /api/admin/articles(22 方法)|
 | SC-A-03 | 平台运营 | 业务域 L:交易延伸 / 内容 / 多端 | 文章发布-审核 | admin.article.manage | RED | 未跑通 | S-cfc-201;AdminArticleController /api/admin/articles(22 方法)|
-| SC-A-04 | 平台运营 | 业务域 L:交易延伸 / 内容 / 多端 | 关系类型 CRUD | — | — | 未跑通 | ⚠ 真缺口:family/RelationshipQualityController 仅只读评分,无 admin 关系类型 CRUD 载体,待建任务 |
+| SC-A-04 | 平台运营 | 业务域 L:交易延伸 / 内容 / 多端 | 关系类型 CRUD | admin.familyRelation.manage | RED | 未跑通 | S-cfc-308;真缺口已立项:需新增 AdminFamilyRelationController(关系类型 CRUD),当前仅只读评分,实现待定 |
 | SC-A-05 | 平台运营 | 业务域 L:交易延伸 / 内容 / 多端 | 勋章配置 CRUD | admin.badge.manage | RED | 未跑通 | S-cfc-206;BadgeController /api/badges(create/update/delete/grant)|
 | SC-A-05 | 平台运营 | 业务域 L:交易延伸 / 内容 / 多端 | 勋章配置 CRUD | admin.badge.manage | RED | 未跑通 | S-cfc-206;BadgeController /api/badges(create/update/delete/grant)|
 | SC-A-06 | 平台运营 | 业务域 L:交易延伸 / 内容 / 多端 | 供应商管理 | admin.supplier.manage | RED | 未跑通 | S-cfc-200;AdminSupplier/EcomSupplier/SupplyManageController |
 | SC-A-06 | 平台运营 | 业务域 L:交易延伸 / 内容 / 多端 | 供应商管理 | admin.supplier.manage | RED | 未跑通 | S-cfc-200;AdminSupplier/EcomSupplier/SupplyManageController |
 | SC-A-07 | 平台运营 | 业务域 L:交易延伸 / 内容 / 多端 | 活动配置 | admin.activity.config | RED | 未跑通 | S-cfc-203;AdminActivityController + Checkin/Registration |
 | SC-A-07 | 平台运营 | 业务域 L:交易延伸 / 内容 / 多端 | 活动配置 | admin.activity.config | RED | 未跑通 | S-cfc-203;AdminActivityController + Checkin/Registration |
@@ -131,38 +131,38 @@ SC-G-02 / SC-G-06 / SC-P-03 / SC-P-10 / SC-P-11 共 5 个 SC 已于 2026-11-06 O
 | SC-P-17 | 家长 | 业务域 C:五维能量与健康 | 查看五维能量 / 健康指数 | energy.fiveDim.view | RED | 未跑通 | S-cfc-264;五维分配总和=100%,配置错误≠100% 报警 |
 | SC-P-17 | 家长 | 业务域 C:五维能量与健康 | 查看五维能量 / 健康指数 | energy.fiveDim.view | RED | 未跑通 | S-cfc-264;五维分配总和=100%,配置错误≠100% 报警 |
 | SC-P-18 | 家长 | 业务域 C:五维能量与健康 | 能量流水查看 | energy.ledger.view | RED | 未跑通 | S-cfc-265;能量流水(任务/打卡/测评),时间格式统一 |
 | SC-P-18 | 家长 | 业务域 C:五维能量与健康 | 能量流水查看 | energy.ledger.view | RED | 未跑通 | S-cfc-265;能量流水(任务/打卡/测评),时间格式统一 |
 | SC-P-19 | 家长 | 业务域 C:五维能量与健康 | 能量维度配置查看 | energy.dimension.view | RED | 未跑通 | S-cfc-266;五维定义与权重只读,未同步展示兜底 |
 | SC-P-19 | 家长 | 业务域 C:五维能量与健康 | 能量维度配置查看 | energy.dimension.view | RED | 未跑通 | S-cfc-266;五维定义与权重只读,未同步展示兜底 |
-| SC-P-20 | 家长 | 业务域 C:五维能量与健康 | 健康报告 PDF 上传解析(上传报告)⭐ | — | — | 未跑通 | — |
-| SC-P-21 | 家长 | 业务域 C:五维能量与健康 | 报告类型识别 | — | — | 未跑通 | — |
-| SC-P-22 | 家长 | 业务域 C:五维能量与健康 | 报告解析预览 | — | — | 未跑通 | — |
-| SC-P-23 | 家长 | 业务域 C:五维能量与健康 | 报告确认入库 | — | — | 未跑通 | — |
-| SC-P-24 | 家长 | 业务域 C:五维能量与健康 | 多报告管理/对比 | — | — | 未跑通 | — |
-| SC-P-25 | 家长 | 业务域 C:五维能量与健康 | 报告趋势查看 | — | — | 未跑通 | — |
-| SC-P-26 | 家长 | 业务域 C:五维能量与健康 | 基于报告饮食推荐 | — | — | 未跑通 | — |
-| SC-P-27 | 家长 | 业务域 C:五维能量与健康 | 家庭食谱生成(一日三餐)⭐ 家庭食谱 | — | — | 未跑通 | — |
-| SC-P-28 | 家长 | 业务域 C:五维能量与健康 | 食谱按食材筛选/忌口 | — | — | 未跑通 | — |
-| SC-P-29 | 家长 | 业务域 C:五维能量与健康 | 食谱收藏/调整 | — | — | 未跑通 | — |
-| SC-P-30 | 家长 | 业务域 C:五维能量与健康 | 食谱与营养目标对齐 | — | — | 未跑通 | — |
-| SC-P-31 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 浏览测评类型(DAN 8 类) | — | — | 未跑通 | — |
-| SC-P-32 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 选择规划师 | — | — | 未跑通 | — |
-| SC-P-33 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 购买测评套餐/下单 | — | — | 未跑通 | — |
-| SC-P-34 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 测评订单支付 | — | — | 未跑通 | — |
-| SC-P-35 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 获得测评额度 | — | — | 未跑通 | — |
-| SC-P-36 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 创建预约(消耗额度)⭐ | — | — | 未跑通 | — |
-| SC-P-37 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 查看预约状态 | — | — | 未跑通 | — |
-| SC-P-38 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 取消预约(越权校验) | — | — | 未跑通 | — |
-| SC-P-39 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 查看测评结果(DAN 报告) | — | — | 未跑通 | — |
-| SC-P-40 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 生成 AI 方案(一键生成)⭐ 生成方案 | — | — | 未跑通 | — |
-| SC-P-41 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 方案分板块重生成 | — | — | 未跑通 | — |
-| SC-P-42 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 方案编辑/调整 | — | — | 未跑通 | — |
-| SC-P-43 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 查看生成的方案 | — | — | 未跑通 | — |
-| SC-P-44 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 确认方案(送审/家长确认)⭐ 确认方案 | — | — | 未跑通 | — |
-| SC-P-45 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 方案直接发布 | — | — | 未跑通 | — |
-| SC-P-46 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 激活方案生成任务(生成任务)⭐ 生成任务 | — | — | 未跑通 | — |
-| SC-P-47 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 查看方案下任务列表 | — | — | 未跑通 | — |
-| SC-P-48 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 方案版本/历史 | — | — | 未跑通 | — |
-| SC-P-49 | 家长 | 业务域 D:测评与成长规划价值链(核心) | AI 健康管家对话 ⭐ | — | — | 未跑通 | — |
-| SC-P-50 | 家长 | 业务域 E:成长记录与档案 | 成长记录查看 | — | — | 未跑通 | — |
-| SC-P-51 | 家长 | 业务域 E:成长记录与档案 | 成长里程碑 | — | — | 未跑通 | — |
+| SC-P-20 | 家长 | 业务域 C:五维能量与健康 | 健康报告 PDF 上传解析(上传报告)⭐ | report.upload.parse | RED | 未跑通 | 上传+识别 gut_flora+三阶段预览/确认,非募极报告提示,失败进未知聚类 |
+| SC-P-21 | 家长 | 业务域 C:五维能量与健康 | 报告类型识别 | report.upload.parse | RED | 未跑通 | 自动判别报告种类,未知类型进未知聚类待运营自学习 |
+| SC-P-22 | 家长 | 业务域 C:五维能量与健康 | 报告解析预览 | report.upload.parse | RED | 未跑通 | 菌群健康分/平衡/多样性/菌种明细/疾病风险预览,异常提示不入库 |
+| SC-P-23 | 家长 | 业务域 C:五维能量与健康 | 报告确认入库 | report.upload.parse | RED | 未跑通 | 确认→档案+五维刷新,确认前可取消 |
+| SC-P-24 | 家长 | 业务域 C:五维能量与健康 | 多报告管理/对比 | report.archive.manage | RED | 未跑通 | 列表/查看/删除/对比,删除有关联方案提示 |
+| SC-P-25 | 家长 | 业务域 C:五维能量与健康 | 报告趋势查看 | report.archive.manage | RED | 未跑通 | 同类型时间序列趋势图,单报告无趋势 |
+| SC-P-26 | 家长 | 业务域 C:五维能量与健康 | 基于报告饮食推荐 | diet.recommendation.byReport | RED | 未跑通 | 按菌群生成可吃/慎吃清单,无报告无推荐 |
+| SC-P-27 | 家长 | 业务域 C:五维能量与健康 | 家庭食谱生成(一日三餐)⭐ 家庭食谱 | diet.mealPlan.generate | RED | 未跑通 | 菌群报告+营养目标→早/午/晚食谱,无报告降级通用 |
+| SC-P-28 | 家长 | 业务域 C:五维能量与健康 | 食谱按食材筛选/忌口 | diet.mealPlan.generate | RED | 未跑通 | 忌口/过敏→食谱重算排除项,冲突提示 |
+| SC-P-29 | 家长 | 业务域 C:五维能量与健康 | 食谱收藏/调整 | diet.mealPlan.adjust | RED | 未跑通 | 收藏+手动微调替换食材,不破坏营养目标 |
+| SC-P-30 | 家长 | 业务域 C:五维能量与健康 | 食谱与营养目标对齐 | diet.mealPlan.adjust | RED | 未跑通 | 营养合计 vs 目标,超标/不足提示 |
+| SC-P-31 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 浏览测评类型(DAN 8 类) | assessment.type.browse | RED | 未跑通 | S-cfc-273;DAN 8 类目来自后端字典 |
+| SC-P-32 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 选择规划师 | assessment.planner.select | RED | 未跑通 | S-cfc-274;测评选规划师,未绑定提示先绑定 |
+| SC-P-33 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 购买测评套餐/下单 | assessment.order.create | RED | 未跑通 | S-cfc-275;生成 AssessmentOrder(pending),套餐下架不可下单 |
+| SC-P-34 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 测评订单支付 | assessment.order.pay | RED | 未跑通 | S-cfc-276;支付 pending→paid,失败回滚不生成额度 |
+| SC-P-35 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 获得测评额度 | assessment.order.pay | RED | 未跑通 | S-cfc-276;支付成功生成额度(含有效期),过期不可预约 |
+| SC-P-36 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 创建预约(消耗额度)⭐ | assessment.appointment.create | RED | 未跑通 | S-cfc-277;先消耗额度→落预约,额度不足/过期不落 |
+| SC-P-37 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 查看预约状态 | assessment.appointment.create | RED | 未跑通 | S-cfc-277;状态机:待确认/已确认/已完成/已取消/未到 |
+| SC-P-38 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 取消预约(越权校验) | assessment.appointment.create | RED | 未跑通 | S-cfc-277;CFC-AP 越权取消被拒;取消不退款 |
+| SC-P-39 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 查看测评结果(DAN 报告) | assessment.result.view | RED | 未跑通 | S-cfc-278;八类报告,结果未录入不展示,量纲标注 |
+| SC-P-40 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 生成 AI 方案(一键生成)⭐ 生成方案 | plan.ai.generate | RED | 未跑通 | S-cfc-279;LangGraph(AiGateway) 生成 overview+三板块,未购会员提示,超时 Fallback 不 500 |
+| SC-P-41 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 方案分板块重生成 | plan.ai.generate | RED | 未跑通 | S-cfc-279;逐板块重生成,不丢其他板块 |
+| SC-P-42 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 方案编辑/调整 | plan.edit.adjust | RED | 未跑通 | S-cfc-280;编辑文本/任务项,状态标记已改 |
+| SC-P-43 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 查看生成的方案 | plan.edit.adjust | RED | 未跑通 | S-cfc-280;方案详情板块/任务,空方案不白屏 |
+| SC-P-44 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 确认方案(送审/家长确认)⭐ 确认方案 | plan.confirm.submit | RED | 未跑通 | S-cfc-281;家长确认/送审,generated→reviewed,确认幂等 |
+| SC-P-45 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 方案直接发布 | plan.confirm.submit | RED | 未跑通 | S-cfc-281;配置允许直发 active,需审核走审核流 |
+| SC-P-46 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 激活方案生成任务(生成任务)⭐ 生成任务 | teacher.plan.activate | RED | 未跑通 | 复用 S-cfc-180;激活 reviewed→active→生成引导+模板任务,幂等靠状态机 |
+| SC-P-47 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 查看方案下任务列表 | plan.task.list | RED | 未跑通 | S-cfc-282;方案关联任务列表(状态/进度),任务增删与方案同步 |
+| SC-P-48 | 家长 | 业务域 D:测评与成长规划价值链(核心) | 方案版本/历史 | plan.edit.adjust | RED | 未跑通 | S-cfc-280;版本列表对比,版本不可篡改 |
+| SC-P-49 | 家长 | 业务域 D:测评与成长规划价值链(核心) | AI 健康管家对话 ⭐ | ai.coach.consult | RED | 未跑通 | 复用 S-cfc-244;POST /api/ai/health-coach/send 多轮上下文 |
+| SC-P-50 | 家长 | 业务域 E:成长记录与档案 | 成长记录查看 | growth.history.view | GREEN | 未跑通 | 复用既有成长历史键;时间线(任务/测评/报告/打卡),缺失跳过 |
+| SC-P-51 | 家长 | 业务域 E:成长记录与档案 | 成长里程碑 | growth.milestone.mark | RED | 未跑通 | 系统/手动标里程碑→时间线高亮,重复标幂等 |
 | SC-P-52 | 家长 | 业务域 F:商城与会员 | 商品浏览/搜索 | shop.product.browse | RED | 未跑通 | — |
 | SC-P-52 | 家长 | 业务域 F:商城与会员 | 商品浏览/搜索 | shop.product.browse | RED | 未跑通 | — |
 | SC-P-53 | 家长 | 业务域 F:商城与会员 | 商品详情 | shop.product.browse | RED | 未跑通 | — |
 | SC-P-53 | 家长 | 业务域 F:商城与会员 | 商品详情 | shop.product.browse | RED | 未跑通 | — |
 | SC-P-54 | 家长 | 业务域 F:商城与会员 | 加购/下单 | shop.order.create | RED | 未跑通 | — |
 | SC-P-54 | 家长 | 业务域 F:商城与会员 | 加购/下单 | shop.order.create | RED | 未跑通 | — |
@@ -172,43 +172,43 @@ SC-G-02 / SC-G-06 / SC-P-03 / SC-P-10 / SC-P-11 共 5 个 SC 已于 2026-11-06 O
 | SC-P-58 | 家长 | 业务域 F:商城与会员 | 购买会员/套餐 | membership.plan.purchase, family.family.autoCreateForPurchase | YELLOW/RED | 未跑通 | — |
 | SC-P-58 | 家长 | 业务域 F:商城与会员 | 购买会员/套餐 | membership.plan.purchase, family.family.autoCreateForPurchase | YELLOW/RED | 未跑通 | — |
 | SC-P-59 | 家长 | 业务域 F:商城与会员 | 会员权益查看 | membership.plan.purchase | RED | 未跑通 | — |
 | SC-P-59 | 家长 | 业务域 F:商城与会员 | 会员权益查看 | membership.plan.purchase | RED | 未跑通 | — |
 | SC-P-60 | 家长 | 业务域 F:商城与会员 | 会员续费 | membership.plan.purchase | RED | 未跑通 | — |
 | SC-P-60 | 家长 | 业务域 F:商城与会员 | 会员续费 | membership.plan.purchase | RED | 未跑通 | — |
-| SC-P-61 | 家长 | 业务域 G:联系人与社交 | 联系人添加 | — | — | 未跑通 | — |
-| SC-P-62 | 家长 | 业务域 G:联系人与社交 | 联系人分组/导入 | — | — | 未跑通 | — |
-| SC-P-63 | 家长 | 业务域 G:联系人与社交 | 联系人详情/权限 | — | — | 未跑通 | — |
-| SC-P-64 | 家长 | 业务域 H:设置与通知 | 个人资料编辑 | — | — | 未跑通 | — |
-| SC-P-65 | 家长 | 业务域 H:设置与通知 | 消息通知/提醒设置 | — | — | 未跑通 | — |
-| SC-P-66 | 家长 | 业务域 H:设置与通知 | 隐私设置 | — | — | 未跑通 | — |
-| SC-P-67 | 家长 | 业务域 I:报告代传 / 精准营养 / 新健康入口 | 报告代传(家长替孩子/他人代传)⭐ | — | — | 未跑通 | — |
-| SC-P-68 | 家长 | 业务域 I:报告代传 / 精准营养 / 新健康入口 | 家庭天盘查看(全家聚合视图) | — | — | 未跑通 | — |
-| SC-P-69 | 家长 | 业务域 I:报告代传 / 精准营养 / 新健康入口 | 精准营养目标设定 | — | — | 未跑通 | — |
-| SC-P-70 | 家长 | 业务域 I:报告代传 / 精准营养 / 新健康入口 | 舌诊/情绪照片采集 | — | — | 未跑通 | — |
-| SC-P-71 | 家长 | 业务域 J:账号与数据主权 | 账号注销(家长注销自身) | — | — | 未跑通 | — |
-| SC-P-72 | 家长 | 业务域 J:账号与数据主权 | 个人/家庭数据导出(GDPR) | — | — | 未跑通 | — |
-| SC-P-73 | 家长 | 业务域 J:账号与数据主权 | 多孩子切换孩子上下文 | — | — | 未跑通 | — |
-| SC-P-74 | 家长 | 业务域 J:账号与数据主权 | 解绑/重新分配规划师 | — | — | 未跑通 | — |
-| SC-P-75 | 家长 | 业务域 K:营销 / 活动 / 优惠 | 优惠券领取(活动/分享) | — | — | 未跑通 | — |
-| SC-P-76 | 家长 | 业务域 K:营销 / 活动 / 优惠 | 活动报名(名额/并发)⭐ | — | — | 未跑通 | — |
-| SC-P-77 | 家长 | 业务域 K:营销 / 活动 / 优惠 | 活动打卡/助力(营销) | — | — | 未跑通 | — |
-| SC-P-78 | 家长 | 业务域 K:营销 / 活动 / 优惠 | 消息中心/通知查看 | — | — | 未跑通 | — |
-| SC-P-79 | 家长 | 业务域 K:营销 / 活动 / 优惠 | 推送/通知开关设置 | — | — | 未跑通 | — |
-| SC-P-80 | 家长 | 业务域 L:交易延伸 / 内容 / 多端 | 订阅/会员退款与发票 | — | — | 未跑通 | — |
-| SC-P-81 | 家长 | 业务域 L:交易延伸 / 内容 / 多端 | 方案/任务草稿保存与恢复 | — | — | 未跑通 | — |
-| SC-P-82 | 家长 | 业务域 L:交易延伸 / 内容 / 多端 | 全局搜索(报告/方案/任务/商品) | — | — | 未跑通 | — |
-| SC-P-83 | 家长 | 业务域 L:交易延伸 / 内容 / 多端 | 意见反馈/在线客服 | — | — | 未跑通 | — |
-| SC-P-84 | 家长 | 业务域 L:交易延伸 / 内容 / 多端 | 分享报告/方案给家人或规划师 | — | — | 未跑通 | — |
-| SC-P-85 | 家长 | 业务域 L:交易延伸 / 内容 / 多端 | 多端登录/会话互斥 | — | — | 未跑通 | — |
-| SC-P-86 | 家长 | 业务域 L:交易延伸 / 内容 / 多端 | 成长打卡榜/家庭排行 | — | — | 未跑通 | — |
-| SC-P-87 | 家长 | 业务域 C-2(家长):五维能量变化规则 | 任务完成触发五维能量按规则归因增长 | — | — | 未跑通 | — |
-| SC-P-88 | 家长 | 业务域 C-2(家长):五维能量变化规则 | 心愿兑换扣减五维能量(余额不为负) | — | — | 未跑通 | — |
-| SC-P-89 | 家长 | 业务域 C-2(家长):五维能量变化规则 | 手动发放能量 grant | — | — | 未跑通 | — |
-| SC-P-90 | 家长 | 业务域 C-2(家长):五维能量变化规则 | 五维每日获取上限 DailyLimit | — | — | 未跑通 | — |
-| SC-P-91 | 家长 | 业务域 C-2(家长):五维能量变化规则 | 健康报告入库刷新五维评分 | — | — | 未跑通 | — |
-| SC-P-92 | 家长 | 业务域 C-2(家长):五维能量变化规则 | 五维权重/来源比例配置变更后重算 | — | — | 未跑通 | — |
-| SC-P-93 | 家长 | 业务域 C-2(家长):五维能量变化规则 | 文章互动与活动参与触发五维能量 | — | — | 未跑通 | — |
+| SC-P-61 | 家长 | 业务域 G:联系人与社交 | 联系人添加 | contact.card.manage | RED | 未跑通 | S-cfc-283;姓名/电话/关系,重复号码去重 |
+| SC-P-62 | 家长 | 业务域 G:联系人与社交 | 联系人分组/导入 | contact.group.import | RED | 未跑通 | S-cfc-284;ContactImport 分组导入,格式错误跳过 |
+| SC-P-63 | 家长 | 业务域 G:联系人与社交 | 联系人详情/权限 | contact.card.manage | RED | 未跑通 | S-cfc-283;可见权限设置,越权查看被拒 |
+| SC-P-64 | 家长 | 业务域 H:设置与通知 | 个人资料编辑 | auth.profile.edit | RED | 未跑通 | S-cfc-285;昵称/头像/绑定手机,格式校验 |
+| SC-P-65 | 家长 | 业务域 H:设置与通知 | 消息通知/提醒设置 | notification.setting.config | RED | 未跑通 | S-cfc-286;通知开关/提醒时间,关键安全不可关闭 |
+| SC-P-66 | 家长 | 业务域 H:设置与通知 | 隐私设置 | privacy.setting.config | RED | 未跑通 | S-cfc-287;数据共享范围,最小权限保底 |
+| SC-P-67 | 家长 | 业务域 I:报告代传 / 精准营养 / 新健康入口 | 报告代传(家长替孩子/他人代传)⭐ | teacher.report.proxy | RED | 未跑通 | 复用 S-cfc-182;代传授权令牌,越权代传被拒 |
+| SC-P-68 | 家长 | 业务域 I:报告代传 / 精准营养 / 新健康入口 | 家庭天盘查看(全家聚合视图) | ai.coach.familyBoard | RED | 未跑通 | 复用 S-cfc-249;全家聚合视图,缺失降级展示 |
+| SC-P-69 | 家长 | 业务域 I:报告代传 / 精准营养 / 新健康入口 | 精准营养目标设定 | diet.nutrition.target | RED | 未跑通 | S-cfc-288;每日营养/热量目标,非法值拦截 |
+| SC-P-70 | 家长 | 业务域 I:报告代传 / 精准营养 / 新健康入口 | 舌诊/情绪照片采集 | tongue.emotion.capture | RED | 未跑通 | S-cfc-289;/api/mind/emotion/analyze 照片 DeepFace |
+| SC-P-71 | 家长 | 业务域 J:账号与数据主权 | 账号注销(家长注销自身) | auth.account.deactivate | RED | 未跑通 | S-cfc-290;身份验证→冷静期→归档/删除 |
+| SC-P-72 | 家长 | 业务域 J:账号与数据主权 | 个人/家庭数据导出(GDPR) | data.export.gdpr | RED | 未跑通 | S-cfc-291;申请→加密包→下载,脱敏+频次限制 |
+| SC-P-73 | 家长 | 业务域 J:账号与数据主权 | 多孩子切换孩子上下文 | family.childContext.switch | RED | 未跑通 | S-cfc-292;/api/family/member/switch 孩子间切换 |
+| SC-P-74 | 家长 | 业务域 J:账号与数据主权 | 解绑/重新分配规划师 | teacher.family.bind | RED | 未跑通 | 复用 S-cfc-190;解绑前处理在途方案/预约 |
+| SC-P-75 | 家长 | 业务域 K:营销 / 活动 / 优惠 | 优惠券领取(活动/分享) | shop.coupon.claim | RED | 未跑通 | S-cfc-293;领券入卡包,库存/已领拦截,幂等 |
+| SC-P-76 | 家长 | 业务域 K:营销 / 活动 / 优惠 | 活动报名(名额/并发)⭐ | activity.enroll.join | RED | 未跑通 | S-cfc-294;占名额,并发超卖拦截(CFC bug 已修) |
+| SC-P-77 | 家长 | 业务域 K:营销 / 活动 / 优惠 | 活动打卡/助力(营销) | activity.checkin.boost | RED | 未跑通 | S-cfc-295;进度累计,刷量/机器行为拦截 |
+| SC-P-78 | 家长 | 业务域 K:营销 / 活动 / 优惠 | 消息中心/通知查看 | notification.center.view | RED | 未跑通 | S-cfc-296;/api/notification/list 未读优先 LIMIT 100 |
+| SC-P-79 | 家长 | 业务域 K:营销 / 活动 / 优惠 | 推送/通知开关设置 | notification.setting.config | RED | 未跑通 | S-cfc-286;同 P-65 键,H/K 域开关设置 |
+| SC-P-80 | 家长 | 业务域 L:交易延伸 / 内容 / 多端 | 订阅/会员退款与发票 | membership.plan.refund | RED | 未跑通 | S-cfc-297;/api/product/order/refund/apply+发票 |
+| SC-P-81 | 家长 | 业务域 L:交易延伸 / 内容 / 多端 | 方案/任务草稿保存与恢复 | plan.draft.save | RED | 未跑通 | S-cfc-298;存草稿→恢复,有效期+冲突提示 |
+| SC-P-82 | 家长 | 业务域 L:交易延伸 / 内容 / 多端 | 全局搜索(报告/方案/任务/商品) | global.search.query | RED | 未跑通 | S-cfc-299;§4.59 未实现 /api/search/global,需新建 |
+| SC-P-83 | 家长 | 业务域 L:交易延伸 / 内容 / 多端 | 意见反馈/在线客服 | feedback.consult.submit | RED | 未跑通 | S-cfc-303;/api/health/feedback/submit 持久化+AI 解答 |
+| SC-P-84 | 家长 | 业务域 L:交易延伸 / 内容 / 多端 | 分享报告/方案给家人或规划师 | share.link.generate | RED | 未跑通 | S-cfc-300;/api/share/create 统一入口+qrcode |
+| SC-P-85 | 家长 | 业务域 L:交易延伸 / 内容 / 多端 | 多端登录/会话互斥 | auth.session.mutex | RED | 未跑通 | S-cfc-301;新设备踢下线/二次验证,退出清 token |
+| SC-P-86 | 家长 | 业务域 L:交易延伸 / 内容 / 多端 | 成长打卡榜/家庭排行 | growth.leaderboard.view | RED | 未跑通 | S-cfc-302;/api/game/leaderboard+/api/badges/ranking/family |
+| SC-P-87 | 家长 | 业务域 C-2(家长):五维能量变化规则 | 任务完成触发五维能量按规则归因增长 | energy.ruleEngine.apply | RED | 未跑通 | S-cfc-304;任务审批通过→EnergyRuleEngine 归因到对应维度+写 EnergyLog/EnergyBalance,重复审批幂等,未配置归因维度不 500 |
+| SC-P-88 | 家长 | 业务域 C-2(家长):五维能量变化规则 | 心愿兑换扣减五维能量(余额不为负) | energy.ruleEngine.apply | RED | 未跑通 | S-cfc-304;deduct-for-wish 扣减写负流水,余额不足拒不出现负余额,并发兑换幂等 |
+| SC-P-89 | 家长 | 业务域 C-2(家长):五维能量变化规则 | 手动发放能量 grant | energy.admin.adjust | RED | 未跑通 | S-cfc-305;家长/运营手动 grant 留痕,负额/超大额 400,非本家庭 403 |
+| SC-P-90 | 家长 | 业务域 C-2(家长):五维能量变化规则 | 五维每日获取上限 DailyLimit | energy.dailyLimit.guard | RED | 未跑通 | S-cfc-306;单维度单日触顶不累加次日重置,多来源共享日上限,跨时区以服务端日期为准 |
+| SC-P-91 | 家长 | 业务域 C-2(家长):五维能量变化规则 | 健康报告入库刷新五维评分 | energy.admin.adjust | RED | 未跑通 | S-cfc-305;报告确认→刷新 HealthDimensionScore(七维/六维)+概览/沙盘/score-history,重复入库幂等 |
+| SC-P-92 | 家长 | 业务域 C-2(家长):五维能量变化规则 | 五维权重/来源比例配置变更后重算 | energy.admin.adjust | RED | 未跑通 | S-cfc-305;configure/权重变更→概览/沙盘/天盘按新口径重算,权重和≠100% 报警,非 admin 改全局 403 |
+| SC-P-93 | 家长 | 业务域 C-2(家长):五维能量变化规则 | 文章互动与活动参与触发五维能量 | energy.ruleEngine.apply | RED | 未跑通 | S-cfc-304;发布/评论/活动参与按规则加对应维度能量,受 DailyLimit 约束,刷量风控拦截,内容下架回收 |
 | SC-P-94 | 家长 | 业务域(家长):成就与成长 | 我的成就「完成任务」→ 历史完成任务页面 | task.history.view | YELLOW | 未跑通 | 实现已在 origin/cfclub(ed8f1c347);三类流证据待补 |
 | SC-P-94 | 家长 | 业务域(家长):成就与成长 | 我的成就「完成任务」→ 历史完成任务页面 | task.history.view | YELLOW | 未跑通 | 实现已在 origin/cfclub(ed8f1c347);三类流证据待补 |
-| SC-P-95 | 家长 | 业务域(家长):成就与成长 | 我的成就「成长记录」→ 成长历史记录页(含今日成长打卡记录) | growth.history.view | YELLOW | 未跑通 | 实现已在 origin/cfclub(da2727f53);三类流证据待补 |
-| SC-P-96 | 家长 | 业务域(家长):成就与成长 | 我的成就固定五列显示 | achievement.grid.fiveCol | YELLOW | 未跑通 | 实现已在 origin/cfclub(e205656ee);三类流证据待补 |
-| SC-T-01 | 成长规划师 | 业务域 L:交易延伸 / 内容 / 多端 | 规划师登录 + 切回家长 | teacher.login.switch | RED | 未跑通 | — |
+| SC-P-95 | 家长 | 业务域(家长):成就与成长 | 我的成就「成长记录」→ 成长历史记录页(含今日成长打卡记录) | task.history.view | GREEN | 未跑通 | 复用 S-cfc-29;成就区「完成任务」→/api/tasks/history 分页列表,无记录空态 |
+| SC-P-96 | 家长 | 业务域(家长):成就与成长 | 我的成就固定五列显示 | growth.history.dayGroupView | YELLOW | 未跑通 | 复用 S-cfc-134;成就区「成长记录」→HealthCheckinController+GrowthController 聚合打卡按日期倒序/筛选/分页 |
+| SC-T-01 | 成长规划师 | 业务域 L:交易延伸 / 内容 / 多端 | 规划师登录 + 切回家长 | ui.profile.achievementLayout | RED | 未跑通 | S-cfc-307;成就区 5 卡单行五列等比排布不换行,团队/卡券中心布局不变 |
 | SC-T-02 | 成长规划师 | 业务域 L:交易延伸 / 内容 / 多端 | 绑定测试家庭 | teacher.family.bind | RED | 未跑通 | — |
 | SC-T-02 | 成长规划师 | 业务域 L:交易延伸 / 内容 / 多端 | 绑定测试家庭 | teacher.family.bind | RED | 未跑通 | — |
 | SC-T-03 | 成长规划师 | 业务域 L:交易延伸 / 内容 / 多端 | 代传报告 + 确认入库(上传报告)⭐ | teacher.report.proxy | RED | 未跑通 | — |
 | SC-T-03 | 成长规划师 | 业务域 L:交易延伸 / 内容 / 多端 | 代传报告 + 确认入库(上传报告)⭐ | teacher.report.proxy | RED | 未跑通 | — |
 | SC-T-04 | 成长规划师 | 业务域 L:交易延伸 / 内容 / 多端 | 方案审核列表 + 批准 | teacher.plan.review | RED | 未跑通 | — |
 | SC-T-04 | 成长规划师 | 业务域 L:交易延伸 / 内容 / 多端 | 方案审核列表 + 批准 | teacher.plan.review | RED | 未跑通 | — |