Просмотр исходного кода

fix: 五维自检未传familyMemberId时自动解析为当前用户本人

修复新用户完成五维问卷后,点加入今日微行动报'未找到家庭成员信息'的问题。
根因:submitSelfCheck/submitSubDimensionCheck 直接存储 dto.familyMemberId,
前端提交问卷时不传该字段,导致自检记录与返回VO的 familyMemberId 为 null,
前端 joinMicroAction 无法拿到 memberId。
修复:familyMemberId 为空时,按 userId 自动解析用户本人对应的家庭成员(注册时已自动创建)。
Xiaogang Liao 3 недель назад
Родитель
Сommit
29da052d57

+ 27 - 2
cfc-backend/src/main/java/com/etotem/cfc/service/FiveDimensionSelfCheckService.java

@@ -5,7 +5,9 @@ import com.etotem.cfc.dto.SelfCheckQuestionVO;
 import com.etotem.cfc.dto.SelfCheckResultVO;
 import com.etotem.cfc.dto.SubmitSelfCheckDTO;
 import com.etotem.cfc.dto.WuxingSourcingAdviceVO;
+import com.etotem.cfc.entity.FamilyMember;
 import com.etotem.cfc.entity.FiveDimensionSelfCheck;
+import com.etotem.cfc.mapper.FamilyMemberMapper;
 import com.etotem.cfc.mapper.FiveDimensionSelfCheckMapper;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -39,6 +41,9 @@ public class FiveDimensionSelfCheckService {
     @Resource
     private FiveDimensionSelfCheckMapper selfCheckMapper;
 
+    @Resource
+    private FamilyMemberMapper familyMemberMapper;
+
     @Resource
     private WuxingSourcingService wuxingSourcingService;
 
@@ -268,7 +273,7 @@ public class FiveDimensionSelfCheckService {
         // 保存到现有记录(复用 scoresJson,以 sub_ 前缀区分)
         FiveDimensionSelfCheck record = new FiveDimensionSelfCheck();
         record.setUserId(userId);
-        record.setFamilyMemberId(dto.getFamilyMemberId());
+        record.setFamilyMemberId(resolveFamilyMemberId(dto.getFamilyMemberId(), userId));
         record.setAnswersJson(toJson(dto.getAnswers()));
         // 合并到现有 scoresJson(保留 dimension 级 key + 新增 sub_ 前缀 key)
         try {
@@ -384,7 +389,7 @@ public class FiveDimensionSelfCheckService {
         // 保存记录
         FiveDimensionSelfCheck record = new FiveDimensionSelfCheck();
         record.setUserId(userId);
-        record.setFamilyMemberId(dto.getFamilyMemberId());
+        record.setFamilyMemberId(resolveFamilyMemberId(dto.getFamilyMemberId(), userId));
         record.setAnswersJson(toJson(dto.getAnswers()));
         record.setScoresJson(toJson(scoreMap));
         record.setTotalScore(totalScore);
@@ -474,6 +479,26 @@ public class FiveDimensionSelfCheckService {
         return 0;
     }
 
+    /**
+     * 解析自检关联的家庭成员ID。
+     * familyMemberId 未传时默认解析为当前用户本人对应的家庭成员(注册时已自动创建),
+     * 保证前端"加入今日微行动"等后续联动能拿到有效的 memberId。
+     */
+    private Long resolveFamilyMemberId(Long memberId, Long userId) {
+        if (memberId != null) {
+            return memberId;
+        }
+        if (userId == null) {
+            return null;
+        }
+        FamilyMember own = familyMemberMapper.selectOne(
+                new LambdaQueryWrapper<FamilyMember>()
+                        .eq(FamilyMember::getUserId, userId)
+                        .last("LIMIT 1")
+        );
+        return own != null ? own.getId() : null;
+    }
+
     private String toJson(Object obj) {
         try {
             return objectMapper.writeValueAsString(obj);

+ 130 - 0
cfc-backend/src/test/java/com/etotem/cfc/service/FiveDimensionSelfCheckServiceTest.java

@@ -0,0 +1,130 @@
+package com.etotem.cfc.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.etotem.cfc.dto.SelfCheckResultVO;
+import com.etotem.cfc.dto.SubmitSelfCheckDTO;
+import com.etotem.cfc.entity.FamilyMember;
+import com.etotem.cfc.entity.FiveDimensionSelfCheck;
+import com.etotem.cfc.mapper.FamilyMemberMapper;
+import com.etotem.cfc.mapper.FiveDimensionSelfCheckMapper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * FiveDimensionSelfCheckService 纯单元测试(无 SpringBootTest,避免 MySQL 连接)
+ *
+ * 测试范围:
+ * - submitSelfCheck: familyMemberId 未传时自动解析为当前用户本人对应的家庭成员(Bug 修复回归)
+ * - submitSelfCheck: familyMemberId 已传时直接使用,不覆盖
+ */
+public class FiveDimensionSelfCheckServiceTest {
+
+    @Mock
+    private FiveDimensionSelfCheckMapper selfCheckMapper;
+
+    @Mock
+    private WuxingSourcingService wuxingSourcingService;
+
+    @Mock
+    private OnboardingService onboardingService;
+
+    @Mock
+    private FamilyMemberMapper familyMemberMapper;
+
+    private FiveDimensionSelfCheckService service;
+
+    @BeforeEach
+    public void setup() throws Exception {
+        MockitoAnnotations.openMocks(this);
+        service = new FiveDimensionSelfCheckService();
+        setField(service, "selfCheckMapper", selfCheckMapper);
+        setField(service, "wuxingSourcingService", wuxingSourcingService);
+        setField(service, "onboardingService", onboardingService);
+        setField(service, "familyMemberMapper", familyMemberMapper);
+
+        // 默认 mock:全部维度健康,无寻源建议
+        when(wuxingSourcingService.getAdvicesForLowScores(anyMap())).thenReturn(new ArrayList<>());
+        when(wuxingSourcingService.levelOf(anyInt())).thenReturn("healthy");
+        when(wuxingSourcingService.levelName(anyString())).thenReturn("健康");
+    }
+
+    @Test
+    public void submitSelfCheck_resolvesOwnFamilyMemberId_whenNotProvided() throws Exception {
+        Long userId = 7L;
+
+        // 当前用户本人对应一个家庭成员(注册时自动创建)
+        FamilyMember ownMember = new FamilyMember();
+        ownMember.setId(100L);
+        ownMember.setUserId(userId);
+        when(familyMemberMapper.selectOne(any(LambdaQueryWrapper.class))).thenReturn(ownMember);
+
+        // 捕获 insert 的记录并赋予 id(toVO 读取)
+        ArgumentCaptor<FiveDimensionSelfCheck> captor = ArgumentCaptor.forClass(FiveDimensionSelfCheck.class);
+        doAnswer(inv -> {
+            FiveDimensionSelfCheck r = inv.getArgument(0);
+            r.setId(5L);
+            return 1;
+        }).when(selfCheckMapper).insert(captor.capture());
+
+        SubmitSelfCheckDTO dto = new SubmitSelfCheckDTO();
+        dto.setAnswers(fullAnswers());
+
+        SelfCheckResultVO vo = service.submitSelfCheck(dto, userId);
+
+        // 返回给前端的 VO 应带上解析出的家庭成员ID
+        assertNotNull(vo.getFamilyMemberId(), "未传 familyMemberId 时应自动解析为当前用户本人的家庭成员");
+        assertEquals(100L, vo.getFamilyMemberId().longValue());
+        // 落库记录也应带解析出的家庭成员ID
+        assertEquals(100L, captor.getValue().getFamilyMemberId().longValue());
+    }
+
+    @Test
+    public void submitSelfCheck_keepsProvidedFamilyMemberId() throws Exception {
+        Long userId = 7L;
+
+        ArgumentCaptor<FiveDimensionSelfCheck> captor = ArgumentCaptor.forClass(FiveDimensionSelfCheck.class);
+        doAnswer(inv -> {
+            FiveDimensionSelfCheck r = inv.getArgument(0);
+            r.setId(6L);
+            return 1;
+        }).when(selfCheckMapper).insert(captor.capture());
+
+        SubmitSelfCheckDTO dto = new SubmitSelfCheckDTO();
+        dto.setFamilyMemberId(200L);
+        dto.setAnswers(fullAnswers());
+
+        SelfCheckResultVO vo = service.submitSelfCheck(dto, userId);
+
+        assertEquals(200L, vo.getFamilyMemberId().longValue());
+        assertEquals(200L, captor.getValue().getFamilyMemberId().longValue());
+        // 未触发家庭成员查询
+        verify(familyMemberMapper, never()).selectOne(any(LambdaQueryWrapper.class));
+    }
+
+    private List<SubmitSelfCheckDTO.AnswerItem> fullAnswers() {
+        List<SubmitSelfCheckDTO.AnswerItem> answers = new ArrayList<>();
+        for (int i = 1; i <= 15; i++) {
+            SubmitSelfCheckDTO.AnswerItem item = new SubmitSelfCheckDTO.AnswerItem();
+            item.setQuestionId(i);
+            item.setAnswer("A");
+            answers.add(item);
+        }
+        return answers;
+    }
+
+    private static void setField(Object target, String fieldName, Object value) throws Exception {
+        java.lang.reflect.Field f = target.getClass().getDeclaredField(fieldName);
+        f.setAccessible(true);
+        f.set(target, value);
+    }
+}