package com.yunhai.userbase.service.push; import com.yunhai.userbase.entity.SysGroup; import com.yunhai.userbase.entity.SysUser; import com.yunhai.userbase.entity.SysUserGroupRel; import com.yunhai.userbase.enums.GroupTypeEnum; import com.yunhai.userbase.enums.StatusEnum; import com.yunhai.userbase.model.push.dto.PushGroupDTO; import com.yunhai.userbase.model.push.dto.PushUserDTO; import com.yunhai.userbase.repository.SysGroupRepository; import com.yunhai.userbase.repository.SysUserGroupRelRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.time.LocalDate; import java.util.*; import java.util.stream.Collectors; /** * 领域模型 → 文档契约 DTO 的装配器。

* 关键映射(方案 v5 §2 / 文档 3.x): * * * @author chenx * @date 2026/8/14 */ @Service public class PushPayloadAssembler { private static final int ROOT_PARENT_ID = 0; private static final int IS_MAIN = 1; @Autowired private SysGroupRepository sysGroupRepository; @Autowired private SysUserGroupRelRepository sysUserGroupRelRepository; @Autowired private GroupScopeHelper groupScopeHelper; // ===================== 组 ===================== /** 组节点(不含 children)。AddGroup / UpdateGroup 使用。 */ public PushGroupDTO toGroupDto(SysGroup g) { if (g == null) { return null; } PushGroupDTO d = new PushGroupDTO(); d.setId(g.getId()); d.setName(g.getName()); // 文档 code 为「机构代码」,userbase 的 sys_group 无此属性,按可选项留空(序列化后为 null) d.setType(g.getType() == null ? null : g.getType().getKey()); d.setPid(g.getParentId()); d.setNo(g.getSort()); return d; } /** 组节点 + 递归 children(SyncGroup 使用)。 */ public PushGroupDTO toGroupDtoWithChildren(SysGroup g, Map> childrenMap) { PushGroupDTO d = toGroupDto(g); List children = childrenMap.get(g.getId()); if (children != null) { d.setChildren(children.stream() .map(c -> toGroupDtoWithChildren(c, childrenMap)) .collect(Collectors.toList())); } return d; } /** 多个组(AddGroup / UpdateGroup),无 children,带 pid。 */ public List toGroupDtoList(List groups) { if (groups == null) { return Collections.emptyList(); } return groups.stream().map(this::toGroupDto).collect(Collectors.toList()); } // ===================== 用户 ===================== /** 单个用户 → PushUserDTO(含 groups / roleList,学校 id 已解析)。 */ public PushUserDTO toUserDto(SysUser u) { if (u == null) { return null; } PushUserDTO d = new PushUserDTO(); d.setId(u.getId()); d.setAccount(u.getAccount()); d.setName(u.getRealName()); d.setIdcard(u.getIdcard()); d.setPhone(u.getPhone()); d.setEmail(u.getEmail()); d.setGender(u.getGender()); d.setBirthday(u.getBirthday() == null ? null : u.getBirthday().toString()); // status:NORMAL(0)→0正常 / DISABLED(1)→2禁用 d.setStatus(u.getDisabled() == StatusEnum.NORMAL ? 0 : 2); d.setProfilePicture(u.getProfilePicture()); List rels = sysUserGroupRelRepository.findByUserId(u.getId()); Integer mainSchoolId = resolveMainSchoolId(rels); // groups List groupRefs = new ArrayList<>(); for (SysUserGroupRel rel : rels) { PushUserDTO.PushUserGroupRef ref = new PushUserDTO.PushUserGroupRef(); ref.setGroupId(rel.getGroupId()); ref.setNo(rel.getSortNo()); ref.setSchoolId(groupScopeHelper.resolveSchoolId(rel.getGroupId())); groupRefs.add(ref); } d.setGroups(groupRefs); // roleList:每个 roleId 一行,schId 取主组学校(userbase 无角色-学校关联) List roleRefs = new ArrayList<>(); if (u.getRoles() != null) { for (Integer roleId : u.getRoles()) { PushUserDTO.PushUserRoleRef ref = new PushUserDTO.PushUserRoleRef(); ref.setRoleId(roleId); ref.setSchId(mainSchoolId); roleRefs.add(ref); } } d.setRoleList(roleRefs); return d; } /** 多个用户(AddUser / UpdateUser / SyncUser)。 */ public List toUserDtoList(List users) { if (users == null) { return Collections.emptyList(); } return users.stream().map(this::toUserDto).collect(Collectors.toList()); } /** 取用户主组(isMain=1,否则首个关联)所属学校范围 id。 */ private Integer resolveMainSchoolId(List rels) { if (rels == null || rels.isEmpty()) { return null; } Integer mainGroupId = rels.stream() .filter(r -> r.getIsMain() != null && r.getIsMain() == IS_MAIN) .map(SysUserGroupRel::getGroupId) .findFirst() .orElse(rels.get(0).getGroupId()); return groupScopeHelper.resolveSchoolId(mainGroupId); } // ===================== 公共工具 ===================== /** 构建 parentId → 直接子节点列表(null 视为根 0),同层按 sort / id 升序。 */ public Map> buildChildrenMap(List all) { Map> childrenMap = new HashMap<>(); for (SysGroup g : all) { Integer key = (g.getParentId() == null) ? ROOT_PARENT_ID : g.getParentId(); childrenMap.computeIfAbsent(key, k -> new ArrayList<>()).add(g); } Comparator order = (a, b) -> { int sa = a.getSort() == null ? 999 : a.getSort(); int sb = b.getSort() == null ? 999 : b.getSort(); if (sa != sb) { return Integer.compare(sa, sb); } return Integer.compare(a.getId(), b.getId()); }; for (List siblings : childrenMap.values()) { siblings.sort(order); } return childrenMap; } /** 构建整棵组树(从根 pid=0 起),SyncGroup 全量同步入口。 */ public List buildFullGroupTree() { List all = sysGroupRepository.findAll(); if (all.isEmpty()) { return Collections.emptyList(); } Map> childrenMap = buildChildrenMap(all); List roots = childrenMap.getOrDefault(ROOT_PARENT_ID, new ArrayList<>()); return roots.stream() .map(r -> toGroupDtoWithChildren(r, childrenMap)) .collect(Collectors.toList()); } }