Parcourir la source

feat(selfcheck): 新增子维度自检题库(行6维/心自驱力自我概念/智成长型思维/富家族传承)及计分

Xiaogang Liao il y a 1 mois
Parent
commit
242f786564

+ 3 - 0
cfc-backend/src/main/java/com/etotem/cfc/dto/SelfCheckQuestionVO.java

@@ -16,6 +16,9 @@ public class SelfCheckQuestionVO {
     /** 维度 code(body/wisdom/wealth/action/mind) */
     private String dimension;
 
+    /** 子维度 key(如 actionParentChild / driveAutonomy,维度级题目为空) */
+    private String subDim;
+
     /** 维度中文名 */
     private String dimensionName;
 

+ 178 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/FiveDimensionSelfCheckService.java

@@ -60,6 +60,9 @@ public class FiveDimensionSelfCheckService {
     /** 15 题静态题库(书稿终章原文) */
     private static final List<SelfCheckQuestionVO> QUESTION_BANK = buildQuestionBank();
 
+    /** 子维度附加题库(行6维 + 心·自驱力/自我概念 + 智·成长型思维 + 富·家族传承) */
+    private static final List<SelfCheckQuestionVO> SUB_DIMENSION_QUESTION_BANK = buildSubDimensionQuestionBank();
+
     private static List<SelfCheckQuestionVO> buildQuestionBank() {
         List<SelfCheckQuestionVO> bank = new ArrayList<>();
         // 身(1-3)
@@ -115,6 +118,79 @@ public class FiveDimensionSelfCheckService {
         return bank;
     }
 
+    /** 子维度附加题库构建(对应五维内部子维度:行6维 + 心自驱力/自我概念 + 智成长型思维 + 富家族传承) */
+    private static List<SelfCheckQuestionVO> buildSubDimensionQuestionBank() {
+        List<SelfCheckQuestionVO> bank = new ArrayList<>();
+        // 行·亲子关系
+        bank.add(sq(101, "action", "actionParentChild",
+                "你每天有固定时间陪伴家人/孩子聊天吗?",
+                "A", "几乎每天", "B", "每周多次", "C", "偶尔", "D", "几乎没有"));
+        bank.add(sq(102, "action", "actionParentChild",
+                "家人/孩子愿意主动跟你分享心里话吗?",
+                "A", "总是愿意", "B", "经常", "C", "有时", "D", "很少开口"));
+        // 行·夫妻/同胞关系
+        bank.add(sq(103, "action", "actionMarital",
+                "你和伴侣有专属的二人交流时间吗?",
+                "A", "每周都有", "B", "每月几次", "C", "很少", "D", "几乎没有"));
+        bank.add(sq(104, "action", "actionSibling",
+                "你和兄弟姐妹相处融洽、能互相支持吗?",
+                "A", "非常融洽", "B", "比较和谐", "C", "一般", "D", "常有摩擦"));
+        // 行·父母/亲属关系
+        bank.add(sq(105, "action", "actionParental",
+                "你有定期探望或问候父母吗?",
+                "A", "每周都联系", "B", "每月数次", "C", "偶尔", "D", "很少联系"));
+        bank.add(sq(106, "action", "actionRelative",
+                "你与祖辈/其他长辈相处和睦、有沟通吗?",
+                "A", "非常和睦", "B", "比较和睦", "C", "一般", "D", "疏远"));
+        // 行·领导/师生关系
+        bank.add(sq(107, "action", "actionLeadership",
+                "你与上级/同事的协作关系顺畅吗?",
+                "A", "非常顺畅", "B", "比较顺畅", "C", "一般", "D", "常有冲突"));
+        bank.add(sq(108, "action", "actionTeacherStudent",
+                "你与老师/导师互动积极、关系良好吗?",
+                "A", "非常积极", "B", "比较积极", "C", "一般", "D", "很少互动"));
+        // 行·社会性
+        bank.add(sq(109, "action", "actionSociality",
+                "你积极参与朋友/同事/社区的社交活动吗?",
+                "A", "经常参与", "B", "偶尔参与", "C", "很少", "D", "几乎不参与"));
+        bank.add(sq(110, "action", "actionChildSociality",
+                "你乐于和同伴合作分享、遵守规则吗?",
+                "A", "总是如此", "B", "经常", "C", "有时", "D", "很少"));
+        // 行·人际韧性
+        bank.add(sq(111, "action", "actionInterpersonal",
+                "遇到人际冲突时你能冷静处理并修复关系吗?",
+                "A", "总能", "B", "经常能", "C", "有时能", "D", "常感无助"));
+        bank.add(sq(112, "action", "actionChildInterpersonal",
+                "受委屈时你能恰当表达并处理情绪吗?",
+                "A", "总能", "B", "经常能", "C", "有时能", "D", "常憋在心里"));
+        // 心·自驱力
+        bank.add(sq(113, "mind", "driveAutonomy",
+                "面对目标你能主动开始、不需他人催促吗?",
+                "A", "总是主动", "B", "经常主动", "C", "需要提醒", "D", "总要催"));
+        bank.add(sq(114, "mind", "driveCompetence",
+                "你对自己完成任务的能力有信心吗?",
+                "A", "非常有信心", "B", "比较有信心", "C", "有些不确定", "D", "常怀疑自己"));
+        bank.add(sq(115, "mind", "driveRelatedness",
+                "你对家庭/团队有强烈的归属感和认同感吗?",
+                "A", "非常强烈", "B", "比较强烈", "C", "一般", "D", "几乎没有"));
+        // 心·自我概念
+        bank.add(sq(116, "mind", "selfConceptOverall",
+                "你总体上接纳并喜欢现在的自己吗?",
+                "A", "非常接纳", "B", "比较接纳", "C", "一般", "D", "不太接纳"));
+        // 智·成长型思维
+        bank.add(sq(117, "wisdom", "wisdomGrowthMindset",
+                "遇到困难时你相信凭借努力可以提升和进步吗?",
+                "A", "非常相信", "B", "比较相信", "C", "不确定", "D", "觉得天赋决定一切"));
+        // 富·家族传承
+        bank.add(sq(118, "wealth", "wealthLegacy",
+                "你有为家庭做长期财务规划或传承安排吗?",
+                "A", "有系统规划", "B", "有初步想法", "C", "想过但没做", "D", "完全没想过"));
+        bank.add(sq(119, "wealth", "wealthChildLegacy",
+                "你会引导孩子养成储蓄和规划的理财习惯吗?",
+                "A", "经常引导", "B", "偶尔引导", "C", "很少", "D", "从未"));
+        return bank;
+    }
+
     private static SelfCheckQuestionVO q(int id, String dimension, String text, String... opts) {
         SelfCheckQuestionVO vo = new SelfCheckQuestionVO();
         vo.setId(id);
@@ -135,11 +211,113 @@ public class FiveDimensionSelfCheckService {
         return vo;
     }
 
+    private static SelfCheckQuestionVO sq(int id, String dimension, String subDim, String text, String... opts) {
+        SelfCheckQuestionVO vo = q(id, dimension, text, opts);
+        vo.setSubDim(subDim);
+        return vo;
+    }
+
     /** 获取题库(15题) */
     public List<SelfCheckQuestionVO> getQuestions() {
         return QUESTION_BANK;
     }
 
+    /** 获取子维度附加题库 */
+    public List<SelfCheckQuestionVO> getSubDimensionQuestions() {
+        return SUB_DIMENSION_QUESTION_BANK;
+    }
+
+    /**
+     * 提交子维度自检问卷并计算子维度评分(0-100)
+     * 答案格式:A=100, B=66, C=33, D=0
+     * 结果以 sub_ 前缀存入 scoresJson(如 sub_actionParentChild: 75)
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public Map<String, Integer> submitSubDimensionCheck(SubmitSelfCheckDTO dto, Long userId) {
+        if (dto.getAnswers() == null || dto.getAnswers().isEmpty()) {
+            throw new IllegalArgumentException("答案不能为空");
+        }
+
+        // 题目ID → 答案
+        Map<Integer, String> answerMap = new LinkedHashMap<>();
+        for (SubmitSelfCheckDTO.AnswerItem item : dto.getAnswers()) {
+            if (item.getQuestionId() == null || item.getAnswer() == null) continue;
+            answerMap.put(item.getQuestionId(), item.getAnswer().trim().toUpperCase());
+        }
+
+        // 逐子维度计分
+        Map<String, int[]> subDimScores = new LinkedHashMap<>(); // subDim → [sum, count]
+        for (SelfCheckQuestionVO qv : SUB_DIMENSION_QUESTION_BANK) {
+            String subDim = qv.getSubDim();
+            if (subDim == null) continue;
+            String ans = answerMap.get(qv.getId());
+            if (ans == null || "E".equals(ans)) continue;
+            int score = subScoreOf(ans);
+            subDimScores.computeIfAbsent(subDim, k -> new int[2]);
+            subDimScores.get(subDim)[0] += score;
+            subDimScores.get(subDim)[1]++;
+        }
+
+        Map<String, Integer> result = new LinkedHashMap<>();
+        for (Map.Entry<String, int[]> entry : subDimScores.entrySet()) {
+            int avg = entry.getValue()[1] > 0 ? entry.getValue()[0] / entry.getValue()[1] : 0;
+            result.put("sub_" + entry.getKey(), avg);
+        }
+
+        // 保存到现有记录(复用 scoresJson,以 sub_ 前缀区分)
+        FiveDimensionSelfCheck record = new FiveDimensionSelfCheck();
+        record.setUserId(userId);
+        record.setFamilyMemberId(dto.getFamilyMemberId());
+        record.setAnswersJson(toJson(dto.getAnswers()));
+        // 合并到现有 scoresJson(保留 dimension 级 key + 新增 sub_ 前缀 key)
+        try {
+            Map<String, Object> merged = new LinkedHashMap<>();
+            String existingScoresJson = getLatestSubScores(userId);
+            if (existingScoresJson != null) {
+                Map<String, Object> existing = objectMapper.readValue(existingScoresJson, LinkedHashMap.class);
+                merged.putAll(existing);
+            }
+            merged.putAll(result);
+            record.setScoresJson(objectMapper.writeValueAsString(merged));
+        } catch (Exception e) {
+            record.setScoresJson(toJson(result));
+        }
+        record.setCreatedAt(new Date());
+        selfCheckMapper.insert(record);
+
+        return result;
+    }
+
+    /** 获取最新子维度得分JSON */
+    public String getLatestSubScores(Long userId) {
+        LambdaQueryWrapper<FiveDimensionSelfCheck> wrapper = new LambdaQueryWrapper<FiveDimensionSelfCheck>()
+                .eq(FiveDimensionSelfCheck::getUserId, userId)
+                .orderByDesc(FiveDimensionSelfCheck::getCreatedAt)
+                .last("LIMIT 1");
+        FiveDimensionSelfCheck record = selfCheckMapper.selectOne(wrapper);
+        if (record == null || record.getScoresJson() == null) return null;
+        // 仅返回 sub_ 前缀的 key
+        try {
+            Map<String, Object> all = objectMapper.readValue(record.getScoresJson(), LinkedHashMap.class);
+            Map<String, Object> sub = new LinkedHashMap<>();
+            for (Map.Entry<String, Object> e : all.entrySet()) {
+                if (e.getKey().startsWith("sub_")) {
+                    sub.put(e.getKey().substring(4), e.getValue());
+                }
+            }
+            return sub.isEmpty() ? null : objectMapper.writeValueAsString(sub);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    private int subScoreOf(String answer) {
+        if ("A".equals(answer)) return 100;
+        if ("B".equals(answer)) return 66;
+        if ("C".equals(answer)) return 33;
+        return 0;
+    }
+
     /**
      * 提交自检问卷并计算评分
      */