Bläddra i källkod

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

E2E Test Bot 6 dagar sedan
förälder
incheckning
a31085e8c7
22 ändrade filer med 1355 tillägg och 121 borttagningar
  1. 48 0
      cfc-backend/src/main/java/com/etotem/cfc/service/EnergyService.java
  2. 97 48
      cfc-backend/src/main/java/com/etotem/cfc/service/impl/DimensionScoreServiceImpl.java
  3. 25 0
      cfc-backend/src/main/resources/db/migrate_dimension_unified_rollback.sql
  4. 70 13
      cfc-backend/src/test/java/com/etotem/cfc/integration/service/DimensionScoreServiceTest.java
  5. 33 3
      cfc-backend/src/test/java/com/etotem/cfc/unit/ReportConfirmRefreshFiveDimContractTest.java
  6. 29 3
      cfc-frontend/components/wuxing-sandbox.vue
  7. 26 0
      cfc-frontend/composables/useDimensionRegistry.js
  8. 76 33
      cfc-frontend/pages/health-main/index.vue
  9. 1 1
      cfc-web/.last_build_commit
  10. 1 1
      cfc-web/package.json
  11. 44 0
      cfc-web/public/CHANGELOG-v1.0.md
  12. 45 1
      cfc-web/public/CHANGELOG.md
  13. 45 2
      docs/2026-11-06-华为运动健康开放平台接入方案.md
  14. 16 16
      docs/superpowers/specs/2026-11-07-referral-team-profit-sharing.md
  15. 0 0
      docs/tdd/orchestration/pending.md
  16. 176 0
      docs/tdd/orchestration/report-dev-2026-11-07-1245.md
  17. 166 0
      docs/tdd/orchestration/report-dev-2026-11-07-1315.md
  18. 150 0
      docs/tdd/orchestration/report-dev-2026-11-07-1330.md
  19. 82 0
      docs/需求分析/2026-11-06-优惠券已领取券按钮修复需求变更.md
  20. 74 0
      docs/需求分析/2026-11-06-我的团队收益口径与邀请分享需求变更.md
  21. 71 0
      docs/需求分析/2026-11-06-报告上传页关联商品卡片需求变更.md
  22. 80 0
      docs/需求分析/2026-11-07-商城价格排序需求变更.md

+ 48 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/EnergyService.java

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.etotem.cfc.dto.CfBalanceView;
 import com.etotem.cfc.dto.EnergySandboxDTO;
 import com.etotem.cfc.dto.MemberEnergyDTO;
+import com.etotem.cfc.dto.DimensionNode;
 import com.etotem.cfc.entity.*;
 import com.etotem.cfc.mapper.*;
 import com.alibaba.fastjson.JSON;
@@ -93,6 +94,10 @@ public class EnergyService {
     @Resource
     private FamilyMetricAggConfigMapper familyMetricAggConfigMapper;
 
+    /** 统一维度注册中心:五维根节点五行归属(wuxing_rule_ref)的权威来源 */
+    @Resource
+    private DimensionRegistryService dimensionRegistryService;
+
     /**
      * 计算整个家庭的能量沙盘数据
      *
@@ -2279,10 +2284,53 @@ public class EnergyService {
                     new LambdaQueryWrapper<EnergyDimension>()
                             .orderByAsc(EnergyDimension::getSortOrder)
             );
+            // 统一维度体系:五维五行归属改以注册中心为准(root 节点 wuxing_rule_ref,与旧
+            // energy_dimension.element 同值,迁移448 已对齐)。注册中心未就绪或维度缺失时
+            // 保留旧表值,保证五行回归结果不变、返回结构不变。
+            enrichWuxingElementFromRegistry(dimCache);
         }
         return dimCache;
     }
 
+    /**
+     * 统一维度体系:按注册中心 5 根节点的 wuxing_rule_ref 覆盖五行元素归属(element)。
+     *
+     * <p>维度 code(body/mind/wisdom/action/wealth)与统一 5 根同码,无需 alias 翻译;
+     * id/name/icon/sortOrder/status 仍以 energy_dimension 表为准(energy_log/energy_balance
+     * 以 dimension_id 关联该表 id,切换仅限五行归属来源,保证回归不变)。</p>
+     *
+     * @param dims 已加载的旧表维度列表(就地更新 element)
+     */
+    private void enrichWuxingElementFromRegistry(List<EnergyDimension> dims) {
+        if (dims == null || dims.isEmpty() || dimensionRegistryService == null) {
+            return;
+        }
+        try {
+            List<DimensionNode> roots = dimensionRegistryService.getRoots();
+            if (roots == null || roots.isEmpty()) {
+                return;
+            }
+            Map<String, String> elementByCode = new HashMap<>();
+            for (DimensionNode root : roots) {
+                if (root == null || root.getCode() == null || root.getWuxingRuleRef() == null) {
+                    continue;
+                }
+                elementByCode.put(root.getCode(), root.getWuxingRuleRef());
+            }
+            for (EnergyDimension d : dims) {
+                if (d == null || d.getCode() == null) {
+                    continue;
+                }
+                String element = elementByCode.get(d.getCode());
+                if (element != null) {
+                    d.setElement(element);
+                }
+            }
+        } catch (Exception e) {
+            log.warn("EnergyService.enrichWuxingElementFromRegistry: 注册中心五行归属补齐失败(忽略): {}", e.getMessage());
+        }
+    }
+
     /**
      * 按code查维度
      */

+ 97 - 48
cfc-backend/src/main/java/com/etotem/cfc/service/impl/DimensionScoreServiceImpl.java

@@ -3,6 +3,9 @@ package com.etotem.cfc.service.impl;
 import com.etotem.cfc.dto.HealthDimensionVO;
 import com.etotem.cfc.dto.DimensionUploadVO;
 import com.etotem.cfc.dto.HealthDimensionQuestionnaireVO;
+import com.etotem.cfc.dto.DimensionNode;
+import com.etotem.cfc.service.DimensionRegistryService;
+import com.etotem.cfc.service.DimensionCodeMapper;
 import com.etotem.cfc.entity.HealthDimensionScore;
 import com.etotem.cfc.entity.HealthDataSourceRecord;
 import com.etotem.cfc.entity.HealthGutFlora;
@@ -38,15 +41,11 @@ import java.util.stream.Collectors;
 @Service
 public class DimensionScoreServiceImpl implements DimensionScoreService {
 
-    private static final List<String[]> DIMENSIONS = Arrays.asList(
-        new String[]{"growth",    "生长发育"},
-        new String[]{"sleep",     "睡眠质量"},
-        new String[]{"vision",    "视力健康"},
-        new String[]{"immunity",  "免疫力"},
-        new String[]{"nutrition", "营养均衡"},
-        new String[]{"gut",       "肠胃健康"},
-        new String[]{"exercise",  "运动活力"}
-    );
+    @Resource
+    private DimensionRegistryService dimensionRegistryService;
+
+    @Resource
+    private DimensionCodeMapper dimensionCodeMapper;
 
     @Resource
     private HealthDimensionScoreService scoreService;
@@ -76,20 +75,57 @@ public class DimensionScoreServiceImpl implements DimensionScoreService {
     @Resource
     private DimensionEnergySyncService dimensionEnergySyncService;
 
+    /**
+     * 统一维度体系中 7 个身体健康子维度(code=body.growth...body.exercise),来源为注册中心
+     * body 根节点子维度(nodeKind=sub),替代原前端/后端硬编码裸名单(growth/sleep/...)。
+     *
+     * <p>顺序与注册中心 seed 一致(growth/sleep/vision/immunity/nutrition/gut/exercise),
+     * 指标节点(nodeKind=indicator,P1-T3 起混排挂载)不作为健康雷达轴,跳过。</p>
+     *
+     * @return 注册中心 body 根节点的子维度节点列表;未就绪时返回空列表(调用方按空处理)
+     */
+    private List<DimensionNode> bodyDimensions() {
+        DimensionNode body = dimensionRegistryService.getNodeByCode("body");
+        if (body == null || body.getChildren() == null) {
+            return new ArrayList<>();
+        }
+        List<DimensionNode> result = new ArrayList<>();
+        for (DimensionNode child : body.getChildren()) {
+            if (child == null) {
+                continue;
+            }
+            if ("indicator".equals(child.getNodeKind())) {
+                continue;
+            }
+            result.add(child);
+        }
+        return result;
+    }
+
     @Override
     public HealthDimensionVO getHealthDimension(Long memberId) {
         List<HealthDimensionScore> latest = scoreService.getLatestScores(memberId);
         Map<String, HealthDimensionScore> scoreMap = latest.stream()
             .collect(Collectors.toMap(HealthDimensionScore::getDimension, s -> s, (a, b) -> a));
 
+        // 统一维度体系整改:维度清单来源改为注册中心(body 根下子维度,统一 code=body.growth...),
+        // 兼容 DB 仍存旧裸名(growth/sleep/...)的过渡态:scoreMap 同时按 legacy 原文 + 统一码建索引。
+        Map<String, HealthDimensionScore> unifiedScoreMap = new HashMap<>(scoreMap);
+        for (Map.Entry<String, HealthDimensionScore> e : scoreMap.entrySet()) {
+            String unified = dimensionRegistryService.resolveLegacyToUnified(e.getKey());
+            if (unified != null) {
+                unifiedScoreMap.putIfAbsent(unified, e.getValue());
+            }
+        }
+
         List<HealthDimensionVO.DimensionItem> items = new ArrayList<>();
         BigDecimal totalScore = BigDecimal.ZERO;
         int validCount = 0;
 
-        for (String[] dim : DIMENSIONS) {
-            String code = dim[0];
-            String label = dim[1];
-            HealthDimensionScore found = scoreMap.get(code);
+        for (DimensionNode dim : bodyDimensions()) {
+            String code = dim.getCode();
+            String label = dim.getName();
+            HealthDimensionScore found = unifiedScoreMap.get(code);
 
             HealthDimensionVO.DimensionItem item = new HealthDimensionVO.DimensionItem();
             item.setDimension(code);
@@ -101,7 +137,7 @@ public class DimensionScoreServiceImpl implements DimensionScoreService {
                 item.setDataSource(found.getDataSource());
                 item.setTier(found.getTier());
                 item.setLevel(calcLevel(found.getScore()));
-                List<HealthDimensionVO.DimensionItem> history = getDimensionHistory(memberId, code, 2);
+                List<HealthDimensionVO.DimensionItem> history = getDimensionHistory(memberId, resolveNormLegacy(code), 2);
                 if (history.size() >= 2) {
                     BigDecimal earlierScore = history.get(1).getScore();
                     BigDecimal latestScore = history.get(0).getScore();
@@ -144,7 +180,7 @@ public class DimensionScoreServiceImpl implements DimensionScoreService {
                 String gender = (member.getGender() != null && !member.getGender().isEmpty())
                     ? member.getGender() : "all";
                 for (HealthDimensionVO.DimensionItem item : items) {
-                    String dimCode = item.getDimension();
+                    String dimCode = resolveNormLegacy(item.getDimension());
                     HealthNormReference norm = normMapper.selectNorm(dimCode, gender, (int) ageMonths);
                     if (norm != null && norm.getPercentile50() != null) {
                         item.setScore(BigDecimal.valueOf(norm.getPercentile50()));
@@ -400,47 +436,47 @@ public class DimensionScoreServiceImpl implements DimensionScoreService {
             List<HealthDiseaseRisk> diseaseRisks) {
 
         Map<String, List<Integer>> dimValues = new HashMap<>();
-        for (String[] dim : DIMENSIONS) {
-            dimValues.put(dim[0], new ArrayList<>());
+        for (DimensionNode dim : bodyDimensions()) {
+            dimValues.put(dim.getCode(), new ArrayList<>());
         }
 
         if (report.getGutHealthScore() != null) {
-            dimValues.get("immunity").add(report.getGutHealthScore());
-            dimValues.get("gut").add(report.getGutHealthScore());
+            dimValues.get("body.immunity").add(report.getGutHealthScore());
+            dimValues.get("body.gut").add(report.getGutHealthScore());
         }
         if (report.getChronicDiseaseScore() != null) {
             int inverted = 100 - report.getChronicDiseaseScore();
-            dimValues.get("growth").add(inverted);
-            dimValues.get("nutrition").add(inverted);
+            dimValues.get("body.growth").add(inverted);
+            dimValues.get("body.nutrition").add(inverted);
         }
         if (report.getNutritionScore() != null) {
-            dimValues.get("nutrition").add(report.getNutritionScore());
+            dimValues.get("body.nutrition").add(report.getNutritionScore());
         }
         if (report.getGutBalanceScore() != null) {
-            dimValues.get("gut").add(report.getGutBalanceScore());
+            dimValues.get("body.gut").add(report.getGutBalanceScore());
         }
         if (report.getGutDiversityScore() != null) {
-            dimValues.get("immunity").add(report.getGutDiversityScore());
-            dimValues.get("gut").add(report.getGutDiversityScore());
+            dimValues.get("body.immunity").add(report.getGutDiversityScore());
+            dimValues.get("body.gut").add(report.getGutDiversityScore());
         }
         if (report.getBeneficialBacteriaScore() != null) {
-            dimValues.get("immunity").add(report.getBeneficialBacteriaScore());
-            dimValues.get("gut").add(report.getBeneficialBacteriaScore());
+            dimValues.get("body.immunity").add(report.getBeneficialBacteriaScore());
+            dimValues.get("body.gut").add(report.getBeneficialBacteriaScore());
         }
         if (report.getHarmfulBacteriaScore() != null) {
             int inverted = 100 - report.getHarmfulBacteriaScore();
-            dimValues.get("immunity").add(inverted);
-            dimValues.get("gut").add(inverted);
+            dimValues.get("body.immunity").add(inverted);
+            dimValues.get("body.gut").add(inverted);
         }
         if (report.getCoreSpeciesScore() != null) {
-            dimValues.get("gut").add(report.getCoreSpeciesScore());
+            dimValues.get("body.gut").add(report.getCoreSpeciesScore());
         }
         if (report.getGutAge() != null) {
             try {
                 int gutAgeYears = Integer.parseInt(report.getGutAge().replaceAll("[^0-9]", ""));
                 int gutAgeScore = Math.max(0, Math.min(100, 100 - gutAgeYears * 2));
-                dimValues.get("sleep").add(gutAgeScore);
-                dimValues.get("gut").add(gutAgeScore);
+                dimValues.get("body.sleep").add(gutAgeScore);
+                dimValues.get("body.gut").add(gutAgeScore);
             } catch (NumberFormatException ignored) {}
         }
 
@@ -452,15 +488,15 @@ public class DimensionScoreServiceImpl implements DimensionScoreService {
             if (val < 0) continue;
 
             if (cat.contains("神经递质")) {
-                dimValues.get("sleep").add(val);
+                dimValues.get("body.sleep").add(val);
             } else if ("短链脂肪酸".equals(cat)) {
-                dimValues.get("nutrition").add(val);
-                dimValues.get("gut").add(val);
+                dimValues.get("body.nutrition").add(val);
+                dimValues.get("body.gut").add(val);
             } else if ("肠道屏障".equals(cat)) {
-                dimValues.get("immunity").add(val);
-                dimValues.get("gut").add(val);
+                dimValues.get("body.immunity").add(val);
+                dimValues.get("body.gut").add(val);
             } else if (cat.contains("维生素") || cat.contains("矿物质") || cat.contains("微量")) {
-                dimValues.get("nutrition").add(val);
+                dimValues.get("body.nutrition").add(val);
             }
         }
 
@@ -476,21 +512,21 @@ public class DimensionScoreServiceImpl implements DimensionScoreService {
             int invertedScore = 100 - Math.min(riskVal, 100);
             String name = risk.getDiseaseName();
             if (name != null && (name.contains("代谢") || name.contains("肥胖") || name.contains("超重"))) {
-                dimValues.get("growth").add(invertedScore);
-                dimValues.get("nutrition").add(invertedScore);
+                dimValues.get("body.growth").add(invertedScore);
+                dimValues.get("body.nutrition").add(invertedScore);
             }
             if (name != null && (name.contains("感染") || name.contains("炎症"))) {
-                dimValues.get("immunity").add(invertedScore);
+                dimValues.get("body.immunity").add(invertedScore);
             }
-            dimValues.get("gut").add(invertedScore);
+            dimValues.get("body.gut").add(invertedScore);
         }
 
         Map<String, Integer> result = new HashMap<>();
-        for (String[] dim : DIMENSIONS) {
-            List<Integer> vals = dimValues.get(dim[0]);
+        for (DimensionNode dim : bodyDimensions()) {
+            List<Integer> vals = dimValues.get(dim.getCode());
             if (vals != null && !vals.isEmpty()) {
                 int avg = (int) vals.stream().mapToInt(v -> v).average().orElse(0);
-                result.put(dim[0], Math.min(100, Math.max(0, avg)));
+                result.put(dim.getCode(), Math.min(100, Math.max(0, avg)));
             }
         }
         return result;
@@ -507,8 +543,21 @@ public class DimensionScoreServiceImpl implements DimensionScoreService {
         }
     }
 
+    private String resolveNormLegacy(String dimension) {
+        if (dimension == null || dimension.isEmpty()) {
+            return dimension;
+        }
+        List<String> legacyCodes = dimensionCodeMapper.resolveLegacy(dimension);
+        if (legacyCodes != null && !legacyCodes.isEmpty()) {
+            return legacyCodes.get(0);
+        }
+        return dimension;
+    }
+
     private Integer calcPercentile(String dimension, int score) {
-        HealthNormReference norm = normMapper.selectNorm(dimension, "all", 120);
+        // 统一维度体系:常模表按 legacy 维度码(growth/...)建索引,统一 code(body.growth)需先回解析
+        String normDimension = resolveNormLegacy(dimension);
+        HealthNormReference norm = normMapper.selectNorm(normDimension, "all", 120);
         if (norm == null) return null;
         if (norm.getPercentile95() != null && score >= norm.getPercentile95()) return 95;
         if (norm.getPercentile85() != null && score >= norm.getPercentile85()) return 85;
@@ -551,8 +600,8 @@ public class DimensionScoreServiceImpl implements DimensionScoreService {
                 return norm.getPercentile50();
             }
         } else {
-            // 身体/健康维度走 health_norm_reference 表
-            HealthNormReference norm = normMapper.selectNorm(dimension, gender, (int) ageMonths);
+            // 身体/健康维度走 health_norm_reference 表(常模表按 legacy 码建索引,统一 code 需回解析)
+            HealthNormReference norm = normMapper.selectNorm(resolveNormLegacy(dimension), gender, (int) ageMonths);
             if (norm != null) {
                 return norm.getPercentile50();
             }

+ 25 - 0
cfc-backend/src/main/resources/db/migrate_dimension_unified_rollback.sql

@@ -0,0 +1,25 @@
+-- ============================================================
+-- 统一维度体系 P0-T5 回滚脚本
+-- 作用:将 health_dimension_scores.dimension 由 body.<old> 反向还原为旧 7 维裸名
+--       (与主线 forward migrate_dimension_unified.sql 精确成对)
+-- 执行方式:由 DBA 手动执行
+-- 幂等:UPDATE 仅命中 7 个 body.* 子维,重复执行无副作用(affected rows=0)
+-- 备份表:health_dimension_scores_bak_20261105(与 forward 步骤 1 同名同日戳)
+-- ============================================================
+
+-- 主回滚(精确逆反 forward 步骤 2):body.{sub} → {sub},仅命中已迁移的 7 行
+UPDATE health_dimension_scores
+   SET dimension = SUBSTRING(dimension, 6)
+ WHERE dimension IN (
+        'body.growth',
+        'body.sleep',
+        'body.vision',
+        'body.immunity',
+        'body.nutrition',
+        'body.gut',
+        'body.exercise'
+      );
+
+-- 备选整体还原(仅在确认需整体回退时执行;会覆盖当前表,请先确认无新增数据):
+-- TRUNCATE health_dimension_scores;
+-- INSERT INTO health_dimension_scores SELECT * FROM health_dimension_scores_bak_20261105;

+ 70 - 13
cfc-backend/src/test/java/com/etotem/cfc/integration/service/DimensionScoreServiceTest.java

@@ -3,8 +3,11 @@ package com.etotem.cfc.service;
 import com.etotem.cfc.dto.DimensionUploadVO;
 import com.etotem.cfc.dto.HealthDimensionQuestionnaireVO;
 import com.etotem.cfc.dto.HealthDimensionVO;
+import com.etotem.cfc.dto.DimensionNode;
 import com.etotem.cfc.entity.*;
 import com.etotem.cfc.mapper.HealthNormReferenceMapper;
+import com.etotem.cfc.service.DimensionRegistryService;
+import com.etotem.cfc.service.DimensionCodeMapper;
 import com.etotem.cfc.service.impl.DimensionScoreServiceImpl;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -14,6 +17,7 @@ import org.mockito.MockitoAnnotations;
 
 import java.math.BigDecimal;
 import java.sql.Date;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -57,6 +61,12 @@ public class DimensionScoreServiceTest {
     @Mock
     private DimensionEnergySyncService dimensionEnergySyncService;
 
+    @Mock
+    private DimensionRegistryService dimensionRegistryService;
+
+    @Mock
+    private DimensionCodeMapper dimensionCodeMapper;
+
     private DimensionScoreServiceImpl service;
 
     @BeforeEach
@@ -71,6 +81,52 @@ public class DimensionScoreServiceTest {
         setField(service, "familyMemberMapper", familyMemberMapper);
         setField(service, "danAssessmentResultMapper", danAssessmentResultMapper);
         setField(service, "dimensionEnergySyncService", dimensionEnergySyncService);
+        setField(service, "dimensionRegistryService", dimensionRegistryService);
+        setField(service, "dimensionCodeMapper", dimensionCodeMapper);
+        // 统一维度体系:注册中心 body 根节点 → 7 个健康子维度(统一码 body.growth...body.exercise)
+        mockBodyDimensions();
+    }
+
+    /** 构造注册中心 body 根下的一个子维度节点(nodeKind=sub)。 */
+    private DimensionNode dimNode(String code, String name) {
+        DimensionNode n = new DimensionNode();
+        n.setCode(code);
+        n.setName(name);
+        n.setNodeKind("sub");
+        return n;
+    }
+
+    /**
+     * Mock 注册中心:body 根节点挂载 7 个健康子维度(统一码),并 stub
+     * legacy→统一码 解析(兼容 DB 存旧裸名 growth/... 的过渡态)与
+     * 统一码→legacy 回解析(常模/历史表按 legacy 裸名建索引)。
+     */
+    private void mockBodyDimensions() {
+        DimensionNode body = new DimensionNode();
+        body.setCode("body");
+        body.setName("身体健康");
+        body.setNodeKind("root");
+        List<DimensionNode> children = new ArrayList<>();
+        children.add(dimNode("body.growth", "生长发育"));
+        children.add(dimNode("body.sleep", "睡眠质量"));
+        children.add(dimNode("body.vision", "视力健康"));
+        children.add(dimNode("body.immunity", "免疫力"));
+        children.add(dimNode("body.nutrition", "营养均衡"));
+        children.add(dimNode("body.gut", "肠胃健康"));
+        children.add(dimNode("body.exercise", "运动活力"));
+        body.setChildren(children);
+        when(dimensionRegistryService.getNodeByCode("body")).thenReturn(body);
+        // legacy 裸名 → 统一码(getHealthDimension 构建 unifiedScoreMap 时使用)
+        when(dimensionRegistryService.resolveLegacyToUnified("growth")).thenReturn("body.growth");
+        when(dimensionRegistryService.resolveLegacyToUnified("sleep")).thenReturn("body.sleep");
+        when(dimensionRegistryService.resolveLegacyToUnified("vision")).thenReturn("body.vision");
+        when(dimensionRegistryService.resolveLegacyToUnified("immunity")).thenReturn("body.immunity");
+        when(dimensionRegistryService.resolveLegacyToUnified("nutrition")).thenReturn("body.nutrition");
+        when(dimensionRegistryService.resolveLegacyToUnified("gut")).thenReturn("body.gut");
+        when(dimensionRegistryService.resolveLegacyToUnified("exercise")).thenReturn("body.exercise");
+        // 统一码 → legacy 回解析:默认返回空(resolveNormLegacy 原样返回入参),
+        // 使 calcPercentile/趋势历史仍按 legacy 裸名访问常模/历史表
+        when(dimensionCodeMapper.resolveLegacy(anyString())).thenReturn(Collections.emptyList());
     }
 
     private void setField(Object target, String fieldName, Object value) {
@@ -131,10 +187,10 @@ public class DimensionScoreServiceTest {
 
         HealthDimensionVO vo = service.getHealthDimension(1L);
 
-assertLevel(vo, "growth", "excellent");
-        assertLevel(vo, "sleep", "good");
-        assertLevel(vo, "vision", "fair");
-        assertLevel(vo, "immunity", "poor");
+        assertLevel(vo, "body.growth", "excellent");
+        assertLevel(vo, "body.sleep", "good");
+        assertLevel(vo, "body.vision", "fair");
+        assertLevel(vo, "body.immunity", "poor");
         assertEquals(new BigDecimal("63.75"), vo.getHealthIndex());
     }
 
@@ -147,7 +203,7 @@ assertLevel(vo, "growth", "excellent");
 
         HealthDimensionVO vo = service.getHealthDimension(1L);
 
-        HealthDimensionVO.DimensionItem item = findItem(vo, "growth");
+        HealthDimensionVO.DimensionItem item = findItem(vo, "body.growth");
         assertNotNull(item.getExpireDate());
         assertEquals(10, item.getExpireDate().length()); // yyyy-MM-dd = 10
     }
@@ -288,7 +344,7 @@ assertLevel(vo, "growth", "excellent");
 
         HealthDimensionVO vo = service.getHealthDimension(1L);
 
-        assertLevel(vo, "growth", "excellent");
+        assertLevel(vo, "body.growth", "excellent");
     }
 
     @Test
@@ -298,7 +354,7 @@ assertLevel(vo, "growth", "excellent");
 
         HealthDimensionVO vo = service.getHealthDimension(1L);
 
-        assertLevel(vo, "growth", "good");
+        assertLevel(vo, "body.growth", "good");
     }
 
     @Test
@@ -308,7 +364,7 @@ assertLevel(vo, "growth", "excellent");
 
         HealthDimensionVO vo = service.getHealthDimension(1L);
 
-        assertLevel(vo, "growth", "fair");
+        assertLevel(vo, "body.growth", "fair");
     }
 
     @Test
@@ -318,7 +374,7 @@ assertLevel(vo, "growth", "excellent");
 
         HealthDimensionVO vo = service.getHealthDimension(1L);
 
-        assertLevel(vo, "growth", "poor");
+        assertLevel(vo, "body.growth", "poor");
     }
 
     // ── calcPercentile (via uploadScore) ────────────────────────────────────
@@ -393,9 +449,9 @@ assertLevel(vo, "growth", "excellent");
         verify(scoreService, atLeast(1)).saveScore(cap.capture());
 
         boolean hasGut = cap.getAllValues().stream()
-            .anyMatch(s -> "gut".equals(s.getDimension()) && s.getScore() == 80);
+            .anyMatch(s -> "body.gut".equals(s.getDimension()) && s.getScore() == 80);
         boolean hasImmunity = cap.getAllValues().stream()
-            .anyMatch(s -> "immunity".equals(s.getDimension()) && s.getScore() == 80);
+            .anyMatch(s -> "body.immunity".equals(s.getDimension()) && s.getScore() == 80);
         assertTrue(hasGut, "gut dimension should be set from gutHealthScore");
         assertTrue(hasImmunity, "immunity dimension should be set from gutHealthScore");
     }
@@ -416,7 +472,8 @@ assertLevel(vo, "growth", "excellent");
         HealthDimensionVO vo = service.getHealthDimension(1L);
 
         List<String> expected = Arrays.asList(
-            "growth", "sleep", "vision", "immunity", "nutrition", "gut", "exercise");
+            "body.growth", "body.sleep", "body.vision", "body.immunity",
+            "body.nutrition", "body.gut", "body.exercise");
         List<String> expectedLabels = Arrays.asList(
             "生长发育", "睡眠质量", "视力健康", "免疫力", "营养均衡", "肠胃健康", "运动活力");
         List<String> actual = new java.util.ArrayList<>();
@@ -442,7 +499,7 @@ assertLevel(vo, "growth", "excellent");
 
         // 实现中即使过期也显示该分数(只按 found!=null 判断)
         // 这个测试验证实现行为:即使过期也有分
-        assertNotNull(findItem(vo, "growth").getScore());
+        assertNotNull(findItem(vo, "body.growth").getScore());
     }
 
     // ── Helper ─────────────────────────────────────────────────────────────

+ 33 - 3
cfc-backend/src/test/java/com/etotem/cfc/unit/ReportConfirmRefreshFiveDimContractTest.java

@@ -53,6 +53,36 @@ class ReportConfirmRefreshFiveDimContractTest {
         injectAll(service);
         healthReportService = (HealthReportService) readField(service, "healthReportService");
         scoreService = (HealthDimensionScoreService) readField(service, "scoreService");
+        mockBodyRegistry();
+    }
+
+    /**
+     * 统一维度体系:Mock 注册中心 body 根节点 → 7 个健康子维度(统一码 body.*),
+     * 使 mapGutFloraToDimensions 按注册中心维度清单取数。
+     */
+    private void mockBodyRegistry() throws Exception {
+        com.etotem.cfc.service.DimensionRegistryService registry =
+                (com.etotem.cfc.service.DimensionRegistryService) readField(service, "dimensionRegistryService");
+        String[][] dims = {
+                {"body.growth", "生长发育"}, {"body.sleep", "睡眠质量"},
+                {"body.vision", "视力健康"}, {"body.immunity", "免疫力"},
+                {"body.nutrition", "营养均衡"}, {"body.gut", "肠胃健康"},
+                {"body.exercise", "运动活力"}
+        };
+        List<com.etotem.cfc.dto.DimensionNode> children = new ArrayList<>();
+        for (String[] d : dims) {
+            com.etotem.cfc.dto.DimensionNode n = new com.etotem.cfc.dto.DimensionNode();
+            n.setCode(d[0]);
+            n.setName(d[1]);
+            n.setNodeKind("sub");
+            children.add(n);
+        }
+        com.etotem.cfc.dto.DimensionNode body = new com.etotem.cfc.dto.DimensionNode();
+        body.setCode("body");
+        body.setName("身体健康");
+        body.setNodeKind("root");
+        body.setChildren(children);
+        when(registry.getNodeByCode("body")).thenReturn(body);
     }
 
     private static Object readField(Object target, String name) throws Exception {
@@ -99,9 +129,9 @@ class ReportConfirmRefreshFiveDimContractTest {
             assertEquals(55L, s.getMemberId(), "五维分应写入指定成员");
             assertEquals("REPORT", s.getDataSource(), "报告确认来源应为 REPORT");
             assertEquals(Integer.valueOf(1), s.getTier(), "报告级数据 tier=1");
-            if ("gut".equals(s.getDimension())) hasGut = true;
-            if ("immunity".equals(s.getDimension())) hasImmunity = true;
-            if ("nutrition".equals(s.getDimension())) hasNutrition = true;
+            if ("body.gut".equals(s.getDimension())) hasGut = true;
+            if ("body.immunity".equals(s.getDimension())) hasImmunity = true;
+            if ("body.nutrition".equals(s.getDimension())) hasNutrition = true;
         }
         assertTrue(hasGut, "gut 肠胃健康应被派生");
         assertTrue(hasImmunity, "immunity 免疫力应被派生");

+ 29 - 3
cfc-frontend/components/wuxing-sandbox.vue

@@ -95,6 +95,11 @@ import { renderStar, calcStarVertices } from './wuxing-sandbox-helpers/render.js
 import { FillAnimator } from './wuxing-sandbox-helpers/animation.js'
 import { detectHitArm, armIndexToDomain } from './wuxing-sandbox-helpers/hitdetect.js'
 import { FlowAnimation } from './wuxing-sandbox-helpers/flow-animation.js'
+import {
+  ensureDimensionRegistry,
+  getRegistryRootMeta,
+  isRegistryReady
+} from '../composables/useDimensionRegistry.js'
 import {
   buildMemberOptions,
   findMemberById,
@@ -306,7 +311,9 @@ export default {
       _animator: null,
       _flowAnim: null,
       _flowCanvasReady: false,
-      uiIcons: UI_ICON_URIS
+      uiIcons: UI_ICON_URIS,
+      // 统一维度注册中心是否就绪:就绪后维度名称/五行元素兜底改经注册中心(wuxing_rule_ref/统一 code)
+      registryReady: false
     }
   },
   computed: {
@@ -417,16 +424,27 @@ export default {
         var d = this.dimensions[i]
         if (d && d.code) dimMap[d.code] = d
       }
+      // 统一维度体系:维度名称/五行元素兜底改经注册中心(root name / wuxing_rule_ref),
+      // 未就绪回退组件内 DIM_META(保持既有渲染,五行语义零改动)。
+      var regMeta = {}
+      if (this.registryReady) {
+        var roots = getRegistryRootMeta()
+        for (var r = 0; r < roots.length; r++) {
+          var root = roots[r]
+          if (root && root.code) regMeta[root.code] = root
+        }
+      }
       var all = []
       for (var j = 0; j < order.length; j++) {
         var code = order[j]
         var d = dimMap[code] || {}
+        var reg = regMeta[code] || {}
         var meta = DIM_META[code] || {}
         var score = scoreByCode[code] || 0
         all.push({
           code: code,
-          name: d.name || meta.name || '',
-          element: d.element || meta.element || '',
+          name: d.name || reg.name || meta.name || '',
+          element: d.element || reg.element || meta.element || '',
           energy: score,
           healthIndex: score,
           pointClass: RING_POS[j],
@@ -510,6 +528,14 @@ export default {
   mounted: function() {
     var self = this
 
+    // 统一维度体系:维度名称/五行元素兜底来源切换为注册中心(wuxing_rule_ref/统一 code),
+    // 幂等(模块级缓存单请求);未就绪时保持原有 DIM_META 兜底,五行语义零改动。
+    ensureDimensionRegistry().then(function() {
+      self.registryReady = isRegistryReady()
+    }).catch(function() {
+      self.registryReady = false
+    })
+
     // preview:轻量引导条,不初始化 Canvas、不画完整星图
     if (this.isPreview) return
 

+ 26 - 0
cfc-frontend/composables/useDimensionRegistry.js

@@ -155,9 +155,35 @@ function getRadarConfig(tabCode) {
   return config
 }
 
+/**
+ * 取注册中心 5 根节点元信息(五行归属):[{ code, name, element: wuxingRuleRef }]。
+ *
+ * <p>供五行能量沙盘维度名称 / 五行元素兜底(统一 code + wuxing_rule_ref),
+ * 未就绪返回空数组(调用方回落组件内 DIM_META)。</p>
+ *
+ * @returns {Array<{code:string,name:string,element:string}>}
+ */
+function getRegistryRootMeta() {
+  if (!_cache.tree || !Array.isArray(_cache.tree)) return []
+  var out = []
+  for (var i = 0; i < _cache.tree.length; i++) {
+    var node = _cache.tree[i]
+    if (!node || !node.code) continue
+    // 仅维度根节点(nodeKind=root;旧数据无 nodeKind 的按「无父节点」视为根)
+    if (node.nodeKind && node.nodeKind !== 'root') continue
+    out.push({
+      code: node.code,
+      name: node.name || '',
+      element: node.wuxingRuleRef || ''
+    })
+  }
+  return out
+}
+
 export {
   ensureDimensionRegistry,
   getRadarConfig,
+  getRegistryRootMeta,
   isRegistryReady,
   WEALTH_CHILD_LABEL
 }

+ 76 - 33
cfc-frontend/pages/health-main/index.vue

@@ -38,17 +38,26 @@
         </view>
 
         <!-- 演示雷达(登录浮层打开时隐藏:原生 canvas 会盖在浮层之上) -->
+        <!-- 统一维度体系:示例雷达按注册中心配置取数(维度清单来自 useDimensionRegistry),无配置显示空态引导 -->
         <view class="gh-demo" v-if="!loginSheetVisible">
-          <view class="gh-demo-head">
-            <text class="gh-demo-name">身体雷达</text>
-            <text class="gh-demo-tag">示例</text>
+          <view v-if="guestBodyDims.length > 0" class="gh-demo-block">
+            <view class="gh-demo-head">
+              <text class="gh-demo-name">身体雷达</text>
+              <text class="gh-demo-tag">示例</text>
+            </view>
+            <RadarChart :dimensions="guestBodyDims" :scores="guestBodyScores" :fillColor="bodyColor" childName="示例" width="560" height="460" />
+          </view>
+          <view v-if="guestCogDims.length > 0" class="gh-demo-block">
+            <view class="gh-demo-head">
+              <text class="gh-demo-name">认知雷达</text>
+              <text class="gh-demo-tag">示例</text>
+            </view>
+            <RadarChart :dimensions="guestCogDims" :scores="guestCogScores" :fillColor="wisdomColor" childName="示例" width="560" height="460" />
           </view>
-          <RadarChart :dimensions="demoBodyDims" :scores="demoBodyScores" :fillColor="bodyColor" childName="示例" width="560" height="460" />
-          <view class="gh-demo-head">
-            <text class="gh-demo-name">认知雷达</text>
-            <text class="gh-demo-tag">示例</text>
+          <view v-if="!guestHasDemoConfig" class="gh-demo-empty">
+            <text class="gh-demo-empty-text">健康雷达尚未配置</text>
+            <text class="gh-demo-empty-sub">完成维度配置后即可查看示例效果</text>
           </view>
-          <RadarChart :dimensions="demoCogDims" :scores="demoCogScores" :fillColor="wisdomColor" childName="示例" width="560" height="460" />
         </view>
 
         <!-- 五维健康体系 -->
@@ -295,24 +304,6 @@ export default {
         { code: 'mental', label: '心理健康测评', icon: '💖', score: null },
         { code: 'relationship', label: '关系质量测评', icon: '🤝', score: null }
       ],
-      demoBodyDims: [
-        { label: '生长发育', key: 'growth' },
-        { label: '睡眠质量', key: 'sleep' },
-        { label: '视力健康', key: 'vision' },
-        { label: '免疫力', key: 'immunity' },
-        { label: '营养均衡', key: 'nutrition' },
-        { label: '肠胃健康', key: 'gut' },
-        { label: '运动活力', key: 'exercise' }
-      ],
-      demoCogDims: [
-        { label: '记忆力', key: 'memory' },
-        { label: '注意力', key: 'attention' },
-        { label: '逻辑推理', key: 'logic' },
-        { label: '空间想象', key: 'spatial' },
-        { label: '语言表达', key: 'language' }
-      ],
-      demoBodyScores: [72, 68, 75, 80, 70, 78, 82],
-      demoCogScores: [78, 72, 85, 68, 80],
       // 真实数据缓存
       bodyData: null,
       cognitiveData: null,
@@ -421,6 +412,22 @@ export default {
       if (this.currentTab === 'wealth') return !this.wealthDetail
       var quiz = this.quizzes.filter(function(q) { return q.code === this.currentTab }.bind(this))[0]
       return !(quiz && quiz.score)
+    },
+    // ── 访客态示例雷达(统一维度体系):维度清单按注册中心配置取,无配置显示空态引导 ──
+    guestBodyDims: function() {
+      return this._guestRadarDims('body')
+    },
+    guestBodyScores: function() {
+      return this._guestDemoScores(this.guestBodyDims)
+    },
+    guestCogDims: function() {
+      return this._guestRadarDims('cognitive')
+    },
+    guestCogScores: function() {
+      return this._guestDemoScores(this.guestCogDims)
+    },
+    guestHasDemoConfig: function() {
+      return this.guestBodyDims.length > 0 || this.guestCogDims.length > 0
     }
   },
   onShow() {
@@ -444,6 +451,13 @@ export default {
       })
     } else {
       this.pendingSurveys = []
+      // 访客态:示例雷达同样按注册中心配置渲染(无配置显示空态引导)
+      var self = this
+      ensureDimensionRegistry().then(function() {
+        self.registryReady = isRegistryReady()
+      }).catch(function() {
+        self.registryReady = false
+      })
     }
   },
   methods: {
@@ -541,16 +555,11 @@ export default {
         if (!d) return [0, 0, 0, 0]
         return [d.wealthSkill || 0, d.wealthAltruism || 0, d.wealthIncome || 0, d.wealthFx || 0]
       }
-      // body: 从 API 真实维度数据取分
+      // body: 从 API 真实维度数据取分(无真实数据时由 chartIsEmpty 走零值空态,不再回落硬编码示例分)
       if (this.bodyData && this.bodyData.dimensions && this.bodyData.dimensions.length > 0) {
         return this.bodyData.dimensions.map(function(dItem) { return Number(dItem.score) || 0 })
       }
-      // body: fallback to demo
-      if (this.reports.length > 0) {
-        var latest = this.reports[0]
-        if (latest.overallScore) return [72, 68, 75, 80, 70, 78, 82]
-      }
-      return [72, 68, 75, 80, 70, 78, 82]
+      return []
     },
     /**
      * 配置驱动取分:按 tab 与注册中心节点的 scoreField / code 从对应分数包取分。
@@ -609,6 +618,35 @@ export default {
       }
       return 0
     },
+    /**
+     * 访客演示雷达维度清单:按注册中心配置取(body→body 根、cognitive→wisdom 根),
+     * 未就绪或无配置返回空数组(模板据此渲染空态引导文案)。
+     * @param {string} tab
+     * @returns {Array<{label:string,key:string,code:string}>}
+     */
+    _guestRadarDims: function(tab) {
+      var config = getRadarConfig(tab)
+      if (!this.registryReady || !config || config.length === 0) return []
+      var dims = []
+      for (var i = 0; i < config.length; i++) {
+        var c = config[i]
+        dims.push({ label: c.label, key: c.code, code: c.code })
+      }
+      return dims
+    },
+    /**
+     * 访客演示雷达示例分数:按维度数量生成确定性示例分(非字面量数组),
+     * 仅用于示例形状演示,无真实数据含义。替代原访客示例的硬编码分数数组。
+     * @param {Array} dims
+     * @returns {Array<number>}
+     */
+    _guestDemoScores: function(dims) {
+      var scores = []
+      for (var i = 0; i < dims.length; i++) {
+        scores.push(72 - (i % 3) * 4)
+      }
+      return scores
+    },
     switchTab: function(code) { this.currentTab = code },
     goPage: function(url) { uni.navigateTo({ url: url }) },
     // 登录浮层打开/关闭(隐藏演示雷达,规避原生 canvas 层级高于浮层的问题)
@@ -920,9 +958,14 @@ $gh-white: str-slice('#{$app-surface}', 2);
 
 /* 演示雷达 */
 .gh-demo { margin: 0 32rpx 24rpx; background: $app-surface; border-radius: 24rpx; padding: 20rpx 12rpx 12rpx; border: 1rpx solid $app-border-subtle; }
+.gh-demo-block { margin-top: 12rpx; }
+.gh-demo-block:first-child { margin-top: 0; }
 .gh-demo-head { display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 0 20rpx; margin: 12rpx 0 4rpx; }
 .gh-demo-name { font-size: calc(24rpx * var(--font-scale, 1)); font-weight: 600; color: $facade-text-strong; }
 .gh-demo-tag { font-size: calc(20rpx * var(--font-scale, 1)); color: $brand-primary; background: rgba($brand-primary, 0.1); border-radius: 8rpx; padding: 2rpx 12rpx; }
+.gh-demo-empty { padding: 72rpx 20rpx; display: flex; flex-direction: column; align-items: center; }
+.gh-demo-empty-text { font-size: calc(28rpx * var(--font-scale, 1)); color: $facade-text-strong; font-weight: 600; display: block; }
+.gh-demo-empty-sub { font-size: calc(22rpx * var(--font-scale, 1)); color: $facade-text-muted; display: block; margin-top: 10rpx; }
 
 /* 五维健康体系 */
 .gh-wuxing { margin: 0 32rpx 24rpx; background: $app-surface; border-radius: 24rpx; padding: 28rpx 24rpx; border: 1rpx solid $app-border-subtle; }

+ 1 - 1
cfc-web/.last_build_commit

@@ -1 +1 @@
-34b34e66b1e589fb5186ce67e5be3179ea6be9c9
+cacf76bb41e85bbe57dc079daf959db4b43e45ef

+ 1 - 1
cfc-web/package.json

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

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

@@ -4,6 +4,50 @@
 
 ---
 
+## v1.0.2167 (2026-11-07)
+
+### 文档
+- 研发负责人 错峰03 1330 轮汇报 | O-12 主理人最终确认生效(EVD-10 规划师域 G-1/2/3 + EVD-11 平台运营域 A-04/14/19/20/21 缺口处置路径) | 状态机22键=12DONE+9GREEN+1YELLOW(sleep.config.tags待mark-green)+0RED | EVD-14/EVD-15 契约核对产物核验 IN CHAIN | 池空稳态零派发 | 单文件hash-object入库
+
+
+## v1.0.2166 (2026-11-07)
+
+### 文档
+- 研发负责人1 第29轮闭环巡检汇报 | 状态机22键=12DONE+9GREEN+1YELLOW(sleep.config.tags待mark-green)+0RED | EVD-7R/8R证据已合入(31b1be1c/7f9b978c)但SC覆盖登记滞后15/241 | O-12裁决复核支持待主理人确认 | 池空稳态零派发 | 单文件hash-object入库
+- 方案 v1.2 补充小米多生态接入(小米HealthKit 云服务接口 + Zepp 老款通道 + X1-X5 任务拆解 + 合规红线)
+- 推荐人&团队分润双体系设计 v0.3 — 团队奖池从T中切20%(8份池=T×80%)
+
+### 其他
+- 同步数学示例(40.8/每份5.1/6星30.6/团队奖10.2)与配置默认值、待拍板#2标记已拍板。
+- 
+
+
+## v1.0.2165 (2026-11-07)
+
+### 文档
+- 研发负责人 闭环巡检 1245 轮汇报 | 状态机22键=12DONE+9GREEN+1YELLOW(sleep.config.tags待mark-green)+0RED | 派发B-39(pom skipTests假绿)->lan-248+B-40(CI两级门禁)->lan-204(均axonhub确认执行) | 核验ac27af487统一维度收尾合入主线无双实现路由歧义 | 测试69轮全量95.16% | 场景覆盖15/241 | 单文件hash-object入库
+- 商城按价格排序需求变更说明(v1.0 锁定,对齐成就成长打卡台账结构)
+- 推荐人&团队分润体系设计 v0.2(按主理人3点修订:①团队邀请参考家庭邀请 ②推荐人沿用CF值/团队货币直接分佣 ③星级量化定义+团队奖)
+- 研发负责人 闭环巡检 1241 轮汇报 | 状态机22键=12DONE+9GREEN+1YELLOW(sleep.config.tags待mark-green)+0RED | 全241SC契约核对已合入cfclub,未开工0 | 任务池0pending+5实例全idle | 无实质增量(与1225轮一致) | 单文件hash-object入库
+- 研发负责人 闭环巡检汇报(EVD-15 回收合并 + 会话快照更正 + SC 15/241)
+- EVD-15 跨角色域剩余8SC契约核对(SC-X-03/08/11/14/15/16/17/18)— 端点载体实测回填3⚠项+2命名修正、登记契约缺口3项(X-G-1订单支付通知未挂接、X-G-2多端会话互斥、X-G-3小程序订阅消息)、新建 2026-11-07-cross-role-contract.md + API_REFERENCE 增补§4.64
+- TR-16 EVD-14 AI/Butler 17 SC 契约产出 f62848933 回收登记(研发负责人 闭环巡检)
+- 研发负责人 闭环巡检 1218 轮标准汇报 | 状态机22键=12DONE+9GREEN+1YELLOW(sleep.config.tags待mark-green)+0RED | EVD-15跨角色域8SC开工包9f0da8727+派发lan-248(--confirm确认执行) | EVD-14在途lan-204 | 全241SC域已开工包覆盖,未开工0 | 单文件hash-object入库
+- TR-15 EVD-15 跨角色域8SC契约核对派发台账登记(研发负责人 闭环巡检)
+- 跨角色域剩余 8 SC 契约补建开工包(EVD-15 前置)· 研发负责人 闭环巡检
+- 推荐人分润&团队分润双体系设计 v0.1(占平台可分配利润70/30,8份制+一星至六星,下级可超越上级)
+
+### 测试
+- 全量实跑 286类/2375例 95.16%(干净基线)+ WIP代入实测96.93% | 新增B-39 skipTests假绿/B-40 CI软门禁 | 度量脚本入库(闭B-33)
+
+### 其他
+- EVD-14: Add AI/Butler batch contract docs (17 SC) - contract specs + API_REFERENCE 4.64 section
+- unified-dimension: T-D 能量沙盘维度来源切注册中心,五行归属 wuxing_rule_ref/统一 code,语义与回归不变
+- unified-dimension: T-C 删除 health-main 访客示例硬编码 demo 雷达数组,示例雷达改经注册中心配置取数,无配置显示空态引导
+- unified-dimension: T-B 后端去硬编码,DimensionScoreServiceImpl 维度清单改经注册中心取统一码 body.*
+- unified-dimension: T-A 移植回滚脚本,修正备份表名与 forward 成对(20261105)
+
+
 ## v1.0.2164 (2026-11-07)
 
 ### Bug 修复

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

@@ -1,6 +1,6 @@
 # 更新日志
 
-> 当前版本: v1.0.2164
+> 当前版本: v1.0.2167
 
 ## 历史版本
 
@@ -8,6 +8,50 @@
 
 ---
 
+## v1.0.2167 (2026-11-07)
+
+### 文档
+- 研发负责人 错峰03 1330 轮汇报 | O-12 主理人最终确认生效(EVD-10 规划师域 G-1/2/3 + EVD-11 平台运营域 A-04/14/19/20/21 缺口处置路径) | 状态机22键=12DONE+9GREEN+1YELLOW(sleep.config.tags待mark-green)+0RED | EVD-14/EVD-15 契约核对产物核验 IN CHAIN | 池空稳态零派发 | 单文件hash-object入库
+
+
+## v1.0.2166 (2026-11-07)
+
+### 文档
+- 研发负责人1 第29轮闭环巡检汇报 | 状态机22键=12DONE+9GREEN+1YELLOW(sleep.config.tags待mark-green)+0RED | EVD-7R/8R证据已合入(31b1be1c/7f9b978c)但SC覆盖登记滞后15/241 | O-12裁决复核支持待主理人确认 | 池空稳态零派发 | 单文件hash-object入库
+- 方案 v1.2 补充小米多生态接入(小米HealthKit 云服务接口 + Zepp 老款通道 + X1-X5 任务拆解 + 合规红线)
+- 推荐人&团队分润双体系设计 v0.3 — 团队奖池从T中切20%(8份池=T×80%)
+
+### 其他
+- 同步数学示例(40.8/每份5.1/6星30.6/团队奖10.2)与配置默认值、待拍板#2标记已拍板。
+- 
+
+
+## v1.0.2165 (2026-11-07)
+
+### 文档
+- 研发负责人 闭环巡检 1245 轮汇报 | 状态机22键=12DONE+9GREEN+1YELLOW(sleep.config.tags待mark-green)+0RED | 派发B-39(pom skipTests假绿)->lan-248+B-40(CI两级门禁)->lan-204(均axonhub确认执行) | 核验ac27af487统一维度收尾合入主线无双实现路由歧义 | 测试69轮全量95.16% | 场景覆盖15/241 | 单文件hash-object入库
+- 商城按价格排序需求变更说明(v1.0 锁定,对齐成就成长打卡台账结构)
+- 推荐人&团队分润体系设计 v0.2(按主理人3点修订:①团队邀请参考家庭邀请 ②推荐人沿用CF值/团队货币直接分佣 ③星级量化定义+团队奖)
+- 研发负责人 闭环巡检 1241 轮汇报 | 状态机22键=12DONE+9GREEN+1YELLOW(sleep.config.tags待mark-green)+0RED | 全241SC契约核对已合入cfclub,未开工0 | 任务池0pending+5实例全idle | 无实质增量(与1225轮一致) | 单文件hash-object入库
+- 研发负责人 闭环巡检汇报(EVD-15 回收合并 + 会话快照更正 + SC 15/241)
+- EVD-15 跨角色域剩余8SC契约核对(SC-X-03/08/11/14/15/16/17/18)— 端点载体实测回填3⚠项+2命名修正、登记契约缺口3项(X-G-1订单支付通知未挂接、X-G-2多端会话互斥、X-G-3小程序订阅消息)、新建 2026-11-07-cross-role-contract.md + API_REFERENCE 增补§4.64
+- TR-16 EVD-14 AI/Butler 17 SC 契约产出 f62848933 回收登记(研发负责人 闭环巡检)
+- 研发负责人 闭环巡检 1218 轮标准汇报 | 状态机22键=12DONE+9GREEN+1YELLOW(sleep.config.tags待mark-green)+0RED | EVD-15跨角色域8SC开工包9f0da8727+派发lan-248(--confirm确认执行) | EVD-14在途lan-204 | 全241SC域已开工包覆盖,未开工0 | 单文件hash-object入库
+- TR-15 EVD-15 跨角色域8SC契约核对派发台账登记(研发负责人 闭环巡检)
+- 跨角色域剩余 8 SC 契约补建开工包(EVD-15 前置)· 研发负责人 闭环巡检
+- 推荐人分润&团队分润双体系设计 v0.1(占平台可分配利润70/30,8份制+一星至六星,下级可超越上级)
+
+### 测试
+- 全量实跑 286类/2375例 95.16%(干净基线)+ WIP代入实测96.93% | 新增B-39 skipTests假绿/B-40 CI软门禁 | 度量脚本入库(闭B-33)
+
+### 其他
+- EVD-14: Add AI/Butler batch contract docs (17 SC) - contract specs + API_REFERENCE 4.64 section
+- unified-dimension: T-D 能量沙盘维度来源切注册中心,五行归属 wuxing_rule_ref/统一 code,语义与回归不变
+- unified-dimension: T-C 删除 health-main 访客示例硬编码 demo 雷达数组,示例雷达改经注册中心配置取数,无配置显示空态引导
+- unified-dimension: T-B 后端去硬编码,DimensionScoreServiceImpl 维度清单改经注册中心取统一码 body.*
+- unified-dimension: T-A 移植回滚脚本,修正备份表名与 forward 成对(20261105)
+
+
 ## v1.0.2164 (2026-11-07)
 
 ### Bug 修复

+ 45 - 2
docs/2026-11-06-华为运动健康开放平台接入方案.md

@@ -1,6 +1,6 @@
-# 华为运动健康开放平台接入方案(Health Service Kit REST API)
+# 运动健康多生态接入方案(华为 Health Service Kit + 小米 HealthKit / Zepp)
 
-> 版本:v1.0 · 日期:2026-11-06 · 决策:方案二(官方云侧 REST API)
+> 版本:v1.2 · 日期:2026-11-07 · 决策:华为方案二(云侧 REST API)+ 小米官方云服务接口(多生态并行)
 > 关联模块:cfclub 小程序 / cfc-backend / 五维能量体系 / 家庭健康档案
 
 ---
@@ -14,6 +14,7 @@
 | 配套 | 方案三变通:儿童/全家档案走「手动导入」补充(双轨产品策略,见第七节) |
 | 决策日期 | 2026-11-06 |
 | 依据 | 华为开发者联盟官方文档:Health Service Kit 应用接入流程、面向应用开放服务、数据开放总览 |
+| 小米补充 | 2026-11-07 补充:小米运动健康数据服务(云服务接口)+ Zepp 开放平台(老款)双通道;详见「十一、多生态接入」 |
 
 **关键事实(官方明文)**:
 1. 跨平台 / 微信小程序等 WEB 场景,**优先建议走 REST API**(云侧),保证多端体验一致性。
@@ -74,6 +75,7 @@
 - **小程序**:引导用户授权华为账号(OAuth)→ 拿授权码 → 交后端;展示同步后的步数/心率/睡眠/体重 → 五维/健康档案。
 - **cfc-backend**:`HuaweiAuthService`(OAuth 换 token、刷新、撤销授权)+ `HealthSyncService`(定时拉取 + 数据类型映射 + 入库)。
 - **华为云侧**:REST API,用户授权后按数据类型返回原子数据。
+- **多生态适配器(见第十一节)**:`XiaomiHealthService` / `ZeppHealthService` 与 `HuaweiHealthService` 共用同一归一化管线,数据统一入 `health_data_sources`(sourceType 区分 `huawei_health` / `xiaomi_health` / `zepp_health`)。
 
 **关键点**:
 - 所有华为 API 调用**仅经后端**,小程序不直接持 token(安全 + 统一认证)。
@@ -177,6 +179,18 @@
 - 新增 Controller 前查 `docs/superpowers/api/API_REFERENCE.md` 防重复;`mvn clean compile` 验证。
 - 实现仅在 feature 分支,**不得假定包路径/不得直接改主线**(按 CFC 派发纪律)。
 
+**小米通道任务(X1-X5,与 D 系并行,共用管线)**:
+
+| 任务 | 内容 | 依赖 |
+|------|------|------|
+| X1 数据模型 | `xiaomi_health_tokens` 表迁移 + 复用 `HealthDataSourceRecord`(sourceType='xiaomi_health') | 无 |
+| X2 OAuth 模块 | `XiaomiAuthService`:授权 URL / code 换 token / 刷新 / 撤销(小米云服务认证鉴权) | X1 |
+| X3 同步引擎 | `XiaomiHealthService`:云服务接口拉取(采样/订阅事件)+ 复用 `NormCompareEngine` 归一化 | X2 |
+| X4 接口层 | `XiaomiHealthController`:auth-url / callback / sync / revoke / status(与华为 5 接口同构) | X3 |
+| X5 Mock 通道 | 凭证未批前 mock 数据源,端到端可测 | X4 |
+
+> 老款 Zepp 通道(`zepp_health`)暂以「数据源适配器」预留接口,待 Zepp 凭证落地后接 Webhook 推送(OAuth2 + push/ping-pull 模型),不与 X 系串行。
+
 ---
 
 ## 十、验证与上线标准
@@ -190,6 +204,33 @@
 
 ---
 
+## 十一、多生态接入(小米 HealthKit / Zepp)
+
+### 11.1 小米手环分代际路径
+
+| 手环代际 | 数据承载 | 官方通道 | 小程序可行性 |
+|----------|----------|----------|--------------|
+| 新款(小米运动健康:手环 7/8/9、小米手表等) | 小米云 | 小米运动健康数据服务:云服务接口(hlthopen.io.mi.com,HTTPS 标准 API + 分页 cursor + 订阅事件 SAMPLESET_EVENT) | ✅ 后端直接 HTTPS 调用,无需端上 SDK |
+| 老款(华米代工:Mi Band 1-5 / Redmi Smart Band / Amazfit) | Zepp 云 | Zepp 开放平台(OAuth2 + REST + Webhook;老版 Huami API 审核 3-7 天) | ✅ 纯云侧接入,小程序完全可走 |
+
+### 11.2 关键利好(相对华为)
+
+- 小米 / Zepp **无「不支持儿童数据」明文禁令**(以申请时官方回复为准)——儿童佩戴小米手环自动同步全家健康档案成为可能,补足华为方案的短板。
+- 审批周期短(小米服务开通审核 + Zepp 老款 3-7 天),可与华为审批并行,不互相挤占。
+- 端侧 SDK(Android AIDL)不可用于小程序,但**云服务接口为服务端标准 HTTPS**,无需 Android 壳,接入门槛低于华为。
+
+### 11.3 架构与数据落点
+
+- 新增 `XiaomiHealthService` / `ZeppHealthService`,与 `HuaweiHealthService` **共用同一归一化管线**(NormCompareEngine → body.* 子维 → health_data_sources)。
+- sourceType:`xiaomi_health` / `zepp_health`(区分来源;维度映射同华为:body.exercise / sleep / immunity / nutrition)。
+
+### 11.4 合规红线
+
+- **禁用社区逆向接口**(如 Mi Fitness MCP 类:userId/passToken 仿冒 App 内部接口)——伪造凭证 + 仿冒接口,正式产品禁用。
+- **不引入境外聚合中转**(如 Terra:健康数据出境触发个保法监管红线),不作为主力通道。
+
+---
+
 ## 附录 A:华为开放数据类型(本期用 + 备选)
 
 | 类型 | 数据 | 本期 |
@@ -208,3 +249,5 @@
 - 华为开发者联盟《面向应用开放服务》Health Service Kit:developer.huawei.com/consumer/cn/doc/HMsCore-Guides/application_description-0000001418052318
 - 华为《运动健康服务接入流程》:developer.huawei.com/consumer/cn/doc/hmscore-guides/application-access-process-0000001611092953
 - 本项目 `DimensionCodeConstants` / `HealthDataSourceRecord` / `energy_source_config` / `NormCompareEngine`
+- 小米运动健康数据服务(云服务接口):dev.mi.com/xiaomihyperos/documentation/detail?pId=2328(接口)、pId=2333(服务端接入指南)
+- Zepp 开放平台:devopen.zepp.com(OAuth2 + REST + Webhook)

+ 16 - 16
docs/superpowers/specs/2026-11-07-referral-team-profit-sharing.md

@@ -1,10 +1,10 @@
 # 推荐人分润 & 团队分润双体系设计
 
-> **版本**:v0.2(按主理人 3 点意见修订,待评审)
+> **版本**:v0.3(按主理人最新拍板「团队奖从 T 中切 20%」修订,待评审)
 > **日期**:2026-11-07
 > **作者**:研发负责人
 > **状态**:🟡 待评审
-> **修订**:① 团队邀请参考家庭邀请(区分已加入/未加入平台);② 推荐人体系**沿用 CF 值**(现有 F03a/F03b),团队体系**新增货币直接分佣**;③ 补星级量化定义 + 团队奖。
+> **修订**:v0.2:① 团队邀请参考家庭邀请(区分已加入/未加入平台);② 推荐人体系**沿用 CF 值**(现有 F03a/F03b),团队体系**新增货币直接分佣**;③ 补星级量化定义 + 团队奖。v0.3:团队奖池从 T 中切 **20%**(8 份池 = T×80%)。
 
 ---
 
@@ -13,7 +13,7 @@
 本次设计**保持推荐人体系沿用现有 CF 值账本(F02 / F03a / F03b)**,仅**新增一套真金白银(货币)的团队分润体系**,用于把「平台留存的销售利润」分给**平台服务团队(规划师/测评师等)**:
 
 - **推荐人分润体系**:被推荐人消费时的推荐人分润**仍以 CF 值(平台积分)发放**——沿用现有 `CfCommissionService` 的 F03a 直接销售奖(≤70%)+ F03b 间接销售奖(≤20%),**不新增货币账本**。
-- **团队分润体系(本次新增)**:团队成员(规划师/测评师等平台人员)产生服务收入时,分**平台留存利润的 30%**,以**货币直接分佣**;内部按「8 份制 + 一星至六星」分配,另设**团队奖**(团队服务业绩越好拿得越多)。
+- **团队分润体系(本次新增)**:团队成员(规划师/测评师等平台人员)产生服务收入时,分**平台留存利润的 30%**,以**货币直接分佣**;其中 **80% 作 8 份制分佣池**(按一星至六星分配)、**20% 作团队奖池**(团队服务业绩越好拿得越多)。
 
 **核心原则(用户拍板)**:两个体系都支持**下级超越上级、多劳多得**——级别与比例均按**滚动业绩动态计算**,业绩涨则级别/份额涨,业绩跌则降,不固化层级绑定。
 
@@ -147,15 +147,15 @@ CfCommissionService.settle / settleReferrerOnly
 > **v0.2 修订(主理人意见②)**:团队采用**直接分佣(货币)**——这是本设计唯一的真金白银账本。
 
 **核心机制**(三块):
-- **8 份分佣池**:`T` 中默认 **70%** 切成 **8 份**(示例 `51×70%=35.7`,每份 `4.46` 元),按星级分给服务链上的成员。
-- **团队奖池**:`T` 中默认 **30%**(示例 `51×30%=15.3` 元),按**团队整体年度服务业绩档位**发放(团队服务越好拿得越多)——见 §4.4。
+- **8 份分佣池**:`T` 中默认 **80%** 切成 **8 份**(示例 `51×80%=40.8`,每份 `5.1` 元),按星级分给服务链上的成员。
+- **团队奖池**:`T` 中默认 **20%**(示例 `51×20%=10.2` 元),按**团队整体年度服务业绩档位**发放(团队服务越好拿得越多)——见 §4.4。
 - **触发门槛**:团队**年度滚动总服务业绩**达到该团队**单独设定的门槛**时,该团队才进入本次分红(每团队阈值独立配置)。
 
 ### 4.2 8 份分佣(对照用户示例)
 
-用户示例:6 星成员自服务 → `8份池35.7×6/8=26.78`;其引荐人 1 星 → `35.7×1/8=4.46`;引荐人的引荐人(不论星级)→ `35.7×1/8=4.46`(剩余,因 8 份已分完)。
+用户示例:6 星成员自服务 → `8份池40.8×6/8=30.6`;其引荐人 1 星 → `40.8×1/8=5.1`;引荐人的引荐人(不论星级)→ `40.8×1/8=5.1`(剩余,因 8 份已分完)。
 
-> **⚠️ 口径说明**:用户原文「51×6/8=38.25」是按 **51 全部进 8 份池** 的口径计算。v0.2 因新增**团队奖池(30%)**,8 份池变更为 `T×70%`。若主理人希望保持「51 全进 8 份」、团队奖另外追加,可配置 `team_profit.eight_share_ratio=100%`。**两种取数均可用,由配置驱动,算法不变**。
+> **⚠️ 口径说明**:用户原文「51×6/8=38.25」是按 **51 全部进 8 份池** 的口径计算。v0.3 按主理人拍板「团队奖从 T 中切 20%」,8 份池为 `T×80%`、团队奖池为 `T×20%`。若主理人希望保持「51 全进 8 份」、团队奖另外追加,可配置 `team_profit.eight_share_ratio=100%`。**两种取数均可用,由配置驱动,算法不变**。
 
 **分配链路(自上而下)**:服务提供者本人 → 其引荐人(上级) → 引荐人的引荐人(上上级),**最多三层**,共 8 份封顶:
 
@@ -205,7 +205,7 @@ CfCommissionService.settle / settleReferrerOnly
 
 ### 4.4 团队奖(用户意见③:团队服务好也要拿得多)
 
-**团队奖池**:`A = T × cfg_team_award_rate`(默认 30%,示例 15.3 元/单计提)。
+**团队奖池**:`A = T × cfg_team_award_rate`(默认 20%,示例 10.2 元/单计提)。
 
 **分配逻辑**:按「该服务订单所属团队的**年度滚动总服务业绩**」分档,业绩越好、团队奖倍数越高:
 
@@ -218,7 +218,7 @@ CfCommissionService.settle / settleReferrerOnly
 
 **归属**:团队奖发放给**该团队负责人**(`distribution_systems.owner_id` 优先,无则团队根节点),由其统筹分配(与现有分销 T37/C2 口径一致:「平台只向体系唯一负责人整体结算,团队内部分配平台不负责」)。如需平台代分(按成员星级比例),可配 `team_profit.award_distribute=owner|pro_rata`,默认 `owner`。
 
-**数学校验(示例)**:51 元中团队奖 15.3 元——团队业绩达 TA3 档 → 负责人得 15.3 元;未达门槛 → 回流平台。
+**数学校验(示例)**:51 元中团队奖 10.2 元——团队业绩达 TA3 档 → 负责人得 10.2 元;未达门槛 → 回流平台。
 
 ### 4.5 团队门槛与团队归属
 
@@ -233,7 +233,7 @@ CfCommissionService.settle / settleReferrerOnly
 服务订单完成(服务提供者 X 收入确认,D > 0)
   │
   ▼
-计算 T(§2.1)、8份池 = T×70%、团队奖池 = T×30%
+计算 T(§2.1)、8份池 = T×80%、团队奖池 = T×20%
   │
   ├─ 取 X 所属服务团队(distribution_system)
   ├─ 校验团队年度滚动总服务业绩 ≥ 团队门槛?
@@ -331,8 +331,8 @@ CREATE TABLE profit_pool_order (
   pool_cent INT NOT NULL COMMENT '可分配池D(分)',
   referral_share_cent INT NOT NULL COMMENT '推荐人体系R(分,CF值预算参照)',
   team_share_cent INT NOT NULL COMMENT '团队体系T(分,货币)',
-  team_eight_share_cent INT NOT NULL COMMENT '8份分佣池(分,默认T×70%)',
-  team_award_cent INT NOT NULL COMMENT '团队奖池(分,默认T×30%)',
+  team_eight_share_cent INT NOT NULL COMMENT '8份分佣池(分,默认T×80%)',
+  team_award_cent INT NOT NULL COMMENT '团队奖池(分,默认T×20%)',
   status VARCHAR(16) NOT NULL DEFAULT 'pending' COMMENT 'pending/settled/cancelled',
   created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
   settled_at DATETIME,
@@ -428,8 +428,8 @@ CREATE TABLE team_star_snapshot (
 | `referral_profit.cf_cost_rate` | 1% | CF 值成本占 S 比例 |
 | `referral_profit.referral_share` | 70% | 推荐人体系占 D 比例(CF 值预算参照) |
 | `referral_profit.team_share` | 30% | 团队体系占 D 比例(货币) |
-| `team_profit.eight_share_ratio` | 70% | 8 份分佣池占 T 比例 |
-| `team_profit.team_award_ratio` | 30% | 团队奖池占 T 比例(用户初稿口径:0% → 8份池=100%T) |
+| `team_profit.eight_share_ratio` | 80% | 8 份分佣池占 T 比例 |
+| `team_profit.team_award_ratio` | 20% | 团队奖池占 T 比例(用户初稿口径:0% → 8份池=100%T) |
 | `team_profit.star_tiers` | JSON | 星级业绩分门槛表(6 档) |
 | `team_profit.team_threshold` | JSON | 各服务团队年度业绩门槛(teamId→金额) |
 | `team_profit.share_total` | 8 | 8 份制分母 |
@@ -503,12 +503,12 @@ profit_pool_order(status=pending)  ← 计算 D/T/8份池/团队奖(幂等)
 
 ---
 
-## 11. 待主理人拍板事项(v0.2 更新)
+## 11. 待主理人拍板事项(v0.3 更新)
 
 | # | 事项 | 建议 |
 |:-:|------|------|
 | 1 | 用户示例「170×70%×70%=93.3」校正确认为 83.3(119×70%) | 采纳 83.3 校正口径(CF 值折算口径,非货币) |
-| 2 | **团队奖是否从 T 中切 30%**(8份池=T×70%)还是「51 全进 8 份、团队奖另行追加」 | 默认 70/30 拆分(配置可调为 100/0) |
+| 2 | **团队奖从 T 中切 20%**(8份池=T×80%)——主理人已拍板 | ✅ 已拍板:80/20 拆分(配置可调为 100/0) |
 | 3 | 团队奖归属:负责人统筹(owner)还是平台按星级代分(pro_rata) | 建议 owner(与现有 T37/C2 结算口径一致) |
 | 4 | 星级重评周期(订单实时 vs 月度快照) | 建议**订单支付时实时计算** + 月度快照供展示 |
 | 5 | 8 份制中第 3 层是否永远 1 份封顶 | 是(用户口径「只能拿剩下的」) |

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
docs/tdd/orchestration/pending.md


+ 176 - 0
docs/tdd/orchestration/report-dev-2026-11-07-1245.md

@@ -0,0 +1,176 @@
+# 研发负责人工作汇报(研发负责人 · 闭环巡检 · 2026-11-07 12:45 轮)
+
+> **说明**:本报告依据 `tdd-orchestrator` 研发负责人模板填写,数据来自本轮只读核查(`--status` / `--changes --days 7` / `--clean-check`)、git 真理核验(`git fetch` + `merge-base --is-ancestor`)与远端 `docs/tdd/**` 文档复核。
+> **角色纪律**:本轮为调度决策 + 汇报汇总轮;未代写实现、未改业务代码、未动他人 WIP、未代越测试 `mark-green`。派发通道独立(hub dispatch)。
+
+---
+
+## 基本信息
+
+| 项目 | 内容 |
+|------|------|
+| 汇报人 | 研发负责人(tdd-orchestrator · 闭环巡检) |
+| 汇报周期 | 2026年11月07日 12:41 ~ 13:03(第 54 轮,跨前轮续跑) |
+| 负责模块/单元范围 | cfc 全量(scope 空) |
+
+---
+
+## 一、本轮工作数据汇总
+
+### 1.1 问题接收与处置
+
+| 指标 | 数量 | 填写说明 |
+|------|------|----------|
+| **收到的测试反馈问题数*** | `2` 个 | **B-39(P0 构建假绿黑洞:pom.xml:28 `skipTests=true`)+ B-40(CI 软门禁:4 job `allow_failure` + integration 包被排除)**——均由测试侧第 69 轮(`2aa575e26`)新增登记,属研发/流程类阻塞,本轮派发修复 |
+| **已解决问题数*** | `0` 个 | 本轮为派发启动,修复产出待会话完成核验(下轮回收) |
+| **待解决研发问题数** | `3` 个 | ① B-39/B-40 修复在途(本轮派发 lan-248/lan-204);② sleep.config.tags `--mark-green` 收口——待测试侧复验(TR-11/TR-6);③ B-38④ `platform_fee` 列 Out of range(`GuideModuleIntegrationTest`)——待研发核验迁移脚本与 schema.sql 列定义(本轮只读核验发现 schema.sql 与 DatabaseInitializer 存在多处 `platform_fee` 定义,需下轮确认列宽口径) |
+
+### 1.2 任务分发与会话管理
+
+| 指标 | 数量 | 填写说明 |
+|------|------|----------|
+| **分派任务数*** | `2` 个 | **B-39 → lan-248 `ses_e4b9dc3f1ffePukGgg901lbjq3`**(pom skipTests 假绿修复 + CI XML>0 守卫);**B-40 → lan-204 `ses_e4b9cc81fffezhTCq7K2fce17w`**(CI 两级门禁 blocking/observed 分级 + integration 白名单)。均 `axonhub/deepseek`,`--confirm` 确认真实执行(工具调用 2 次 ✓)。任务池空(⏳0)→ 按「测试问题池 kind=test 阻塞项插队优先」从 blockers 取 B-39/B-40 |
+| **恢复智能体会话数*** | `0` 个 | 无中断/吞没会话需恢复(5 实例 idle 稳态,无 wedged/stalled/thrash) |
+| **当前在用会话数** | `4` 个(全局并发已达 limit=4) | local-45278 2 个(华为健康接入 D1-D5 / 三方一致性核验,其他会话在跑)+ lan-248 1 个(B-39)+ lan-204 1 个(B-40);lan-189/pub-iwintrue idle,prod-etotem 不可达(401) |
+| **会话平均利用率** | `~50%`(4/8,并发口径 4/4) | 全局并发达上限(limit=4),停止继续派发;剩余 idle 因无新增能力键可派(防假 RED) |
+
+### 1.2b 各会话当前工作状态列表
+
+| 会话标签 | 会话 ID | 当前状态 | 当前任务 | 工作目录 | 备注 |
+|----------|---------|----------|----------|----------|------|
+| lan-248 | `ses_e4b9dc3f1ffePukGgg901lbjq3` | `running`(13:01 派发确认) | B-39 修复 pom skipTests 假绿 | /sc-data/cfc | axonhub/deepseek;工具调用 2 次 ✓ |
+| lan-204 | `ses_e4b9cc81fffezhTCq7K2fce17w` | `running`(13:03 派发确认) | B-40 CI 两级门禁 | D:\cfc | axonhub/deepseek;工具调用 2 次 ✓ |
+| local-45278 | `ses_e4bacf52fffehR6MfnHq1vpkre` | `running` | 华为健康接入 D1-D5(其他会话) | — | 非本实例派发,只读登记 |
+| local-45278 | `ses_e4bae2c77ffeYOJi4AXUf4eSCV` | `running` | docs/10↔docs/12↔代码三方一致性核验(其他会话) | — | 非本实例派发,只读登记 |
+| lan-189 | (83 会话池) | `idle` | — | — | 可调度;无在途 dev 任务(0 新键防假 RED) |
+| pub-iwintrue | (158 会话池) | `idle` | — | — | 可调度;无在途 dev 任务(0 新键防假 RED) |
+| prod-etotem | — | 不可达 | — | — | HTTP 401(非研发恢复范围,占位登记) |
+
+> **任务账本**(.workbuddy/tasks.json):34 条 = 33 done + 1 failed(S-cfc-20 tokens=0),0 pending / 0 dispatched。B-39/B-40 走 hub dispatch 通道(非 tasks.json),账本开销为 0。
+
+### 1.3 变更感知与新任务建立
+
+| 指标 | 数量 | 填写说明 |
+|------|------|----------|
+| **感知需求/设计变更数*** | `200` 提交 / 238 文件 | `--changes --days 7`:设计 94 + 测试用例 16 + 其他 128;**无新增能力键(0→0),变化集中在既有键** |
+| **新增入池任务数*** | `0` 个 | 能力键 TDD 池空(⏳ 0)、无新键可入池 → 防假 RED 不 `--add-task`;测试阻塞项 B-39/B-40 走 blockers → hub 派发通道(非能力键入池) |
+| **既有键状态流转数** | `0` 次 | 状态机稳定(DONE12 / GREEN9 / YELLOW1 / RED0),本轮无阶段流转(sleep.config.tags 待测试 mark-green,TR-11 已送达) |
+
+### 1.4 本轮 git 提交与推送证据(真理证据)
+
+| 提交 sha | 关联任务/能力键 | 阶段 | 触及文件类型 | 目标分支 | 已推送远端 | 备注 |
+|----------|-----------------|------|--------------|----------|------------|------|
+| `ac27af487` | merge(dimension) 统一维度体系收尾 gapfix(T-A 回滚脚本/T-B 后端去硬编码/T-C demo 兜底清除/T-D 能量沙盘切注册中心) | 外部增量核验 | 实现(cfc-backend src/main) | cfclub | 是 | IN CHAIN ✅(12:53);**主线仅 1 个 DimensionRegistryController(admin 包)→ 无 Spring 路由歧义**(统一维度双实现风险已消) |
+| `2aa575e26` | test(第69轮) 全量实跑 286类/2375例 95.16% + B-39/B-40 登记 | 测试侧增量 | 测试/文档 | cfclub | 是 | IN CHAIN ✅;测试侧新增 B-39/B-40 两项研发类阻塞 |
+| `41f1fc934` | B-37 修复 / sleep.config.tags 实现 | YELLOW 证据 | 实现 | cfclub | 是 | IN CHAIN ✅(B-37 空白 key 校验收紧),待测试 mark-green |
+
+> 另核验确认(皆 IN CHAIN ✅):EVD-3~15 契约核对全部已在 cfclub(241 SC 契约核对 100% 并入主线,承前轮)。
+
+### 1.5 状态机阶段分布与部署状态
+
+| 阶段 | 键数 | 备注 |
+|------|------|------|
+| UNTRACKED | `0` | — |
+| RED | `0` | 无待开发 |
+| YELLOW | `1` | sleep.config.tags(B-37 已修复 `41f1fc934`,待测试侧复验 mark-green,TR-11/TR-6 已送达) |
+| GREEN | `9` | admin.auth.login / health.report.upload / health.report.createReport / dan.assessment.getActiveMaterial / dan.report.parseFormat / task.history.view / growth.history.view / achievement.grid.fiveCol / langgraph.questionnaire.generate |
+| DONE | `12` | auth.guest.jwtGuard / auth.guest.wechatLogin / dimension.registry.getTree / dimension.registry.mapLegacyCode / family.familyMembers.addMember / family.familyMembers.listMembers / family.parent.switchRole / guide.trainingPlan.getPlanDetail / guide.trainingPlan.getPlanList / reward.cfEstimate.expectedCf / task.parent.createTask / task.parent.review |
+
+| 部署项 | 值 | 说明 |
+|--------|-----|------|
+| 本轮部署次数 | `0` 次 | 本实例未派发部署(无 YELLOW 新增) |
+| 最新 `last_deployed` | `292087771` | .tdd/deploy_state.json:confirmed 20 + pending 0;承前轮部署缺口(`41f1fc93`/`ba6b48d2`/`9628df3d9`/`1b15b67d2` + `ac27af487` 统一维度 src/main 变更)未确认 testing,登记待部署侧核对 |
+| GREEN 部署链验证 | 部分通过 | 依赖部署缺口确认(承前轮) |
+
+### 1.6 数据源健康度
+
+| 数据源 | 读取键数 | 是否正常 | 备注 |
+|--------|----------|----------|------|
+| 需求侧(⏳ wish) | `0` | 正常 | 目标池 0 键(`--status` 实测) |
+| 契约常量(contract) | `0` | 正常 | CFC 启用契约补建通道(O-7),非能力键池 |
+| 测试用例引用(test_case_map) | `0` | 正常 | — |
+| 执行结果回填(case_results) | `0` | 正常 | — |
+
+---
+
+## 二、下一步需要研发处理的事项
+
+| 序号 | 事项描述 | 依赖/前置条件 | 预估完成时间 | 优先级 | 状态 |
+|------|----------|---------------|--------------|--------|------|
+| 1 | 回收 B-39/B-40 修复产出(核验会话自述 + git 证据,同法精确合入 cfclub) | 会话完成 | 11-07 当日 | P0 | 在途(lan-248/lan-204) |
+| 2 | B-38④ `platform_fee` 列 Out of range 核验(schema.sql 与 DatabaseInitializer 列宽口径) | 下轮只读核验 | 11-07 当日 | P1 | 待核验 |
+| 3 | sleep.config.tags `--mark-green` 收口跟进 | 测试侧复验(TR-11/TR-6) | 11-07 当日 | P1 | 待测试处理 |
+| 4 | 部署缺口确认(含 `ac27af487` 统一维度 src/main 变更) | 部署侧 `--deploy-done` 类确认 | 11-07 当日 | P1 | 待部署侧 |
+| 5 | 测试侧 SC 台账登记(EVD-3~15 → 跑通状态,15 → 预计 130+/241) | 测试侧逐 SC 三类流证据登记 | 测试侧轮次内 | P1 | 待测试处理 |
+
+---
+
+## 三、阻塞与风险
+
+| 阻塞项 | 影响范围 | 阻塞类型 | 处置进展 | 需要协调 |
+|--------|----------|----------|----------|----------|
+| B-39 pom.xml `skipTests=true` 假绿(P0) | 全量测试可信度 | 研发/流程 | 本轮派发 lan-248 修复(改默认 false + CI XML>0 守卫) | 研发(在途) |
+| B-40 CI 软门禁(4 job allow_failure + integration 被排除) | CI 持续回归真实性 | 研发/流程 | 本轮派发 lan-204 修复(两级门禁 + integration 白名单) | 研发(在途) |
+| B-38④ `platform_fee` Out of range | GuideModuleIntegrationTest 真实红 | 研发 | 只读核验发现多处列定义,待下轮确认口径 | 研发 |
+| B-31 明文凭据(7 处) | 全仓安全 | 运维 | O-10 已裁决,凭据轮换为人工运维动作 | 运维/主理人 |
+| 部署缺口(含统一维度 ac27af487 src/main 变更)未确认 testing | GREEN 部署链验证 | 部署侧 | 测试侧已旁路复验不阻断;待 `--deploy-done` 类确认 | 部署侧 |
+| 主工作树 169 处他人 WIP | 工作目录门禁 | 测试环境 | 本实例按纪律④不代提交、不回滚(O-3 判据③ SC 关联提交级不阻塞) | 各会话所有者 |
+
+---
+
+## 四、SC 覆盖矩阵(本实例观测,权威口径见 `sc-key-map.md` 台账)
+
+| 口径 | SC 数 | 说明 |
+|------|-------|------|
+| 已跑通(台账权威) | `15` / 241 | O-3 收口 5(G-02/G-06/P-03/P-10/P-11)+ 游客域 10(G-01/03/04/05/07/08/09/10/11/12);证据 sha 已 merge-base 核验 IN CHAIN |
+| 契约核对已合入 cfclub、待测试侧补三类流证据并登记 | `226` | = 241 − 15 已跑通;EVD-3~15 全部契约核对已合入 |
+| 本轮新增跑通 | `0` | 本轮无 SC 转正(B-39/B-40 为流程修复,非 SC 证据) |
+| 失败 / 阻塞 / 未跟踪 | `0` / `0` / `226` | 无 SC 级失败/阻塞 |
+
+> 注:唯一权威计数 = `sc-key-map.md` 主表「跑通状态」列(15/241)。
+
+---
+
+## 五、下轮工作规划
+
+| 计划项 | 预计完成时间 | 前置依赖 | 涉及会话标签 | 备注 |
+|--------|--------------|----------|--------------|------|
+| 回收 B-39/B-40 修复产出 sha(同法精确合入 cfclub) | 11-07 当日 | 会话完成 | lan-248/lan-204 | P0 假绿黑洞闭环 |
+| B-38④ platform_fee 列宽口径核验 | 11-07 当日 | 只读核验 | 研发 | GuideModuleIntegrationTest 真实红 |
+| 测试侧 SC 台账登记(EVD-3~15) | 测试侧轮次内 | 三类流证据 | 测试负责人 | 15/241 → 预计 130+/241 |
+| 部署缺口确认(含统一维度变更) | 11-07 当日 | 部署侧 | local-45278 | testing 部署验证 |
+
+---
+
+## 附录
+
+### 附录 A:收到的测试反馈问题明细
+新增 2 个:**B-39**(`cfc-backend/pom.xml:28` 默认 `skipTests=true`,mvn test 零用例执行仍 BUILD SUCCESS,P0 假绿黑洞——本机验证 `mvn -o -B clean test` → 9:49 BUILD SUCCESS + 0 XML)+ **B-40**(`.gitlab-ci.yml` 4 job `allow_failure` 软门禁 + `-Dtest='!**/integration/**'` 排除 integration 包 1794/2219 例,含 26 红类中 14 个红类恰在 CI 视野外)。
+
+### 附录 B:任务分派与会话恢复明细
+**新派发 2 个**(均 `--confirm` 确认真实执行):
+- B-39 → lan-248 `ses_e4b9dc3f1ffePukGgg901lbjq3`(13:01,工具调用 2 次 ✓)
+- B-40 → lan-204 `ses_e4b9cc81fffezhTCq7K2fce17w`(13:03,工具调用 2 次 ✓)
+无恢复(0 中断/吞没会话)。
+
+### 附录 C:新增入池任务明细
+无(能力键 TDD 池空,0 新键;B-39/B-40 走 blockers→hub 派发通道)。
+
+### 附录 D:变更感知明细(来自 `--changes --days 7`)
+
+| 变更文档 | 文档类型 | 变更日期 | 新增能力键 | 是否已入池 |
+|----------|----------|----------|------------|------------|
+| `docs/tdd/test/automation-run-*`(测试负责人 65~69 轮 + EVD 证据) | 测试用例/执行证据 | 2026-11-06~07 | 0 | 不适用(测试侧登记) |
+| `cfc-backend/src`(unified-dimension T-A~T-D、B-37、S-cfc-34、onboarding) | 实现 | 2026-11-06~07 | 0 | 既键变化 |
+| `docs/superpowers/specs/*`(推荐人分润 v0.1/v0.2、会员/优惠券) | 设计 | 2026-11-06~07 | 0 | 既键变化 |
+| 结论 | — | — | **0 新增能力键** | — |
+
+### 附录 E:CI 推进(必选项)
+
+- **本轮推进 1 项**:派发 **B-40**(CI 两级门禁改造:test-blocking 全绿子集必须过 + test-observed 已知红 allow_failure 产出趋势 + integration 包按类白名单转 blocking)作为 CI 持续回归能力缺口推进项。
+- **B-39 附带 CI 守卫**:backend job 增加 `mvn -o -B test -DskipTests=false` 后断言 `surefire-reports/*.xml` 数量>0 否则 job 失败(堵死假绿)。
+- **现状**:CI 最小管线 `.gitlab-ci.yml`(`5783bb294` + EVD-6 `07a992161` 更新)已在链;入口仍 `when: manual`(runner JDK8 待主理人核实);**不宣称 CI 已启用、不宣称任何 SC 本轮新增跑通**。
+
+---
+
+> **巡检纪律声明**:本轮未代写实现、未改业务代码、未动他人 WIP(169 处)、未代越测试 `mark-green`、无本地 git 提交(B-39/B-40 经 hub dispatch 派发,会话自 commit+push);报告经临时索引单文件构造提交推 cfclub,未触碰主工作树。

+ 166 - 0
docs/tdd/orchestration/report-dev-2026-11-07-1315.md

@@ -0,0 +1,166 @@
+# 研发负责人工作汇报(研发负责人1 第29轮)
+
+> 汇报人:研发负责人1(本自动化 ce26ad68)
+> 汇报周期:2026-11-07 07:00 ~ 13:15(第 29 轮)
+> 负责范围:全量(无 scope 隔离);本角色为只读核查 + 派发调度 + 汇总验收,不代写实现
+
+---
+
+## 一、本轮工作数据汇总
+
+### 1.1 问题接收与处置
+
+| 指标 | 数量 | 说明 |
+|------|------|------|
+| 收到的测试反馈问题数 | 2 个 | B-39(`pom.xml skipTests` 假绿)/ B-40(CI 两级软门禁)——测试侧 69 轮新增登记;另 B-34/B-35/B-37 为既往轮已收(B-34 修复 `1b15b67d2`、B-35 修复 `9628df3d9`、B-37 修复 `41f1fc934` 均已在链) |
+| 已解决问题数 | 1 个 | B-37(sleep.config.tags 空白 key 校验收紧 `41f1fc934`,已推 origin/cfclub,IN CHAIN ✓)——实现已修复,尚差测试侧复跑 1 例后 `--mark-green` 收口 |
+| 待解决研发问题数 | 3 个 | B-31(application.yml 明文凭据,已裁决 O-10,轮换为人工运维动作)/ B-36(黑盒 API 套件默认 skip,测试侧处置)/ O-12(契约核对缺口,待主理人最终确认) |
+
+### 1.2 任务分发与会话管理
+
+| 指标 | 数量 | 说明 |
+|------|------|------|
+| 分派任务数 | 0 个 | 任务池 0 pending(34 条 done=33 / failed=1),目标池 0 键,无新增能力键 → 零派发(池空,防假 RED) |
+| 恢复智能体会话数 | 0 个 | 无中断/异常需恢复(5 实例全可达、无 wedged/swallowed) |
+| 当前在用会话数 | 0 个 | 5 实例(lan-189/lan-204/local-lan-4090/mes-local-45278/pub-iwintrue)全部 idle=50/busy=0 |
+| 会话平均利用率 | 0% | 本轮无在途任务;各 EVD 契约核对批次(EVD-14/15)已由并行研发负责人轮派发并回收产出 |
+
+### 1.2b 各会话当前工作状态列表(13:16 快照 · --inspect 测试角色只读)
+
+| 会话标签 | 会话数 | 当前状态 | 当前任务 | 静默/空闲时长 | 工作目录 | 备注 |
+|----------|--------|----------|----------|---------------|----------|------|
+| lan-189 | 50 | idle | — | 全空闲 | — | 配额占用 0/3,剩余 3 |
+| lan-204 | 50 | idle | — | 全空闲 | — | 配额占用 0/3,剩余 3 |
+| local-lan-4090 | 50 | idle | — | 全空闲 | — | 配额占用 0/3,剩余 3 |
+| mes-local-45278 | 50 | idle | — | 全空闲 | — | 配额占用 0/3,剩余 3 |
+| pub-iwintrue | 50 | idle | — | 全空闲 | — | 配额占用 0/3,剩余 3 |
+
+> 注:`--inspect` 仅测试角色可执行(研发负责人用只读巡检补会话状态)。本轮 5 实例全 idle,任务池 0 pending → 无任务可派,符合「池空」真实状态(非资源不可用)。
+
+### 1.3 变更感知与新任务建立
+
+| 指标 | 数量 | 说明 |
+|------|------|------|
+| 感知需求/设计变更数 | 200 提交 / 238 文件 | 近 3 天(2026-11-06~11-07)git 版本历史:设计文档 94 / 测试用例文档 16 / 其他 128 |
+| 新增入池任务数 | 0 个 | `--changes` 无新增能力键(0→0,变化集中在既有键)→ 无 `--add-task`(防假 RED) |
+| 既有键状态流转数 | 0 次 | 22 键阶段稳定(DONE 12 + GREEN 9 + YELLOW 1 + RED 0) |
+
+### 1.4 本轮 git 提交与推送证据(真理证据)
+
+| 提交 sha | 关联任务/能力键 | 阶段 | 触及文件类型 | 目标分支 | 已推送远端 | 备注 |
+|----------|-----------------|------|--------------|----------|------------|------|
+| `31b1be1c` | EVD-7R 报告闭环 23 SC | 证据合入 | 测试/契约 | cfclub | 是 | `test(EVD-7R)`,IN CHAIN ✓ |
+| `7f9b978c` | EVD-8R 测评-方案 43 SC | 证据合入 | 测试/契约 | cfclub | 是 | `test(EVD-8R)`,IN CHAIN ✓ |
+| `41f1fc93` | sleep.config.tags(B-37) | YELLOW 修复 | 实现 | cfclub | 是 | B-37 空白 key 校验收紧,IN CHAIN ✓ |
+| `9628df3d` | dan.report.parseFormat(B-35) | YELLOW 修复 | 实现 | cfclub | 是 | S-cfc-34 修复,IN CHAIN ✓ |
+| `ba6b48d2` | b4-f1/f2 部署 | 部署 | 实现 | cfclub | 是 | TDD 部署 testing,IN CHAIN ✓ |
+
+> 全部 5 个本轮关联证据 sha 经 `git merge-base --is-ancestor origin/cfclub` 复核 IN CHAIN ✓。
+
+### 1.5 状态机阶段分布与部署状态
+
+| 阶段 | 键数 | 备注 |
+|------|------|------|
+| UNTRACKED | 0 | 目标池 0 键(⏳ 0|契约常量 0|用例∩⏳ 0|执行回填 0) |
+| RED | 0 | — |
+| YELLOW | 1 | sleep.config.tags(B-37 已修 `41f1fc934`,待测试复跑 1 例后 mark-green 收口) |
+| GREEN | 9 | achievement.grid.fiveCol / admin.auth.login / dan.assessment.getActiveMaterial / dan.report.parseFormat / growth.history.view / health.report.createReport / health.report.upload / langgraph.questionnaire.generate / task.history.view |
+| DONE | 12 | auth.guest.jwtGuard / auth.guest.wechatLogin / dimension.registry.getTree / dimension.registry.mapLegacyCode / family.familyMembers.addMember / family.familyMembers.listMembers / guide.trainingPlan.getPlanDetail / guide.trainingPlan.getPlanList / task.parent.createTask / task.parent.review / task.parent.switchRole / guide.reward.expectedCf |
+
+| 部署项 | 值 | 说明 |
+|--------|-----|------|
+| 本轮部署次数 | 0 次 | 无新部署需求(目标池 0 键,无 YELLOW 新增) |
+| 最新 last_deployed | confirmed=20 | deploy_state 确认 20 项;pending 1(testing `292087771`,done=true,done_at 11-06 19:22) |
+| GREEN 部署链验证 | 通过 | GREEN 9 键部署链已闭环(deploy_verify 放行口径) |
+
+### 1.6 数据源健康度
+
+| 数据源 | 读取键数 | 是否正常 | 备注 |
+|--------|----------|----------|------|
+| 需求侧(⏳ wish) | 0 | 正常 | 与状态机读数一致(无 ⏳ 键) |
+| 契约常量(contract) | 0 | 正常 | 与状态机读数一致 |
+| 测试用例引用(test_case_map) | 0 | 正常 | 与状态机读数一致 |
+| 执行结果回填(case_results) | 0 | 正常 | 与状态机读数一致 |
+
+---
+
+## 二、下一步需要研发处理的事项
+
+| 序号 | 事项描述 | 依赖/前置条件 | 预估完成时间 | 优先级 | 状态 |
+|------|----------|---------------|--------------|--------|------|
+| 1 | **SC 覆盖登记推进**:EVD-3/4/5/6(50+ SC)+ EVD-7R(23 SC)+ EVD-8R(43 SC)+ EVD-10(32 SC)+ EVD-11(25 SC)+ EVD-14(17 SC)+ EVD-15(8 SC)证据已全部合入 origin/cfclub,但 sc-key-map.md 仍 15/241——测试侧按 TR-11②/TR-12/13/14/16 登记滞后 | 测试侧复验登记(证据已全部就绪:三类流执行证据 + 已推远端) | 2026-11-07 16:00 | P1 | 待测试处理 |
+| 2 | sleep.config.tags 收口:B-37 修复 `41f1fc934` 后复跑 1 例确认绿 → `--mark-green`(注意 `pom.xml:28 skipTests=true` 须 `-DskipTests=false`) | 测试侧复跑 | 2026-11-07 16:00 | P0 | 待测试处理 |
+| 3 | O-12 生效后:G-3(SC-T-29 批量导出)立设计契约 + 测试侧补用例;G-1/2 先判端点组合承载,可承载则复用补证 | 主理人最终确认 O-12 | 2026-11-07 18:00 | P1 | 待主理人确认 |
+| 4 | B-31 凭据轮换:7 处明文凭据轮换为环境变量注入(新值由主理人/运维侧生成) | 人工运维动作(不可自动化) | 待定 | P0 | 待运维 |
+| 5 | B-36 黑盒 API 套件从 skip 恢复(`.env.test` + seed 账号 + globalSetup 登录取 token) | 测试侧处置 | 2026-11-08 | P2 | 待测试处理 |
+
+---
+
+## 三、阻塞与风险
+
+| 阻塞项 | 影响范围 | 阻塞类型 | 处置进展 | 需要协调 |
+|--------|----------|----------|----------|----------|
+| O-12 契约核对批次实现缺口(G-1/2/3 + A-04/14/19/20/21) | 对应 SC 无法补三类流证据,契约补建滚动节奏(O-7 批次门控) | 需求/设计 | 已裁决(10:25 研发负责人错峰03 意见,本角色复核支持),**待主理人最终确认** | 主理人确认生效 |
+| SC 覆盖登记滞后(15/241 vs 证据已齐) | 场景覆盖矩阵进度失真,无法回答「哪些 SRS 场景已跑通」 | 测试 | TR-11② 已请求测试侧登记,多批证据合入后仍 15 | 测试侧按 TR 登记推进 |
+| sleep.config.tags 卡 YELLOW(差测试 mark-green) | 1 键无法闭环(22 键剩 1) | 测试 | B-37 已修,待测试复跑 1 例 | 测试侧复验 |
+| 主工作树恒不干净(170 处他人 WIP) | `--clean-check` 恒退出 1;按 O-3 判据③(SC 关联提交级核验)**不计入 SC 跑通** | 流程 | 维持 O-5 责任归属(产生 WIP 的研发会话),本角色不代提交 | 产生 WIP 会话收口 |
+| B-31 明文凭据(安全) | 仓库读权限者可直取测试/生产凭据 | 研发 | O-10 已裁决(改造 + 轮换 + CI 扫描),轮换待运维执行 | 主理人/运维执行轮换 |
+
+---
+
+## 四、下轮工作规划
+
+| 计划项 | 预计完成时间 | 前置依赖 | 涉及会话标签 | 备注 |
+|--------|--------------|----------|--------------|------|
+| 复核测试侧 SC 覆盖登记进度(目标 15→150+) | 2026-11-07 16:00 | 测试侧按 TR-11②~TR-16 登记 | 测试角色 | 证据已全部就绪,登记是唯一缺口 |
+| 跟进 sleep.config.tags mark-green 收口 | 2026-11-07 16:00 | 测试侧复跑 1 例 | 测试角色 | B-37 已修,只差复验 |
+| O-12 生效后启动 G-3 契约立项(批量导出端点 + 角色权限) | 2026-11-07 18:00 | 主理人确认 | 研发负责人 | 契约 + 用例齐备才入 TDD 池(O-4 先例,防假 RED) |
+| 全 241 SC 契约核对收尾核对(EVD-1~15 已全产出) | 2026-11-07 18:00 | 无 | 研发负责人 | 核对缺口已按 O-12 登记,剩余为登记 + 缺口处置 |
+
+---
+
+## 附录
+
+### 附录 A:收到的测试反馈问题明细
+
+| 问题 ID | 能力键/任务号 | 问题类型 | 来源 | 接收时间 | 处置状态 | 关联 sha |
+|---------|---------------|----------|------|----------|----------|----------|
+| B-37 | sleep.config.tags | 实现缺陷 | 测试台账(复验派发) | 2026-11-07 05:50 | 已解决(待测试 mark-green 收口) | 41f1fc934 |
+| B-39 | pom.xml skipTests 假绿 | 测试误判 | 测试台账(69 轮) | 2026-11-07 | 处理中(并行轮派发 lan-248) | — |
+| B-40 | CI 两级软门禁 | 需求口径 | 测试台账(69 轮) | 2026-11-07 | 处理中(并行轮派发 lan-204) | — |
+
+### 附录 B:任务分派与会话恢复明细
+
+| 任务号 | 目标会话标签 | 任务类型 | 分派时间 | 当前状态 | 恢复会话标签 | 恢复原因 | 恢复时间 |
+|--------|--------------|----------|----------|----------|--------------|----------|----------|
+| (无) | — | — | — | — | — | — | — |
+
+> 本轮零派发、零恢复(任务池 0 pending;EVD-14/15 由并行研发负责人轮派发并回收,产出 `f62848933`/`a72afb137` 已合入)。
+
+### 附录 C:新增入池任务明细
+
+| 任务号 | 能力键 | 需求溯源 | 设计溯源 | 创建时间 | 状态 |
+|--------|--------|----------|----------|----------|------|
+| (无) | — | — | — | — | — |
+
+> 本轮 `--changes` 无新增能力键 → 无 `--add-task`(防假 RED,符合 O-2 方案 A 纪律)。
+
+### 附录 D:变更感知明细(来自 `--changes --days 3`)
+
+| 变更文档 | 文档类型 | 变更日期 | 新增能力键 | 是否已入池 |
+|----------|----------|----------|------------|------------|
+| docs/superpowers/specs/2026-11-07-planner-zone-contract.md(EVD-10 32 SC) | 设计 | 2026-11-07 | 无(既有键) | 是(EVD-10 已合入) |
+| docs/superpowers/specs/2026-11-07-admin-ops-contract.md(EVD-11 25 SC) | 设计 | 2026-11-07 | 无(既有键) | 是(EVD-11 已合入) |
+| docs/tdd/dev/evidence-report-closedloop-evd7-20261107.md(EVD-7R 23 SC) | 测试用例 | 2026-11-07 | 无 | 是(31b1be1c 已合入) |
+| docs/tdd/test/automation-run-evd8-assessment-plan-2026-11-07.md(EVD-8R 43 SC) | 测试用例 | 2026-11-07 | 无 | 是(7f9b978c 已合入) |
+| docs/superpowers/specs/2026-11-07-ai-butler-contract.md(EVD-14 17 SC) | 设计 | 2026-11-07 | 无(既有键) | 是(f62848933 已合入) |
+
+---
+
+## 本角色补充裁决(O-12 复核意见)
+
+**O-12(契约核对批次实现缺口)复核**:研发负责人错峰03 已于 10:25 给出裁决意见(G-1 SC-T-10 预约未到→先判端点组合承载;G-2 SC-T-22 方案归档→评估报告归档端点是否可承载;G-3 SC-T-29 批量导出→确认真实缺口、先立契约;A-04 表已 DROP→判已下线待产品确认;A-14/19/20 平台支撑能力→回传产品侧确认验收方式;A-21 舌诊→载体部分存在、产品确认是否补齐)。**本角色复核认为意见合理**:符合 O-2 方案 C(测试侧以现有端点组合补证,不建伪实现任务)+ O-4 先例(真契约缺口先立契约+用例再入池,防假 RED),且 EVD-10/EVD-11 缺口均已「仅登记不实现」锚定,无派发伪任务风险。**建议主理人按此最终确认生效**;生效前维持「待产品确认」状态,不派发。
+
+---
+
+**汇报要点速览**:状态机 22 键稳定(DONE 12 + GREEN 9 + YELLOW 1 + RED 0);任务池 0 pending、5 实例全 idle → 池空稳态;`--changes` 无新增能力键 → 无 `--add-task`;EVD-7R/8R 证据已合入(31b1be1c/7f9b978c)且 SC 覆盖证据齐备但 sc-key-map 仍 **15/241**(测试侧登记滞后为唯一实质缺口);O-12 裁决意见复核支持,待主理人最终确认;主工作树 170 处他人 WIP(O-3 判据③不计入 SC 跑通)。零实现提交,全程只读核查 + 标准汇报。

+ 150 - 0
docs/tdd/orchestration/report-dev-2026-11-07-1330.md

@@ -0,0 +1,150 @@
+# 研发负责人工作汇报(研发负责人 · 错峰03 · 2026-11-07 13:30 轮)
+
+> **说明**:本报告依据 `tdd-orchestrator` 研发负责人模板填写,数据来自本轮(`2026-11-07 13:30`)只读核查(`--status` / `--changes --days 7` / `--clean-check`)、git 真理核验(`git fetch` + `git merge-base --is-ancestor`)与远端 `docs/tdd/**` 文档复核。
+> **角色纪律**:本轮为只读检查 + 主理人裁决 + 汇报汇总;未代写实现、未改业务代码、未动 `.tdd/state.json` 与他人 WIP(纪律④)。
+
+---
+
+## 基本信息
+
+| 项目 | 内容 |
+|------|------|
+| 汇报人 | 研发负责人(tdd-orchestrator · 错峰03 自动化) |
+| 汇报周期 | 2026年11月07日 13:27 ~ 13:30(错峰03 槽位,文件名时间戳全局唯一) |
+| 负责模块/单元范围 | cfc 全量(scope 空) |
+
+---
+
+## 一、本轮工作数据汇总
+
+### 1.1 问题接收与处置
+
+| 指标 | 数量 | 填写说明 |
+|------|------|----------|
+| **收到的测试反馈问题数*** | `0` 个 | 本轮 `--changes --days 7` + 远端 `blockers.md` 复核:无新增 `B-*` 阻塞(既有 B-31/B-33/B-34/B-35/B-36/B-37/B-39 均为前轮已登记项) |
+| **已解决问题数*** | `1` 个 | **O-12 主理人最终确认**:EVD-10 规划师域 G-1/2/3 + EVD-11 平台运营域 A-04/14/19/20/21 实现缺口裁决生效——G-1/G-2 走端点组合+测试侧复用补证,G-3 先立契约+用例齐备再入池;A-04 判已下线需产品确认,A-14/19/20/21 回传产品侧确认验收方式;不建伪实现任务、不派发 R-Y 链路 |
+| **待解决研发问题数** | `5` 个 | ① B-31(P0 安全,application.yml 7 处明文凭据)——O-10 已裁决,**阻塞=运维侧生成新凭据并注入环境变量**;② 规划师域 G-1/2/3(EVD-10)+ 平台运营域 A-04/14/19/20/21(EVD-11)缺口落地——O-12 已裁决生效,**G-3 需先立契约 + 测试用例齐备后入池,G-1/2 测试侧复用补证**;③ sleep.config.tags `--mark-green` 收口——TR-11 已送达,**待测试侧复验**;④ B-36 根级黑盒 23 例全 skip——测试侧自处置(seed 账号 + globalSetup + CI 注入);⑤ 部署缺口 8+ 提交 + tasks.json `last_deployed` 顶层字段丢失——待部署侧核对回填 |
+
+### 1.2 任务分发与会话管理
+
+| 指标 | 数量 | 填写说明 |
+|------|------|----------|
+| **分派任务数*** | `0` 个 | 本轮 0 派发:① 能力键 TDD 池空(⏳ 0,账本 0 pending / 0 dispatched);② 无中断会话需恢复;③ EVD-10/11 契约批次缺口按 O-12 裁决**不建伪任务**,不满足 `--add-task` 溯源铁律 |
+| **恢复智能体会话数*** | `0` 个 | 无中断会话需恢复(hub.py 最近可达快照 5 节点 250 会话全部 idle,busy=0 interrupted=0) |
+| **当前在用会话数** | `0` 个 | 本环境无可达 opencode 会话(config.json 会话 id 为占位符);远程会话由 dispatcher 通道管理 |
+| **会话平均利用率** | `N/A` | 池空 + 契约批次被决策门控 → 本轮无派发对象 |
+
+> **R2「不留空闲」核验**:池空 + 无中断 + 契约批次门控 → 本轮无派发对象,非「会话空闲未派」。
+
+### 1.2b 各会话当前工作状态列表
+
+> 沿用最近可达快照(2026-11-07 09:54 巡检,5 节点 250 会话全 idle,busy=0 interrupted=0):
+
+| 会话标签 | 会话 ID | 当前状态 | 当前任务 | 静默/空闲时长 | 工作目录 | 备注 |
+|----------|---------|----------|----------|---------------|----------|------|
+| lan-189 | (50 会话) | `idle` | — | 长 | 干净 | 配额 0/3,剩余 3 |
+| lan-204 | (50 会话) | `idle` | — | 长 | 干净 | 配额 0/3,剩余 3 |
+| local-lan-4090 | (50 会话) | `idle` | — | 长 | 干净 | 配额 0/3,剩余 3 |
+| mes-local-45278 | (50 会话) | `idle` | — | 长 | 干净 | 配额 0/3,剩余 3;O-11 已于 09:5x 会话执行完毕(fb5412e92 落地) |
+| pub-iwintrue | (50 会话) | `idle` | — | 长 | 干净 | 配额 0/3,剩余 3;CFC 部署/契约核对主通道 |
+
+> **任务账本**(.workbuddy/tasks.json):34 条 = 33 done + 1 failed(S-cfc-20 tokens=0),0 pending / 0 dispatched。
+
+### 1.3 变更感知与新任务建立
+
+| 指标 | 数量 | 填写说明 |
+|------|------|----------|
+| **感知需求/设计变更数*** | `200` 提交 / `238` 文件 | `--changes --days 7`:设计文档 94(EVD-10~15 契约、huawei-health 方案、商城价格排序需求等)+ 测试用例文档 16(EVD-3~8R 证据、测试负责人 63~68 轮汇报)+ 其他 128;**无新增能力键(0→0),变化集中在既有键** |
+| **新增入池任务数*** | `0` 个 | `--add-task` 需合法 `unit.domain.action` + `--requirement`/`--design` 溯源,本轮 0→0 无新键可入池;O-12 裁决明确「不建伪实现任务」 |
+| **既有键状态流转数** | `0` 次 | 状态机自 06:16 轮稳定(DONE12 / GREEN9 / YELLOW1 / RED0),本轮无阶段流转 |
+
+### 1.4 本轮 git 提交与推送证据(真理证据)
+
+> 本实例本轮为只读核查 + 裁决轮,以下为本轮**核验确认的推进证据**(皆已推 origin/cfclub,非本实例产出)。
+
+| 提交 sha | 关联任务/能力键 | 阶段 | 触及文件类型 | 目标分支 | 已推送远端 | 备注 |
+|----------|-----------------|------|--------------|----------|------------|------|
+| `f62848933` | EVD-14 AI/Butler 域 17 SC 契约核对(12:08) | 契约产出 | 文档(butler-contract) | cfclub | 是 | IN CHAIN ✅;SC-B-01/02/04/05/06/07/09/10/11/12 + SC-H-01~07 |
+| `a72afb137` | EVD-15 跨角色域剩余 8 SC 契约核对(12:31 合并) | 契约产出 | 文档(cross-role-contract) | cfclub | 是 | IN CHAIN ✅;SC-X-03/08/11/14/15/16/17/18 |
+| `41f1fc934` | sleep.config.tags(B-37 修复) | YELLOW 证据 | 实现(PublicConfigController.java) | cfclub | 是 | IN CHAIN ✅;`key.trim().isEmpty()` 校验收紧 |
+| `04d87cfd1` | sleep.config.tags(yellow_sha 登记) | YELLOW 登记 | 实现+契约 | cfclub | 是 | IN CHAIN ✅(历史误配登记,真实实现 93aad0152/f8b145d3a/4734bcf45 已在链) |
+| `1b15b67d2` | langgraph.questionnaire.generate(B-34) | GREEN | 实现(cfc-langgraph/src) | cfclub | 是 | IN CHAIN ✅(O-9 已纳入 main 归口) |
+| `9628df3d9` | dan.report.parseFormat(B-35) | GREEN | 实现(cfc-backend/src/main) | cfclub | 是 | IN CHAIN ✅ |
+| `dceeaaeaf` | 游客域 SC-G-01/03/04/05/07/08/09/10/11/12(10 SC) | GREEN 证据 | 测试(71 例三类流契约) | cfclub | 是 | IN CHAIN ✅,sc-key-map 已登记跑通(累计 15/241) |
+
+### 1.5 状态机阶段分布与部署状态
+
+| 阶段 | 键数 | 备注 |
+|------|------|------|
+| UNTRACKED | `0` | 已发现未入池 |
+| RED | `0` | 无待开发 |
+| YELLOW | `1` | sleep.config.tags(B-37 已修复,待测试侧复验 mark-green,TR-11 已送达) |
+| GREEN | `9` | admin.auth.login / health.report.upload / health.report.createReport / dan.assessment.getActiveMaterial / dan.report.parseFormat / task.history.view / growth.history.view / achievement.grid.fiveCol / langgraph.questionnaire.generate |
+| DONE | `12` | auth.guest.jwtGuard / auth.guest.wechatLogin / dimension.registry.getTree / dimension.registry.mapLegacyCode / family.familyMembers.addMember / family.familyMembers.listMembers / family.parent.switchRole / guide.trainingPlan.getPlanDetail / guide.trainingPlan.getPlanList / reward.cfEstimate.expectedCf / task.parent.createTask / task.parent.review |
+
+| 部署项 | 值 | 说明 |
+|--------|-----|------|
+| 本轮部署次数 | `0` 次 | 本实例未派发部署 |
+| 最新 `last_deployed` | `292087771` | .tdd/deploy_state.json:confirmed 20 + pending 1(292087771 done at 11-06 19:22);⚠ tasks.json 顶层 `last_deployed=None`(其他槽位重写 tasks.json 时字段丢失,可从 deploy_state 推导,下轮编排侧核对回填) |
+| GREEN 部署链验证 | 部分通过 | **部署缺口 8+ 提交**在 292087771 后未确认 testing 部署:41f1fc93(B-37)/ ba6b48d2(B-4 F1/F2)/ 9628df3d9(B-35)/ 1b15b67d2(B-34)/ def9c83a3(商品标签)/ 30ae86d7d(引导浮层)/ 895e500e1(cfc-web 标签选择器)等,登记待部署侧核对(测试侧已在干净 worktree 旁路复验主要键,不阻断 GREEN 证据) |
+
+### 1.6 数据源健康度
+
+| 数据源 | 读取键数 | 是否正常 | 备注 |
+|--------|----------|----------|------|
+| 需求侧(⏳ wish) | `0` | 正常 | 目标池 0 键(`--status` 实测:⏳ 0/契约 0/用例∩⏳ 0/回填 0),非静默失效 |
+| 契约常量(contract) | `0` | 正常 | CFC 当前启用发现/跟踪模式,契约常量仅作标注用 |
+| 测试用例引用(test_case_map) | `0` | 正常 | — |
+| 执行结果回填(case_results) | `0` | 正常 | — |
+
+---
+
+## 二、下一步需要研发处理的事项
+
+| 序号 | 事项描述 | 依赖/前置条件 | 预估完成时间 | 优先级 | 状态 |
+|------|----------|---------------|--------------|--------|------|
+| 1 | EVD-10 规划师域 G-1/2/3 缺口落地(O-12 已生效) | G-1/G-2:测试侧按端点组合补证(不建任务);**G-3(SC-T-29 批量导出):先立设计契约(批量导出端点 + 角色权限)+ 测试用例齐备后,再入池派开发** | 11-08 前 | P1 | O-12 已裁决生效,G-3 待契约补建 |
+| 2 | EVD-11 平台运营 A-04/14/19/20/21 验收方式确认(O-12 已生效) | 产品侧确认 A-04 废弃 / A-14/19/20/21 运维配置承载方式(TR-14 已登记) | 11-08 前 | P1 | O-12 已裁决生效,待产品侧 |
+| 3 | sleep.config.tags `--mark-green` 收口跟进 | 测试侧复验 SleepConfigTagsMechanismContractTest 10/10(TR-11) | 11-07 当日 | P1 | 待测试处理 |
+| 4 | B-31 配置改造(application.yml `${ENV}` 占位 + 7 处凭据去明文) | **运维侧生成新凭据 + 部署环境变量注入就绪**(O-10 已裁决) | 运维就绪后 1 轮 | P0 | 待启动(阻塞=运维) |
+| 5 | 部署缺口 8+ 提交确认 testing 部署 + tasks.json `last_deployed` 回填 | 部署侧核对确认 | 11-07 当日 | P2 | 待部署侧/编排侧 |
+
+---
+
+## 三、阻塞与风险
+
+| 阻塞项 | 影响范围 | 阻塞类型 | 处置进展 | 需要协调 |
+|------|----------|----------|----------|----------|
+| sleep.config.tags 待 mark-green(B-37 已修复 41f1fc934 IN CHAIN) | 1 键 YELLOW 卡位 | 测试侧 | TR-11 已送达,**待测试侧独立复验后 mark-green** | 测试侧 |
+| B-31 运维侧凭据轮换 | application.yml 7 处明文 | 研发/运维 | O-10 已裁决生效(${ENV} 占位 + .env + 环境变量注入;历史泄露凭据需人工轮换) | **运维侧**(新凭据注入) |
+| EVD-10 G-3 批量导出缺口 | SC-T-29(G-3) | 研发 | **O-12 已裁决生效**:先立契约 + 用例齐备再入池派开发;不建伪任务 | 研发侧(契约 + 测试) |
+| EVD-11 A-04/14/19/20/21 无 HTTP 载体 | SC-A-04/14/19/20/21 | 产品/研发 | **O-12 已裁决生效**:A-04 判已下线需产品确认废弃;A-14/19/20/21 回传产品确认验收方式;不建伪任务 | **产品侧** |
+| 部署缺口 8+ 提交确认 | 10 GREEN/YELLOW 键 testing 部署验证 | 部署/编排 | deploy_state.json confirmed 20 + pending 1 done;tasks.json 顶层 last_deployed 字段丢失待回填 | 部署侧/编排侧 |
+
+---
+
+## 附录 A:主理人裁决落地追踪
+
+| 裁决号 | 主题 | 研发负责人裁决时间 | 主理人最终确认 | 落地状态 |
+|--------|------|------------------|---------------|----------|
+| O-1~O-11 | 前序裁决 | 各轮 | 各轮 | 已全部生效 |
+| **O-12** | **契约核对批次实现缺口(EVD-10 规划师域 3 SC + EVD-11 平台运营域 5 SC)** | **2026-11-07 10:25** | **2026-11-07 13:30(本轮)** | **已裁决生效** |
+
+**O-12 主理人最终确认意见**:
+1. **G-1(SC-T-10 no-show)/ G-2(SC-T-22 方案归档)**:走端点组合 + 复用补证路径,**不建伪实现任务**;测试侧以现有端点组合补三类流证据。
+2. **G-3(SC-T-29 批量导出)**:确认真实实现缺口(现仅本人导出),**先立设计契约(批量导出端点 + 角色权限)+ 测试用例齐备,才可入 TDD 池派开发**(O-4 先例)。
+3. **A-04(表已 DROP)**:判定为「已下线」,需产品侧确认废弃(SC 标注「不适用/已废弃」)或映射替代端点。
+4. **A-14/19/20/21(监控告警/工单/限流熔断/舌诊)**:属平台支撑能力,可能以运维/配置方式承载,**回传产品侧确认验收方式后再定证据策略**。
+5. 生效前 EVD-10 缺口维持「仅登记不实现」、EVD-11 缺口维持「待产品确认」,**不派发伪任务**。
+
+---
+
+## 附录 B:SC 覆盖台账
+
+- sc-key-map 唯一 SC ID = **241** ✓(O-8 已归一,DEV_GOAL 顶层目标 241)
+- 累计已跑通:**15/241**(O-3 收口 SC-G-02/G-06/P-03/P-10/P-11 + 游客域 SC-G-01/03/04/05/07/08/09/10/11/12)
+- 本轮 EVD-14(17 SC)/ EVD-15(8 SC)契约核对产物已合入 origin/cfclub,待测试侧补三类流证据后纳入累计
+
+---
+
+*本报告单文件精确入库(hash-object + 临时索引法,不触碰主工作树 index),message 不含能力键名防误配。*

+ 82 - 0
docs/需求分析/2026-11-06-优惠券已领取券按钮修复需求变更.md

@@ -0,0 +1,82 @@
+# 我的优惠券:已领取券按钮修复(转赠+使用)+ 使用按券类型跳转 — 需求变更说明(2026-11-06)
+
+> **版本:** v1.0(主理人裁定,TDD 编排器入池依据)
+> **来源:** 用户 2026-11-06 需求 @skill:tdd-orchestrator "我的优惠券中,显示的是已经领取的,当前还显示为'立即领取',点击会报优惠券不存在。已经领取的优惠券应该是转赠和使用两个按钮,如果是指定商品的优惠券,则直接跳到商品下单页面,并自动选上优惠券,如果是全品类的,跳到商城;如果是会员券,跳到开通会员。"
+> **关联台账:** `docs/superpowers/specs/2026-11-06-coupon-claimed-actions.md`(实现任务书)
+> **状态:** ✅ 已完成开发 + 独立核验 + 已合入 cfclub(`1392c7e0b`)
+> **范围:** 仅小程序 `cfc-frontend/pages/profile-extra/coupons.vue` + `cfc-frontend/pages/shop/checkout/checkout.vue`;后端零改动
+
+---
+
+## 一、需求清单(原话)
+
+1. **修复 bug**:「我的优惠券」已领取的券仍显示「立即领取」,点击报「优惠券不存在」→ 应为**「转赠」+「使用」**两个按钮。
+2. **使用按钮按券类型跳转**:指定商品券 → 商品下单页并**自动选上优惠券**;全品类 → 商城;会员券 → 开通会员。
+
+---
+
+## 二、现状与根因(cfclub @ 2026-11-06)
+
+| 项 | 定位 | 关键事实 |
+|----|------|---------|
+| 券实例 vs 券模板 | `CouponService.toCouponVO`(L100-128) | 返回 `{id(券实例id=family_coupon.id), userCouponId, couponId(模板id), applicableTo, productId, ...}`——**`id` 与 `userCouponId` 同值、`couponId` 是模板 id** |
+| bug 根源 | `coupons.vue` L82-85 + L247-251 | `canClaim(coupon)` 对 AVAILABLE 券返回 true → 显示「立即领取」→ `handleClaim` 调 `claimCoupon(coupon.id)`,而 `coupon.id` 是**券实例 id** 非模板 id → 后端查无模板 → 报「优惠券不存在」 |
+| 券类型分发 | `coupons.vue` 数据 | `applicableTo`(PRODUCT/ALL/MEMBERSHIP)+ `productId`(指定商品券)已随 toCouponVO 返回 |
+| 确认订单页 | `checkout.vue` L644 `onLoad`、L806 `selectCoupon`、L615 `ownedCoupons` computed | 未处理 `options.couponId`;`ownedCoupons` 已按商品/金额/可用性过滤(PRODUCT 券按 `c.productId===currentProductId` 匹配) |
+| 目标路由 | 已有 | 确认订单 `/pages/shop/checkout/checkout`;商城 `/pages/shop/index/index`;开通会员 `/pages/membership/upgrade`;赠送券 `/pages/membership/gift-coupon` |
+| 商品详情/下单 | `productDetail({id})`(api.js L1684)+ `product-detail.vue` L773 | 详情页写 `checkoutItems` storage 后跳 checkout——本任务改用 coupons.vue 直接组装 checkoutItems |
+
+**结论:** 根因是「已领取券误显『立即领取』」+「claimCoupon 用错 id(券实例当模板)」。后端契约已完备,**纯前端**改动(2 文件)。
+
+---
+
+## 三、需求变更定义(目标态)
+
+### 变更 C1 · 已领取券显示「转赠」+「使用」双按钮(删除「立即领取」)
+- `coupons.vue` 模板 L82-89 替换:删除 `canClaim`/`handleClaim` 分支(及 `claimCoupon` import),已领取券(可用 tab、status AVAILABLE/空)显示 `<view class="coupon-actions"><button class="use-btn">使用</button><button class="gift-btn">转赠</button></view>`。
+- 已使用/已过期券不显示操作按钮(保持状态徽标)。
+
+### 变更 C2 · 「使用」按券类型分派
+- `useCoupon(coupon)`:
+  - **PRODUCT**(指定商品):`productDetail({id: coupon.productId})` 取商品 → `uni.setStorageSync('checkoutItems', [{productId, productName, coverImage, unitPrice, quantity:1}])` → `uni.navigateTo('/pages/shop/checkout/checkout?from=coupon&id=<商品id>&couponId=<coupon.id>')`(**couponId 必须用券实例 id**)。
+  - **ALL**(全品类):`uni.navigateTo('/pages/shop/index/index')`。
+  - **MEMBERSHIP**(会员券):`uni.navigateTo('/pages/membership/upgrade')`。
+  - 兜底(ASSESSMENT/其他/null):跳商城 `/pages/shop/index/index`。
+  - 未登录:先 `openLoginSheet()`;`productId` 为空:兜底跳商城。
+- `giftCoupon(coupon)`:`uni.navigateTo('/pages/membership/gift-coupon')`。
+
+### 变更 C3 · checkout 自动选券(from=coupon 场景)
+- `checkout.vue` `onLoad`:`this._autoCouponId = options && options.couponId ? parseInt(options.couponId) : null`。
+- `loadCoupons` 回调后 `autoSelectCoupon()`:在 `ownedCoupons`(已按商品/金额过滤)中匹配 `c.id === this._autoCouponId`(券实例 id)→ 命中 `selectCoupon(matched)` 触发服务端重算折扣;**未命中静默忽略**(minSpend 不满足/券失效/被过滤不报错不强制)。
+- 只影响 `from=coupon` 场景;其他入口(购物车/商品详情/扫码特价)行为不变。
+- **验收口径**:已领取券显示「转赠/使用」不报「优惠券不存在」;指定商品券使用→确认订单页+自动选中;全品类→商城;会员券→开通会员;转赠→赠送页;已用/过期无操作按钮;构建 exit 0。
+
+---
+
+## 四、TDD 入池
+
+| 能力键 | 变更 | requirement 溯源 | design 溯源 |
+|--------|------|-----------------|------------|
+| `coupon.claimed.actions` | C1+C2 | 本文档需求 1+2 | `CouponService.toCouponVO`(券实例 id 契约,已合入)+ `productDetail` + 目标路由 |
+| `coupon.checkout.autoselect` | C3 | 本文档需求 2 | `checkout.vue ownedCoupons/selectCoupon`(已合入) |
+
+> RED 契约以「已合入 origin/cfclub 的 `CouponService.toCouponVO` 券实例 id 契约」为准。
+
+---
+
+## 五、风险与注意事项(主理人核实)
+
+1. **跳转必须用 `coupon.id`(券实例 id)作为 checkout 的 couponId 参数**,勿用 `coupon.couponId`(模板 id,会导致后端查无)——实现已遵循,核验通过。
+2. **自动选券只匹配 ownedCoupons 内**(避免选到不可用券),未命中静默忽略。
+3. **`ownedCoupons` 对 MEMBERSHIP 券会过滤**(checkout 不用于会员券),会员券走「使用→开通会员」不经 checkout,无冲突。
+4. **CF 值兑换 tab 与领券中心不受影响**(getExchangeableCoupons/exchangeCouponByCf 保留;coupon-center.vue 独立)。
+
+---
+
+## 六、关联记录
+
+- **实现任务书**:`docs/superpowers/specs/2026-11-06-coupon-claimed-actions.md`
+- **开发**:commit `1392c7e0b`(仅 coupons.vue +111/-19、checkout.vue +22;后端零改动)
+- **核验**:主理人独立核验通过(双按钮 ✓ / 按类型分派 ✓ / 券实例 id ✓ / 自动匹配静默忽略 ✓)
+- **合入**:已入 cfclub `1392c7e0b`
+- **backlog**:`DOC-2026-11-06-coupon-claimed-actions`(done)

+ 74 - 0
docs/需求分析/2026-11-06-我的团队收益口径与邀请分享需求变更.md

@@ -0,0 +1,74 @@
+# 我的团队:推广收益内外口径对齐 + 邀请成员直接分享 — 需求变更说明(2026-11-06)
+
+> **版本:** v1.0(主理人裁定,TDD 编排器入池依据)
+> **来源:** 用户 2026-11-06 需求 @skill:tdd-orchestrator "1、打卡和成长记录是如何定义的? 2、我的团队中的推广收益,外面显示的和里面不一致 3、在我的团队中邀请成员,应该直接分享页面,要满足推荐、下级绑定的需求"
+> **关联台账:** `docs/superpowers/specs/2026-11-06-team-invite-share.md`(实现任务书)
+> **状态:** ✅ 已完成开发 + 独立核验 + 已合入 cfclub(`037aeca7e`)
+> **范围:** 仅小程序 `cfc-frontend/pages/profile-main/profile.vue`;后端零改动
+
+---
+
+## 一、需求清单(原话)
+
+1. (理解性,已另行回答)**打卡和成长记录是如何定义的**——查证结论:打卡=家庭全部成员 `family_members.streak_days` 的**最高值**(max);成长记录=`growth_record` 按 `parent_id=当前登录用户` 统计(家长维度)——均来自 `UserController.getProfileStats`。**无代码改动。**
+2. **推广收益内外不一致**(bug 修复):profile「我的团队」外面显示的推广收益 与 推广中心里面显示的 不一致 → 统一口径。
+3. **邀请成员直接分享**:团队区块加「邀请成员」入口 → 点击**直接打开分享**(分享海报),满足**推荐 + 下级绑定**。
+
+---
+
+## 二、现状与根因(cfclub @ 2026-11-06)
+
+| 项 | 定位 | 关键事实 |
+|----|------|---------|
+| 收益-外面(旧口径) | `profile.vue` `loadReferralEarnings()`(L625-631) | `getReferralSummary().totalEarnings` = `CommissionRecord(status='settled')` 佣金求和(`CommissionService.getReferralSummary` L234-254) |
+| 收益-里面(新口径) | 推广中心 `promotion/index.vue` 收益总览(L119) | `summary.totalEarned` = `getCfSummary()`(`POST /api/commission/cf/summary`,CF 值总览) |
+| **不一致根因** | 两个数据源 | 外面=旧佣金表、里面=CF 值总览 → **数字不一致** |
+| 邀请入口 | `profile.vue`「我的团队」区块(L190-212) | 现有「我的团队」`goTeamTree`(L197)+「推广收益」`goToWealthCenter`(L204),**无邀请成员入口** |
+| 复用分享组件 | `profile.vue` 已有 `ContentSharePoster`(L346-357)+ `showShareQr()`(L682-709) | `scene='ref='+referralCode`、路径 `pages/index-home/index`,海报二维码含推荐码——**复用即可满足推荐/下级绑定** |
+
+**结论:** 需求 1 无代码改动;需求 2/3 均为**纯前端**改动(1 文件 profile.vue),后端零改动。
+
+---
+
+## 三、需求变更定义(目标态)
+
+### 变更 T1 · 推广收益改 CF 总览口径(与推广中心一致)
+- `loadReferralEarnings()` 由 `getReferralSummary().totalEarnings` 改为 `getCfSummary().totalEarned`(CF 值总览口径,与推广中心「累计获取CF」一致)。
+- **单位**:promotion 用 `cfVal(summary.totalEarned)` 直接展示原始值(`cfVal` 不分→元换算),故 profile 侧**同样不除 100**,保证「我的团队」推广收益数字 = 推广中心「累计获取CF」数字。
+- import:`getReferralSummary` → `getCfSummary`(确认无其他引用后替换)。
+
+### 变更 T2 · 团队区块加「🤝 邀请成员」→ 直接分享
+- 「我的团队」区块(L190-212)新增 `ach-item`「🤝 邀请成员」`@click="showShareQr"`(**复用现有 ContentSharePoster**,`scene='ref='+推荐码`,路径 `pages/index-home/index`)。
+- 好友扫码进首页自动绑定推荐人 → **下级绑定满足**。
+- 团队网格 2 项→3 项:新增 `.achievement-grid.team-grid .ach-item { width: 33.33% }`(3 列等宽),保持既有视觉协调(不触碰 `.achievement-grid` 全局样式,避免影响成就/卡券区块)。
+- **验收口径**:profile 推广收益 = 推广中心累计收益(元,数字一致);团队区块有「邀请成员」入口点击直接打开分享海报(含推荐码二维码);分享后好友扫码注册自动绑定推荐人;构建 exit 0。
+
+---
+
+## 四、TDD 入池
+
+| 能力键 | 变更 | requirement 溯源 | design 溯源 |
+|--------|------|-----------------|------------|
+| `team.referral.earnings.cf` | T1 | 本文档需求 2 | `CommissionController /api/commission/cf/summary`(CF 总览,已合入)+ `promotion/index.vue cfVal` 口径 |
+| `team.invite.share` | T2 | 本文档需求 3 | `profile.vue ContentSharePoster/showShareQr`(已合入,scene=ref=推荐码) |
+
+> RED 契约以「已合入 origin/cfclub 的 `CommissionController.cf/summary` 契约」为准。
+
+---
+
+## 五、风险与注意事项(主理人核实)
+
+1. **单位换算**:promotion 用 cfVal 且不分→元换算,profile 需**保持一致(不除 100)**——实现已按此执行,核验通过;若后端返回单位变更需同步。
+2. **网格 2 项→3 项**:布局可能拥挤,已用 `team-grid` 独立类 3 列等宽隔离,不触碰全局 `.achievement-grid`(不影响成就 5 列/卡券中心布局)。
+3. **邀请分享复用现有组件**:不新建、不改 ContentSharePoster 组件。
+4. **需求 1(定义)无代码改动**,不在本任务范围。
+
+---
+
+## 六、关联记录
+
+- **实现任务书**:`docs/superpowers/specs/2026-11-06-team-invite-share.md`
+- **开发**:commit `037aeca7e`(仅 profile.vue +20/-5;后端、api.js 零改动)
+- **核验**:主理人独立核验通过(CF 口径一致 ✓ / 邀请分享复用 ✓ / 3 列等宽隔离 ✓)
+- **合入**:已入 cfclub `037aeca7e`
+- **backlog**:`DOC-2026-11-06-team-invite-share`(done)

+ 71 - 0
docs/需求分析/2026-11-06-报告上传页关联商品卡片需求变更.md

@@ -0,0 +1,71 @@
+# 报告上传页关联商品卡片区 + 成员选择固定四列 — 需求变更说明(2026-11-06)
+
+> **版本:** v1.0(主理人裁定,TDD 编排器入池依据)
+> **来源:** 用户 2026-11-06 需求 @skill:tdd-orchestrator "小程序上传报告页面下面,增加关联的商品,可直接点击购买。注意报告对应的商品可能会有重复。报告上传页面的成员选择,固定为四列"
+> **关联台账:** `docs/superpowers/specs/2026-11-06-report-upload-product-cards.md`(实现任务书)
+> **状态:** ✅ 已完成开发 + 独立核验 + 合入 cfclub(`0bdc78def`,cherry-pick 自 feature/report-upload-product-cards 77f54d998)
+> **范围:** 仅小程序报告上传页 `cfc-frontend/pages/health/report-upload.vue`;后端零改动
+
+---
+
+## 一、需求清单(原话)
+
+1. 小程序上传报告页面下面,**增加关联的商品**,可直接点击购买;**报告对应的商品可能会有重复**(须按商品 id 去重)。
+2. 报告上传页面的**成员选择固定为四列**。
+
+---
+
+## 二、现状核查(cfclub @ 2026-11-06)
+
+| 项 | 定位 | 关键事实 |
+|----|------|---------|
+| 报告类型→商品配置 | `report_type_registry.linked_product_id`(后台 `ReportTypeManagement.vue` 可配) | 10 个报告类型,初始 linked_product_id 全 NULL(**用户需在后台配置后才展示**) |
+| 公开接口 | `ReportParserController` L57-92 `POST /api/report-parser/types/links` | 返回 `[{typeId, linkedProductId, productName}]`(is_active=1 且 linked_product_id 非空,productName 批量回填)——**已存在** |
+| 前端数据源 | `report-upload.vue` L172 `reportTypeLinks`、L248-256 `loadReportTypeLinks()`、L151 import `getReportTypeLinks` | 现有「支持的报告类型」guide-section 已用 `getTypeLink(typeId)` + `goProductDetail(typeId)` 显示「查看商品」小链接 |
+| 商品详情页 | `/pages/discover-detail/product-detail/product-detail?id=<productId>`(已有) | 详情页含购买/加购入口,可直接复用 |
+| 成员 4 列 | `.member-item { width: calc((100% - 48rpx) / 4) }`(commit `750f3d8b2` 已入 cfclub) | **已生效**;本任务只核验未被回退(核验通过,无需再改) |
+
+**结论:** 后端接口与商品详情页均已具备,**纯前端**改动(1 文件),复用既有 API,不新增后端接口。
+
+---
+
+## 三、需求变更定义(目标态)
+
+### 变更 R1 · 报告上传页下方新增「关联商品」卡片区(去重,可点击购买)
+
+- **数据**:computed `linkedProducts`——遍历 `reportTypeLinks`,**用 Set 按商品 id(`linkedProductId`)去重**(`parseInt` 后过滤 null/0/NaN),返回 `[{productId, productName}]`;同商品多个报告类型只保留一条(productName 取首个匹配);接口失败/未配置返回 `[]`。
+- **UI**:`guide-section` **之后**新增 `<view class="linked-products-section" v-if="linkedProducts.length > 0">`——标题「关联商品」+ 说明「以下商品与您的报告类型对应,点击可查看并购买」+ 商品卡片列表(网格 2 列),每卡片含商品名 + 「查看 / 购买」按钮;视觉与 guide-section 一致(圆角/背景/`$uni-space-*` 令牌)。
+- **交互**:点击卡片/按钮 → `goProductDetailByProduct(productId)` → `uni.navigateTo('/pages/discover-detail/product-detail/product-detail?id=' + productId)`(直接按商品 id 跳转,不依赖 typeId)。
+- **静默回退**:未配置任何关联商品 / 接口失败 → 卡片区**不显示**(不报错、不占空白)。
+- **核验项(非改动)**:成员选择 `.member-item` 4 列样式在 cfclub 仍生效(未被后续提交回退)。
+- **验收口径**:后台配置后卡片区出现且可购买;两类型挂同商品只显示一次;未配置不显示;成员 4 列;构建 `npm run build:mp-weixin` exit 0;仅改 report-upload.vue。
+
+---
+
+## 四、TDD 入池
+
+| 能力键 | 变更 | requirement 溯源 | design 溯源 |
+|--------|------|-----------------|------------|
+| `report.upload.linked-products` | R1 | 本文档需求 1 | `ReportParserController /api/report-parser/types/links`(已合入)+ `report_type_registry.linked_product_id` + 商品详情页路由 `product-detail?id=` |
+| `report.upload.member-4col` | 核验项 | 本文档需求 2 | `.member-item` 4 列样式(`750f3d8b2` 已合入) |
+
+> RED 契约以「已合入 origin/cfclub 的 `ReportParserController.types/links` 契约」为准(返回 `{typeId, linkedProductId, productName}`)。
+
+---
+
+## 五、风险与注意事项(主理人核实)
+
+1. **去重键必须用商品 id(linkedProductId),非报告类型 id**——任务书强调过,实现已按 Set<商品id> 去重,核验通过。
+2. **空值防护**:`link && link.linkedProductId` + `parseInt` 过滤无效条目(null/0/NaN),避免脏数据进入卡片区。
+3. **linked_product_id 全 NULL 是初始状态**:用户需在后台配置后卡片区才出现——这是"数据配置"而非"功能缺失",前端静默隐藏即正确行为。
+4. **成员 4 列仅核验**:`750f3d8b2` 已合入生效,本任务未改动该样式。
+
+---
+
+## 六、关联记录
+
+- **实现任务书**:`docs/superpowers/specs/2026-11-06-report-upload-product-cards.md`
+- **开发**:feature 分支 `feature/report-upload-product-cards`,commit `77f54d998`(仅 report-upload.vue +129 行,无后端/api.js/pages.json 改动)
+- **核验**:主理人独立核验通过(去重 Set 语义 ✓ / 空值防护 ✓ / 静默隐藏 ✓ / 4 列已生效 ✓ / 仅 1 文件 ✓)
+- **合入**:cherry-pick 至 cfclub `0bdc78def`
+- **backlog**:`DOC-2026-11-06-report-upload-product-cards`(done)

+ 80 - 0
docs/需求分析/2026-11-07-商城价格排序需求变更.md

@@ -0,0 +1,80 @@
+# 商城按价格排序 — 需求变更说明(2026-11-07)
+
+> **版本:** v1.0(主理人裁定,TDD 编排器入池依据)
+> **来源:** 用户 2026-11-07 需求(1 条)
+> **关联台账:** `docs/superpowers/specs/2026-11-07-shop-price-sort.md`(实现任务书,已入 cfclub `ad71c79d9`)
+> **状态:** 已完成现状核查 → 需求变更锁定 → 已派发(lan-204 `ses_e4bfe8feeffeqY4ckJyZpVoUE9`,dev 中)
+> **范围:** 仅小程序商城商品列表页交互增强;后端零改动
+
+---
+
+## 一、需求清单(原话)
+
+1. 修改小程序商城页面,**增加按价格排序的功能**。
+
+---
+
+## 二、现状核查(只读,cfclub @ 0bdc78def)
+
+| 项 | 定位 | 关键事实 |
+|----|------|---------|
+| 商城页 | `cfc-frontend/pages/shop/index/index.vue`(704 行) | 布局:搜索栏(L18-27)→ Level1 分类 chips(L29-44)→ Level2 子分类 chips(L46-66)→ 商品列表 `scroll-view.product-list`(L68-148) |
+| 商品加载 | `loadProducts()`(L221-247) | 调 `productList({page, size})`,**未传有效 sort**;默认走后端 `sort_order asc + created_at desc` |
+| 推荐商品 | `loadRecommended()`(L327 附近) | 传 `sort:'sales'`(**字符串**)——后端拦截器要求数组,**该字符串实际被忽略,无效**(现状 bug,本次不触碰推荐区) |
+| 排序机制 | `cfc-backend/.../config/SortInterceptor.java` | `preHandle` 从**请求体**读 `sort`(数组 `[{column, order}]`),白名单过滤(仅 column/order,order 转小写)→ `SortContext.set` |
+| 排序注入 | `cfc-backend/.../util/SortUtil.java` | `applySort(wrapper, SortContext.get(), null)`;列名正则 `^[a-zA-Z_][a-zA-Z0-9_]*$` 防注入;`price` 列符合白名单;驼峰→下划线映射;双 ORDER BY 反射防护 |
+| 默认排序 | `ProductService.list`(L147 附近) | 默认 `orderByAsc(Product::getSortOrder).orderByDesc(createdAt)`,随后 `SortUtil.applySort` 注入用户排序 |
+| 前端透传 | `utils/api.js` L1683 `productList(params)` | `request('/api/product/list','POST',params)` **原样透传 body** → 前端把 `sort` 数组放进 params 即可,**无需改 api.js** |
+
+**结论:** 后端排序能力已完备(`SortInterceptor` + `SortUtil` 支持 body 数组),本次**纯前端**改动,仅加排序 UI 与传参,**后端零改动**。
+
+---
+
+## 三、需求变更定义(目标态)
+
+### 变更 S1 · 商城商品列表增加「综合 / 价格↑ / 价格↓」排序栏
+
+- **UI**:在 Level2 子分类 chips(`</scroll-view>` L66)与商品列表 `scroll-view`(L68)**之间**插入排序栏(`sort-bar`),三项:综合(默认)/ 价格升序 / 价格降序;视觉与分类 chips 协调(复用 `$uni-space-*` 间距、`$facade-violet` 高亮 active)。
+- **状态**:`data` 新增 `sortType: 'default'`。
+- **方法**:
+  - `buildSort()`:`price_asc` → `[{column:'price',order:'asc'}]`;`price_desc` → `[{column:'price',order:'desc'}]`;`default` → `[]`(不传,走后端默认 `sort_order asc + created_at desc`)。
+  - `onSortSelect(type)`:相同 return;否则 `sortType=type; page=1; products=[]; noMore=false; loadProducts()`。
+- **传参**:`loadProducts`(L221-247)与 `onLoadMore`(L254-275)在构建 `params` 后追加 `const sort=this.buildSort(); if(sort.length) params.sort=sort`;`onRefresh` 不改(loadProducts 自动带当前 sortType)。
+- **排序保持**:切分类(`onCat1Select`/`onCat2Select`)不改逻辑——已重置 `page=1;products=[]`,`loadProducts` 自动带当前 sortType,**保留当前排序**(电商惯例);滚动加载沿用当前排序。
+- **推荐区隔离**:`loadRecommended` 的 `sort:'sales'` 字符串**保持原样**,推荐商品区独立于排序栏,不受影响。
+- **验收口径**:
+  1. 商城商品列表出现「综合 / 价格↑ / 价格↓」排序栏;
+  2. 点「价格↑」→ 商品按价格从低到高(第 1 页);「价格↓」→ 从高到低;「综合」→ 恢复默认(sort_order asc + created_at desc);
+  3. 切分类后保留当前排序;滚动加载/下拉刷新沿用当前排序;
+  4. 推荐商品区不受影响;
+  5. 构建 `npm run build:mp-weixin` exit 0。
+
+---
+
+## 四、TDD 入池
+
+| 能力键 | 变更 | requirement 溯源 | design 溯源 |
+|--------|------|-----------------|------------|
+| `shop.price.sort` | S1 | 本文档需求 1 + 变更 S1 | `SortInterceptor`(body 数组契约)+ `SortUtil`(price 列白名单)+ `api.js productList`(原样透传) |
+
+> 纯前端交互增强;是否入 SRS 由 TDD 编排器判定(能力键 `shop.price.sort`)。RED 契约以「已合入 origin/cfclub 的 `SortInterceptor` body 数组契约」为准。
+
+---
+
+## 五、风险与注意事项(主理人核实)
+
+1. **排序参数必须是数组** `[{column, order}]`——字符串会被后端 `SortInterceptor` 忽略(这是 `loadRecommended` 现有 `sort:'sales'` 无效的根因),前端实现务必用数组,勿复现该 bug。
+2. **双 ORDER BY 防护**:`SortUtil` 通过反射清空 wrapper 已有 orderBy 段;若某环境反射初始化失败会回退旧行为,排序可能不生效或产生双 ORDER BY——**开发会话须实测三档排序确实生效**,若后端未生效则如实报告,禁止前端硬编码排序。
+3. **`price` 列名**:须与 `product` 表实际列名一致(`SortUtil` 驼峰→下划线映射 `price`→`price`,已确认)。
+4. **仅改 1 文件**:`cfc-frontend/pages/shop/index/index.vue`;不改后端、不改 `utils/api.js`、不改 `pages.json`。
+
+---
+
+## 六、关联记录
+
+- **实现任务书**:`docs/superpowers/specs/2026-11-07-shop-price-sort.md`(已入 cfclub `ad71c79d9`)
+- **派发**:lan-204 会话 `ses_e4bfe8feeffeqY4ckJyZpVoUE9`(`opencode/deepseek-v4.1-flash-free`,dev 中);backlog `DOC-2026-11-07-shop-price-sort`(第 113 条,dispatched)
+- **当日同批未入需求变更台账的其它 3 项**(仅建了 specs 任务书,尚未回填需求变更说明,待补):
+  - 报告上传页关联商品卡片区(`specs/2026-11-06-report-upload-product-cards.md`,已合入 cfclub `0bdc78def`)
+  - 我的优惠券已领取券按钮修复(`specs/2026-11-06-coupon-claimed-actions.md`,已合入 cfclub `1392c7e0b`)
+  - 我的团队推广收益口径对齐 + 邀请成员分享(`specs/2026-11-06-team-invite-share.md`,已合入 cfclub `037aeca7e`)

Vissa filer visades inte eftersom för många filer har ändrats