|
@@ -7,6 +7,7 @@ import com.etotem.cfc.entity.Family;
|
|
|
import com.etotem.cfc.entity.FamilyJoinRequest;
|
|
import com.etotem.cfc.entity.FamilyJoinRequest;
|
|
|
import com.etotem.cfc.entity.FamilyMember;
|
|
import com.etotem.cfc.entity.FamilyMember;
|
|
|
import com.etotem.cfc.entity.User;
|
|
import com.etotem.cfc.entity.User;
|
|
|
|
|
+import com.etotem.cfc.enums.GenerationLevel;
|
|
|
import com.etotem.cfc.mapper.FamilyInvitationMapper;
|
|
import com.etotem.cfc.mapper.FamilyInvitationMapper;
|
|
|
import com.etotem.cfc.mapper.FamilyJoinRequestMapper;
|
|
import com.etotem.cfc.mapper.FamilyJoinRequestMapper;
|
|
|
import com.etotem.cfc.mapper.FamilyMapper;
|
|
import com.etotem.cfc.mapper.FamilyMapper;
|
|
@@ -43,6 +44,9 @@ public class FamilyJoinRequestService {
|
|
|
@Resource
|
|
@Resource
|
|
|
private FamilyInvitationService familyInvitationService;
|
|
private FamilyInvitationService familyInvitationService;
|
|
|
|
|
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private FamilyRelationshipService familyRelationshipService;
|
|
|
|
|
+
|
|
|
@Resource
|
|
@Resource
|
|
|
private UnlockGateService unlockGateService;
|
|
private UnlockGateService unlockGateService;
|
|
|
|
|
|
|
@@ -128,7 +132,8 @@ public class FamilyJoinRequestService {
|
|
|
request.setInviterUserId(inviterUserId);
|
|
request.setInviterUserId(inviterUserId);
|
|
|
request.setJoinMethod(joinMethod);
|
|
request.setJoinMethod(joinMethod);
|
|
|
request.setInviteRef(inviteCode.trim());
|
|
request.setInviteRef(inviteCode.trim());
|
|
|
- request.setFamilyRole(familyRole);
|
|
|
|
|
|
|
+ // 身份改由邀请人审批时指定(规格 2026-11-07-family-join-dynamic-identity):
|
|
|
|
|
+ // familyRole 保留接收但忽略,不在此写入申请表;审批通过时按「绝对辈分+性别」回写推导值
|
|
|
request.setStatus("pending");
|
|
request.setStatus("pending");
|
|
|
request.setCreatedAt(new Date());
|
|
request.setCreatedAt(new Date());
|
|
|
request.setUpdatedAt(new Date());
|
|
request.setUpdatedAt(new Date());
|
|
@@ -140,9 +145,17 @@ public class FamilyJoinRequestService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 审批通过加入请求(审批人=邀请人,谁分享的卡谁确认;旧数据无邀请人时回退为家庭管理员)
|
|
|
|
|
|
|
+ * 审批通过加入请求(兼容旧调用:不指定辈分 → 仅写 familyRole,不落地辈分/关系)
|
|
|
*/
|
|
*/
|
|
|
public void approveJoinRequest(Long requestId, Long approverUserId, String comment) {
|
|
public void approveJoinRequest(Long requestId, Long approverUserId, String comment) {
|
|
|
|
|
+ approveJoinRequest(requestId, approverUserId, comment, null, null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 审批通过加入请求(审批人=邀请人,谁分享的卡谁确认;旧数据无邀请人时回退为家庭管理员)
|
|
|
|
|
+ * 身份由邀请人在审批时指定:generationLevel(辈分)+ peerType(同辈类型)
|
|
|
|
|
+ */
|
|
|
|
|
+ public void approveJoinRequest(Long requestId, Long approverUserId, String comment, String generationLevel, String peerType) {
|
|
|
FamilyJoinRequest request = familyJoinRequestMapper.selectById(requestId);
|
|
FamilyJoinRequest request = familyJoinRequestMapper.selectById(requestId);
|
|
|
if (request == null) {
|
|
if (request == null) {
|
|
|
throw new RuntimeException("加入请求不存在");
|
|
throw new RuntimeException("加入请求不存在");
|
|
@@ -164,6 +177,35 @@ public class FamilyJoinRequestService {
|
|
|
throw new RuntimeException("请求用户不存在");
|
|
throw new RuntimeException("请求用户不存在");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 身份解析:generationLevel 为空 → 旧逻辑(仅写 familyRole);否则校验并计算绝对辈分
|
|
|
|
|
+ GenerationLevel level = GenerationLevel.fromValue(generationLevel);
|
|
|
|
|
+ boolean withIdentity = generationLevel != null && !generationLevel.trim().isEmpty();
|
|
|
|
|
+ if (withIdentity) {
|
|
|
|
|
+ if (level == null) {
|
|
|
|
|
+ throw new RuntimeException("请选择辈分关系");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (level == GenerationLevel.PEER && !"sibling".equals(peerType) && !"spouse".equals(peerType)) {
|
|
|
|
|
+ throw new RuntimeException("请选择同辈关系类型");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 绝对辈分 = 审批人成员辈分(null 视为 0)+ 选项 offset
|
|
|
|
|
+ // anchorMemberId = 审批人在本家庭的成员记录(关系锚点:配偶关系只对锚点生效)
|
|
|
|
|
+ Integer absoluteGeneration = null;
|
|
|
|
|
+ Long anchorMemberId = null;
|
|
|
|
|
+ boolean isSpouse = false;
|
|
|
|
|
+ if (withIdentity) {
|
|
|
|
|
+ FamilyMember anchor = familyMemberMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<FamilyMember>()
|
|
|
|
|
+ .eq(FamilyMember::getFamilyId, family.getId())
|
|
|
|
|
+ .eq(FamilyMember::getUserId, approverUserId)
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ int anchorGen = anchor != null && anchor.getGeneration() != null ? anchor.getGeneration() : 0;
|
|
|
|
|
+ absoluteGeneration = anchorGen + level.getOffset();
|
|
|
|
|
+ anchorMemberId = anchor != null ? anchor.getId() : null;
|
|
|
|
|
+ isSpouse = "peer".equals(generationLevel) && "spouse".equals(peerType);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if (user.getFamilyId() != null && user.getFamilyId() != 0L && !user.getFamilyId().equals(family.getId())) {
|
|
if (user.getFamilyId() != null && user.getFamilyId() != 0L && !user.getFamilyId().equals(family.getId())) {
|
|
|
// 申请人已有其他家庭:切换会被拒绝的情形前置拦截,给出明确主体与解决指引。
|
|
// 申请人已有其他家庭:切换会被拒绝的情形前置拦截,给出明确主体与解决指引。
|
|
|
// 关键:错误主体是【申请人】而不是审批人——申请人若为原家庭创建者,切换家庭
|
|
// 关键:错误主体是【申请人】而不是审批人——申请人若为原家庭创建者,切换家庭
|
|
@@ -189,7 +231,9 @@ public class FamilyJoinRequestService {
|
|
|
} else {
|
|
} else {
|
|
|
if (user.getFamilyId() == null || user.getFamilyId() == 0L || !user.getFamilyId().equals(family.getId())) {
|
|
if (user.getFamilyId() == null || user.getFamilyId() == 0L || !user.getFamilyId().equals(family.getId())) {
|
|
|
user.setFamilyId(family.getId());
|
|
user.setFamilyId(family.getId());
|
|
|
- user.setFamilyRole(request.getFamilyRole());
|
|
|
|
|
|
|
+ if (!withIdentity) {
|
|
|
|
|
+ user.setFamilyRole(request.getFamilyRole());
|
|
|
|
|
+ }
|
|
|
user.setUpdatedAt(new Date());
|
|
user.setUpdatedAt(new Date());
|
|
|
userMapper.updateById(user);
|
|
userMapper.updateById(user);
|
|
|
|
|
|
|
@@ -218,6 +262,46 @@ public class FamilyJoinRequestService {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 身份与关系落地(新建家庭分支与合并家庭分支统一处理):
|
|
|
|
|
+ // 合并分支 mergeIntoFamily 已迁移成员记录并对旧关系做清理,此处补写辈分并重建与新家庭的关系。
|
|
|
|
|
+ if (withIdentity) {
|
|
|
|
|
+ FamilyMember member = familyMemberMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<FamilyMember>()
|
|
|
|
|
+ .eq(FamilyMember::getFamilyId, family.getId())
|
|
|
|
|
+ .eq(FamilyMember::getUserId, user.getId())
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ if (member == null) {
|
|
|
|
|
+ // 兜底创建(merge 路径已保证成员记录存在,仅历史异常数据走此分支)
|
|
|
|
|
+ String memberAvatar = (user.getAvatar() != null && !user.getAvatar().isEmpty())
|
|
|
|
|
+ ? user.getAvatar() : "/static/avatar.jpg";
|
|
|
|
|
+ member = new FamilyMember();
|
|
|
|
|
+ member.setFamilyId(family.getId());
|
|
|
|
|
+ member.setUserId(user.getId());
|
|
|
|
|
+ member.setNickname(user.getNickname());
|
|
|
|
|
+ member.setAvatar(memberAvatar);
|
|
|
|
|
+ member.setGender(user.getGender());
|
|
|
|
|
+ member.setShowToFamily(1);
|
|
|
|
|
+ member.setTotalPoints(0);
|
|
|
|
|
+ member.setCreatedAt(new Date());
|
|
|
|
|
+ member.setUpdatedAt(new Date());
|
|
|
|
|
+ familyMemberMapper.insert(member);
|
|
|
|
|
+ }
|
|
|
|
|
+ member.setGeneration(absoluteGeneration);
|
|
|
|
|
+ member.setIsSpouse(isSpouse);
|
|
|
|
|
+ member.setUpdatedAt(new Date());
|
|
|
|
|
+ familyMemberMapper.updateById(member);
|
|
|
|
|
+
|
|
|
|
|
+ // 与新家庭全体现有成员建立双向关系(含 relation_type: lineal/sibling/spouse)
|
|
|
|
|
+ // 锚点=审批人成员:spouse 只写在「加入者 ↔ 审批人」之间,其他同辈一律 sibling
|
|
|
|
|
+ familyRelationshipService.createRelationsForNewMember(
|
|
|
|
|
+ family.getId(), member.getId(), absoluteGeneration, peerType, anchorMemberId);
|
|
|
|
|
+
|
|
|
|
|
+ // User.familyRole 由绝对辈分 + 性别推导,保证下游 DTO 不为空
|
|
|
|
|
+ user.setFamilyRole(deriveFamilyRole(absoluteGeneration, user.getGender()));
|
|
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
|
|
+ userMapper.updateById(user);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 平台推荐人绑定(需求:未注册/已注册但无推荐人 → 绑定为邀请人的上级推荐人;已有推荐人 → 不动)
|
|
// 平台推荐人绑定(需求:未注册/已注册但无推荐人 → 绑定为邀请人的上级推荐人;已有推荐人 → 不动)
|
|
|
bindReferrerFromInviter(request, user);
|
|
bindReferrerFromInviter(request, user);
|
|
|
|
|
|
|
@@ -232,6 +316,12 @@ public class FamilyJoinRequestService {
|
|
|
request.setStatus("approved");
|
|
request.setStatus("approved");
|
|
|
request.setApproverUserId(approverUserId);
|
|
request.setApproverUserId(approverUserId);
|
|
|
request.setComment(comment);
|
|
request.setComment(comment);
|
|
|
|
|
+ if (withIdentity) {
|
|
|
|
|
+ // 回写申请表:推导的 familyRole + 审批时指定的绝对辈分/同辈类型(可追溯)
|
|
|
|
|
+ request.setFamilyRole(deriveFamilyRole(absoluteGeneration, user.getGender()));
|
|
|
|
|
+ request.setAssignedGeneration(absoluteGeneration);
|
|
|
|
|
+ request.setAssignedPeerType(peerType);
|
|
|
|
|
+ }
|
|
|
request.setUpdatedAt(new Date());
|
|
request.setUpdatedAt(new Date());
|
|
|
familyJoinRequestMapper.updateById(request);
|
|
familyJoinRequestMapper.updateById(request);
|
|
|
|
|
|
|
@@ -242,7 +332,8 @@ public class FamilyJoinRequestService {
|
|
|
log.warn("刷新家庭通关解锁失败(不阻断审批): requestId={}, error={}", requestId, e.getMessage());
|
|
log.warn("刷新家庭通关解锁失败(不阻断审批): requestId={}, error={}", requestId, e.getMessage());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- log.info("已审批通过加入请求: requestId={}, userId={}, approverId={}", requestId, user.getId(), approverUserId);
|
|
|
|
|
|
|
+ log.info("已审批通过加入请求: requestId={}, userId={}, approverId={}, generationLevel={}, peerType={}",
|
|
|
|
|
+ requestId, user.getId(), approverUserId, generationLevel, peerType);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -299,6 +390,7 @@ public class FamilyJoinRequestService {
|
|
|
dto.setId(req.getId());
|
|
dto.setId(req.getId());
|
|
|
dto.setRequesterUserId(req.getRequesterUserId());
|
|
dto.setRequesterUserId(req.getRequesterUserId());
|
|
|
dto.setRequesterNickname(user != null ? user.getNickname() : "未知用户");
|
|
dto.setRequesterNickname(user != null ? user.getNickname() : "未知用户");
|
|
|
|
|
+ dto.setRequesterRealName(user != null ? user.getRealName() : null);
|
|
|
dto.setRequesterAvatar(user != null ? user.getAvatar() : "");
|
|
dto.setRequesterAvatar(user != null ? user.getAvatar() : "");
|
|
|
dto.setInviterUserId(req.getInviterUserId());
|
|
dto.setInviterUserId(req.getInviterUserId());
|
|
|
dto.setInviteCode(req.getInviteRef());
|
|
dto.setInviteCode(req.getInviteRef());
|
|
@@ -307,6 +399,9 @@ public class FamilyJoinRequestService {
|
|
|
dto.setTargetFamilyName(family != null ? family.getName() : "");
|
|
dto.setTargetFamilyName(family != null ? family.getName() : "");
|
|
|
dto.setStatus(req.getStatus());
|
|
dto.setStatus(req.getStatus());
|
|
|
dto.setFamilyRole(req.getFamilyRole());
|
|
dto.setFamilyRole(req.getFamilyRole());
|
|
|
|
|
+ dto.setAssignedGeneration(req.getAssignedGeneration());
|
|
|
|
|
+ dto.setAssignedPeerType(req.getAssignedPeerType());
|
|
|
|
|
+ dto.setAssignedPeerLabel(assignedPeerLabel(req.getAssignedPeerType()));
|
|
|
dto.setAdminComment(req.getComment());
|
|
dto.setAdminComment(req.getComment());
|
|
|
dto.setCreatedAt(req.getCreatedAt() != null ? req.getCreatedAt().toString() : "");
|
|
dto.setCreatedAt(req.getCreatedAt() != null ? req.getCreatedAt().toString() : "");
|
|
|
return dto;
|
|
return dto;
|
|
@@ -327,13 +422,21 @@ public class FamilyJoinRequestService {
|
|
|
return null;
|
|
return null;
|
|
|
}
|
|
}
|
|
|
Family family = familyMapper.selectById(req.getTargetFamilyId());
|
|
Family family = familyMapper.selectById(req.getTargetFamilyId());
|
|
|
|
|
+ User requester = req.getRequesterUserId() != null ? userMapper.selectById(req.getRequesterUserId()) : null;
|
|
|
FamilyJoinRequestDTO dto = new FamilyJoinRequestDTO();
|
|
FamilyJoinRequestDTO dto = new FamilyJoinRequestDTO();
|
|
|
dto.setId(req.getId());
|
|
dto.setId(req.getId());
|
|
|
|
|
+ dto.setRequesterUserId(req.getRequesterUserId());
|
|
|
|
|
+ dto.setRequesterNickname(requester != null ? requester.getNickname() : "");
|
|
|
|
|
+ dto.setRequesterRealName(requester != null ? requester.getRealName() : null);
|
|
|
|
|
+ dto.setRequesterAvatar(requester != null ? requester.getAvatar() : "");
|
|
|
dto.setTargetFamilyId(req.getTargetFamilyId());
|
|
dto.setTargetFamilyId(req.getTargetFamilyId());
|
|
|
dto.setTargetFamilyName(family != null ? family.getName() : "");
|
|
dto.setTargetFamilyName(family != null ? family.getName() : "");
|
|
|
dto.setStatus(req.getStatus());
|
|
dto.setStatus(req.getStatus());
|
|
|
dto.setInviteCode(req.getInviteRef());
|
|
dto.setInviteCode(req.getInviteRef());
|
|
|
dto.setFamilyRole(req.getFamilyRole());
|
|
dto.setFamilyRole(req.getFamilyRole());
|
|
|
|
|
+ dto.setAssignedGeneration(req.getAssignedGeneration());
|
|
|
|
|
+ dto.setAssignedPeerType(req.getAssignedPeerType());
|
|
|
|
|
+ dto.setAssignedPeerLabel(assignedPeerLabel(req.getAssignedPeerType()));
|
|
|
dto.setAdminComment(req.getComment());
|
|
dto.setAdminComment(req.getComment());
|
|
|
dto.setCreatedAt(req.getCreatedAt() != null ? req.getCreatedAt().toString() : "");
|
|
dto.setCreatedAt(req.getCreatedAt() != null ? req.getCreatedAt().toString() : "");
|
|
|
return dto;
|
|
return dto;
|
|
@@ -415,4 +518,34 @@ public class FamilyJoinRequestService {
|
|
|
: "只有家庭管理员才能审批");
|
|
: "只有家庭管理员才能审批");
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * User.familyRole 推导表(绝对辈分 + 性别):
|
|
|
|
|
+ * ≥2 → 爷爷/奶奶(「姥爷/姥姥」无法由辈分推导,统一落奶奶/爷爷,用户可在「我的资料」修正)
|
|
|
|
|
+ * 1 → 爸爸/妈妈
|
|
|
|
|
+ * 其余 → 其他(保证 MemberCoverageDTO/MemberEnergyDTO/InsuranceGapDTO 等下游不为空)
|
|
|
|
|
+ */
|
|
|
|
|
+ private String deriveFamilyRole(int generation, String gender) {
|
|
|
|
|
+ boolean male = "male".equals(gender);
|
|
|
|
|
+ if (generation >= 2) {
|
|
|
|
|
+ return male ? "爷爷" : "奶奶";
|
|
|
|
|
+ }
|
|
|
|
|
+ if (generation == 1) {
|
|
|
|
|
+ return male ? "爸爸" : "妈妈";
|
|
|
|
|
+ }
|
|
|
|
|
+ return "其他";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 同辈类型中文标签(展示层冗余,与 utils/generationLevel.js PEER_TYPES 对应)
|
|
|
|
|
+ */
|
|
|
|
|
+ private String assignedPeerLabel(String peerType) {
|
|
|
|
|
+ if ("spouse".equals(peerType)) {
|
|
|
|
|
+ return "配偶";
|
|
|
|
|
+ }
|
|
|
|
|
+ if ("sibling".equals(peerType)) {
|
|
|
|
|
+ return "兄弟姐妹";
|
|
|
|
|
+ }
|
|
|
|
|
+ return "";
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|