254 lines
11 KiB
Java
254 lines
11 KiB
Java
package com.yunhai.userbase.service.impl;
|
||
|
||
import com.yunhai.platform.utils.enums.IEnum;
|
||
import com.yunhai.platform.utils.model.BoaGroup;
|
||
import com.yunhai.platform.utils.model.BoaRoleInfo;
|
||
import com.yunhai.platform.utils.model.BoaUser;
|
||
import com.yunhai.userbase.entity.SysGroup;
|
||
import com.yunhai.userbase.entity.SysUser;
|
||
import com.yunhai.userbase.entity.SysUserCurrentRole;
|
||
import com.yunhai.userbase.entity.SysUserGroupRel;
|
||
import com.yunhai.userbase.entity.SysUserRoleRel;
|
||
import com.yunhai.userbase.enums.GroupTypeEnum;
|
||
import com.yunhai.userbase.enums.StatusEnum;
|
||
import com.yunhai.userbase.enums.SysRoleEnum;
|
||
import com.yunhai.userbase.repository.SysGroupRepository;
|
||
import com.yunhai.userbase.repository.SysUserGroupRelRepository;
|
||
import com.yunhai.userbase.repository.SysUserRepository;
|
||
import com.yunhai.userbase.repository.SysUserRoleRelRepository;
|
||
import com.yunhai.userbase.repository.SysUserCurrentRoleRepository;
|
||
import com.yunhai.userbase.service.impl.IdentityService;
|
||
import com.yunhai.userbase.service.resolve.MainGroupResolver;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import org.springframework.stereotype.Service;
|
||
|
||
import java.util.ArrayList;
|
||
import java.util.HashSet;
|
||
import java.util.LinkedHashSet;
|
||
import java.util.List;
|
||
import java.util.Objects;
|
||
import java.util.Set;
|
||
import java.util.stream.Collectors;
|
||
|
||
/**
|
||
* 用户中心核心服务:把 UserBase 自身的 sys_user / sys_group 组装成基础平台统一的
|
||
* {@link BoaUser}(含部门/学校/教育局多级组织)。<br><br>
|
||
* 这是「UserBase 由子应用升级为用户中心」的关键:平台侧 {@code UserDetailsProvider}
|
||
* ({@code UserBaseUserDetailsProvider})与开放接口({@code OpenBaseController})都复用本服务,
|
||
* 保证 CAS 会话流与签名开放流返回的 BoaUser 结构完全一致。
|
||
*/
|
||
@Service
|
||
public class UserCenterService {
|
||
|
||
/** 虚拟根节点 parentId(与 SysGroupService 约定一致)。 */
|
||
private static final int ROOT_PARENT_ID = 0;
|
||
|
||
@Autowired
|
||
private SysUserRepository sysUserRepository;
|
||
@Autowired
|
||
private SysUserGroupRelRepository sysUserGroupRelRepository;
|
||
@Autowired
|
||
private SysGroupRepository sysGroupRepository;
|
||
@Autowired
|
||
private SysUserRoleRelRepository sysUserRoleRelRepository;
|
||
@Autowired
|
||
private SysUserCurrentRoleRepository sysUserCurrentRoleRepository;
|
||
@Autowired
|
||
private MainGroupResolver mainGroupResolver;
|
||
@Autowired
|
||
private IdentityService identityService;
|
||
|
||
public BoaUser getUserInfoWithDeptByAccount(String account) {
|
||
if (account == null) {
|
||
return null;
|
||
}
|
||
return resolve(sysUserRepository.findByAccount(account.trim()));
|
||
}
|
||
|
||
public BoaUser getUserInfoWithDeptByPhone(String phone) {
|
||
if (phone == null) {
|
||
return null;
|
||
}
|
||
return resolve(sysUserRepository.findByPhone(phone.trim()));
|
||
}
|
||
|
||
public BoaUser getUserInfoWithDeptById(Integer id) {
|
||
if (id == null) {
|
||
return null;
|
||
}
|
||
return resolve(sysUserRepository.findById(id).orElse(null));
|
||
}
|
||
|
||
private BoaUser resolve(SysUser user) {
|
||
if (user == null) {
|
||
return null;
|
||
}
|
||
BoaUser boa = new BoaUser();
|
||
boa.setId(user.getId());
|
||
boa.setAccount(user.getAccount());
|
||
boa.setName(user.getRealName());
|
||
boa.setIdcard(user.getIdcard());
|
||
boa.setPhone(user.getPhone());
|
||
boa.setProfilePicture(user.getProfilePicture());
|
||
// 用户表无 email / gender 列,置 null
|
||
boa.setEmail(null);
|
||
boa.setGender(null);
|
||
boa.setType(user.getType() != null ? user.getType().getKey() : null);
|
||
// 状态语义对齐:本表 disabled(NORMAL=0 正常 / DISABLED=1 禁用) -> BoaUser(1 正常 / 0 禁用)
|
||
boa.setStatus(user.getDisabled() == StatusEnum.NORMAL ? 1 : 0);
|
||
// 角色列表:从 sys_user_role_rel(平台身份角色权威源)取正常态 roleId 去重,不再读 sys_user.roles
|
||
List<Integer> roleIds = sysUserRoleRelRepository
|
||
.findDistinctRoleIdsByUserIdAndStatus(user.getId(), StatusEnum.NORMAL);
|
||
boa.setRoleIds(roleIds);
|
||
|
||
List<BoaGroup> groups = buildGroups(user.getId());
|
||
boa.setGroups(groups);
|
||
// 部门/教育局:仍从组织链推导(与主组/区域组语义绑定)
|
||
deriveOrg(groups, boa);
|
||
// 学校/当前激活身份/可选学校:从身份关联表推导(school 语义=当前激活学校)
|
||
deriveIdentity(user.getId(), boa);
|
||
return boa;
|
||
}
|
||
|
||
/**
|
||
* 组装用户组织链:取主归属组(isMain=1 取 id 最小,无主兜底 id 最小),沿 parentId 向上回溯到根,
|
||
* 顺序为「主组 -> ... -> 根方向」(近 -> 远)。
|
||
*/
|
||
private List<BoaGroup> buildGroups(Integer userId) {
|
||
List<SysUserGroupRel> rels = sysUserGroupRelRepository.findByUserId(userId);
|
||
if (rels == null || rels.isEmpty()) {
|
||
return new ArrayList<>();
|
||
}
|
||
SysUserGroupRel mainRel = mainGroupResolver.resolve(rels);
|
||
Integer groupId = mainRel != null ? mainRel.getGroupId() : null;
|
||
if (groupId == null) {
|
||
return new ArrayList<>();
|
||
}
|
||
|
||
List<BoaGroup> chain = new ArrayList<>();
|
||
Integer cursor = groupId;
|
||
Set<Integer> seen = new HashSet<>();
|
||
while (cursor != null && cursor != ROOT_PARENT_ID && seen.add(cursor)) {
|
||
SysGroup g = sysGroupRepository.findById(cursor).orElse(null);
|
||
if (g == null) {
|
||
break;
|
||
}
|
||
BoaGroup bg = new BoaGroup();
|
||
bg.setId(g.getId());
|
||
bg.setName(g.getName());
|
||
bg.setParentId(g.getParentId());
|
||
bg.setType(g.getType() != null ? g.getType().getKey() : null);
|
||
chain.add(bg);
|
||
cursor = g.getParentId();
|
||
}
|
||
return chain;
|
||
}
|
||
|
||
/** 从组织链推导 school / bureau / dept。 */
|
||
private void deriveOrg(List<BoaGroup> groups, BoaUser boa) {
|
||
if (groups == null || groups.isEmpty()) {
|
||
return;
|
||
}
|
||
BoaGroup school = null;
|
||
BoaGroup bureau = null;
|
||
for (BoaGroup g : groups) {
|
||
if (school == null && Objects.equals(g.getType(), GroupTypeEnum.SCHOOL_GROUP.getKey())) {
|
||
school = g;
|
||
}
|
||
if (bureau == null && Objects.equals(g.getType(), GroupTypeEnum.AREA_GROUP.getKey())) {
|
||
bureau = g;
|
||
}
|
||
}
|
||
boa.setSchool(school);
|
||
boa.setBureau(bureau);
|
||
// 部门:主组(链的近端第一个);若主组本身为学校,则部门即学校
|
||
boa.setDept(groups.get(0));
|
||
}
|
||
|
||
/**
|
||
* 从身份关联推导当前激活身份与可选学校。school 字段语义=当前激活学校:<br>
|
||
* ① 优先读 {@code sys_user_current_role}(用户主动切换后的状态);<br>
|
||
* ② 无切换记录时兜底取主身份整条记录(动态解析:主组 → 主校 → 该校 sortNo/id 最小行,
|
||
* 保证是合法存在的身份,避免「主组+主角色」组合出 B 学校老师的非法身份);<br>
|
||
* ③ currentRoleId:优先当前激活记录 roleId,兜底主身份行 roleId(本期补码,此前从未填充);<br>
|
||
* ④ schools:用户在全部学校下的可选身份去重 schoolId(>0)集合组装 BoaGroup 列表(本期补码)。<br>
|
||
* school 类型守卫:仅当身份 schoolId 对应组类型=SCHOOL_GROUP 时回填 boa.school,
|
||
* 区域组/根直属用户保持 null——等价旧约定「填 0 不回填」的行为,避免子应用读到 school=区域组 对象。
|
||
*/
|
||
private void deriveIdentity(Integer userId, BoaUser boa) {
|
||
Integer currentRoleId = null;
|
||
Integer currentSchoolId = null;
|
||
String currentSchoolName = null;
|
||
|
||
SysUserCurrentRole current = sysUserCurrentRoleRepository.findByUserId(userId).orElse(null);
|
||
if (current != null && current.getRoleId() != null) {
|
||
currentRoleId = current.getRoleId();
|
||
currentSchoolId = current.getSchoolId();
|
||
currentSchoolName = current.getSchoolName();
|
||
} else {
|
||
// 兜底:主身份整条记录(动态解析:主组 → 主校 → 该校 sortNo/id 最小行),
|
||
// 而不是「主组+主角色」组合(避免组合出 B 学校老师的非法身份)
|
||
SysUserRoleRel mainRel = identityService.resolveMainIdentity(userId);
|
||
if (mainRel != null) {
|
||
currentRoleId = mainRel.getRoleId();
|
||
currentSchoolId = mainRel.getSchoolId();
|
||
if (currentSchoolId != null && currentSchoolId > 0) {
|
||
SysGroup sch = sysGroupRepository.findById(currentSchoolId).orElse(null);
|
||
currentSchoolName = sch != null ? sch.getName() : null;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 当前激活学校:仅当对应组为学校组时回填(区域组/根直属保持 null)
|
||
if (currentSchoolId != null && currentSchoolId > 0 && isSchoolGroup(currentSchoolId)) {
|
||
boa.setSchool(toBoaGroup(currentSchoolId, currentSchoolName));
|
||
}
|
||
// 当前激活身份(优先切换记录,兜底主身份)——组装为 BoaRoleInfo 富对象(childId/childName 本期不做,恒空)
|
||
BoaRoleInfo currentRole = new BoaRoleInfo();
|
||
currentRole.setRoleId(currentRoleId);
|
||
currentRole.setRoleName(resolveRoleName(currentRoleId));
|
||
currentRole.setSchId(currentSchoolId);
|
||
currentRole.setSchName(currentSchoolName);
|
||
boa.setCurrentRole(currentRole);
|
||
|
||
// 可选学校集合:全部正常态身份行的 schoolId 去重(>0 过滤,恒为真实组 id)
|
||
List<Integer> schoolIds = sysUserRoleRelRepository
|
||
.findDistinctSchoolIdsByUserIdAndStatus(userId, StatusEnum.NORMAL);
|
||
if (schoolIds != null && !schoolIds.isEmpty()) {
|
||
List<BoaGroup> schools = schoolIds.stream()
|
||
.filter(sid -> sid != null && sid > 0)
|
||
.map(sid -> toBoaGroup(sid, null))
|
||
.collect(Collectors.toList());
|
||
boa.setSchools(schools);
|
||
}
|
||
}
|
||
|
||
/** 该组是否为学校组(SCHOOL_GROUP);组不存在视为 false。 */
|
||
private boolean isSchoolGroup(Integer groupId) {
|
||
SysGroup g = sysGroupRepository.findById(groupId).orElse(null);
|
||
return g != null && g.getType() == GroupTypeEnum.SCHOOL_GROUP;
|
||
}
|
||
|
||
/** 由角色 id 反查角色名(SysRoleEnum.value);未知角色返回 null。 */
|
||
private String resolveRoleName(Integer roleId) {
|
||
if (roleId == null) {
|
||
return null;
|
||
}
|
||
SysRoleEnum role = IEnum.getByKey(SysRoleEnum.class, roleId);
|
||
return role != null ? role.getValue() : null;
|
||
}
|
||
|
||
/** 组装一个 BoaGroup(仅填充 id 与 name;name 为 null 时回源查 sys_group) */
|
||
private BoaGroup toBoaGroup(Integer groupId, String fallbackName) {
|
||
BoaGroup bg = new BoaGroup();
|
||
bg.setId(groupId);
|
||
if (fallbackName != null) {
|
||
bg.setName(fallbackName);
|
||
} else {
|
||
SysGroup g = sysGroupRepository.findById(groupId).orElse(null);
|
||
bg.setName(g != null ? g.getName() : null);
|
||
}
|
||
return bg;
|
||
}
|
||
}
|