|
|
@@ -0,0 +1,325 @@
|
|
|
+package com.etotem.cfc.service;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.etotem.cfc.entity.FamilyMember;
|
|
|
+import com.etotem.cfc.entity.ReportMemberPermission;
|
|
|
+import com.etotem.cfc.mapper.FamilyMemberMapper;
|
|
|
+import com.etotem.cfc.mapper.ReportMemberPermissionMapper;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 报告成员级查看权限服务
|
|
|
+ *
|
|
|
+ * 模型:
|
|
|
+ * - 家庭管理员(family_members.is_admin=1 或家庭创建者):可见家庭内全部成员的报告
|
|
|
+ * - 普通成员:仅可见自己的报告;管理员可通过 report_member_permissions 授权某用户查看某成员的全部报告
|
|
|
+ * - 授权按「成员」粒度:授权后该用户可查看该成员的所有报告(健康报告 + DAN 测评报告),含未来新报告
|
|
|
+ */
|
|
|
+@Service
|
|
|
+public class ReportMemberPermissionService {
|
|
|
+
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(ReportMemberPermissionService.class);
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ReportMemberPermissionMapper permissionMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private FamilyMemberMapper familyMemberMapper;
|
|
|
+
|
|
|
+ /* ============ 管理员判定 ============ */
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断用户是否为某家庭的管理员(family_members.is_admin=1 或该家庭创建者)
|
|
|
+ */
|
|
|
+ public boolean isFamilyAdmin(Long userId, Long familyId) {
|
|
|
+ if (userId == null || familyId == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ Long count = familyMemberMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<FamilyMember>()
|
|
|
+ .eq(FamilyMember::getFamilyId, familyId)
|
|
|
+ .eq(FamilyMember::getUserId, userId)
|
|
|
+ .eq(FamilyMember::getIsAdmin, 1)
|
|
|
+ .last("LIMIT 1"));
|
|
|
+ return count != null && count > 0;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("判断家庭管理员失败 userId={}, familyId={}: {}", userId, familyId, e.getMessage());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* ============ 可见性校验 ============ */
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 用户是否可查看某成员的报告(管理员 / 本人 / 被授权)
|
|
|
+ *
|
|
|
+ * @param userId 查看者用户ID
|
|
|
+ * @param targetMemberId 被查看的成员ID(family_members.id)
|
|
|
+ * @param familyId 家庭ID(隔离作用域)
|
|
|
+ */
|
|
|
+ public boolean canViewMemberReports(Long userId, Long targetMemberId, Long familyId) {
|
|
|
+ if (userId == null || targetMemberId == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 1. 管理员:家庭内全部可见
|
|
|
+ if (familyId != null && isFamilyAdmin(userId, familyId)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ // 2. 本人:看自己的报告
|
|
|
+ if (isOwnMember(userId, targetMemberId, familyId)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ // 3. 被授权(targetMemberId 先归一化为 family_members.id,兼容 users.id 语义)
|
|
|
+ if (familyId != null) {
|
|
|
+ Long normalized = normalizeToMemberId(targetMemberId, familyId);
|
|
|
+ Long queryTarget = (normalized != null) ? normalized : targetMemberId;
|
|
|
+ Long count = permissionMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<ReportMemberPermission>()
|
|
|
+ .eq(ReportMemberPermission::getFamilyId, familyId)
|
|
|
+ .eq(ReportMemberPermission::getTargetMemberId, queryTarget)
|
|
|
+ .eq(ReportMemberPermission::getGranteeUserId, userId)
|
|
|
+ .last("LIMIT 1"));
|
|
|
+ return count != null && count > 0;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将成员ID归一化为 family_members.id:
|
|
|
+ * - 已是 family_members.id 则原样返回
|
|
|
+ * - 若是 users.id 则反查其在家庭内的 family_members 记录
|
|
|
+ * - 查不到返回 null
|
|
|
+ */
|
|
|
+ private Long normalizeToMemberId(Long id, Long familyId) {
|
|
|
+ if (id == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ FamilyMember m = familyMemberMapper.selectById(id);
|
|
|
+ if (m != null) {
|
|
|
+ return m.getId();
|
|
|
+ }
|
|
|
+ List<FamilyMember> byUser = familyMemberMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<FamilyMember>()
|
|
|
+ .eq(FamilyMember::getUserId, id)
|
|
|
+ .eq(FamilyMember::getFamilyId, familyId)
|
|
|
+ .last("LIMIT 1"));
|
|
|
+ if (!byUser.isEmpty()) {
|
|
|
+ return byUser.get(0).getId();
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("归一化成员ID失败 id={}: {}", id, e.getMessage());
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断用户是否为某成员的本人(family_members.user_id == userId)
|
|
|
+ * 兼容 targetMemberId 可能是 family_members.id 或 users.id 两种语义
|
|
|
+ */
|
|
|
+ private boolean isOwnMember(Long userId, Long targetMemberId, Long familyId) {
|
|
|
+ try {
|
|
|
+ // 直接匹配:family_members.id == targetMemberId 且 user_id == userId
|
|
|
+ List<FamilyMember> members = familyMemberMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<FamilyMember>()
|
|
|
+ .eq(FamilyMember::getUserId, userId));
|
|
|
+ for (FamilyMember m : members) {
|
|
|
+ if (m.getId().equals(targetMemberId)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ // targetMemberId 可能是 users.id 语义
|
|
|
+ if (m.getUserId() != null && m.getUserId().equals(targetMemberId)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("判断本人成员失败 userId={}, targetMemberId={}: {}", userId, targetMemberId, e.getMessage());
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* ============ 授权管理(管理员操作) ============ */
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 管理员授权:指定用户可查看指定成员的全部报告
|
|
|
+ */
|
|
|
+ public boolean grant(Long operatorUserId, Long familyId, Long targetMemberId, Long granteeUserId) {
|
|
|
+ // 仅管理员可授权
|
|
|
+ if (!isFamilyAdmin(operatorUserId, familyId)) {
|
|
|
+ throw new IllegalArgumentException("仅家庭管理员可进行报告授权");
|
|
|
+ }
|
|
|
+ if (targetMemberId == null || granteeUserId == null) {
|
|
|
+ throw new IllegalArgumentException("被授权成员与用户不能为空");
|
|
|
+ }
|
|
|
+ // 幂等:已存在则跳过
|
|
|
+ Long exists = permissionMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<ReportMemberPermission>()
|
|
|
+ .eq(ReportMemberPermission::getFamilyId, familyId)
|
|
|
+ .eq(ReportMemberPermission::getTargetMemberId, targetMemberId)
|
|
|
+ .eq(ReportMemberPermission::getGranteeUserId, granteeUserId)
|
|
|
+ .last("LIMIT 1"));
|
|
|
+ if (exists != null && exists > 0) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ ReportMemberPermission p = new ReportMemberPermission();
|
|
|
+ p.setFamilyId(familyId);
|
|
|
+ p.setAuthorizerUserId(operatorUserId);
|
|
|
+ p.setTargetMemberId(targetMemberId);
|
|
|
+ p.setGranteeUserId(granteeUserId);
|
|
|
+ p.setCreatedAt(new Date());
|
|
|
+ try {
|
|
|
+ permissionMapper.insert(p);
|
|
|
+ return true;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("报告授权失败 familyId={}, targetMemberId={}, granteeUserId={}: {}",
|
|
|
+ familyId, targetMemberId, granteeUserId, e.getMessage());
|
|
|
+ // 并发插入唯一键冲突:视为已授权
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 管理员撤销授权
|
|
|
+ */
|
|
|
+ public boolean revoke(Long operatorUserId, Long familyId, Long targetMemberId, Long granteeUserId) {
|
|
|
+ if (!isFamilyAdmin(operatorUserId, familyId)) {
|
|
|
+ throw new IllegalArgumentException("仅家庭管理员可进行报告授权");
|
|
|
+ }
|
|
|
+ permissionMapper.delete(
|
|
|
+ new LambdaQueryWrapper<ReportMemberPermission>()
|
|
|
+ .eq(ReportMemberPermission::getFamilyId, familyId)
|
|
|
+ .eq(ReportMemberPermission::getTargetMemberId, targetMemberId)
|
|
|
+ .eq(ReportMemberPermission::getGranteeUserId, granteeUserId));
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询某成员的授权列表(管理员操作)
|
|
|
+ * 返回:已授权查看该成员的用户ID列表
|
|
|
+ */
|
|
|
+ public List<Long> listGrantedUserIds(Long operatorUserId, Long familyId, Long targetMemberId) {
|
|
|
+ if (!isFamilyAdmin(operatorUserId, familyId)) {
|
|
|
+ throw new IllegalArgumentException("仅家庭管理员可查询报告授权");
|
|
|
+ }
|
|
|
+ List<ReportMemberPermission> list = permissionMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<ReportMemberPermission>()
|
|
|
+ .eq(ReportMemberPermission::getFamilyId, familyId)
|
|
|
+ .eq(ReportMemberPermission::getTargetMemberId, targetMemberId));
|
|
|
+ List<Long> ids = new ArrayList<>();
|
|
|
+ for (ReportMemberPermission p : list) {
|
|
|
+ ids.add(p.getGranteeUserId());
|
|
|
+ }
|
|
|
+ return ids;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询某家庭全部授权记录(管理员操作,前端渲染权限矩阵用)
|
|
|
+ */
|
|
|
+ public List<Map<String, Object>> listAllPermissions(Long operatorUserId, Long familyId) {
|
|
|
+ if (!isFamilyAdmin(operatorUserId, familyId)) {
|
|
|
+ throw new IllegalArgumentException("仅家庭管理员可查询报告授权");
|
|
|
+ }
|
|
|
+ List<ReportMemberPermission> list = permissionMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<ReportMemberPermission>()
|
|
|
+ .eq(ReportMemberPermission::getFamilyId, familyId)
|
|
|
+ .orderByAsc(ReportMemberPermission::getTargetMemberId));
|
|
|
+ List<Map<String, Object>> result = new ArrayList<>();
|
|
|
+ for (ReportMemberPermission p : list) {
|
|
|
+ Map<String, Object> m = new HashMap<>();
|
|
|
+ m.put("id", p.getId());
|
|
|
+ m.put("targetMemberId", p.getTargetMemberId());
|
|
|
+ m.put("granteeUserId", p.getGranteeUserId());
|
|
|
+ m.put("createdAt", p.getCreatedAt());
|
|
|
+ result.add(m);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* ============ 列表过滤辅助 ============ */
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 返回用户在家庭内「可见报告」的身份ID集合(含 family_members.id 与 users.id 两种语义):
|
|
|
+ * - 管理员:返回 null,表示全部可见(调用方不做成员过滤)
|
|
|
+ * - 普通成员:本人 userId + 本人成员ID + 被授权成员ID(含其绑定的 userId)
|
|
|
+ * 用于「家庭全部视图」按报告 subject_id/user_id 过滤。
|
|
|
+ */
|
|
|
+ public Set<Long> visibleIdentityIds(Long userId, Long familyId) {
|
|
|
+ if (familyId == null) {
|
|
|
+ return Collections.emptySet();
|
|
|
+ }
|
|
|
+ if (isFamilyAdmin(userId, familyId)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ Set<Long> ids = new HashSet<>();
|
|
|
+ ids.add(userId); // users.id 语义(报告 user_id 直接匹配)
|
|
|
+ try {
|
|
|
+ List<FamilyMember> mine = familyMemberMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<FamilyMember>()
|
|
|
+ .eq(FamilyMember::getFamilyId, familyId)
|
|
|
+ .eq(FamilyMember::getUserId, userId));
|
|
|
+ for (FamilyMember m : mine) {
|
|
|
+ ids.add(m.getId());
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("查询本人成员失败 userId={}, familyId={}: {}", userId, familyId, e.getMessage());
|
|
|
+ }
|
|
|
+ List<ReportMemberPermission> grants = permissionMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<ReportMemberPermission>()
|
|
|
+ .eq(ReportMemberPermission::getFamilyId, familyId)
|
|
|
+ .eq(ReportMemberPermission::getGranteeUserId, userId));
|
|
|
+ for (ReportMemberPermission p : grants) {
|
|
|
+ ids.add(p.getTargetMemberId());
|
|
|
+ try {
|
|
|
+ FamilyMember tm = familyMemberMapper.selectById(p.getTargetMemberId());
|
|
|
+ if (tm != null && tm.getUserId() != null) {
|
|
|
+ ids.add(tm.getUserId());
|
|
|
+ }
|
|
|
+ } catch (Exception ignored) {
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return ids;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 返回用户在家庭内「可见报告」的成员ID集合:
|
|
|
+ * - 管理员:null 表示全部可见(调用方不做成员过滤)
|
|
|
+ * - 普通成员:本人成员ID + 被授权的成员ID
|
|
|
+ */
|
|
|
+ public Set<Long> visibleMemberIds(Long userId, Long familyId) {
|
|
|
+ if (familyId == null) {
|
|
|
+ return Collections.emptySet();
|
|
|
+ }
|
|
|
+ Set<Long> result = new HashSet<>();
|
|
|
+ // 管理员:返回 null 语义(全部可见)
|
|
|
+ if (isFamilyAdmin(userId, familyId)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ // 本人成员ID
|
|
|
+ try {
|
|
|
+ List<FamilyMember> mine = familyMemberMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<FamilyMember>()
|
|
|
+ .eq(FamilyMember::getFamilyId, familyId)
|
|
|
+ .eq(FamilyMember::getUserId, userId));
|
|
|
+ for (FamilyMember m : mine) {
|
|
|
+ result.add(m.getId());
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("查询本人成员失败 userId={}, familyId={}: {}", userId, familyId, e.getMessage());
|
|
|
+ }
|
|
|
+ // 被授权的成员ID
|
|
|
+ List<ReportMemberPermission> grants = permissionMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<ReportMemberPermission>()
|
|
|
+ .eq(ReportMemberPermission::getFamilyId, familyId)
|
|
|
+ .eq(ReportMemberPermission::getGranteeUserId, userId));
|
|
|
+ for (ReportMemberPermission p : grants) {
|
|
|
+ result.add(p.getTargetMemberId());
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+}
|