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

feat: 注册引导-新增统一backfill回写接口

Xiaogang Liao 3 месяцев назад
Родитель
Сommit
38982a4cca

+ 13 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/UserController.java

@@ -3,6 +3,7 @@ package com.etotem.cfc.controller;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.etotem.cfc.common.Result;
 import com.etotem.cfc.common.Result;
 import com.etotem.cfc.dto.FamilyMemberInfoDTO;
 import com.etotem.cfc.dto.FamilyMemberInfoDTO;
+import com.etotem.cfc.dto.ProfileBackfillDTO;
 import com.etotem.cfc.entity.AssessmentOrder;
 import com.etotem.cfc.entity.AssessmentOrder;
 import com.etotem.cfc.entity.FamilyMember;
 import com.etotem.cfc.entity.FamilyMember;
 import com.etotem.cfc.entity.Family;
 import com.etotem.cfc.entity.Family;
@@ -104,6 +105,18 @@ public class UserController {
         return success ? Result.success(true) : Result.error("更新失败");
         return success ? Result.success(true) : Result.error("更新失败");
     }
     }
 
 
+    @PostMapping("/profile/backfill")
+    @Operation(summary = "就地补充信息回写(个人资料按需补充)")
+    public Result<Map<String, Object>> backfillProfile(HttpServletRequest request, @RequestBody ProfileBackfillDTO dto) {
+        Long userId = (Long) request.getAttribute("userId");
+        try {
+            Map<String, Object> result = userService.backfillProfile(userId, dto);
+            return Result.success(result);
+        } catch (RuntimeException e) {
+            return Result.error(e.getMessage());
+        }
+    }
+
     @PostMapping("/family-members")
     @PostMapping("/family-members")
     @Operation(summary = "获取家庭成员列表")
     @Operation(summary = "获取家庭成员列表")
     public Result<Map<String, Object>> getFamilyMembers(HttpServletRequest request) {
     public Result<Map<String, Object>> getFamilyMembers(HttpServletRequest request) {

+ 15 - 0
cfc-backend/src/main/java/com/etotem/cfc/dto/ProfileBackfillDTO.java

@@ -0,0 +1,15 @@
+package com.etotem.cfc.dto;
+
+import lombok.Data;
+
+import java.util.Map;
+
+@Data
+public class ProfileBackfillDTO {
+    /** 回写目标类型: user=本人 / member=家庭成员 */
+    private String targetType;
+    /** targetType=member 时的 family_members.id;user 时可为空 */
+    private Long targetId;
+    /** 回写字段: { phone: "138...", birthday: "2015-06-01", ... } */
+    private Map<String, String> fields;
+}

+ 127 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/UserService.java

@@ -336,6 +336,133 @@ private FamilyInvitationService familyInvitationService;
         return userMapper.selectById(userId);
         return userMapper.selectById(userId);
     }
     }
 
 
+    /** 回写字段白名单(user 目标) */
+    private static final String[] USER_BACKFILL_WHITELIST = {"phone", "gender", "birthday", "birthHour", "dietPreferences", "address"};
+
+    /** 回写字段白名单(member 目标,family_members 表无 dietPreferences/address 列) */
+    private static final String[] MEMBER_BACKFILL_WHITELIST = {"phone", "gender", "birthday", "birthHour"};
+
+    /**
+     * 就地补充信息回写(字段白名单校验 + 目标归属校验)
+     * @return Map: { updatedFields: List<String>, profile: Object(最新完整信息) }
+     */
+    public Map<String, Object> backfillProfile(Long userId, ProfileBackfillDTO dto) {
+        if (dto == null || dto.getFields() == null || dto.getFields().isEmpty()) {
+            throw new RuntimeException("回写字段不能为空");
+        }
+        User me = userMapper.selectById(userId);
+        if (me == null) {
+            throw new RuntimeException("用户不存在");
+        }
+        List<String> updatedFields = new ArrayList<>();
+        Object profile;
+
+        if ("user".equals(dto.getTargetType())) {
+            // 仅允许回写自己
+            if (dto.getTargetId() != null && !dto.getTargetId().equals(userId)) {
+                throw new RuntimeException("仅能回写本人的个人信息");
+            }
+            for (String field : USER_BACKFILL_WHITELIST) {
+                String value = dto.getFields().get(field);
+                if (value == null || value.isEmpty()) continue;
+                applyUserBackfillField(me, field, value.trim());
+                updatedFields.add(field);
+            }
+            me.setUpdatedAt(new Date());
+            userMapper.updateById(me);
+            profile = me;
+        } else if ("member".equals(dto.getTargetType())) {
+            Long memberId = dto.getTargetId();
+            if (memberId == null) {
+                throw new RuntimeException("回写家庭成员时 targetId 不能为空");
+            }
+            FamilyMember member = familyMemberMapper.selectById(memberId);
+            if (member == null) {
+                throw new RuntimeException("家庭成员不存在");
+            }
+            // 归属校验:成员必须属于当前用户家庭
+            Long myFamilyId = me.getFamilyId();
+            if (myFamilyId == null || !myFamilyId.equals(member.getFamilyId())) {
+                throw new RuntimeException("该成员不属于您的家庭");
+            }
+            for (String field : MEMBER_BACKFILL_WHITELIST) {
+                String value = dto.getFields().get(field);
+                if (value == null || value.isEmpty()) continue;
+                applyMemberBackfillField(member, field, value.trim());
+                updatedFields.add(field);
+            }
+            familyMemberMapper.updateById(member);
+            profile = member;
+        } else {
+            throw new RuntimeException("targetType 仅支持 user/member");
+        }
+
+        Map<String, Object> result = new HashMap<>();
+        result.put("updatedFields", updatedFields);
+        result.put("profile", profile);
+        return result;
+    }
+
+    private void applyUserBackfillField(User user, String field, String value) {
+        switch (field) {
+            case "phone":
+                if (!value.matches("^1[3-9]\\d{9}$")) throw new RuntimeException("手机号格式不正确");
+                user.setPhone(value);
+                break;
+            case "gender":
+                if (!"male".equals(value) && !"female".equals(value)) throw new RuntimeException("性别取值非法");
+                user.setGender(value);
+                break;
+            case "birthday":
+                try {
+                    java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
+                    user.setBirthday(sdf.parse(value));
+                } catch (Exception e) {
+                    throw new RuntimeException("生日格式不正确,应为 yyyy-MM-dd");
+                }
+                break;
+            case "birthHour":
+                if (!"子丑寅卯辰巳午未申酉戌亥".contains(value)) throw new RuntimeException("出生时辰取值非法");
+                user.setBirthHour(value);
+                break;
+            case "dietPreferences":
+                user.setDietPreferences(value);
+                break;
+            case "address":
+                user.setAddress(value);
+                break;
+            default:
+                break;
+        }
+    }
+
+    private void applyMemberBackfillField(FamilyMember member, String field, String value) {
+        switch (field) {
+            case "phone":
+                if (!value.matches("^1[3-9]\\d{9}$")) throw new RuntimeException("手机号格式不正确");
+                member.setPhone(value);
+                break;
+            case "gender":
+                if (!"male".equals(value) && !"female".equals(value)) throw new RuntimeException("性别取值非法");
+                member.setGender(value);
+                break;
+            case "birthday":
+                try {
+                    java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
+                    member.setBirthday(sdf.parse(value));
+                } catch (Exception e) {
+                    throw new RuntimeException("生日格式不正确,应为 yyyy-MM-dd");
+                }
+                break;
+            case "birthHour":
+                if (!"子丑寅卯辰巳午未申酉戌亥".contains(value)) throw new RuntimeException("出生时辰取值非法");
+                member.setBirthHour(value);
+                break;
+            default:
+                break;
+        }
+    }
+
     /**
     /**
      * 检查手机号是否已注册
      * 检查手机号是否已注册
      * @param phone 手机号
      * @param phone 手机号