Files
UserBaseService/src/main/java/com/yunhai/userbase/service/impl/UserCenterService.java
T
chenxing c588dedea7
ci-userbase-service / build-and-push (push) Successful in 44s
跨校身份修订
2026-08-27 20:14:12 +08:00

254 lines
11 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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(&gt;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;
}
}