From 6117c47d7cacc3b78a943a47280740484ecf94c6 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Thu, 30 Oct 2025 11:35:53 +0800 Subject: [PATCH] =?UTF-8?q?feat(user):=20=E6=94=AF=E6=8C=81=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=A4=9A=E9=83=A8=E9=97=A8=E5=85=B3=E8=81=94=E5=8F=8A?= =?UTF-8?q?=E4=B8=BB=E9=83=A8=E9=97=A8=E5=88=87=E6=8D=A2=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在SysUser实体类中新增deptId字段标识主部门 - 扩展UserDTO支持多部门ID列表传递 - 重构用户部门关联逻辑,支持设置主部门 - 新增获取用户部门列表和切换主部门接口 - 优化部门查询SQL确保主部门排序优先 - 调整用户详情查询逻辑适配多部门场景 --- db/2pigxx.sql | 3 +- .../datascope/PigxDefaultDataScopeHandle.java | 29 +- .../PigxTocDefaultUserDetailsServiceImpl.java | 137 +- .../PigxTocMobileUserDetailsServiceImpl.java | 127 +- .../common/security/service/PigxUser.java | 237 +-- .../service/PigxUserDetailsService.java | 122 +- .../pig4cloud/pigx/admin/api/dto/UserDTO.java | 6 + .../pigx/admin/api/entity/SysUser.java | 6 + .../pig4cloud/pigx/admin/api/vo/UserVO.java | 6 + .../admin/controller/SysUserController.java | 469 +++--- .../pigx/admin/service/SysUserService.java | 273 ++-- .../service/impl/SysUserServiceImpl.java | 1359 +++++++++-------- .../main/resources/mapper/SysDeptMapper.xml | 1 + .../main/resources/mapper/SysUserMapper.xml | 35 +- 14 files changed, 1513 insertions(+), 1297 deletions(-) diff --git a/db/2pigxx.sql b/db/2pigxx.sql index 7e20a2529..e1b5f81cf 100644 --- a/db/2pigxx.sql +++ b/db/2pigxx.sql @@ -1328,6 +1328,7 @@ CREATE TABLE `sys_user` ( `osc_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '开源中国标识', `wx_cp_userid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '企业微信唯一ID', `wx_ding_userid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '钉钉唯一ID', + `dept_id` bigint(20) DEFAULT NULL COMMENT '主部门ID', `tenant_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '所属租户ID', PRIMARY KEY (`user_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户表'; @@ -1336,7 +1337,7 @@ CREATE TABLE `sys_user` ( -- Records of sys_user -- ---------------------------- BEGIN; -INSERT INTO `sys_user` VALUES (1, 'admin', '$2a$10$c/Ae0pRjJtMZg3BnvVpO.eIK6WYWVbKTzqgdy3afR7w.vd.xi3Mgy', '', '17338122125', '/admin/sys-file/local/2a14ae08150e483c93e12ac8934173e2.png', '管理员666777', '管理员', 'sw@mail.pigxl.vip', ' ', 'admin', '2018-04-20 07:15:18', '2023-04-03 14:00:06', '0', '0', NULL, '0', NULL, 'oBxPy5E-v82xWGsfzZVzkD3wEX64', NULL, 'log4j', NULL, NULL,NULL,1); +INSERT INTO `sys_user` VALUES (1, 'admin', '$2a$10$c/Ae0pRjJtMZg3BnvVpO.eIK6WYWVbKTzqgdy3afR7w.vd.xi3Mgy', '', '17338122125', '/admin/sys-file/local/2a14ae08150e483c93e12ac8934173e2.png', '管理员666777', '管理员', 'sw@mail.pigxl.vip', ' ', 'admin', '2018-04-20 07:15:18', '2023-04-03 14:00:06', '0', '0', NULL, '0', NULL, 'oBxPy5E-v82xWGsfzZVzkD3wEX64', NULL, 'log4j', NULL, NULL,NULL,1,1); COMMIT; -- ---------------------------- diff --git a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/PigxDefaultDataScopeHandle.java b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/PigxDefaultDataScopeHandle.java index 394bb2e63..b0ccb1e9e 100644 --- a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/PigxDefaultDataScopeHandle.java +++ b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/PigxDefaultDataScopeHandle.java @@ -22,11 +22,13 @@ import cn.hutool.core.util.StrUtil; import com.pig4cloud.pigx.admin.api.entity.SysDept; import com.pig4cloud.pigx.admin.api.entity.SysRole; import com.pig4cloud.pigx.admin.api.feign.RemoteDataScopeService; +import com.pig4cloud.pigx.common.core.constant.SecurityConstants; import com.pig4cloud.pigx.common.core.constant.enums.UserTypeEnum; import com.pig4cloud.pigx.common.core.util.RetOps; import com.pig4cloud.pigx.common.security.service.PigxUser; import com.pig4cloud.pigx.common.security.util.SecurityUtils; import lombok.RequiredArgsConstructor; +import org.springframework.security.core.GrantedAuthority; import java.util.Arrays; import java.util.Collections; @@ -65,7 +67,12 @@ public class PigxDefaultDataScopeHandle implements DataScopeHandle { } // 获取用户角色ID列表 - List roleIdList = user.getRoleIds(); + List roleIdList = user.getAuthorities() + .stream() + .map(GrantedAuthority::getAuthority) + .filter(authority -> authority.startsWith(SecurityConstants.ROLE)) + .map(authority -> authority.split(StrUtil.UNDERLINE)[1]) + .toList(); if (CollUtil.isEmpty(roleIdList)) { return false; } @@ -128,24 +135,20 @@ public class PigxDefaultDataScopeHandle implements DataScopeHandle { * 处理本级及下级数据权限 */ private void handleOwnChildLevelScope(PigxUser user, List deptList) { - - for (Long deptId : user.getDeptIds()) { - List descendantDeptIds = RetOps.of(dataScopeService.getDescendantList(deptId)) - .getData() - .orElseGet(Collections::emptyList) - .stream() - .map(SysDept::getDeptId) - .toList(); - deptList.addAll(descendantDeptIds); - } - + List descendantDeptIds = RetOps.of(dataScopeService.getDescendantList(user.getDeptId())) + .getData() + .orElseGet(Collections::emptyList) + .stream() + .map(SysDept::getDeptId) + .toList(); + deptList.addAll(descendantDeptIds); } /** * 处理本级数据权限 */ private void handleOwnLevelScope(PigxUser user, List deptList) { - deptList.addAll(user.getDeptIds()); + deptList.add(user.getDeptId()); } /** diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxTocDefaultUserDetailsServiceImpl.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxTocDefaultUserDetailsServiceImpl.java index a561ad446..cef32c1f1 100755 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxTocDefaultUserDetailsServiceImpl.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxTocDefaultUserDetailsServiceImpl.java @@ -52,88 +52,91 @@ import java.util.*; @RequiredArgsConstructor public class PigxTocDefaultUserDetailsServiceImpl implements PigxUserDetailsService { - private final CacheManager cacheManager; + private final CacheManager cacheManager; - private final Optional remoteAppUserServiceOptional; + private final Optional remoteAppUserServiceOptional; - /** - * 用户密码登录 - * @param username 用户密码登录 - * @return - */ - @Override - @SneakyThrows - public UserDetails loadUserByUsername(String username) { - Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS_MINI); - if (Objects.nonNull(cache) && Objects.nonNull(cache.get(username))) { - AppUserInfo appUserInfo = cache.get(username, AppUserInfo.class); - return getUserDetailsAppUser(R.ok(appUserInfo)); - } + /** + * 用户密码登录 + * + * @param username 用户密码登录 + * @return + */ + @Override + @SneakyThrows + public UserDetails loadUserByUsername(String username) { + Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS_MINI); + if (Objects.nonNull(cache) && Objects.nonNull(cache.get(username))) { + AppUserInfo appUserInfo = cache.get(username, AppUserInfo.class); + return getUserDetailsAppUser(R.ok(appUserInfo)); + } - R result = remoteAppUserServiceOptional.get().info(username); - Optional userInfoOptional = RetOps.of(result).getData(); - userInfoOptional.ifPresent(userInfo -> Objects.requireNonNull(cache).put(username, userInfo)); - return getUserDetailsAppUser(result); - } + R result = remoteAppUserServiceOptional.get().info(username); + Optional userInfoOptional = RetOps.of(result).getData(); + userInfoOptional.ifPresent(userInfo -> Objects.requireNonNull(cache).put(username, userInfo)); + return getUserDetailsAppUser(result); + } - @Override - public UserDetails loadUserByUser(PigxUser pigxUser) { - return pigxUser; - } + @Override + public UserDetails loadUserByUser(PigxUser pigxUser) { + return pigxUser; + } - UserDetails getUserDetailsAppUser(R result) { - // @formatter:off + UserDetails getUserDetailsAppUser(R result) { + // @formatter:off return RetOps.of(result) .assertSuccess(r -> new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, MsgUtils.getSecurityMessage(NOTFOUND_USER_ERROR_CODE),null))) .getData() .map(this::convertUserDetailsAppUser) .orElseThrow(() -> new UsernameNotFoundException(MsgUtils.getSecurityMessage(NOTFOUND_USER_ERROR_CODE))); // @formatter:on - } + } - /** - * UserInfo 转 UserDetails - * @param info - * @return 返回UserDetails对象 - */ - UserDetails convertUserDetailsAppUser(AppUserInfo info) { - Set dbAuthsSet = new HashSet<>(); - if (ArrayUtil.isNotEmpty(info.getRoles())) { - // 获取角色 - Arrays.stream(info.getRoles()).forEach(roleId -> dbAuthsSet.add(SecurityConstants.ROLE + roleId)); - // 获取资源 - dbAuthsSet.addAll(Arrays.asList(info.getPermissions())); + /** + * UserInfo 转 UserDetails + * + * @param info + * @return 返回UserDetails对象 + */ + UserDetails convertUserDetailsAppUser(AppUserInfo info) { + Set dbAuthsSet = new HashSet<>(); + if (ArrayUtil.isNotEmpty(info.getRoles())) { + // 获取角色 + Arrays.stream(info.getRoles()).forEach(roleId -> dbAuthsSet.add(SecurityConstants.ROLE + roleId)); + // 获取资源 + dbAuthsSet.addAll(Arrays.asList(info.getPermissions())); - } - Collection authorities = AuthorityUtils - .createAuthorityList(dbAuthsSet.toArray(new String[0])); - AppUser user = info.getAppUser(); - // 构造security用户 + } + Collection authorities = AuthorityUtils + .createAuthorityList(dbAuthsSet.toArray(new String[0])); + AppUser user = info.getAppUser(); + // 构造security用户 - return new PigxUser(user.getUserId(), user.getUsername(), null, null, null, user.getPhone(), user.getAvatar(), - user.getNickname(), user.getName(), user.getEmail(), user.getTenantId(), - SecurityConstants.BCRYPT + user.getPassword(), true, true, UserTypeEnum.TOC.getStatus(), true, null, - !CommonConstants.STATUS_LOCK.equals(user.getLockFlag()), authorities); - } + return new PigxUser(user.getUserId(), user.getUsername(), null, null, null, null, user.getPhone(), user.getAvatar(), + user.getNickname(), user.getName(), user.getEmail(), user.getTenantId(), + SecurityConstants.BCRYPT + user.getPassword(), true, true, UserTypeEnum.TOC.getStatus(), true, null, + !CommonConstants.STATUS_LOCK.equals(user.getLockFlag()), authorities); + } - @Override - public int getOrder() { - return 10; - } + @Override + public int getOrder() { + return 10; + } - /** - * 支持所有的 mobile 类型 - * @param clientId 目标客户端 - * @param grantType 授权类型 - * @return true/false - */ - @Override - public boolean support(String clientId, String grantType) { - if (Objects.isNull(WebUtils.getRequest())) { - return false; - } - String header = WebUtils.getRequest().getHeader(SecurityConstants.HEADER_TOC); - return SecurityConstants.HEADER_TOC_YES.equals(header); - } + /** + * 支持所有的 mobile 类型 + * + * @param clientId 目标客户端 + * @param grantType 授权类型 + * @return true/false + */ + @Override + public boolean support(String clientId, String grantType) { + if (Objects.isNull(WebUtils.getRequest())) { + return false; + } + String header = WebUtils.getRequest().getHeader(SecurityConstants.HEADER_TOC); + return SecurityConstants.HEADER_TOC_YES.equals(header); + } } diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxTocMobileUserDetailsServiceImpl.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxTocMobileUserDetailsServiceImpl.java index f510ba320..b168501b3 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxTocMobileUserDetailsServiceImpl.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxTocMobileUserDetailsServiceImpl.java @@ -49,82 +49,85 @@ import java.util.*; @RequiredArgsConstructor public class PigxTocMobileUserDetailsServiceImpl implements PigxUserDetailsService { - private final Optional remoteAppUserServiceOptional; + private final Optional remoteAppUserServiceOptional; - /** - * 用户密码登录 - * @param phone 用户密码登录 - * @return - */ - @Override - @SneakyThrows - public UserDetails loadUserByUsername(String phone) { - if (!remoteAppUserServiceOptional.isPresent()) { - throw new UnsupportedOperationException(); - } - R info = remoteAppUserServiceOptional.get().social(phone); - return this.getUserDetailsAppUser(info); - } + /** + * 用户密码登录 + * + * @param phone 用户密码登录 + * @return + */ + @Override + @SneakyThrows + public UserDetails loadUserByUsername(String phone) { + if (!remoteAppUserServiceOptional.isPresent()) { + throw new UnsupportedOperationException(); + } + R info = remoteAppUserServiceOptional.get().social(phone); + return this.getUserDetailsAppUser(info); + } - @Override - public UserDetails loadUserByUser(PigxUser pigxUser) { - return pigxUser; - } + @Override + public UserDetails loadUserByUser(PigxUser pigxUser) { + return pigxUser; + } - UserDetails getUserDetailsAppUser(R result) { - // @formatter:off + UserDetails getUserDetailsAppUser(R result) { + // @formatter:off return RetOps.of(result) .assertSuccess(r -> new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,MsgUtils.getSecurityMessage(NOTFOUND_USER_ERROR_CODE),null))) .getData() .map(this::convertUserDetailsAppUser) .orElseThrow(() -> new UsernameNotFoundException(MsgUtils.getSecurityMessage(NOTFOUND_USER_ERROR_CODE))); // @formatter:on - } + } - /** - * UserInfo 转 UserDetails - * @param info - * @return 返回UserDetails对象 - */ - UserDetails convertUserDetailsAppUser(AppUserInfo info) { - Set dbAuthsSet = new HashSet<>(); - if (ArrayUtil.isNotEmpty(info.getRoles())) { - // 获取角色 - Arrays.stream(info.getRoles()).forEach(roleId -> dbAuthsSet.add(SecurityConstants.ROLE + roleId)); - // 获取资源 - dbAuthsSet.addAll(Arrays.asList(info.getPermissions())); + /** + * UserInfo 转 UserDetails + * + * @param info + * @return 返回UserDetails对象 + */ + UserDetails convertUserDetailsAppUser(AppUserInfo info) { + Set dbAuthsSet = new HashSet<>(); + if (ArrayUtil.isNotEmpty(info.getRoles())) { + // 获取角色 + Arrays.stream(info.getRoles()).forEach(roleId -> dbAuthsSet.add(SecurityConstants.ROLE + roleId)); + // 获取资源 + dbAuthsSet.addAll(Arrays.asList(info.getPermissions())); - } - Collection authorities = AuthorityUtils - .createAuthorityList(dbAuthsSet.toArray(new String[0])); - AppUser user = info.getAppUser(); - // 构造security用户 + } + Collection authorities = AuthorityUtils + .createAuthorityList(dbAuthsSet.toArray(new String[0])); + AppUser user = info.getAppUser(); + // 构造security用户 - return new PigxUser(user.getUserId(), user.getUsername(), null, null, null, user.getPhone(), user.getAvatar(), - user.getNickname(), user.getName(), user.getEmail(), user.getTenantId(), - SecurityConstants.BCRYPT + user.getPassword(), true, true, UserTypeEnum.TOC.getStatus(), true, null, - !CommonConstants.STATUS_LOCK.equals(user.getLockFlag()), authorities); - } + return new PigxUser(user.getUserId(), user.getUsername(), null, null, null, null, user.getPhone(), user.getAvatar(), + user.getNickname(), user.getName(), user.getEmail(), user.getTenantId(), + SecurityConstants.BCRYPT + user.getPassword(), true, true, UserTypeEnum.TOC.getStatus(), true, null, + !CommonConstants.STATUS_LOCK.equals(user.getLockFlag()), authorities); + } - @Override - public int getOrder() { - return 15; - } + @Override + public int getOrder() { + return 15; + } - /** - * 支持所有的 mobile 类型 - * @param clientId 目标客户端 - * @param grantType 授权类型 - * @return true/false - */ - @Override - public boolean support(String clientId, String grantType) { - if (Objects.isNull(WebUtils.getRequest())) { - return false; - } + /** + * 支持所有的 mobile 类型 + * + * @param clientId 目标客户端 + * @param grantType 授权类型 + * @return true/false + */ + @Override + public boolean support(String clientId, String grantType) { + if (Objects.isNull(WebUtils.getRequest())) { + return false; + } - String header = WebUtils.getRequest().getHeader(SecurityConstants.HEADER_TOC); - return SecurityConstants.HEADER_TOC_YES.equals(header) && SecurityConstants.GRANT_MOBILE.equals(grantType); - } + String header = WebUtils.getRequest().getHeader(SecurityConstants.HEADER_TOC); + return SecurityConstants.HEADER_TOC_YES.equals(header) && SecurityConstants.GRANT_MOBILE.equals(grantType); + } } diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxUser.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxUser.java index bf6503f37..efd02e20f 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxUser.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxUser.java @@ -39,131 +39,146 @@ import java.util.Map; */ public class PigxUser extends User implements OAuth2AuthenticatedPrincipal { - @Serial - private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; + @Serial + private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; - /** - * 扩展属性,方便存放oauth 上下文相关信息 - */ - private final Map attributes = new HashMap<>(); + /** + * 扩展属性,方便存放oauth 上下文相关信息 + */ + private final Map attributes = new HashMap<>(); - /** - * 用户ID - */ - @Getter - private Long id; + /** + * 用户ID + */ + @Getter + private Long id; - /** 角色ID列表 */ - @Getter - private List roleIds; + /** + * 角色ID列表 + */ + @Getter + private List roleIds; - /** - * 部门ID - */ - @Getter - private List deptIds; + /** + * 主部门ID + */ + @Getter + private Long deptId; + /** + * (全部)部门ID + */ + @Getter + private List deptIds; - /** 岗位ID列表 */ - @Getter - private List postIds; + /** + * 岗位ID列表 + */ + @Getter + private List postIds; - /** - * 手机号 - */ - @Getter - private String phone; + /** + * 手机号 + */ + @Getter + private String phone; - /** - * 头像 - */ - @Getter - private String avatar; + /** + * 头像 + */ + @Getter + private String avatar; - /** - * 租户ID - */ - @Getter - private Long tenantId; + /** + * 租户ID + */ + @Getter + private Long tenantId; - /** - * 拓展字段:昵称 - */ - @Getter - private String nickname; + /** + * 拓展字段:昵称 + */ + @Getter + private String nickname; - /** - * 拓展字段:姓名 - */ - @Getter - private String name; + /** + * 拓展字段:姓名 + */ + @Getter + private String name; - /** - * 拓展字段:邮箱 - */ - @Getter - private String email; + /** + * 拓展字段:邮箱 + */ + @Getter + private String email; - /** 用户类型 */ - @Getter - private String userType; + /** + * 用户类型 + */ + @Getter + private String userType; - /** 密码修改时间 */ - @Getter - private LocalDateTime passwordModifyTime; + /** + * 密码修改时间 + */ + @Getter + private LocalDateTime passwordModifyTime; - /** - * Construct the User with the details required by - * {@link DaoAuthenticationProvider}. - * @param id 用户ID - * @param deptIds 部门ID - * @param tenantId 租户ID - * @param nickname 昵称 - * @param name 姓名 - * @param email 邮箱 the username presented to the - * DaoAuthenticationProvider - * @param password the password that should be presented to the - * DaoAuthenticationProvider - * @param enabled set to true if the user is enabled - * @param accountNonExpired set to true if the account has not expired - * @param credentialsNonExpired set to true if the credentials have not - * expired - * @param accountNonLocked set to true if the account is not locked - * @param authorities the authorities that should be granted to the caller if they - * presented the correct username and password and the user is enabled. Not null. - * @throws IllegalArgumentException if a null value was passed either as - * a parameter or as an element in the GrantedAuthority collection - */ - @JsonCreator - public PigxUser(@JsonProperty("id") Long id, @JsonProperty("username") String username, - @JsonProperty("roleIds") List roleIds, @JsonProperty("deptIds") List deptIds, - @JsonProperty("postIds") List postIds, @JsonProperty("phone") String phone, - @JsonProperty("avatar") String avatar, @JsonProperty("nickname") String nickname, - @JsonProperty("name") String name, @JsonProperty("email") String email, - @JsonProperty("tenantId") Long tenantId, @JsonProperty("password") String password, - @JsonProperty("enabled") boolean enabled, @JsonProperty("accountNonExpired") boolean accountNonExpired, - @JsonProperty("userType") String userType, - @JsonProperty("credentialsNonExpired") boolean credentialsNonExpired, - @JsonProperty("passwordModifyTime") LocalDateTime passwordModifyTime, - @JsonProperty("accountNonLocked") boolean accountNonLocked, - @JsonProperty("authorities") Collection authorities) { - super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); - this.id = id; - this.roleIds = roleIds; - this.deptIds = deptIds; - this.postIds = postIds; - this.phone = phone; - this.avatar = avatar; - this.tenantId = tenantId; - this.nickname = nickname; - this.name = name; - this.email = email; - this.userType = userType; - this.passwordModifyTime = passwordModifyTime; - } + /** + * Construct the User with the details required by + * {@link DaoAuthenticationProvider}. + * + * @param id 用户ID + * @param deptIds 部门ID + * @param tenantId 租户ID + * @param nickname 昵称 + * @param name 姓名 + * @param email 邮箱 the username presented to the + * DaoAuthenticationProvider + * @param password the password that should be presented to the + * DaoAuthenticationProvider + * @param enabled set to true if the user is enabled + * @param accountNonExpired set to true if the account has not expired + * @param credentialsNonExpired set to true if the credentials have not + * expired + * @param accountNonLocked set to true if the account is not locked + * @param authorities the authorities that should be granted to the caller if they + * presented the correct username and password and the user is enabled. Not null. + * @throws IllegalArgumentException if a null value was passed either as + * a parameter or as an element in the GrantedAuthority collection + */ + @JsonCreator + public PigxUser(@JsonProperty("id") Long id, @JsonProperty("username") String username, + @JsonProperty("roleIds") List roleIds, @JsonProperty("deptId") Long deptId, @JsonProperty("deptIds") List deptIds, + @JsonProperty("postIds") List postIds, @JsonProperty("phone") String phone, + @JsonProperty("avatar") String avatar, @JsonProperty("nickname") String nickname, + @JsonProperty("name") String name, @JsonProperty("email") String email, + @JsonProperty("tenantId") Long tenantId, @JsonProperty("password") String password, + @JsonProperty("enabled") boolean enabled, @JsonProperty("accountNonExpired") boolean accountNonExpired, + @JsonProperty("userType") String userType, + @JsonProperty("credentialsNonExpired") boolean credentialsNonExpired, + @JsonProperty("passwordModifyTime") LocalDateTime passwordModifyTime, + @JsonProperty("accountNonLocked") boolean accountNonLocked, + @JsonProperty("authorities") Collection authorities) { + super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); + this.id = id; + this.roleIds = roleIds; + this.deptId = deptId; + this.deptIds = deptIds; + this.postIds = postIds; + this.phone = phone; + this.avatar = avatar; + this.tenantId = tenantId; + this.nickname = nickname; + this.name = name; + this.email = email; + this.userType = userType; + this.passwordModifyTime = passwordModifyTime; + } - @Override - public Map getAttributes() { - return this.attributes; - } + @Override + public Map getAttributes() { + return this.attributes; + } } diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxUserDetailsService.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxUserDetailsService.java index e156865dc..d1ed98c35 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxUserDetailsService.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxUserDetailsService.java @@ -42,75 +42,81 @@ import java.util.Set; */ public interface PigxUserDetailsService extends UserDetailsService, Ordered { - /** - * Notfound 用户错误代码 - */ - String NOTFOUND_USER_ERROR_CODE = "UserDetailsService.notFound"; + /** + * Notfound 用户错误代码 + */ + String NOTFOUND_USER_ERROR_CODE = "UserDetailsService.notFound"; - /** - * 是否支持此客户端校验 - * @param clientId 请求客户端 - * @param grantType 授权类型 - * @return true/false - */ - default boolean support(String clientId, String grantType) { - return true; - } + /** + * 是否支持此客户端校验 + * + * @param clientId 请求客户端 + * @param grantType 授权类型 + * @return true/false + */ + default boolean support(String clientId, String grantType) { + return true; + } - /** - * 排序值 默认取最大的 - * @return 排序值 - */ - default int getOrder() { - return 0; - } + /** + * 排序值 默认取最大的 + * + * @return 排序值 + */ + default int getOrder() { + return 0; + } - /** - * 获取用户详细信息 - * @param userInfoOptional 用户信息:可选 - * @return {@link UserDetails } - */ - default UserDetails getUserDetails(Optional userInfoOptional) { - // @formatter:off + /** + * 获取用户详细信息 + * + * @param userInfoOptional 用户信息:可选 + * @return {@link UserDetails } + */ + default UserDetails getUserDetails(Optional userInfoOptional) { + // @formatter:off return userInfoOptional .map(this::convertUserDetails) .orElseThrow(() -> new UsernameNotFoundException(MsgUtils.getSecurityMessage(NOTFOUND_USER_ERROR_CODE))); // @formatter:on - } + } - /** - * UserInfo 转 UserDetails - * @param info - * @return 返回UserDetails对象 - */ - default UserDetails convertUserDetails(UserInfo info) { - Set dbAuthsSet = new HashSet<>(); + /** + * UserInfo 转 UserDetails + * + * @param info + * @return 返回UserDetails对象 + */ + default UserDetails convertUserDetails(UserInfo info) { + Set dbAuthsSet = new HashSet<>(); - // 维护角色列表 - info.getRoleList().forEach(role -> dbAuthsSet.add(SecurityConstants.ROLE + role.getRoleId())); + // 维护角色列表 + info.getRoleList().forEach(role -> dbAuthsSet.add(SecurityConstants.ROLE + role.getRoleId())); - // 维护权限列表 - dbAuthsSet.addAll(info.getPermissions()); - Collection authorities = AuthorityUtils - .createAuthorityList(dbAuthsSet.toArray(new String[0])); + // 维护权限列表 + dbAuthsSet.addAll(info.getPermissions()); + Collection authorities = AuthorityUtils + .createAuthorityList(dbAuthsSet.toArray(new String[0])); - return new PigxUser(info.getUserId(), info.getUsername(), - info.getRoleList().stream().map(SysRole::getRoleId).toList(), - info.getDeptList().stream().map(SysDept::getDeptId).toList(), - info.getPostList().stream().map(SysPost::getPostId).toList(), info.getPhone(), info.getAvatar(), - info.getNickname(), info.getName(), info.getEmail(), info.getTenantId(), - SecurityConstants.BCRYPT + info.getPassword(), true, true, UserTypeEnum.TOB.getStatus(), - !CommonConstants.STATUS_LOCK.equals(info.getPasswordExpireFlag()) // 密码过期判断 - , info.getPasswordModifyTime(), !CommonConstants.STATUS_LOCK.equals(info.getLockFlag()), authorities); - } + return new PigxUser(info.getUserId(), info.getUsername(), + info.getRoleList().stream().map(SysRole::getRoleId).toList(), + info.getDeptId(), + info.getDeptList().stream().map(SysDept::getDeptId).toList(), + info.getPostList().stream().map(SysPost::getPostId).toList(), info.getPhone(), info.getAvatar(), + info.getNickname(), info.getName(), info.getEmail(), info.getTenantId(), + SecurityConstants.BCRYPT + info.getPassword(), true, true, UserTypeEnum.TOB.getStatus(), + !CommonConstants.STATUS_LOCK.equals(info.getPasswordExpireFlag()) // 密码过期判断 + , info.getPasswordModifyTime(), !CommonConstants.STATUS_LOCK.equals(info.getLockFlag()), authorities); + } - /** - * 通过用户实体查询 - * @param pigxUser user - * @return - */ - default UserDetails loadUserByUser(PigxUser pigxUser) { - return this.loadUserByUsername(pigxUser.getUsername()); - } + /** + * 通过用户实体查询 + * + * @param pigxUser user + * @return + */ + default UserDetails loadUserByUser(PigxUser pigxUser) { + return this.loadUserByUsername(pigxUser.getUsername()); + } } diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/UserDTO.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/UserDTO.java index 42bf9a176..19b9d22b9 100644 --- a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/UserDTO.java +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/UserDTO.java @@ -47,6 +47,12 @@ public class UserDTO extends SysUser { @Schema(description = "部门id") private Long deptId; + /** + * 部门id集合(支持多部门) + */ + @Schema(description = "部门id集合") + private List deptIds; + /** * 岗位ID */ diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUser.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUser.java index 04d172319..5c68126e7 100644 --- a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUser.java +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUser.java @@ -150,6 +150,12 @@ public class SysUser extends Model implements Serializable { @Schema(description = "用户所属租户id") private Long tenantId; + /** + * 主部门ID + */ + @Schema(description = "用户主部门id") + private Long deptId; + /** * 微信openid */ diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/vo/UserVO.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/vo/UserVO.java index 53f74b811..13a9700d0 100644 --- a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/vo/UserVO.java +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/vo/UserVO.java @@ -160,6 +160,12 @@ public class UserVO implements Serializable { @Schema(description = "所属租户") private Long tenantId; + /** + * 主部门ID + */ + @Schema(description = "主部门id") + private Long deptId; + @Schema(description = "所属部门名称") private List deptList = new ArrayList<>(); diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysUserController.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysUserController.java index 5daf57c64..9e4ef4ed1 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysUserController.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysUserController.java @@ -24,6 +24,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.pig4cloud.pigx.admin.api.dto.UserDTO; import com.pig4cloud.pigx.admin.api.dto.UserInfo; +import com.pig4cloud.pigx.admin.api.entity.SysDept; import com.pig4cloud.pigx.admin.api.entity.SysUser; import com.pig4cloud.pigx.admin.api.vo.UserExcelVO; import com.pig4cloud.pigx.admin.service.SysUserService; @@ -61,238 +62,280 @@ import java.util.List; @SecurityRequirement(name = HttpHeaders.AUTHORIZATION) public class SysUserController { - private final SysUserService userService; + private final SysUserService userService; - /** - * 获取指定用户全部信息 - * @return 用户信息 - */ - @Inner - @GetMapping("/info/{username}") - public R info(@PathVariable String username) { - UserDTO userDTO = new UserDTO(); - userDTO.setUsername(username); - return userService.getUserInfo(userDTO); - } + /** + * 获取指定用户全部信息 + * + * @return 用户信息 + */ + @Inner + @GetMapping("/info/{username}") + public R info(@PathVariable String username) { + UserDTO userDTO = new UserDTO(); + userDTO.setUsername(username); + return userService.getUserInfo(userDTO); + } - /** - * 获取当前用户全部信息 - * @return 用户信息 - */ - @GetMapping(value = { "/info" }) - public R info() { - String username = SecurityUtils.getUser().getUsername(); - UserDTO userDTO = new UserDTO(); - userDTO.setUsername(username); - // 获取用户信息,不返回数据库密码字段 - R userInfoR = userService.getUserInfo(userDTO); - if (userInfoR.getData() != null) { - userInfoR.getData().setPassword(null); - } - return userInfoR; - } + /** + * 获取当前用户全部信息 + * + * @return 用户信息 + */ + @GetMapping(value = {"/info"}) + public R info() { + String username = SecurityUtils.getUser().getUsername(); + UserDTO userDTO = new UserDTO(); + userDTO.setUsername(username); + // 获取用户信息,不返回数据库密码字段 + R userInfoR = userService.getUserInfo(userDTO); + if (userInfoR.getData() != null) { + userInfoR.getData().setPassword(null); + } + return userInfoR; + } - /** - * 通过ID查询用户信息 - * @param id ID - * @return 用户信息 - */ - @GetMapping("/details/{id}") - public R user(@PathVariable Long id) { - return R.ok(userService.selectUserVoById(id)); - } + /** + * 通过ID查询用户信息 + * + * @param id ID + * @return 用户信息 + */ + @GetMapping("/details/{id}") + public R user(@PathVariable Long id) { + return R.ok(userService.selectUserVoById(id)); + } - /** - * 查询用户信息 - * @param query 查询条件 - * @return 不为空返回用户名 - */ - @Inner(value = false) - @GetMapping("/details") - public R getDetails(@ParameterObject SysUser query) { - // 根据用户名、手机号 查询不区分租户 - SysUser sysUser = TenantBroker.noneAs(() -> userService.getOne(Wrappers.query(query), false)); - return R.ok(sysUser == null ? null : CommonConstants.SUCCESS); - } + /** + * 查询用户信息 + * + * @param query 查询条件 + * @return 不为空返回用户名 + */ + @Inner(value = false) + @GetMapping("/details") + public R getDetails(@ParameterObject SysUser query) { + // 根据用户名、手机号 查询不区分租户 + SysUser sysUser = TenantBroker.noneAs(() -> userService.getOne(Wrappers.query(query), false)); + return R.ok(sysUser == null ? null : CommonConstants.SUCCESS); + } - /** - * 删除用户信息 - * @param ids ID - * @return R - */ - @SysLog("删除用户信息") - @DeleteMapping - @HasPermission("sys_user_del") - @Operation(summary = "删除用户", description = "根据ID删除用户") - public R userDel(@RequestBody Long[] ids) { - return R.ok(userService.deleteUserByIds(ids)); - } + /** + * 删除用户信息 + * + * @param ids ID + * @return R + */ + @SysLog("删除用户信息") + @DeleteMapping + @HasPermission("sys_user_del") + @Operation(summary = "删除用户", description = "根据ID删除用户") + public R userDel(@RequestBody Long[] ids) { + return R.ok(userService.deleteUserByIds(ids)); + } - /** - * 添加用户 - * @param userDto 用户信息 - * @return success/false - */ - @SysLog("添加用户") - @PostMapping - @HasPermission("sys_user_add") - public R user(@RequestBody UserDTO userDto) { - return R.ok(userService.saveUser(userDto)); - } + /** + * 添加用户 + * + * @param userDto 用户信息 + * @return success/false + */ + @SysLog("添加用户") + @PostMapping + @HasPermission("sys_user_add") + public R user(@RequestBody UserDTO userDto) { + return R.ok(userService.saveUser(userDto)); + } - /** - * 分页查询用户 - * @param page 参数集 - * @param userDTO 查询参数列表 - * @return 用户集合 - */ - @GetMapping("/page") - @HasPermission("sys_user_view") - public R getUserPage(@ParameterObject Page page, @ParameterObject UserDTO userDTO) { - return R.ok(userService.getUsersWithRolePage(page, userDTO)); - } + /** + * 分页查询用户 + * + * @param page 参数集 + * @param userDTO 查询参数列表 + * @return 用户集合 + */ + @GetMapping("/page") + @HasPermission("sys_user_view") + public R getUserPage(@ParameterObject Page page, @ParameterObject UserDTO userDTO) { + return R.ok(userService.getUsersWithRolePage(page, userDTO)); + } - /** - * 管理员更新用户信息 - * @param userDto 用户信息 - * @return R - */ - @SysLog("更新用户信息") - @PutMapping - @HasPermission("sys_user_edit") - public R updateUser(@Valid @RequestBody UserDTO userDto) { - return R.ok(userService.updateUser(userDto)); - } + /** + * 管理员更新用户信息 + * + * @param userDto 用户信息 + * @return R + */ + @SysLog("更新用户信息") + @PutMapping + @HasPermission("sys_user_edit") + public R updateUser(@Valid @RequestBody UserDTO userDto) { + return R.ok(userService.updateUser(userDto)); + } - /** - * 修改个人信息 (当前用户) - * @param userDto userDto - * @return success/false - */ - @SysLog("修改个人信息") - @PutMapping("/personal/edit") - public R updateUserInfo(@Valid @RequestBody UserDTO userDto) { - return userService.updateUserInfo(userDto); - } + /** + * 修改个人信息 (当前用户) + * + * @param userDto userDto + * @return success/false + */ + @SysLog("修改个人信息") + @PutMapping("/personal/edit") + public R updateUserInfo(@Valid @RequestBody UserDTO userDto) { + return userService.updateUserInfo(userDto); + } - /** - * 修改个人密码 (当前用户) - * @param userDto 用户DTO对象,包含需要修改密码的用户信息 - * @return R 返回结果对象,包含修改密码操作的结果信息 - */ - @PutMapping("/personal/password") - public R updateUserPassword(@RequestBody UserDTO userDto) { - String username = SecurityUtils.getUser().getUsername(); - userDto.setUsername(username); - return userService.changePassword(userDto); - } + /** + * 修改个人密码 (当前用户) + * + * @param userDto 用户DTO对象,包含需要修改密码的用户信息 + * @return R 返回结果对象,包含修改密码操作的结果信息 + */ + @PutMapping("/personal/password") + public R updateUserPassword(@RequestBody UserDTO userDto) { + String username = SecurityUtils.getUser().getUsername(); + userDto.setUsername(username); + return userService.changePassword(userDto); + } - /** - * @param username 用户名称 - * @return 上级部门用户列表 - */ - @GetMapping("/ancestor/{username}") - public R listAncestorUsers(@PathVariable String username) { - return R.ok(userService.listAncestorUsers(username)); - } + /** + * @param username 用户名称 + * @return 上级部门用户列表 + */ + @GetMapping("/ancestor/{username}") + public R listAncestorUsers(@PathVariable String username) { + return R.ok(userService.listAncestorUsers(username)); + } - /** - * 导出excel 表格 - * @param userDTO 查询条件 - * @return - */ - @ResponseExcel - @GetMapping("/export") - @HasPermission("sys_user_export") - public List export(UserDTO userDTO, Long[] ids) { - return userService.listUser(userDTO, ids); - } + /** + * 导出excel 表格 + * + * @param userDTO 查询条件 + * @return + */ + @ResponseExcel + @GetMapping("/export") + @HasPermission("sys_user_export") + public List export(UserDTO userDTO, Long[] ids) { + return userService.listUser(userDTO, ids); + } - /** - * 导入用户 - * @param excelVOList 用户列表 - * @param bindingResult 错误信息列表 - * @return R - */ - @PostMapping("/import") - @HasPermission("sys_user_export") - public R importUser(@RequestExcel List excelVOList, BindingResult bindingResult) { - return userService.importUser(excelVOList, bindingResult); - } + /** + * 导入用户 + * + * @param excelVOList 用户列表 + * @param bindingResult 错误信息列表 + * @return R + */ + @PostMapping("/import") + @HasPermission("sys_user_export") + public R importUser(@RequestExcel List excelVOList, BindingResult bindingResult) { + return userService.importUser(excelVOList, bindingResult); + } - /** - * 锁定指定用户 - * @param username 用户名 - * @return R - */ - @Inner - @PutMapping("/lock/{username}") - public R lockUser(@PathVariable String username) { - return userService.lockUser(username); - } + /** + * 锁定指定用户 + * + * @param username 用户名 + * @return R + */ + @Inner + @PutMapping("/lock/{username}") + public R lockUser(@PathVariable String username) { + return userService.lockUser(username); + } - /** - * 解绑定接口 - * @param type 需要解绑定的类型 - * @return R 返回结果对象,包含解绑定操作的结果信息 - */ - @PostMapping("/unbinding") - public R unbinding(String type) { - return userService.unbinding(type); - } + /** + * 解绑定接口 + * + * @param type 需要解绑定的类型 + * @return R 返回结果对象,包含解绑定操作的结果信息 + */ + @PostMapping("/unbinding") + public R unbinding(String type) { + return userService.unbinding(type); + } - /** - * 校验密码接口 - * @param password 需要校验的密码 - * @return R 返回结果对象,包含校验密码操作的结果信息 - */ - @PostMapping("/check") - public R check(String password) { - return userService.checkPassword(SecurityUtils.getUser().getUsername(), password); - } + /** + * 校验密码接口 + * + * @param password 需要校验的密码 + * @return R 返回结果对象,包含校验密码操作的结果信息 + */ + @PostMapping("/check") + public R check(String password) { + return userService.checkPassword(SecurityUtils.getUser().getUsername(), password); + } - /** - * 根据角色ID列表获取用户ID列表接口 - * @param roleIdList 角色ID列表 - * @return R 返回结果对象,包含根据角色ID列表获取到的用户ID列表信息 - */ - @Inner - @GetMapping("/getUserIdListByRoleIdList") - public R> getUserIdListByRoleIdList(Long[] roleIdList) { - return R.ok(userService.listUserIdByRoleIds(CollUtil.toList(roleIdList))); - } + /** + * 根据角色ID列表获取用户ID列表接口 + * + * @param roleIdList 角色ID列表 + * @return R 返回结果对象,包含根据角色ID列表获取到的用户ID列表信息 + */ + @Inner + @GetMapping("/getUserIdListByRoleIdList") + public R> getUserIdListByRoleIdList(Long[] roleIdList) { + return R.ok(userService.listUserIdByRoleIds(CollUtil.toList(roleIdList))); + } - /** - * 根据部门ID列表获取用户ID列表接口 - * @param deptIdList 部门ID列表 - * @return R 返回结果对象,包含根据部门ID列表获取到的用户ID列表信息 - */ - @Inner - @GetMapping("/getUserIdListByDeptIdList") - public R> getUserIdListByDeptIdList(Long[] deptIdList) { - return R.ok(userService.listUserIdByDeptIds(CollUtil.toList(deptIdList))); - } + /** + * 根据部门ID列表获取用户ID列表接口 + * + * @param deptIdList 部门ID列表 + * @return R 返回结果对象,包含根据部门ID列表获取到的用户ID列表信息 + */ + @Inner + @GetMapping("/getUserIdListByDeptIdList") + public R> getUserIdListByDeptIdList(Long[] deptIdList) { + return R.ok(userService.listUserIdByDeptIds(CollUtil.toList(deptIdList))); + } - /** - * 根据用户名获取用户列表 - * @param username 用户名 - * @return 用户列表 - */ - @GetMapping("/getUserListByUserName") - public R> getUserListByUserName(String username) { - return R.ok(TenantBroker - .noneAs(() -> userService.list(Wrappers.lambdaQuery().like(SysUser::getUsername, username)))); - } + /** + * 根据用户名获取用户列表 + * + * @param username 用户名 + * @return 用户列表 + */ + @GetMapping("/getUserListByUserName") + public R> getUserListByUserName(String username) { + return R.ok(TenantBroker + .noneAs(() -> userService.list(Wrappers.lambdaQuery().like(SysUser::getUsername, username)))); + } - /** - * 根据IDS获取用户列表 - * @param userIds ID列表 - * @return 用户列表 - */ - @GetMapping("/list") - public R> getUserListByIds(@RequestParam("userIds") List userIds) { - return R.ok(TenantBroker - .noneAs(() -> userService.list(Wrappers.lambdaQuery().in(SysUser::getUserId, userIds)))); - } + /** + * 根据IDS获取用户列表 + * + * @param userIds ID列表 + * @return 用户列表 + */ + @GetMapping("/list") + public R> getUserListByIds(@RequestParam("userIds") List userIds) { + return R.ok(TenantBroker + .noneAs(() -> userService.list(Wrappers.lambdaQuery().in(SysUser::getUserId, userIds)))); + } + + /** + * 获取当前用户的部门列表 + * + * @return 部门列表 + */ + @GetMapping("/personal/dept") + @Operation(summary = "获取当前用户的部门列表", description = "返回当前登录用户所属的所有部门") + public R> getUserDeptList() { + return R.ok(userService.getUserDeptList()); + } + + /** + * 切换当前用户的部门 + * + * @param userDTO 部门ID + * @return 操作结果 + */ + @PutMapping("/personal/dept/update") + @Operation(summary = "切换当前部门", description = "切换当前用户的工作部门") + public R updateUserDept(@RequestBody UserDTO userDTO) { + return userService.updateUserDept(userDTO.getDeptId()); + } } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysUserService.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysUserService.java index bfc61d1ba..876cccd5a 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysUserService.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysUserService.java @@ -25,6 +25,7 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.pig4cloud.pigx.admin.api.dto.RegisterUserDTO; import com.pig4cloud.pigx.admin.api.dto.UserDTO; import com.pig4cloud.pigx.admin.api.dto.UserInfo; +import com.pig4cloud.pigx.admin.api.entity.SysDept; import com.pig4cloud.pigx.admin.api.entity.SysUser; import com.pig4cloud.pigx.admin.api.vo.UserExcelVO; import com.pig4cloud.pigx.admin.api.vo.UserVO; @@ -39,142 +40,176 @@ import java.util.List; */ public interface SysUserService extends IService { - /** - * 查询用户信息 - * @param userDTO 用户数据传输对象 - * @return 用户信息对象 - */ - R getUserInfo(UserDTO userDTO); + /** + * 查询用户信息 + * + * @param userDTO 用户数据传输对象 + * @return 用户信息对象 + */ + R getUserInfo(UserDTO userDTO); - /** - * 分页查询用户信息(含有角色信息) - * @param page 分页对象 - * @param userDTO 参数列表 - * @return - */ - IPage getUsersWithRolePage(Page page, UserDTO userDTO); + /** + * 分页查询用户信息(含有角色信息) + * + * @param page 分页对象 + * @param userDTO 参数列表 + * @return + */ + IPage getUsersWithRolePage(Page page, UserDTO userDTO); - /** - * 删除用户 - * @param ids 用户 - * @return boolean - */ - Boolean deleteUserByIds(Long[] ids); + /** + * 删除用户 + * + * @param ids 用户 + * @return boolean + */ + Boolean deleteUserByIds(Long[] ids); - /** - * 更新当前用户基本信息 - * @param userDto 用户信息 - * @return Boolean - */ - R updateUserInfo(UserDTO userDto); + /** + * 更新当前用户基本信息 + * + * @param userDto 用户信息 + * @return Boolean + */ + R updateUserInfo(UserDTO userDto); - /** - * 更新指定用户信息 - * @param userDto 用户信息DTO对象 - * @return 更新操作是否成功 - */ - Boolean updateUser(UserDTO userDto); + /** + * 更新指定用户信息 + * + * @param userDto 用户信息DTO对象 + * @return 更新操作是否成功 + */ + Boolean updateUser(UserDTO userDto); - /** - * 通过ID查询用户信息 - * @param id 用户ID - * @return 用户信息 - */ - UserVO selectUserVoById(Long id); + /** + * 通过ID查询用户信息 + * + * @param id 用户ID + * @return 用户信息 + */ + UserVO selectUserVoById(Long id); - /** - * 查询上级部门的用户信息 - * @param username 用户名 - * @return R - */ - List listAncestorUsers(String username); + /** + * 查询上级部门的用户信息 + * + * @param username 用户名 + * @return R + */ + List listAncestorUsers(String username); - /** - * 保存用户信息 - * @param userDto DTO 对象 - * @return success/fail - */ - Boolean saveUser(UserDTO userDto); + /** + * 保存用户信息 + * + * @param userDto DTO 对象 + * @return success/fail + */ + Boolean saveUser(UserDTO userDto); - /** - * 查询全部的用户 - * @param userDTO 查询条件 - * @param ids 目标列表 - * @return list - */ - List listUser(UserDTO userDTO, Long[] ids); + /** + * 查询全部的用户 + * + * @param userDTO 查询条件 + * @param ids 目标列表 + * @return list + */ + List listUser(UserDTO userDTO, Long[] ids); - /** - * excel 导入用户 - * @param excelVOList excel 列表数据 - * @param bindingResult 错误数据 - * @return ok fail - */ - R importUser(List excelVOList, BindingResult bindingResult); + /** + * excel 导入用户 + * + * @param excelVOList excel 列表数据 + * @param bindingResult 错误数据 + * @return ok fail + */ + R importUser(List excelVOList, BindingResult bindingResult); - /** - * 注册用户 - * @param userDto 用户信息 - * @return success/false - */ - R registerUser(RegisterUserDTO userDto); + /** + * 注册用户 + * + * @param userDto 用户信息 + * @return success/false + */ + R registerUser(RegisterUserDTO userDto); - /** - * 锁定用户 - * @param username 用户名 - * @return R - */ - R lockUser(String username); + /** + * 锁定用户 + * + * @param username 用户名 + * @return R + */ + R lockUser(String username); - /** - * 修改用户密码 - * @param userDto 包含用户信息的DTO对象 - * @return 操作结果 - */ - R changePassword(UserDTO userDto); + /** + * 修改用户密码 + * + * @param userDto 包含用户信息的DTO对象 + * @return 操作结果 + */ + R changePassword(UserDTO userDto); - /** - * 解绑社交登录 - * @param type 社交登录类型 - * @return R - */ - R unbinding(String type); + /** + * 解绑社交登录 + * + * @param type 社交登录类型 + * @return R + */ + R unbinding(String type); - /** - * 校验密码 - * @param username 用户名 - * @param password 密码 - * @return R - */ - R checkPassword(String username, String password); + /** + * 校验密码 + * + * @param username 用户名 + * @param password 密码 + * @return R + */ + R checkPassword(String username, String password); - /** - * 根据角色ID列表获取用户ID列表接口 - * @param roleIdList 角色ID列表 - * @return List 返回结果对象,包含根据角色ID列表获取到的用户ID列表信息 - */ - List listUserIdByRoleIds(List roleIdList); + /** + * 根据角色ID列表获取用户ID列表接口 + * + * @param roleIdList 角色ID列表 + * @return List 返回结果对象,包含根据角色ID列表获取到的用户ID列表信息 + */ + List listUserIdByRoleIds(List roleIdList); - /** - * 根据部门ID列表获取用户ID列表接口 - * @param deptIdList 部门ID列表 - * @return List 返回结果对象,包含根据部门ID列表获取到的用户ID列表信息 - */ - List listUserIdByDeptIds(List deptIdList); + /** + * 根据部门ID列表获取用户ID列表接口 + * + * @param deptIdList 部门ID列表 + * @return List 返回结果对象,包含根据部门ID列表获取到的用户ID列表信息 + */ + List listUserIdByDeptIds(List deptIdList); - /** - * 重置用户密码 - * @param userDto 用户信息DTO - * @return 操作结果,包含是否成功的布尔值 - */ - R resetUserPassword(RegisterUserDTO userDto); + /** + * 重置用户密码 + * + * @param userDto 用户信息DTO + * @return 操作结果,包含是否成功的布尔值 + */ + R resetUserPassword(RegisterUserDTO userDto); - /** - * 找回用户密码 - * @param userDto 用户信息DTO - * @param code 验证码 - * @return 操作结果,包含是否成功的布尔值 - */ - R forgetUserPassword(RegisterUserDTO userDto, String code); + /** + * 找回用户密码 + * + * @param userDto 用户信息DTO + * @param code 验证码 + * @return 操作结果,包含是否成功的布尔值 + */ + R forgetUserPassword(RegisterUserDTO userDto, String code); + + /** + * 获取当前用户的部门列表 + * + * @return 部门列表 + */ + List getUserDeptList(); + + /** + * 切换用户当前部门 + * + * @param deptId 部门ID + * @return 操作结果 + */ + R updateUserDept(Long deptId); } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysUserServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysUserServiceImpl.java index 77c9f8637..2a1f7b43c 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysUserServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysUserServiceImpl.java @@ -76,640 +76,729 @@ import java.util.stream.Collectors; @AllArgsConstructor public class SysUserServiceImpl extends ServiceImpl implements SysUserService { - private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder(); - - private final SysMenuService sysMenuService; - - private final SysRoleService sysRoleService; - - private final SysPostService sysPostService; - - private final SysDeptService sysDeptService; - - private final SysTenantService sysTenantService; - - private final SysUserRoleMapper sysUserRoleMapper; - - private final SysUserPostMapper sysUserPostMapper; - - private final SysUserDeptMapper sysUserDeptMapper; - - private final SysTenantUserMapper sysTenantUserMapper; - - private final CacheManager cacheManager; - - private final SysDeptMapper sysDeptMapper; - - /** - * 保存用户信息 - * @param userDto DTO 对象 - * @return success/fail - */ - @Override - @Transactional(rollbackFor = Exception.class) - public Boolean saveUser(UserDTO userDto) { - SysUser sysUser = new SysUser(); - BeanUtils.copyProperties(userDto, sysUser); - sysUser.setLockFlag( - StrUtil.isBlank(userDto.getLockFlag()) ? UserStateEnum.NORMAL.getCode() : userDto.getLockFlag()); - sysUser.setCreateBy(userDto.getUsername()); - sysUser.setUpdateBy(userDto.getUsername()); - sysUser.setPassword(ENCODER.encode(userDto.getPassword())); - sysUser.setPasswordModifyTime(LocalDateTime.now()); - baseMapper.insert(sysUser); - // 保存用户岗位信息 - Optional.ofNullable(userDto.getPost()).ifPresent(posts -> { - posts.stream().map(postId -> { - SysUserPost userPost = new SysUserPost(); - userPost.setUserId(sysUser.getUserId()); - userPost.setPostId(postId); - return userPost; - }).forEach(sysUserPostMapper::insert); - }); - - // 如果角色为空,赋默认角色 - if (CollUtil.isEmpty(userDto.getRole())) { - // 获取默认角色编码 - String defaultRole = ParamResolver.getStr("USER_DEFAULT_ROLE"); - // 默认角色 - SysRole sysRole = sysRoleService - .getOne(Wrappers.lambdaQuery().eq(SysRole::getRoleCode, defaultRole)); - userDto.setRole(Collections.singletonList(sysRole.getRoleId())); - } - - // 插入用户角色关系表 - userDto.getRole().stream().map(roleId -> { - SysUserRole userRole = new SysUserRole(); - userRole.setUserId(sysUser.getUserId()); - userRole.setRoleId(roleId); - return userRole; - }).forEach(sysUserRoleMapper::insert); - - // 插入用户部门信息 - if (Objects.nonNull(userDto.getDeptId())) { - SysUserDept sysUserDept = new SysUserDept(); - sysUserDept.setUserId(sysUser.getUserId()); - sysUserDept.setDeptId(userDto.getDeptId()); - sysUserDeptMapper.insert(sysUserDept); - } - - // 插入用户租户关系表 - SysTenantUser sysTenantUser = new SysTenantUser(); - sysTenantUser.setUserId(sysUser.getUserId()); - sysTenantUser.setTenantId(Objects.nonNull(SecurityUtils.getUser()) ? SecurityUtils.getUser().getTenantId() - : userDto.getTenantId()); - sysTenantUserMapper.insert(sysTenantUser); - return Boolean.TRUE; - } - - /** - * 根据用户信息查询用户全部信息,包括角色和权限 - * @param userDTO 用户信息DTO - * @return 包含用户角色和权限的用户信息对象 - */ - @Override - public R getUserInfo(UserDTO userDTO) { - UserVO dbUser = baseMapper.getUserVo(userDTO); - - if (dbUser == null) { - return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_USER_USERINFO_EMPTY, userDTO.getUsername())); - } - - // 设置角色列表 (ID) - UserInfo userInfo = new UserInfo(); - BeanUtils.copyProperties(dbUser, userInfo); - // 设置权限列表(menu.permission) - List permissions = dbUser.getRoleList() - .stream() - .map(SysRole::getRoleId) - .flatMap(roleId -> sysMenuService.findMenuByRoleId(roleId).stream()) - .map(SysMenu::getPermission) - .filter(StrUtil::isNotEmpty) - .toList(); - userInfo.setPermissions(permissions); - - // 如果是登录成功以后查询租户信息并进行校验 - if (Objects.nonNull(SecurityUtils.getUser())) { - Long updateTenantId = sysTenantService.getOrUpdateTenant(); - if (Objects.nonNull(updateTenantId)) { - userInfo.setTenantId(updateTenantId); - } - } - - return R.ok(userInfo); - } - - /** - * 分页查询用户信息(含有角色信息) - * @param page 分页对象 - * @param userDTO 参数列表 - * @return - */ - @Override - public IPage getUsersWithRolePage(Page page, UserDTO userDTO) { - userDTO.setTenantId(SecurityUtils.getUser().getTenantId()); - return baseMapper.getUserVosPage(page, userDTO, DataScope.of()); - } - - /** - * 通过ID查询用户信息 - * @param id 用户ID - * @return 用户信息 - */ - @Override - public UserVO selectUserVoById(Long id) { - return baseMapper.getUserVoById(id); - } - - /** - * 删除用户 - * @param ids 用户ID 列表 - * @return Boolean - */ - @Override - @Transactional(rollbackFor = Exception.class) - public Boolean deleteUserByIds(Long[] ids) { - // 删除 spring cache - List userList = baseMapper.selectByIds(CollUtil.toList(ids)); - Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS); - for (SysUser sysUser : userList) { - cache.evict(sysUser.getUsername()); - } - - // 删除用户角色 - sysUserRoleMapper.delete(Wrappers.lambdaQuery().in(SysUserRole::getUserId, CollUtil.toList(ids))); - sysUserDeptMapper.delete(Wrappers.lambdaQuery().in(SysUserDept::getUserId, CollUtil.toList(ids))); - sysUserPostMapper.delete(Wrappers.lambdaQuery().in(SysUserPost::getUserId, CollUtil.toList(ids))); - sysTenantUserMapper.delete(Wrappers.lambdaQuery() - .in(SysTenantUser::getUserId, CollUtil.toList(ids)) - .eq(SysTenantUser::getTenantId, SecurityUtils.getUser().getTenantId())); - this.removeBatchByIds(CollUtil.toList(ids)); - return Boolean.TRUE; - } - - @Override - @CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username") - public R updateUserInfo(UserDTO userDto) { - Long userId = SecurityUtils.getUser().getId(); - SysUser sysUser = new SysUser(); - sysUser.setPhone(userDto.getPhone()); - sysUser.setUserId(userId); - sysUser.setAvatar(userDto.getAvatar()); - sysUser.setNickname(userDto.getNickname()); - sysUser.setName(userDto.getName()); - sysUser.setEmail(userDto.getEmail()); - return R.ok(this.updateById(sysUser)); - } - - @Override - @Transactional(rollbackFor = Exception.class) - @Audit(name = "用户更新", spel = "@sysUserMapper.selectById(#userDto.userId)") - @CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username") - public Boolean updateUser(UserDTO userDto) { - // 更新用户表信息 - SysUser sysUser = new SysUser(); - BeanUtils.copyProperties(userDto, sysUser); - sysUser.setUpdateTime(LocalDateTime.now()); - - if (StrUtil.isNotBlank(userDto.getPassword())) { - sysUser.setPassword(ENCODER.encode(userDto.getPassword())); - } - - this.updateById(sysUser); - - // 更新用户角色表 - if (userDto.getRole() != null) { - sysUserRoleMapper - .delete(Wrappers.lambdaQuery().eq(SysUserRole::getUserId, userDto.getUserId())); - userDto.getRole().stream().map(roleId -> { - SysUserRole userRole = new SysUserRole(); - userRole.setUserId(sysUser.getUserId()); - userRole.setRoleId(roleId); - return userRole; - }).forEach(SysUserRole::insert); - } - - // 更新用户岗位表 - if (userDto.getPost() != null) { - sysUserPostMapper - .delete(Wrappers.lambdaQuery().eq(SysUserPost::getUserId, userDto.getUserId())); - userDto.getPost().stream().map(postId -> { - SysUserPost userPost = new SysUserPost(); - userPost.setUserId(sysUser.getUserId()); - userPost.setPostId(postId); - return userPost; - }).forEach(SysUserPost::insert); - } - - // 更新用户部门表 - if (Objects.nonNull(userDto.getDeptId())) { - sysUserDeptMapper - .delete(Wrappers.lambdaQuery().eq(SysUserDept::getUserId, userDto.getUserId())); - SysUserDept sysUserDept = new SysUserDept(); - sysUserDept.setUserId(sysUser.getUserId()); - sysUserDept.setDeptId(userDto.getDeptId()); - sysUserDept.insert(); - } - - return Boolean.TRUE; - } - - /** - * 查询上级部门的用户信息 - * @param username 用户名 - * @return R - */ - @Override - public List listAncestorUsers(String username) { - SysUser sysUser = this.getOne(Wrappers.query().lambda().eq(SysUser::getUsername, username)); - - // 查询所属部门 - List sysDeptList = sysDeptMapper.listDeptsByUserId(sysUser.getUserId(), sysUser.getTenantId()); - if (CollUtil.isEmpty(sysDeptList)) { - return null; - } - - // 查询用户部门ID - List parentDeptList = sysDeptList.stream().map(SysDept::getParentId).toList(); - List userIdList = sysUserDeptMapper - .selectList(Wrappers.lambdaQuery().in(SysUserDept::getDeptId, parentDeptList)) - .stream() - .map(SysUserDept::getUserId) - .toList(); - return this.list(Wrappers.lambdaQuery().in(SysUser::getUserId, userIdList)); - } - - /** - * 查询全部的用户 - * @param userDTO 查询条件 - * @param ids ids 用户列表 - * @return list - */ - @Override - public List listUser(UserDTO userDTO, Long[] ids) { - // 根据数据权限查询全部的用户信息 - userDTO.setTenantId(SecurityUtils.getUser().getTenantId()); - List voList = baseMapper.getUserVoListByScope(userDTO, ids, DataScope.of()); - return voList.stream().map(userVO -> { - UserExcelVO excelVO = new UserExcelVO(); - BeanUtils.copyProperties(userVO, excelVO); - // 转换角色名称 - String roleNameList = userVO.getRoleList() - .stream() - .map(SysRole::getRoleName) - .collect(Collectors.joining(StrUtil.COMMA)); - excelVO.setRoleNameList(roleNameList); - // 转化岗位名称 - String postNameList = userVO.getPostList() - .stream() - .map(SysPost::getPostName) - .collect(Collectors.joining(StrUtil.COMMA)); - excelVO.setPostNameList(postNameList); - // 转化部门名称 - String deptNameList = userVO.getDeptList() - .stream() - .map(SysDept::getName) - .collect(Collectors.joining(StrUtil.COMMA)); - excelVO.setDeptNameList(deptNameList); - return excelVO; - }).toList(); - } - - /** - * excel 导入用户, 插入正确的 错误的提示行号 - * @param excelVOList excel 列表数据 - * @param bindingResult 错误数据 - * @return ok fail - */ - @Override - public R importUser(List excelVOList, BindingResult bindingResult) { - // 通用校验获取失败的数据 - List errorMessageList = (List) bindingResult.getTarget(); - List deptList = sysDeptService.list(); - List roleList = sysRoleService.list(); - List postList = sysPostService.list(); - - // 执行数据插入操作 组装 UserDto - for (UserExcelVO excel : excelVOList) { - // 个性化校验逻辑 - List userList = TenantBroker.noneAs(this::list); - - Set errorMsg = new HashSet<>(); - // 校验用户名是否存在 - boolean exsitUserName = userList.stream() - .anyMatch(sysUser -> excel.getUsername().equals(sysUser.getUsername())); - - if (exsitUserName) { - errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_USER_USERNAME_EXISTING, excel.getUsername())); - } - - // 校验手机号是否存在 - boolean exsitPhone = userList.stream().anyMatch(sysUser -> excel.getPhone().equals(sysUser.getPhone())); - if (exsitPhone) { - errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_USER_PHONE_EXISTING, excel.getPhone())); - } - - // 判断输入的部门名称列表是否合法 - Optional deptOptional = deptList.stream() - .filter(dept -> excel.getDeptNameList().equals(dept.getName())) - .findFirst(); - if (deptOptional.isEmpty()) { - errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_DEPT_DEPTNAME_INEXISTENCE, excel.getDeptNameList())); - } - - // 判断输入的角色名称列表是否合法 - List roleNameList = StrUtil.split(excel.getRoleNameList(), StrUtil.COMMA); - List roleCollList = roleList.stream() - .filter(role -> roleNameList.stream().anyMatch(name -> role.getRoleName().equals(name))) - .toList(); - - if (roleCollList.size() != roleNameList.size()) { - errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_ROLE_ROLENAME_INEXISTENCE, excel.getRoleNameList())); - } - - // 判断输入的部门名称列表是否合法 - List postNameList = StrUtil.split(excel.getPostNameList(), StrUtil.COMMA); - List postCollList = postList.stream() - .filter(post -> postNameList.stream().anyMatch(name -> post.getPostName().equals(name))) - .toList(); - - if (postCollList.size() != postNameList.size()) { - errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_POST_POSTNAME_INEXISTENCE, excel.getPostNameList())); - } - - // 数据合法情况 - if (CollUtil.isEmpty(errorMsg)) { - insertExcelUser(excel, deptOptional, roleCollList, postCollList); - } - else { - // 数据不合法情况 - errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg)); - } - - } - - if (CollUtil.isNotEmpty(errorMessageList)) { - return R.failed(errorMessageList); - } - return R.ok(null, MsgUtils.getMessage(ErrorCodes.SYS_USER_IMPORT_SUCCEED)); - } - - /** - * 插入excel User - */ - private void insertExcelUser(UserExcelVO excel, Optional deptOptional, List roleCollList, - List postCollList) { - UserDTO userDTO = new UserDTO(); - userDTO.setUsername(excel.getUsername()); - userDTO.setPhone(excel.getPhone()); - userDTO.setNickname(excel.getNickname()); - userDTO.setName(excel.getName()); - userDTO.setEmail(excel.getEmail()); - userDTO.setLockFlag(excel.getLockFlag()); - // 批量导入初始密码为手机号 - userDTO.setPasswordModifyTime(LocalDateTime.now()); - userDTO.setPassword(userDTO.getPhone()); - // 根据部门名称查询部门ID - userDTO.setDeptId(deptOptional.get().getDeptId()); - // 插入岗位名称 - List postIdList = postCollList.stream().map(SysPost::getPostId).toList(); - userDTO.setPost(postIdList); - // 根据角色名称查询角色ID - List roleIdList = roleCollList.stream().map(SysRole::getRoleId).toList(); - userDTO.setRole(roleIdList); - // 插入用户 - this.saveUser(userDTO); - } - - /** - * 注册用户 赋予用户默认角色 - * @param userDto 用户信息 - * @return success/false - */ - @Override - @Transactional(rollbackFor = Exception.class) - public R registerUser(RegisterUserDTO userDto) { - // 判断用户名是否存在 - boolean usernameExists = TenantBroker - .noneAs(() -> this.exists(Wrappers.lambdaQuery().eq(SysUser::getUsername, userDto.getUsername()))); - if (usernameExists) { - String message = MsgUtils.getMessage(ErrorCodes.SYS_USER_USERNAME_EXISTING, userDto.getUsername()); - return R.failed(message); - } - - // 判断手机号是否存在 - boolean phoneExists = TenantBroker - .noneAs(() -> this.exists(Wrappers.lambdaQuery().eq(SysUser::getPhone, userDto.getPhone()))); - if (phoneExists) { - String message = MsgUtils.getMessage(ErrorCodes.SYS_USER_PHONE_EXISTING, userDto.getPhone()); - return R.failed(message); - } - - // 单独的用户避免越权 - UserDTO user = new UserDTO(); - BeanUtils.copyProperties(userDto, user); - return R.ok(saveUser(user)); - } - - /** - * 锁定用户 - * @param username 用户名 - * @return 操作结果,包含是否成功 - */ - @Override - @CacheEvict(value = CacheConstants.USER_DETAILS, key = "#username") - public R lockUser(String username) { - SysUser sysUser = baseMapper.selectOne(Wrappers.lambdaQuery().eq(SysUser::getUsername, username)); - - if (Objects.nonNull(sysUser)) { - sysUser.setLockFlag(UserStateEnum.LOCK.getCode()); - baseMapper.updateById(sysUser); - } - return R.ok(); - } - - /** - * 修改用户密码 - * @param userDto 用户数据传输对象,包含原密码和新密码 - * @return 操作结果 - * @CacheEvict 清除用户详情缓存 - */ - @Override - @CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username") - public R changePassword(UserDTO userDto) { - SysUser user = baseMapper.selectById(SecurityUtils.getUser().getId()); - if (StrUtil.isEmpty(userDto.getPassword())) { - return R.failed("原密码不能为空"); - } - - if (!ENCODER.matches(userDto.getPassword(), user.getPassword())) { - log.info("原密码错误,修改个人信息失败:{}", userDto.getUsername()); - return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_USER_UPDATE_PASSWORDERROR)); - } - - if (StrUtil.isEmpty(userDto.getNewpassword1())) { - return R.failed("新密码不能为空"); - } - String password = ENCODER.encode(userDto.getNewpassword1()); - - this.update(Wrappers.lambdaUpdate() - .set(SysUser::getPassword, password) - .set(SysUser::getPasswordModifyTime, LocalDateTime.now()) - .eq(SysUser::getUserId, user.getUserId())); - return R.ok(); - } - - /** - * 解绑用户第三方登录账号 - * @param type 登录类型,参见LoginTypeEnum枚举 - * @return 操作结果,成功返回R.ok(),失败返回R.failed() - */ - @Override - public R unbinding(String type) { - PigxUser user = SecurityUtils.getUser(); - LambdaUpdateWrapper wrapper = null; - - // 微信开放平台 (普通用户扫码登录) - if (type.equals(LoginTypeEnum.WECHAT.getType())) { - wrapper = Wrappers.lambdaUpdate() - .set(SysUser::getWxOpenid, null) - .eq(SysUser::getUserId, user.getId()); - // 码云登录 (方便申请) - } - else if (type.equals(LoginTypeEnum.GITEE.getType())) { - wrapper = Wrappers.lambdaUpdate() - .set(SysUser::getGiteeLogin, null) - .eq(SysUser::getUserId, user.getId()); - // 企业微信登录 - } - else if (type.equals(LoginTypeEnum.WEIXIN_CP.getType())) { - wrapper = Wrappers.lambdaUpdate() - .set(SysUser::getWxCpUserid, null) - .eq(SysUser::getUserId, user.getId()); - // 钉钉登录 - } - else if (type.equals(LoginTypeEnum.DINGTALK.getType())) { - wrapper = Wrappers.lambdaUpdate() - .set(SysUser::getWxDingUserid, null) - .eq(SysUser::getUserId, user.getId()); - } - - if (Objects.isNull(wrapper)) { - return R.failed("解绑账号类型不存在"); - } - this.update(wrapper); - return R.ok(); - } - - /** - * 检查用户密码是否正确 - * @param username 用户名 - * @param password 待验证的密码 - * @return 验证结果,成功返回R.ok(),失败返回R.failed() - */ - @Override - public R checkPassword(String username, String password) { - SysUser condition = new SysUser(); - condition.setUsername(username); - SysUser sysUser = this.getOne(new QueryWrapper<>(condition)); - - if (!ENCODER.matches(password, sysUser.getPassword())) { - log.info("原密码错误"); - return R.failed("密码输入错误"); - } - else { - return R.ok(); - } - } - - /** - * 重置用户密码 - * @param userDto 用户信息DTO,包含用户名、原密码和新密码等信息 - * @return 返回操作结果,成功返回true,失败返回错误信息 - */ - @Override - @CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username") - public R resetUserPassword(RegisterUserDTO userDto) { - // 校验密码 - R checkedPassword = checkPassword(userDto.getUsername(), userDto.getPassword()); - if (!checkedPassword.isOk()) { - return checkedPassword; - } - - // 新密码校验 - if (StrUtil.equals(userDto.getPassword(), userDto.getNewpassword1())) { - return R.failed("新旧密码不能相同"); - } - - // 重置密码 - String password = ENCODER.encode(userDto.getNewpassword1()); - this.update(Wrappers.lambdaUpdate() - .set(SysUser::getPassword, password) - .set(SysUser::getPasswordModifyTime, LocalDateTime.now()) - .set(SysUser::getPasswordExpireFlag, CommonConstants.STATUS_NORMAL) - .eq(SysUser::getUsername, userDto.getUsername())); - return R.ok(); - } - - /** - * 忘记用户密码 - * @param userDto 用户信息DTO - * @param code 验证码 - * @return 操作结果,成功返回true - */ - @Override - public R forgetUserPassword(RegisterUserDTO userDto, String code) { - if (StrUtil.isBlank(userDto.getPhone())) { - return R.failed("非法参数"); - } - - String codeObj = RedisUtils - .get(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.SMS.getType() + StringPool.AT + userDto.getPhone()); - if (!StrUtil.equals(codeObj, code)) { - return R.failed("验证码错误"); - } - - TenantBroker.noneAs(() -> { - String username = lambdaQuery().select(SysUser::getUsername) - .eq(SysUser::getPhone, userDto.getPhone()) - .one() - .getUsername(); - - // 重置密码 - String password = ENCODER.encode(userDto.getNewpassword1()); - this.update(Wrappers.lambdaUpdate() - .set(SysUser::getPassword, password) - .set(SysUser::getPasswordModifyTime, LocalDateTime.now()) - .set(SysUser::getPasswordExpireFlag, CommonConstants.STATUS_NORMAL) - .eq(SysUser::getPhone, userDto.getPhone())); - cacheManager.getCache(CacheConstants.USER_DETAILS).evict(username); - return username; - }); - - return R.ok(); - } - - @Override - public List listUserIdByRoleIds(List roleIdList) { - return sysUserRoleMapper.selectList(Wrappers.lambdaQuery().in(SysUserRole::getRoleId, roleIdList)) - .stream() - .map(SysUserRole::getUserId) - .toList(); - } - - /** - * 根据部门ID列表获取用户ID列表接口 - * @param deptIdList 部门ID列表 - * @return List 返回结果对象,包含根据部门ID列表获取到的用户ID列表信息 - */ - @Override - public List listUserIdByDeptIds(List deptIdList) { - List userList = sysUserDeptMapper - .selectList(Wrappers.lambdaQuery().in(SysUserDept::getDeptId, deptIdList)) - .stream() - .map(SysUserDept::getUserId) - .toList(); - return baseMapper.selectList(Wrappers.lambdaQuery().in(SysUser::getUserId, userList)); - } + private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder(); + + private final SysMenuService sysMenuService; + + private final SysRoleService sysRoleService; + + private final SysPostService sysPostService; + + private final SysDeptService sysDeptService; + + private final SysTenantService sysTenantService; + + private final SysUserRoleMapper sysUserRoleMapper; + + private final SysUserPostMapper sysUserPostMapper; + + private final SysUserDeptMapper sysUserDeptMapper; + + private final SysTenantUserMapper sysTenantUserMapper; + + private final CacheManager cacheManager; + + private final SysDeptMapper sysDeptMapper; + + /** + * 保存用户信息 + * + * @param userDto DTO 对象 + * @return success/fail + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean saveUser(UserDTO userDto) { + SysUser sysUser = new SysUser(); + BeanUtils.copyProperties(userDto, sysUser); + sysUser.setLockFlag( + StrUtil.isBlank(userDto.getLockFlag()) ? UserStateEnum.NORMAL.getCode() : userDto.getLockFlag()); + sysUser.setCreateBy(userDto.getUsername()); + sysUser.setUpdateBy(userDto.getUsername()); + sysUser.setPassword(ENCODER.encode(userDto.getPassword())); + sysUser.setPasswordModifyTime(LocalDateTime.now()); + baseMapper.insert(sysUser); + // 保存用户岗位信息 + Optional.ofNullable(userDto.getPost()).ifPresent(posts -> { + posts.stream().map(postId -> { + SysUserPost userPost = new SysUserPost(); + userPost.setUserId(sysUser.getUserId()); + userPost.setPostId(postId); + return userPost; + }).forEach(sysUserPostMapper::insert); + }); + + // 如果角色为空,赋默认角色 + if (CollUtil.isEmpty(userDto.getRole())) { + // 获取默认角色编码 + String defaultRole = ParamResolver.getStr("USER_DEFAULT_ROLE"); + // 默认角色 + SysRole sysRole = sysRoleService + .getOne(Wrappers.lambdaQuery().eq(SysRole::getRoleCode, defaultRole)); + userDto.setRole(Collections.singletonList(sysRole.getRoleId())); + } + + // 插入用户角色关系表 + userDto.getRole().stream().map(roleId -> { + SysUserRole userRole = new SysUserRole(); + userRole.setUserId(sysUser.getUserId()); + userRole.setRoleId(roleId); + return userRole; + }).forEach(sysUserRoleMapper::insert); + + // 插入用户部门信息 + List deptIdList = CollUtil.isNotEmpty(userDto.getDeptIds()) ? userDto.getDeptIds() + : (Objects.nonNull(userDto.getDeptId()) ? Collections.singletonList(userDto.getDeptId()) : null); + + if (CollUtil.isNotEmpty(deptIdList)) { + // 设置第一个部门为主部门 + sysUser.setDeptId(deptIdList.get(0)); + baseMapper.updateById(sysUser); + + // 插入用户部门关联关系 + deptIdList.forEach(deptId -> { + SysUserDept sysUserDept = new SysUserDept(); + sysUserDept.setUserId(sysUser.getUserId()); + sysUserDept.setDeptId(deptId); + sysUserDeptMapper.insert(sysUserDept); + }); + } + + // 插入用户租户关系表 + SysTenantUser sysTenantUser = new SysTenantUser(); + sysTenantUser.setUserId(sysUser.getUserId()); + sysTenantUser.setTenantId(Objects.nonNull(SecurityUtils.getUser()) ? SecurityUtils.getUser().getTenantId() + : userDto.getTenantId()); + sysTenantUserMapper.insert(sysTenantUser); + return Boolean.TRUE; + } + + /** + * 根据用户信息查询用户全部信息,包括角色和权限 + * + * @param userDTO 用户信息DTO + * @return 包含用户角色和权限的用户信息对象 + */ + @Override + public R getUserInfo(UserDTO userDTO) { + UserVO dbUser = baseMapper.getUserVo(userDTO); + + if (dbUser == null) { + return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_USER_USERINFO_EMPTY, userDTO.getUsername())); + } + + // 设置角色列表 (ID) + UserInfo userInfo = new UserInfo(); + BeanUtils.copyProperties(dbUser, userInfo); + // 设置权限列表(menu.permission) + List permissions = dbUser.getRoleList() + .stream() + .map(SysRole::getRoleId) + .flatMap(roleId -> sysMenuService.findMenuByRoleId(roleId).stream()) + .map(SysMenu::getPermission) + .filter(StrUtil::isNotEmpty) + .toList(); + userInfo.setPermissions(permissions); + + // 如果是登录成功以后查询租户信息并进行校验 + if (Objects.nonNull(SecurityUtils.getUser())) { + Long updateTenantId = sysTenantService.getOrUpdateTenant(); + if (Objects.nonNull(updateTenantId)) { + userInfo.setTenantId(updateTenantId); + } + } + + return R.ok(userInfo); + } + + /** + * 分页查询用户信息(含有角色信息) + * + * @param page 分页对象 + * @param userDTO 参数列表 + * @return + */ + @Override + public IPage getUsersWithRolePage(Page page, UserDTO userDTO) { + userDTO.setTenantId(SecurityUtils.getUser().getTenantId()); + return baseMapper.getUserVosPage(page, userDTO, DataScope.of()); + } + + /** + * 通过ID查询用户信息 + * + * @param id 用户ID + * @return 用户信息 + */ + @Override + public UserVO selectUserVoById(Long id) { + return baseMapper.getUserVoById(id); + } + + /** + * 删除用户 + * + * @param ids 用户ID 列表 + * @return Boolean + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteUserByIds(Long[] ids) { + // 删除 spring cache + List userList = baseMapper.selectByIds(CollUtil.toList(ids)); + Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS); + for (SysUser sysUser : userList) { + cache.evict(sysUser.getUsername()); + } + + // 删除用户角色 + sysUserRoleMapper.delete(Wrappers.lambdaQuery().in(SysUserRole::getUserId, CollUtil.toList(ids))); + sysUserDeptMapper.delete(Wrappers.lambdaQuery().in(SysUserDept::getUserId, CollUtil.toList(ids))); + sysUserPostMapper.delete(Wrappers.lambdaQuery().in(SysUserPost::getUserId, CollUtil.toList(ids))); + sysTenantUserMapper.delete(Wrappers.lambdaQuery() + .in(SysTenantUser::getUserId, CollUtil.toList(ids)) + .eq(SysTenantUser::getTenantId, SecurityUtils.getUser().getTenantId())); + this.removeBatchByIds(CollUtil.toList(ids)); + return Boolean.TRUE; + } + + @Override + @CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username") + public R updateUserInfo(UserDTO userDto) { + Long userId = SecurityUtils.getUser().getId(); + SysUser sysUser = new SysUser(); + sysUser.setPhone(userDto.getPhone()); + sysUser.setUserId(userId); + sysUser.setAvatar(userDto.getAvatar()); + sysUser.setNickname(userDto.getNickname()); + sysUser.setName(userDto.getName()); + sysUser.setEmail(userDto.getEmail()); + return R.ok(this.updateById(sysUser)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @Audit(name = "用户更新", spel = "@sysUserMapper.selectById(#userDto.userId)") + @CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username") + public Boolean updateUser(UserDTO userDto) { + // 更新用户表信息 + SysUser sysUser = new SysUser(); + BeanUtils.copyProperties(userDto, sysUser); + sysUser.setUpdateTime(LocalDateTime.now()); + + if (StrUtil.isNotBlank(userDto.getPassword())) { + sysUser.setPassword(ENCODER.encode(userDto.getPassword())); + } + + this.updateById(sysUser); + + // 更新用户角色表 + if (userDto.getRole() != null) { + sysUserRoleMapper + .delete(Wrappers.lambdaQuery().eq(SysUserRole::getUserId, userDto.getUserId())); + userDto.getRole().stream().map(roleId -> { + SysUserRole userRole = new SysUserRole(); + userRole.setUserId(sysUser.getUserId()); + userRole.setRoleId(roleId); + return userRole; + }).forEach(SysUserRole::insert); + } + + // 更新用户岗位表 + if (userDto.getPost() != null) { + sysUserPostMapper + .delete(Wrappers.lambdaQuery().eq(SysUserPost::getUserId, userDto.getUserId())); + userDto.getPost().stream().map(postId -> { + SysUserPost userPost = new SysUserPost(); + userPost.setUserId(sysUser.getUserId()); + userPost.setPostId(postId); + return userPost; + }).forEach(SysUserPost::insert); + } + + // 更新用户部门表 + List deptIdList = CollUtil.isNotEmpty(userDto.getDeptIds()) ? userDto.getDeptIds() + : (Objects.nonNull(userDto.getDeptId()) ? Collections.singletonList(userDto.getDeptId()) : null); + + if (CollUtil.isNotEmpty(deptIdList)) { + // 设置第一个部门为主部门 + sysUser.setDeptId(deptIdList.get(0)); + this.updateById(sysUser); + + // 删除原有部门关联关系 + sysUserDeptMapper + .delete(Wrappers.lambdaQuery().eq(SysUserDept::getUserId, userDto.getUserId())); + + // 插入新的部门关联关系 + deptIdList.forEach(deptId -> { + SysUserDept sysUserDept = new SysUserDept(); + sysUserDept.setUserId(sysUser.getUserId()); + sysUserDept.setDeptId(deptId); + sysUserDept.insert(); + }); + } + + return Boolean.TRUE; + } + + /** + * 查询上级部门的用户信息 + * + * @param username 用户名 + * @return R + */ + @Override + public List listAncestorUsers(String username) { + SysUser sysUser = this.getOne(Wrappers.query().lambda().eq(SysUser::getUsername, username)); + + // 查询所属部门 + List sysDeptList = sysDeptMapper.listDeptsByUserId(sysUser.getUserId(), sysUser.getTenantId()); + if (CollUtil.isEmpty(sysDeptList)) { + return null; + } + + // 查询用户部门ID + List parentDeptList = sysDeptList.stream().map(SysDept::getParentId).toList(); + List userIdList = sysUserDeptMapper + .selectList(Wrappers.lambdaQuery().in(SysUserDept::getDeptId, parentDeptList)) + .stream() + .map(SysUserDept::getUserId) + .toList(); + return this.list(Wrappers.lambdaQuery().in(SysUser::getUserId, userIdList)); + } + + /** + * 查询全部的用户 + * + * @param userDTO 查询条件 + * @param ids ids 用户列表 + * @return list + */ + @Override + public List listUser(UserDTO userDTO, Long[] ids) { + // 根据数据权限查询全部的用户信息 + userDTO.setTenantId(SecurityUtils.getUser().getTenantId()); + List voList = baseMapper.getUserVoListByScope(userDTO, ids, DataScope.of()); + return voList.stream().map(userVO -> { + UserExcelVO excelVO = new UserExcelVO(); + BeanUtils.copyProperties(userVO, excelVO); + // 转换角色名称 + String roleNameList = userVO.getRoleList() + .stream() + .map(SysRole::getRoleName) + .collect(Collectors.joining(StrUtil.COMMA)); + excelVO.setRoleNameList(roleNameList); + // 转化岗位名称 + String postNameList = userVO.getPostList() + .stream() + .map(SysPost::getPostName) + .collect(Collectors.joining(StrUtil.COMMA)); + excelVO.setPostNameList(postNameList); + // 转化部门名称 + String deptNameList = userVO.getDeptList() + .stream() + .map(SysDept::getName) + .collect(Collectors.joining(StrUtil.COMMA)); + excelVO.setDeptNameList(deptNameList); + return excelVO; + }).toList(); + } + + /** + * excel 导入用户, 插入正确的 错误的提示行号 + * + * @param excelVOList excel 列表数据 + * @param bindingResult 错误数据 + * @return ok fail + */ + @Override + public R importUser(List excelVOList, BindingResult bindingResult) { + // 通用校验获取失败的数据 + List errorMessageList = (List) bindingResult.getTarget(); + List deptList = sysDeptService.list(); + List roleList = sysRoleService.list(); + List postList = sysPostService.list(); + + // 执行数据插入操作 组装 UserDto + for (UserExcelVO excel : excelVOList) { + // 个性化校验逻辑 + List userList = TenantBroker.noneAs(this::list); + + Set errorMsg = new HashSet<>(); + // 校验用户名是否存在 + boolean exsitUserName = userList.stream() + .anyMatch(sysUser -> excel.getUsername().equals(sysUser.getUsername())); + + if (exsitUserName) { + errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_USER_USERNAME_EXISTING, excel.getUsername())); + } + + // 校验手机号是否存在 + boolean exsitPhone = userList.stream().anyMatch(sysUser -> excel.getPhone().equals(sysUser.getPhone())); + if (exsitPhone) { + errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_USER_PHONE_EXISTING, excel.getPhone())); + } + + // 判断输入的部门名称列表是否合法 + Optional deptOptional = deptList.stream() + .filter(dept -> excel.getDeptNameList().equals(dept.getName())) + .findFirst(); + if (deptOptional.isEmpty()) { + errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_DEPT_DEPTNAME_INEXISTENCE, excel.getDeptNameList())); + } + + // 判断输入的角色名称列表是否合法 + List roleNameList = StrUtil.split(excel.getRoleNameList(), StrUtil.COMMA); + List roleCollList = roleList.stream() + .filter(role -> roleNameList.stream().anyMatch(name -> role.getRoleName().equals(name))) + .toList(); + + if (roleCollList.size() != roleNameList.size()) { + errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_ROLE_ROLENAME_INEXISTENCE, excel.getRoleNameList())); + } + + // 判断输入的部门名称列表是否合法 + List postNameList = StrUtil.split(excel.getPostNameList(), StrUtil.COMMA); + List postCollList = postList.stream() + .filter(post -> postNameList.stream().anyMatch(name -> post.getPostName().equals(name))) + .toList(); + + if (postCollList.size() != postNameList.size()) { + errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_POST_POSTNAME_INEXISTENCE, excel.getPostNameList())); + } + + // 数据合法情况 + if (CollUtil.isEmpty(errorMsg)) { + insertExcelUser(excel, deptOptional, roleCollList, postCollList); + } else { + // 数据不合法情况 + errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg)); + } + + } + + if (CollUtil.isNotEmpty(errorMessageList)) { + return R.failed(errorMessageList); + } + return R.ok(null, MsgUtils.getMessage(ErrorCodes.SYS_USER_IMPORT_SUCCEED)); + } + + /** + * 插入excel User + */ + private void insertExcelUser(UserExcelVO excel, Optional deptOptional, List roleCollList, + List postCollList) { + UserDTO userDTO = new UserDTO(); + userDTO.setUsername(excel.getUsername()); + userDTO.setPhone(excel.getPhone()); + userDTO.setNickname(excel.getNickname()); + userDTO.setName(excel.getName()); + userDTO.setEmail(excel.getEmail()); + userDTO.setLockFlag(excel.getLockFlag()); + // 批量导入初始密码为手机号 + userDTO.setPasswordModifyTime(LocalDateTime.now()); + userDTO.setPassword(userDTO.getPhone()); + // 根据部门名称查询部门ID + userDTO.setDeptId(deptOptional.get().getDeptId()); + // 插入岗位名称 + List postIdList = postCollList.stream().map(SysPost::getPostId).toList(); + userDTO.setPost(postIdList); + // 根据角色名称查询角色ID + List roleIdList = roleCollList.stream().map(SysRole::getRoleId).toList(); + userDTO.setRole(roleIdList); + // 插入用户 + this.saveUser(userDTO); + } + + /** + * 注册用户 赋予用户默认角色 + * + * @param userDto 用户信息 + * @return success/false + */ + @Override + @Transactional(rollbackFor = Exception.class) + public R registerUser(RegisterUserDTO userDto) { + // 判断用户名是否存在 + boolean usernameExists = TenantBroker + .noneAs(() -> this.exists(Wrappers.lambdaQuery().eq(SysUser::getUsername, userDto.getUsername()))); + if (usernameExists) { + String message = MsgUtils.getMessage(ErrorCodes.SYS_USER_USERNAME_EXISTING, userDto.getUsername()); + return R.failed(message); + } + + // 判断手机号是否存在 + boolean phoneExists = TenantBroker + .noneAs(() -> this.exists(Wrappers.lambdaQuery().eq(SysUser::getPhone, userDto.getPhone()))); + if (phoneExists) { + String message = MsgUtils.getMessage(ErrorCodes.SYS_USER_PHONE_EXISTING, userDto.getPhone()); + return R.failed(message); + } + + // 单独的用户避免越权 + UserDTO user = new UserDTO(); + BeanUtils.copyProperties(userDto, user); + return R.ok(saveUser(user)); + } + + /** + * 锁定用户 + * + * @param username 用户名 + * @return 操作结果,包含是否成功 + */ + @Override + @CacheEvict(value = CacheConstants.USER_DETAILS, key = "#username") + public R lockUser(String username) { + SysUser sysUser = baseMapper.selectOne(Wrappers.lambdaQuery().eq(SysUser::getUsername, username)); + + if (Objects.nonNull(sysUser)) { + sysUser.setLockFlag(UserStateEnum.LOCK.getCode()); + baseMapper.updateById(sysUser); + } + return R.ok(); + } + + /** + * 修改用户密码 + * + * @param userDto 用户数据传输对象,包含原密码和新密码 + * @return 操作结果 + * @CacheEvict 清除用户详情缓存 + */ + @Override + @CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username") + public R changePassword(UserDTO userDto) { + SysUser user = baseMapper.selectById(SecurityUtils.getUser().getId()); + if (StrUtil.isEmpty(userDto.getPassword())) { + return R.failed("原密码不能为空"); + } + + if (!ENCODER.matches(userDto.getPassword(), user.getPassword())) { + log.info("原密码错误,修改个人信息失败:{}", userDto.getUsername()); + return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_USER_UPDATE_PASSWORDERROR)); + } + + if (StrUtil.isEmpty(userDto.getNewpassword1())) { + return R.failed("新密码不能为空"); + } + String password = ENCODER.encode(userDto.getNewpassword1()); + + this.update(Wrappers.lambdaUpdate() + .set(SysUser::getPassword, password) + .set(SysUser::getPasswordModifyTime, LocalDateTime.now()) + .eq(SysUser::getUserId, user.getUserId())); + return R.ok(); + } + + /** + * 解绑用户第三方登录账号 + * + * @param type 登录类型,参见LoginTypeEnum枚举 + * @return 操作结果,成功返回R.ok(),失败返回R.failed() + */ + @Override + public R unbinding(String type) { + PigxUser user = SecurityUtils.getUser(); + LambdaUpdateWrapper wrapper = null; + + // 微信开放平台 (普通用户扫码登录) + if (type.equals(LoginTypeEnum.WECHAT.getType())) { + wrapper = Wrappers.lambdaUpdate() + .set(SysUser::getWxOpenid, null) + .eq(SysUser::getUserId, user.getId()); + // 码云登录 (方便申请) + } else if (type.equals(LoginTypeEnum.GITEE.getType())) { + wrapper = Wrappers.lambdaUpdate() + .set(SysUser::getGiteeLogin, null) + .eq(SysUser::getUserId, user.getId()); + // 企业微信登录 + } else if (type.equals(LoginTypeEnum.WEIXIN_CP.getType())) { + wrapper = Wrappers.lambdaUpdate() + .set(SysUser::getWxCpUserid, null) + .eq(SysUser::getUserId, user.getId()); + // 钉钉登录 + } else if (type.equals(LoginTypeEnum.DINGTALK.getType())) { + wrapper = Wrappers.lambdaUpdate() + .set(SysUser::getWxDingUserid, null) + .eq(SysUser::getUserId, user.getId()); + } + + if (Objects.isNull(wrapper)) { + return R.failed("解绑账号类型不存在"); + } + this.update(wrapper); + return R.ok(); + } + + /** + * 检查用户密码是否正确 + * + * @param username 用户名 + * @param password 待验证的密码 + * @return 验证结果,成功返回R.ok(),失败返回R.failed() + */ + @Override + public R checkPassword(String username, String password) { + SysUser condition = new SysUser(); + condition.setUsername(username); + SysUser sysUser = this.getOne(new QueryWrapper<>(condition)); + + if (!ENCODER.matches(password, sysUser.getPassword())) { + log.info("原密码错误"); + return R.failed("密码输入错误"); + } else { + return R.ok(); + } + } + + /** + * 重置用户密码 + * + * @param userDto 用户信息DTO,包含用户名、原密码和新密码等信息 + * @return 返回操作结果,成功返回true,失败返回错误信息 + */ + @Override + @CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username") + public R resetUserPassword(RegisterUserDTO userDto) { + // 校验密码 + R checkedPassword = checkPassword(userDto.getUsername(), userDto.getPassword()); + if (!checkedPassword.isOk()) { + return checkedPassword; + } + + // 新密码校验 + if (StrUtil.equals(userDto.getPassword(), userDto.getNewpassword1())) { + return R.failed("新旧密码不能相同"); + } + + // 重置密码 + String password = ENCODER.encode(userDto.getNewpassword1()); + this.update(Wrappers.lambdaUpdate() + .set(SysUser::getPassword, password) + .set(SysUser::getPasswordModifyTime, LocalDateTime.now()) + .set(SysUser::getPasswordExpireFlag, CommonConstants.STATUS_NORMAL) + .eq(SysUser::getUsername, userDto.getUsername())); + return R.ok(); + } + + /** + * 忘记用户密码 + * + * @param userDto 用户信息DTO + * @param code 验证码 + * @return 操作结果,成功返回true + */ + @Override + public R forgetUserPassword(RegisterUserDTO userDto, String code) { + if (StrUtil.isBlank(userDto.getPhone())) { + return R.failed("非法参数"); + } + + String codeObj = RedisUtils + .get(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.SMS.getType() + StringPool.AT + userDto.getPhone()); + if (!StrUtil.equals(codeObj, code)) { + return R.failed("验证码错误"); + } + + TenantBroker.noneAs(() -> { + String username = lambdaQuery().select(SysUser::getUsername) + .eq(SysUser::getPhone, userDto.getPhone()) + .one() + .getUsername(); + + // 重置密码 + String password = ENCODER.encode(userDto.getNewpassword1()); + this.update(Wrappers.lambdaUpdate() + .set(SysUser::getPassword, password) + .set(SysUser::getPasswordModifyTime, LocalDateTime.now()) + .set(SysUser::getPasswordExpireFlag, CommonConstants.STATUS_NORMAL) + .eq(SysUser::getPhone, userDto.getPhone())); + cacheManager.getCache(CacheConstants.USER_DETAILS).evict(username); + return username; + }); + + return R.ok(); + } + + @Override + public List listUserIdByRoleIds(List roleIdList) { + return sysUserRoleMapper.selectList(Wrappers.lambdaQuery().in(SysUserRole::getRoleId, roleIdList)) + .stream() + .map(SysUserRole::getUserId) + .toList(); + } + + /** + * 根据部门ID列表获取用户ID列表接口 + * + * @param deptIdList 部门ID列表 + * @return List 返回结果对象,包含根据部门ID列表获取到的用户ID列表信息 + */ + @Override + public List listUserIdByDeptIds(List deptIdList) { + List userList = sysUserDeptMapper + .selectList(Wrappers.lambdaQuery().in(SysUserDept::getDeptId, deptIdList)) + .stream() + .map(SysUserDept::getUserId) + .toList(); + return baseMapper.selectList(Wrappers.lambdaQuery().in(SysUser::getUserId, userList)); + } + + /** + * 获取当前用户的部门列表 + * + * @return 部门列表,主部门排在第一位 + */ + @Override + public List getUserDeptList() { + Long userId = SecurityUtils.getUser().getId(); + + // 1. 查询用户的所有部门关联 + List userDepts = sysUserDeptMapper.selectList( + Wrappers.lambdaQuery() + .eq(SysUserDept::getUserId, userId) + ); + + if (CollUtil.isEmpty(userDepts)) { + return Collections.emptyList(); + } + + // 2. 提取部门ID列表 + List deptIds = userDepts.stream() + .map(SysUserDept::getDeptId) + .toList(); + + // 3. 查询部门详细信息 + return sysDeptService.list( + Wrappers.lambdaQuery() + .in(SysDept::getDeptId, deptIds) + ); + + } + + /** + * 切换用户当前部门 + * + * @param deptId 部门ID + * @return 操作结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public R updateUserDept(Long deptId) { + PigxUser user = SecurityUtils.getUser(); + Long userId = user.getId(); + String username = user.getUsername(); + + // 1. 直接更新用户表的主部门ID + this.update(Wrappers.lambdaUpdate() + .set(SysUser::getDeptId, deptId) + .eq(SysUser::getUserId, userId)); + + // 2. 清除用户缓存,触发权限重新计算 + cacheManager.getCache(CacheConstants.USER_DETAILS).evict(username); + + return R.ok(true, "部门切换成功"); + } } diff --git a/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysDeptMapper.xml b/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysDeptMapper.xml index 0ea564cea..10ddc04c0 100644 --- a/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysDeptMapper.xml +++ b/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysDeptMapper.xml @@ -30,5 +30,6 @@ AND sys_dept.del_flag = '0' and sys_user_dept.user_id = #{userId} and sys_user_dept.tenant_id = #{tenantId} + ORDER BY sys_user_dept.is_primary DESC diff --git a/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysUserMapper.xml b/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysUserMapper.xml index fd323ae1b..49dab7241 100644 --- a/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysUserMapper.xml +++ b/pigx-upms/pigx-upms-biz/src/main/resources/mapper/SysUserMapper.xml @@ -26,6 +26,7 @@ + @@ -61,6 +62,7 @@ u.password_expire_flag, u.password_modify_time, u.tenant_id, + u.dept_id, u.create_by, u.create_time, u.update_time @@ -109,16 +111,18 @@ SELECT - , - ud.dept_id + FROM sys_user u - LEFT JOIN sys_user_dept ud ON u.user_id = ud.user_id + + + INNER JOIN sys_user_dept ud ON u.user_id = ud.user_id + AND ud.dept_id = #{query.deptId} + AND ud.tenant_id = #{query.tenantId} + WHERE u.del_flag = '0' - AND (ud.tenant_id = #{query.tenantId} OR ud.tenant_id IS NULL) + AND u.tenant_id = #{query.tenantId} AND u.username LIKE #{usernameLike} - - - AND ud.dept_id = #{query.deptId} -