Explorar o código

统一家庭成员口径:memberType 赋值 + family_members userId 去重兜底

- FamilyMemberService.listMembers(): family_members 段内部按 userId 兜底去重
  同一 userId 多条记录只保留一条(优先 status=active + updated_at 最新)
  userId 为 null 的孤儿记录保持原样(local 成员)
- 补全 memberType 赋值:userId 非空且关联 User 有 openid → 'login';否则 → 'local'
  parent 段维持 'parent' 不变
- 保持 isSelf 唯一标记逻辑不变
- UserService.getVisibleMembers() 两段去重逻辑保持正确(复用 listMembers)
- mvn clean compile 验证通过
- 不改 schema、不新增接口
zxyj-web hai 2 semanas
pai
achega
c399a5acf5

+ 45 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/FamilyMemberService.java

@@ -244,6 +244,43 @@ public class FamilyMemberService {
                 new LambdaQueryWrapper<FamilyMember>()
                         .eq(FamilyMember::getFamilyId, familyId)
                         .ne(FamilyMember::getStatus, "recycled"));
+        // 兜底去重:同一 userId 多条记录只保留一条记录
+        // 优先 status=active + userId 非空,其次 updated_at 最新
+        // userId 为 null 的孤儿记录保持原样
+        if (familyMembers != null && !familyMembers.isEmpty()) {
+            Map<Long, List<FamilyMember>> userIdGroups = new java.util.HashMap<>();
+            List<FamilyMember> orphans = new ArrayList<>();
+            for (FamilyMember fm : familyMembers) {
+                if (fm.getUserId() == null) {
+                    orphans.add(fm);
+                } else {
+                    userIdGroups.computeIfAbsent(fm.getUserId(), k -> new ArrayList<>()).add(fm);
+                }
+            }
+            List<FamilyMember> deduped = new ArrayList<>();
+            deduped.addAll(orphans);
+            for (List<FamilyMember> group : userIdGroups.values()) {
+                if (group.size() == 1) {
+                    deduped.add(group.get(0));
+                } else {
+                    // status=active 优先,其次 updated_at 最新
+                    FamilyMember winner = group.stream()
+                            .max((a, b) -> {
+                                boolean aActive = "active".equals(a.getStatus());
+                                boolean bActive = "active".equals(b.getStatus());
+                                if (aActive && !bActive) return -1;
+                                if (!aActive && bActive) return 1;
+                                if (a.getUpdatedAt() != null && b.getUpdatedAt() != null) {
+                                    return b.getUpdatedAt().compareTo(a.getUpdatedAt());
+                                }
+                                return 0;
+                            })
+                            .orElse(group.get(0));
+                    deduped.add(winner);
+                }
+            }
+            familyMembers = deduped;
+        }
 
         // 查找当前查看者在 family_members 中的记录(用于计算视角标签)
         FamilyMember viewerMember = null;
@@ -305,6 +342,14 @@ public class FamilyMemberService {
             vo.setIntimacyScore(fm.getIntimacyScore());
             vo.setCommunicationScore(fm.getCommunicationScore());
 vo.setIsSelf(viewerMember != null && viewerMember.getId().equals(fm.getId()));
+            // 设置 memberType:userId 非空且关联 User 有 openid -> 'login';否则 -> 'local'
+            if (fm.getUserId() != null) {
+                User memberUser = userMapper.selectById(fm.getUserId());
+                String openid = memberUser != null ? memberUser.getOpenid() : null;
+                vo.setMemberType(openid != null && !openid.isEmpty() ? "login" : "local");
+            } else {
+                vo.setMemberType("local");
+            }
             // 设置 isSwitchable:仅无 openid 的不可登录成员可被切换(可登录成员不允许切换视角)
             if (fm.getUserId() != null) {
                 User memberUser = userMapper.selectById(fm.getUserId());