Browse Source

docs: 修复先天画像设计规格5处缺陷(复用calcInnateScore/数字能量mind_base/权重替换/报告表职责/配置表关系)

Xiaogang Liao 3 weeks ago
parent
commit
770c02ff54

+ 39 - 8
docs/superpowers/specs/2026-08-28-innate-portrait-mind-dimension-design.md

@@ -71,7 +71,7 @@
 ┌──────────────────────────▼──────────────────────────────────┐
 │          先天画像聚合层(新增 InnatePortraitService)          │
 │  · 聚合八字/五行/星座/血型/灵数/天赋数/命运数                  │
-│  · 计算统一 mind_base_score(各来源加权)    
+│  · 复用 calcInnateScore 计算 mind_base_score                 │
 │  · 数字能量补强(天赋数/生日数/命运数计算)                    │
 └──────────────┬───────────────────────────┬─────────────────┘
                │ 原始数据                    │ 先天画像
@@ -96,6 +96,8 @@
 
 管理端可配置各玄学来源对 `mind_base_score` 的权重、启停开关、AI 解读开关。
 
+> **与现有硬编码权重的关系**:现有 `FamilyMemberAttributeService.calcInnateScore()` 硬编码 zodiac×50% + bazi×30% + blood×20%。本表权重将**替换**该硬编码逻辑——`calcInnateScore` 改为读取本表权重(详见 §7.5)。数字能量(numsoul)作为第 4 个来源参与加权。
+
 ```sql
 CREATE TABLE IF NOT EXISTS innate_portrait_config (
     id BIGINT AUTO_INCREMENT PRIMARY KEY,
@@ -115,6 +117,8 @@ CREATE TABLE IF NOT EXISTS innate_portrait_config (
 
 AI 解读结果缓存,避免重复调用 LLM。
 
+> **职责边界**:本表只存 `portrait_json`(画像数据)+ `ai_reading`(AI解读)。**不重复存 `mind_base_score`**——先天分从 `family_member_attributes.mind_base_score` 读取,避免职责重叠。
+
 ```sql
 CREATE TABLE IF NOT EXISTS innate_portrait_report (
     id BIGINT AUTO_INCREMENT PRIMARY KEY,
@@ -123,8 +127,6 @@ CREATE TABLE IF NOT EXISTS innate_portrait_report (
     family_id BIGINT NOT NULL COMMENT '家庭ID',
     portrait_json TEXT COMMENT '先天画像数据JSON',
     ai_reading TEXT COMMENT 'AI解读文案',
-    mind_base_score INT DEFAULT 0 COMMENT '心先天基础分',
-    wisdom_base_score INT DEFAULT 0 COMMENT '智先天基础分',
     generated_at DATETIME COMMENT '生成时间',
     created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
     UNIQUE KEY uk_member (member_id, member_type)
@@ -135,6 +137,8 @@ CREATE TABLE IF NOT EXISTS innate_portrait_report (
 
 覆盖所有数字类型(生命灵数/天赋数/生日数/命运数),每个类型每个数字都有解读文案。
 
+> **与现有 `numsoul_config` 的关系**:`numsoul_config`(灵数1-9基础配置)**保留不动**,兼容现有 `getNumSoulConfig()` 调用。`numsoul_detail_config` 是**扩展**,覆盖更细的数字类型(生命灵数/天赋数/生日数/命运数),并新增 `mind_base` 字段使数字能量参与 `mind_base_score` 加权计算。
+
 ```sql
 CREATE TABLE IF NOT EXISTS numsoul_detail_config (
     id BIGINT AUTO_INCREMENT PRIMARY KEY,
@@ -142,6 +146,7 @@ CREATE TABLE IF NOT EXISTS numsoul_detail_config (
     number_value INT NOT NULL COMMENT '数字值 1-9 或主数 11/22/33',
     title VARCHAR(50) COMMENT '称号',
     keywords VARCHAR(200) COMMENT '性格关键词',
+    mind_base INT DEFAULT 0 COMMENT '心先天基础分(百分位)',
     mind_advice TEXT COMMENT '心维度成长建议',
     color_hex VARCHAR(10) COMMENT '代表色',
     created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
@@ -165,10 +170,12 @@ CREATE TABLE IF NOT EXISTS numsoul_detail_config (
 
 核心服务,职责:
 1. **聚合先天画像**:从 `family_member_attributes` + 各配置表聚合出完整先天画像(八字/五行/星座/血型/灵数/天赋数/生日数/命运数)
-2. **计算统一 mind_base_score**:按 `innate_portrait_config` 配置的权重,对各来源的 mind_base 加权求和
+2. **复用 `calcInnateScore` 计算 mind_base_score**:**不重复实现**加权逻辑,直接调用现有 `FamilyMemberAttributeService.calcInnateScore()`(该服务已实现 zodiac×50% + bazi×30% + blood×20% 加权并缓存到 `mind_base_score`)
 3. **数字能量计算**:从出生日期推导生命灵数/天赋数/生日数/命运数
 4. **AI 解读编排**:调用 LangGraph graph 生成解读,结果缓存到 `innate_portrait_report`
 
+> **关键决策**:`mind_base_score` 的计算逻辑**已存在于** `FamilyMemberAttributeService.calcInnateScore()`(zodiac×50% + bazi×30% + blood×20%,已缓存)。`InnatePortraitService` **复用**该方法,不重复实现。管理端可配置权重(`innate_portrait_config`)将**替换** `calcInnateScore` 中的硬编码权重(详见 §7.5)。
+
 ```java
 @Service
 public class InnatePortraitService {
@@ -189,13 +196,13 @@ public class InnatePortraitService {
     private AiGateway aiGateway;
 
     /**
-     * 聚合先天画像 + 计算 mind_base_score
+     * 聚合先天画像 + 复用 calcInnateScore 计算 mind_base_score
      */
     public InnatePortraitVO getInnatePortrait(Long memberId, String memberType) {
         // 1. 读取 family_member_attributes
         // 2. 聚合各来源数据(八字/五行/星座/血型/灵数)
         // 3. 计算数字能量(生命灵数/天赋数/生日数/命运数)
-        // 4. 按权重计算 mind_base_score
+        // 4. 复用 familyMemberAttributeService.calcInnateScore(memberId, memberType, "mind")
         // 5. 返回完整画像
     }
 
@@ -387,6 +394,26 @@ public Map<String, Object> generateInnateReading(Map<String, Object> portrait) {
 - 先天画像配置
 - 数字能量配置
 
+### 7.5 权重替换硬编码逻辑(缺陷修复)
+
+**现有逻辑**:`FamilyMemberAttributeService.calcInnateScore()` 硬编码:
+```java
+Integer result = (zodiacScore * 5000 + baziScore * 3000 + bloodScore * 2000) / 10000;
+```
+
+**改造**:`calcInnateScore()` 改为读取 `innate_portrait_config` 表权重:
+```java
+// 读取配置(默认 zodiac/bazi/blood/numsoul 各 25%)
+// 只累加 enabled=1 的来源
+// 权重未配置时回退到现有硬编码 50/30/20
+Integer result = (zodiacScore * wZodiac + baziScore * wBazi + bloodScore * wBlood + numsoulScore * wNumsoul) / 10000;
+```
+
+**要求**:
+- 权重未配置(表空)时回退到现有 50/30/20 硬编码,保证存量数据不变化
+- `numsoul_score` 从 `numsoul_detail_config.life_path.mind_base` 读取(按生命灵数匹配)
+- 启用的来源权重之和不足 100% 时按比例归一化,超过则按比例缩放
+
 ---
 
 ## 八、前端小程序展示设计
@@ -429,23 +456,27 @@ public Map<String, Object> generateInnateReading(Map<String, Object> portrait) {
 
 1. **迁移267**:创建 `innate_portrait_config` 表 + 种子数据(4 来源默认权重 25%)
 2. **迁移268**:创建 `innate_portrait_report` 表
-3. **迁移269**:创建 `numsoul_detail_config` 表 + 种子数据(4 类型 × 数字 1-9 + 主数)
+3. **迁移269**:创建 `numsoul_detail_config` 表 + 种子数据(4 类型 × 数字 1-9 + 主数,含 `mind_base` 字段
 
 同步更新 `schema.sql` 的 CREATE TABLE 定义。
 
+> **注意**:`numsoul_detail_config` 的 `mind_base` 字段是数字能量参与 `mind_base_score` 加权计算的关键。种子数据需为每个数字类型(life_path/talent/birthday/destiny)的每个数字(1-9 + 主数 11/22/33)配置 `mind_base` 值。
+
 ---
 
 ## 十、测试策略
 
 ### 10.1 后端单元测试
 
-- `InnatePortraitServiceTest`:数字能量计算(生命灵数/天赋数/生日数/命运数)、mind_base_score 加权计算
+- `InnatePortraitServiceTest`:数字能量计算(生命灵数/天赋数/生日数/命运数)、先天画像聚合
 - `NumSoulDetailConfigMapperTest`:配置表 CRUD
+- `FamilyMemberAttributeServiceTest`(扩展):`calcInnateScore` 权重替换后——配置表权重生效、表空时回退硬编码 50/30/20、numsoul 参与加权、权重归一化
 
 ### 10.2 后端集成测试
 
 - `InnatePortraitControllerTest`:portrait/reading/numsoul/trajectory 接口
 - AI 解读 fallback 测试(AI 不可用时降级模板文案)
+- `calcInnateScore` 权重替换回归测试(存量数据 mind_base_score 不因权重替换而变化)
 
 ### 10.3 前端测试