2026-09-10-manual-dimension-mbti-plan.md 28 KB

五维手动填分 + MBTI 完整体系 实施计划

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 实现"只填结果"的五维手动填分功能(0-100 分,家长可给家庭成员填),以及 MBTI 完整体系(标准版 ~30题测评 + 16型人格百科 + 五维联动解读)。

Architecture: 后端新增两组 Controller/Service/Mapper/Entity;前端新增 5 个页面 + 1 个管理端页面;数据库 3 张新表(mbti_questions, mbti_results, mbti_profiles)通过 DatabaseInitializer 迁移;种子数据脚本初始化题库与 16 型画像。

Tech Stack: Java 8 + Spring Boot 2.7.18 + MyBatis-Plus / uni-app Vue 2 (微信小程序) / Vue 2 + Element UI (Web 管理端) / MySQL 8.0

Spec: 本计划基于前期 brainstorming 确认的设计,包含 Part 1(手动填分)与 Part 2(MBTI 体系)两大模块。


Global Constraints

  • 统一 @PostMapping,禁止 @GetMapping/@PutMapping/@DeleteMapping
  • 响应包装 Result<T> (code/message/data)
  • JWT Bearer Token 认证,JwtInterceptor 拦截 /api/**
  • DI 使用 @Resource,字段名必须与 Bean Name 一致
  • MyBatis-Plus @TableName + @TableId(type = IdType.AUTO)
  • 小程序限制:禁止 ?.、CSS Grid、:key 表达式、new Date(string)(用 parseDate())
  • 数据库迁移唯一入口:DatabaseInitializer.runMigrations(),使用 ensureColumn / try-catch 幂等
  • 每次迁移必须同步更新 schema.sql 对应 CREATE TABLE
  • 新增接口前必须查阅 docs/superpowers/api/API_REFERENCE.md 确认无重复
  • Bean 命名冲突检查:新增 Controller/Service 确保类名不重复
  • 编译验证:mvn clean compile 唯一验证方式

文件结构映射

后端新增/修改文件

文件 类型 说明
entity/MbtiQuestion.java 新增 MBTI 题库实体
entity/MbtiResult.java 新增 用户 MBTI 测评结果实体
entity/MbtiProfile.java 新增 16型人格画像库实体
mapper/MbtiQuestionMapper.java 新增 题库 Mapper
mapper/MbtiResultMapper.java 新增 结果 Mapper
mapper/MbtiProfileMapper.java 新增 画像 Mapper
service/MbtiService.java 新增 MBTI 业务接口
service/impl/MbtiServiceImpl.java 新增 MBTI 业务实现
controller/family/MbtiController.java 新增 /api/family/mbti 接口
controller/family/ManualDimensionScoreController.java 新增 /api/family/manual-score 接口
service/ManualDimensionScoreService.java 新增 手动填分业务接口
service/impl/ManualDimensionScoreServiceImpl.java 新增 手动填分业务实现
dto/MbtiQuestionVO.java 新增 题目 VO
dto/MbtiResultVO.java 新增 结果 VO
dto/MbtiProfileVO.java 新增 画像 VO
dto/ManualScoreSubmitDTO.java 新增 手动提交 DTO
dto/ManualScoreVO.java 新增 手动填分 VO
config/DatabaseInitializer.java 修改 添加 3 表迁移

前端新增/修改文件

文件 类型 说明
pages/family/manual-score-entry.vue 新增 手动填分录入页
pages/family/mbti-entry.vue 新增 MBTI 入口页(选成员)
pages/family/mbti-test.vue 新增 MBTI 答题页
pages/family/mbti-result.vue 新增 MBTI 结果页
pages/family/mbti-profile.vue 新增 MBTI 16型百科页
pages/family/self-check-entry.vue 修改 增加"直接填分"按钮 + MBTI 卡片入口
utils/api.js 修改 新增 10+ API 封装
pages.json 修改 注册 5 个新页面

管理端新增文件

文件 类型 说明
src/views/admin/MbtiProfiles.vue 新增 16 型配置管理页
src/api/mbti.js 新增 管理端 MBTI API
src/router/index.js 修改 新增路由

任务分解

Phase 0: 数据库迁移与种子数据

Task 0.1: DatabaseInitializer 添加 3 表迁移

Files:

  • Modify: cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java (搜索 // 迁移 找到最新编号)
  • Modify: cfc-backend/src/main/resources/schema.sql (追加 3 表 CREATE TABLE)

Interfaces:

  • Produces: 3 张表结构在数据库中可用

    - [ ] **Step 1: 在 DatabaseInitializer.runMigrations() 末尾添加 3 个迁移块**
    

    java

// 迁移XXX: 创建 mbti_questions 表 try {

jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS mbti_questions (" +
    "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
    "dimension CHAR(1) NOT NULL COMMENT 'E/I/S/N/T/F/J/P', " +
    "question_text TEXT NOT NULL COMMENT '题目文本', " +
    "option_a TEXT NOT NULL COMMENT '选项 A(正向)', " +
    "option_b TEXT NOT NULL COMMENT '选项 B(反向)', " +
    "weight DECIMAL(3,2) DEFAULT 1.0 COMMENT '权重', " +
    "sort_order INT DEFAULT 0, " +
    "is_active TINYINT DEFAULT 1, " +
    "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
    "INDEX idx_dim_active (dimension, is_active)" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MBTI 测评题库'");
log.info("已创建 mbti_questions 表");

} catch (Exception e) { /* 忽略 */ }

// 迁移XXX+1: 创建 mbti_results 表 try {

jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS mbti_results (" +
    "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
    "user_id BIGINT NOT NULL COMMENT '答题人用户ID', " +
    "family_member_id BIGINT COMMENT '关联家庭成员ID', " +
    "answers_json TEXT COMMENT '用户答案JSON', " +
    "type_code CHAR(4) NOT NULL COMMENT 'MBTI 类型', " +
    "ei_score INT COMMENT 'E/I 原始分', " +
    "sn_score INT COMMENT 'S/N 原始分', " +
    "tf_score INT COMMENT 'T/F 原始分', " +
    "jp_score INT COMMENT 'J/P 原始分', " +
    "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
    "INDEX idx_user (user_id), " +
    "INDEX idx_member (family_member_id)" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MBTI 测评结果'");
log.info("已创建 mbti_results 表");

} catch (Exception e) { /* 忽略 */ }

// 迁移XXX+2: 创建 mbti_profiles 表 try {

jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS mbti_profiles (" +
    "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
    "type_code CHAR(4) NOT NULL UNIQUE COMMENT '类型代码', " +
    "name VARCHAR(50) COMMENT '中文名称', " +
    "description TEXT COMMENT '核心特质描述', " +
    "strengths TEXT COMMENT '优势', " +
    "weaknesses TEXT COMMENT '挑战', " +
    "career_tendency TEXT COMMENT '职业倾向', " +
    "family_style TEXT COMMENT '家庭/育儿风格', " +
    "pairing_notes TEXT COMMENT '伴侣/孩子类型搭配建议', " +
    "energy_suggestion JSON COMMENT '五维能量联动建议', " +
    "is_active TINYINT DEFAULT 1, " +
    "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
    "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MBTI 人格画像库'");
log.info("已创建 mbti_profiles 表");

} catch (Exception e) { /* 忽略 */ }


- [ ] **Step 2: 同步追加到 schema.sql 末尾**

sql -- 与上面 3 个 CREATE TABLE IF NOT EXISTS 完全一致


- [ ] **Step 3: 编译验证**

bash cd cfc-backend && mvn clean compile


#### Task 0.2: MBTI 种子数据 SQL 脚本

**Files:**
- Create: `cfc-backend/src/main/resources/migration_mbti_seed_data.sql`

**Interfaces:**
- Produces: 30 题题库 + 16 型基础画像数据

markdown

  • Step 1: 编写种子数据脚本 包含:
  • mbti_questions: 30 题(E/I 8题, S/N 8题, T/F 7题, J/P 7题),每题 dimension + question_text + option_a/option_b + weight + sort_order
  • mbti_profiles: 16 条记录(INTJ/INTP/ENTJ/ENTP/INFJ/INFP/ENFJ/ENFP/ISTJ/ISFJ/ESTJ/ESFJ/ISTP/ISFP/ESTP/ESFP),每条含 name/description/strengths/weaknesses/career_tendency/family_style/pairing_notes/energy_suggestion(JSON)

  • [ ] Step 2: 在 DatabaseInitializer 执行种子数据

    // 迁移XXX+3: MBTI 种子数据初始化
    try {
    String seedSql = FileUtils.readFileToString(new File("src/main/resources/migration_mbti_seed_data.sql"), "UTF-8");
    for (String stmt : seedSql.split(";")) {
        String trimmed = stmt.trim();
        if (!trimmed.isEmpty()) {
            jdbcTemplate.execute(trimmed);
        }
    }
    log.info("MBTI 种子数据初始化完成");
    } catch (Exception e) {
    log.warn("MBTI 种子数据初始化失败(可能已存在): {}", e.getMessage());
    }
    
  • [ ] Step 3: 编译验证

    cd cfc-backend && mvn clean compile
    

Phase 1: 后端手动填分模块

Task 1.1: ManualDimensionScore 实体、Mapper、DTO

Files:

  • Create: entity/ManualDimensionScore.java (可复用 FiveDimensionScore,仅 source_type='manual' 区分,无需新表)
  • Create: dto/ManualScoreSubmitDTO.java
  • Create: dto/ManualScoreVO.java

    - [ ] **Step 1: ManualScoreSubmitDTO**
    

    java

package com.etotem.cfc.dto; import lombok.Data; import java.math.BigDecimal; import java.util.Map;

@Data public class ManualScoreSubmitDTO {

private Long memberId;                 // 必填
private Map<String, Integer> scores;   // 必填: {"body":80,"mind":60,"wisdom":70,"action":50,"wealth":90}
private String note;                   // 可选备注

}


- [ ] **Step 2: ManualScoreVO (返回前端)**

java package com.etotem.cfc.dto; import lombok.Data; import java.util.Date; import java.util.List;

@Data public class ManualScoreVO {

private Long id;                       // five_dimension_scores.id
private Long memberId;
private String memberName;
private Map<String, Integer> scores;   // 5维度分数
private Integer totalScore;            // 5维总和
private Date createdAt;

}


- [ ] **Step 3: 编译验证**

#### Task 1.2: ManualDimensionScoreService 接口与实现

**Files:**
- Create: `service/ManualDimensionScoreService.java`
- Create: `service/impl/ManualDimensionScoreServiceImpl.java`

markdown

  • [ ] Step 1: 接口定义

    package com.etotem.cfc.service;
    import com.etotem.cfc.dto.ManualScoreSubmitDTO;
    import com.etotem.cfc.dto.ManualScoreVO;
    import java.util.List;
    
    public interface ManualDimensionScoreService {
    ManualScoreVO submit(ManualScoreSubmitDTO dto, Long userId);
    ManualScoreVO getLatest(Long userId, Long memberId);
    List<ManualScoreVO> getHistory(Long userId, Long memberId, int limit);
    }
    
  • [ ] Step 2: 实现类

    @Service
    public class ManualDimensionScoreServiceImpl implements ManualDimensionScoreService {
    @Resource private FiveDimensionScoreMapper fiveDimensionScoreMapper;
    @Resource private FiveDimensionSelfCheckMapper selfCheckMapper;
    @Resource private FamilyMemberMapper familyMemberMapper;
    @Resource private UserMapper userMapper;
    @Resource private FiveDimensionScoreService fiveDimensionScoreService;
    private final ObjectMapper objectMapper = new ObjectMapper();
    
    @Transactional(rollbackFor = Exception.class)
    public ManualScoreVO submit(ManualScoreSubmitDTO dto, Long userId) {
        // 1. 校验 memberId 存在且属于当前用户家庭
        // 2. 校验 5 维度键全为 body/mind/wisdom/action/wealth,值 0-100,至少一项>0
        // 3. 写入 five_dimension_scores (source_type='manual', source_id=自增ID)
        // 4. 写入 five_dimension_self_checks (scores_json 存 0-100 映射,totalScore=5维总和,questionIdsJson=[])
        // 5. 返回 VO
    }
    
    public ManualScoreVO getLatest(Long userId, Long memberId) {
        // 查 five_dimension_scores 最近一条 source_type='manual'
    }
    
    public List<ManualScoreVO> getHistory(Long userId, Long memberId, int limit) {
        // 查历史
    }
    }
    
  • [ ] Step 3: 编译验证

Task 1.3: ManualDimensionScoreController

Files:

  • Create: controller/family/ManualDimensionScoreController.java

    - [ ] **Step 1: Controller 实现**
    

    java

@Tag(name = "五维手动填分", description = "直接填写 0-100 分,跳过问卷") @RestController @RequestMapping("/api/family/manual-score") public class ManualDimensionScoreController {

@Resource private ManualDimensionScoreService manualScoreService;

@PostMapping("/submit")
public Result<ManualScoreVO> submit(@RequestBody ManualScoreSubmitDTO dto, @RequestAttribute("userId") Long userId) {
    // 校验 userId, 调用 service, 返回 Result
}

@PostMapping("/latest")
public Result<ManualScoreVO> latest(@RequestBody(required=false) Map<String,Object> body, @RequestAttribute("userId") Long userId) {
    Long memberId = ParamUtils.getLong(body?.get("memberId"));
    // 调用 service
}

@PostMapping("/history")
public Result<List<ManualScoreVO>> history(@RequestBody(required=false) Map<String,Object> body, @RequestAttribute("userId") Long userId) {
    Long memberId = ParamUtils.getLong(body?.get("memberId"));
    Integer limit = body?.get("limit") != null ? Integer.valueOf(body.get("limit").toString()) : 20;
    // 调用 service
}

}


- [ ] **Step 2: 编译验证**

---

### Phase 2: 后端 MBTI 模块

#### Task 2.1: MBTI 实体、Mapper

**Files:**
- Create: `entity/MbtiQuestion.java`
- Create: `entity/MbtiResult.java`
- Create: `entity/MbtiProfile.java`
- Create: `mapper/MbtiQuestionMapper.java`
- Create: `mapper/MbtiResultMapper.java`
- Create: `mapper/MbtiProfileMapper.java`

markdown

  • [ ] Step 1: MbtiQuestion 实体

    @Data @TableName("mbti_questions")
    public class MbtiQuestion implements Serializable {
    @TableId(type=IdType.AUTO) private Long id;
    private String dimension;      // E/I/S/N/T/F/J/P
    private String questionText;
    private String optionA;
    private String optionB;
    private BigDecimal weight;
    private Integer sortOrder;
    private Integer isActive;
    private Date createdAt;
    }
    
  • [ ] Step 2: MbtiResult 实体

    @Data @TableName("mbti_results")
    public class MbtiResult implements Serializable {
    @TableId(type=IdType.AUTO) private Long id;
    private Long userId;
    private Long familyMemberId;
    private String answersJson;
    private String typeCode;       // INTJ 等
    private Integer eiScore;
    private Integer snScore;
    private Integer tfScore;
    private Integer jpScore;
    private Date createdAt;
    }
    
  • [ ] Step 3: MbtiProfile 实体

    @Data @TableName("mbti_profiles")
    public class MbtiProfile implements Serializable {
    @TableId(type=IdType.AUTO) private Long id;
    private String typeCode;       // INTJ 等,唯一
    private String name;           // 架构师
    private String description;
    private String strengths;
    private String weaknesses;
    private String careerTendency;
    private String familyStyle;
    private String pairingNotes;
    private String energySuggestion; // JSON 字符串
    private Integer isActive;
    private Date createdAt;
    private Date updatedAt;
    }
    
  • [ ] Step 4: 3 个 Mapper 接口 (extends BaseMapper)

  • [ ] Step 5: 编译验证

Task 2.2: MBTI DTO/VO

Files:

  • Create: dto/MbtiQuestionVO.java
  • Create: dto/MbtiResultVO.java
  • Create: dto/MbtiProfileVO.java
  • Create: dto/SubmitMbtiDTO.java

    - [ ] **Step 1: MbtiQuestionVO** (题目含选项 A/B)
    

    java

@Data public class MbtiQuestionVO {

private Long id;
private String dimension;        // E/I/S/N/T/F/J/P
private String questionText;
private OptionVO optionA;
private OptionVO optionB;
@Data static class OptionVO { String value; String label; }

}


- [ ] **Step 2: SubmitMbtiDTO**

java @Data public class SubmitMbtiDTO {

private Long familyMemberId;
private List<AnswerItem> answers;
@Data static class AnswerItem { Long questionId; String answer; } // answer: "A" 或 "B"

}


- [ ] **Step 3: MbtiResultVO** (结果含 4 维度分 + 类型代码)

java @Data public class MbtiResultVO {

private Long id;
private String typeCode;
private String typeName;         // 中文名
private Integer eiScore;
private Integer snScore;
private Integer tfScore;
private Integer jpScore;
private String eiType;           // "E" 或 "I"
private String snType;           // "S" 或 "N"
private String tfType;           // "T" 或 "F"
private String jpType;           // "J" 或 "P"
private MbtiProfileVO profile;   // 画像详情
private Date createdAt;

}


- [ ] **Step 4: MbtiProfileVO** (画像详情)

java @Data public class MbtiProfileVO {

private String typeCode;
private String name;
private String description;
private String strengths;
private String weaknesses;
private String careerTendency;
private String familyStyle;
private String pairingNotes;
private Map<String, String> energySuggestion; // 5维度建议

}


- [ ] **Step 5: 编译验证**

#### Task 2.3: MbtiService 接口与实现

**Files:**
- Create: `service/MbtiService.java`
- Create: `service/impl/MbtiServiceImpl.java`

markdown

  • [ ] Step 1: 接口

    public interface MbtiService {
    List<MbtiQuestionVO> getQuestions();
    MbtiResultVO submit(SubmitMbtiDTO dto, Long userId);
    MbtiResultVO getLatest(Long userId, Long memberId);
    MbtiProfileVO getProfile(String typeCode);
    List<MbtiResultVO> getHistory(Long userId, Long memberId, int limit);
    }
    
  • [ ] Step 2: 实现核心计算逻辑

    @Service
    public class MbtiServiceImpl implements MbtiService {
    @Resource private MbtiQuestionMapper questionMapper;
    @Resource private MbtiResultMapper resultMapper;
    @Resource private MbtiProfileMapper profileMapper;
    @Resource private FamilyMemberMapper familyMemberMapper;
    private final ObjectMapper objectMapper = new ObjectMapper();
    
    public List<MbtiQuestionVO> getQuestions() {
        // 查 is_active=1,按 dimension+sort_order 排序,转 VO
    }
    
    @Transactional(rollbackFor = Exception.class)
    public MbtiResultVO submit(SubmitMbtiDTO dto, Long userId) {
        // 1. 校验 memberId
        // 2. 加载题库 Map<id, MbtiQuestion>
        // 3. 遍历 answers,累加 4 维度分:
        //    dimension='E' -> eiScore += (answer=="A"? weight : -weight)
        //    dimension='I' -> eiScore -= (answer=="A"? weight : -weight)  // 反向
        //    S/N、T/F、J/P 同理
        // 4. 判定 4 字母:eiScore>0?"E":"I" 等
        // 5. typeCode = ei+sn+tf+jp
        // 6. 查 profileMapper.selectByTypeCode(typeCode)
        // 7. 写入 mbti_results
        // 8. 组装 VO 返回
    }
    
    // getLatest, getProfile, getHistory 均为简单查询
    }
    
  • [ ] Step 3: 编译验证

Task 2.4: MbtiController

Files:

  • Create: controller/family/MbtiController.java

    - [ ] **Step 1: Controller 实现**
    

    java

@Tag(name = "MBTI 人格测评", description = "标准版 ~30题 + 16型画像 + 五维联动") @RestController @RequestMapping("/api/family/mbti") public class MbtiController {

@Resource private MbtiService mbtiService;

@PostMapping("/questions")
public Result<List<MbtiQuestionVO>> questions(@RequestAttribute("userId") Long userId) {
    return Result.success(mbtiService.getQuestions());
}

@PostMapping("/submit")
public Result<MbtiResultVO> submit(@RequestBody SubmitMbtiDTO dto, @RequestAttribute("userId") Long userId) {
    return Result.success(mbtiService.submit(dto, userId));
}

@PostMapping("/latest")
public Result<MbtiResultVO> latest(@RequestBody(required=false) Map<String,Object> body, @RequestAttribute("userId") Long userId) {
    Long memberId = ParamUtils.getLong(body?.get("memberId"));
    return Result.success(mbtiService.getLatest(userId, memberId));
}

@PostMapping("/profile/{typeCode}")
public Result<MbtiProfileVO> profile(@PathVariable String typeCode, @RequestAttribute("userId") Long userId) {
    return Result.success(mbtiService.getProfile(typeCode.toUpperCase()));
}

@PostMapping("/history")
public Result<List<MbtiResultVO>> history(@RequestBody(required=false) Map<String,Object> body, @RequestAttribute("userId") Long userId) {
    Long memberId = ParamUtils.getLong(body?.get("memberId"));
    Integer limit = body?.get("limit") != null ? Integer.valueOf(body.get("limit").toString()) : 20;
    return Result.success(mbtiService.getHistory(userId, memberId, limit));
}

}


- [ ] **Step 2: 编译验证**

---

### Phase 3: 前端手动填分页面

#### Task 3.1: manual-score-entry.vue

**Files:**
- Create: `cfc-frontend/pages/family/manual-score-entry.vue`

markdown

  • Step 1: 页面结构
  • 顶部:成员选择器(横向滑动卡片,显示头像+昵称+角色标签,复用 getFamilyMemberList)
  • 中部:5 个维度滑块(Slider 组件或自定义拖拽条),每维度 0-100,实时显示数值
  • 底部:提交按钮(主色调渐变),提交后跳转 self-check-result.vue (复用结果页,传参 source=manual)

  • [ ] Step 2: 逻辑

  • onLoad 调用 getFamilyMemberList({visibleOnly:true}) 获取成员列表

  • 默认选中当前用户自己

  • scores 对象响应式绑定 5 维度

  • 提交调用 submitManualScore({memberId, scores}) → 跳转结果页

  • [ ] Step 3: 样式遵循现有设计 Token (--color-primary, --bg, --shadow-clay 等)

Task 3.2: 修改 self-check-entry.vue 入口

Files:

  • Modify: cfc-frontend/pages/family/self-check-entry.vue

    - [ ] **Step 1: 底部按钮区域增加两个按钮**
    

    html

再次自检 忽略本次 开始自检

直接填分

MBTI 人格测试


- [ ] **Step 2: methods 新增跳转**

javascript goManualScore() { uni.navigateTo({ url: '/pages/family/manual-score-entry' }) }, goMbti() { uni.navigateTo({ url: '/pages/family/mbti-entry' }) }


---

### Phase 4: 前端 MBTI 页面 (4 个页面)

#### Task 4.1: mbti-entry.vue (入口选成员)

**Files:**
- Create: `cfc-frontend/pages/family/mbti-entry.vue`

markdown

  • 成员横向选择卡片 (复用 manual-score-entry 逻辑)
  • 大字标题:"MBTI 人格测评" + 副标题"发现你的天赋型,获得专属育儿建议"
  • "开始测评" 按钮 → navigateTo mbti-test.vue?memberId=xxx

    
    #### Task 4.2: mbti-test.vue (答题页)
    
    **Files:**
    - Create: `cfc-frontend/pages/family/mbti-test.vue`
    
    

    markdown

  • [ ] 单题卡片模式 (类似 self-check.vue)

  • 进度条:当前题号 / 总题数

  • 题目文本 + 两个选项按钮 (A/B),左右分布

  • 选中高亮,可点击切换

  • 底部:上一题 / 下一题 (最后一题显示"提交")

  • 答案暂存本地数组 answers[]

  • 提交调用 submitMbti({familyMemberId, answers}) → navigateTo mbti-result.vue?id=xxx

    
    #### Task 4.3: mbti-result.vue (结果页)
    
    **Files:**
    - Create: `cfc-frontend/pages/family/mbti-result.vue`
    
    

    markdown

  • [ ] 大字展示类型代码 (如 INTJ) + 中文名 (架构师) + 动画入场

  • 4 维度雷达图 (Canvas 2D,复用 self-check-result 雷达逻辑,4 轴 E/I S/N T/F J/P)

  • 画像卡片:核心特质 / 优势 / 挑战 / 职业倾向 / 家庭风格 / 搭配建议

  • 五维能量联动建议卡片 (从 profile.energySuggestion 读取,展示 5 个维度建议)

  • 底部按钮:查看百科、分享、重新测评

    
    #### Task 4.4: mbti-profile.vue (16 型百科)
    
    **Files:**
    - Create: `cfc-frontend/pages/family/mbti-profile.vue`
    
    

    markdown

  • [ ] 4x4 网格卡片 (16 种类型),每卡片显示类型代码 + 中文名 + 1 行标签

  • 点击卡片 → 调用 getMbtiProfile(typeCode) → 弹窗或新页展示完整画像

  • 顶部搜索/筛选:按 E/I、S/N、T/F、J/P 筛选

    
    ---
    
    ### Phase 5: 管理端 MBTI 配置
    
    #### Task 5.1: 管理端 API 与页面
    
    **Files:**
    - Create: `cfc-web/src/api/mbti.js`
    - Create: `cfc-web/src/views/admin/MbtiProfiles.vue`
    - Modify: `cfc-web/src/router/index.js`
    
    

    markdown

  • [ ] Step 1: API 封装 (mbti.js)

    export function getMbtiProfileList() { return request('/api/admin/mbti/profile/list', 'POST') }
    export function saveMbtiProfile(data) { return request('/api/admin/mbti/profile/save', 'POST', data) }
    export function deleteMbtiProfile(id) { return request('/api/admin/mbti/profile/delete', 'POST', {id}) }
    export function initMbtiSeedData() { return request('/api/admin/mbti/seed/init', 'POST') }
    
  • [ ] Step 2: MbtiProfiles.vue (Element UI Table + Dialog 编辑)

  • 列表展示 16 行:type_code, name, is_active, 操作(编辑/删除)

  • 编辑弹窗:所有字段可编辑,energy_suggestion 用 JSON 编辑器 (textarea + 格式化)

  • "初始化种子数据"按钮调用 initMbtiSeedData()

  • [ ] Step 3: 路由注册 (admin 子路由)

    
    #### Task 5.2: 管理端 Controller (可选,若需后端增删改)
    
    **Files:**
    - Create: `controller/admin/MbtiProfileController.java` (若管理端走后端 CRUD)
    - 或直接在 MbtiService 暴露 admin 方法,MbtiController 复用
    
    

    markdown

  • [ ] 视情况新增或复用,编译验证

    
    ---
    
    ### Phase 6: API 文档同步与验收
    
    #### Task 6.1: 更新 API_REFERENCE.md
    
    **Files:**
    - Modify: `docs/superpowers/api/API_REFERENCE.md`
    
    

    markdown

  • [ ] 在 Controller 总览表新增: | ManualDimensionScoreController | /api/family/manual-score | 五维手动填分 | — | | MbtiController | /api/family/mbti | MBTI 人格测评 | — | | MbtiProfileController | /api/admin/mbti/profile | MBTI 画像管理(管理端) | — |

  • [ ] 详细记录每个接口的请求/响应示例

    
    #### Task 6.2: 全量编译 + 功能自测
    
    

    markdown

  • [ ] cd cfc-backend && mvn clean compile

  • [ ] 启动后端,用 Postman/curl 测试 8 个新接口

  • [ ] 微信开发者工具预览 5 个新页面流程

  • [ ] 管理端 npm run serve 验证 MBTI 配置页 ```


    验收标准 (Definition of Done)

    1. 手动填分:家长可在 self-check-entry 点"直接填分" → 选成员 → 5 个滑块 0-100 → 提交 → 结果页显示雷达图/总分,能量沙盘实时更新
    2. MBTI 测评:入口 → 选成员 → 30 题答完 → 显示类型代码+画像+4维雷达+五维建议 → 可查百科
    3. 管理端:16 型画像可增删改,种子数据一键初始化
    4. 数据库:3 表结构正确,迁移幂等,schema.sql 同步
    5. 编译:mvn clean compile 通过,无 Lint 报错

    执行建议

    推荐 Subagent-Driven 并行执行 (7 个独立子任务可并行):

    并行组 任务 预计耗时
    A Task 0.1+0.2 (DB迁移+种子) 30 min
    B Task 1.1-1.3 (手动填分后端) 45 min
    C Task 2.1-2.4 (MBTI 后端) 60 min
    D Task 3.1-3.2 (前端手动填分) 45 min
    E Task 4.1-4.4 (前端 MBTI 4页) 90 min
    F Task 5.1-5.2 (管理端) 45 min
    G Task 6.1-6.2 (文档+验收) 30 min

    两阶段 Review: 每组完成后暂停,人工确认无冲突再合并。


    Plan complete and saved to docs/superpowers/plans/2026-09-10-manual-dimension-mbti-plan.md. Two execution options:

    1. Subagent-Driven (recommended) - I dispatch a fresh subagent per task group, review between groups, fast iteration

    2. Inline Execution - Execute tasks in this session using executing-plans, batch execution with checkpoints

    Which approach?