SysUserService.java 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  1. package com.jtzx.crm.module.sys.service;
  2. import cc.uncarbon.framework.core.context.TenantContext;
  3. import cc.uncarbon.framework.core.context.TenantContextHolder;
  4. import cc.uncarbon.framework.core.context.UserContextHolder;
  5. import cc.uncarbon.framework.core.enums.EnabledStatusEnum;
  6. import cc.uncarbon.framework.core.exception.BusinessException;
  7. import cc.uncarbon.framework.core.page.PageParam;
  8. import cc.uncarbon.framework.core.page.PageResult;
  9. import cc.uncarbon.framework.core.props.HelioProperties;
  10. import cn.hutool.core.bean.BeanUtil;
  11. import cn.hutool.core.collection.CollUtil;
  12. import cn.hutool.core.date.LocalDateTimeUtil;
  13. import cn.hutool.core.text.CharSequenceUtil;
  14. import cn.hutool.core.util.IdUtil;
  15. import cn.hutool.core.util.ObjectUtil;
  16. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  17. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  18. import com.jtzx.crm.module.sys.constant.SysConstant;
  19. import com.jtzx.crm.module.sys.entity.SysTenantEntity;
  20. import com.jtzx.crm.module.sys.entity.SysUserEntity;
  21. import com.jtzx.crm.module.sys.enums.SysErrorEnum;
  22. import com.jtzx.crm.module.sys.enums.SysUserStatusEnum;
  23. import com.jtzx.crm.module.sys.mapper.SysUserMapper;
  24. import com.jtzx.crm.module.sys.model.interior.UserDeptContainer;
  25. import com.jtzx.crm.module.sys.model.interior.UserRoleContainer;
  26. import com.jtzx.crm.module.sys.model.request.*;
  27. import com.jtzx.crm.module.sys.model.response.SysUserBO;
  28. import com.jtzx.crm.module.sys.model.response.SysUserLoginBO;
  29. import com.jtzx.crm.module.sys.model.response.VbenAdminUserInfoVO;
  30. import com.jtzx.crm.module.sys.util.PwdUtil;
  31. import lombok.RequiredArgsConstructor;
  32. import lombok.extern.slf4j.Slf4j;
  33. import org.springframework.lang.Nullable;
  34. import org.springframework.stereotype.Service;
  35. import org.springframework.transaction.annotation.Transactional;
  36. import java.math.BigInteger;
  37. import java.time.LocalDateTime;
  38. import java.util.*;
  39. /**
  40. * 后台用户
  41. */
  42. @RequiredArgsConstructor
  43. @Service
  44. @Slf4j
  45. public class SysUserService {
  46. private final SysUserMapper sysUserMapper;
  47. private final SysRoleService sysRoleService;
  48. private final SysDeptService sysDeptService;
  49. private final SysMenuService sysMenuService;
  50. private final SysTenantService sysTenantService;
  51. private final SysUserDeptRelationService sysUserDeptRelationService;
  52. private final SysUserRoleRelationService sysUserRoleRelationService;
  53. private final SysRoleMenuRelationService sysRoleMenuRelationService;
  54. private final HelioProperties helioProperties;
  55. /**
  56. * 后台管理-分页列表
  57. */
  58. public PageResult<SysUserBO> adminList(PageParam pageParam, AdminListSysUserDTO dto) {
  59. // 预处理:根据【手动选择的部门】筛选用户
  60. Set<Long> deptUserIds = Collections.emptySet();
  61. if (dto.needFilterBySelectedDeptId()) {
  62. deptUserIds = sysUserDeptRelationService.listUserIdsByDeptIds(Collections.singleton(dto.getSelectedDeptId()));
  63. if (CollUtil.isEmpty(deptUserIds)) {
  64. // 【手动选择的部门】没有任何用户ID,直接返回空列表
  65. return new PageResult<>(pageParam);
  66. }
  67. }
  68. // 预处理:根据【只能看到本部门及下级部门原则】筛选用户
  69. Set<Long> visibleUserIds = determineVisibleDeptUserIds();
  70. if (Objects.equals(CollUtil.getFirst(visibleUserIds), BigInteger.ZERO.longValue())) {
  71. // 其实啥也看不到……
  72. return new PageResult<>(pageParam);
  73. }
  74. Set<Long> invisibleUserIds = determineInvisibleUserIds();
  75. Page<SysUserEntity> entityPage = sysUserMapper.selectPage(
  76. new Page<>(pageParam.getPageNum(), pageParam.getPageSize()),
  77. new QueryWrapper<SysUserEntity>()
  78. .lambda()
  79. // 手机号
  80. .like(CharSequenceUtil.isNotBlank(dto.getPhoneNo()), SysUserEntity::getPhoneNo, CharSequenceUtil.cleanBlank(dto.getPhoneNo()))
  81. // 根据【手动选择的部门ID】筛选用户
  82. .in(CollUtil.isNotEmpty(deptUserIds), SysUserEntity::getId, deptUserIds)
  83. // 根据【只能看到本部门及下级部门原则】筛选用户
  84. .in(CollUtil.isNotEmpty(visibleUserIds), SysUserEntity::getId, visibleUserIds)
  85. // 不显示特定用户
  86. .notIn(CollUtil.isNotEmpty(invisibleUserIds), SysUserEntity::getId, invisibleUserIds)
  87. // 排序
  88. .orderByDesc(SysUserEntity::getCreatedAt)
  89. );
  90. return this.entityPage2BOPage(entityPage);
  91. }
  92. /**
  93. * 根据 ID 取详情
  94. *
  95. * @param id 主键ID
  96. * @return null or BO
  97. */
  98. public SysUserBO getOneById(Long id) {
  99. return this.getOneById(id, false);
  100. }
  101. /**
  102. * 根据 ID 取详情
  103. *
  104. * @param id 主键ID
  105. * @param throwIfInvalidId 是否在 ID 无效时抛出异常
  106. * @return null or BO
  107. */
  108. public SysUserBO getOneById(Long id, boolean throwIfInvalidId) throws BusinessException {
  109. dataScopeCheck(Collections.singleton(id));
  110. SysUserEntity entity = sysUserMapper.selectById(id);
  111. if (throwIfInvalidId) {
  112. SysErrorEnum.INVALID_ID.assertNotNull(entity);
  113. }
  114. return this.entity2BO(entity, true);
  115. }
  116. /**
  117. * 后台管理-新增
  118. *
  119. * @return 主键ID
  120. */
  121. @Transactional(rollbackFor = Exception.class)
  122. public Long adminInsert(AdminInsertOrUpdateSysUserDTO dto) {
  123. log.info("[后台管理-新增后台用户] >> 入参={}", dto);
  124. this.checkExistence(dto);
  125. if (Objects.nonNull(dto.getDeptId())) {
  126. // 对传入的部门ID,做数据越权检查
  127. UserDeptContainer deptContainer = sysDeptService.getCurrentUserDeptContainer(true);
  128. if (deptContainer.hasVisibleDepts() && !CollUtil.contains(deptContainer.getVisibleDeptIds(), dto.getDeptId())) {
  129. throw new BusinessException(SysErrorEnum.CANNOT_OPERATE_THIS_USER);
  130. }
  131. }
  132. dto.setId(null);
  133. SysUserEntity entity = new SysUserEntity();
  134. BeanUtil.copyProperties(dto, entity);
  135. String salt = IdUtil.randomUUID();
  136. entity
  137. .setSalt(salt)
  138. .setPin(dto.getUsername())
  139. .setPwd(PwdUtil.encrypt(dto.getPasswordOfNewUser(), salt));
  140. sysUserMapper.insert(entity);
  141. sysUserDeptRelationService.cleanAndBind(entity.getId(), dto.getDeptId());
  142. return entity.getId();
  143. }
  144. /**
  145. * 后台管理-编辑
  146. */
  147. @Transactional(rollbackFor = Exception.class)
  148. public void adminUpdate(AdminInsertOrUpdateSysUserDTO dto) {
  149. log.info("[后台管理-编辑后台用户] >> 入参={}", dto);
  150. preUpdateCheck(dto.getId(), dto.getStatus());
  151. this.checkExistence(dto);
  152. SysUserEntity entity = new SysUserEntity();
  153. BeanUtil.copyProperties(dto, entity);
  154. // 手动处理异名字段
  155. entity.setPin(dto.getUsername());
  156. sysUserDeptRelationService.cleanAndBind(dto.getId(), dto.getDeptId());
  157. sysUserMapper.updateById(entity);
  158. }
  159. /**
  160. * 后台管理-删除
  161. */
  162. @Transactional(rollbackFor = Exception.class)
  163. public void adminDelete(Collection<Long> ids) {
  164. log.info("[后台管理-删除后台用户] >> 入参={}", ids);
  165. preDeleteCheck(ids);
  166. sysUserMapper.deleteByIds(ids);
  167. }
  168. /**
  169. * 后台管理-登录
  170. */
  171. public SysUserLoginBO adminLogin(SysUserLoginDTO dto) {
  172. // 不要直接提示“账号不存在”或“密码不正确”,避免撞库攻击
  173. SysUserEntity sysUserEntity = this.getUserByPin(dto.getUsername());
  174. if (sysUserEntity == null) {
  175. return null;
  176. }
  177. if (!PwdUtil.encrypt(dto.getPassword(), sysUserEntity.getSalt()).equals(sysUserEntity.getPwd())) {
  178. return null;
  179. }
  180. if (SysUserStatusEnum.BANNED == sysUserEntity.getStatus()) {
  181. return null;
  182. }
  183. /*
  184. 以上为有效性校验, 进入实际业务逻辑
  185. ---------------------------------------------------
  186. */
  187. this.updateLastLoginAt(sysUserEntity.getId(), LocalDateTimeUtil.now());
  188. // 取账号完整信息
  189. SysUserBO sysUserBO = this.entity2BO(sysUserEntity, false);
  190. Map<Long, String> roleMap = sysRoleService.getRoleMapByUserId(sysUserBO.getId());
  191. // Map<Long, Set<String>> roleIdPermissionMap = sysMenuService.getRoleIdPermissionMap(roleMap.keySet());
  192. // 包装返回体;有的字段类型不一致, 单独转换
  193. SysUserLoginBO ret = new SysUserLoginBO();
  194. BeanUtil.copyProperties(sysUserBO, ret);
  195. // Set<String> permissions = roleIdPermissionMap.values().stream().flatMap(Collection::stream).collect(Collectors.toSet());
  196. ret
  197. .setRoleIds(new HashSet<>(roleMap.keySet()))
  198. .setRoles(new ArrayList<>(roleMap.values()));
  199. // .setPermissions(permissions)
  200. // .setRoleIdPermissionMap(roleIdPermissionMap);
  201. return ret;
  202. }
  203. /**
  204. * 后台管理-取当前用户信息
  205. */
  206. public VbenAdminUserInfoVO adminGetCurrentUserInfo() {
  207. SysUserBO sysUserBO = this.getOneById(UserContextHolder.getUserId(), true);
  208. return VbenAdminUserInfoVO.builder()
  209. .username(sysUserBO.getUsername())
  210. .nickname(sysUserBO.getNickname())
  211. .lastLoginAt(sysUserBO.getLastLoginAt())
  212. .gender(sysUserBO.getGender())
  213. .email(sysUserBO.getEmail())
  214. .phoneNo(sysUserBO.getPhoneNo())
  215. .avatar(sysUserBO.getAvatarUrl())
  216. .build();
  217. }
  218. /**
  219. * 后台管理-重置某用户密码
  220. */
  221. public void adminResetUserPassword(AdminResetSysUserPasswordDTO dto) {
  222. preUpdateCheck(dto.getUserId(), null);
  223. SysUserEntity sysUserEntity = sysUserMapper.selectById(dto.getUserId());
  224. SysUserEntity templateEntity = new SysUserEntity();
  225. templateEntity
  226. .setPwd(PwdUtil.encrypt(dto.getRandomPassword(), sysUserEntity.getSalt()))
  227. .setId(dto.getUserId());
  228. sysUserMapper.updateById(templateEntity);
  229. }
  230. /**
  231. * 后台管理-修改当前用户密码
  232. */
  233. public void adminUpdateCurrentUserPassword(AdminUpdateCurrentSysUserPasswordDTO dto) {
  234. SysUserEntity sysUserEntity = sysUserMapper.selectById(UserContextHolder.getUserId());
  235. if (sysUserEntity == null || !sysUserEntity.getPwd().equals(PwdUtil.encrypt(dto.getOldPassword(), sysUserEntity.getSalt()))) {
  236. throw new BusinessException(SysErrorEnum.INCORRECT_OLD_PASSWORD);
  237. }
  238. sysUserEntity
  239. .setPwd(PwdUtil.encrypt(dto.getConfirmNewPassword(), sysUserEntity.getSalt()))
  240. .setId(UserContextHolder.getUserId());
  241. sysUserMapper.updateById(sysUserEntity);
  242. }
  243. /**
  244. * 后台管理-绑定用户与角色关联关系
  245. */
  246. public void adminBindRoles(AdminBindUserRoleRelationDTO dto) {
  247. preBindUserRoleRelationCheck(dto);
  248. sysUserRoleRelationService.cleanAndBind(dto.getUserId(), dto.getRoleIds());
  249. }
  250. /**
  251. * 根据用户账号查询
  252. */
  253. public SysUserEntity getUserByPin(String pin) {
  254. return sysUserMapper.getUserByPin(pin);
  255. }
  256. /**
  257. * 后台管理 - 取指定用户关联角色ID
  258. *
  259. * @param userId 用户ID
  260. * @return 角色Ids
  261. */
  262. public Set<Long> listRelatedRoleIds(Long userId) {
  263. if (ObjectUtil.isNull(userId)) {
  264. return Collections.emptySet();
  265. }
  266. return sysRoleService.getRoleMapByUserId(userId).keySet();
  267. }
  268. /**
  269. * 后台管理 - 取租户用户IDs
  270. *
  271. * @param tenantId 租户ID,非主键ID
  272. * @param statusEnums 仅保留符合指定状态的,可以为null
  273. */
  274. public List<Long> listUserIdsByTenantId(Long tenantId, Collection<EnabledStatusEnum> statusEnums) {
  275. if (Objects.isNull(tenantId)) {
  276. return Collections.emptyList();
  277. }
  278. // 备份原始租户上下文;以下查询方式可同时兼容行级、数据源级多租户
  279. TenantContext originContext = TenantContextHolder.getTenantContext();
  280. try {
  281. // 临时切换租户
  282. TenantContextHolder.setTenantContext(new TenantContext(tenantId, CharSequenceUtil.EMPTY));
  283. return sysUserMapper.selectIds(statusEnums);
  284. } finally {
  285. TenantContextHolder.setTenantContext(originContext);
  286. }
  287. }
  288. /**
  289. * 后台管理-更新当前用户信息资料
  290. */
  291. @Transactional(rollbackFor = Exception.class)
  292. public void adminUpdateCurrentUserInfo(AdminUpdateCurrentSysUserInfoDTO dto) {
  293. SysUserEntity update = SysUserEntity.of(dto);
  294. update.setId(UserContextHolder.getUserId());
  295. sysUserMapper.updateById(update);
  296. }
  297. /**
  298. * 后台管理-更新当前用户头像
  299. */
  300. @Transactional(rollbackFor = Exception.class)
  301. public void adminUpdateCurrentUserAvatar(AdminUpdateCurrentSysUserAvatarDTO dto) {
  302. dto.securityCheck();
  303. SysUserEntity update = SysUserEntity.of(dto);
  304. update.setId(UserContextHolder.getUserId());
  305. sysUserMapper.updateById(update);
  306. }
  307. /*
  308. ----------------------------------------------------------------
  309. 私有方法 private methods
  310. ----------------------------------------------------------------
  311. */
  312. /**
  313. * 实体转 BO
  314. *
  315. * @param entity 实体
  316. * @param fillDeptInfo 是否根据实体部门ID,查询关联部门信息并填充到BO
  317. * @return BO
  318. */
  319. private SysUserBO entity2BO(SysUserEntity entity, boolean fillDeptInfo) {
  320. if (entity == null) {
  321. return null;
  322. }
  323. SysUserBO bo = new SysUserBO();
  324. BeanUtil.copyProperties(entity, bo);
  325. // 可以在此处为BO填充字段
  326. bo.setUsername(entity.getPin());
  327. if (fillDeptInfo) {
  328. Optional.ofNullable(sysDeptService.getSpecifiedUserDeptContainer(bo.getId(), false))
  329. .map(UserDeptContainer::primaryRelatedDept)
  330. .ifPresent(deptInfo -> bo.setDeptId(deptInfo.getId()).setDeptTitle(deptInfo.getTitle()));
  331. }
  332. return bo;
  333. }
  334. /**
  335. * 实体 List 转 BO List
  336. *
  337. * @param entityList 实体 List
  338. * @return BO List
  339. */
  340. private List<SysUserBO> entityList2BOs(List<SysUserEntity> entityList) {
  341. if (CollUtil.isEmpty(entityList)) {
  342. return Collections.emptyList();
  343. }
  344. // 深拷贝
  345. List<SysUserBO> ret = new ArrayList<>(entityList.size());
  346. entityList.forEach(
  347. entity -> ret.add(this.entity2BO(entity, true))
  348. );
  349. return ret;
  350. }
  351. /**
  352. * 实体分页转 BO 分页
  353. *
  354. * @param entityPage 实体分页
  355. * @return BO 分页
  356. */
  357. private PageResult<SysUserBO> entityPage2BOPage(Page<SysUserEntity> entityPage) {
  358. return new PageResult<SysUserBO>()
  359. .setCurrent(entityPage.getCurrent())
  360. .setSize(entityPage.getSize())
  361. .setTotal(entityPage.getTotal())
  362. .setRecords(this.entityList2BOs(entityPage.getRecords()));
  363. }
  364. /**
  365. * 检查是否已存在相同数据
  366. *
  367. * @param dto DTO
  368. */
  369. private void checkExistence(AdminInsertOrUpdateSysUserDTO dto) {
  370. SysUserEntity existingEntity = this.getUserByPin(dto.getUsername());
  371. if (existingEntity != null && !existingEntity.getId().equals(dto.getId())) {
  372. throw new BusinessException(400, "已存在相同账号,请重新输入");
  373. }
  374. }
  375. /**
  376. * 确定本部门及下级部门用户IDs
  377. * 返回空集合代表不限制
  378. * 返回[0]或有元素集合,表示有限制
  379. */
  380. private Set<Long> determineVisibleDeptUserIds() {
  381. Set<Long> visibleUserIds = Collections.emptySet();
  382. UserDeptContainer deptContainer = sysDeptService.getCurrentUserDeptContainer(true);
  383. if (deptContainer.hasVisibleDepts()) {
  384. visibleUserIds = sysUserDeptRelationService.listUserIdsByDeptIds(deptContainer.getVisibleDeptIds());
  385. if (CollUtil.isEmpty(visibleUserIds)) {
  386. // 【可见部门】没有任何用户ID,直接返回[0]
  387. return Collections.singleton(BigInteger.ZERO.longValue());
  388. }
  389. }
  390. return visibleUserIds;
  391. }
  392. /**
  393. * 确定不可见用户IDs
  394. * 租户管理员:列表中不显示超级管理员用户
  395. * 普通用户:列表中不显示超级管理员、租户管理员用户
  396. */
  397. private Set<Long> determineInvisibleUserIds() {
  398. Set<Long> invisibleRoleIds = sysRoleService.determineInvisibleRoleIds();
  399. return sysUserRoleRelationService.listUserIdsByRoleIds(invisibleRoleIds);
  400. }
  401. /**
  402. * 数据越权检查
  403. */
  404. private void dataScopeCheck(Collection<Long> userIds) {
  405. Set<Long> visibleUserIds = determineVisibleDeptUserIds();
  406. Set<Long> invisibleUserIds = determineInvisibleUserIds();
  407. if (CollUtil.isNotEmpty(visibleUserIds) && !CollUtil.containsAll(visibleUserIds, userIds)
  408. || CollUtil.isNotEmpty(invisibleUserIds) && CollUtil.containsAny(invisibleUserIds, userIds)) {
  409. throw new BusinessException(SysErrorEnum.CANNOT_OPERATE_THIS_USER);
  410. }
  411. }
  412. /**
  413. * 检查并获取租户上下文 bean,无效或被禁用则直接抛出异常
  414. *
  415. * @param tenantId 租户ID
  416. * @return TenantContext
  417. */
  418. private TenantContext checkAndGetTenantContext(Long tenantId) throws BusinessException {
  419. // 查询租户是否仍有效
  420. SysTenantEntity tenantEntity = sysTenantService.getTenantEntityByTenantId(tenantId);
  421. if (tenantEntity == null) {
  422. throw new BusinessException(SysErrorEnum.INVALID_TENANT);
  423. }
  424. if (EnabledStatusEnum.DISABLED == tenantEntity.getStatus()) {
  425. throw new BusinessException(SysErrorEnum.DISABLED_TENANT);
  426. }
  427. return TenantContext.builder()
  428. .tenantId(tenantEntity.getTenantId())
  429. .tenantName(tenantEntity.getTenantName())
  430. .build();
  431. }
  432. private void updateLastLoginAt(Long userId, LocalDateTime lastLoginAt) {
  433. SysUserEntity entity = new SysUserEntity();
  434. entity
  435. .setLastLoginAt(lastLoginAt)
  436. .setId(userId);
  437. sysUserMapper.updateById(entity);
  438. }
  439. /**
  440. * 编辑后台用户信息前检查
  441. *
  442. * @param specifiedUserId 被操作用户ID
  443. * @param statusEnum 用户状态枚举,可以为null
  444. */
  445. private void preUpdateCheck(Long specifiedUserId, @Nullable SysUserStatusEnum statusEnum) {
  446. UserRoleContainer currentUser = sysRoleService.getCurrentUserRoleContainer();
  447. if (currentUser.isAdmin()) {
  448. // 超级管理员除禁用自己外为所欲为
  449. if (statusEnum == SysUserStatusEnum.BANNED && Objects.equals(specifiedUserId, UserContextHolder.getUserId())) {
  450. throw new BusinessException(SysErrorEnum.CANNOT_OPERATE_SELF_USER);
  451. }
  452. return;
  453. }
  454. if (Objects.equals(specifiedUserId, UserContextHolder.getUserId())) {
  455. // 不能动自身用户
  456. throw new BusinessException(SysErrorEnum.CANNOT_OPERATE_SELF_USER);
  457. }
  458. // 目标是超级管理员or租户管理员时,均不能编辑
  459. UserRoleContainer specifiedUser = sysRoleService.getSpecifiedUserRoleContainer(specifiedUserId);
  460. if (specifiedUser.isGroupManager()) {
  461. throw new BusinessException(SysErrorEnum.CANNOT_OPERATE_THIS_USER);
  462. }
  463. // dataScopeCheck(Collections.singleton(specifiedUserId));
  464. // 暂未实现角色层级,一律平级
  465. }
  466. /**
  467. * 删除后台用户前检查
  468. */
  469. private void preDeleteCheck(Collection<Long> ids) {
  470. if (CollUtil.contains(ids, UserContextHolder.getUserId())) {
  471. // 不能动自身用户
  472. throw new BusinessException(SysErrorEnum.CANNOT_OPERATE_SELF_USER);
  473. }
  474. // 目标是超级管理员时,不能删除
  475. List<UserRoleContainer> specifiedUsers = ids.stream().map(sysRoleService::getSpecifiedUserRoleContainer).toList();
  476. if (specifiedUsers.stream().anyMatch(UserRoleContainer::isAdmin)) {
  477. throw new BusinessException(SysErrorEnum.CANNOT_OPERATE_THIS_USER);
  478. }
  479. // 只有超级管理员可以删租户管理员用户
  480. UserRoleContainer currentUser = sysRoleService.getCurrentUserRoleContainer();
  481. if (!currentUser.isGroupManager()) {
  482. throw new BusinessException(SysErrorEnum.CANNOT_OPERATE_THIS_USER);
  483. }
  484. dataScopeCheck(ids);
  485. // 暂未实现角色层级,一律平级
  486. }
  487. /**
  488. * 绑定后台用户与角色关联关系前检查
  489. * 防止越权访问漏洞
  490. */
  491. private void preBindUserRoleRelationCheck(AdminBindUserRoleRelationDTO dto) {
  492. UserRoleContainer currentUser = sysRoleService.getCurrentUserRoleContainer();
  493. // 是否对自己操作
  494. boolean selfFlag = Objects.equals(dto.getUserId(), UserContextHolder.getUserId());
  495. if (currentUser.isAdmin()) {
  496. // 超级管理员不能去掉自己的超级管理员角色
  497. if (selfFlag && !CollUtil.contains(dto.getRoleIds(), SysConstant.SUPER_ADMIN_ROLE_ID)) {
  498. throw new BusinessException(SysErrorEnum.CANNOT_OPERATE_SELF_USER);
  499. }
  500. // 也不能赋予其他人超级管理员角色
  501. if (!selfFlag && CollUtil.contains(dto.getRoleIds(), SysConstant.SUPER_ADMIN_ROLE_ID)) {
  502. throw new BusinessException(SysErrorEnum.CANNOT_OPERATE_THIS_USER);
  503. }
  504. return;
  505. }
  506. if (selfFlag) {
  507. // 不能动自身用户
  508. throw new BusinessException(SysErrorEnum.CANNOT_OPERATE_SELF_USER);
  509. }
  510. // 目标已经是超级管理员or租户管理员时,均不能绑定
  511. UserRoleContainer specifiedUser = sysRoleService.getSpecifiedUserRoleContainer(dto.getUserId());
  512. if (specifiedUser.isAdmin()) {
  513. throw new BusinessException(SysErrorEnum.CANNOT_OPERATE_THIS_USER);
  514. }
  515. // 超级管理员之外的用户,都需要校验自身角色范围是否满足输入值
  516. currentUserNotSuperAdmin(dto, currentUser);
  517. dataScopeCheck(Collections.singleton(dto.getUserId()));
  518. }
  519. /**
  520. * 绑定后台用户与角色关联关系前检查
  521. * 超级管理员之外的用户,都需要校验自身角色范围是否满足输入值
  522. * 拆分子方法以降低Cognitive Complexity
  523. */
  524. private void currentUserNotSuperAdmin(AdminBindUserRoleRelationDTO dto, UserRoleContainer currentUser) {
  525. if (CollUtil.isNotEmpty(dto.getRoleIds()) && !currentUser.isAdmin()) {
  526. boolean overRoles = !CollUtil.containsAll(currentUser.getRelatedRoleIds(), dto.getRoleIds());
  527. if (overRoles && currentUser.isGroupManager()) {
  528. // 普通用户超自身角色授予了;如果当前用户拥有新角色的所有菜单,那么也放行
  529. Set<Long> grantedMenuIds = sysRoleMenuRelationService.listMenuIdsByRoleIds(currentUser.getRelatedRoleIds());
  530. Set<Long> needMenuIds = sysRoleMenuRelationService.listMenuIdsByRoleIds(dto.getRoleIds());
  531. if (!CollUtil.containsAll(grantedMenuIds, needMenuIds)) {
  532. throw new BusinessException(SysErrorEnum.BEYOND_AUTHORITY_BIND_ROLES);
  533. }
  534. }
  535. // if (currentUser.isTenantAdmin()) {
  536. // // 超自身权限,但作为租户管理员有额外情况
  537. // Set<Long> invisibleRoleIds = sysRoleService.determineInvisibleRoleIds();
  538. // // 除非超越了可见角色IDs授予 or 想要授予用户租户管理员角色,否则不管
  539. // invisibleRoleIds.addAll(currentUser.getRelatedRoleIds());
  540. // if (CollUtil.containsAny(invisibleRoleIds, dto.getRoleIds())) {
  541. // throw new BusinessException(SysErrorEnum.BEYOND_AUTHORITY_BIND_ROLES);
  542. // }
  543. // }
  544. }
  545. }
  546. }