From 6117c47d7cacc3b78a943a47280740484ecf94c6 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Thu, 30 Oct 2025 11:35:53 +0800 Subject: [PATCH 01/44] =?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} - From 74110199e26ce7af3375f5122d47eb941953b7a4 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Thu, 30 Oct 2025 12:31:24 +0800 Subject: [PATCH 02/44] =?UTF-8?q?fix(login):=20=E5=A2=9E=E5=8A=A0=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E5=8F=82=E6=95=B0=E7=A9=BA=E5=80=BC=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在OscChinaLoginHandler中增加identify空值校验 - 在WeChatCPLoginHandler中增加userid空值校验 - 在CasLoginHandler中增加username空值校验 - 在MiniAppLoginHandler中增加openId空值校验 - 在SmsLoginHandler中增加identify空值校验 - 在GiteeLoginHandler中增加identify空值校验 - 在WeChatLoginHandler中增加openId空值校验 - 在DingTalkLoginHandler中增加openId空值校验 --- .../pigx/admin/handler/CasLoginHandler.java | 64 +++++----- .../admin/handler/DingTalkLoginHandler.java | 6 + .../pigx/admin/handler/GiteeLoginHandler.java | 5 + .../admin/handler/MiniAppLoginHandler.java | 119 +++++++++--------- .../admin/handler/OscChinaLoginHandler.java | 9 +- .../pigx/admin/handler/SmsLoginHandler.java | 28 +++-- .../admin/handler/WeChatCPLoginHandler.java | 6 + .../admin/handler/WeChatLoginHandler.java | 106 ++++++++-------- 8 files changed, 196 insertions(+), 147 deletions(-) diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/CasLoginHandler.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/CasLoginHandler.java index 2975acd6f..23c9265fa 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/CasLoginHandler.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/CasLoginHandler.java @@ -17,6 +17,7 @@ package com.pig4cloud.pigx.admin.handler; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.pig4cloud.pigx.admin.api.dto.UserDTO; import com.pig4cloud.pigx.admin.api.dto.UserInfo; @@ -41,47 +42,54 @@ import org.springframework.stereotype.Component; @AllArgsConstructor public class CasLoginHandler extends AbstractLoginHandler { - private final SysSocialDetailsMapper sysSocialDetailsMapper; + private final SysSocialDetailsMapper sysSocialDetailsMapper; - private final SysUserService sysUserService; + private final SysUserService sysUserService; - /** - * cas 回到的ticket - *

- * 通过ticket 调用CAS获取唯一标识 - * @param ticket - * @return - */ - @Override - @SneakyThrows - public String identify(String ticket) { - SysSocialDetails condition = new SysSocialDetails(); - condition.setType(LoginTypeEnum.CAS.getType()); - SysSocialDetails socialDetails = sysSocialDetailsMapper.selectOne(new QueryWrapper<>(condition)); - // remark 字段填写 CAS 服务器的URL - Cas30ProxyTicketValidator cas30ProxyTicketValidator = new Cas30ProxyTicketValidator(socialDetails.getRemark()); - Assertion validate = cas30ProxyTicketValidator.validate(ticket, socialDetails.getRedirectUrl()); - return validate.getPrincipal().getName(); - } + /** + * cas 回到的ticket + *

+ * 通过ticket 调用CAS获取唯一标识 + * + * @param ticket + * @return + */ + @Override + @SneakyThrows + public String identify(String ticket) { + SysSocialDetails condition = new SysSocialDetails(); + condition.setType(LoginTypeEnum.CAS.getType()); + SysSocialDetails socialDetails = sysSocialDetailsMapper.selectOne(new QueryWrapper<>(condition)); + // remark 字段填写 CAS 服务器的URL + Cas30ProxyTicketValidator cas30ProxyTicketValidator = new Cas30ProxyTicketValidator(socialDetails.getRemark()); + Assertion validate = cas30ProxyTicketValidator.validate(ticket, socialDetails.getRedirectUrl()); + return validate.getPrincipal().getName(); + } - /** + /** * 根据用户名获取用户信息 + * * @param username 用户名 * @return 用户信息对象,如果用户不存在则返回null - */ - @Override - public UserInfo info(String username) { + */ + @Override + public UserInfo info(String username) { + if (StrUtil.isBlank(username)) { + log.warn("CAS用户名为空,无法获取用户信息"); + return null; + } + UserDTO userDTO = new UserDTO(); userDTO.setUsername(username); R userInfoR = sysUserService.getUserInfo(userDTO); if (userInfoR.getData() == null) { - log.info("CAS 不存在用户:{}", username); - return null; - } + log.info("CAS 不存在用户:{}", username); + return null; + } return userInfoR.getData(); - } + } } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/DingTalkLoginHandler.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/DingTalkLoginHandler.java index 1f6e06d26..ca3a8748e 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/DingTalkLoginHandler.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/DingTalkLoginHandler.java @@ -17,6 +17,7 @@ package com.pig4cloud.pigx.admin.handler; +import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONUtil; @@ -91,6 +92,11 @@ public class DingTalkLoginHandler extends AbstractLoginHandler { */ @Override public UserInfo info(String openId) { + if (StrUtil.isBlank(openId)) { + log.warn("钉钉openId为空,无法获取用户信息"); + return null; + } + UserDTO userDTO = new UserDTO(); userDTO.setWxDingUserid(openId); diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/GiteeLoginHandler.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/GiteeLoginHandler.java index 3941d3ff8..dc6900d27 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/GiteeLoginHandler.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/GiteeLoginHandler.java @@ -17,6 +17,7 @@ package com.pig4cloud.pigx.admin.handler; +import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.URLUtil; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONObject; @@ -87,6 +88,10 @@ public class GiteeLoginHandler extends AbstractLoginHandler { */ @Override public UserInfo info(String identify) { + if (StrUtil.isBlank(identify)) { + log.warn("Gitee标识为空,无法获取用户信息"); + return null; + } UserDTO userDTO = new UserDTO(); userDTO.setGiteeLogin(identify); diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/MiniAppLoginHandler.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/MiniAppLoginHandler.java index ed48115c6..81803fbe1 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/MiniAppLoginHandler.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/MiniAppLoginHandler.java @@ -17,6 +17,7 @@ package com.pig4cloud.pigx.admin.handler; +import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; @@ -46,72 +47,76 @@ import org.springframework.stereotype.Component; @AllArgsConstructor public class MiniAppLoginHandler extends AbstractLoginHandler { - private final SysUserService sysUserService; + private final SysUserService sysUserService; - private final SysSocialDetailsMapper sysSocialDetailsMapper; + private final SysSocialDetailsMapper sysSocialDetailsMapper; - /** - * 小程序登录传入code - *

- * 通过code 调用qq 获取唯一标识 - * - * @param code - * @return - */ - @Override - public String identify(String code) { - SysSocialDetails condition = new SysSocialDetails(); - condition.setType(LoginTypeEnum.MINI_APP.getType()); - SysSocialDetails socialDetails = sysSocialDetailsMapper.selectOne(new QueryWrapper<>(condition)); + /** + * 小程序登录传入code + *

+ * 通过code 调用qq 获取唯一标识 + * + * @param code + * @return + */ + @Override + public String identify(String code) { + SysSocialDetails condition = new SysSocialDetails(); + condition.setType(LoginTypeEnum.MINI_APP.getType()); + SysSocialDetails socialDetails = sysSocialDetailsMapper.selectOne(new QueryWrapper<>(condition)); - String url = String.format(SecurityConstants.MINI_APP_AUTHORIZATION_CODE_URL, socialDetails.getAppId(), - socialDetails.getAppSecret(), code); - String result = HttpUtil.get(url); - log.debug("微信小程序响应报文:{}", result); - JSONObject resultJsonObj = JSONUtil.parseObj(result); + String url = String.format(SecurityConstants.MINI_APP_AUTHORIZATION_CODE_URL, socialDetails.getAppId(), + socialDetails.getAppSecret(), code); + String result = HttpUtil.get(url); + log.debug("微信小程序响应报文:{}", result); + JSONObject resultJsonObj = JSONUtil.parseObj(result); - if (resultJsonObj.containsKey("errcode")) { - log.error("微信小程序登录失败:{}", result); - throw new CheckedException("微信小程序登录失败"); - } + if (resultJsonObj.containsKey("errcode")) { + log.error("微信小程序登录失败:{}", result); + throw new CheckedException("微信小程序登录失败"); + } - Object obj = resultJsonObj.get("openid"); - return obj.toString(); - } + return resultJsonObj.getStr("openid"); + } - /** - * 根据openId获取用户信息 - * - * @param openId 用户openId - * @return 用户信息对象,未找到时返回null - */ - @Override - public UserInfo info(String openId) { - UserDTO userDTO = new UserDTO(); - userDTO.setMiniOpenid(openId); + /** + * 根据openId获取用户信息 + * + * @param openId 用户openId + * @return 用户信息对象,未找到时返回null + */ + @Override + public UserInfo info(String openId) { + if (StrUtil.isBlank(openId)) { + log.warn("小程序openId为空,无法获取用户信息"); + return null; + } - R userInfoR = sysUserService.getUserInfo(userDTO); + UserDTO userDTO = new UserDTO(); + userDTO.setMiniOpenid(openId); - if (userInfoR.getData() == null) { - log.info("小程序不存在用户:{}", openId); - return null; - } + R userInfoR = sysUserService.getUserInfo(userDTO); - return userInfoR.getData(); - } + if (userInfoR.getData() == null) { + log.info("小程序不存在用户:{}", openId); + return null; + } - /** - * 绑定逻辑 - * - * @param user 用户实体 - * @param identify 渠道返回唯一标识 - * @return - */ - @Override - public Boolean bind(SysUser user, String identify) { - user.setMiniOpenid(identify); - sysUserService.updateById(user); - return true; - } + return userInfoR.getData(); + } + + /** + * 绑定逻辑 + * + * @param user 用户实体 + * @param identify 渠道返回唯一标识 + * @return + */ + @Override + public Boolean bind(SysUser user, String identify) { + user.setMiniOpenid(identify); + sysUserService.updateById(user); + return true; + } } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/OscChinaLoginHandler.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/OscChinaLoginHandler.java index b2bfeb0ca..34ba9b02e 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/OscChinaLoginHandler.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/OscChinaLoginHandler.java @@ -17,6 +17,7 @@ package com.pig4cloud.pigx.admin.handler; +import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; @@ -86,8 +87,7 @@ public class OscChinaLoginHandler extends AbstractLoginHandler { JSONObject userInfo = JSONUtil.parseObj(resp); // 开源中国唯一标识 - String id = userInfo.getStr("id"); - return id; + return userInfo.getStr("id"); } /** @@ -98,6 +98,11 @@ public class OscChinaLoginHandler extends AbstractLoginHandler { */ @Override public UserInfo info(String identify) { + if (StrUtil.isBlank(identify)) { + log.warn("开源中国标识为空,无法获取用户信息"); + return null; + } + UserDTO userDTO = new UserDTO(); userDTO.setOscId(identify); diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/SmsLoginHandler.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/SmsLoginHandler.java index f9bf59779..d9866eb62 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/SmsLoginHandler.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/SmsLoginHandler.java @@ -17,6 +17,7 @@ package com.pig4cloud.pigx.admin.handler; +import cn.hutool.core.util.StrUtil; import com.pig4cloud.pigx.admin.api.dto.UserDTO; import com.pig4cloud.pigx.admin.api.dto.UserInfo; import com.pig4cloud.pigx.admin.api.entity.SysUser; @@ -48,23 +49,28 @@ public class SmsLoginHandler extends AbstractLoginHandler { } /** - * 通过手机号获取用户信息 - * @param identify 手机号标识 - * @return 用户信息,未找到时返回null + * 通过手机号获取用户信息 + * @param identify 手机号标识 + * @return 用户信息,未找到时返回null */ @Override public UserInfo info(String identify) { - UserDTO userDTO = new UserDTO(); - userDTO.setPhone(identify); - - R userInfoR = sysUserService.getUserInfo(userDTO); - - if (userInfoR.getData() == null) { - log.info("手机号 不存在用户:{}", identify); + if (StrUtil.isBlank(identify)) { + log.warn("手机号为空,无法获取用户信息"); return null; } - return userInfoR.getData(); + UserDTO userDTO = new UserDTO(); + userDTO.setPhone(identify); + + R userInfoR = sysUserService.getUserInfo(userDTO); + + if (userInfoR.getData() == null) { + log.info("手机号 不存在用户:{}", identify); + return null; + } + + return userInfoR.getData(); } /** diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/WeChatCPLoginHandler.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/WeChatCPLoginHandler.java index af636b1ba..f25f2942b 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/WeChatCPLoginHandler.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/WeChatCPLoginHandler.java @@ -17,6 +17,7 @@ package com.pig4cloud.pigx.admin.handler; +import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @@ -82,6 +83,11 @@ public class WeChatCPLoginHandler extends AbstractLoginHandler { */ @Override public UserInfo info(String openId) { + if (StrUtil.isBlank(openId)) { + log.warn("企业微信userid为空,无法获取用户信息"); + return null; + } + UserDTO userDTO = new UserDTO(); userDTO.setWxCpUserid(openId); diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/WeChatLoginHandler.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/WeChatLoginHandler.java index 2c347f6e7..f7b774c1a 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/WeChatLoginHandler.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/WeChatLoginHandler.java @@ -17,6 +17,7 @@ package com.pig4cloud.pigx.admin.handler; +import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @@ -42,63 +43,70 @@ import org.springframework.stereotype.Component; @AllArgsConstructor public class WeChatLoginHandler extends AbstractLoginHandler { - private final SysUserService sysUserService; + private final SysUserService sysUserService; - private final SysSocialDetailsMapper sysSocialDetailsMapper; + private final SysSocialDetailsMapper sysSocialDetailsMapper; - /** - * 微信登录传入code - *

- * 通过code 调用qq 获取唯一标识 - * @param code - * @return - */ - @Override - public String identify(String code) { - SysSocialDetails condition = new SysSocialDetails(); - condition.setType(LoginTypeEnum.WECHAT.getType()); - SysSocialDetails socialDetails = sysSocialDetailsMapper.selectOne(new QueryWrapper<>(condition)); + /** + * 微信登录传入code + *

+ * 通过code 调用qq 获取唯一标识 + * + * @param code + * @return + */ + @Override + public String identify(String code) { + SysSocialDetails condition = new SysSocialDetails(); + condition.setType(LoginTypeEnum.WECHAT.getType()); + SysSocialDetails socialDetails = sysSocialDetailsMapper.selectOne(new QueryWrapper<>(condition)); - String url = String.format(SecurityConstants.WX_AUTHORIZATION_CODE_URL, socialDetails.getAppId(), - socialDetails.getAppSecret(), code); - String result = HttpUtil.get(url); - log.debug("微信响应报文:{}", result); + String url = String.format(SecurityConstants.WX_AUTHORIZATION_CODE_URL, socialDetails.getAppId(), + socialDetails.getAppSecret(), code); + String result = HttpUtil.get(url); + log.debug("微信响应报文:{}", result); - Object obj = JSONUtil.parseObj(result).get("openid"); - return obj.toString(); - } + return JSONUtil.parseObj(result).getStr("openid"); + } - /** - * 根据微信openId获取用户信息 - * @param openId 微信openId - * @return 用户信息,未找到时返回null - */ - @Override - public UserInfo info(String openId) { - UserDTO userDTO = new UserDTO(); - userDTO.setWxOpenid(openId); + /** + * 根据微信openId获取用户信息 + * + * @param openId 微信openId + * @return 用户信息,未找到时返回null + */ + @Override + public UserInfo info(String openId) { + if (StrUtil.isBlank(openId)) { + log.warn("微信openId为空,无法获取用户信息"); + return null; + } - R userInfoR = sysUserService.getUserInfo(userDTO); + UserDTO userDTO = new UserDTO(); + userDTO.setWxOpenid(openId); - if (userInfoR.getData() == null) { - log.info("微信 不存在用户:{}", openId); - return null; - } + R userInfoR = sysUserService.getUserInfo(userDTO); - return userInfoR.getData(); - } + if (userInfoR.getData() == null) { + log.info("微信 不存在用户:{}", openId); + return null; + } - /** - * 绑定逻辑 - * @param user 用户实体 - * @param identify 渠道返回唯一标识 - * @return - */ - @Override - public Boolean bind(SysUser user, String identify) { - user.setWxOpenid(identify); - sysUserService.updateById(user); - return true; - } + return userInfoR.getData(); + } + + /** + * 绑定逻辑 + * + * @param user 用户实体 + * @param identify 渠道返回唯一标识 + * @return + */ + @Override + public Boolean bind(SysUser user, String identify) { + user.setWxOpenid(identify); + sysUserService.updateById(user); + return true; + } } From b7c41327655ab3daf58697ee854f1fd2e01832bf Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Thu, 30 Oct 2025 17:25:38 +0800 Subject: [PATCH 03/44] =?UTF-8?q?feat(security):=20=E4=BC=98=E5=8C=96Feign?= =?UTF-8?q?=20token=E4=BC=A0=E9=80=92=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构PigxOAuthRequestInterceptor的token获取流程 - 优先从Web请求中解析token,失败后从NonWebTokenContextHolder获取 - 增加Objects.nonNull检查确保request不为空 - 优化token传递逻辑,支持消息队列等非Web场景 --- .../feign/PigxOAuthRequestInterceptor.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/feign/PigxOAuthRequestInterceptor.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/feign/PigxOAuthRequestInterceptor.java index d7f9421dc..35cd240e8 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/feign/PigxOAuthRequestInterceptor.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/feign/PigxOAuthRequestInterceptor.java @@ -7,15 +7,15 @@ import com.pig4cloud.pigx.common.core.util.WebUtils; import com.pig4cloud.pigx.common.security.util.NonWebTokenContextHolder; import feign.RequestInterceptor; import feign.RequestTemplate; +import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver; -import jakarta.servlet.http.HttpServletRequest; - import java.util.Collection; +import java.util.Objects; /** * oauth2 feign token传递 @@ -48,19 +48,23 @@ public class PigxOAuthRequestInterceptor implements RequestInterceptor { return; } - // 非web 请求直接跳过 - String token = ""; - if (WebUtils.getRequest() == null) { - token = NonWebTokenContextHolder.getToken(); - } else { - HttpServletRequest request = WebUtils.getRequest(); - // 避免请求参数的 query token 无法传递 + String token = null; + HttpServletRequest request = WebUtils.getRequest(); + + // 优先尝试从 Web 请求中解析 token + if (Objects.nonNull(request)) { token = tokenResolver.resolve(request); } + // 如果 request 中没有 token,从 NonWebTokenContextHolder 获取(适配消息队列等非 Web 场景) + if (StrUtil.isBlank(token)) { + token = NonWebTokenContextHolder.getToken(); + } + if (StrUtil.isBlank(token)) { return; } + template.header(HttpHeaders.AUTHORIZATION, String.format("%s %s", OAuth2AccessToken.TokenType.BEARER.getValue(), token)); From 646859625b13f64e674131030d3f61eb3ce13747 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Thu, 30 Oct 2025 21:44:51 +0800 Subject: [PATCH 04/44] =?UTF-8?q?refactor(ConnectServiceImpl):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E7=AC=AC=E4=B8=89=E6=96=B9API=E9=9B=86=E6=88=90?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 提取DingTalk/WeChat API字段名为常量 - 统一语言代码为常量LANGUAGE_ZH_CN - 优化JSON字段访问方式避免硬编码 - 重构企业微信配置获取逻辑 - 增强代码可读性与维护性 --- .../service/impl/ConnectServiceImpl.java | 482 ++++++++++-------- 1 file changed, 257 insertions(+), 225 deletions(-) diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/ConnectServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/ConnectServiceImpl.java index b86d6aa56..36ad6224a 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/ConnectServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/ConnectServiceImpl.java @@ -62,274 +62,306 @@ import java.util.List; @RequiredArgsConstructor public class ConnectServiceImpl implements ConnectService { - private final SysSocialDetailsMapper sysSocialDetailsMapper; + private final SysSocialDetailsMapper sysSocialDetailsMapper; - private final SysDeptService sysDeptService; + private final SysDeptService sysDeptService; - private final SysUserService sysUserService; + private final SysUserService sysUserService; - /** - * 同步钉钉部门 - * @return Boolean - */ - @Override - @Transactional(rollbackFor = Exception.class) - public Boolean syncDingDept() { - OapiV2DepartmentListsubRequest req = new OapiV2DepartmentListsubRequest(); - req.setDeptId(1L); - req.setLanguage("zh_CN"); + // JSON field name constants for DingTalk/WeChat API + private static final String FIELD_MOBILE = "mobile"; - // 数据库中现有的部门 - List deptList = sysDeptService.list(Wrappers.emptyWrapper()); + private static final String FIELD_AVATAR = "avatar"; - // 查询根部门下的所有部门 - List insertDept = queryChildDept(req, getDingAccessToken(), deptList); + private static final String FIELD_NICKNAME = "nickname"; - // 去重 + private static final String FIELD_NAME = "name"; + + private static final String FIELD_EMAIL = "email"; + + private static final String FIELD_DEPT_ID = "dept_id"; + + private static final String FIELD_PARENT_ID = "parent_id"; + + private static final String FIELD_DEPT_ORDER_LIST = "dept_order_list"; + + private static final String FIELD_USERID_LIST = "userid_list"; + + private static final String FIELD_RESULT = "result"; + + // WeChat Enterprise config field name constants + private static final String FIELD_AGENT_ID = "agentId"; + + private static final String FIELD_TOKEN = "token"; + + private static final String FIELD_AES_KEY = "aesKey"; + + // Language code constant + private static final String LANGUAGE_ZH_CN = "zh_CN"; + + /** + * 同步钉钉部门 + * + * @return Boolean + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean syncDingDept() { + OapiV2DepartmentListsubRequest req = new OapiV2DepartmentListsubRequest(); + req.setDeptId(1L); + req.setLanguage(LANGUAGE_ZH_CN); + + // 数据库中现有的部门 + List deptList = sysDeptService.list(Wrappers.emptyWrapper()); + + // 查询根部门下的所有部门 + List insertDept = queryChildDept(req, getDingAccessToken(), deptList); + + // 去重 List syncDept = insertDept.stream().distinct().toList(); - // 保存部门以及部门关系 - if (CollectionUtils.isNotEmpty(syncDept)) { - syncDept.forEach(sysDeptService::save); - } + // 保存部门以及部门关系 + if (CollectionUtils.isNotEmpty(syncDept)) { + syncDept.forEach(sysDeptService::save); + } - return Boolean.TRUE; - } + return Boolean.TRUE; + } - /** - * 同步部门下的钉钉用户 - * @param deptId 部门ID - * @return R - */ - @SneakyThrows - @Override - @Transactional(rollbackFor = Exception.class) - public R syncDingUser(Long deptId) { - // 数据库中现有用户 - List users = sysUserService.list(Wrappers.emptyWrapper()); + /** + * 同步部门下的钉钉用户 + * + * @param deptId 部门ID + * @return R + */ + @SneakyThrows + @Override + @Transactional(rollbackFor = Exception.class) + public R syncDingUser(Long deptId) { + // 数据库中现有用户 + List users = sysUserService.list(Wrappers.emptyWrapper()); - String token = getDingAccessToken(); + String token = getDingAccessToken(); - // 查询部门下的用户id列表 - DingTalkClient client = new DefaultDingTalkClient(SecurityConstants.DING_DEPT_USERIDS_URL); - OapiUserListidRequest req = new OapiUserListidRequest(); - req.setDeptId(deptId); - OapiUserListidResponse rsp = client.execute(req, token); + // 查询部门下的用户id列表 + DingTalkClient client = new DefaultDingTalkClient(SecurityConstants.DING_DEPT_USERIDS_URL); + OapiUserListidRequest req = new OapiUserListidRequest(); + req.setDeptId(deptId); + OapiUserListidResponse rsp = client.execute(req, token); - JSONObject jsonObject = JSONUtil.parseObj(rsp.getBody()); - JSONObject result = jsonObject.getJSONObject("result"); + JSONObject jsonObject = JSONUtil.parseObj(rsp.getBody()); + JSONObject result = jsonObject.getJSONObject(FIELD_RESULT); - if (result == null) { - return R.ok(); - } - List useridList = result.get("userid_list", List.class); + if (result == null) { + return R.ok(); + } + List useridList = result.get(FIELD_USERID_LIST, List.class); - // 查询用户详情 - for (String userid : useridList) { - DingTalkClient userClient = new DefaultDingTalkClient(SecurityConstants.DING_USER_INFO_URL); - OapiV2UserGetRequest userGetRequest = new OapiV2UserGetRequest(); - userGetRequest.setUserid(userid); - OapiV2UserGetResponse userGetResponse = userClient.execute(userGetRequest, token); - JSONObject userResult = JSONUtil.parseObj(userGetResponse.getBody()); - JSONObject userObj = JSONUtil.parseObj(userResult.get("result")); + // 查询用户详情 + for (String userid : useridList) { + DingTalkClient userClient = new DefaultDingTalkClient(SecurityConstants.DING_USER_INFO_URL); + OapiV2UserGetRequest userGetRequest = new OapiV2UserGetRequest(); + userGetRequest.setUserid(userid); + OapiV2UserGetResponse userGetResponse = userClient.execute(userGetRequest, token); + JSONObject userResult = JSONUtil.parseObj(userGetResponse.getBody()); + JSONObject userObj = JSONUtil.parseObj(userResult.get(FIELD_RESULT)); - boolean exist = users.stream() - .filter(user -> StrUtil.isNotBlank(user.getPhone())) - .anyMatch(user -> user.getPhone().equals(userObj.getStr("mobile"))); + boolean exist = users.stream() + .filter(user -> StrUtil.isNotBlank(user.getPhone())) + .anyMatch(user -> user.getPhone().equals(userObj.getStr(FIELD_MOBILE))); - if (exist || StrUtil.isBlank(userObj.getStr("mobile"))) { - log.info("用户已存在或手机号不合法 {}", userid); - continue; - } + if (exist || StrUtil.isBlank(userObj.getStr(FIELD_MOBILE))) { + log.info("用户已存在或手机号不合法 {}", userid); + continue; + } - // 用户部门信息 - JSONArray deptOrders = userObj.getJSONArray("dept_order_list"); - JSONObject deptInfo = (JSONObject) deptOrders.get(0); + // 用户部门信息 + JSONArray deptOrders = userObj.getJSONArray(FIELD_DEPT_ORDER_LIST); + JSONObject deptInfo = (JSONObject) deptOrders.get(0); - // 用户信息 - UserDTO userDTO = new UserDTO(); - userDTO.setDeptId(deptInfo.getLong("dept_id")); - userDTO.setAvatar(userObj.getStr("avatar")); - userDTO.setUsername(userObj.getStr("mobile")); - userDTO.setPhone(userObj.getStr("mobile")); - userDTO.setNickname(userObj.getStr("nickname")); - userDTO.setName(userObj.getStr("name")); - userDTO.setEmail(userObj.getStr("email")); - // 初始化密码为 手机号 - userDTO.setPassword(userObj.getStr("mobile")); - userDTO.setPasswordModifyTime(LocalDateTime.now()); - sysUserService.saveUser(userDTO); - } + // 用户信息 + UserDTO userDTO = new UserDTO(); + userDTO.setDeptId(deptInfo.getLong(FIELD_DEPT_ID)); + userDTO.setAvatar(userObj.getStr(FIELD_AVATAR)); + userDTO.setUsername(userObj.getStr(FIELD_MOBILE)); + userDTO.setPhone(userObj.getStr(FIELD_MOBILE)); + userDTO.setNickname(userObj.getStr(FIELD_NICKNAME)); + userDTO.setName(userObj.getStr(FIELD_NAME)); + userDTO.setEmail(userObj.getStr(FIELD_EMAIL)); + // 初始化密码为 手机号 + userDTO.setPassword(userObj.getStr(FIELD_MOBILE)); + userDTO.setPasswordModifyTime(LocalDateTime.now()); + sysUserService.saveUser(userDTO); + } - return R.ok(); - } + return R.ok(); + } - /** - * 同步企微部门 - * @return R - */ - @Override - @Transactional(rollbackFor = Exception.class) - public R syncCpDept() { - WxCpService wxCpService = new WxCpServiceImpl(); - wxCpService.setWxCpConfigStorage(getCpConfig()); + /** + * 同步企微部门 + * + * @return R + */ + @Override + @Transactional(rollbackFor = Exception.class) + public R syncCpDept() { + WxCpService wxCpService = new WxCpServiceImpl(); + wxCpService.setWxCpConfigStorage(getCpConfig()); - List departList; - try { - departList = wxCpService.getDepartmentService().list(null); - } - catch (WxErrorException e) { - log.error("获取企业微信部门列表失败", e); - return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_CONNECT_CP_DEPT_SYNC_ERROR)); - } + List departList; + try { + departList = wxCpService.getDepartmentService().list(null); + } catch (WxErrorException e) { + log.error("获取企业微信部门列表失败", e); + return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_CONNECT_CP_DEPT_SYNC_ERROR)); + } - if (CollUtil.isEmpty(departList)) { - return R.ok(); - } + if (CollUtil.isEmpty(departList)) { + return R.ok(); + } - // 数据库中现有的部门 - List deptList = sysDeptService.list(Wrappers.emptyWrapper()); - departList.stream() - .filter(depart -> deptList.stream().noneMatch(sysDept -> depart.getName().equals(sysDept.getName()))) - .map(dept -> { - SysDept sysDept = new SysDept(); - sysDept.setName(dept.getName()); - sysDept.setDeptId(dept.getId()); - sysDept.setParentId(dept.getParentId()); - sysDept.setSortOrder(dept.getOrder().intValue()); - return sysDept; - }) - .forEach(sysDeptService::save); - return R.ok(); - } + // 数据库中现有的部门 + List deptList = sysDeptService.list(Wrappers.emptyWrapper()); + departList.stream() + .filter(depart -> deptList.stream().noneMatch(sysDept -> depart.getName().equals(sysDept.getName()))) + .map(dept -> { + SysDept sysDept = new SysDept(); + sysDept.setName(dept.getName()); + sysDept.setDeptId(dept.getId()); + sysDept.setParentId(dept.getParentId()); + sysDept.setSortOrder(dept.getOrder().intValue()); + return sysDept; + }) + .forEach(sysDeptService::save); + return R.ok(); + } - /** - * 同步企微用户 - * @return R - */ - @Override - public R syncCpUser() { - WxCpService wxCpService = new WxCpServiceImpl(); - wxCpService.setWxCpConfigStorage(getCpConfig()); + /** + * 同步企微用户 + * + * @return R + */ + @Override + public R syncCpUser() { + WxCpService wxCpService = new WxCpServiceImpl(); + wxCpService.setWxCpConfigStorage(getCpConfig()); - List cpUserList; - try { - cpUserList = wxCpService.getUserService().listByDepartment(1L, true, 0); - } - catch (WxErrorException e) { - log.error("获取企业微信用户列表失败", e); - return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_CONNECT_CP_USER_SYNC_ERROR)); - } + List cpUserList; + try { + cpUserList = wxCpService.getUserService().listByDepartment(1L, true, 0); + } catch (WxErrorException e) { + log.error("获取企业微信用户列表失败", e); + return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_CONNECT_CP_USER_SYNC_ERROR)); + } - if (CollUtil.isEmpty(cpUserList)) { - return R.ok(); - } + if (CollUtil.isEmpty(cpUserList)) { + return R.ok(); + } - // 系统原有用户 - List userList = sysUserService.list(Wrappers.emptyWrapper()); + // 系统原有用户 + List userList = sysUserService.list(Wrappers.emptyWrapper()); - for (WxCpUser cpUser : cpUserList) { - boolean exist = userList.stream() - .filter(user -> StrUtil.isNotBlank(user.getPhone())) - .anyMatch(user -> user.getPhone().equals(cpUser.getMobile())); - if (exist || StrUtil.isBlank(cpUser.getMobile())) { - log.info("用户已存在或手机号不合法跳过 {}", cpUser); - continue; - } + for (WxCpUser cpUser : cpUserList) { + boolean exist = userList.stream() + .filter(user -> StrUtil.isNotBlank(user.getPhone())) + .anyMatch(user -> user.getPhone().equals(cpUser.getMobile())); + if (exist || StrUtil.isBlank(cpUser.getMobile())) { + log.info("用户已存在或手机号不合法跳过 {}", cpUser); + continue; + } - UserDTO user = new UserDTO(); - user.setUsername(cpUser.getMobile()); - user.setName(cpUser.getName()); - user.setDeptId(cpUser.getDepartIds()[0]); - user.setEmail(cpUser.getEmail()); - user.setPhone(cpUser.getMobile()); - user.setAvatar(cpUser.getAvatar()); - // 初始化密码为 手机号 - user.setPassword(cpUser.getMobile()); - sysUserService.saveUser(user); - } + UserDTO user = new UserDTO(); + user.setUsername(cpUser.getMobile()); + user.setName(cpUser.getName()); + user.setDeptId(cpUser.getDepartIds()[0]); + user.setEmail(cpUser.getEmail()); + user.setPhone(cpUser.getMobile()); + user.setAvatar(cpUser.getAvatar()); + // 初始化密码为 手机号 + user.setPassword(cpUser.getMobile()); + sysUserService.saveUser(user); + } - return R.ok(); - } + return R.ok(); + } - private String getDingAccessToken() { - SysSocialDetails dingTalk = sysSocialDetailsMapper.selectOne(Wrappers.lambdaQuery() - .eq(SysSocialDetails::getType, LoginTypeEnum.DINGTALK.getType())); + private String getDingAccessToken() { + SysSocialDetails dingTalk = sysSocialDetailsMapper.selectOne(Wrappers.lambdaQuery() + .eq(SysSocialDetails::getType, LoginTypeEnum.DINGTALK.getType())); - DingTalkClient client = new DefaultDingTalkClient(SecurityConstants.DING_OLD_GET_TOKEN); - OapiGettokenRequest request = new OapiGettokenRequest(); - request.setAppkey(dingTalk.getAppId()); - request.setAppsecret(dingTalk.getAppSecret()); - request.setHttpMethod(HttpMethod.GET.name()); - try { - OapiGettokenResponse response = client.execute(request); - return response.getAccessToken(); - } - catch (Exception e) { - log.error("调用钉钉异常", e); - } - return null; - } + DingTalkClient client = new DefaultDingTalkClient(SecurityConstants.DING_OLD_GET_TOKEN); + OapiGettokenRequest request = new OapiGettokenRequest(); + request.setAppkey(dingTalk.getAppId()); + request.setAppsecret(dingTalk.getAppSecret()); + request.setHttpMethod(HttpMethod.GET.name()); + try { + OapiGettokenResponse response = client.execute(request); + return response.getAccessToken(); + } catch (Exception e) { + log.error("调用钉钉异常", e); + } + return null; + } - public WxCpDefaultConfigImpl getCpConfig() { - SysSocialDetails cp = sysSocialDetailsMapper.selectOne(Wrappers.lambdaQuery() - .eq(SysSocialDetails::getType, LoginTypeEnum.WEIXIN_CP.getType())); + public WxCpDefaultConfigImpl getCpConfig() { + SysSocialDetails cp = sysSocialDetailsMapper.selectOne(Wrappers.lambdaQuery() + .eq(SysSocialDetails::getType, LoginTypeEnum.WEIXIN_CP.getType())); - WxCpDefaultConfigImpl config = new WxCpDefaultConfigImpl(); - config.setCorpId(cp.getAppId()); // 设置微信企业号的appid - config.setCorpSecret(cp.getAppSecret()); // 设置微信企业号的app - // corpSecret - JSONObject ext = JSONUtil.parseObj(cp.getExt()); - config.setAgentId(ext.getInt("agentId")); // 设置微信企业号应用ID - config.setToken(ext.getStr("token")); // 设置微信企业号应用的token - config.setAesKey(ext.getStr("aesKey")); // 设置微信企业号应用的EncodingAESKey + WxCpDefaultConfigImpl config = new WxCpDefaultConfigImpl(); + config.setCorpId(cp.getAppId()); // 设置微信企业号的appid + config.setCorpSecret(cp.getAppSecret()); // 设置微信企业号的app + // corpSecret + JSONObject ext = JSONUtil.parseObj(cp.getExt()); + config.setAgentId(ext.getInt(FIELD_AGENT_ID)); // 设置微信企业号应用ID + config.setToken(ext.getStr(FIELD_TOKEN)); // 设置微信企业号应用的token + config.setAesKey(ext.getStr(FIELD_AES_KEY)); // 设置微信企业号应用的EncodingAESKey - return config; - } + return config; + } - /** - * 查询当前部门下的所有部门 - */ - @SneakyThrows - private List queryChildDept(OapiV2DepartmentListsubRequest req, String token, List deptList) { - List sysDepts = new LinkedList<>(); + /** + * 查询当前部门下的所有部门 + */ + @SneakyThrows + private List queryChildDept(OapiV2DepartmentListsubRequest req, String token, List deptList) { + List sysDepts = new LinkedList<>(); - // 调用Api查询 - DingTalkClient client = new DefaultDingTalkClient(SecurityConstants.DING_OLD_DEPT_URL); - OapiV2DepartmentListsubResponse rsp = client.execute(req, token); + // 调用Api查询 + DingTalkClient client = new DefaultDingTalkClient(SecurityConstants.DING_OLD_DEPT_URL); + OapiV2DepartmentListsubResponse rsp = client.execute(req, token); - JSONObject result = JSONUtil.parseObj(rsp.getBody()); - List resultList = result.get("result", List.class); + JSONObject result = JSONUtil.parseObj(rsp.getBody()); + List resultList = result.get(FIELD_RESULT, List.class); - if (CollUtil.isEmpty(resultList)) { - return sysDepts; - } + if (CollUtil.isEmpty(resultList)) { + return sysDepts; + } - // 过滤出数据库存在的部门 - List depts = resultList.stream() - .filter(dept -> deptList.stream().noneMatch(sysDept -> sysDept.getName().equals(dept.get("name")))) - .map(dept -> { - SysDept sysDept = new SysDept(); - sysDept.setName(Convert.toStr(dept.get("name"))); - sysDept.setDeptId(Convert.toLong(dept.get("dept_id"))); - sysDept.setParentId(Convert.toLong(dept.get("parent_id"))); - return sysDept; - }) + // 过滤出数据库存在的部门 + List depts = resultList.stream() + .filter(dept -> deptList.stream().noneMatch(sysDept -> sysDept.getName().equals(dept.get(FIELD_NAME)))) + .map(dept -> { + SysDept sysDept = new SysDept(); + sysDept.setName(Convert.toStr(dept.get(FIELD_NAME))); + sysDept.setDeptId(Convert.toLong(dept.get(FIELD_DEPT_ID))); + sysDept.setParentId(Convert.toLong(dept.get(FIELD_PARENT_ID))); + return sysDept; + }) .toList(); - // 递归查询下一级 - if (CollectionUtils.isNotEmpty(depts)) { - sysDepts.addAll(depts); - depts.forEach(dept -> { - OapiV2DepartmentListsubRequest request = new OapiV2DepartmentListsubRequest(); - request.setDeptId(dept.getDeptId()); - request.setLanguage("zh_CN"); - List childDept = queryChildDept(request, token, deptList); - sysDepts.addAll(childDept); - }); - } + // 递归查询下一级 + if (CollectionUtils.isNotEmpty(depts)) { + sysDepts.addAll(depts); + depts.forEach(dept -> { + OapiV2DepartmentListsubRequest request = new OapiV2DepartmentListsubRequest(); + request.setDeptId(dept.getDeptId()); + request.setLanguage(LANGUAGE_ZH_CN); + List childDept = queryChildDept(request, token, deptList); + sysDepts.addAll(childDept); + }); + } - return sysDepts; - } + return sysDepts; + } } From 45146adec14adbf83a3c7d39cb319a7cad7b0c21 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Thu, 30 Oct 2025 21:47:00 +0800 Subject: [PATCH 05/44] =?UTF-8?q?refactor(dependencies):=20=E5=8D=87?= =?UTF-8?q?=E7=BA=A7log4j2=E7=89=88=E6=9C=AC=E8=87=B32.25.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新log4j2依赖版本以修复潜在安全漏洞 - 保持与其他依赖版本的兼容性 --- pigx-common/pigx-common-bom/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pigx-common/pigx-common-bom/pom.xml b/pigx-common/pigx-common-bom/pom.xml index 8a90c4811..a5e39e518 100644 --- a/pigx-common/pigx-common-bom/pom.xml +++ b/pigx-common/pigx-common-bom/pom.xml @@ -42,7 +42,7 @@ 1.7.0 7.1 0.21.0 - 2.17.1 + 2.25.2 1.59.1 0.33.0 1.0.0 From 15f28dcd351bd484c4082c9f8c7bb96c76bf929b Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Mon, 3 Nov 2025 17:24:07 +0800 Subject: [PATCH 06/44] =?UTF-8?q?feat(personal):=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E6=89=8B=E6=9C=BA=E5=8F=B7=EF=BC=8C=E6=96=B0=E5=A2=9E=E7=9F=AD?= =?UTF-8?q?=E4=BF=A1=E9=AA=8C=E8=AF=81=E7=A0=81=E6=A0=A1=E9=AA=8C=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在UserDTO中增加验证码字段用于手机号修改校验 - 修改用户信息时增加短信验证码校验逻辑 - 新增验证码错误提示信息到i18n配置 - 扩展SysMessageService支持区分已注册/未注册手机号验证 --- .../common/core/exception/ErrorCodes.java | 167 ++-- .../resources/i18n/messages_zh_CN.properties | 3 +- .../pig4cloud/pigx/admin/api/dto/UserDTO.java | 54 +- .../controller/SysMessageController.java | 21 +- .../pigx/admin/service/SysMessageService.java | 14 +- .../service/impl/SysMessageServiceImpl.java | 764 +++++++++--------- .../service/impl/SysUserServiceImpl.java | 11 +- 7 files changed, 544 insertions(+), 490 deletions(-) diff --git a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/exception/ErrorCodes.java b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/exception/ErrorCodes.java index 08c223cb1..4e2bb402e 100644 --- a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/exception/ErrorCodes.java +++ b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/exception/ErrorCodes.java @@ -8,106 +8,111 @@ package com.pig4cloud.pigx.common.core.exception; */ public interface ErrorCodes { - /** - * 系统编码错误 - */ - String SYS_PARAM_CONFIG_ERROR = "sys.param.config.error"; + /** + * 系统编码错误 + */ + String SYS_PARAM_CONFIG_ERROR = "sys.param.config.error"; - /** - * 系统内置参数不能删除 - */ - String SYS_PARAM_DELETE_SYSTEM = "sys.param.delete.system"; + /** + * 系统内置参数不能删除 + */ + String SYS_PARAM_DELETE_SYSTEM = "sys.param.delete.system"; - /** - * 用户已存在 - */ - String SYS_USER_USERNAME_EXISTING = "sys.user.username.existing"; + /** + * 用户已存在 + */ + String SYS_USER_USERNAME_EXISTING = "sys.user.username.existing"; - /** - * 用户已存在 - */ - String SYS_USER_PHONE_EXISTING = "sys.user.phone.existing"; + /** + * 用户已存在 + */ + String SYS_USER_PHONE_EXISTING = "sys.user.phone.existing"; - /** - * 用户原密码错误,修改失败 - */ - String SYS_USER_UPDATE_PASSWORDERROR = "sys.user.update.passwordError"; + /** + * 用户原密码错误,修改失败 + */ + String SYS_USER_UPDATE_PASSWORDERROR = "sys.user.update.passwordError"; - /** - * 用户信息为空 - */ - String SYS_USER_USERINFO_EMPTY = "sys.user.userInfo.empty"; + /** + * 用户信息为空 + */ + String SYS_USER_USERINFO_EMPTY = "sys.user.userInfo.empty"; - /** - * 获取当前用户信息失败 - */ - String SYS_USER_QUERY_ERROR = "sys.user.query.error"; + /** + * 获取当前用户信息失败 + */ + String SYS_USER_QUERY_ERROR = "sys.user.query.error"; - String SYS_USER_IMPORT_SUCCEED = "sys.user.import.succeed"; + String SYS_USER_IMPORT_SUCCEED = "sys.user.import.succeed"; - /** - * 部门名称不存在 - */ - String SYS_DEPT_DEPTNAME_INEXISTENCE = "sys.dept.deptName.inexistence"; + /** + * 部门名称不存在 + */ + String SYS_DEPT_DEPTNAME_INEXISTENCE = "sys.dept.deptName.inexistence"; - /** - * 岗位名称不存在 - */ - String SYS_POST_POSTNAME_INEXISTENCE = "sys.post.postName.inexistence"; + /** + * 岗位名称不存在 + */ + String SYS_POST_POSTNAME_INEXISTENCE = "sys.post.postName.inexistence"; - /** - * 岗位名称或编码已经存在 - */ - String SYS_POST_NAMEORCODE_EXISTING = "sys.post.nameOrCode.existing"; + /** + * 岗位名称或编码已经存在 + */ + String SYS_POST_NAMEORCODE_EXISTING = "sys.post.nameOrCode.existing"; - /** - * 角色名称不存在 - */ - String SYS_ROLE_ROLENAME_INEXISTENCE = "sys.role.roleName.inexistence"; + /** + * 角色名称不存在 + */ + String SYS_ROLE_ROLENAME_INEXISTENCE = "sys.role.roleName.inexistence"; - /** - * 角色名或角色编码已经存在 - */ - String SYS_ROLE_NAMEORCODE_EXISTING = "sys.role.nameOrCode.existing"; + /** + * 角色名或角色编码已经存在 + */ + String SYS_ROLE_NAMEORCODE_EXISTING = "sys.role.nameOrCode.existing"; - /** - * 菜单存在下级节点 删除失败 - */ - String SYS_MENU_DELETE_EXISTING = "sys.menu.delete.existing"; + /** + * 菜单存在下级节点 删除失败 + */ + String SYS_MENU_DELETE_EXISTING = "sys.menu.delete.existing"; - /** - * 系统内置字典不允许删除 - */ - String SYS_DICT_DELETE_SYSTEM = "sys.dict.delete.system"; + /** + * 系统内置字典不允许删除 + */ + String SYS_DICT_DELETE_SYSTEM = "sys.dict.delete.system"; - /** - * 系统内置字典不能修改 - */ - String SYS_DICT_UPDATE_SYSTEM = "sys.dict.update.system"; + /** + * 系统内置字典不能修改 + */ + String SYS_DICT_UPDATE_SYSTEM = "sys.dict.update.system"; - /** - * 验证码发送频繁 - */ - String SYS_APP_SMS_OFTEN = "sys.app.sms.often"; + /** + * 验证码发送频繁 + */ + String SYS_APP_SMS_OFTEN = "sys.app.sms.often"; - /** - * 手机号未注册 - */ - String SYS_APP_PHONE_UNREGISTERED = "sys.app.phone.unregistered"; + /** + * 系统应用短信错误编码 + */ + String SYS_APP_SMS_ERROR = "sys.app.sms.error"; - /** - * 企微调用接口错误 - */ - String SYS_CONNECT_CP_DEPT_SYNC_ERROR = "sys.connect.cp.dept.sync.error"; + /** + * 手机号未注册 + */ + String SYS_APP_PHONE_UNREGISTERED = "sys.app.phone.unregistered"; - /** - * 企微调用接口错误 - */ - String SYS_CONNECT_CP_USER_SYNC_ERROR = "sys.connect.cp.user.sync.error"; + /** + * 企微调用接口错误 + */ + String SYS_CONNECT_CP_DEPT_SYNC_ERROR = "sys.connect.cp.dept.sync.error"; - /** - * 用户信息为空 - */ - String APP_USER_USERINFO_EMPTY = "app.user.userInfo.empty"; + /** + * 企微调用接口错误 + */ + String SYS_CONNECT_CP_USER_SYNC_ERROR = "sys.connect.cp.user.sync.error"; + + /** + * 用户信息为空 + */ + String APP_USER_USERINFO_EMPTY = "app.user.userInfo.empty"; } diff --git a/pigx-common/pigx-common-core/src/main/resources/i18n/messages_zh_CN.properties b/pigx-common/pigx-common-core/src/main/resources/i18n/messages_zh_CN.properties index 0699ff526..96cff7f7b 100644 --- a/pigx-common/pigx-common-core/src/main/resources/i18n/messages_zh_CN.properties +++ b/pigx-common/pigx-common-core/src/main/resources/i18n/messages_zh_CN.properties @@ -2,7 +2,7 @@ sys.user.update.passwordError=\u539F\u5BC6\u7801\u9519\u8BEF\uFF0C\u4FEE\u6539\u sys.user.query.error=\u83B7\u53D6\u5F53\u524D\u7528\u6237\u4FE1\u606F\u5931\u8D25 sys.user.import.succeed=\u7528\u6237\u5BFC\u5165\u6210\u529F\uFF0C\u9ED8\u8BA4\u5BC6\u7801\u4E3A\u624B\u673A\u53F7 sys.user.username.existing={0} \u7528\u6237\u540D\u5DF2\u5B58\u5728 -sys.user.phone.existing={0} \u624b\u673a\u53f7\u5df2\u5b58\u5728 +sys.user.phone.existing={0} \u624B\u673A\u53F7\u5DF2\u5B58\u5728 sys.user.userInfo.empty={0} \u7528\u6237\u4FE1\u606F\u4E3A\u7A7A sys.dept.deptName.inexistence={0} \u90E8\u95E8\u540D\u79F0\u4E0D\u5B58\u5728 sys.post.postName.inexistence={0} \u5C97\u4F4D\u540D\u79F0\u4E0D\u5B58\u5728 @@ -13,6 +13,7 @@ sys.param.delete.system=\u7CFB\u7EDF\u5185\u7F6E\u53C2\u6570\u4E0D\u80FD\u5220\u sys.param.config.error={0} \u7CFB\u7EDF\u53C2\u6570\u914D\u7F6E\u9519\u8BEF sys.menu.delete.existing=\u83DC\u5355\u542B\u6709\u4E0B\u7EA7\u4E0D\u80FD\u5220\u9664 sys.app.sms.often=\u9A8C\u8BC1\u7801\u53D1\u9001\u8FC7\u9891\u7E41 +sys.app.sms.error=\u9A8C\u8BC1\u7801\u9519\u8BEF sys.app.phone.unregistered={0} \u624B\u673A\u53F7\u672A\u6CE8\u518C sys.dict.delete.system=\u7CFB\u7EDF\u5185\u7F6E\u5B57\u5178\u9879\u76EE\u4E0D\u80FD\u5220\u9664 sys.dict.update.system=\u7CFB\u7EDF\u5185\u7F6E\u5B57\u5178\u9879\u76EE\u4E0D\u80FD\u4FEE\u6539 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 19b9d22b9..7a93923ec 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 @@ -35,33 +35,39 @@ import java.util.List; @EqualsAndHashCode(callSuper = true) public class UserDTO extends SysUser { - /** - * 角色ID - */ - @Schema(description = "角色id集合") - private List role; + /** + * 角色ID + */ + @Schema(description = "角色id集合") + private List role; - /** - * 部门id - */ - @Schema(description = "部门id") - private Long deptId; + /** + * 部门id + */ + @Schema(description = "部门id") + private Long deptId; - /** - * 部门id集合(支持多部门) - */ - @Schema(description = "部门id集合") - private List deptIds; + /** + * 部门id集合(支持多部门) + */ + @Schema(description = "部门id集合") + private List deptIds; - /** - * 岗位ID - */ - private List post; + /** + * 岗位ID + */ + private List post; - /** - * 新密码 - */ - @Schema(description = "新密码") - private String newpassword1; + /** + * 新密码 + */ + @Schema(description = "新密码") + private String newpassword1; + + /** + * 验证码 + */ + @Schema(description = "验证码") + private String code; } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysMessageController.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysMessageController.java index a9a78a766..978211887 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysMessageController.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysMessageController.java @@ -182,18 +182,31 @@ public class SysMessageController { } /** - * 发送短信验证码 - * @param mobile 手机号 + * 发送短信(验证码) + * + * @param mobile 手机号 (需要注册,对外提供,安全性第一) * @return {@link R } */ @Inner(value = false) @GetMapping("/send/smsCode") public R sendSmsCode(@RequestParam String mobile) { - return sysMessageService.sendSmsCode(mobile); + return sysMessageService.sendSmsCode(mobile, true); + } + + /** + * 发送内部短信验证码 + * + * @param mobile 手机号码 (系统内不一定存在) + * @return 发送结果 + */ + @GetMapping("/send/smsInnerCode") + public R sendSmsInnerCode(@RequestParam String mobile) { + return sysMessageService.sendSmsCode(mobile, false); } /** * 发送短信 + * * @param messageSmsDTO 消息 短信 DTO * @return {@link R } */ @@ -204,6 +217,7 @@ public class SysMessageController { /** * 发送邮件 + * * @param messageEmailDTO 留言内容 电子邮件 DTO * @return {@link R } */ @@ -214,6 +228,7 @@ public class SysMessageController { /** * 发送 webhook + * * @param messageHookDTO 消息挂钩 DTO * @return {@link R } */ diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysMessageService.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysMessageService.java index 400acd4ff..14ccb1699 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysMessageService.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysMessageService.java @@ -37,6 +37,7 @@ public interface SysMessageService extends IService { /** * 读取信息 + * * @param id id * @return true/false */ @@ -44,6 +45,7 @@ public interface SysMessageService extends IService { /** * 获取信息 + * * @param id id * @return messageVO */ @@ -61,13 +63,16 @@ public interface SysMessageService extends IService { /** * 发送手机验证码 - * @param mobile mobile - * @return code + * + * @param mobile 手机号码 + * @param registered 是否已注册 + * @return 发送结果 */ - R sendSmsCode(String mobile); + R sendSmsCode(String mobile, boolean registered); /** * 发送短信 + * * @param messageSmsDTO 消息 短信 DTO * @return {@link R } */ @@ -75,6 +80,7 @@ public interface SysMessageService extends IService { /** * 发送邮件 + * * @param messageEmailDTO 留言内容 电子邮件 DTO * @return {@link R } */ @@ -82,6 +88,7 @@ public interface SysMessageService extends IService { /** * 发送钩子消息 + * * @param messageHookDTO 消息挂钩 DTO * @return {@link R } */ @@ -89,6 +96,7 @@ public interface SysMessageService extends IService { /** * 列出 Hook 业务代码 + * * @param messageHookDTO 消息钩子 DTO * @return {@link R } */ diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMessageServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMessageServiceImpl.java index cebfeb7bc..b48263765 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMessageServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMessageServiceImpl.java @@ -97,428 +97,438 @@ import java.util.Objects; @Service @RequiredArgsConstructor public class SysMessageServiceImpl extends ServiceImpl - implements SysMessageService { + implements SysMessageService { - /** - * 支持短信服务商 - */ - private static final Class[] SUPPORT_CONFIG_CLAZZ = { AlibabaConfig.class, CloopenConfig.class, CtyunConfig.class, - EmayConfig.class, HuaweiConfig.class, JdCloudConfig.class, NeteaseConfig.class, TencentConfig.class, - UniConfig.class, YunpianConfig.class, ZhutongConfig.class, DingZhongConfig.class, QiNiuConfig.class }; + /** + * 支持短信服务商 + */ + private static final Class[] SUPPORT_CONFIG_CLAZZ = {AlibabaConfig.class, CloopenConfig.class, CtyunConfig.class, + EmayConfig.class, HuaweiConfig.class, JdCloudConfig.class, NeteaseConfig.class, TencentConfig.class, + UniConfig.class, YunpianConfig.class, ZhutongConfig.class, DingZhongConfig.class, QiNiuConfig.class}; - private final RemoteLogService remoteLogService; + private final RemoteLogService remoteLogService; - private final SysSystemConfigMapper sysSystemConfigMapper; + private final SysSystemConfigMapper sysSystemConfigMapper; - private final SysMessageRelationMapper relationMapper; + private final SysMessageRelationMapper relationMapper; - private final SysUserMapper userMapper; + private final SysUserMapper userMapper; - private final SysFileMapper fileMapper; + private final SysFileMapper fileMapper; - private final FileTemplate fileTemplate; + private final FileTemplate fileTemplate; - /** - * 发送信息 - * @param id id - * @return true/false - */ - @Override - @Transactional(rollbackFor = Exception.class) - public Boolean sendMessage(Long id) { - // 更新信息状态 - SysMessageEntity sysMessage = new SysMessageEntity(); - sysMessage.setSendFlag(YesNoEnum.YES.getCode()); - baseMapper.update(sysMessage, Wrappers.lambdaQuery().eq(SysMessageEntity::getId, id)); - return Boolean.TRUE; - } + /** + * 发送信息 + * + * @param id id + * @return true/false + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean sendMessage(Long id) { + // 更新信息状态 + SysMessageEntity sysMessage = new SysMessageEntity(); + sysMessage.setSendFlag(YesNoEnum.YES.getCode()); + baseMapper.update(sysMessage, Wrappers.lambdaQuery().eq(SysMessageEntity::getId, id)); + return Boolean.TRUE; + } - /** - * 保存消息及其关联关系 - * @param sysMessage 消息对象 - * @return 是否保存成功 - */ - @Transactional(rollbackFor = Exception.class) - public Boolean saveOrUpdateMessage(SysMessageVO sysMessage) { - SysMessageEntity messageEntity = new SysMessageEntity(); - BeanUtils.copyProperties(sysMessage, messageEntity); + /** + * 保存消息及其关联关系 + * + * @param sysMessage 消息对象 + * @return 是否保存成功 + */ + @Transactional(rollbackFor = Exception.class) + public Boolean saveOrUpdateMessage(SysMessageVO sysMessage) { + SysMessageEntity messageEntity = new SysMessageEntity(); + BeanUtils.copyProperties(sysMessage, messageEntity); - if (Objects.isNull(sysMessage.getId())) { - baseMapper.insert(messageEntity); - } - else { - baseMapper.updateById(sysMessage); - } + if (Objects.isNull(sysMessage.getId())) { + baseMapper.insert(messageEntity); + } else { + baseMapper.updateById(sysMessage); + } - // 删除原有用户 - relationMapper.delete(Wrappers.lambdaQuery() - .eq(SysMessageRelationEntity::getMsgId, messageEntity.getId())); + // 删除原有用户 + relationMapper.delete(Wrappers.lambdaQuery() + .eq(SysMessageRelationEntity::getMsgId, messageEntity.getId())); - List userList = sysMessage.getUserList(); - if (CollUtil.isNotEmpty(userList)) { - userList.forEach(user -> { - SysMessageRelationEntity relationEntity = new SysMessageRelationEntity(); - relationEntity.setMsgId(messageEntity.getId()); - relationEntity.setUserId(user.getId()); - relationMapper.insert(relationEntity); - }); - } - else { - userMapper.selectList(Wrappers.emptyWrapper()).stream().map(SysUser::getUserId).forEach(userId -> { - SysMessageRelationEntity relationEntity = new SysMessageRelationEntity(); - relationEntity.setMsgId(messageEntity.getId()); - relationEntity.setUserId(userId); - relationMapper.insert(relationEntity); - }); - } - return Boolean.TRUE; - } + List userList = sysMessage.getUserList(); + if (CollUtil.isNotEmpty(userList)) { + userList.forEach(user -> { + SysMessageRelationEntity relationEntity = new SysMessageRelationEntity(); + relationEntity.setMsgId(messageEntity.getId()); + relationEntity.setUserId(user.getId()); + relationMapper.insert(relationEntity); + }); + } else { + userMapper.selectList(Wrappers.emptyWrapper()).stream().map(SysUser::getUserId).forEach(userId -> { + SysMessageRelationEntity relationEntity = new SysMessageRelationEntity(); + relationEntity.setMsgId(messageEntity.getId()); + relationEntity.setUserId(userId); + relationMapper.insert(relationEntity); + }); + } + return Boolean.TRUE; + } - /** - * 获取用户的信息列表 - * @return list - */ - @Override - public IPage pageUserMessage(Page page, SysMessageVO sysMessage) { - MPJLambdaWrapper wrapper = new MPJLambdaWrapper() - .selectAll(SysMessageEntity.class) - .select(SysMessageRelationEntity::getReadFlag) - .leftJoin(SysMessageRelationEntity.class, SysMessageRelationEntity::getMsgId, SysMessageEntity::getId) - .eq(SysMessageRelationEntity::getUserId, SecurityUtils.getUser().getId()) - .eq(SysMessageEntity::getSendFlag, YesNoEnum.YES.getCode()) - .eq(StrUtil.isNotBlank(sysMessage.getCategory()), SysMessageEntity::getCategory, sysMessage.getCategory()) - .eq(StrUtil.isNotBlank(sysMessage.getReadFlag()), SysMessageRelationEntity::getReadFlag, - sysMessage.getReadFlag()) - .orderByDesc(SysMessageEntity::getSort) - .orderByDesc(SysMessageEntity::getCreateTime); - return baseMapper.selectJoinPage(page, SysMessageVO.class, wrapper); - } + /** + * 获取用户的信息列表 + * + * @return list + */ + @Override + public IPage pageUserMessage(Page page, SysMessageVO sysMessage) { + MPJLambdaWrapper wrapper = new MPJLambdaWrapper() + .selectAll(SysMessageEntity.class) + .select(SysMessageRelationEntity::getReadFlag) + .leftJoin(SysMessageRelationEntity.class, SysMessageRelationEntity::getMsgId, SysMessageEntity::getId) + .eq(SysMessageRelationEntity::getUserId, SecurityUtils.getUser().getId()) + .eq(SysMessageEntity::getSendFlag, YesNoEnum.YES.getCode()) + .eq(StrUtil.isNotBlank(sysMessage.getCategory()), SysMessageEntity::getCategory, sysMessage.getCategory()) + .eq(StrUtil.isNotBlank(sysMessage.getReadFlag()), SysMessageRelationEntity::getReadFlag, + sysMessage.getReadFlag()) + .orderByDesc(SysMessageEntity::getSort) + .orderByDesc(SysMessageEntity::getCreateTime); + return baseMapper.selectJoinPage(page, SysMessageVO.class, wrapper); + } - /** - * 读取信息 - * @param id id - * @return true/false - */ - @Override - public Boolean readMessage(Long id) { - SysMessageRelationEntity relationEntity = new SysMessageRelationEntity(); - relationEntity.setReadFlag(YesNoEnum.YES.getCode()); - relationMapper.update(relationEntity, - Wrappers.lambdaQuery() - .eq(SysMessageRelationEntity::getMsgId, id) - .eq(SysMessageRelationEntity::getUserId, SecurityUtils.getUser().getId())); - return Boolean.TRUE; - } + /** + * 读取信息 + * + * @param id id + * @return true/false + */ + @Override + public Boolean readMessage(Long id) { + SysMessageRelationEntity relationEntity = new SysMessageRelationEntity(); + relationEntity.setReadFlag(YesNoEnum.YES.getCode()); + relationMapper.update(relationEntity, + Wrappers.lambdaQuery() + .eq(SysMessageRelationEntity::getMsgId, id) + .eq(SysMessageRelationEntity::getUserId, SecurityUtils.getUser().getId())); + return Boolean.TRUE; + } - /** - * 获取信息 - * @param id id - * @return messageVO - */ - @Override - public SysMessageVO getMessage(Long id) { - SysMessageEntity messageEntity = baseMapper.selectById(id); - SysMessageVO SysMessageVO = new SysMessageVO(); - BeanUtils.copyProperties(messageEntity, SysMessageVO); + /** + * 获取信息 + * + * @param id id + * @return messageVO + */ + @Override + public SysMessageVO getMessage(Long id) { + SysMessageEntity messageEntity = baseMapper.selectById(id); + SysMessageVO SysMessageVO = new SysMessageVO(); + BeanUtils.copyProperties(messageEntity, SysMessageVO); - // 全体通知 - if (messageEntity.getAllFlag().equals(YesNoEnum.YES.getCode())) { - return SysMessageVO; - } - List userIdList = relationMapper - .selectList(Wrappers.lambdaQuery().eq(SysMessageRelationEntity::getMsgId, id)) - .stream() - .map(SysMessageRelationEntity::getUserId) - .toList(); + // 全体通知 + if (messageEntity.getAllFlag().equals(YesNoEnum.YES.getCode())) { + return SysMessageVO; + } + List userIdList = relationMapper + .selectList(Wrappers.lambdaQuery().eq(SysMessageRelationEntity::getMsgId, id)) + .stream() + .map(SysMessageRelationEntity::getUserId) + .toList(); - if (CollUtil.isNotEmpty(userIdList)) { - List orgNodeUserVoList = userMapper.selectByIds(userIdList).stream().map(user -> { - OrgTreeVO nodeUserVo = new OrgTreeVO(); - nodeUserVo.setId(user.getUserId()); - nodeUserVo.setAvatar(user.getAvatar()); - nodeUserVo.setName(user.getName()); - return nodeUserVo; - }).toList(); + if (CollUtil.isNotEmpty(userIdList)) { + List orgNodeUserVoList = userMapper.selectByIds(userIdList).stream().map(user -> { + OrgTreeVO nodeUserVo = new OrgTreeVO(); + nodeUserVo.setId(user.getUserId()); + nodeUserVo.setAvatar(user.getAvatar()); + nodeUserVo.setName(user.getName()); + return nodeUserVo; + }).toList(); - SysMessageVO.setUserList(orgNodeUserVoList); - } + SysMessageVO.setUserList(orgNodeUserVoList); + } - return SysMessageVO; - } + return SysMessageVO; + } - /** - * 查询用户阅读情况 - * @param page 分页 - * @param messageId 消息ID - * @param name 姓名 - * @return page - */ - @Override - public Page pageUserRead(Page page, Long messageId, String name) { - MPJLambdaWrapper wrapper = new MPJLambdaWrapper() - .selectAll(SysMessageRelationEntity.class) - .select(SysUser::getUsername, SysUser::getName, SysUser::getUserId) - .select(SysMessageEntity::getTitle) - .leftJoin(SysUser.class, SysUser::getUserId, SysMessageRelationEntity::getUserId) - .leftJoin(SysMessageEntity.class, SysMessageEntity::getId, SysMessageRelationEntity::getMsgId) - .eq(SysMessageRelationEntity::getMsgId, messageId) - .like(StrUtil.isNotBlank(name), SysUser::getName, name); - return relationMapper.selectJoinPage(page, SysMessageReadVO.class, wrapper); - } + /** + * 查询用户阅读情况 + * + * @param page 分页 + * @param messageId 消息ID + * @param name 姓名 + * @return page + */ + @Override + public Page pageUserRead(Page page, Long messageId, String name) { + MPJLambdaWrapper wrapper = new MPJLambdaWrapper() + .selectAll(SysMessageRelationEntity.class) + .select(SysUser::getUsername, SysUser::getName, SysUser::getUserId) + .select(SysMessageEntity::getTitle) + .leftJoin(SysUser.class, SysUser::getUserId, SysMessageRelationEntity::getUserId) + .leftJoin(SysMessageEntity.class, SysMessageEntity::getId, SysMessageRelationEntity::getMsgId) + .eq(SysMessageRelationEntity::getMsgId, messageId) + .like(StrUtil.isNotBlank(name), SysUser::getName, name); + return relationMapper.selectJoinPage(page, SysMessageReadVO.class, wrapper); + } - /** - * 发送手机验证码 TODO: 调用短信网关发送验证码,测试返回前端 - * @param mobile mobile - * @return code - */ - @Override - public R sendSmsCode(String mobile) { - List userList = TenantBroker - .noneAs(() -> userMapper.selectList(Wrappers.query().lambda().eq(SysUser::getPhone, mobile))); + /** + * 发送手机验证码 + * + * @param mobile 手机号码 + * @param registered 是否已注册 + * @return 发送结果 + */ + @Override + public R sendSmsCode(String mobile, boolean registered) { + List userList = TenantBroker + .noneAs(() -> userMapper.selectList(Wrappers.query().lambda().eq(SysUser::getPhone, mobile))); - if (CollUtil.isEmpty(userList)) { - log.info("手机号未注册:{}", mobile); - return R.ok(Boolean.FALSE, MsgUtils.getMessage(ErrorCodes.SYS_APP_PHONE_UNREGISTERED, mobile)); - } + if (registered && CollUtil.isEmpty(userList)) { + log.info("手机号未注册:{}", mobile); + return R.ok(Boolean.FALSE, MsgUtils.getMessage(ErrorCodes.SYS_APP_PHONE_UNREGISTERED, mobile)); + } - String codeObj = RedisUtils - .get(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.SMS.getType() + StringPool.AT + mobile); + String codeObj = RedisUtils + .get(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.SMS.getType() + StringPool.AT + mobile); - if (StrUtil.isNotBlank(codeObj)) { - log.info("手机号验证码未过期:{},{}", mobile, codeObj); - return R.ok(Boolean.FALSE, MsgUtils.getMessage(ErrorCodes.SYS_APP_SMS_OFTEN)); - } + if (StrUtil.isNotBlank(codeObj)) { + log.info("手机号验证码未过期:{},{}", mobile, codeObj); + return R.ok(Boolean.FALSE, MsgUtils.getMessage(ErrorCodes.SYS_APP_SMS_OFTEN)); + } - String code = RandomUtil.randomNumbers(Integer.parseInt(SecurityConstants.CODE_SIZE)); - log.info("手机号生成验证码成功:{},{}", mobile, code); - RedisUtils.set(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.SMS.getType() + StringPool.AT + mobile, code, - SecurityConstants.CODE_TIME); + String code = RandomUtil.randomNumbers(Integer.parseInt(SecurityConstants.CODE_SIZE)); + log.info("手机号生成验证码成功:{},{}", mobile, code); + RedisUtils.set(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.SMS.getType() + StringPool.AT + mobile, code, + SecurityConstants.CODE_TIME); - // 发送 - return this.sendSms(MessageSmsDTO.builder() - .mobile(mobile) - .biz(SmsBizCodeEnum.SMS_NORMAL_CODE.getCode()) - .param(code) - .build()); - } + // 发送 + return this.sendSms(MessageSmsDTO.builder() + .mobile(mobile) + .biz(SmsBizCodeEnum.SMS_NORMAL_CODE.getCode()) + .param(code) + .build()); + } - /** - * 发送短信 - * @param messageSmsDTO 消息 短信 DTO - * @return {@link R } - */ - @Override - public R sendSms(MessageSmsDTO messageSmsDTO) { + /** + * 发送短信 + * + * @param messageSmsDTO 消息 短信 DTO + * @return {@link R } + */ + @Override + public R sendSms(MessageSmsDTO messageSmsDTO) { - // 根据业务编码获取短信通道配置 - List configEntityList = sysSystemConfigMapper - .selectList(Wrappers.lambdaQuery() - .eq(SysSystemConfigEntity::getConfigType, SystemConfigTypeEnum.SMS.getValue()) - .eq(SysSystemConfigEntity::getConfigStatus, YesNoEnum.YES.getCode()) - .eq(SysSystemConfigEntity::getConfigKey, messageSmsDTO.getBizCode())); - if (CollUtil.isEmpty(configEntityList)) { - return R.failed("发送失败,短信通道配置不存在"); - } + // 根据业务编码获取短信通道配置 + List configEntityList = sysSystemConfigMapper + .selectList(Wrappers.lambdaQuery() + .eq(SysSystemConfigEntity::getConfigType, SystemConfigTypeEnum.SMS.getValue()) + .eq(SysSystemConfigEntity::getConfigStatus, YesNoEnum.YES.getCode()) + .eq(SysSystemConfigEntity::getConfigKey, messageSmsDTO.getBizCode())); + if (CollUtil.isEmpty(configEntityList)) { + return R.failed("发送失败,短信通道配置不存在"); + } - SysSystemConfigEntity configEntity = configEntityList.get(0); - JSONObject configMap = JSONUtil.parseObj(configEntity.getConfigValue()); + SysSystemConfigEntity configEntity = configEntityList.get(0); + JSONObject configMap = JSONUtil.parseObj(configEntity.getConfigValue()); - // 组装SMS4J 的发送对象 - SmsBlend smsBlend = SmsFactory.getSmsBlend(messageSmsDTO.getBizCode()); - if (Objects.isNull(smsBlend)) { - Class targetClass = Arrays.stream(SUPPORT_CONFIG_CLAZZ) - .filter(config -> config.getSimpleName() - .toLowerCase() - .contains(configMap.getStr(MessageSmsDTO.Fields.supplier))) - .findFirst() - .orElseThrow(() -> new CheckedException("发送失败,短信通道配置不存在")); - BaseConfig baseConfig = (BaseConfig) JSONUtil.toBean(configEntity.getConfigValue(), targetClass); + // 组装SMS4J 的发送对象 + SmsBlend smsBlend = SmsFactory.getSmsBlend(messageSmsDTO.getBizCode()); + if (Objects.isNull(smsBlend)) { + Class targetClass = Arrays.stream(SUPPORT_CONFIG_CLAZZ) + .filter(config -> config.getSimpleName() + .toLowerCase() + .contains(configMap.getStr(MessageSmsDTO.Fields.supplier))) + .findFirst() + .orElseThrow(() -> new CheckedException("发送失败,短信通道配置不存在")); + BaseConfig baseConfig = (BaseConfig) JSONUtil.toBean(configEntity.getConfigValue(), targetClass); - // 如果渠道扩展参数不为空则增加特性化参数 - if (StrUtil.isNotBlank(configMap.getStr(MessageSmsDTO.Fields.extParams))) { - JSONObject extParams = configMap.getJSONObject(MessageSmsDTO.Fields.extParams); - BeanUtil.copyProperties(extParams, baseConfig); - } + // 如果渠道扩展参数不为空则增加特性化参数 + if (StrUtil.isNotBlank(configMap.getStr(MessageSmsDTO.Fields.extParams))) { + JSONObject extParams = configMap.getJSONObject(MessageSmsDTO.Fields.extParams); + BeanUtil.copyProperties(extParams, baseConfig); + } - baseConfig.setConfigId(configEntity.getConfigKey()); - SmsFactory.createSmsBlend(baseConfig); - smsBlend = SmsFactory.getSmsBlend(messageSmsDTO.getBizCode()); - } + baseConfig.setConfigId(configEntity.getConfigKey()); + SmsFactory.createSmsBlend(baseConfig); + smsBlend = SmsFactory.getSmsBlend(messageSmsDTO.getBizCode()); + } - // 异步发送短信 - SysLogDTO sysLog = SysLogUtils.getSysLog(); - Long start = System.currentTimeMillis(); - sysLog.setTenantId(TenantContextHolder.getTenantId()); - for (String mobile : messageSmsDTO.getMobiles()) { - smsBlend.sendMessageAsync(mobile, configMap.getStr(MessageSmsDTO.Fields.templateId), - messageSmsDTO.getParams(), smsResponse -> { - log.info("发送短信结果:{}", smsResponse); - sysLog.setTitle(String.format("发送短信 %s", mobile)); - sysLog.setLogType(LogTypeEnum.NORMAL.getType()); - sysLog.setServiceId(SystemConfigTypeEnum.SMS.getValue()); - sysLog.setParams(JacksonSensitiveFieldUtil.writeValueAsString(messageSmsDTO)); - sysLog.setTime(System.currentTimeMillis() - start); - sysLog.setException(JacksonSensitiveFieldUtil.writeValueAsString(smsResponse)); - remoteLogService.saveLog(sysLog); - }); - } - return R.ok(); - } + // 异步发送短信 + SysLogDTO sysLog = SysLogUtils.getSysLog(); + Long start = System.currentTimeMillis(); + sysLog.setTenantId(TenantContextHolder.getTenantId()); + for (String mobile : messageSmsDTO.getMobiles()) { + smsBlend.sendMessageAsync(mobile, configMap.getStr(MessageSmsDTO.Fields.templateId), + messageSmsDTO.getParams(), smsResponse -> { + log.info("发送短信结果:{}", smsResponse); + sysLog.setTitle(String.format("发送短信 %s", mobile)); + sysLog.setLogType(LogTypeEnum.NORMAL.getType()); + sysLog.setServiceId(SystemConfigTypeEnum.SMS.getValue()); + sysLog.setParams(JacksonSensitiveFieldUtil.writeValueAsString(messageSmsDTO)); + sysLog.setTime(System.currentTimeMillis() - start); + sysLog.setException(JacksonSensitiveFieldUtil.writeValueAsString(smsResponse)); + remoteLogService.saveLog(sysLog); + }); + } + return R.ok(); + } - /** - * 发送钩子消息 - * @param messageHookDTO 消息挂钩 DTO - * @return {@link R } - */ - @Override - public R sendHook(MessageHookDTO messageHookDTO) { + /** + * 发送钩子消息 + * + * @param messageHookDTO 消息挂钩 DTO + * @return {@link R } + */ + @Override + public R sendHook(MessageHookDTO messageHookDTO) { - // 根据业务编码获取短信通道配置 - List configEntityList = sysSystemConfigMapper - .selectList(Wrappers.lambdaQuery() - .eq(SysSystemConfigEntity::getConfigType, SystemConfigTypeEnum.WEBHOOK.getValue()) - .eq(SysSystemConfigEntity::getConfigStatus, YesNoEnum.YES.getCode()) - .eq(SysSystemConfigEntity::getConfigKey, messageHookDTO.getBizCode())); - if (CollUtil.isEmpty(configEntityList)) { - return R.failed("发送失败,短信通道配置不存在"); - } + // 根据业务编码获取短信通道配置 + List configEntityList = sysSystemConfigMapper + .selectList(Wrappers.lambdaQuery() + .eq(SysSystemConfigEntity::getConfigType, SystemConfigTypeEnum.WEBHOOK.getValue()) + .eq(SysSystemConfigEntity::getConfigStatus, YesNoEnum.YES.getCode()) + .eq(SysSystemConfigEntity::getConfigKey, messageHookDTO.getBizCode())); + if (CollUtil.isEmpty(configEntityList)) { + return R.failed("发送失败,短信通道配置不存在"); + } - SysSystemConfigEntity configEntity = configEntityList.get(0); - JSONObject configMap = JSONUtil.parseObj(configEntity.getConfigValue()); - String supplier = configMap.getStr("supplier"); + SysSystemConfigEntity configEntity = configEntityList.get(0); + JSONObject configMap = JSONUtil.parseObj(configEntity.getConfigValue()); + String supplier = configMap.getStr("supplier"); - // 根据配置创建服务实例并注册 - OaSender alarm = OaFactory.getSmsOaBlend(messageHookDTO.getBizCode()); - if (Objects.isNull(alarm)) { - if (StrUtil.equalsAnyIgnoreCase(OaType.DING_TALK.toString(), supplier)) { - DingTalkConfig dingTalkConfig = new DingTalkConfig(); - dingTalkConfig.setConfigId(messageHookDTO.getBizCode()); - dingTalkConfig.setSign(configMap.getStr("sign")); - dingTalkConfig.setTokenId(configMap.getStr("tokenId")); - OaFactory.createAndRegisterOaSender(dingTalkConfig); - } - if (StrUtil.equalsAnyIgnoreCase(OaType.BYTE_TALK.toString(), supplier)) { - ByteTalkConfig byteTalkConfig = new ByteTalkConfig(); - byteTalkConfig.setConfigId(messageHookDTO.getBizCode()); - byteTalkConfig.setSign(configMap.getStr("sign")); - byteTalkConfig.setTokenId(configMap.getStr("tokenId")); - OaFactory.createAndRegisterOaSender(byteTalkConfig); - } + // 根据配置创建服务实例并注册 + OaSender alarm = OaFactory.getSmsOaBlend(messageHookDTO.getBizCode()); + if (Objects.isNull(alarm)) { + if (StrUtil.equalsAnyIgnoreCase(OaType.DING_TALK.toString(), supplier)) { + DingTalkConfig dingTalkConfig = new DingTalkConfig(); + dingTalkConfig.setConfigId(messageHookDTO.getBizCode()); + dingTalkConfig.setSign(configMap.getStr("sign")); + dingTalkConfig.setTokenId(configMap.getStr("tokenId")); + OaFactory.createAndRegisterOaSender(dingTalkConfig); + } + if (StrUtil.equalsAnyIgnoreCase(OaType.BYTE_TALK.toString(), supplier)) { + ByteTalkConfig byteTalkConfig = new ByteTalkConfig(); + byteTalkConfig.setConfigId(messageHookDTO.getBizCode()); + byteTalkConfig.setSign(configMap.getStr("sign")); + byteTalkConfig.setTokenId(configMap.getStr("tokenId")); + OaFactory.createAndRegisterOaSender(byteTalkConfig); + } - if (StrUtil.equalsAnyIgnoreCase(OaType.WE_TALK.toString(), supplier)) { - WeTalkConfig weTalkConfig = new WeTalkConfig(); - weTalkConfig.setConfigId(messageHookDTO.getBizCode()); - weTalkConfig.setSign(configMap.getStr("sign")); - weTalkConfig.setTokenId(configMap.getStr("tokenId")); - OaFactory.createAndRegisterOaSender(weTalkConfig); - } - alarm = OaFactory.getSmsOaBlend(messageHookDTO.getBizCode()); - } + if (StrUtil.equalsAnyIgnoreCase(OaType.WE_TALK.toString(), supplier)) { + WeTalkConfig weTalkConfig = new WeTalkConfig(); + weTalkConfig.setConfigId(messageHookDTO.getBizCode()); + weTalkConfig.setSign(configMap.getStr("sign")); + weTalkConfig.setTokenId(configMap.getStr("tokenId")); + OaFactory.createAndRegisterOaSender(weTalkConfig); + } + alarm = OaFactory.getSmsOaBlend(messageHookDTO.getBizCode()); + } - Request request = new Request(); - request.setContent(messageHookDTO.getMessageContent()); - request.setTitle(messageHookDTO.getMessageTitle()); - request.setMessageUrl(messageHookDTO.getMessageUrl()); - request.setPicUrl(messageHookDTO.getPicUrl()); - request.setIsNoticeAll(messageHookDTO.isNoticeAll()); - request.setPhoneList(messageHookDTO.getPhoneList()); + Request request = new Request(); + request.setContent(messageHookDTO.getMessageContent()); + request.setTitle(messageHookDTO.getMessageTitle()); + request.setMessageUrl(messageHookDTO.getMessageUrl()); + request.setPicUrl(messageHookDTO.getPicUrl()); + request.setIsNoticeAll(messageHookDTO.isNoticeAll()); + request.setPhoneList(messageHookDTO.getPhoneList()); - SysLogDTO sysLog = SysLogUtils.getSysLog(); - sysLog.setTenantId(TenantContextHolder.getTenantId()); - Long start = System.currentTimeMillis(); - alarm.senderAsync(request, - MessageType.valueOf(String.format("%s_%s", supplier, messageHookDTO.getMessageType()).toUpperCase()), - response -> { - log.info("发送Hook结果:{}", response); - sysLog.setTitle(String.format("发送Hook %s", messageHookDTO.getBizCode())); - sysLog.setLogType(LogTypeEnum.NORMAL.getType()); - sysLog.setServiceId(SystemConfigTypeEnum.WEBHOOK.getValue()); - sysLog.setParams(JacksonSensitiveFieldUtil.writeValueAsString(messageHookDTO)); - sysLog.setTime(System.currentTimeMillis() - start); - sysLog.setException(JacksonSensitiveFieldUtil.writeValueAsString(response)); - remoteLogService.saveLog(sysLog); - }); - return R.ok(); - } + SysLogDTO sysLog = SysLogUtils.getSysLog(); + sysLog.setTenantId(TenantContextHolder.getTenantId()); + Long start = System.currentTimeMillis(); + alarm.senderAsync(request, + MessageType.valueOf(String.format("%s_%s", supplier, messageHookDTO.getMessageType()).toUpperCase()), + response -> { + log.info("发送Hook结果:{}", response); + sysLog.setTitle(String.format("发送Hook %s", messageHookDTO.getBizCode())); + sysLog.setLogType(LogTypeEnum.NORMAL.getType()); + sysLog.setServiceId(SystemConfigTypeEnum.WEBHOOK.getValue()); + sysLog.setParams(JacksonSensitiveFieldUtil.writeValueAsString(messageHookDTO)); + sysLog.setTime(System.currentTimeMillis() - start); + sysLog.setException(JacksonSensitiveFieldUtil.writeValueAsString(response)); + remoteLogService.saveLog(sysLog); + }); + return R.ok(); + } - /** - * 发送邮件 - * @param messageEmailDTO 留言内容 电子邮件 DTO - * @return {@link R } - */ - @SneakyThrows - @Override - public R sendEmail(MessageEmailDTO messageEmailDTO) { - // 根据业务编码获取短信通道配置 - List configEntityList = sysSystemConfigMapper - .selectList(Wrappers.lambdaQuery() - .eq(SysSystemConfigEntity::getConfigType, SystemConfigTypeEnum.EMAIL.getValue()) - .eq(SysSystemConfigEntity::getConfigStatus, YesNoEnum.YES.getCode()) - .eq(SysSystemConfigEntity::getConfigKey, messageEmailDTO.getBizCode())); - if (CollUtil.isEmpty(configEntityList)) { - return R.failed("发送失败,通道配置不存在"); - } - SysSystemConfigEntity configEntity = configEntityList.get(0); - JSONObject configMap = JSONUtil.parseObj(configEntity.getConfigValue()); + /** + * 发送邮件 + * + * @param messageEmailDTO 留言内容 电子邮件 DTO + * @return {@link R } + */ + @SneakyThrows + @Override + public R sendEmail(MessageEmailDTO messageEmailDTO) { + // 根据业务编码获取短信通道配置 + List configEntityList = sysSystemConfigMapper + .selectList(Wrappers.lambdaQuery() + .eq(SysSystemConfigEntity::getConfigType, SystemConfigTypeEnum.EMAIL.getValue()) + .eq(SysSystemConfigEntity::getConfigStatus, YesNoEnum.YES.getCode()) + .eq(SysSystemConfigEntity::getConfigKey, messageEmailDTO.getBizCode())); + if (CollUtil.isEmpty(configEntityList)) { + return R.failed("发送失败,通道配置不存在"); + } + SysSystemConfigEntity configEntity = configEntityList.get(0); + JSONObject configMap = JSONUtil.parseObj(configEntity.getConfigValue()); - List tempFileList = new ArrayList<>(); - if (CollUtil.isNotEmpty(messageEmailDTO.getAttachmentList())) { - TenantContextHolder.setTenantSkip(); - for (String fileName : messageEmailDTO.getAttachmentList()) { - SysFile sysFile = fileMapper - .selectOne(Wrappers.lambdaQuery().eq(SysFile::getFileName, fileName)); - S3Object s3Object = fileTemplate.getObject(sysFile.getBucketName(), sysFile.getDir(), fileName); - File file = FileUtil.file(FileUtil.getTmpDirPath(), sysFile.getOriginal()); - tempFileList.add(file); - IoUtil.copy(s3Object.getObjectContent(), FileUtil.getOutputStream(file)); - } - } + List tempFileList = new ArrayList<>(); + if (CollUtil.isNotEmpty(messageEmailDTO.getAttachmentList())) { + TenantContextHolder.setTenantSkip(); + for (String fileName : messageEmailDTO.getAttachmentList()) { + SysFile sysFile = fileMapper + .selectOne(Wrappers.lambdaQuery().eq(SysFile::getFileName, fileName)); + S3Object s3Object = fileTemplate.getObject(sysFile.getBucketName(), sysFile.getDir(), fileName); + File file = FileUtil.file(FileUtil.getTmpDirPath(), sysFile.getOriginal()); + tempFileList.add(file); + IoUtil.copy(s3Object.getObjectContent(), FileUtil.getOutputStream(file)); + } + } - MailAccount account = new MailAccount(); - account.setHost(configMap.getStr("smtpServer")); - account.setPort(configMap.getInt("port")); - account.setAuth(configMap.getBool("isAuth")); - account.setSslEnable(configMap.getBool("isSSL")); - account.setFrom(configMap.getStr("username")); - account.setUser(configMap.getStr("username")); - account.setPass(configMap.getStr("password")); + MailAccount account = new MailAccount(); + account.setHost(configMap.getStr("smtpServer")); + account.setPort(configMap.getInt("port")); + account.setAuth(configMap.getBool("isAuth")); + account.setSslEnable(configMap.getBool("isSSL")); + account.setFrom(configMap.getStr("username")); + account.setUser(configMap.getStr("username")); + account.setPass(configMap.getStr("password")); - // 渲染模板 - TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig()); - Template template = engine.getTemplate(configMap.getStr("html")); - String result = template.render(messageEmailDTO.getHtmlValues()); + // 渲染模板 + TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig()); + Template template = engine.getTemplate(configMap.getStr("html")); + String result = template.render(messageEmailDTO.getHtmlValues()); - Long start = System.currentTimeMillis(); - MailUtil.send(account, messageEmailDTO.getMailAddress(), messageEmailDTO.getCcList(), - messageEmailDTO.getBccList(), messageEmailDTO.getTitle(), result, true, - tempFileList.toArray(new File[0])); - Long end = System.currentTimeMillis(); - // 写入日志 - SysLogDTO sysLog = SysLogUtils.getSysLog(); - sysLog.setTitle(String.format("发送邮件 %s", messageEmailDTO.getTitle())); - sysLog.setLogType(LogTypeEnum.NORMAL.getType()); - sysLog.setServiceId(SystemConfigTypeEnum.EMAIL.getValue()); - sysLog.setParams(JacksonSensitiveFieldUtil.writeValueAsString(messageEmailDTO)); - sysLog.setTenantId(TenantContextHolder.getTenantId()); - sysLog.setTime(end - start); - remoteLogService.saveLog(sysLog); - // 删除临时文件 - tempFileList.forEach(FileUtil::del); - return R.ok(); - } + Long start = System.currentTimeMillis(); + MailUtil.send(account, messageEmailDTO.getMailAddress(), messageEmailDTO.getCcList(), + messageEmailDTO.getBccList(), messageEmailDTO.getTitle(), result, true, + tempFileList.toArray(new File[0])); + Long end = System.currentTimeMillis(); + // 写入日志 + SysLogDTO sysLog = SysLogUtils.getSysLog(); + sysLog.setTitle(String.format("发送邮件 %s", messageEmailDTO.getTitle())); + sysLog.setLogType(LogTypeEnum.NORMAL.getType()); + sysLog.setServiceId(SystemConfigTypeEnum.EMAIL.getValue()); + sysLog.setParams(JacksonSensitiveFieldUtil.writeValueAsString(messageEmailDTO)); + sysLog.setTenantId(TenantContextHolder.getTenantId()); + sysLog.setTime(end - start); + remoteLogService.saveLog(sysLog); + // 删除临时文件 + tempFileList.forEach(FileUtil::del); + return R.ok(); + } - /** - * 列出 Hook 业务代码 - * @param messageHookDTO 消息钩子 DTO - * @return {@link R } - */ - @Override - public R listHookBizCode(MessageHookDTO messageHookDTO) { - List configEntityList = sysSystemConfigMapper - .selectList(Wrappers.lambdaQuery() - .eq(SysSystemConfigEntity::getConfigType, messageHookDTO.getMessageType()) - .eq(SysSystemConfigEntity::getConfigStatus, YesNoEnum.YES.getCode())); - return R.ok(configEntityList); - } + /** + * 列出 Hook 业务代码 + * + * @param messageHookDTO 消息钩子 DTO + * @return {@link R } + */ + @Override + public R listHookBizCode(MessageHookDTO messageHookDTO) { + List configEntityList = sysSystemConfigMapper + .selectList(Wrappers.lambdaQuery() + .eq(SysSystemConfigEntity::getConfigType, messageHookDTO.getMessageType()) + .eq(SysSystemConfigEntity::getConfigStatus, YesNoEnum.YES.getCode())); + return R.ok(configEntityList); + } } 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 2a1f7b43c..337f37494 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 @@ -267,12 +267,21 @@ public class SysUserServiceImpl extends ServiceImpl impl 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()); + + // 校验手机号 + if (StrUtil.isNotBlank(userDto.getPhone())) { + Object codeObj = RedisUtils.get(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.SMS.getType() + + StringPool.AT + userDto.getPhone()); + if (!StrUtil.equals(userDto.getCode(), codeObj.toString())) { + return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_APP_SMS_ERROR)); + } + } + return R.ok(this.updateById(sysUser)); } From a25963323b9fde60d0c22a8c344c7d5be3a6bc50 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Tue, 4 Nov 2025 11:03:04 +0800 Subject: [PATCH 07/44] =?UTF-8?q?feat(security):=20=E5=A2=9E=E5=BC=BAOAuth?= =?UTF-8?q?2=20token=E9=AA=8C=E8=AF=81=E7=9A=84=E7=94=A8=E6=88=B7=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在PigxCustomOpaqueTokenIntrospector中增加用户账号状态检查逻辑 - 当用户账号被锁定或过期时拒绝访问并抛出InvalidBearerTokenException - 完善账号异常状态的日志记录和错误处理机制 - 保持原有客户端信息注入功能不变 --- .../PigxCustomOpaqueTokenIntrospector.java | 97 +++++++++++-------- 1 file changed, 56 insertions(+), 41 deletions(-) diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxCustomOpaqueTokenIntrospector.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxCustomOpaqueTokenIntrospector.java index 694199e32..16128fc00 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxCustomOpaqueTokenIntrospector.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxCustomOpaqueTokenIntrospector.java @@ -33,52 +33,67 @@ import java.util.Optional; @RequiredArgsConstructor public class PigxCustomOpaqueTokenIntrospector implements OpaqueTokenIntrospector { - private final OAuth2AuthorizationService authorizationService; + private final OAuth2AuthorizationService authorizationService; - @Override - public OAuth2AuthenticatedPrincipal introspect(String token) { - OAuth2Authorization oldAuthorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN); - if (Objects.isNull(oldAuthorization)) { - throw new InvalidBearerTokenException(token); - } + @Override + public OAuth2AuthenticatedPrincipal introspect(String token) { + OAuth2Authorization oldAuthorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN); + if (Objects.isNull(oldAuthorization)) { + throw new InvalidBearerTokenException(token); + } - // 客户端模式默认返回 - if (AuthorizationGrantType.CLIENT_CREDENTIALS.equals(oldAuthorization.getAuthorizationGrantType())) { - return new PigxClientCredentialsOAuth2AuthenticatedPrincipal( - Objects.requireNonNull(oldAuthorization.getAccessToken().getClaims()), - AuthorityUtils.NO_AUTHORITIES, oldAuthorization.getPrincipalName()); - } + // 客户端模式默认返回 + if (AuthorizationGrantType.CLIENT_CREDENTIALS.equals(oldAuthorization.getAuthorizationGrantType())) { + return new PigxClientCredentialsOAuth2AuthenticatedPrincipal( + Objects.requireNonNull(oldAuthorization.getAccessToken().getClaims()), + AuthorityUtils.NO_AUTHORITIES, oldAuthorization.getPrincipalName()); + } - Map userDetailsServiceMap = SpringContextHolder - .getBeansOfType(PigxUserDetailsService.class); + Map userDetailsServiceMap = SpringContextHolder + .getBeansOfType(PigxUserDetailsService.class); - Optional optional = userDetailsServiceMap.values() - .stream() - .filter(service -> service.support(Objects.requireNonNull(oldAuthorization).getRegisteredClientId(), - oldAuthorization.getAuthorizationGrantType().getValue())) - .max(Comparator.comparingInt(Ordered::getOrder)); + Optional optional = userDetailsServiceMap.values() + .stream() + .filter(service -> service.support(Objects.requireNonNull(oldAuthorization).getRegisteredClientId(), + oldAuthorization.getAuthorizationGrantType().getValue())) + .max(Comparator.comparingInt(Ordered::getOrder)); - UserDetails userDetails = null; - try { - Object principal = Objects.requireNonNull(oldAuthorization).getAttributes().get(Principal.class.getName()); - UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = (UsernamePasswordAuthenticationToken) principal; - Object tokenPrincipal = usernamePasswordAuthenticationToken.getPrincipal(); - userDetails = optional.get().loadUserByUser((PigxUser) tokenPrincipal); - } - catch (UsernameNotFoundException notFoundException) { - log.warn("用户不不存在 {}", notFoundException.getLocalizedMessage()); - throw notFoundException; - } - catch (Exception ex) { - log.error("资源服务器 introspect Token error {}", ex.getLocalizedMessage()); - } + UserDetails userDetails = null; + try { + Object principal = Objects.requireNonNull(oldAuthorization).getAttributes().get(Principal.class.getName()); + UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = (UsernamePasswordAuthenticationToken) principal; + Object tokenPrincipal = usernamePasswordAuthenticationToken.getPrincipal(); + userDetails = optional.get().loadUserByUser((PigxUser) tokenPrincipal); + } catch (UsernameNotFoundException notFoundException) { + log.warn("用户不不存在 {}", notFoundException.getLocalizedMessage()); + throw notFoundException; + } catch (Exception ex) { + log.error("资源服务器 introspect Token error {}", ex.getLocalizedMessage()); + } - // 注入客户端信息,方便上下文中获取 - PigxUser pigxUser = (PigxUser) userDetails; - Objects.requireNonNull(pigxUser) - .getAttributes() - .put(SecurityConstants.CLIENT_ID, oldAuthorization.getRegisteredClientId()); - return pigxUser; - } + // 检查用户账号状态 + if (Objects.nonNull(userDetails)) { + boolean isLocked = !userDetails.isAccountNonLocked(); + boolean isExpired = !userDetails.isAccountNonExpired(); + + if (isLocked) { + log.warn("用户账号 {} 已被锁定,拒绝访问", userDetails.getUsername()); + throw new InvalidBearerTokenException(token); + } + + if (isExpired) { + log.warn("用户账号 {} 已过期,拒绝访问", userDetails.getUsername()); + throw new InvalidBearerTokenException(token); + } + } + + // 注入客户端信息,方便上下文中获取 + PigxUser pigxUser = (PigxUser) userDetails; + + Objects.requireNonNull(pigxUser) + .getAttributes() + .put(SecurityConstants.CLIENT_ID, oldAuthorization.getRegisteredClientId()); + return pigxUser; + } } From 6de57dfab1b6c14ebe0544e1f28c854a874fa7f8 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Wed, 5 Nov 2025 19:05:09 +0800 Subject: [PATCH 08/44] =?UTF-8?q?fix(upms):=20=E4=BF=AE=E5=A4=8D=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=99=BB=E5=BD=95=E5=A4=B1=E8=B4=A5=E6=AC=A1=E6=95=B0?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E6=B8=85=E7=90=86=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在用户登录成功时清理对应的登录失败次数缓存 - 使用RedisUtils删除格式化的缓存key - 防止因缓存残留导致用户被错误锁定 --- .../pigx/admin/service/impl/SysUserServiceImpl.java | 5 +++++ 1 file changed, 5 insertions(+) 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 337f37494..bd36cb265 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 @@ -347,6 +347,11 @@ public class SysUserServiceImpl extends ServiceImpl impl }); } + // 同时清理用户失败次数的缓存 + String key = String.format("%s%s:%s", CacheConstants.GLOBALLY + , CacheConstants.LOGIN_ERROR_TIMES, userDto.getUsername()); + RedisUtils.delete(key); + return Boolean.TRUE; } From c0112af1890b69ff0b3423f4bd1821112102ea03 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Fri, 7 Nov 2025 15:37:03 +0800 Subject: [PATCH 09/44] =?UTF-8?q?fix(nacos):=20=E6=9B=B4=E6=96=B0nacos?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E4=B8=BA3.1.0-bugfix=E4=BB=A5=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E5=B7=B2=E7=9F=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 99daf9db958a172cdefa6e4aa81fa8c4c06697b7) --- pigx-register/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pigx-register/pom.xml b/pigx-register/pom.xml index 52e9d4cf8..3925ce774 100644 --- a/pigx-register/pom.xml +++ b/pigx-register/pom.xml @@ -27,7 +27,7 @@ nacos 注册配置中心 - 3.1.0 + 3.1.0-bugfix From 1a8d0c0898f47f1d8e436b1b28823b56cb29c942 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Sat, 8 Nov 2025 23:27:17 +0800 Subject: [PATCH 10/44] =?UTF-8?q?fix(SysDeptMapper):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E7=BB=93=E6=9E=9C=E7=9A=84=E6=8E=92=E5=BA=8F?= =?UTF-8?q?=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pigx-upms-biz/src/main/resources/mapper/SysDeptMapper.xml | 1 - 1 file changed, 1 deletion(-) 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 10ddc04c0..0ea564cea 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,6 +30,5 @@ 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 From 6d837c6f1c6cb1a52255276362db0119a7a83ada Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Mon, 10 Nov 2025 22:46:31 +0800 Subject: [PATCH 11/44] =?UTF-8?q?fix(ImageCodeEndpoint):=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E9=AA=8C=E8=AF=81=E7=A0=81=E7=B1=BB=E5=9E=8B=E4=B8=BA?= =?UTF-8?q?=E5=8A=A8=E6=80=81=E5=8F=82=E6=95=B0=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=A4=9A=E7=A7=8D=E9=AA=8C=E8=AF=81=E7=A0=81=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=20fix(ValidateCodeFilter):=20=E6=9B=B4=E6=96=B0=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E7=A0=81=E6=A0=A1=E9=AA=8C=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=BB=91=E5=9D=97=E5=92=8C=E7=82=B9=E5=87=BB?= =?UTF-8?q?=E6=96=87=E5=AD=97=E9=AA=8C=E8=AF=81=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pigx/auth/endpoint/ImageCodeEndpoint.java | 7 +- .../support/filter/ValidateCodeFilter.java | 203 +++++++++--------- .../common/core/constant/CommonConstants.java | 181 ++++++++-------- 3 files changed, 198 insertions(+), 193 deletions(-) diff --git a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/ImageCodeEndpoint.java b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/ImageCodeEndpoint.java index 1088b677c..c38778b9a 100644 --- a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/ImageCodeEndpoint.java +++ b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/ImageCodeEndpoint.java @@ -4,7 +4,6 @@ import cn.hutool.core.lang.Validator; import com.anji.captcha.model.vo.CaptchaVO; import com.anji.captcha.service.CaptchaService; import com.pig4cloud.pigx.common.core.constant.CacheConstants; -import com.pig4cloud.pigx.common.core.constant.CommonConstants; import com.pig4cloud.pigx.common.core.constant.SecurityConstants; import com.pig4cloud.pigx.common.core.util.R; import com.pig4cloud.pigx.common.core.util.SpringContextHolder; @@ -59,9 +58,9 @@ public class ImageCodeEndpoint { */ @SneakyThrows @GetMapping("/create") - public R behavior() { + public R behavior(String captchaType) { CaptchaVO vo = new CaptchaVO(); - vo.setCaptchaType(CommonConstants.IMAGE_CODE_TYPE); + vo.setCaptchaType(captchaType); CaptchaService captchaService = SpringContextHolder.getBean(CaptchaService.class); return R.ok(captchaService.get(vo)); } @@ -75,7 +74,7 @@ public class ImageCodeEndpoint { CaptchaVO vo = new CaptchaVO(); vo.setPointJson(request.getParameter("pointJson")); vo.setToken(request.getParameter("token")); - vo.setCaptchaType(CommonConstants.IMAGE_CODE_TYPE); + vo.setCaptchaType(request.getParameter("captchaType")); CaptchaService captchaService = SpringContextHolder.getBean(CaptchaService.class); return R.ok(captchaService.check(vo)); diff --git a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/filter/ValidateCodeFilter.java b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/filter/ValidateCodeFilter.java index bce6da8e7..38cbd03f8 100644 --- a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/filter/ValidateCodeFilter.java +++ b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/filter/ValidateCodeFilter.java @@ -45,129 +45,130 @@ import java.io.IOException; @RequiredArgsConstructor public class ValidateCodeFilter extends OncePerRequestFilter { - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException { + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { - String requestUrl = request.getServletPath(); + String requestUrl = request.getServletPath(); - // 不是登录URL 请求直接跳过 - if (!SecurityConstants.OAUTH_TOKEN_URL.equals(requestUrl)) { - filterChain.doFilter(request, response); - return; - } + // 不是登录URL 请求直接跳过 + if (!SecurityConstants.OAUTH_TOKEN_URL.equals(requestUrl)) { + filterChain.doFilter(request, response); + return; + } - // 如果登录URL 但是刷新token的请求,直接向下执行 - String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE); - if (StrUtil.containsAny(grantType, SecurityConstants.REFRESH_TOKEN)) { - filterChain.doFilter(request, response); - return; - } + // 如果登录URL 但是刷新token的请求,直接向下执行 + String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE); + if (StrUtil.containsAny(grantType, SecurityConstants.REFRESH_TOKEN)) { + filterChain.doFilter(request, response); + return; + } - // mobile模式, 如果请求不包含mobile 参数直接 - String mobile = request.getParameter(SecurityConstants.GRANT_MOBILE); - if (StrUtil.equals(SecurityConstants.GRANT_MOBILE, grantType) && StrUtil.isBlank(mobile)) { - throw new OAuth2AuthenticationException(SecurityConstants.GRANT_MOBILE); - } + // mobile模式, 如果请求不包含mobile 参数直接 + String mobile = request.getParameter(SecurityConstants.GRANT_MOBILE); + if (StrUtil.equals(SecurityConstants.GRANT_MOBILE, grantType) && StrUtil.isBlank(mobile)) { + throw new OAuth2AuthenticationException(SecurityConstants.GRANT_MOBILE); + } - // mobile模式, 社交登录模式不校验验证码直接跳过 - if (StrUtil.equals(SecurityConstants.GRANT_MOBILE, grantType) && !StrUtil.contains(mobile, "SMS")) { - filterChain.doFilter(request, response); - return; - } + // mobile模式, 社交登录模式不校验验证码直接跳过 + if (StrUtil.equals(SecurityConstants.GRANT_MOBILE, grantType) && !StrUtil.contains(mobile, "SMS")) { + filterChain.doFilter(request, response); + return; + } - // 判断客户端是否跳过检验 - if (!isCheckCaptchaClient(request)) { - filterChain.doFilter(request, response); - return; - } + // 判断客户端是否跳过检验 + if (!isCheckCaptchaClient(request)) { + filterChain.doFilter(request, response); + return; + } - // 校验验证码 1. 客户端开启验证码 2. 短信模式 - try { - checkCode(); - filterChain.doFilter(request, response); + // 校验验证码 1. 客户端开启验证码 2. 短信模式 + try { + checkCode(); + filterChain.doFilter(request, response); } catch (ValidateCodeException validateCodeException) { - throw new OAuth2AuthenticationException(validateCodeException.getMessage()); - } - } + throw new OAuth2AuthenticationException(validateCodeException.getMessage()); + } + } - /** - * 校验验证码 - */ - private void checkCode() throws ValidateCodeException { - HttpServletRequest request = WebUtils.getRequest(); - String code = request.getParameter("code"); + /** + * 校验验证码 + */ + private void checkCode() throws ValidateCodeException { + HttpServletRequest request = WebUtils.getRequest(); + String code = request.getParameter("code"); - if (StrUtil.isBlank(code)) { - throw new ValidateCodeException("验证码不能为空"); - } + if (StrUtil.isBlank(code)) { + throw new ValidateCodeException("验证码不能为空"); + } - String randomStr = request.getParameter("randomStr"); + String randomStr = request.getParameter("randomStr"); - // https://gitee.com/log4j/pig/issues/IWA0D - String mobile = request.getParameter("mobile"); - if (StrUtil.isNotBlank(mobile)) { - randomStr = mobile; - } + // https://gitee.com/log4j/pig/issues/IWA0D + String mobile = request.getParameter("mobile"); + if (StrUtil.isNotBlank(mobile)) { + randomStr = mobile; + } - // 若是滑块登录 - if (CommonConstants.IMAGE_CODE_TYPE.equalsIgnoreCase(randomStr)) { - CaptchaService captchaService = SpringContextHolder.getBean(CaptchaService.class); - CaptchaVO vo = new CaptchaVO(); - vo.setCaptchaVerification(code); - vo.setCaptchaType(CommonConstants.IMAGE_CODE_TYPE); - if (!captchaService.verification(vo).isSuccess()) { - throw new ValidateCodeException("验证码不能为空"); - } - return; - } + // 若是滑块登录 + if (StrUtil.equalsAnyIgnoreCase(randomStr, CommonConstants.IMAGE_CODE_BLOCK_PUZZLE, CommonConstants.IMAGE_CODE_CLICK_WORD)) { + CaptchaService captchaService = SpringContextHolder.getBean(CaptchaService.class); + CaptchaVO vo = new CaptchaVO(); + vo.setCaptchaVerification(code); + vo.setCaptchaType(randomStr); + if (!captchaService.verification(vo).isSuccess()) { + throw new ValidateCodeException("验证码不能为空"); + } + return; + } - String key = CacheConstants.DEFAULT_CODE_KEY + randomStr; - if (!RedisUtils.hasKey(key)) { - throw new ValidateCodeException("验证码不合法"); - } + String key = CacheConstants.DEFAULT_CODE_KEY + randomStr; + if (!RedisUtils.hasKey(key)) { + throw new ValidateCodeException("验证码不合法"); + } - String codeObj = RedisUtils.get(key); + String codeObj = RedisUtils.get(key); - if (codeObj == null) { - throw new ValidateCodeException("验证码不合法"); - } + if (codeObj == null) { + throw new ValidateCodeException("验证码不合法"); + } - if (StrUtil.isBlank(codeObj)) { - RedisUtils.delete(key); - throw new ValidateCodeException("验证码不合法"); - } + if (StrUtil.isBlank(codeObj)) { + RedisUtils.delete(key); + throw new ValidateCodeException("验证码不合法"); + } - if (!StrUtil.equals(codeObj, code)) { - RedisUtils.delete(key); - throw new ValidateCodeException("验证码不合法"); - } + if (!StrUtil.equals(codeObj, code)) { + RedisUtils.delete(key); + throw new ValidateCodeException("验证码不合法"); + } - RedisUtils.delete(key); - } + RedisUtils.delete(key); + } - /** - * 是否需要校验客户端,根据client 查询客户端配置 - * @param request 请求 - * @return true 需要校验, false 不需要校验 - */ - private boolean isCheckCaptchaClient(HttpServletRequest request) { - String header = request.getHeader(HttpHeaders.AUTHORIZATION); - String clientId = WebUtils.extractClientId(header).orElse(null); - // 获取租户拼接区分租户的key - String tenantId = request.getHeader(CommonConstants.TENANT_ID); - String key = String.format("%s:%s:%s", StrUtil.isBlank(tenantId) ? CommonConstants.TENANT_ID_1 : tenantId, - CacheConstants.CLIENT_FLAG, clientId); + /** + * 是否需要校验客户端,根据client 查询客户端配置 + * + * @param request 请求 + * @return true 需要校验, false 不需要校验 + */ + private boolean isCheckCaptchaClient(HttpServletRequest request) { + String header = request.getHeader(HttpHeaders.AUTHORIZATION); + String clientId = WebUtils.extractClientId(header).orElse(null); + // 获取租户拼接区分租户的key + String tenantId = request.getHeader(CommonConstants.TENANT_ID); + String key = String.format("%s:%s:%s", StrUtil.isBlank(tenantId) ? CommonConstants.TENANT_ID_1 : tenantId, + CacheConstants.CLIENT_FLAG, clientId); - String val = RedisUtils.get(key); + String val = RedisUtils.get(key); - // 当配置不存在时,不用校验 - if (val == null) { - return false; - } + // 当配置不存在时,不用校验 + if (val == null) { + return false; + } - JSONObject information = JSONUtil.parseObj(val); - return !StrUtil.equals(CaptchaFlagTypeEnum.OFF.getType(), information.getStr(CommonConstants.CAPTCHA_FLAG)); - } + JSONObject information = JSONUtil.parseObj(val); + return !StrUtil.equals(CaptchaFlagTypeEnum.OFF.getType(), information.getStr(CommonConstants.CAPTCHA_FLAG)); + } } diff --git a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/constant/CommonConstants.java b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/constant/CommonConstants.java index 49dafb451..ef21efe45 100644 --- a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/constant/CommonConstants.java +++ b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/constant/CommonConstants.java @@ -25,114 +25,119 @@ package com.pig4cloud.pigx.common.core.constant; */ public interface CommonConstants { - /** - * header 中租户ID - */ - String TENANT_ID = "TENANT-ID"; + /** + * header 中租户ID + */ + String TENANT_ID = "TENANT-ID"; - /** - * header 中版本信息 - */ - String VERSION = "VERSION"; + /** + * header 中版本信息 + */ + String VERSION = "VERSION"; - /** - * 租户ID - */ - Long TENANT_ID_1 = 1L; + /** + * 租户ID + */ + Long TENANT_ID_1 = 1L; - /** - * 删除 - */ - String STATUS_DEL = "1"; + /** + * 删除 + */ + String STATUS_DEL = "1"; - /** - * 正常 - */ - String STATUS_NORMAL = "0"; + /** + * 正常 + */ + String STATUS_NORMAL = "0"; - /** - * 锁定 - */ - String STATUS_LOCK = "9"; + /** + * 锁定 + */ + String STATUS_LOCK = "9"; - /** - * 菜单树根节点 - */ - Long MENU_TREE_ROOT_ID = -1L; + /** + * 菜单树根节点 + */ + Long MENU_TREE_ROOT_ID = -1L; - /** - * 编码 - */ - String UTF8 = "UTF-8"; + /** + * 编码 + */ + String UTF8 = "UTF-8"; - /** - * 前端工程名 - */ - String FRONT_END_PROJECT = "pigx-ui"; + /** + * 前端工程名 + */ + String FRONT_END_PROJECT = "pigx-ui"; - /** - * 移动端工程名 - */ - String UNI_END_PROJECT = "pigx-app"; + /** + * 移动端工程名 + */ + String UNI_END_PROJECT = "pigx-app"; - /** - * 后端工程名 - */ - String BACK_END_PROJECT = "pigx"; + /** + * 后端工程名 + */ + String BACK_END_PROJECT = "pigx"; - /** - * 公共参数 - */ - String PIG_PUBLIC_PARAM_KEY = "PIG_PUBLIC_PARAM_KEY"; + /** + * 公共参数 + */ + String PIG_PUBLIC_PARAM_KEY = "PIG_PUBLIC_PARAM_KEY"; - /** - * 成功标记 - */ - Integer SUCCESS = 0; + /** + * 成功标记 + */ + Integer SUCCESS = 0; - /** - * 失败标记 - */ - Integer FAIL = 1; + /** + * 失败标记 + */ + Integer FAIL = 1; - /** - * 默认存储bucket - */ - String BUCKET_NAME = "lengleng"; + /** + * 默认存储bucket + */ + String BUCKET_NAME = "lengleng"; - /** - * 滑块验证码 - */ - String IMAGE_CODE_TYPE = "blockPuzzle"; + /** + * 滑块验证码 + */ + String IMAGE_CODE_BLOCK_PUZZLE = "blockPuzzle"; - /** - * 验证码开关 - */ - String CAPTCHA_FLAG = "captcha_flag"; + /** + * 点击文字验证码类型 + */ + String IMAGE_CODE_CLICK_WORD = "clickWord"; - /** - * 密码传输是否加密 - */ - String ENC_FLAG = "enc_flag"; + /** + * 验证码开关 + */ + String CAPTCHA_FLAG = "captcha_flag"; - /** - * 客户端允许同时在线数量 - */ - String ONLINE_QUANTITY = "online_quantity"; + /** + * 密码传输是否加密 + */ + String ENC_FLAG = "enc_flag"; - /** - * 请求开始时间 - */ - String REQUEST_START_TIME = "REQUEST-START-TIME"; + /** + * 客户端允许同时在线数量 + */ + String ONLINE_QUANTITY = "online_quantity"; - /** - * 当前页 - */ - String CURRENT = "current"; + /** + * 请求开始时间 + */ + String REQUEST_START_TIME = "REQUEST-START-TIME"; - /** - * size - */ - String SIZE = "size"; + /** + * 当前页 + */ + String CURRENT = "current"; + + /** + * size + */ + String SIZE = "size"; } From c88762a433def1ef4f539e0a81b0cb9d40af39ab Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Tue, 11 Nov 2025 10:58:54 +0800 Subject: [PATCH 12/44] =?UTF-8?q?fix(SysUserDept):=20=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=E7=94=A8=E6=88=B7ID=E5=92=8C=E9=83=A8=E9=97=A8ID=E7=9A=84Schem?= =?UTF-8?q?a=E6=8F=8F=E8=BF=B0=E4=B8=BA=E6=AD=A3=E7=A1=AE=E7=9A=84?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/pig4cloud/pigx/admin/api/entity/SysUserDept.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUserDept.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUserDept.java index 225df6d73..a339d923b 100644 --- a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUserDept.java +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/entity/SysUserDept.java @@ -41,13 +41,13 @@ public class SysUserDept extends Model { /** * 用户ID */ - @Schema(description = "用户id") + @Schema(description = "用户ID") private Long userId; /** - * 岗位ID + * 部门ID */ - @Schema(description = "岗位id") + @Schema(description = "部门ID") private Long deptId; } From 470f5e7fdb416e33b953da6cc5643bee1ae09626 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Wed, 12 Nov 2025 11:44:51 +0800 Subject: [PATCH 13/44] =?UTF-8?q?feat(db):=20=E5=9C=A8sys=5Fuser=E8=A1=A8?= =?UTF-8?q?=E6=96=B0=E5=A2=9Edept=5Fid=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加主部门ID字段用于关联用户与部门关系 - 更新admin用户的初始化数据包含dept_id值 --- db/0pigxx_boot.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/db/0pigxx_boot.sql b/db/0pigxx_boot.sql index 4affc6981..8f4053924 100644 --- a/db/0pigxx_boot.sql +++ b/db/0pigxx_boot.sql @@ -1344,6 +1344,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) NOT NULL DEFAULT '0' 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='用户表'; @@ -1352,7 +1353,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; -- ---------------------------- From 5123e3f1163b1caf8680c8fa0212f9b71222e2cc Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Wed, 12 Nov 2025 17:15:46 +0800 Subject: [PATCH 14/44] =?UTF-8?q?feat(security):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=8D=95=E4=BD=93=E4=B8=80=E9=94=AE=20CORS=E8=B7=A8=E5=9F=9F?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=94=AF=E6=8C=81=20security.cors.enabled?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增PigxBootCorsProperties配置类用于管理CORS跨域参数 - 在AuthorizationServer和ResourceServer中集成CORS配置逻辑 - 支持通过security.cors.enabled开关控制跨域功能 - 提供allowedOriginPatterns等细粒度跨域参数配置 --- .../AuthorizationServerConfiguration.java | 206 ++++++++++-------- .../component/PigxBootCorsProperties.java | 70 ++++++ .../PigxResourceServerConfiguration.java | 19 ++ ...ot.autoconfigure.AutoConfiguration.imports | 2 + 4 files changed, 204 insertions(+), 93 deletions(-) create mode 100644 pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxBootCorsProperties.java diff --git a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/config/AuthorizationServerConfiguration.java b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/config/AuthorizationServerConfiguration.java index 54a5c99fb..e7f454003 100644 --- a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/config/AuthorizationServerConfiguration.java +++ b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/config/AuthorizationServerConfiguration.java @@ -30,6 +30,7 @@ import com.pig4cloud.pigx.auth.support.password.OAuth2ResourceOwnerPasswordAuthe import com.pig4cloud.pigx.auth.support.sms.OAuth2ResourceOwnerSmsAuthenticationConverter; import com.pig4cloud.pigx.auth.support.sms.OAuth2ResourceOwnerSmsAuthenticationProvider; import com.pig4cloud.pigx.common.core.constant.SecurityConstants; +import com.pig4cloud.pigx.common.security.component.PigxBootCorsProperties; import com.pig4cloud.pigx.common.security.handler.FormAuthenticationFailureHandler; import com.pig4cloud.pigx.common.security.handler.SsoLogoutSuccessHandler; import lombok.RequiredArgsConstructor; @@ -56,6 +57,8 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand import org.springframework.security.web.authentication.DelegatingAuthenticationConverter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import java.util.Arrays; @@ -69,114 +72,131 @@ import java.util.Arrays; @RequiredArgsConstructor public class AuthorizationServerConfiguration { - private final OAuth2AuthorizationService authorizationService; + private final OAuth2AuthorizationService authorizationService; - private final PasswordDecoderFilter passwordDecoderFilter; + private final PasswordDecoderFilter passwordDecoderFilter; - private final ValidateCodeFilter validateCodeFilter; + private final ValidateCodeFilter validateCodeFilter; - @Bean - @Order(Ordered.HIGHEST_PRECEDENCE) - public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http, - PigxAuthenticationSuccessEventHandler successEventHandler, - PigxAuthenticationFailureEventHandler failureEventHandler) throws Exception { - // 配置授权服务器的安全策略,只有/oauth2/**的请求才会走如下的配置 - http.securityMatcher("/oauth2/**"); - OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer(); + private final PigxBootCorsProperties corsProperties; - // 增加验证码过滤器 - http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class); - // 增加密码解密过滤器 - http.addFilterBefore(passwordDecoderFilter, UsernamePasswordAuthenticationFilter.class); + @Bean + @Order(Ordered.HIGHEST_PRECEDENCE) + public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http, + PigxAuthenticationSuccessEventHandler successEventHandler, + PigxAuthenticationFailureEventHandler failureEventHandler) throws Exception { + // 配置授权服务器的安全策略,只有/oauth2/**的请求才会走如下的配置 + http.securityMatcher("/oauth2/**"); + OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer(); - http.with(authorizationServerConfigurer.tokenEndpoint((tokenEndpoint) -> {// 个性化认证授权端点 - tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter()) // 注入自定义的授权认证Converter - .accessTokenResponseHandler(successEventHandler) // 登录成功处理器 - .errorResponseHandler(failureEventHandler);// 登录失败处理器 - }).clientAuthentication(oAuth2ClientAuthenticationConfigurer -> // 个性化客户端认证 - oAuth2ClientAuthenticationConfigurer.errorResponseHandler(failureEventHandler))// 处理客户端认证异常 - .authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint// 授权码端点个性化confirm页面 - .consentPage(SecurityConstants.CUSTOM_CONSENT_PAGE_URI)), Customizer.withDefaults()) - .authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated()); + // 增加验证码过滤器 + http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class); + // 增加密码解密过滤器 + http.addFilterBefore(passwordDecoderFilter, UsernamePasswordAuthenticationFilter.class); - // 设置 Token 存储的策略 - http.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现 - .authorizationServerSettings( - AuthorizationServerSettings.builder().issuer(SecurityConstants.PIGX_LICENSE).build()), - Customizer.withDefaults()); + http.with(authorizationServerConfigurer.tokenEndpoint((tokenEndpoint) -> {// 个性化认证授权端点 + tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter()) // 注入自定义的授权认证Converter + .accessTokenResponseHandler(successEventHandler) // 登录成功处理器 + .errorResponseHandler(failureEventHandler);// 登录失败处理器 + }).clientAuthentication(oAuth2ClientAuthenticationConfigurer -> // 个性化客户端认证 + oAuth2ClientAuthenticationConfigurer.errorResponseHandler(failureEventHandler))// 处理客户端认证异常 + .authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint// 授权码端点个性化confirm页面 + .consentPage(SecurityConstants.CUSTOM_CONSENT_PAGE_URI)), Customizer.withDefaults()) + .authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated()); - // 设置授权码模式登录页面 - http.with(new FormIdentityLoginConfigurer(), Customizer.withDefaults()); - DefaultSecurityFilterChain securityFilterChain = http.build(); + // 设置 Token 存储的策略 + http.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现 + .authorizationServerSettings( + AuthorizationServerSettings.builder().issuer(SecurityConstants.PIGX_LICENSE).build()), + Customizer.withDefaults()); - // 注入自定义授权模式实现 - addCustomOAuth2GrantAuthenticationProvider(http); - return securityFilterChain; - } + // 设置授权码模式登录页面 + http.with(new FormIdentityLoginConfigurer(), Customizer.withDefaults()); - /** - * 令牌生成规则实现
- * client:username:uuid - * - * @return OAuth2TokenGenerator - */ - @Bean - public OAuth2TokenGenerator oAuth2TokenGenerator() { - CustomeOAuth2AccessTokenGenerator accessTokenGenerator = new CustomeOAuth2AccessTokenGenerator(); - // 注入Token 增加关联用户信息 - accessTokenGenerator.setAccessTokenCustomizer(new CustomeOAuth2TokenCustomizer()); - return new DelegatingOAuth2TokenGenerator(accessTokenGenerator, new OAuth2RefreshTokenGenerator()); - } + // 单体版本:根据配置启用CORS跨域支持(仅在security.cors.enabled=true时生效) + if (Boolean.TRUE.equals(corsProperties.getEnabled())) { + http.cors(cors -> cors.configurationSource(request -> { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration corsConfiguration = new CorsConfiguration(); + corsProperties.getAllowedOriginPatterns().forEach(corsConfiguration::addAllowedOriginPattern); + corsProperties.getAllowedHeaders().forEach(corsConfiguration::addAllowedHeader); + corsProperties.getAllowedMethods().forEach(corsConfiguration::addAllowedMethod); + corsConfiguration.setAllowCredentials(corsProperties.getAllowCredentials()); + source.registerCorsConfiguration(corsProperties.getPathPattern(), corsConfiguration); + return corsConfiguration; + })); + } - @Bean - public AuthenticationFailureHandler authenticationFailureHandler() { - return new FormAuthenticationFailureHandler(); - } + DefaultSecurityFilterChain securityFilterChain = http.build(); - @Bean - public LogoutSuccessHandler logoutSuccessHandler() { - return new SsoLogoutSuccessHandler(); - } + // 注入自定义授权模式实现 + addCustomOAuth2GrantAuthenticationProvider(http); + return securityFilterChain; + } - /** - * request -> xToken 注入请求转换器 - * - * @return DelegatingAuthenticationConverter - */ - @Bean - public AuthenticationConverter accessTokenRequestConverter() { - return new DelegatingAuthenticationConverter( - Arrays.asList(new OAuth2ResourceOwnerPasswordAuthenticationConverter(), - new OAuth2ResourceOwnerSmsAuthenticationConverter(), - new PigxOAuth2RefreshTokenAuthenticationConverter(), - new OAuth2ClientCredentialsAuthenticationConverter(), - new OAuth2AuthorizationCodeAuthenticationConverter(), - new OAuth2AuthorizationCodeRequestAuthenticationConverter())); - } + /** + * 令牌生成规则实现
+ * client:username:uuid + * + * @return OAuth2TokenGenerator + */ + @Bean + public OAuth2TokenGenerator oAuth2TokenGenerator() { + CustomeOAuth2AccessTokenGenerator accessTokenGenerator = new CustomeOAuth2AccessTokenGenerator(); + // 注入Token 增加关联用户信息 + accessTokenGenerator.setAccessTokenCustomizer(new CustomeOAuth2TokenCustomizer()); + return new DelegatingOAuth2TokenGenerator(accessTokenGenerator, new OAuth2RefreshTokenGenerator()); + } - /** - * 注入授权模式实现提供方 - *

- * 1. 密码模式
- * 2. 短信登录
- */ - @SuppressWarnings("unchecked") - private void addCustomOAuth2GrantAuthenticationProvider(HttpSecurity http) { - AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class); - OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class); + @Bean + public AuthenticationFailureHandler authenticationFailureHandler() { + return new FormAuthenticationFailureHandler(); + } - OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider = new OAuth2ResourceOwnerPasswordAuthenticationProvider( - authenticationManager, authorizationService, oAuth2TokenGenerator()); + @Bean + public LogoutSuccessHandler logoutSuccessHandler() { + return new SsoLogoutSuccessHandler(); + } - OAuth2ResourceOwnerSmsAuthenticationProvider resourceOwnerSmsAuthenticationProvider = new OAuth2ResourceOwnerSmsAuthenticationProvider( - authenticationManager, authorizationService, oAuth2TokenGenerator()); + /** + * request -> xToken 注入请求转换器 + * + * @return DelegatingAuthenticationConverter + */ + @Bean + public AuthenticationConverter accessTokenRequestConverter() { + return new DelegatingAuthenticationConverter( + Arrays.asList(new OAuth2ResourceOwnerPasswordAuthenticationConverter(), + new OAuth2ResourceOwnerSmsAuthenticationConverter(), + new PigxOAuth2RefreshTokenAuthenticationConverter(), + new OAuth2ClientCredentialsAuthenticationConverter(), + new OAuth2AuthorizationCodeAuthenticationConverter(), + new OAuth2AuthorizationCodeRequestAuthenticationConverter())); + } - // 处理 UsernamePasswordAuthenticationToken - http.authenticationProvider(new PigxDaoAuthenticationProvider()); - // 处理 OAuth2ResourceOwnerPasswordAuthenticationToken - http.authenticationProvider(resourceOwnerPasswordAuthenticationProvider); - // 处理 OAuth2ResourceOwnerSmsAuthenticationToken - http.authenticationProvider(resourceOwnerSmsAuthenticationProvider); - } + /** + * 注入授权模式实现提供方 + *

+ * 1. 密码模式
+ * 2. 短信登录
+ */ + @SuppressWarnings("unchecked") + private void addCustomOAuth2GrantAuthenticationProvider(HttpSecurity http) { + AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class); + OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class); + + OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider = new OAuth2ResourceOwnerPasswordAuthenticationProvider( + authenticationManager, authorizationService, oAuth2TokenGenerator()); + + OAuth2ResourceOwnerSmsAuthenticationProvider resourceOwnerSmsAuthenticationProvider = new OAuth2ResourceOwnerSmsAuthenticationProvider( + authenticationManager, authorizationService, oAuth2TokenGenerator()); + + // 处理 UsernamePasswordAuthenticationToken + http.authenticationProvider(new PigxDaoAuthenticationProvider()); + // 处理 OAuth2ResourceOwnerPasswordAuthenticationToken + http.authenticationProvider(resourceOwnerPasswordAuthenticationProvider); + // 处理 OAuth2ResourceOwnerSmsAuthenticationToken + http.authenticationProvider(resourceOwnerSmsAuthenticationProvider); + } } diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxBootCorsProperties.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxBootCorsProperties.java new file mode 100644 index 000000000..204d82d65 --- /dev/null +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxBootCorsProperties.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.pig4cloud.pigx.common.security.component; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author lengleng + * @date 2025-11-12 + *

+ * CORS 跨域资源共享配置属性 + */ +@Data +@ConfigurationProperties(prefix = "security.cors") +public class PigxBootCorsProperties { + + /** + * 是否启用CORS跨域支持 + */ + private Boolean enabled = false; + + /** + * 允许的源模式列表,支持通配符 + * 例如:http://localhost:*, https://*.example.com + */ + private List allowedOriginPatterns = new ArrayList<>(List.of("*")); + + /** + * 允许的请求头列表 + * 默认允许所有请求头 + */ + private List allowedHeaders = new ArrayList<>(List.of("*")); + + /** + * 允许的HTTP方法列表 + * 默认允许常用的HTTP方法 + */ + private List allowedMethods = new ArrayList<>(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH")); + + /** + * 是否允许携带凭证(如Cookie) + * 当设置为true时,allowedOriginPatterns不能使用通配符* + */ + private Boolean allowCredentials = true; + + /** + * 应用CORS配置的路径模式 + * 默认应用到所有路径 + */ + private String pathPattern = "/**"; + +} diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxResourceServerConfiguration.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxResourceServerConfiguration.java index 0ce1e1d03..7ea3506ad 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxResourceServerConfiguration.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxResourceServerConfiguration.java @@ -32,6 +32,8 @@ import org.springframework.security.core.annotation.AnnotationTemplateExpression import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; /** * @author lengleng @@ -52,6 +54,8 @@ public class PigxResourceServerConfiguration { @Lazy private final PermitAllUrlProperties permitAllUrl; + private final PigxBootCorsProperties corsProperties; + @Bean @Order(Ordered.HIGHEST_PRECEDENCE) SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { @@ -71,6 +75,20 @@ public class PigxResourceServerConfiguration { .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) .csrf(AbstractHttpConfigurer::disable); + // 仅支持单体版本:根据配置启用CORS跨域支持(仅在security.cors.enabled=true时生效) + if (Boolean.TRUE.equals(corsProperties.getEnabled())) { + http.cors(cors -> cors.configurationSource(request -> { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration corsConfiguration = new CorsConfiguration(); + corsProperties.getAllowedOriginPatterns().forEach(corsConfiguration::addAllowedOriginPattern); + corsProperties.getAllowedHeaders().forEach(corsConfiguration::addAllowedHeader); + corsProperties.getAllowedMethods().forEach(corsConfiguration::addAllowedMethod); + corsConfiguration.setAllowCredentials(corsProperties.getAllowCredentials()); + source.registerCorsConfiguration(corsProperties.getPathPattern(), corsConfiguration); + return corsConfiguration; + })); + } + return http.build(); } @@ -83,4 +101,5 @@ public class PigxResourceServerConfiguration { AnnotationTemplateExpressionDefaults prePostTemplateDefaults() { return new AnnotationTemplateExpressionDefaults(); } + } diff --git a/pigx-common/pigx-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/pigx-common/pigx-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index a5ea77dac..947141ae7 100644 --- a/pigx-common/pigx-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/pigx-common/pigx-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -4,7 +4,9 @@ com.pig4cloud.pigx.common.security.service.PigxMobileUserDetailServiceImpl com.pig4cloud.pigx.common.security.service.PigxTocMobileUserDetailsServiceImpl com.pig4cloud.pigx.common.security.service.PigxRedisOAuth2AuthorizationService com.pig4cloud.pigx.common.security.service.PigxRedisOAuth2AuthorizationConsentService +com.pig4cloud.pigx.common.security.component.PigxBootCorsProperties com.pig4cloud.pigx.common.security.component.PigxSecurityInnerAspect com.pig4cloud.pigx.common.security.component.PigxInnerHeaderRemoveFilter com.pig4cloud.pigx.common.security.component.PigxSecurityMessageSourceConfiguration com.pig4cloud.pigx.common.security.service.PigxRemoteRegisteredClientRepository + From 0b939f3d57b63e0ba4d41e6f990656940b33d8a8 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Sat, 15 Nov 2025 20:38:30 +0800 Subject: [PATCH 15/44] =?UTF-8?q?feat(gen-table):=20=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=94=9F=E6=88=90=E6=94=AF=E6=8C=81=E5=A4=8D?= =?UTF-8?q?=E7=94=A8=E5=8D=95=E8=A1=A8=E7=9A=84=E9=85=8D=E7=BD=AE=E4=BF=A1?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/GenTableController.java | 229 ++++++++++-------- 1 file changed, 122 insertions(+), 107 deletions(-) diff --git a/pigx-visual/pigx-codegen/src/main/java/com/pig4cloud/pigx/codegen/controller/GenTableController.java b/pigx-visual/pigx-codegen/src/main/java/com/pig4cloud/pigx/codegen/controller/GenTableController.java index 2d112f644..3da6ac9b2 100644 --- a/pigx-visual/pigx-codegen/src/main/java/com/pig4cloud/pigx/codegen/controller/GenTableController.java +++ b/pigx-visual/pigx-codegen/src/main/java/com/pig4cloud/pigx/codegen/controller/GenTableController.java @@ -48,123 +48,138 @@ import java.util.List; @SecurityRequirement(name = HttpHeaders.AUTHORIZATION) public class GenTableController { - private final GenTableColumnService tableColumnService; + private final GenTableColumnService tableColumnService; - private final GenTableService tableService; + private final GenTableService tableService; - /** - * 分页查询 - * @param page 分页对象 - * @param table 列属性 - * @return - */ - @Operation(summary = "分页查询", description = "分页查询") - @GetMapping("/page") - public R getTablePage(Page page, GenTable table) { - return R.ok(tableService.queryTablePage(page, table)); - } + /** + * 分页查询 + * + * @param page 分页对象 + * @param table 列属性 + * @return + */ + @Operation(summary = "分页查询", description = "分页查询") + @GetMapping("/page") + public R getTablePage(Page page, GenTable table) { + return R.ok(tableService.queryTablePage(page, table)); + } - /** - * 通过id查询表信息(代码生成设置 + 表 + 字段设置) - * @param id id - * @return R - */ - @Operation(summary = "通过id查询", description = "通过id查询") - @GetMapping("/{id}") - public R getTable(@PathVariable("id") Long id) { - return R.ok(tableService.getById(id)); - } + /** + * 通过id查询表信息(代码生成设置 + 表 + 字段设置) + * + * @param id id + * @return R + */ + @Operation(summary = "通过id查询", description = "通过id查询") + @GetMapping("/{id}") + public R getTable(@PathVariable("id") Long id) { + return R.ok(tableService.getById(id)); + } - /** - * 查询数据源所有表 - * @param dsName 数据源 - */ - @GetMapping("/list/{dsName}") - public R listTable(@PathVariable("dsName") String dsName) { - return R.ok(tableService.queryTableList(dsName)); - } + /** + * 查询数据源所有表 + * + * @param dsName 数据源 + */ + @GetMapping("/list/{dsName}") + public R listTable(@PathVariable("dsName") String dsName) { + return R.ok(tableService.queryTableList(dsName)); + } - /** - * 获取表信息 - * @param dsName 数据源 - * @param tableName 表名称 - */ - @GetMapping("/{dsName}/{tableName}") - public R getTable(@PathVariable("dsName") String dsName, @PathVariable String tableName) { - return R.ok(tableService.queryOrBuildTable(dsName, tableName)); - } + /** + * 获取表信息 + * + * @param dsName 数据源 + * @param tableName 表名称 + */ + @GetMapping("/{dsName}/{tableName}") + public R getTable(@PathVariable("dsName") String dsName, @PathVariable String tableName) { + return R.ok(tableService.queryOrBuildTable(dsName, tableName)); + } - /** - * 查询表DDL语句 - * @param dsName 数据源 - * @param tableName 表名称 - */ - @GetMapping("/column/{dsName}/{tableName}") - public R getColumn(@PathVariable("dsName") String dsName, @PathVariable String tableName) throws Exception { - return R.ok(tableService.queryTableColumn(dsName, tableName)); - } + /** + * 查询表DDL语句 + * + * @param dsName 数据源 + * @param tableName 表名称 + */ + @GetMapping("/column/{dsName}/{tableName}") + public R getColumn(@PathVariable("dsName") String dsName, @PathVariable String tableName) throws Exception { + return R.ok(tableService.queryTableColumn(dsName, tableName)); + } - /** - * 查询表DDL语句 - * @param dsName 数据源 - * @param tableName 表名称 - */ - @GetMapping("/ddl/{dsName}/{tableName}") - public R getDdl(@PathVariable("dsName") String dsName, @PathVariable String tableName) throws Exception { - return R.ok(tableService.queryTableDdl(dsName, tableName)); - } + /** + * 查询表DDL语句 + * + * @param dsName 数据源 + * @param tableName 表名称 + */ + @GetMapping("/ddl/{dsName}/{tableName}") + public R getDdl(@PathVariable("dsName") String dsName, @PathVariable String tableName) throws Exception { + return R.ok(tableService.queryTableDdl(dsName, tableName)); + } - /** - * 同步表信息 - * @param dsName 数据源 - * @param tableName 表名称 - */ - @GetMapping("/sync/{dsName}/{tableName}") - public R syncTable(@PathVariable("dsName") String dsName, @PathVariable String tableName) { - // 表配置删除 - tableService.remove( - Wrappers.lambdaQuery().eq(GenTable::getDsName, dsName).eq(GenTable::getTableName, tableName)); - // 字段配置删除 - tableColumnService.remove(Wrappers.lambdaQuery() - .eq(GenTableColumnEntity::getDsName, dsName) - .eq(GenTableColumnEntity::getTableName, tableName)); - return R.ok(tableService.queryOrBuildTable(dsName, tableName)); - } + /** + * 同步表信息 + * + * @param dsName 数据源名称 + * @param tableName 表名称 + * @param isBatch 是否批量操作 + * @return 操作结果 + */ + @GetMapping("/sync/{dsName}/{tableName}") + public R syncTable(@PathVariable("dsName") String dsName, @PathVariable String tableName, @RequestParam(required = false) boolean isBatch) { + // 批量操作同步的时候,不需要删除 + if (!isBatch) { + // 表配置删除 + tableService.remove( + Wrappers.lambdaQuery().eq(GenTable::getDsName, dsName).eq(GenTable::getTableName, tableName)); + // 字段配置删除 + tableColumnService.remove(Wrappers.lambdaQuery() + .eq(GenTableColumnEntity::getDsName, dsName) + .eq(GenTableColumnEntity::getTableName, tableName)); + } + return R.ok(tableService.queryOrBuildTable(dsName, tableName)); + } - /** - * 修改列属性 - * @param table 列属性 - * @return R - */ - @Operation(summary = "修改列属性", description = "修改列属性") - @SysLog("修改列属性") - @PutMapping - public R updateById(@RequestBody GenTable table) { - return R.ok(tableService.updateById(table)); - } + /** + * 修改列属性 + * + * @param table 列属性 + * @return R + */ + @Operation(summary = "修改列属性", description = "修改列属性") + @SysLog("修改列属性") + @PutMapping + public R updateById(@RequestBody GenTable table) { + return R.ok(tableService.updateById(table)); + } - /** - * 修改表字段数据 - * @param dsName 数据源 - * @param tableName 表名称 - * @param tableFieldList 字段列表 - */ - @PutMapping("/field/{dsName}/{tableName}") - public R updateTableField(@PathVariable("dsName") String dsName, @PathVariable String tableName, - @RequestBody List tableFieldList) { - tableColumnService.updateTableField(dsName, tableName, tableFieldList); - return R.ok(); - } + /** + * 修改表字段数据 + * + * @param dsName 数据源 + * @param tableName 表名称 + * @param tableFieldList 字段列表 + */ + @PutMapping("/field/{dsName}/{tableName}") + public R updateTableField(@PathVariable("dsName") String dsName, @PathVariable String tableName, + @RequestBody List tableFieldList) { + tableColumnService.updateTableField(dsName, tableName, tableFieldList); + return R.ok(); + } - /** - * 导出excel 表格 - * @param table 查询条件 - * @return excel 文件流 - */ - @ResponseExcel - @GetMapping("/export") - public List export(GenTable table) { - return tableService.list(Wrappers.query(table)); - } + /** + * 导出excel 表格 + * + * @param table 查询条件 + * @return excel 文件流 + */ + @ResponseExcel + @GetMapping("/export") + public List export(GenTable table) { + return tableService.list(Wrappers.query(table)); + } } From efd195ae374d810f32d2ddb5b43008b0fd78580c Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Tue, 18 Nov 2025 09:54:56 +0800 Subject: [PATCH 16/44] =?UTF-8?q?feat(RemoteDataScopeService):=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9getRoleList=E5=8F=82=E6=95=B0=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E4=B8=BAList=E4=BB=A5=E6=94=AF=E6=8C=81=E5=AD=97?= =?UTF-8?q?=E7=AC=A6=E4=B8=B2=E7=B1=BB=E5=9E=8BroleId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将getRoleList方法的roleIdList参数类型从List改为List - 保持getDescendantList方法的deptId参数类型仍为Long不变 --- .../api/feign/RemoteDataScopeService.java | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/feign/RemoteDataScopeService.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/feign/RemoteDataScopeService.java index f99fc2981..ddb3544af 100644 --- a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/feign/RemoteDataScopeService.java +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/feign/RemoteDataScopeService.java @@ -38,20 +38,22 @@ import java.util.List; @FeignClient(contextId = "remoteDataScopeService", value = ServiceNameConstants.UPMS_SERVICE) public interface RemoteDataScopeService { - /** - * 通过角色ID 查询角色列表 - * @param roleIdList 角色ID - * @return - */ - @PostMapping("/role/getRoleList") - R> getRoleList(@RequestBody List roleIdList); + /** + * 通过角色ID 查询角色列表 + * + * @param roleIdList 角色ID + * @return + */ + @PostMapping("/role/getRoleList") + R> getRoleList(@RequestBody List roleIdList); - /** - * 获取子级部门 - * @param deptId 部门ID - * @return - */ - @GetMapping("/dept/getDescendantList/{deptId}") - R> getDescendantList(@PathVariable("deptId") Long deptId); + /** + * 获取子级部门 + * + * @param deptId 部门ID + * @return + */ + @GetMapping("/dept/getDescendantList/{deptId}") + R> getDescendantList(@PathVariable("deptId") Long deptId); } From 71a6f5c2d704cfe01f85ad166d7f9f83c513961e Mon Sep 17 00:00:00 2001 From: kukinghan Date: Tue, 18 Nov 2025 16:23:50 +0800 Subject: [PATCH 17/44] =?UTF-8?q?fix(sql):=20=E4=BF=AE=E6=AD=A3=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E6=9F=A5=E7=9C=8B=E8=8F=9C=E5=8D=95=E7=9A=84=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E6=A0=87=E8=AF=86=E4=B8=BA'sys=5Fsyspublicparam=5Fvie?= =?UTF-8?q?w'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/0pigxx_boot.sql | 2 +- db/2pigxx.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/db/0pigxx_boot.sql b/db/0pigxx_boot.sql index 8f4053924..c26bde9ce 100644 --- a/db/0pigxx_boot.sql +++ b/db/0pigxx_boot.sql @@ -591,7 +591,7 @@ INSERT INTO `sys_menu` VALUES (2210,'参数管理',NULL,'/admin/param/index',NUL INSERT INTO `sys_menu` VALUES (2211,'参数新增','sys_syspublicparam_add',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:17:36',' ','2020-03-24 08:57:11','0',1); INSERT INTO `sys_menu` VALUES (2212,'参数删除','sys_syspublicparam_del',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:17:55',' ','2020-03-24 08:57:12','0',1); INSERT INTO `sys_menu` VALUES (2213,'参数编辑','sys_syspublicparam_edit',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:18:14',' ','2020-03-24 08:57:13','0',1); -INSERT INTO `sys_menu` VALUES (2214,'参数查看','sys_syspublicparam_edit',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:18:14',' ','2020-03-24 08:57:13','0',1); +INSERT INTO `sys_menu` VALUES (2214,'参数查看','sys_syspublicparam_view',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:18:14',' ','2020-03-24 08:57:13','0',1); INSERT INTO `sys_menu` VALUES (2300,'代码生成',NULL,'/gen/table/index',NULL,9000,'iconfont icon-daimashengcheng','1',2,'0','0','0','','2018-01-20 13:17:19','admin','2023-02-20 13:54:35','0',1); INSERT INTO `sys_menu` VALUES (2400,'终端管理',NULL,'/admin/client/index',NULL,2000,'iconfont icon-shouji','1',9,'1',NULL,'0','','2018-01-20 13:17:19','admin','2023-02-16 15:25:28','0',1); INSERT INTO `sys_menu` VALUES (2401,'客户端新增','sys_client_add',NULL,NULL,2400,'1','1',1,'0',NULL,'1',' ','2018-05-15 21:35:18',' ','2021-05-25 03:12:55','0',1); diff --git a/db/2pigxx.sql b/db/2pigxx.sql index e1b5f81cf..f369f28b8 100644 --- a/db/2pigxx.sql +++ b/db/2pigxx.sql @@ -573,7 +573,7 @@ INSERT INTO `sys_menu` VALUES (2210,'参数管理',NULL,'/admin/param/index',NUL INSERT INTO `sys_menu` VALUES (2211,'参数新增','sys_syspublicparam_add',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:17:36',' ','2020-03-24 08:57:11','0',1); INSERT INTO `sys_menu` VALUES (2212,'参数删除','sys_syspublicparam_del',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:17:55',' ','2020-03-24 08:57:12','0',1); INSERT INTO `sys_menu` VALUES (2213,'参数编辑','sys_syspublicparam_edit',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:18:14',' ','2020-03-24 08:57:13','0',1); -INSERT INTO `sys_menu` VALUES (2214,'参数查看','sys_syspublicparam_edit',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:18:14',' ','2020-03-24 08:57:13','0',1); +INSERT INTO `sys_menu` VALUES (2214,'参数查看','sys_syspublicparam_view',NULL,NULL,2210,NULL,'1',1,'0',NULL,'1',' ','2019-04-29 22:18:14',' ','2020-03-24 08:57:13','0',1); INSERT INTO `sys_menu` VALUES (2300,'代码生成',NULL,'/gen/table/index',NULL,9000,'iconfont icon-daimashengcheng','1',2,'0','0','0','','2018-01-20 13:17:19','admin','2023-02-20 13:54:35','0',1); INSERT INTO `sys_menu` VALUES (2400,'终端管理',NULL,'/admin/client/index',NULL,2000,'iconfont icon-shouji','1',9,'1',NULL,'0','','2018-01-20 13:17:19','admin','2023-02-16 15:25:28','0',1); INSERT INTO `sys_menu` VALUES (2401,'客户端新增','sys_client_add',NULL,NULL,2400,'1','1',1,'0',NULL,'1',' ','2018-05-15 21:35:18',' ','2021-05-25 03:12:55','0',1); From 437970b926b263d71a89502a66f2a36a4b1801c6 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Wed, 19 Nov 2025 20:58:08 +0800 Subject: [PATCH 18/44] =?UTF-8?q?feat(SentinelParser):=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E8=AF=B7=E6=B1=82=E6=9D=A5=E6=BA=90=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8C=E5=9F=BA=E4=BA=8E=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=20IP=20=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../parser/PigxHeaderRequestOriginParser.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pigx-common/pigx-common-sentinel/src/main/java/com/pig4cloud/pigx/common/sentinel/parser/PigxHeaderRequestOriginParser.java b/pigx-common/pigx-common-sentinel/src/main/java/com/pig4cloud/pigx/common/sentinel/parser/PigxHeaderRequestOriginParser.java index c31f7c2da..31c8b542b 100644 --- a/pigx-common/pigx-common-sentinel/src/main/java/com/pig4cloud/pigx/common/sentinel/parser/PigxHeaderRequestOriginParser.java +++ b/pigx-common/pigx-common-sentinel/src/main/java/com/pig4cloud/pigx/common/sentinel/parser/PigxHeaderRequestOriginParser.java @@ -18,6 +18,7 @@ package com.pig4cloud.pigx.common.sentinel.parser; +import cn.hutool.extra.servlet.JakartaServletUtil; import com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.callback.RequestOriginParser; import jakarta.servlet.http.HttpServletRequest; @@ -25,23 +26,18 @@ import jakarta.servlet.http.HttpServletRequest; * @author lengleng * @date 2019-10-11 *

- * sentinel 请求头解析判断 + * sentinel 请求来源解析,基于客户端 IP 地址 */ public class PigxHeaderRequestOriginParser implements RequestOriginParser { - /** - * 请求头获取allow - */ - private static final String ALLOW = "Allow"; - /** * Parse the origin from given HTTP request. * @param request HTTP request - * @return parsed origin + * @return parsed origin (client IP address) */ @Override public String parseOrigin(HttpServletRequest request) { - return request.getHeader(ALLOW); + return JakartaServletUtil.getClientIP(request); } } From 1268e9d6041e02cb2655c1b88925ae076fdf7cfc Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Wed, 19 Nov 2025 21:41:17 +0800 Subject: [PATCH 19/44] =?UTF-8?q?refactor(pigx-common-sentinel):=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0hutool-extra=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增hutool-extra工具库依赖以增强sentinel模块功能 --- pigx-common/pigx-common-sentinel/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pigx-common/pigx-common-sentinel/pom.xml b/pigx-common/pigx-common-sentinel/pom.xml index 5c38d52d6..267b139c9 100755 --- a/pigx-common/pigx-common-sentinel/pom.xml +++ b/pigx-common/pigx-common-sentinel/pom.xml @@ -17,6 +17,10 @@ com.pig4cloud pigx-common-core + + cn.hutool + hutool-extra + com.alibaba.cloud spring-cloud-starter-alibaba-sentinel From e1ac291a1ebed549fb3b8c131f7072535db0fa6b Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Wed, 19 Nov 2025 21:45:21 +0800 Subject: [PATCH 20/44] =?UTF-8?q?feat(auth):=20=E5=A2=9E=E5=BC=BAXSS?= =?UTF-8?q?=E9=98=B2=E6=8A=A4=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在confirm.ftl模板中对principalName等变量添加?html转义 - 在login.ftl模板中对tenant.id和error变量添加防护处理 - 移除冗余的SsoLogoutSuccessHandler实现类 - 在PigxTokenEndpoint中添加XSS防护注释说明 --- .../pigx/auth/endpoint/PigxTokenEndpoint.java | 2 + .../handler/SsoLogoutSuccessHandler.java | 41 ------------------- .../main/resources/templates/ftl/confirm.ftl | 10 ++--- .../main/resources/templates/ftl/login.ftl | 4 +- 4 files changed, 9 insertions(+), 48 deletions(-) delete mode 100644 pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/handler/SsoLogoutSuccessHandler.java diff --git a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/PigxTokenEndpoint.java b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/PigxTokenEndpoint.java index 03fe11b7d..1a2798404 100644 --- a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/PigxTokenEndpoint.java +++ b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/PigxTokenEndpoint.java @@ -91,6 +91,7 @@ public class PigxTokenEndpoint { @GetMapping("/token/login") public ModelAndView require(ModelAndView modelAndView, @RequestParam(required = false) String error) { modelAndView.setViewName("ftl/login"); + // Note: XSS prevention is handled by FreeMarker template using ?html directive modelAndView.addObject("error", error); R> tenantList = tenantService.list(); @@ -113,6 +114,7 @@ public class PigxTokenEndpoint { .orElseThrow(() -> new OAuthClientException("clientId 不合法")); Set authorizedScopes = StringUtils.commaDelimitedListToSet(clientDetails.getScope()); + // Note: XSS prevention is handled by FreeMarker template using ?html directive modelAndView.addObject("clientId", clientId); modelAndView.addObject("state", state); modelAndView.addObject("scopeList", authorizedScopes); diff --git a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/handler/SsoLogoutSuccessHandler.java b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/handler/SsoLogoutSuccessHandler.java deleted file mode 100644 index e9f1ac330..000000000 --- a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/handler/SsoLogoutSuccessHandler.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.pig4cloud.pigx.auth.support.handler; - -import cn.hutool.core.util.StrUtil; -import org.springframework.http.HttpHeaders; -import org.springframework.security.core.Authentication; -import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * @author lengleng - * @date 2022-06-02 - *

- * sso 退出功能 ,根据客户端传入跳转 - */ -public class SsoLogoutSuccessHandler implements LogoutSuccessHandler { - - private static final String REDIRECT_URL = "redirect_url"; - - @Override - public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) - throws IOException { - if (response == null) { - return; - } - - // 获取请求参数中是否包含 回调地址 - String redirectUrl = request.getParameter(REDIRECT_URL); - if (StrUtil.isNotBlank(redirectUrl)) { - response.sendRedirect(redirectUrl); - } - else if (StrUtil.isNotBlank(request.getHeader(HttpHeaders.REFERER))) { - // 默认跳转referer 地址 - String referer = request.getHeader(HttpHeaders.REFERER); - response.sendRedirect(referer); - } - } - -} diff --git a/pigx-auth/src/main/resources/templates/ftl/confirm.ftl b/pigx-auth/src/main/resources/templates/ftl/confirm.ftl index 2161c611b..57495b13d 100644 --- a/pigx-auth/src/main/resources/templates/ftl/confirm.ftl +++ b/pigx-auth/src/main/resources/templates/ftl/confirm.ftl @@ -10,7 +10,7 @@ <#else> - ${principalName} + ${principalName?html} @@ -20,8 +20,8 @@

- - + +
@@ -29,9 +29,9 @@
<#list scopeList as scope>
- - +
diff --git a/pigx-auth/src/main/resources/templates/ftl/login.ftl b/pigx-auth/src/main/resources/templates/ftl/login.ftl index b582267fe..809217fda 100755 --- a/pigx-auth/src/main/resources/templates/ftl/login.ftl +++ b/pigx-auth/src/main/resources/templates/ftl/login.ftl @@ -22,7 +22,7 @@ @@ -40,7 +40,7 @@ <#if error??>
- ${error} + ${error?html}
From f181712cf28eb963603a901d7d6e1b806a5b39e6 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Wed, 19 Nov 2025 21:45:33 +0800 Subject: [PATCH 21/44] =?UTF-8?q?feat(pay):=20=E5=A2=9E=E5=BC=BA=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E5=9B=9E=E8=B0=83=E6=8E=A5=E5=8F=A3=E7=9A=84XSS?= =?UTF-8?q?=E9=98=B2=E6=8A=A4=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在aliCallbak和mergeCallbak接口中添加Content-Type和X-Content-Type-Options安全头 - 在mergeBuy接口中设置安全响应头防止XSS攻击 - 统一使用MediaType.TEXT_PLAIN作为响应内容类型 --- .../controller/PayGoodsOrderController.java | 299 +++++++++--------- .../controller/PayNotifyRecordController.java | 271 ++++++++-------- 2 files changed, 299 insertions(+), 271 deletions(-) diff --git a/pigx-visual/pigx-pay-platform/src/main/java/com/pig4cloud/pigx/pay/controller/PayGoodsOrderController.java b/pigx-visual/pigx-pay-platform/src/main/java/com/pig4cloud/pigx/pay/controller/PayGoodsOrderController.java index 47027919e..571073854 100755 --- a/pigx-visual/pigx-pay-platform/src/main/java/com/pig4cloud/pigx/pay/controller/PayGoodsOrderController.java +++ b/pigx-visual/pigx-pay-platform/src/main/java/com/pig4cloud/pigx/pay/controller/PayGoodsOrderController.java @@ -53,6 +53,8 @@ import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; @@ -74,170 +76,179 @@ import java.util.Objects; @SecurityRequirement(name = HttpHeaders.AUTHORIZATION) public class PayGoodsOrderController { - private final PayGoodsOrderService payGoodsOrderService; + private final PayGoodsOrderService payGoodsOrderService; - private final PayChannelService channelService; + private final PayChannelService channelService; - private final ObjectMapper objectMapper; + private final ObjectMapper objectMapper; - /** - * 商品订单 - * @param goods 商品 - * @param response - * - * AliPayApiConfigKit.setAppId WxPayApiConfigKit.setAppId shezhi - * - */ - @SneakyThrows - @Inner(false) - @GetMapping("/buy") - @Operation(summary = "购买商品", description = "购买商品") - public void buy(PayGoodsOrder goods, HttpServletRequest request, HttpServletResponse response) { - String ua = request.getHeader(HttpHeaders.USER_AGENT); - log.info("当前扫码方式 UA:{}", ua); + /** + * 商品订单 + * + * @param goods 商品 + * @param response AliPayApiConfigKit.setAppId WxPayApiConfigKit.setAppId shezhi + * + */ + @SneakyThrows + @Inner(false) + @GetMapping("/buy") + @Operation(summary = "购买商品", description = "购买商品") + public void buy(PayGoodsOrder goods, HttpServletRequest request, HttpServletResponse response) { + String ua = request.getHeader(HttpHeaders.USER_AGENT); + log.info("当前扫码方式 UA:{}", ua); - if (ua.contains(PayConstants.MICRO_MESSENGER)) { - PayChannel channel = channelService.getOne( - Wrappers.lambdaQuery().eq(PayChannel::getChannelId, PayChannelNameEnum.WEIXIN_MP), - false); + if (ua.contains(PayConstants.MICRO_MESSENGER)) { + PayChannel channel = channelService.getOne( + Wrappers.lambdaQuery().eq(PayChannel::getChannelId, PayChannelNameEnum.WEIXIN_MP), + false); - if (channel == null) { - throw new IllegalArgumentException("公众号支付配置不存在"); - } + if (channel == null) { + throw new IllegalArgumentException("公众号支付配置不存在"); + } - String wxUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s" - + "&redirect_uri=%s&response_type=code&scope=snsapi_base&state=%s"; + String wxUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s" + + "&redirect_uri=%s&response_type=code&scope=snsapi_base&state=%s"; - String redirectUri = String.format("%s/api/%s/goods/wx?amount=%s&TENANT-ID=%s", channel.getNotifyUrl(), - WebUtils.isMicro() ? "pay" : "admin", goods.getAmount(), TenantContextHolder.getTenantId()); + String redirectUri = String.format("%s/api/%s/goods/wx?amount=%s&TENANT-ID=%s", channel.getNotifyUrl(), + WebUtils.isMicro() ? "pay" : "admin", goods.getAmount(), TenantContextHolder.getTenantId()); - response.sendRedirect( - String.format(wxUrl, channel.getAppId(), URLUtil.encode(redirectUri), channel.getAppId())); - } + response.sendRedirect( + String.format(wxUrl, channel.getAppId(), URLUtil.encode(redirectUri), channel.getAppId())); + } - if (ua.contains(PayConstants.ALIPAY)) { - payGoodsOrderService.buy(goods, false); - } + if (ua.contains(PayConstants.ALIPAY)) { + payGoodsOrderService.buy(goods, false); + } - } + } - @SneakyThrows - @Inner(false) - @GetMapping("/merge/buy") - @Operation(summary = "聚合支付购买商品", description = "聚合支付购买商品") - public void mergeBuy(PayGoodsOrder goods, HttpServletResponse response) { - Map result = payGoodsOrderService.buy(goods, true); - response.setContentType(ContentType.JSON.getValue()); - response.getWriter().print(objectMapper.writeValueAsString(result)); - } + @SneakyThrows + @Inner(false) + @GetMapping("/merge/buy") + @Operation(summary = "聚合支付购买商品", description = "聚合支付购买商品") + public void mergeBuy(PayGoodsOrder goods, HttpServletResponse response) { + Map result = payGoodsOrderService.buy(goods, true); + response.setContentType(ContentType.JSON.getValue()); + // Add security headers to prevent XSS + response.setContentType(MediaType.TEXT_PLAIN_VALUE); + response.setHeader(com.google.common.net.HttpHeaders.X_CONTENT_TYPE_OPTIONS, ContentTypeOptionsServerHttpHeadersWriter.NOSNIFF); + response.getWriter().print(objectMapper.writeValueAsString(result)); + } - /** - * oauth - * @param goods 商品信息 - * @param code 回调code - * @param modelAndView - * @return - * @throws WxErrorException - */ - @Inner(false) - @SneakyThrows - @GetMapping("/wx") - @Operation(summary = "商品信息", description = "回调code") - public ModelAndView wx(PayGoodsOrder goods, String code, ModelAndView modelAndView) { - PayChannel channel = channelService.getOne( - Wrappers.lambdaQuery().eq(PayChannel::getChannelId, PayChannelNameEnum.WEIXIN_MP), false); + /** + * oauth + * + * @param goods 商品信息 + * @param code 回调code + * @param modelAndView + * @return + * @throws WxErrorException + */ + @Inner(false) + @SneakyThrows + @GetMapping("/wx") + @Operation(summary = "商品信息", description = "回调code") + public ModelAndView wx(PayGoodsOrder goods, String code, ModelAndView modelAndView) { + PayChannel channel = channelService.getOne( + Wrappers.lambdaQuery().eq(PayChannel::getChannelId, PayChannelNameEnum.WEIXIN_MP), false); - if (channel == null) { - throw new IllegalArgumentException("公众号支付配置不存在"); - } + if (channel == null) { + throw new IllegalArgumentException("公众号支付配置不存在"); + } - JSONObject params = JSONUtil.parseObj(channel.getParam()); - WxMpService wxMpService = new WxMpServiceImpl(); - WxMpDefaultConfigImpl storage = new WxMpDefaultConfigImpl(); - storage.setAppId(channel.getAppId()); - storage.setSecret(params.getStr("secret")); - wxMpService.setWxMpConfigStorage(storage); + JSONObject params = JSONUtil.parseObj(channel.getParam()); + WxMpService wxMpService = new WxMpServiceImpl(); + WxMpDefaultConfigImpl storage = new WxMpDefaultConfigImpl(); + storage.setAppId(channel.getAppId()); + storage.setSecret(params.getStr("secret")); + wxMpService.setWxMpConfigStorage(storage); - WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().getAccessToken(code); - goods.setUserId(accessToken.getOpenId()); - goods.setAmount(goods.getAmount()); - modelAndView.setViewName("pay"); - modelAndView.addAllObjects(payGoodsOrderService.buy(goods, false)); - return modelAndView; - } + WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().getAccessToken(code); + goods.setUserId(accessToken.getOpenId()); + goods.setAmount(goods.getAmount()); + modelAndView.setViewName("pay"); + modelAndView.addAllObjects(payGoodsOrderService.buy(goods, false)); + return modelAndView; + } - /** - * 分页查询 - * @param page 分页对象 - * @param payGoodsOrder 商品订单表 - * @return - */ - @Operation(summary = "分页查询", description = "分页查询") - @GetMapping("/page") - public R getpayGoodsOrderPage(Page page, PayGoodsOrder payGoodsOrder) { - LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); - wrapper.eq(StrUtil.isNotBlank(payGoodsOrder.getStatus()), PayGoodsOrder::getStatus, payGoodsOrder.getStatus()); - wrapper.like(Objects.nonNull(payGoodsOrder.getPayOrderId()), PayGoodsOrder::getPayOrderId, - payGoodsOrder.getPayOrderId()); - return R.ok(payGoodsOrderService.page(page, wrapper)); - } + /** + * 分页查询 + * + * @param page 分页对象 + * @param payGoodsOrder 商品订单表 + * @return + */ + @Operation(summary = "分页查询", description = "分页查询") + @GetMapping("/page") + public R getpayGoodsOrderPage(Page page, PayGoodsOrder payGoodsOrder) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(StrUtil.isNotBlank(payGoodsOrder.getStatus()), PayGoodsOrder::getStatus, payGoodsOrder.getStatus()); + wrapper.like(Objects.nonNull(payGoodsOrder.getPayOrderId()), PayGoodsOrder::getPayOrderId, + payGoodsOrder.getPayOrderId()); + return R.ok(payGoodsOrderService.page(page, wrapper)); + } - /** - * 通过id查询商品订单表 - * @param goodsOrderId id - * @return R - */ - @Operation(summary = "通过id查询", description = "通过id查询") - @GetMapping("/{goodsOrderId}") - public R getById(@PathVariable("goodsOrderId") Long goodsOrderId) { - return R.ok(payGoodsOrderService.getById(goodsOrderId)); - } + /** + * 通过id查询商品订单表 + * + * @param goodsOrderId id + * @return R + */ + @Operation(summary = "通过id查询", description = "通过id查询") + @GetMapping("/{goodsOrderId}") + public R getById(@PathVariable("goodsOrderId") Long goodsOrderId) { + return R.ok(payGoodsOrderService.getById(goodsOrderId)); + } - /** - * 新增商品订单表 - * @param payGoodsOrder 商品订单表 - * @return R - */ - @Operation(summary = "新增商品订单表", description = "新增商品订单表") - @SysLog("新增商品订单表") - @PostMapping - public R save(@RequestBody PayGoodsOrder payGoodsOrder) { - return R.ok(payGoodsOrderService.save(payGoodsOrder)); - } + /** + * 新增商品订单表 + * + * @param payGoodsOrder 商品订单表 + * @return R + */ + @Operation(summary = "新增商品订单表", description = "新增商品订单表") + @SysLog("新增商品订单表") + @PostMapping + public R save(@RequestBody PayGoodsOrder payGoodsOrder) { + return R.ok(payGoodsOrderService.save(payGoodsOrder)); + } - /** - * 修改商品订单表 - * @param payGoodsOrder 商品订单表 - * @return R - */ - @Operation(summary = "修改商品订单表", description = "修改商品订单表") - @SysLog("修改商品订单表") - @PutMapping - public R updateById(@RequestBody PayGoodsOrder payGoodsOrder) { - return R.ok(payGoodsOrderService.updateById(payGoodsOrder)); - } + /** + * 修改商品订单表 + * + * @param payGoodsOrder 商品订单表 + * @return R + */ + @Operation(summary = "修改商品订单表", description = "修改商品订单表") + @SysLog("修改商品订单表") + @PutMapping + public R updateById(@RequestBody PayGoodsOrder payGoodsOrder) { + return R.ok(payGoodsOrderService.updateById(payGoodsOrder)); + } - /** - * 通过id删除商品订单表 - * @param ids goodsOrderId列表 - * @return R - */ - @Operation(summary = "通过id删除商品订单表", description = "通过id删除商品订单表") - @SysLog("通过id删除商品订单表") - @DeleteMapping - public R removeById(@RequestBody Long[] ids) { - return R.ok(payGoodsOrderService.removeBatchByIds(CollUtil.toList(ids))); - } + /** + * 通过id删除商品订单表 + * + * @param ids goodsOrderId列表 + * @return R + */ + @Operation(summary = "通过id删除商品订单表", description = "通过id删除商品订单表") + @SysLog("通过id删除商品订单表") + @DeleteMapping + public R removeById(@RequestBody Long[] ids) { + return R.ok(payGoodsOrderService.removeBatchByIds(CollUtil.toList(ids))); + } - /** - * 导出excel 表格 - * @param payGoodsOrder 查询条件 - * @return excel 文件流 - */ - @ResponseExcel - @GetMapping("/export") - public List export(PayGoodsOrder payGoodsOrder) { - return payGoodsOrderService.list(Wrappers.query(payGoodsOrder)); - } + /** + * 导出excel 表格 + * + * @param payGoodsOrder 查询条件 + * @return excel 文件流 + */ + @ResponseExcel + @GetMapping("/export") + public List export(PayGoodsOrder payGoodsOrder) { + return payGoodsOrderService.list(Wrappers.query(payGoodsOrder)); + } } diff --git a/pigx-visual/pigx-pay-platform/src/main/java/com/pig4cloud/pigx/pay/controller/PayNotifyRecordController.java b/pigx-visual/pigx-pay-platform/src/main/java/com/pig4cloud/pigx/pay/controller/PayNotifyRecordController.java index 38710c919..6f986d5b8 100755 --- a/pigx-visual/pigx-pay-platform/src/main/java/com/pig4cloud/pigx/pay/controller/PayNotifyRecordController.java +++ b/pigx-visual/pigx-pay-platform/src/main/java/com/pig4cloud/pigx/pay/controller/PayNotifyRecordController.java @@ -44,6 +44,8 @@ import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -63,144 +65,159 @@ import java.util.Map; @SecurityRequirement(name = HttpHeaders.AUTHORIZATION) public class PayNotifyRecordController { - private final PayNotifyRecordService payNotifyRecordService; + private final PayNotifyRecordService payNotifyRecordService; - private final PayNotifyCallbakHandler alipayCallback; + private final PayNotifyCallbakHandler alipayCallback; - private final PayNotifyCallbakHandler weChatCallback; + private final PayNotifyCallbakHandler weChatCallback; - private final PayNotifyCallbakHandler mergePayCallback; + private final PayNotifyCallbakHandler mergePayCallback; - /** - * 分页查询 - * @param page 分页对象 - * @param payNotifyRecord 通知记录日志表 - * @return - */ - @Operation(summary = "分页查询", description = "分页查询") - @GetMapping("/page") - @HasPermission("pay_record_view") - public R getpayNotifyRecordPage(Page page, PayNotifyRecord payNotifyRecord) { - LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); - wrapper.eq(StrUtil.isNotBlank(payNotifyRecord.getNotifyId()), PayNotifyRecord::getNotifyId, - payNotifyRecord.getNotifyId()); - wrapper.eq(StrUtil.isNotBlank(payNotifyRecord.getOrderNo()), PayNotifyRecord::getOrderNo, - payNotifyRecord.getOrderNo()); - return R.ok(payNotifyRecordService.page(page, wrapper)); - } + /** + * 分页查询 + * + * @param page 分页对象 + * @param payNotifyRecord 通知记录日志表 + * @return + */ + @Operation(summary = "分页查询", description = "分页查询") + @GetMapping("/page") + @HasPermission("pay_record_view") + public R getpayNotifyRecordPage(Page page, PayNotifyRecord payNotifyRecord) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(StrUtil.isNotBlank(payNotifyRecord.getNotifyId()), PayNotifyRecord::getNotifyId, + payNotifyRecord.getNotifyId()); + wrapper.eq(StrUtil.isNotBlank(payNotifyRecord.getOrderNo()), PayNotifyRecord::getOrderNo, + payNotifyRecord.getOrderNo()); + return R.ok(payNotifyRecordService.page(page, wrapper)); + } - /** - * 通过id查询通知记录日志表 - * @param id id - * @return R - */ - @Operation(summary = "通过id查询", description = "通过id查询") - @GetMapping("/{id}") - @HasPermission("pay_record_view") - public R getById(@PathVariable("id") Long id) { - return R.ok(payNotifyRecordService.getById(id)); - } + /** + * 通过id查询通知记录日志表 + * + * @param id id + * @return R + */ + @Operation(summary = "通过id查询", description = "通过id查询") + @GetMapping("/{id}") + @HasPermission("pay_record_view") + public R getById(@PathVariable("id") Long id) { + return R.ok(payNotifyRecordService.getById(id)); + } - /** - * 新增通知记录日志表 - * @param payNotifyRecord 通知记录日志表 - * @return R - */ - @Operation(summary = "新增通知记录日志表", description = "新增通知记录日志表") - @SysLog("新增通知记录日志表") - @PostMapping - public R save(@RequestBody PayNotifyRecord payNotifyRecord) { - return R.ok(payNotifyRecordService.save(payNotifyRecord)); - } + /** + * 新增通知记录日志表 + * + * @param payNotifyRecord 通知记录日志表 + * @return R + */ + @Operation(summary = "新增通知记录日志表", description = "新增通知记录日志表") + @SysLog("新增通知记录日志表") + @PostMapping + public R save(@RequestBody PayNotifyRecord payNotifyRecord) { + return R.ok(payNotifyRecordService.save(payNotifyRecord)); + } - /** - * 修改通知记录日志表 - * @param payNotifyRecord 通知记录日志表 - * @return R - */ - @Operation(summary = "修改通知记录日志表", description = "修改通知记录日志表") - @SysLog("修改通知记录日志表") - @PutMapping - @HasPermission("pay_record_edit") - public R updateById(@RequestBody PayNotifyRecord payNotifyRecord) { - return R.ok(payNotifyRecordService.updateById(payNotifyRecord)); - } + /** + * 修改通知记录日志表 + * + * @param payNotifyRecord 通知记录日志表 + * @return R + */ + @Operation(summary = "修改通知记录日志表", description = "修改通知记录日志表") + @SysLog("修改通知记录日志表") + @PutMapping + @HasPermission("pay_record_edit") + public R updateById(@RequestBody PayNotifyRecord payNotifyRecord) { + return R.ok(payNotifyRecordService.updateById(payNotifyRecord)); + } - /** - * 通过id删除通知记录日志表 - * @param ids id列表 - * @return R - */ - @Operation(summary = "通过id删除通知记录日志表", description = "通过id删除通知记录日志表") - @SysLog("通过id删除通知记录日志表") - @DeleteMapping - @HasPermission("pay_record_del") - public R removeById(@RequestBody Long[] ids) { - return R.ok(payNotifyRecordService.removeBatchByIds(CollUtil.toList(ids))); - } + /** + * 通过id删除通知记录日志表 + * + * @param ids id列表 + * @return R + */ + @Operation(summary = "通过id删除通知记录日志表", description = "通过id删除通知记录日志表") + @SysLog("通过id删除通知记录日志表") + @DeleteMapping + @HasPermission("pay_record_del") + public R removeById(@RequestBody Long[] ids) { + return R.ok(payNotifyRecordService.removeBatchByIds(CollUtil.toList(ids))); + } - /** - * 支付宝渠道异步回调 - * @param request 渠道请求 - * @return - */ - @Inner(false) - @SneakyThrows - @XssCleanIgnore - @NoEncrypt - @PostMapping("/ali/callbak") - @Operation(summary = "支付宝渠道异步回调", description = "支付宝渠道异步回调") - public void aliCallbak(HttpServletRequest request, HttpServletResponse response) { - // 解析回调信息 - Map params = AliPayApi.toMap(request); - response.getWriter().print(alipayCallback.handle(params)); - } + /** + * 支付宝渠道异步回调 + * + * @param request 渠道请求 + * @return + */ + @Inner(false) + @SneakyThrows + @XssCleanIgnore + @NoEncrypt + @PostMapping("/ali/callbak") + @Operation(summary = "支付宝渠道异步回调", description = "支付宝渠道异步回调") + public void aliCallbak(HttpServletRequest request, HttpServletResponse response) { + // 解析回调信息 + Map params = AliPayApi.toMap(request); + // Set content type and security headers to prevent XSS + response.setContentType(MediaType.TEXT_PLAIN_VALUE); + response.setHeader(com.google.common.net.HttpHeaders.X_CONTENT_TYPE_OPTIONS, ContentTypeOptionsServerHttpHeadersWriter.NOSNIFF); + response.getWriter().print(alipayCallback.handle(params)); + } - /** - * 微信渠道支付回调 - * @param request - * @return - */ - @Inner(false) - @SneakyThrows - @NoEncrypt - @XssCleanIgnore - @PostMapping("/wx/callbak") - @Operation(summary = "微信渠道支付回调", description = "微信渠道支付回调") - public String wxCallbak(HttpServletRequest request) { - String xmlMsg = HttpKit.readData(request); - log.info("微信订单回调信息:{}", xmlMsg); - Map params = WxPayKit.xmlToMap(xmlMsg); - return weChatCallback.handle(params); - } + /** + * 微信渠道支付回调 + * + * @param request + * @return + */ + @Inner(false) + @SneakyThrows + @NoEncrypt + @XssCleanIgnore + @PostMapping("/wx/callbak") + @Operation(summary = "微信渠道支付回调", description = "微信渠道支付回调") + public String wxCallbak(HttpServletRequest request) { + String xmlMsg = HttpKit.readData(request); + log.info("微信订单回调信息:{}", xmlMsg); + Map params = WxPayKit.xmlToMap(xmlMsg); + return weChatCallback.handle(params); + } - /** - * 聚合渠道异步回调 - * @param request 渠道请求 - * @return - */ - @Inner(false) - @SneakyThrows - @NoEncrypt - @XssCleanIgnore - @PostMapping("/merge/callbak") - @Operation(summary = "聚合渠道异步回调", description = "聚合渠道异步回调") - public void mergeCallbak(HttpServletRequest request, HttpServletResponse response) { - // 解析回调信息 - Map params = AliPayApi.toMap(request); - response.getWriter().print(mergePayCallback.handle(params)); - } + /** + * 聚合渠道异步回调 + * + * @param request 渠道请求 + * @return + */ + @Inner(false) + @SneakyThrows + @NoEncrypt + @XssCleanIgnore + @PostMapping("/merge/callbak") + @Operation(summary = "聚合渠道异步回调", description = "聚合渠道异步回调") + public void mergeCallbak(HttpServletRequest request, HttpServletResponse response) { + // 解析回调信息 + Map params = AliPayApi.toMap(request); + // Set content type and security headers to prevent XSS + response.setContentType(MediaType.TEXT_PLAIN_VALUE); + response.setHeader(com.google.common.net.HttpHeaders.X_CONTENT_TYPE_OPTIONS, ContentTypeOptionsServerHttpHeadersWriter.NOSNIFF); + response.getWriter().print(mergePayCallback.handle(params)); + } - /** - * 导出excel 表格 - * @param payNotifyRecord 查询条件 - * @return excel 文件流 - */ - @ResponseExcel - @GetMapping("/export") - @HasPermission("pay_record_export") - public List export(PayNotifyRecord payNotifyRecord) { - return payNotifyRecordService.list(Wrappers.query(payNotifyRecord)); - } + /** + * 导出excel 表格 + * + * @param payNotifyRecord 查询条件 + * @return excel 文件流 + */ + @ResponseExcel + @GetMapping("/export") + @HasPermission("pay_record_export") + public List export(PayNotifyRecord payNotifyRecord) { + return payNotifyRecordService.list(Wrappers.query(payNotifyRecord)); + } } From 4adc7b00de155291cb2a9be361f8cc805e171be5 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Wed, 19 Nov 2025 22:18:49 +0800 Subject: [PATCH 22/44] =?UTF-8?q?feat(auth):=20=E9=87=8D=E6=9E=84FormIdent?= =?UTF-8?q?ityLoginConfigurer=E7=9A=84handler=E4=BE=9D=E8=B5=96=E7=BB=93?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将SsoLogoutSuccessHandler移动到common-security模块中统一管理 - 使用TenantSavedRequestAwareAuthenticationSuccessHandler替代原有success handler - 保持FormAuthenticationFailureHandler作为默认failure handler --- .../pigx/auth/support/core/FormIdentityLoginConfigurer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/core/FormIdentityLoginConfigurer.java b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/core/FormIdentityLoginConfigurer.java index d0d3c92cd..b526f630b 100644 --- a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/core/FormIdentityLoginConfigurer.java +++ b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/core/FormIdentityLoginConfigurer.java @@ -1,8 +1,8 @@ package com.pig4cloud.pigx.auth.support.core; import com.pig4cloud.pigx.auth.support.handler.FormAuthenticationFailureHandler; -import com.pig4cloud.pigx.auth.support.handler.SsoLogoutSuccessHandler; import com.pig4cloud.pigx.auth.support.handler.TenantSavedRequestAwareAuthenticationSuccessHandler; +import com.pig4cloud.pigx.common.security.handler.SsoLogoutSuccessHandler; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; From 87d1162c45ac09cc3370a4c48ef47e27af71c238 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Mon, 24 Nov 2025 08:25:03 +0800 Subject: [PATCH 23/44] =?UTF-8?q?feat(build):=20=E5=8D=87=E7=BA=A7Spring?= =?UTF-8?q?=20Boot=E7=89=88=E6=9C=AC=E8=87=B33.5.8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新pom.xml中的spring-boot.version属性至3.5.8版本 - 保持其他依赖版本不变 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 40e3d547a..2b16b5037 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ - 3.5.7 + 3.5.8 2025.0.0 2025.0.0.0 UTF-8 From 2716c65170b4a52e2df2ec7bc4544c5288128ac6 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Fri, 28 Nov 2025 15:04:33 +0800 Subject: [PATCH 24/44] =?UTF-8?q?feat(oss):=20=E4=BC=98=E5=8C=96OssTemplat?= =?UTF-8?q?e=20=E5=9C=A8afterPropertiesSet=E6=96=B9=E6=B3=95=E4=B8=AD?= =?UTF-8?q?=E7=A6=81=E7=94=A8AWS=20v1=E5=BC=83=E7=94=A8=E5=85=AC=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 统一调整代码缩进为4个空格提升可读性 - - 保持原有功能逻辑不变 --- .../common/file/oss/service/OssTemplate.java | 422 +++++++++--------- 1 file changed, 217 insertions(+), 205 deletions(-) diff --git a/pigx-common/pigx-common-oss/src/main/java/com/pig4cloud/pigx/common/file/oss/service/OssTemplate.java b/pigx-common/pigx-common-oss/src/main/java/com/pig4cloud/pigx/common/file/oss/service/OssTemplate.java index 99a41f793..250f7153c 100755 --- a/pigx-common/pigx-common-oss/src/main/java/com/pig4cloud/pigx/common/file/oss/service/OssTemplate.java +++ b/pigx-common/pigx-common-oss/src/main/java/com/pig4cloud/pigx/common/file/oss/service/OssTemplate.java @@ -52,229 +52,241 @@ import java.util.*; @RequiredArgsConstructor public class OssTemplate implements InitializingBean, FileTemplate { - private final FileProperties properties; + private final FileProperties properties; - private AmazonS3 amazonS3; + private AmazonS3 amazonS3; - /** - * 创建bucket - * @param bucketName bucket名称 - */ - @SneakyThrows - public void createBucket(String bucketName) { - if (!amazonS3.doesBucketExistV2(bucketName)) { - amazonS3.createBucket((bucketName)); - } - } + /** + * 创建bucket + * + * @param bucketName bucket名称 + */ + @SneakyThrows + public void createBucket(String bucketName) { + if (!amazonS3.doesBucketExistV2(bucketName)) { + amazonS3.createBucket((bucketName)); + } + } - /** - * 获取全部bucket - *

- * - * @see AWS - * API Documentation - */ - @SneakyThrows - public List getAllBuckets() { - return amazonS3.listBuckets(); - } + /** + * 获取全部bucket + *

+ * + * @see AWS + * API Documentation + */ + @SneakyThrows + public List getAllBuckets() { + return amazonS3.listBuckets(); + } - /** - * @param bucketName bucket名称 - * @see AWS - * API Documentation - */ - @SneakyThrows - public Optional getBucket(String bucketName) { - return amazonS3.listBuckets().stream().filter(b -> b.getName().equals(bucketName)).findFirst(); - } + /** + * @param bucketName bucket名称 + * @see AWS + * API Documentation + */ + @SneakyThrows + public Optional getBucket(String bucketName) { + return amazonS3.listBuckets().stream().filter(b -> b.getName().equals(bucketName)).findFirst(); + } - /** - * @param bucketName bucket名称 - * @see AWS API - * Documentation - */ - @SneakyThrows - public void removeBucket(String bucketName) { - amazonS3.deleteBucket(bucketName); - } + /** + * @param bucketName bucket名称 + * @see AWS API + * Documentation + */ + @SneakyThrows + public void removeBucket(String bucketName) { + amazonS3.deleteBucket(bucketName); + } - /** - * 根据文件前置查询文件 - * @param bucketName bucket名称 - * @param prefix 前缀 - * @param recursive 是否递归查询 - * @return S3ObjectSummary 列表 - * @see AWS - * API Documentation - */ - @SneakyThrows - public List getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) { - ObjectListing objectListing = amazonS3.listObjects(bucketName, prefix); - return new ArrayList<>(objectListing.getObjectSummaries()); - } + /** + * 根据文件前置查询文件 + * + * @param bucketName bucket名称 + * @param prefix 前缀 + * @param recursive 是否递归查询 + * @return S3ObjectSummary 列表 + * @see AWS + * API Documentation + */ + @SneakyThrows + public List getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) { + ObjectListing objectListing = amazonS3.listObjects(bucketName, prefix); + return new ArrayList<>(objectListing.getObjectSummaries()); + } - /** - * 获取文件外链 - * @param bucketName bucket名称 - * @param objectName 文件名称 - * @param expires 过期时间 <=7 - * @return url - * @see AmazonS3#generatePresignedUrl(String bucketName, String key, Date expiration) - */ - @SneakyThrows - public String getObjectURL(String bucketName, String objectName, Integer expires) { - Date date = new Date(); - Calendar calendar = new GregorianCalendar(); - calendar.setTime(date); - calendar.add(Calendar.DAY_OF_MONTH, expires); - URL url = amazonS3.generatePresignedUrl(bucketName, objectName, calendar.getTime()); - return url.toString(); - } + /** + * 获取文件外链 + * + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @param expires 过期时间 <=7 + * @return url + * @see AmazonS3#generatePresignedUrl(String bucketName, String key, Date expiration) + */ + @SneakyThrows + public String getObjectURL(String bucketName, String objectName, Integer expires) { + Date date = new Date(); + Calendar calendar = new GregorianCalendar(); + calendar.setTime(date); + calendar.add(Calendar.DAY_OF_MONTH, expires); + URL url = amazonS3.generatePresignedUrl(bucketName, objectName, calendar.getTime()); + return url.toString(); + } - /** - * 获取文件 - * @param bucketName bucket名称 - * @param objectName 文件名称 - * @return 二进制流 - * @see AWS - * API Documentation - */ - @SneakyThrows - public S3Object getObject(String bucketName, String objectName) { - if (properties.getOss().isSkipMd5Check()) { - System.setProperty("com.amazonaws.services.s3.disableGetObjectMD5Validation", "false"); - } - return amazonS3.getObject(bucketName, objectName); - } + /** + * 获取文件 + * + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @return 二进制流 + * @see AWS + * API Documentation + */ + @SneakyThrows + public S3Object getObject(String bucketName, String objectName) { + if (properties.getOss().isSkipMd5Check()) { + System.setProperty("com.amazonaws.services.s3.disableGetObjectMD5Validation", "false"); + } + return amazonS3.getObject(bucketName, objectName); + } - /** - * 获取文件 - * @param bucketName bucket名称 - * @param dir 文件夹名称 - * @param objectName 文件名称 - * @return 二进制流 API Documentation - */ - @Override - public S3Object getObject(String bucketName, String dir, String objectName) { - if (StrUtil.isNotBlank(dir)) { - objectName = dir + "/" + objectName; - } - return getObject(bucketName, objectName); - } + /** + * 获取文件 + * + * @param bucketName bucket名称 + * @param dir 文件夹名称 + * @param objectName 文件名称 + * @return 二进制流 API Documentation + */ + @Override + public S3Object getObject(String bucketName, String dir, String objectName) { + if (StrUtil.isNotBlank(dir)) { + objectName = dir + "/" + objectName; + } + return getObject(bucketName, objectName); + } - /** - * 上传文件 - * @param bucketName bucket名称 - * @param objectName 文件名称 - * @param stream 文件流 - * @throws Exception - */ - public void putObject(String bucketName, String objectName, InputStream stream) throws Exception { - putObject(bucketName, objectName, stream, stream.available(), "application/octet-stream"); - } + /** + * 上传文件 + * + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @param stream 文件流 + * @throws Exception + */ + public void putObject(String bucketName, String objectName, InputStream stream) throws Exception { + putObject(bucketName, objectName, stream, stream.available(), "application/octet-stream"); + } - /** - * 上传文件 - * @param bucketName bucket名称 - * @param objectName 文件名称 - * @param stream 文件流 - * @param contextType 文件类型 - * @throws Exception - */ - public void putObject(String bucketName, String objectName, InputStream stream, String contextType) - throws Exception { - putObject(bucketName, objectName, stream, stream.available(), contextType); - } + /** + * 上传文件 + * + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @param stream 文件流 + * @param contextType 文件类型 + * @throws Exception + */ + public void putObject(String bucketName, String objectName, InputStream stream, String contextType) + throws Exception { + putObject(bucketName, objectName, stream, stream.available(), contextType); + } - /** - * 上传文件 - * @param bucketName bucket名称 - * @param dir 文件夹名称 - * @param objectName 文件名称 - * @param stream 文件流 - * @param contextType 文件类型 - * @throws Exception - */ - @Override - public void putObject(String bucketName, String dir, String objectName, InputStream stream, String contextType) - throws Exception { - if (StrUtil.isNotBlank(dir)) { - // dir 路径为 a/b - objectName = dir + FileUtil.FILE_SEPARATOR + objectName; - } + /** + * 上传文件 + * + * @param bucketName bucket名称 + * @param dir 文件夹名称 + * @param objectName 文件名称 + * @param stream 文件流 + * @param contextType 文件类型 + * @throws Exception + */ + @Override + public void putObject(String bucketName, String dir, String objectName, InputStream stream, String contextType) + throws Exception { + if (StrUtil.isNotBlank(dir)) { + // dir 路径为 a/b + objectName = dir + FileUtil.FILE_SEPARATOR + objectName; + } - putObject(bucketName, objectName, stream, stream.available(), contextType); - } + putObject(bucketName, objectName, stream, stream.available(), contextType); + } - /** - * 上传文件 - * @param bucketName bucket名称 - * @param objectName 文件名称 - * @param stream 文件流 - * @param size 大小 - * @param contextType 类型 - * @throws Exception - * @see AWS - * API Documentation - */ - public PutObjectResult putObject(String bucketName, String objectName, InputStream stream, long size, - String contextType) throws Exception { - // String fileName = getFileName(objectName); - byte[] bytes = IOUtils.toByteArray(stream); - ObjectMetadata objectMetadata = new ObjectMetadata(); - objectMetadata.setContentLength(size); - objectMetadata.setContentType(contextType); - ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); - // 上传 - return amazonS3.putObject(bucketName, objectName, byteArrayInputStream, objectMetadata); + /** + * 上传文件 + * + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @param stream 文件流 + * @param size 大小 + * @param contextType 类型 + * @throws Exception + * @see AWS + * API Documentation + */ + public PutObjectResult putObject(String bucketName, String objectName, InputStream stream, long size, + String contextType) throws Exception { + // String fileName = getFileName(objectName); + byte[] bytes = IOUtils.toByteArray(stream); + ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentLength(size); + objectMetadata.setContentType(contextType); + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); + // 上传 + return amazonS3.putObject(bucketName, objectName, byteArrayInputStream, objectMetadata); - } + } - /** - * 获取文件信息 - * @param bucketName bucket名称 - * @param objectName 文件名称 - * @see AWS - * API Documentation - */ - public S3Object getObjectInfo(String bucketName, String objectName) throws Exception { - @Cleanup - S3Object object = amazonS3.getObject(bucketName, objectName); - return object; - } + /** + * 获取文件信息 + * + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @see AWS + * API Documentation + */ + public S3Object getObjectInfo(String bucketName, String objectName) throws Exception { + @Cleanup + S3Object object = amazonS3.getObject(bucketName, objectName); + return object; + } - /** - * 删除文件 - * @param bucketName bucket名称 - * @param objectName 文件名称 - * @throws Exception - * @see AWS API - * Documentation - */ - public void removeObject(String bucketName, String objectName) throws Exception { - amazonS3.deleteObject(bucketName, objectName); - } + /** + * 删除文件 + * + * @param bucketName bucket名称 + * @param objectName 文件名称 + * @throws Exception + * @see AWS API + * Documentation + */ + public void removeObject(String bucketName, String objectName) throws Exception { + amazonS3.deleteObject(bucketName, objectName); + } - @Override - public void afterPropertiesSet() { - ClientConfiguration clientConfiguration = new ClientConfiguration(); - clientConfiguration.setMaxConnections(properties.getOss().getMaxConnections()); + @Override + public void afterPropertiesSet() { + System.setProperty("aws.java.v1.disableDeprecationAnnouncement", "true"); + ClientConfiguration clientConfiguration = new ClientConfiguration(); + clientConfiguration.setMaxConnections(properties.getOss().getMaxConnections()); - AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration( - properties.getOss().getEndpoint(), properties.getOss().getRegion()); - AWSCredentials awsCredentials = new BasicAWSCredentials(properties.getOss().getAccessKey(), - properties.getOss().getSecretKey()); - AWSCredentialsProvider awsCredentialsProvider = new AWSStaticCredentialsProvider(awsCredentials); - this.amazonS3 = AmazonS3Client.builder() - .withEndpointConfiguration(endpointConfiguration) - .withClientConfiguration(clientConfiguration) - .withCredentials(awsCredentialsProvider) - .disableChunkedEncoding() - .withPathStyleAccessEnabled(properties.getOss().getPathStyleAccess()) - .build(); - } + AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration( + properties.getOss().getEndpoint(), properties.getOss().getRegion()); + AWSCredentials awsCredentials = new BasicAWSCredentials(properties.getOss().getAccessKey(), + properties.getOss().getSecretKey()); + AWSCredentialsProvider awsCredentialsProvider = new AWSStaticCredentialsProvider(awsCredentials); + this.amazonS3 = AmazonS3Client.builder() + .withEndpointConfiguration(endpointConfiguration) + .withClientConfiguration(clientConfiguration) + .withCredentials(awsCredentialsProvider) + .disableChunkedEncoding() + .withPathStyleAccessEnabled(properties.getOss().getPathStyleAccess()) + .build(); + } } From 65297a5574923ebc349e2f6c3f32b3146f23a18c Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Wed, 3 Dec 2025 20:56:23 +0800 Subject: [PATCH 25/44] =?UTF-8?q?feat(deps):=20=20=E5=8D=87=E7=BA=A7mybati?= =?UTF-8?q?s-plus=E8=87=B33.5.15=E7=89=88=E6=9C=AC=20=E3=80=81=E5=8D=87?= =?UTF-8?q?=E7=BA=A7hutool=E8=87=B35.8.42=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pigx-common/pigx-common-bom/pom.xml | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/pigx-common/pigx-common-bom/pom.xml b/pigx-common/pigx-common-bom/pom.xml index a5e39e518..e83b979b9 100644 --- a/pigx-common/pigx-common-bom/pom.xml +++ b/pigx-common/pigx-common-bom/pom.xml @@ -11,14 +11,14 @@ pigx 公共版本控制,在此pom定义的依赖,全局业务微服务不需要指定版本。 - 5.10.0 + 5.11.0 3.5.14 1.5.4 4.3.1 3.1.0 1.2.23 - 5.8.41 - 8.3.0 + 5.8.42 + 9.5.0 21.3.0.0 12.10.0.jre8 8.1.3.140 @@ -26,8 +26,8 @@ 3.0.5 2.8.5 2.2.28 - 4.6.7.B - 2.9.10-17 + 4.7.8-20251028.120007 + 2.9.12.1-17 3.0.3 4.0.1 1.15.3 @@ -52,7 +52,7 @@ 1.4.7 2.0.1 2.20.1 - 2.3 + 2.4.1 2.16.1 3.1 @@ -186,6 +186,22 @@ pigx-flow-api ${pigx.version} + + + com.pig4cloud + pigx-common-milvus + ${pigx.version} + + + com.pig4cloud + pigx-common-mcp + ${pigx.version} + + + com.pig4cloud + pigx-common-neo4j + ${pigx.version} + io.github.pig-mesh.ai From ab97082cd8b8def22571f8d27b20e7b09e2c7fb5 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Sat, 6 Dec 2025 08:38:07 +0800 Subject: [PATCH 26/44] =?UTF-8?q?feat(menu):=20=E4=BC=98=E5=8C=96removeMen?= =?UTF-8?q?uById=E6=96=B9=E6=B3=95=E5=AE=9E=E7=8E=B0=E9=80=92=E5=BD=92?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=AD=90=E8=8F=9C=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/SysMenuServiceImpl.java | 303 +++++++++++------- 1 file changed, 191 insertions(+), 112 deletions(-) diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMenuServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMenuServiceImpl.java index 4c6cb0064..119db01bb 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMenuServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMenuServiceImpl.java @@ -37,9 +37,11 @@ import com.pig4cloud.pigx.admin.service.SysMenuService; import com.pig4cloud.pigx.common.core.constant.CacheConstants; import com.pig4cloud.pigx.common.core.constant.CommonConstants; import com.pig4cloud.pigx.common.core.constant.enums.MenuTypeEnum; +import com.pig4cloud.pigx.common.core.constant.enums.YesNoEnum; import com.pig4cloud.pigx.common.core.exception.ErrorCodes; import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.common.security.util.SecurityUtils; import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -66,134 +68,211 @@ import java.util.function.Predicate; @Slf4j public class SysMenuServiceImpl extends ServiceImpl implements SysMenuService { - private final SysRoleMenuMapper sysRoleMenuMapper; + private final SysRoleMenuMapper sysRoleMenuMapper; - private final SysI18nService sysI18nService; + private final SysI18nService sysI18nService; - @Override - @Cacheable(value = CacheConstants.MENU_DETAILS, key = "#roleId", unless = "#result.isEmpty()") - public List findMenuByRoleId(Long roleId) { - return baseMapper.listMenusByRoleId(roleId); - } + @Override + @Cacheable(value = CacheConstants.MENU_DETAILS, key = "#roleId", unless = "#result.isEmpty()") + public List findMenuByRoleId(Long roleId) { + return baseMapper.listMenusByRoleId(roleId); + } - @Override - @Transactional(rollbackFor = Exception.class) - @CacheEvict(value = CacheConstants.MENU_DETAILS, allEntries = true) - public R removeMenuById(Long id) { - // 查询父节点为当前节点的节点 - List menuList = this.list(Wrappers.query().lambda().eq(SysMenu::getParentId, id)); - if (CollUtil.isNotEmpty(menuList)) { - return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_MENU_DELETE_EXISTING)); - } + @Override + @Transactional(rollbackFor = Exception.class) + @CacheEvict(value = CacheConstants.MENU_DETAILS, allEntries = true) + public R removeMenuById(Long id) { + // 1. 检查菜单是否存在 + SysMenu menu = this.getById(id); + if (menu == null) { + return R.failed("菜单不存在"); + } - sysRoleMenuMapper.delete(Wrappers.query().lambda().eq(SysRoleMenu::getMenuId, id)); - // 删除当前菜单及其子菜单 - return R.ok(this.removeById(id)); - } + // 2. 递归收集当前菜单及所有子孙菜单的ID + List menuIdsToDelete = new ArrayList<>(); + collectMenuIds(id, menuIdsToDelete); - @Override - @CacheEvict(value = CacheConstants.MENU_DETAILS, allEntries = true) - public Boolean updateMenuById(SysMenu sysMenu) { - return this.updateById(sysMenu); - } + // 3. 批量删除所有角色-菜单关联 + if (CollUtil.isNotEmpty(menuIdsToDelete)) { + sysRoleMenuMapper.delete(Wrappers.lambdaQuery().in(SysRoleMenu::getMenuId, menuIdsToDelete)); + } - /** - * 构建树查询 1. 不是懒加载情况,查询全部 2. 是懒加载,根据parentId 查询 2.1 父节点为空,则查询ID -1 - * @param parentId 父节点ID - * @param menuName 菜单名称 - * @return - */ - @Override - public List> treeMenu(Long parentId, String menuName, String type) { - Long parent = parentId == null ? CommonConstants.MENU_TREE_ROOT_ID : parentId; + // 4. 批量逻辑删除菜单 + return R.ok(this.removeByIds(menuIdsToDelete)); + } - List> collect = baseMapper - .selectList(Wrappers.lambdaQuery() - .eq(Objects.nonNull(parentId), SysMenu::getParentId, parentId) - .like(StrUtil.isNotBlank(menuName), SysMenu::getName, menuName) - .eq(StrUtil.isNotBlank(type), SysMenu::getMenuType, type) - .orderByAsc(SysMenu::getSortOrder)) - .stream() - .map(getNodeFunction()) - .toList(); + /** + * 递归收集菜单ID及其所有子孙菜单ID + * @param menuId 菜单ID + * @param menuIds 收集结果列表 + */ + private void collectMenuIds(Long menuId, List menuIds) { + menuIds.add(menuId); - // 模糊查询 不组装树结构 直接返回 表格方便编辑 - if (StrUtil.isNotBlank(menuName)) { - return collect.stream().map(node -> { - Tree tree = new Tree<>(); - tree.putAll(node.getExtra()); - BeanUtils.copyProperties(node, tree); - return tree; + List children = this.list(Wrappers.lambdaQuery().eq(SysMenu::getParentId, menuId)); + + if (CollUtil.isNotEmpty(children)) { + for (SysMenu child : children) { + collectMenuIds(child.getMenuId(), menuIds); + } + } + } + + @Override + @CacheEvict(value = CacheConstants.MENU_DETAILS, allEntries = true) + public Boolean updateMenuById(SysMenu sysMenu) { + return this.updateById(sysMenu); + } + + /** + * 构建树查询 1. 不是懒加载情况,查询全部 2. 是懒加载,根据parentId 查询 2.1 父节点为空,则查询ID -1 + * + * @param parentId 父节点ID + * @param menuName 菜单名称 + * @return + */ + @Override + public List> treeMenu(Long parentId, String menuName, String type) { + Long parent = parentId == null ? CommonConstants.MENU_TREE_ROOT_ID : parentId; + + List> collect = baseMapper.selectList(Wrappers.lambdaQuery().eq(Objects.nonNull(parentId), SysMenu::getParentId, parentId).like(StrUtil.isNotBlank(menuName), SysMenu::getName, menuName).eq(StrUtil.isNotBlank(type), SysMenu::getMenuType, type).orderByAsc(SysMenu::getSortOrder)).stream().map(getNodeFunction()).toList(); + + // 模糊查询 不组装树结构 直接返回 表格方便编辑 + if (StrUtil.isNotBlank(menuName)) { + return collect.stream().map(node -> { + Tree tree = new Tree<>(); + tree.putAll(node.getExtra()); + BeanUtils.copyProperties(node, tree); + return tree; }).toList(); - } + } - return TreeUtil.build(collect, parent); - } + return TreeUtil.build(collect, parent); + } - /** - * 查询菜单 - * @param all 全部菜单 - * @param type 类型 - * @param parentId 父节点ID - * @return - */ - @Override - public List> filterMenu(Set all, String type, Long parentId) { - List list = sysI18nService.list(); - List> collect = all.stream().filter(menuTypePredicate(type)).peek(item -> { - Optional first = list.stream().filter(it -> it.getZhCn().equals(item.getName())).findFirst(); - first.ifPresent(sysI18nEntity -> item.setName(sysI18nEntity.getName())); + /** + * 查询菜单 + * + * @param all 全部菜单 + * @param type 类型 + * @param parentId 父节点ID + * @return + */ + @Override + public List> filterMenu(Set all, String type, Long parentId) { + List list = sysI18nService.list(); + List> collect = all.stream().filter(menuTypePredicate(type)).peek(item -> { + Optional first = list.stream().filter(it -> it.getZhCn().equals(item.getName())).findFirst(); + first.ifPresent(sysI18nEntity -> item.setName(sysI18nEntity.getName())); }).map(getNodeFunction()).toList(); - Long parent = parentId == null ? CommonConstants.MENU_TREE_ROOT_ID : parentId; - return TreeUtil.build(collect, parent); - } + Long parent = parentId == null ? CommonConstants.MENU_TREE_ROOT_ID : parentId; + return TreeUtil.build(collect, parent); + } - @NotNull - private Function> getNodeFunction() { - return menu -> { - TreeNode node = new TreeNode<>(); - node.setId(menu.getMenuId()); - node.setName(menu.getName()); - node.setParentId(menu.getParentId()); - node.setWeight(menu.getSortOrder()); - // 扩展属性 - Map extra = new HashMap<>(); - extra.put("path", menu.getPath()); - extra.put("componentPath", menu.getComponent()); - extra.put("menuType", menu.getMenuType()); - extra.put("permission", menu.getPermission()); - extra.put("sortOrder", menu.getSortOrder()); + @NotNull + private Function> getNodeFunction() { + return menu -> { + TreeNode node = new TreeNode<>(); + node.setId(menu.getMenuId()); + node.setName(menu.getName()); + node.setParentId(menu.getParentId()); + node.setWeight(menu.getSortOrder()); + // 扩展属性 + Map extra = new HashMap<>(); + extra.put("path", menu.getPath()); + extra.put("componentPath", menu.getComponent()); + extra.put("menuType", menu.getMenuType()); + extra.put("permission", menu.getPermission()); + extra.put("sortOrder", menu.getSortOrder()); + extra.put("isHomePage", menu.getIsHomePage()); - // 适配 vue3 - Map meta = new HashMap<>(); - meta.put("title", menu.getName()); - meta.put("isLink", menu.getPath() != null && menu.getPath().startsWith("http") ? menu.getPath() : ""); - meta.put("isHide", !BooleanUtil.toBooleanObject(menu.getVisible())); - meta.put("isKeepAlive", BooleanUtil.toBooleanObject(menu.getKeepAlive())); - meta.put("isAffix", false); - meta.put("isIframe", BooleanUtil.toBooleanObject(menu.getEmbedded())); - meta.put("icon", menu.getIcon()); + // 适配 vue3 + Map meta = new HashMap<>(); + meta.put("title", menu.getName()); + meta.put("isLink", menu.getPath() != null && menu.getPath().startsWith("http") ? menu.getPath() : ""); + meta.put("isHide", !BooleanUtil.toBooleanObject(menu.getVisible())); + meta.put("isKeepAlive", BooleanUtil.toBooleanObject(menu.getKeepAlive())); + meta.put("isAffix", false); + meta.put("isIframe", BooleanUtil.toBooleanObject(menu.getEmbedded())); + meta.put("icon", menu.getIcon()); - extra.put("meta", meta); - node.setExtra(extra); - return node; - }; - } + extra.put("meta", meta); + node.setExtra(extra); + return node; + }; + } - /** - * menu 类型断言 - * @param type 类型 - * @return Predicate - */ - private Predicate menuTypePredicate(String type) { - return vo -> { - if (MenuTypeEnum.TOP_MENU.getDescription().equals(type)) { - return MenuTypeEnum.TOP_MENU.getType().equals(vo.getMenuType()); - } - // 其他查询 左侧 + 顶部 - return !MenuTypeEnum.BUTTON.getType().equals(vo.getMenuType()); - }; - } + /** + * menu 类型断言 + * + * @param type 类型 + * @return Predicate + */ + private Predicate menuTypePredicate(String type) { + return vo -> { + if (MenuTypeEnum.TOP_MENU.getDescription().equals(type)) { + return MenuTypeEnum.TOP_MENU.getType().equals(vo.getMenuType()); + } + // 其他查询 左侧 + 顶部 + return !MenuTypeEnum.BUTTON.getType().equals(vo.getMenuType()); + }; + } + + /** + * 设置首页菜单 + * + * @param menuId 菜单ID + * @return 是否成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + @CacheEvict(value = CacheConstants.MENU_DETAILS, allEntries = true) + public R setHomePage(Long menuId) { + // 查询菜单是否存在且为菜单类型(非按钮) + SysMenu menu = this.getById(menuId); + if (menu == null) { + return R.failed("菜单不存在"); + } + if (MenuTypeEnum.BUTTON.getType().equals(menu.getMenuType())) { + return R.failed("按钮类型不能设置为首页"); + } + + // Toggle 模式:如果已是首页则取消,否则设置为首页 + if (YesNoEnum.YES.getCode().equals(menu.getIsHomePage())) { + // 已是首页,取消首页设置 + menu.setIsHomePage(YesNoEnum.NO.getCode()); + this.updateById(menu); + return R.ok(Boolean.TRUE, "取消首页成功"); + } else { + // 不是首页,先将所有菜单的首页标记重置为0,再设置当前菜单为首页 + this.update(Wrappers.lambdaUpdate().set(SysMenu::getIsHomePage, YesNoEnum.NO.getCode())); + menu.setIsHomePage(YesNoEnum.YES.getCode()); + this.updateById(menu); + return R.ok(Boolean.TRUE, "设置首页成功"); + } + } + + /** + * 获取首页菜单 + * 根据当前用户的角色ID查询其有权限访问的首页菜单 + * + * @return 首页菜单 + */ + @Override + public SysMenu getHomePage() { + // 获取当前用户的角色ID列表 + List roleIds = SecurityUtils.getRoleIds(); + if (CollUtil.isEmpty(roleIds)) { + return null; + } + + // 获取用户所有角色对应的菜单(直接调用mapper避免self-invocation导致缓存失效) + Set userMenus = new HashSet<>(); + roleIds.forEach(roleId -> userMenus.addAll(baseMapper.listMenusByRoleId(roleId))); + + // 在用户有权限的菜单中查找首页菜单 + return userMenus.stream().filter(menu -> YesNoEnum.YES.getCode().equals(menu.getIsHomePage())).findFirst().orElse(null); + } } From d6c1a0aad1cd3e8ef6bbaa68e8dcd9755c40cc80 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Mon, 8 Dec 2025 15:09:45 +0800 Subject: [PATCH 27/44] =?UTF-8?q?feat(task):=20completeTask=E6=97=B6?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=9B=B4=E6=96=B0=E4=B8=BB=E8=A1=A8=E5=8D=95?= =?UTF-8?q?=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 扩展queryTask接口支持选择主表单数据源 - - 优化已完成任务的数据获取逻辑 - 保持原有节点快照数据作为备选方案 --- .../controller/ProcessTaskController.java | 6 +- .../pigx/flow/service/ITaskService.java | 12 + .../flow/service/impl/TaskServiceImpl.java | 707 ++++++++++-------- 3 files changed, 416 insertions(+), 309 deletions(-) diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/controller/ProcessTaskController.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/controller/ProcessTaskController.java index 04c07a41e..62d5be7df 100644 --- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/controller/ProcessTaskController.java +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/controller/ProcessTaskController.java @@ -48,13 +48,15 @@ public class ProcessTaskController { *

* @param taskId 任务ID * @param view 是否为查看模式:true-仅查看,false-可编辑处理 + * @param useMainFormData 是否使用主表单数据(true=从process_instance_record.form_data读取最新数据,false=从节点快照读取,默认true) * @return R 统一响应对象,包含任务的完整信息和可操作项 */ @SneakyThrows @GetMapping("queryTask") - public R queryTask(String taskId, boolean view) { + public R queryTask(String taskId, boolean view, + @RequestParam(defaultValue = "true") boolean useMainFormData) { - return taskService.queryTask(taskId, view); + return taskService.queryTask(taskId, view, useMainFormData); } diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/ITaskService.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/ITaskService.java index f4edd92e2..bff9632bd 100644 --- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/ITaskService.java +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/ITaskService.java @@ -24,6 +24,18 @@ public interface ITaskService { */ R queryTask(String taskId, boolean view); + /** + * 查询任务详情(支持选择表单数据源) + *

+ * 获取任务的详细信息,包括: - 任务基本信息(名称、创建时间、执行人等) - 所属流程信息 - 表单数据和权限 - 历史审批记录 + *

+ * @param taskId 任务ID + * @param view 是否为查看模式(true=只查看,false=处理模式) + * @param useMainFormData 是否使用主表单数据(true=从process_instance_record.form_data读取最新数据,false=从节点快照读取) + * @return 任务详情信息 + */ + R queryTask(String taskId, boolean view, boolean useMainFormData); + /** * 完成任务 *

diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceImpl.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceImpl.java index aac8309aa..de2f5b541 100644 --- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceImpl.java +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceImpl.java @@ -44,10 +44,10 @@ import java.util.*; *

* 该类是流程任务操作的核心服务,负责处理用户与流程任务的交互,主要功能包括: 1. 任务查询(待办任务详情、任务表单权限等) 2. 任务操作(完成、委托、转办、退回等) 3. * 任务统计(首页数据看板) 4. 流程控制(终止流程、加签等高级操作) - * + *

* 该服务整合了流程引擎的任务API,为前端提供统一的任务操作接口 *

- * + * * @author pigx code generator * @date 2023-10-01 */ @@ -56,364 +56,457 @@ import java.util.*; @RequiredArgsConstructor public class TaskServiceImpl implements ITaskService { - private final IProcessService processService; + private final IProcessService processService; - private final IProcessCopyService processCopyService; + private final IProcessCopyService processCopyService; - private final IProcessNodeDataService nodeDataService; + private final IProcessNodeDataService nodeDataService; - private final IProcessNodeRecordAssignUserService processNodeRecordAssignUserService; + private final IProcessNodeRecordAssignUserService processNodeRecordAssignUserService; - private final IProcessInstanceRecordService processInstanceRecordService; + private final IProcessInstanceRecordService processInstanceRecordService; - private final IFlowableEngineService flowableEngineService; + private final IFlowableEngineService flowableEngineService; - private final TaskService taskService; + private final TaskService taskService; - private final HistoryService historyService; + private final HistoryService historyService; - private final RuntimeService runtimeService; + private final RuntimeService runtimeService; - private final ObjectMapper objectMapper; + private final ObjectMapper objectMapper; - /** - * 查询首页数据看板 - *

- * 获取当前用户的任务统计数据,包括: 1. 待办任务数量 2. 已办任务数量 3. 我发起的流程数量 4. 抄送给我的数量 - *

- * @return R 包含IndexPageStatistics对象,含有各项统计数据 - */ - @Override - public R queryTaskData() { + /** + * 查询首页数据看板 + *

+ * 获取当前用户的任务统计数据,包括: 1. 待办任务数量 2. 已办任务数量 3. 我发起的流程数量 4. 抄送给我的数量 + *

+ * + * @return R 包含IndexPageStatistics对象,含有各项统计数据 + */ + @Override + public R queryTaskData() { - TaskQuery taskQuery = taskService.createTaskQuery(); + TaskQuery taskQuery = taskService.createTaskQuery(); - // 待办数量 - long pendingNum = taskQuery.taskAssignee(String.valueOf(SecurityUtils.getUser().getId())) - .taskTenantId(TenantContextHolder.getTenantId().toString()) - .count(); - // 已完成任务 - HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService - .createHistoricActivityInstanceQuery(); + // 待办数量 + long pendingNum = taskQuery.taskAssignee(String.valueOf(SecurityUtils.getUser().getId())) + .taskTenantId(TenantContextHolder.getTenantId().toString()) + .count(); + // 已完成任务 + HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService + .createHistoricActivityInstanceQuery(); - long completedNum = historicActivityInstanceQuery.taskAssignee(String.valueOf(SecurityUtils.getUser().getId())) - .tenantIdIn(List.of(TenantContextHolder.getTenantId().toString())) - .finished() - .count(); + long completedNum = historicActivityInstanceQuery.taskAssignee(String.valueOf(SecurityUtils.getUser().getId())) + .tenantIdIn(List.of(TenantContextHolder.getTenantId().toString())) + .finished() + .count(); - IndexPageStatistics indexPageStatistics = IndexPageStatistics.builder() - .pendingNum(pendingNum) - .completedNum(completedNum) - .build(); + IndexPageStatistics indexPageStatistics = IndexPageStatistics.builder() + .pendingNum(pendingNum) + .completedNum(completedNum) + .build(); - // 获取抄送任务 - Long copyCount = processCopyService.lambdaQuery() - .eq(ProcessCopy::getUserId, SecurityUtils.getUser().getId()) - .count(); - indexPageStatistics.setCopyNum(copyCount); - return R.ok(indexPageStatistics); - } + // 获取抄送任务 + Long copyCount = processCopyService.lambdaQuery() + .eq(ProcessCopy::getUserId, SecurityUtils.getUser().getId()) + .count(); + indexPageStatistics.setCopyNum(copyCount); + return R.ok(indexPageStatistics); + } - /** - * 查询任务 - *

- * 查询指定任务的详细信息,包括: 1. 任务的基本信息(节点名称、执行人等) 2. 流程表单定义和表单数据 3. 当前节点的表单权限配置 4. - * 任务是否可委托、是否是委托任务等状态 - * - * 对于已完成的任务,会从历史记录中获取任务数据 - *

- * @param taskId 任务ID - * @param view 是否是查看模式(true:只查看,false:可操作) - * @return R 包含任务详情的Dict对象 - * @throws Exception 当解析JSON数据失败时抛出异常 - */ - @SneakyThrows - @Override - public R queryTask(String taskId, boolean view) { + /** + * 查询任务 + *

+ * 查询指定任务的详细信息,包括: 1. 任务的基本信息(节点名称、执行人等) 2. 流程表单定义和表单数据 3. 当前节点的表单权限配置 4. + * 任务是否可委托、是否是委托任务等状态 + *

+ * 对于已完成的任务,会从历史记录中获取任务数据 + *

+ * + * @param taskId 任务ID + * @param view 是否是查看模式(true:只查看,false:可操作) + * @return R 包含任务详情的Dict对象 + * @throws Exception 当解析JSON数据失败时抛出异常 + */ + @SneakyThrows + @Override + public R queryTask(String taskId, boolean view) { + // 默认使用主表单数据源 + return queryTask(taskId, view, true); + } - long userId = SecurityUtils.getUser().getId(); + /** + * 查询任务(支持选择表单数据源) + *

+ * 查询指定任务的详细信息,包括: 1. 任务的基本信息(节点名称、执行人等) 2. 流程表单定义和表单数据 3. 当前节点的表单权限配置 4. + * 任务是否可委托、是否是委托任务等状态 + *

+ * 对于已完成的任务,可选择从主表单数据或节点快照读取表单数据 + *

+ * + * @param taskId 任务ID + * @param view 是否是查看模式(true:只查看,false:可操作) + * @param useMainFormData 是否使用主表单数据(true=从process_instance_record.form_data读取最新数据,false=从节点快照读取) + * @return R 包含任务详情的Dict对象 + * @throws Exception 当解析JSON数据失败时抛出异常 + */ + @SneakyThrows + @Override + public R queryTask(String taskId, boolean view, boolean useMainFormData) { - Optional task = Optional.ofNullable(taskService.createTaskQuery() - .taskId(taskId) - .taskTenantId(TenantContextHolder.getTenantId().toString()) - .taskAssignee(String.valueOf(userId)) - .singleResult()); - TaskResultDto taskResultDto = new TaskResultDto(); + long userId = SecurityUtils.getUser().getId(); - if (task.isPresent()) { - String processDefinitionId = task.get().getProcessDefinitionId(); - String taskDefinitionKey = task.get().getTaskDefinitionKey(); - DelegationState delegationState = task.get().getDelegationState(); - String processInstanceId = task.get().getProcessInstanceId(); - Object delegateVariable = taskService.getVariableLocal(taskId, "delegate"); + Optional task = Optional.ofNullable(taskService.createTaskQuery() + .taskId(taskId) + .taskTenantId(TenantContextHolder.getTenantId().toString()) + .taskAssignee(String.valueOf(userId)) + .singleResult()); + TaskResultDto taskResultDto = new TaskResultDto(); - String flowId = NodeUtil.getFlowId(processDefinitionId); - Map variables = taskService.getVariables(taskId); - Map variableAll = new HashMap<>(variables); + if (task.isPresent()) { + String processDefinitionId = task.get().getProcessDefinitionId(); + String taskDefinitionKey = task.get().getTaskDefinitionKey(); + DelegationState delegationState = task.get().getDelegationState(); + String processInstanceId = task.get().getProcessInstanceId(); + Object delegateVariable = taskService.getVariableLocal(taskId, "delegate"); - taskResultDto.setFlowId(flowId); - taskResultDto.setNodeId(taskDefinitionKey); - taskResultDto.setCurrentTask(true); - taskResultDto.setDelegate(Convert.toBool(delegateVariable, false)); - taskResultDto.setVariableAll(variableAll); - taskResultDto.setProcessInstanceId(processInstanceId); - taskResultDto.setDelegationState(delegationState == null ? null : delegationState.toString()); - } - else { - Optional historicTaskInstance = Optional - .ofNullable(historyService.createHistoricTaskInstanceQuery() - .taskId(taskId) - .taskAssignee(String.valueOf(userId)) - .singleResult()); + String flowId = NodeUtil.getFlowId(processDefinitionId); + Map variables = taskService.getVariables(taskId); + Map variableAll = new HashMap<>(variables); - String processDefinitionId = historicTaskInstance.get().getProcessDefinitionId(); - String taskDefinitionKey = historicTaskInstance.get().getTaskDefinitionKey(); - String processInstanceId = historicTaskInstance.get().getProcessInstanceId(); + taskResultDto.setFlowId(flowId); + taskResultDto.setNodeId(taskDefinitionKey); + taskResultDto.setCurrentTask(true); + taskResultDto.setDelegate(Convert.toBool(delegateVariable, false)); + taskResultDto.setVariableAll(variableAll); + taskResultDto.setProcessInstanceId(processInstanceId); + taskResultDto.setDelegationState(delegationState == null ? null : delegationState.toString()); + } else { + Optional historicTaskInstance = Optional + .ofNullable(historyService.createHistoricTaskInstanceQuery() + .taskId(taskId) + .taskAssignee(String.valueOf(userId)) + .singleResult()); - String flowId = NodeUtil.getFlowId(processDefinitionId); + String processDefinitionId = historicTaskInstance.get().getProcessDefinitionId(); + String taskDefinitionKey = historicTaskInstance.get().getTaskDefinitionKey(); + String processInstanceId = historicTaskInstance.get().getProcessInstanceId(); - List variableInstanceList = historyService.createHistoricVariableInstanceQuery() - .processInstanceId(processInstanceId) - .list(); - Map variableAll = new HashMap<>(); + String flowId = NodeUtil.getFlowId(processDefinitionId); - if (CollUtil.isNotEmpty(variableInstanceList)) { - variableInstanceList.forEach(historicVariableInstance -> variableAll - .put(historicVariableInstance.getVariableName(), historicVariableInstance.getValue())); - } + List variableInstanceList = historyService.createHistoricVariableInstanceQuery() + .processInstanceId(processInstanceId) + .list(); + Map variableAll = new HashMap<>(); - taskResultDto.setFlowId(flowId); - taskResultDto.setNodeId(taskDefinitionKey); - taskResultDto.setCurrentTask(false); - taskResultDto.setVariableAll(variableAll); - taskResultDto.setProcessInstanceId(processInstanceId); + if (CollUtil.isNotEmpty(variableInstanceList)) { + variableInstanceList.forEach(historicVariableInstance -> variableAll + .put(historicVariableInstance.getVariableName(), historicVariableInstance.getValue())); + } - } + taskResultDto.setFlowId(flowId); + taskResultDto.setNodeId(taskDefinitionKey); + taskResultDto.setCurrentTask(false); + taskResultDto.setVariableAll(variableAll); + taskResultDto.setProcessInstanceId(processInstanceId); - // 变量 - Map paramMap = taskResultDto.getVariableAll(); - // 是否是当前活动任务 - Boolean currentTask = taskResultDto.getCurrentTask(); - if (!currentTask) { - ProcessNodeRecordAssignUser processNodeRecordAssignUser = processNodeRecordAssignUserService.lambdaQuery() - .eq(ProcessNodeRecordAssignUser::getTaskId, taskId) - .eq(ProcessNodeRecordAssignUser::getUserId, userId) - .eq(ProcessNodeRecordAssignUser::getStatus, NodeStatusEnum.YJS.getCode()) - .last("limit 1") - .orderByDesc(ProcessNodeRecordAssignUser::getEndTime) - .one(); + } - if (processNodeRecordAssignUser != null) { - String data = processNodeRecordAssignUser.getData(); - if (StrUtil.isNotBlank(data)) { - Map collect = objectMapper.readValue(data, new TypeReference<>() { - }); - paramMap.putAll(collect); + // 变量 + Map paramMap = taskResultDto.getVariableAll(); + // 是否是当前活动任务 + Boolean currentTask = taskResultDto.getCurrentTask(); + if (!currentTask) { + // 判断是否使用主表单数据源 + boolean shouldUseMainFormData = view && useMainFormData; - } - } + if (shouldUseMainFormData) { + // 新增: 从主流程实例记录读取最新表单数据 + String processInstanceId = taskResultDto.getProcessInstanceId(); + ProcessInstanceRecord processInstanceRecord = processInstanceRecordService.lambdaQuery() + .eq(ProcessInstanceRecord::getProcessInstanceId, processInstanceId) + .one(); - } + if (processInstanceRecord != null && StrUtil.isNotBlank(processInstanceRecord.getFormData())) { + try { + Map mainFormData = objectMapper.readValue(processInstanceRecord.getFormData(), + new TypeReference>() { + }); + paramMap.putAll(mainFormData); + log.debug("Using main form data for completed task {}", taskId); + } catch (Exception e) { + log.warn("Failed to parse main form data, falling back to node snapshot: {}", e.getMessage()); + shouldUseMainFormData = false; + } + } else { + log.debug("Main form data empty, falling back to node snapshot for task {}", taskId); + shouldUseMainFormData = false; + } + } - // 当前节点数据 - Node node = nodeDataService.getNodeData(taskResultDto.getFlowId(), taskResultDto.getNodeId()).getData(); - Map formPerms = node.getFormPerms(); + // 回退: 使用节点级别快照(原有逻辑) + if (!shouldUseMainFormData) { + ProcessNodeRecordAssignUser processNodeRecordAssignUser = processNodeRecordAssignUserService.lambdaQuery() + .eq(ProcessNodeRecordAssignUser::getTaskId, taskId) + .eq(ProcessNodeRecordAssignUser::getUserId, userId) + .eq(ProcessNodeRecordAssignUser::getStatus, NodeStatusEnum.YJS.getCode()) + .last("limit 1") + .orderByDesc(ProcessNodeRecordAssignUser::getEndTime) + .one(); - Process oaForms = processService.getByFlowId(taskResultDto.getFlowId()); - if (oaForms == null) { - return R.failed("流程不存在"); - } + if (processNodeRecordAssignUser != null) { + String data = processNodeRecordAssignUser.getData(); + if (StrUtil.isNotBlank(data)) { + Map collect = objectMapper.readValue(data, new TypeReference<>() { + }); + paramMap.putAll(collect); + log.debug("Using node-level snapshot for task {}", taskId); + } + } + } - Dict set = Dict.create() - .set("processInstanceId", taskResultDto.getProcessInstanceId()) - .set("node", taskResultDto.getTaskNode()) - .set("process", oaForms.getProcess()) - .set("delegateAgain", taskResultDto.getDelegate()) - .set("delegationTask", StrUtil.equals(taskResultDto.getDelegationState(), "PENDING")) - .set("formItems", oaForms.getFormItems()) - .set("formData", paramMap) - .set("formConfig", oaForms.getFormConfig()) - .set("formPerms", formPerms); + } - return R.ok(set); - } + // 当前节点数据 + Node node = nodeDataService.getNodeData(taskResultDto.getFlowId(), taskResultDto.getNodeId()).getData(); + Map formPerms = node.getFormPerms(); - /** - * 完成任务 - *

- * 执行任务的完成操作,将任务标记为已完成并流转到下一个节点。 该方法会将当前用户ID设置到任务参数中,确保任务由正确的用户完成 - *

- * @param taskParamDto 任务参数,包含任务ID、审批意见、流程变量等 - * @return R 操作结果 - */ - @Override - public R completeTask(TaskParamDto taskParamDto) { - long userId = SecurityUtils.getUser().getId(); - taskParamDto.setUserId(String.valueOf(userId)); + Process oaForms = processService.getByFlowId(taskResultDto.getFlowId()); + if (oaForms == null) { + return R.failed("流程不存在"); + } - Map taskLocalParamMap = taskParamDto.getTaskLocalParamMap(); - if (CollUtil.isNotEmpty(taskLocalParamMap)) { - taskService.setVariablesLocal(taskParamDto.getTaskId(), taskLocalParamMap); - } + Dict set = Dict.create() + .set("processInstanceId", taskResultDto.getProcessInstanceId()) + .set("node", taskResultDto.getTaskNode()) + .set("process", oaForms.getProcess()) + .set("delegateAgain", taskResultDto.getDelegate()) + .set("delegationTask", StrUtil.equals(taskResultDto.getDelegationState(), "PENDING")) + .set("formItems", oaForms.getFormItems()) + .set("formData", paramMap) + .set("formConfig", oaForms.getFormConfig()) + .set("formPerms", formPerms); - if (CollUtil.isNotEmpty(taskParamDto.getFormData())) { - // 获取当前任务 - Task task = taskService.createTaskQuery().taskId(taskParamDto.getTaskId()).singleResult(); - runtimeService.setVariables(task.getExecutionId(), taskParamDto.getFormData()); - } + return R.ok(set); + } - taskService.complete(taskParamDto.getTaskId(), taskParamDto.getParamMap()); + /** + * 完成任务 + *

+ * 执行任务的完成操作,将任务标记为已完成并流转到下一个节点。 该方法会将当前用户ID设置到任务参数中,确保任务由正确的用户完成 + *

+ * + * @param taskParamDto 任务参数,包含任务ID、审批意见、流程变量等 + * @return R 操作结果 + */ + @Override + public R completeTask(TaskParamDto taskParamDto) { + long userId = SecurityUtils.getUser().getId(); + taskParamDto.setUserId(String.valueOf(userId)); - return R.ok(); - } + Map taskLocalParamMap = taskParamDto.getTaskLocalParamMap(); + if (CollUtil.isNotEmpty(taskLocalParamMap)) { + taskService.setVariablesLocal(taskParamDto.getTaskId(), taskLocalParamMap); + } - /** - * 前加签 - *

- * 将任务委托给其他用户处理,委托后任务会分配给被委托人, 被委托人完成后任务会返回给原委托人 - *

- * @param taskParamDto 任务参数,包含任务ID、被委托人ID列表等 - * @return R 操作结果 - */ - @Transactional - @Override - public R delegateTask(TaskParamDto taskParamDto) { - taskParamDto.setUserId(SecurityUtils.getUser().getId().toString()); - return flowableEngineService.delegateTask(taskParamDto); - } + if (CollUtil.isNotEmpty(taskParamDto.getFormData())) { + // 获取当前任务 + Task task = taskService.createTaskQuery().taskId(taskParamDto.getTaskId()).singleResult(); + runtimeService.setVariables(task.getExecutionId(), taskParamDto.getFormData()); - /** - * 加签完成任务 - *

- * 被委托人完成委托任务后,通过此方法将任务返回给原委托人。 这是委托流程的第二步,用于结束委托状态 - *

- * @param taskParamDto 任务参数,包含任务ID、审批意见等 - * @return R 操作结果 - */ - @Override - public R resolveTask(TaskParamDto taskParamDto) { - return flowableEngineService.resolveTask(taskParamDto); - } + // 新增: 同步更新流程实例记录的主表单数据 + String processInstanceId = task.getProcessInstanceId(); + ProcessInstanceRecord processInstanceRecord = processInstanceRecordService.lambdaQuery() + .eq(ProcessInstanceRecord::getProcessInstanceId, processInstanceId) + .one(); - /** - * 设置执行人 - *

- * 转办任务给其他用户,与委托不同,转办后任务直接由新执行人处理, 不会返回给原执行人 - *

- * @param taskParamDto 任务参数,包含任务ID、新执行人ID等 - * @return R 操作结果 - */ - @Override - public R setAssignee(TaskParamDto taskParamDto) { - Map taskLocalParamMap = taskParamDto.getTaskLocalParamMap(); - if (CollUtil.isNotEmpty(taskLocalParamMap)) { - taskService.setVariablesLocal(taskParamDto.getTaskId(), taskLocalParamMap); - } + if (processInstanceRecord != null) { + try { + // 合并现有表单数据和新提交的数据 + Map existingFormData = new HashMap<>(); + if (StrUtil.isNotBlank(processInstanceRecord.getFormData())) { + existingFormData = objectMapper.readValue(processInstanceRecord.getFormData(), + new TypeReference>() { + }); + } - // 设置任务的 owner 为当前任务的 assignee - Task task = taskService.createTaskQuery() - .taskId(taskParamDto.getTaskId()) - .includeTaskLocalVariables() - .singleResult(); - taskService.setOwner(taskParamDto.getTaskId(), task.getAssignee()); + // 用新数据覆盖现有数据 + existingFormData.putAll(taskParamDto.getFormData()); - // 设置任务的 assignee 为目标用户 (转办用户) - for (Long targetUserId : taskParamDto.getTargetUserIdList()) { - taskService.setAssignee(taskParamDto.getTaskId(), targetUserId.toString()); - } + // 更新到数据库 + String updatedFormData = objectMapper.writeValueAsString(existingFormData); + processInstanceRecordService.lambdaUpdate() + .set(ProcessInstanceRecord::getFormData, updatedFormData) + .eq(ProcessInstanceRecord::getProcessInstanceId, processInstanceId) + .update(); - // 更新表单 - if (CollUtil.isNotEmpty(taskParamDto.getFormData())) { - runtimeService.setVariables(task.getExecutionId(), taskParamDto.getFormData()); - } - return R.ok(); - } + log.debug("流程实例 {} 的表单数据已同步更新", processInstanceId); + } catch (Exception e) { + log.error("同步更新流程实例表单数据失败: {}", e.getMessage(), e); + // 不中断流程执行,仅记录错误 + } + } + } - /** - * 结束流程 - *

- * 强制终止流程实例的执行。该方法会: 1. 递归查找并终止所有子流程实例 2. 更新流程实例记录状态为已结束 3. - * 设置结束原因为强制终止(finishReason=9) - * - * 通常用于流程异常或需要强制结束的场景 - *

- * @param taskParamDto 任务参数,包含流程实例ID - * @return R 操作结果 - */ - @Override - public R stopProcessInstance(TaskParamDto taskParamDto) { - String reqProcessInstanceId = taskParamDto.getProcessInstanceId(); - List allStopProcessInstanceIdList = getAllStopProcessInstanceIdList(reqProcessInstanceId); - CollUtil.reverse(allStopProcessInstanceIdList); - allStopProcessInstanceIdList.add(reqProcessInstanceId); - allStopProcessInstanceIdList.forEach(processInstanceId -> { - // 查询流程实例 - ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() - .processInstanceId(processInstanceId) - .processInstanceTenantId(TenantContextHolder.getTenantId().toString()) - .singleResult(); - if (Optional.ofNullable(processInstance).isPresent()) { - // 查询执行实例 - List executionIds = runtimeService.createExecutionQuery() - .parentId(processInstanceId) - .list() - .stream() - .map(Execution::getId) - .toList(); - // 更改活动状态为结束 - runtimeService.createChangeActivityStateBuilder() - .moveExecutionsToSingleActivityId(executionIds, "end") - .changeState(); - } - }); + taskService.complete(taskParamDto.getTaskId(), taskParamDto.getParamMap()); - // 停止流程实例后,更新流程实例记录状态 - processInstanceRecordService.lambdaUpdate() - .set(ProcessInstanceRecord::getStatus, NodeStatusEnum.YJS.getCode()) - .set(ProcessInstanceRecord::getFinishReason, "9") - .set(ProcessInstanceRecord::getEndTime, new Date()) - .eq(ProcessInstanceRecord::getProcessInstanceId, reqProcessInstanceId) - .update(new ProcessInstanceRecord()); - return R.ok(); - } + return R.ok(); + } - /** - * 退回 - *

- * 将任务退回到指定的历史节点重新处理。 常用于审批不通过需要重新修改的场景 - *

- * @param taskParamDto 任务参数,包含任务ID、目标节点ID、退回原因等 - * @return R 操作结果 - */ - @Override - public R back(TaskParamDto taskParamDto) { - taskParamDto.setUserId(SecurityUtils.getUser().getId().toString()); - return flowableEngineService.back(taskParamDto); - } + /** + * 前加签 + *

+ * 将任务委托给其他用户处理,委托后任务会分配给被委托人, 被委托人完成后任务会返回给原委托人 + *

+ * + * @param taskParamDto 任务参数,包含任务ID、被委托人ID列表等 + * @return R 操作结果 + */ + @Transactional + @Override + public R delegateTask(TaskParamDto taskParamDto) { + taskParamDto.setUserId(SecurityUtils.getUser().getId().toString()); + return flowableEngineService.delegateTask(taskParamDto); + } - /** - * 递归获取所有需要停止的子流程实例ID列表 - *

- * 当一个流程包含子流程时,终止主流程需要同时终止所有子流程。 该方法递归查找指定流程的所有子流程实例ID - *

- * @param processInstanceId 父流程实例ID - * @return 所有子流程实例ID列表 - */ - private List getAllStopProcessInstanceIdList(String processInstanceId) { - List list = processInstanceRecordService.lambdaQuery() - .eq(ProcessInstanceRecord::getParentProcessInstanceId, processInstanceId) - .list(); + /** + * 加签完成任务 + *

+ * 被委托人完成委托任务后,通过此方法将任务返回给原委托人。 这是委托流程的第二步,用于结束委托状态 + *

+ * + * @param taskParamDto 任务参数,包含任务ID、审批意见等 + * @return R 操作结果 + */ + @Override + public R resolveTask(TaskParamDto taskParamDto) { + return flowableEngineService.resolveTask(taskParamDto); + } - List collect = new ArrayList<>(list.stream().map(ProcessInstanceRecord::getProcessInstanceId).toList()); + /** + * 设置执行人 + *

+ * 转办任务给其他用户,与委托不同,转办后任务直接由新执行人处理, 不会返回给原执行人 + *

+ * + * @param taskParamDto 任务参数,包含任务ID、新执行人ID等 + * @return R 操作结果 + */ + @Override + public R setAssignee(TaskParamDto taskParamDto) { + Map taskLocalParamMap = taskParamDto.getTaskLocalParamMap(); + if (CollUtil.isNotEmpty(taskLocalParamMap)) { + taskService.setVariablesLocal(taskParamDto.getTaskId(), taskLocalParamMap); + } - for (ProcessInstanceRecord processInstanceRecord : list) { - List allStopProcessInstanceIdList = getAllStopProcessInstanceIdList( - processInstanceRecord.getProcessInstanceId()); + // 设置任务的 owner 为当前任务的 assignee + Task task = taskService.createTaskQuery() + .taskId(taskParamDto.getTaskId()) + .includeTaskLocalVariables() + .singleResult(); + taskService.setOwner(taskParamDto.getTaskId(), task.getAssignee()); - collect.addAll(allStopProcessInstanceIdList); + // 设置任务的 assignee 为目标用户 (转办用户) + for (Long targetUserId : taskParamDto.getTargetUserIdList()) { + taskService.setAssignee(taskParamDto.getTaskId(), targetUserId.toString()); + } - } - return collect; - } + // 更新表单 + if (CollUtil.isNotEmpty(taskParamDto.getFormData())) { + runtimeService.setVariables(task.getExecutionId(), taskParamDto.getFormData()); + } + return R.ok(); + } + + /** + * 结束流程 + *

+ * 强制终止流程实例的执行。该方法会: 1. 递归查找并终止所有子流程实例 2. 更新流程实例记录状态为已结束 3. + * 设置结束原因为强制终止(finishReason=9) + *

+ * 通常用于流程异常或需要强制结束的场景 + *

+ * + * @param taskParamDto 任务参数,包含流程实例ID + * @return R 操作结果 + */ + @Override + public R stopProcessInstance(TaskParamDto taskParamDto) { + String reqProcessInstanceId = taskParamDto.getProcessInstanceId(); + List allStopProcessInstanceIdList = getAllStopProcessInstanceIdList(reqProcessInstanceId); + CollUtil.reverse(allStopProcessInstanceIdList); + allStopProcessInstanceIdList.add(reqProcessInstanceId); + allStopProcessInstanceIdList.forEach(processInstanceId -> { + // 查询流程实例 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(processInstanceId) + .processInstanceTenantId(TenantContextHolder.getTenantId().toString()) + .singleResult(); + if (Optional.ofNullable(processInstance).isPresent()) { + // 查询执行实例 + List executionIds = runtimeService.createExecutionQuery() + .parentId(processInstanceId) + .list() + .stream() + .map(Execution::getId) + .toList(); + // 更改活动状态为结束 + runtimeService.createChangeActivityStateBuilder() + .moveExecutionsToSingleActivityId(executionIds, "end") + .changeState(); + } + }); + + // 停止流程实例后,更新流程实例记录状态 + processInstanceRecordService.lambdaUpdate() + .set(ProcessInstanceRecord::getStatus, NodeStatusEnum.YJS.getCode()) + .set(ProcessInstanceRecord::getFinishReason, "9") + .set(ProcessInstanceRecord::getEndTime, new Date()) + .eq(ProcessInstanceRecord::getProcessInstanceId, reqProcessInstanceId) + .update(new ProcessInstanceRecord()); + return R.ok(); + } + + /** + * 退回 + *

+ * 将任务退回到指定的历史节点重新处理。 常用于审批不通过需要重新修改的场景 + *

+ * + * @param taskParamDto 任务参数,包含任务ID、目标节点ID、退回原因等 + * @return R 操作结果 + */ + @Override + public R back(TaskParamDto taskParamDto) { + taskParamDto.setUserId(SecurityUtils.getUser().getId().toString()); + return flowableEngineService.back(taskParamDto); + } + + /** + * 递归获取所有需要停止的子流程实例ID列表 + *

+ * 当一个流程包含子流程时,终止主流程需要同时终止所有子流程。 该方法递归查找指定流程的所有子流程实例ID + *

+ * + * @param processInstanceId 父流程实例ID + * @return 所有子流程实例ID列表 + */ + private List getAllStopProcessInstanceIdList(String processInstanceId) { + List list = processInstanceRecordService.lambdaQuery() + .eq(ProcessInstanceRecord::getParentProcessInstanceId, processInstanceId) + .list(); + + List collect = new ArrayList<>(list.stream().map(ProcessInstanceRecord::getProcessInstanceId).toList()); + + for (ProcessInstanceRecord processInstanceRecord : list) { + List allStopProcessInstanceIdList = getAllStopProcessInstanceIdList( + processInstanceRecord.getProcessInstanceId()); + + collect.addAll(allStopProcessInstanceIdList); + + } + return collect; + } } From 0ad5864476e755f14108c7e9c424a1620fcd613f Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Mon, 8 Dec 2025 20:01:41 +0800 Subject: [PATCH 28/44] =?UTF-8?q?feat(process):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=B5=81=E7=A8=8BID=E6=9C=89=E6=95=88=E6=80=A7=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在IProcessService接口添加validateFlowId方法声明 - 在ProcessServiceImpl实现流程ID验证逻辑 - 在ProcessController添加validateFlowId接口供前端调用 - 支持检查流程ID是否在IProcessInstanceStatusEventService实现类中注册 --- .../flow/controller/ProcessController.java | 18 +++++++++- .../pigx/flow/service/IProcessService.java | 10 +++++- .../flow/service/impl/ProcessServiceImpl.java | 35 +++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/controller/ProcessController.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/controller/ProcessController.java index a4c3d6c14..bd8b157bb 100644 --- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/controller/ProcessController.java +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/controller/ProcessController.java @@ -66,7 +66,7 @@ public class ProcessController { * - delete: 删除流程(逻辑删除,可恢复) * - 修改分组:传入groupId参数可将流程移动到其他分组 *

- * + * * @param flowId 流程定义ID * @param type 操作类型(stop/using/delete) * @param groupId 流程分组ID(可选,用于修改流程所属分组) @@ -78,4 +78,20 @@ public class ProcessController { return processService.update(flowId, type, groupId); } + /** + * 验证流程ID是否在所有IProcessInstanceStatusEventService实现类中存在 + *

+ * 用于前端表单验证,检查用户输入的流程ID是否已在系统中注册。 + * 流程ID必须在某个IProcessInstanceStatusEventService实现类的getFlowId()方法中返回, + * 才能被认为是有效的流程ID。 + *

+ * + * @param flowId 流程定义ID + * @return 验证结果:true表示流程ID存在,false表示不存在 + */ + @GetMapping("validateFlowId") + public R validateFlowId(@RequestParam String flowId) { + return processService.validateFlowId(flowId); + } + } diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/IProcessService.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/IProcessService.java index 345df70e8..abfa1f3c7 100644 --- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/IProcessService.java +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/IProcessService.java @@ -61,7 +61,7 @@ public interface IProcessService extends IService { /** * 更新流程状态或分组 - * + * * @param flowId 流程定义ID * @param type 操作类型:stop(停用)、using(启用)、delete(删除) * @param groupId 流程分组ID(可选,用于修改流程分组) @@ -69,4 +69,12 @@ public interface IProcessService extends IService { */ R update(String flowId, String type, Long groupId); + /** + * 验证流程ID是否在所有IProcessInstanceStatusEventService实现类中存在 + * + * @param flowId 流程定义ID + * @return 验证结果:true表示存在,false表示不存在 + */ + R validateFlowId(String flowId); + } diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/ProcessServiceImpl.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/ProcessServiceImpl.java index 08b48bf1b..3c67cef54 100644 --- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/ProcessServiceImpl.java +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/ProcessServiceImpl.java @@ -24,6 +24,7 @@ import com.pig4cloud.pigx.flow.dto.NodeUser; import com.pig4cloud.pigx.flow.entity.Process; import com.pig4cloud.pigx.flow.entity.ProcessStarter; import com.pig4cloud.pigx.flow.mapper.ProcessMapper; +import com.pig4cloud.pigx.flow.service.IProcessInstanceStatusEventService; import com.pig4cloud.pigx.flow.service.IProcessService; import com.pig4cloud.pigx.flow.service.IProcessStarterService; import com.pig4cloud.pigx.flow.support.utils.ModelUtil; @@ -42,6 +43,7 @@ import org.springframework.stereotype.Service; import java.util.Date; import java.util.List; import java.util.Objects; +import java.util.Optional; /** * 流程定义服务实现类 @@ -66,6 +68,8 @@ public class ProcessServiceImpl extends ServiceImpl impl private final Sequence flowSequence; + private final Optional> processInstanceStatusEventServicesOptional; + /** * 获取流程详细信息 *

@@ -295,4 +299,35 @@ public class ProcessServiceImpl extends ServiceImpl impl return R.ok(); } + /** + * 验证流程ID是否在所有IProcessInstanceStatusEventService实现类中存在 + *

+ * 遍历所有实现了IProcessInstanceStatusEventService接口的服务类,检查输入的flowId + * 是否与任意一个服务的getFlowId()返回值匹配。如果匹配则返回true,否则返回false。 + *

+ * + * @param flowId 流程定义ID + * @return 验证结果:true表示存在,false表示不存在 + */ + @Override + public R validateFlowId(String flowId) { + // 如果flowId为空,直接返回false + if (StrUtil.isBlank(flowId)) { + return R.ok(false); + } + + // 如果没有IProcessInstanceStatusEventService实现类,返回false + if (processInstanceStatusEventServicesOptional.isEmpty()) { + log.warn("未找到任何IProcessInstanceStatusEventService实现类"); + return R.ok(false); + } + + // 遍历所有实现类,检查flowId是否存在 + List serviceList = processInstanceStatusEventServicesOptional.get(); + boolean exists = serviceList.stream() + .anyMatch(service -> flowId.equals(service.getFlowId())); + + return R.ok(exists); + } + } From 16a7f9309f423b37d6e1208a374380226212aae9 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Tue, 9 Dec 2025 15:43:41 +0800 Subject: [PATCH 29/44] =?UTF-8?q?feat(flow):=20=E6=96=B0=E5=A2=9E=E9=A9=B3?= =?UTF-8?q?=E5=9B=9E=E5=88=B0=E5=8F=91=E8=B5=B7=E4=BA=BA=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在ModelUtil重构开始节点结构,添加root UserTask节点 - 新增RootTaskCreateListener处理驳回到发起人场景 - 新增RootTaskHandler解析发起人执行人 - 在ApproveServiceTask添加TO_START驳回处理逻辑 - 新增ProcessInstanceRecordVo包含驳回状态信息 - 在TaskService添加resubmitTask接口处理重新提交 - 在ProcessTaskController添加resubmitTask接口 --- .../constant/ProcessInstanceConstant.java | 10 +++ .../pigx/flow/vo/ProcessInstanceRecordVo.java | 38 ++++++++++ .../controller/ProcessTaskController.java | 19 +++++ .../pigx/flow/service/ITaskService.java | 14 ++++ .../impl/ProcessInstanceServiceImpl.java | 35 ++++++++- .../flow/service/impl/TaskServiceImpl.java | 74 +++++++++++++++++++ .../listeners/RootTaskCreateListener.java | 66 +++++++++++++++++ .../flow/support/node/RootTaskHandler.java | 67 +++++++++++++++++ .../support/tasks/ApproveServiceTask.java | 5 ++ .../pigx/flow/support/utils/ModelUtil.java | 57 ++++++++++++-- 10 files changed, 377 insertions(+), 8 deletions(-) create mode 100644 pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/vo/ProcessInstanceRecordVo.java create mode 100644 pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/listeners/RootTaskCreateListener.java create mode 100644 pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/node/RootTaskHandler.java diff --git a/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/constant/ProcessInstanceConstant.java b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/constant/ProcessInstanceConstant.java index c64c3f40c..2ab6f372c 100644 --- a/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/constant/ProcessInstanceConstant.java +++ b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/constant/ProcessInstanceConstant.java @@ -50,6 +50,16 @@ public class ProcessInstanceConstant { */ public static final String USER_TASK_REFUSE_TYPE_TO_NODE = "TO_NODE"; + /** + * 拒绝处理策略:驳回到发起人 审批人拒绝后,流程跳转回发起人节点,发起人可重新编辑提交 + */ + public static final String USER_TASK_REFUSE_TYPE_TO_START = "TO_START"; + + /** + * 流程变量:驳回到发起人标识 用于标记流程实例是否处于驳回待重新提交状态 + */ + public static final String REJECT_TO_STARTER_VAR = "rejectToStarter"; + /** * 多人审批模式:会签 所有审批人都必须同意才能通过,任何一人拒绝则不通过 */ diff --git a/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/vo/ProcessInstanceRecordVo.java b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/vo/ProcessInstanceRecordVo.java new file mode 100644 index 000000000..43b39975a --- /dev/null +++ b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/vo/ProcessInstanceRecordVo.java @@ -0,0 +1,38 @@ +package com.pig4cloud.pigx.flow.vo; + +import com.pig4cloud.pigx.flow.entity.ProcessInstanceRecord; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 流程实例记录视图对象 + *

+ * 继承自ProcessInstanceRecord实体类,用于扩展查询返回的额外信息。 + * 包含流程实例的驳回状态等运行时信息。 + *

+ * + * @author pigx + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class ProcessInstanceRecordVo extends ProcessInstanceRecord { + + /** + * 是否驳回到发起人状态 + *

+ * true表示流程已被驳回到发起人,等待发起人重新编辑提交; + * false或null表示流程正常运行中 + *

+ */ + private Boolean rejectToStarter; + + /** + * 重新提交的任务ID + *

+ * 当rejectToStarter为true时,此字段保存发起人节点(root)的任务ID, + * 用于前端调用重新提交接口 + *

+ */ + private String resubmitTaskId; + +} diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/controller/ProcessTaskController.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/controller/ProcessTaskController.java index 62d5be7df..94f0116d7 100644 --- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/controller/ProcessTaskController.java +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/controller/ProcessTaskController.java @@ -159,4 +159,23 @@ public class ProcessTaskController { return taskService.back(taskParamDto); } + /** + * 重新提交任务(驳回到发起人后) + *

+ * 当流程被驳回到发起人后,发起人可使用此接口重新编辑表单并提交。 + * 与completeTask不同: + * 1. 不需要填写审批意见 + * 2. 会自动清理驳回标识 + * 3. 提交后流程继续到下一个审批节点 + *

+ * @param taskParamDto 重新提交参数,包含: + * - taskId: 发起人节点的任务ID + * - formData: 更新后的表单数据 + * @return R 统一响应对象,操作成功返回提交结果 + */ + @PostMapping("resubmitTask") + public R resubmitTask(@RequestBody TaskParamDto taskParamDto) { + return taskService.resubmitTask(taskParamDto); + } + } diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/ITaskService.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/ITaskService.java index bff9632bd..b15de35a5 100644 --- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/ITaskService.java +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/ITaskService.java @@ -106,4 +106,18 @@ public interface ITaskService { */ R queryTaskData(); + /** + * 重新提交任务(驳回到发起人后) + *

+ * 当流程被驳回到发起人时,发起人使用此方法重新编辑表单并提交。 + * 与普通completeTask不同: + * 1. 不需要填写审批意见 + * 2. 会清理 rejectToStarter 流程变量 + * 3. 完成后流程继续到下一个审批节点 + *

+ * @param taskParamDto 任务参数,包含taskId和更新后的formData + * @return 操作结果 + */ + R resubmitTask(TaskParamDto taskParamDto); + } diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/ProcessInstanceServiceImpl.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/ProcessInstanceServiceImpl.java index 00fdd60e8..e3b09edd1 100644 --- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/ProcessInstanceServiceImpl.java +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/ProcessInstanceServiceImpl.java @@ -29,6 +29,7 @@ import com.pig4cloud.pigx.flow.support.utils.NodeUtil; import com.pig4cloud.pigx.flow.vo.NodeFormatParamVo; import com.pig4cloud.pigx.flow.vo.NodeVo; import com.pig4cloud.pigx.flow.vo.ProcessCopyVo; +import com.pig4cloud.pigx.flow.vo.ProcessInstanceRecordVo; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -364,7 +365,39 @@ public class ProcessInstanceServiceImpl implements IProcessInstanceService { Page instanceRecordPage = processInstanceRecordService.lambdaQuery().eq(ProcessInstanceRecord::getUserId, userId).eq(ProcessInstanceRecord::getStatus, taskQueryParamDto.getStatus()).between(ArrayUtil.isNotEmpty(taskQueryParamDto.getTaskTime()), ProcessInstanceRecord::getCreateTime, ArrayUtil.isNotEmpty(taskQueryParamDto.getTaskTime()) ? taskQueryParamDto.getTaskTime()[0] : null, ArrayUtil.isNotEmpty(taskQueryParamDto.getTaskTime()) ? taskQueryParamDto.getTaskTime()[1] : null).orderByDesc(ProcessInstanceRecord::getCreateTime).page(new Page<>(taskQueryParamDto.getCurrent(), taskQueryParamDto.getSize())); - return R.ok(instanceRecordPage); + // 转换为 VO 并添加驳回状态信息 + List voList = BeanUtil.copyToList(instanceRecordPage.getRecords(), ProcessInstanceRecordVo.class); + + for (ProcessInstanceRecordVo vo : voList) { + try { + // 从 Flowable 变量中获取 rejectToStarter 状态 + Object rejectVar = runtimeService.getVariable( + vo.getProcessInstanceId(), + "rejectToStarter" + ); + vo.setRejectToStarter(Boolean.TRUE.equals(rejectVar)); + + // 如果是驳回状态,获取 root 任务 ID 用于重新提交 + if (Boolean.TRUE.equals(vo.getRejectToStarter())) { + Task task = taskService.createTaskQuery() + .processInstanceId(vo.getProcessInstanceId()) + .taskDefinitionKey("root") + .singleResult(); + if (task != null) { + vo.setResubmitTaskId(task.getId()); + } + } + } catch (Exception e) { + // 流程可能已结束,设置为 false + vo.setRejectToStarter(false); + } + } + + // 构建返回的分页对象 + Page resultPage = new Page<>(instanceRecordPage.getCurrent(), instanceRecordPage.getSize(), instanceRecordPage.getTotal()); + resultPage.setRecords(voList); + + return R.ok(resultPage); } /** diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceImpl.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceImpl.java index de2f5b541..daf3163fd 100644 --- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceImpl.java +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceImpl.java @@ -10,6 +10,7 @@ import com.pig4cloud.pigx.common.core.util.R; import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder; import com.pig4cloud.pigx.common.security.util.SecurityUtils; import com.pig4cloud.pigx.flow.constant.NodeStatusEnum; +import com.pig4cloud.pigx.flow.constant.ProcessInstanceConstant; import com.pig4cloud.pigx.flow.dto.IndexPageStatistics; import com.pig4cloud.pigx.flow.dto.Node; import com.pig4cloud.pigx.flow.dto.TaskParamDto; @@ -483,6 +484,79 @@ public class TaskServiceImpl implements ITaskService { return flowableEngineService.back(taskParamDto); } + /** + * 重新提交任务(驳回到发起人后) + *

+ * 当流程被驳回到发起人时,发起人使用此方法重新编辑表单并提交。 + * 该方法会: + * 1. 验证任务存在且属于当前用户 + * 2. 验证任务是 root 节点任务 + * 3. 更新表单数据 + * 4. 清理 rejectToStarter 流程变量 + * 5. 完成任务,流程继续到下一个审批节点 + *

+ * + * @param taskParamDto 任务参数,包含taskId和更新后的formData + * @return R 操作结果 + */ + @Override + @SneakyThrows + @Transactional(rollbackFor = Exception.class) + public R resubmitTask(TaskParamDto taskParamDto) { + long userId = SecurityUtils.getUser().getId(); + + // 1. 获取任务并验证 + Task task = taskService.createTaskQuery() + .taskId(taskParamDto.getTaskId()) + .taskTenantId(TenantContextHolder.getTenantId().toString()) + .taskAssignee(String.valueOf(userId)) + .singleResult(); + + if (task == null) { + return R.failed("任务不存在或无权限操作"); + } + + // 2. 验证是 root 任务(发起人节点) + if (!StrUtil.equals(task.getTaskDefinitionKey(), "root")) { + return R.failed("非重新提交任务,无法操作"); + } + + String processInstanceId = task.getProcessInstanceId(); + + // 3. 更新表单数据 + if (CollUtil.isNotEmpty(taskParamDto.getFormData())) { + runtimeService.setVariables(task.getExecutionId(), taskParamDto.getFormData()); + + // 同步更新到 ProcessInstanceRecord + ProcessInstanceRecord processInstanceRecord = processInstanceRecordService.lambdaQuery() + .eq(ProcessInstanceRecord::getProcessInstanceId, processInstanceId) + .one(); + + if (processInstanceRecord != null) { + Map existingFormData = new HashMap<>(); + if (StrUtil.isNotBlank(processInstanceRecord.getFormData())) { + existingFormData = objectMapper.readValue(processInstanceRecord.getFormData(), + new TypeReference>() {}); + } + existingFormData.putAll(taskParamDto.getFormData()); + String updatedFormData = objectMapper.writeValueAsString(existingFormData); + + processInstanceRecordService.lambdaUpdate() + .set(ProcessInstanceRecord::getFormData, updatedFormData) + .eq(ProcessInstanceRecord::getProcessInstanceId, processInstanceId) + .update(); + } + } + + // 4. 清理 rejectToStarter 变量 + runtimeService.removeVariable(processInstanceId, "rejectToStarter"); + + // 5. 完成任务,流程继续 + taskService.complete(taskParamDto.getTaskId()); + + return R.ok("重新提交成功"); + } + /** * 递归获取所有需要停止的子流程实例ID列表 *

diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/listeners/RootTaskCreateListener.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/listeners/RootTaskCreateListener.java new file mode 100644 index 000000000..d5bc542bb --- /dev/null +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/listeners/RootTaskCreateListener.java @@ -0,0 +1,66 @@ +package com.pig4cloud.pigx.flow.support.listeners; + +import cn.hutool.extra.spring.SpringUtil; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.task.service.delegate.DelegateTask; +import org.flowable.task.service.delegate.TaskListener; + +/** + * 发起人任务创建监听器 + *

+ * 用于处理发起人节点(root节点)的任务创建事件。 + * 该监听器的核心职责是区分"正常发起流程"和"驳回到发起人"两种场景: + *

    + *
  • 正常发起流程:自动完成root任务,让流程继续到第一个审批节点
  • + *
  • 驳回到发起人:保留任务,让发起人可以在待办任务中看到并处理
  • + *
+ *

+ *

+ * 判断逻辑: + * 通过检查流程变量 rejectToStarter 是否为 true 来判断是否是驳回场景。 + * 该变量在 ApproveServiceTask 处理驳回到发起人时设置。 + *

+ * + * @author pigx + * @see com.pig4cloud.pigx.flow.support.tasks.ApproveServiceTask + */ +@Slf4j +public class RootTaskCreateListener implements TaskListener { + + /** + * 任务创建事件处理方法 + *

+ * 当root任务被创建时触发。根据是否是驳回场景决定是否自动完成任务。 + *

+ * + * @param delegateTask 委派的任务对象 + */ + @Override + public void notify(DelegateTask delegateTask) { + RuntimeService runtimeService = SpringUtil.getBean(RuntimeService.class); + TaskService taskService = SpringUtil.getBean(TaskService.class); + + String processInstanceId = delegateTask.getProcessInstanceId(); + String taskId = delegateTask.getId(); + String assignee = delegateTask.getAssignee(); + + log.debug("发起人任务创建 - 任务ID: {}, 执行人: {}, 流程实例ID: {}", taskId, assignee, processInstanceId); + + // 检查是否是驳回场景 + Object rejectToStarter = runtimeService.getVariable(processInstanceId, "rejectToStarter"); + + if (!Boolean.TRUE.equals(rejectToStarter)) { + // 非驳回场景:自动完成 root 任务,让流程继续到下一个节点 + log.debug("非驳回场景,自动完成发起人任务 - 任务ID: {}", taskId); + taskService.complete(taskId); + } + else { + // 驳回场景:保留任务给发起人处理 + // 发起人可以在待办任务列表中看到这个任务,并进行编辑重新提交 + log.info("驳回场景,保留任务给发起人 - 任务ID: {}, 执行人: {}", taskId, assignee); + } + } + +} diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/node/RootTaskHandler.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/node/RootTaskHandler.java new file mode 100644 index 000000000..c2da4e999 --- /dev/null +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/node/RootTaskHandler.java @@ -0,0 +1,67 @@ +package com.pig4cloud.pigx.flow.support.node; + +import cn.hutool.extra.spring.SpringUtil; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pig4cloud.pigx.flow.constant.ProcessInstanceConstant; +import com.pig4cloud.pigx.flow.dto.NodeUser; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 发起人任务处理器 + *

+ * 用于处理驳回到发起人场景下,发起人任务的执行人解析。 + * 该处理器作为Spring Bean注入,在BPMN模型中通过EL表达式调用。 + *

+ *

+ * 使用方式: + * 在UserTask的assignee属性中配置:${rootTaskHandler.resolveAssignee(execution)} + *

+ * + * @author pigx + */ +@Component("rootTaskHandler") +@Slf4j +public class RootTaskHandler { + + /** + * 解析发起人作为任务执行人 + *

+ * 从流程变量中获取发起人信息(root变量),返回发起人的用户ID作为任务执行人。 + * 该方法在任务创建时由Flowable引擎通过EL表达式调用。 + *

+ * + * @param execution 流程执行上下文,包含流程变量等信息 + * @return 发起人用户ID字符串,如果未找到则返回默认空执行人标识 + */ + public String resolveAssignee(DelegateExecution execution) { + // 从流程变量中获取发起人信息 + Object rootUserObj = execution.getVariable("root"); + if (rootUserObj == null) { + log.warn("未找到发起人信息,流程实例ID: {}", execution.getProcessInstanceId()); + return ProcessInstanceConstant.DEFAULT_EMPTY_ASSIGN.toString(); + } + + ObjectMapper objectMapper = SpringUtil.getBean(ObjectMapper.class); + try { + // 发起人信息存储为List格式 + List nodeUsers = objectMapper.readValue(objectMapper.writeValueAsString(rootUserObj), + new TypeReference>() { + }); + if (!nodeUsers.isEmpty()) { + Long userId = nodeUsers.get(0).getId(); + log.debug("解析发起人成功,用户ID: {}", userId); + return userId.toString(); + } + } + catch (Exception e) { + log.error("解析发起人信息失败,流程实例ID: {}", execution.getProcessInstanceId(), e); + } + return ProcessInstanceConstant.DEFAULT_EMPTY_ASSIGN.toString(); + } + +} diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/tasks/ApproveServiceTask.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/tasks/ApproveServiceTask.java index f1d2e4ad1..7a0a22db1 100644 --- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/tasks/ApproveServiceTask.java +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/tasks/ApproveServiceTask.java @@ -105,6 +105,11 @@ public class ApproveServiceTask implements JavaDelegate { // 根据拒绝设置重定向到特定节点 stateChangeBuilder.moveActivityIdTo(currentActivityId, refuseConfig.getNodeId()).changeState(); } + else if (StrUtil.equals(rejectionHandler, "TO_START")) { + // 驳回到发起人:设置驳回标识变量,跳转到 root 节点 + runtimeService.setVariable(processInstanceId, "rejectToStarter", true); + stateChangeBuilder.moveActivityIdTo(currentActivityId, "root").changeState(); + } else { // 默认行为:重定向到结束节点 stateChangeBuilder.moveActivityIdTo(currentActivityId, "end").changeState(); diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/utils/ModelUtil.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/utils/ModelUtil.java index a126071a2..d9fdd3d89 100644 --- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/utils/ModelUtil.java +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/utils/ModelUtil.java @@ -13,6 +13,7 @@ import com.pig4cloud.pigx.flow.service.IProcessNodeDataService; import com.pig4cloud.pigx.flow.support.expression.condition.NodeExpressionStrategyFactory; import com.pig4cloud.pigx.flow.support.listeners.ApprovalCreateListener; import com.pig4cloud.pigx.flow.support.listeners.FlowProcessEventListener; +import com.pig4cloud.pigx.flow.support.listeners.RootTaskCreateListener; import com.pig4cloud.pigx.flow.support.tasks.ApproveServiceTask; import com.pig4cloud.pigx.flow.support.tasks.CopyServiceTask; import lombok.extern.slf4j.Slf4j; @@ -352,22 +353,53 @@ public class ModelUtil { /** * 构建开始节点 *

- * 创建BPMN流程的开始事件节点。 - * 每个流程必须有且只有一个开始节点。 + * 创建BPMN流程的开始事件节点和发起人任务节点。 + * 流程结构:StartEvent(start) → UserTask(root) → 第一个审批节点 + *

+ *

+ * 发起人任务节点的作用: + *

    + *
  • 正常发起流程时,通过RootTaskCreateListener自动完成,流程继续到下一个节点
  • + *
  • 驳回到发起人时,任务保留给发起人,发起人可以在待办任务中编辑并重新提交
  • + *
+ *

* * @param node 前端传输的开始节点数据 - * @return 包含开始事件的流程元素列表 + * @return 包含开始事件和发起人任务的流程元素列表 */ private static List buildStartNode(Node node) { List flowElementList = new ArrayList<>(); + // 1. 创建真正的开始事件(ID 为 start) StartEvent startEvent = new StartEvent(); - startEvent.setId(node.getId()); - startEvent.setName(node.getName()); - + startEvent.setId("start"); + startEvent.setName("开始"); flowElementList.add(startEvent); + // 2. 创建发起人任务节点(ID 保持为 root) + // 配置任务监听器,用于处理驳回场景 + FlowableListener createListener = new FlowableListener(); + createListener.setImplementation(RootTaskCreateListener.class.getCanonicalName()); + createListener.setImplementationType("class"); + createListener.setEvent("create"); + + UserTask rootTask = new UserTask(); + rootTask.setId(node.getId()); // 保持 "root" 作为 ID + rootTask.setName(node.getName()); + // 动态获取发起人作为执行人 + rootTask.setAssignee("${rootTaskHandler.resolveAssignee(execution)}"); + + List taskListeners = new ArrayList<>(); + taskListeners.add(createListener); + rootTask.setTaskListeners(taskListeners); + + flowElementList.add(rootTask); + + // 3. 更新节点的 head/tail 信息 + node.setHeadId("start"); // 头部是 StartEvent + node.setTailId(node.getId()); // 尾部是 root UserTask + return flowElementList; } @@ -655,7 +687,11 @@ public class ModelUtil { /** * 创建节点内部连接线 *

- * 创建节点内部的连接线,例如审批节点与其后续服务任务之间的连接。 + * 创建节点内部的连接线,例如: + *

    + *
  • 开始节点:start → root(StartEvent到发起人UserTask)
  • + *
  • 审批节点:审批任务 → 服务任务
  • + *
* * @param node 节点数据 * @param flowId 流程定义ID @@ -673,6 +709,13 @@ public class ModelUtil { return sequenceFlowList; } + // 开始节点:创建 start → root 的连线 + if (node.getType() == NodeTypeEnum.ROOT.getValue().intValue()) { + SequenceFlow sequenceFlow = buildSingleSequenceFlow("start", nodeId, null, "start->root"); + sequenceFlowList.add(sequenceFlow); + } + + // 审批节点:创建审批任务到服务任务的连线 if (node.getType() == NodeTypeEnum.APPROVAL.getValue().intValue()) { String gatewayId = StrUtil.format("approve_service_task_{}", nodeId); From 825b9a147b8bd9c59526a803a0b7559b359cd36a Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Wed, 10 Dec 2025 10:17:04 +0800 Subject: [PATCH 30/44] =?UTF-8?q?feat(data-permission):=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=95=B0=E6=8D=AE=E6=9D=83=E9=99=90=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E7=BB=84=E5=90=88=E6=A8=A1=E5=BC=8F=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增DataScopeLogicEnum枚举类定义AND/OR逻辑组合模式 - 在DataScope类中添加logicMode字段控制权限组合方式 - 修改DataScopeInnerInterceptor支持动态组合权限条件 - 默认保持OR逻辑以向后兼容现有功能 --- .../pigx/common/data/datascope/DataScope.java | 20 +++++++ .../datascope/DataScopeInnerInterceptor.java | 9 +++- .../data/datascope/DataScopeLogicEnum.java | 53 +++++++++++++++++++ 3 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/DataScopeLogicEnum.java diff --git a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/DataScope.java b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/DataScope.java index f609b6aeb..34b088553 100644 --- a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/DataScope.java +++ b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/DataScope.java @@ -71,6 +71,16 @@ public class DataScope extends HashMap { */ private DataScopeFuncEnum func = DataScopeFuncEnum.ALL; + /** + * 逻辑组合模式,默认OR(向后兼容) + * 当同时有本人权限(username)和部门权限(deptList)时的逻辑组合方式 + *
    + *
  • OR: 满足任一条件即可(默认)
  • + *
  • AND: 需同时满足所有条件
  • + *
+ */ + private DataScopeLogicEnum logicMode = DataScopeLogicEnum.OR; + /** * of 获取实例 */ @@ -93,4 +103,14 @@ public class DataScope extends HashMap { return this; } + /** + * 设置逻辑组合模式 + * @param logicMode ��辑模式(AND/OR) + * @return DataScope + */ + public DataScope logic(DataScopeLogicEnum logicMode) { + this.logicMode = logicMode; + return this; + } + } diff --git a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/DataScopeInnerInterceptor.java b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/DataScopeInnerInterceptor.java index 90e736b1b..74f2e8d3e 100644 --- a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/DataScopeInnerInterceptor.java +++ b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/DataScopeInnerInterceptor.java @@ -63,8 +63,13 @@ public class DataScopeInnerInterceptor implements DataScopeInterceptor { // 2.如果为本人权限 + 部门权限控制 else if (StrUtil.isNotBlank(dataScope.getUsername()) && CollUtil.isNotEmpty(deptIds)) { String join = CollectionUtil.join(deptIds, ","); - originalSql = String.format("SELECT %s FROM (%s) temp_data_scope WHERE temp_data_scope.%s = '%s' OR temp_data_scope.%s IN (%s)", - dataScope.getFunc().getType(), originalSql, dataScope.getScopeUserName(), dataScope.getUsername(), dataScope.getScopeDeptName(), join); + // 使用配置的组合模式(AND/OR),默认OR保持向后兼容 + String logicKeyword = dataScope.getLogicMode() != null ? dataScope.getLogicMode().getKeyword() + : DataScopeLogicEnum.OR.getKeyword(); + originalSql = String.format( + "SELECT %s FROM (%s) temp_data_scope WHERE temp_data_scope.%s = '%s' %s temp_data_scope.%s IN (%s)", + dataScope.getFunc().getType(), originalSql, dataScope.getScopeUserName(), dataScope.getUsername(), + logicKeyword, dataScope.getScopeDeptName(), join); } // 3. 如果为本人 else if (StrUtil.isNotBlank(dataScope.getUsername())) { diff --git a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/DataScopeLogicEnum.java b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/DataScopeLogicEnum.java new file mode 100644 index 000000000..5f17fa794 --- /dev/null +++ b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/DataScopeLogicEnum.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018-2025, lengleng All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the pig4cloud.com developer nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Author: lengleng (wangiegie@gmail.com) + */ + +package com.pig4cloud.pigx.common.data.datascope; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据权限逻辑组合类型 + * + * @author lengleng + * @date 2025-12-10 + */ +@Getter +@AllArgsConstructor +public enum DataScopeLogicEnum { + + /** + * OR逻辑 - 满足任一条件即可(默认) 示例: WHERE create_by='user' OR dept_id IN (1,2,3) + */ + OR("OR", "或"), + + /** + * AND逻辑 - 需同时满足所有条件 示例: WHERE create_by='user' AND dept_id IN (1,2,3) + */ + AND("AND", "且"); + + /** + * SQL关键字 + */ + private final String keyword; + + /** + * 描述 + */ + private final String description; + +} From 79117616531670b86ad4377c926ac154e20e52e7 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Wed, 10 Dec 2025 17:47:37 +0800 Subject: [PATCH 31/44] =?UTF-8?q?refactor(RemoteUserService):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=96=B9=E6=B3=95=E5=8F=82=E6=95=B0=E6=B3=A8=E8=A7=A3?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E4=BB=A5=E6=8F=90=E9=AB=98=E5=8F=AF=E8=AF=BB?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/api/feign/RemoteUserService.java | 145 +++++++++--------- 1 file changed, 76 insertions(+), 69 deletions(-) diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/feign/RemoteUserService.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/feign/RemoteUserService.java index cb98cf4b6..6756e33d8 100644 --- a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/feign/RemoteUserService.java +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/feign/RemoteUserService.java @@ -37,82 +37,89 @@ import java.util.List; @FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.UPMS_SERVICE) public interface RemoteUserService { - /** - * 通过用户名查询用户、角色信息 - * @param username 用户名 - * @return R - */ - @NoToken - @GetMapping("/user/info/{username}") - R info(@PathVariable("username") String username); + /** + * 通过用户名查询用户、角色信息 + * + * @param username 用户名 + * @return R + */ + @NoToken + @GetMapping("/user/info/{username}") + R info(@PathVariable String username); - /** - * 根据用户ID获取用户 - * @param userIds ID - * @return SysUser - */ - @GetMapping("/user/list") - R> getUserById(@RequestParam("userIds") List userIds); + /** + * 根据用户ID获取用户 + * + * @param userIds ID + * @return SysUser + */ + @GetMapping("/user/list") + R> getUserById(@RequestParam("userIds") List userIds); - /** - * 通过社交账号或手机号查询用户、角色信息 - * @param inStr appid@code - * @return - */ - @NoToken - @GetMapping("/social/info/{inStr}") - R social(@PathVariable("inStr") String inStr); + /** + * 通过社交账号或手机号查询用户、角色信息 + * + * @param inStr appid@code + * @return + */ + @NoToken + @GetMapping("/social/info/{inStr}") + R social(@PathVariable String inStr); - /** - * 查询上级部门的用户信息 - * @param username 用户名 - * @return R - */ - @GetMapping("/user/ancestor/{username}") - R> ancestorUsers(@PathVariable("username") String username); + /** + * 查询上级部门的用户信息 + * + * @param username 用户名 + * @return R + */ + @GetMapping("/user/ancestor/{username}") + R> ancestorUsers(@PathVariable String username); - /** - * 锁定用户 - * @param username 用户名 - * @return - */ - @NoToken - @PutMapping("/user/lock/{username}") - R lockUser(@PathVariable("username") String username); + /** + * 锁定用户 + * + * @param username 用户名 + * @return + */ + @NoToken + @PutMapping("/user/lock/{username}") + R lockUser(@PathVariable String username); - /** - * 根据角色ID查询用户列表 - * @param roleIdList 角色ID列表 - * @return 用户ID列表 - */ - @NoToken - @GetMapping("/user/getUserIdListByRoleIdList") - R> getUserIdListByRoleIdList(@RequestParam("roleIdList") List roleIdList); + /** + * 根据角色ID查询用户列表 + * + * @param roleIdList 角色ID列表 + * @return 用户ID列表 + */ + @GetMapping("/user/getUserIdListByRoleIdList") + R> getUserIdListByRoleIdList(@RequestParam("roleIdList") List roleIdList); - /** - * 根据部门ID列表获取用户ID列表接口 - * @param deptIdList 部门ID列表 - * @return 用户ID列表 - */ - @NoToken - @GetMapping("/user/getUserIdListByDeptIdList") - R> getUserIdListByDeptIdList(@RequestParam("deptIdList") List deptIdList); + /** + * 根据部门ID列表获取用户ID列表接口 + * + * @param deptIdList 部门ID列表 + * @return 用户ID列表 + */ + @GetMapping("/user/getUserIdListByDeptIdList") + R> getUserIdListByDeptIdList(@RequestParam("deptIdList") List deptIdList); - /** - * 通过用户名查询用户列表 - * @param userName 用户名 - * @return 用户列表 - */ - @GetMapping("/user/getUserListByUserName") - R> getUserListByUserName(@RequestParam("username") String userName); + /** + * 通过用户名查询用户列表 + * + * @param userName 用户名 + * @return 用户列表 + */ + @GetMapping("/user/getUserListByUserName") + R> getUserListByUserName(@RequestParam("username") String userName); - /** - * 注册用户 - * @param userDTO 用户信息 - * @return success/false - */ - @NoToken - @PostMapping("/register/user") - R registerUser(@RequestBody UserDTO userDTO); + /** + * 注册用户 + * + * @param userDTO 用户信息 + * @return success/false + */ + @NoToken + @PostMapping("/register/user") + R registerUser(@RequestBody UserDTO userDTO); } From f30b32a28a71229dc5768ea9c05bb4f0237009a7 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Fri, 12 Dec 2025 16:08:26 +0800 Subject: [PATCH 32/44] =?UTF-8?q?feat(upms):=20=E9=87=8D=E6=9E=84=E5=9B=BD?= =?UTF-8?q?=E9=99=85=E5=8C=96=E6=B6=88=E6=81=AF=E5=92=8C=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=A0=81=E6=A8=A1=E5=9D=97=E5=8C=96=E6=9E=B6=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除全局ErrorCodes接口,按模块拆分Auth/Upms/App错误码定义 - 新增模块化i18n消息文件,支持多JAR包扫描加载机制 - 重构MsgUtils支持LocaleContextHolder动态区域解析 - 统一OAuthClientException异常处理支持错误码和描述参数 - 优化企业微信/钉钉同步错误消息国际化支持 --- .../pigx/app/api/enums/AppErrorCodes.java | 28 + .../resources/i18n/messages_en.properties | 14 + .../resources/i18n/messages_zh_CN.properties | 14 + .../app/controller/AppUserController.java | 276 +++++---- .../service/impl/AppMobileServiceImpl.java | 37 +- .../app/service/impl/AppRoleServiceImpl.java | 148 +++-- .../app/service/impl/AppUserServiceImpl.java | 574 +++++++++--------- .../pigx/auth/endpoint/PigxTokenEndpoint.java | 6 +- .../auth/support/core/AuthErrorCodes.java | 18 + .../support/filter/ValidateCodeFilter.java | 14 +- .../resources/i18n/messages_en.properties | 5 + .../resources/i18n/messages_zh_CN.properties | 5 + .../config/MessageSourceConfiguration.java | 142 ++++- .../common/core/exception/ErrorCodes.java | 118 ---- .../pigx/common/core/util/MsgUtils.java | 77 ++- ...igxSecurityMessageSourceConfiguration.java | 18 +- .../security/util/OAuthClientException.java | 8 + .../admin/api/constant/UpmsErrorCodes.java | 97 +++ .../resources/i18n/messages_en.properties | 62 ++ .../resources/i18n/messages_zh_CN.properties | 54 +- .../controller/SysSystemInfoController.java | 49 +- .../admin/handler/MiniAppLoginHandler.java | 4 +- .../service/impl/ConnectServiceImpl.java | 6 +- .../service/impl/SysDeptServiceImpl.java | 573 ++++++++--------- .../service/impl/SysDictItemServiceImpl.java | 72 +-- .../service/impl/SysDictServiceImpl.java | 91 +-- .../service/impl/SysMenuServiceImpl.java | 15 +- .../service/impl/SysMessageServiceImpl.java | 15 +- .../service/impl/SysPostServiceImpl.java | 121 ++-- .../impl/SysPublicParamServiceImpl.java | 130 ++-- .../service/impl/SysRoleServiceImpl.java | 209 +++---- .../service/impl/SysUserServiceImpl.java | 40 +- 32 files changed, 1719 insertions(+), 1321 deletions(-) create mode 100644 pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/enums/AppErrorCodes.java create mode 100644 pigx-app-server/pigx-app-server-api/src/main/resources/i18n/messages_en.properties create mode 100644 pigx-app-server/pigx-app-server-api/src/main/resources/i18n/messages_zh_CN.properties create mode 100644 pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/core/AuthErrorCodes.java create mode 100644 pigx-auth/src/main/resources/i18n/messages_en.properties create mode 100644 pigx-auth/src/main/resources/i18n/messages_zh_CN.properties delete mode 100644 pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/exception/ErrorCodes.java create mode 100644 pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/constant/UpmsErrorCodes.java create mode 100644 pigx-upms/pigx-upms-api/src/main/resources/i18n/messages_en.properties rename {pigx-common/pigx-common-core => pigx-upms/pigx-upms-api}/src/main/resources/i18n/messages_zh_CN.properties (55%) diff --git a/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/enums/AppErrorCodes.java b/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/enums/AppErrorCodes.java new file mode 100644 index 000000000..7de762840 --- /dev/null +++ b/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/enums/AppErrorCodes.java @@ -0,0 +1,28 @@ +package com.pig4cloud.pigx.app.api.enums; + +/** + * App Server 模块错误编码 + * + * @author lengleng + * @date 2022/3/30 + */ +public interface AppErrorCodes { + + // === App 用户相关 === + String APP_USER_USERINFO_EMPTY = "app.user.userInfo.empty"; + + String APP_USER_USERNAME_EXISTING = "app.user.username.existing"; + + String APP_USER_IMPORT_SUCCEED = "app.user.import.succeed"; + + String APP_USER_QUERY_ERROR = "app.user.query.error"; + + // === App 角色相关 === + String APP_ROLE_ROLENAME_INEXISTENCE = "app.role.roleName.inexistence"; + + String APP_ROLE_NAMEORCODE_EXISTING = "app.role.nameOrCode.existing"; + + // === App 短信相关 === + String APP_SMS_OFTEN = "app.sms.often"; + +} diff --git a/pigx-app-server/pigx-app-server-api/src/main/resources/i18n/messages_en.properties b/pigx-app-server/pigx-app-server-api/src/main/resources/i18n/messages_en.properties new file mode 100644 index 000000000..aef21848c --- /dev/null +++ b/pigx-app-server/pigx-app-server-api/src/main/resources/i18n/messages_en.properties @@ -0,0 +1,14 @@ +# App Server Module i18n Messages - English + +# App user related +app.user.userInfo.empty=App user information is empty for {0} +app.user.username.existing=Username {0} already exists +app.user.import.succeed=User import succeeded; default password is the phone number +app.user.query.error=Failed to fetch current user information + +# App role related +app.role.roleName.inexistence=Role name {0} does not exist +app.role.nameOrCode.existing=Role name or code already exists ({0} {1}) + +# App SMS related +app.sms.often=Verification code sent too frequently diff --git a/pigx-app-server/pigx-app-server-api/src/main/resources/i18n/messages_zh_CN.properties b/pigx-app-server/pigx-app-server-api/src/main/resources/i18n/messages_zh_CN.properties new file mode 100644 index 000000000..1fc97d12c --- /dev/null +++ b/pigx-app-server/pigx-app-server-api/src/main/resources/i18n/messages_zh_CN.properties @@ -0,0 +1,14 @@ +# App Server 模块国际化消息 - 中文 + +# App 用户相关 +app.user.userInfo.empty={0} \u7528\u6237\u4FE1\u606F\u4E3A\u7A7A +app.user.username.existing={0} \u7528\u6237\u540D\u5DF2\u5B58\u5728 +app.user.import.succeed=\u7528\u6237\u5BFC\u5165\u6210\u529F\uFF0C\u9ED8\u8BA4\u5BC6\u7801\u4E3A\u624B\u673A\u53F7 +app.user.query.error=\u83B7\u53D6\u5F53\u524D\u7528\u6237\u4FE1\u606F\u5931\u8D25 + +# App 角色相关 +app.role.roleName.inexistence={0} \u89D2\u8272\u540D\u79F0\u4E0D\u5B58\u5728 +app.role.nameOrCode.existing={0} {1} \u89D2\u8272\u540D\u6216\u89D2\u8272\u7F16\u7801\u5DF2\u7ECF\u5B58\u5728 + +# App 短信相关 +app.sms.often=\u9A8C\u8BC1\u7801\u53D1\u9001\u8FC7\u9891\u7E41 diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppUserController.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppUserController.java index 9ccb5162f..73eb55977 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppUserController.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppUserController.java @@ -21,11 +21,11 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.pig4cloud.pigx.app.api.dto.AppUserDTO; import com.pig4cloud.pigx.app.api.entity.AppUser; +import com.pig4cloud.pigx.app.api.enums.AppErrorCodes; import com.pig4cloud.pigx.app.api.vo.AppUserExcelVO; import com.pig4cloud.pigx.app.service.AppUserService; import com.pig4cloud.pigx.common.core.constant.CommonConstants; import com.pig4cloud.pigx.common.core.constant.enums.UserTypeEnum; -import com.pig4cloud.pigx.common.core.exception.ErrorCodes; import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; import com.pig4cloud.pigx.common.excel.annotation.RequestExcel; @@ -58,150 +58,158 @@ import java.util.List; @SecurityRequirement(name = HttpHeaders.AUTHORIZATION) public class AppUserController { - private final AppUserService appUserService; + private final AppUserService appUserService; - @Inner - @GetMapping("/info/{username}") - public R info(@PathVariable String username) { - AppUser user = appUserService.getOne(Wrappers.query() - .lambda() - .eq(AppUser::getUsername, username) - .or() - .eq(AppUser::getPhone, username)); - if (user == null) { - return R.failed(MsgUtils.getMessage(ErrorCodes.APP_USER_USERINFO_EMPTY, username)); - } - return R.ok(appUserService.findUserInfo(user)); - } + @Inner + @GetMapping("/info/{username}") + public R info(@PathVariable String username) { + AppUser user = appUserService.getOne(Wrappers.query() + .lambda() + .eq(AppUser::getUsername, username) + .or() + .eq(AppUser::getPhone, username)); + if (user == null) { + return R.failed(MsgUtils.getMessage(AppErrorCodes.APP_USER_USERINFO_EMPTY, username)); + } + return R.ok(appUserService.findUserInfo(user)); + } - /** - * 获取当前用户全部信息 - * @return 用户信息 - */ - @GetMapping(value = { "/info" }) - public R info() { - String username = SecurityUtils.getUser().getUsername(); - AppUser user = appUserService.getOne(Wrappers.query().lambda().eq(AppUser::getUsername, username)); - if (user == null) { - return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_USER_QUERY_ERROR)); - } - return R.ok(appUserService.findUserInfo(user)); - } + /** + * 获取当前用户全部信息 + * + * @return 用户信息 + */ + @GetMapping(value = {"/info"}) + public R info() { + String username = SecurityUtils.getUser().getUsername(); + AppUser user = appUserService.getOne(Wrappers.query().lambda().eq(AppUser::getUsername, username)); + if (user == null) { + return R.failed(MsgUtils.getMessage(AppErrorCodes.APP_USER_QUERY_ERROR)); + } + return R.ok(appUserService.findUserInfo(user)); + } - /** - * 分页查询 - * @param page 分页对象 - * @param appUserDTO app用户表 - * @return - */ - @Operation(summary = "分页查询", description = "分页查询") - @GetMapping("/page") - public R getAppUserPage(Page page, AppUserDTO appUserDTO) { - return R.ok(appUserService.getUsersWithRolePage(page, appUserDTO)); - } + /** + * 分页查询 + * + * @param page 分页对象 + * @param appUserDTO app用户表 + * @return + */ + @Operation(summary = "分页查询", description = "分页查询") + @GetMapping("/page") + public R getAppUserPage(Page page, AppUserDTO appUserDTO) { + return R.ok(appUserService.getUsersWithRolePage(page, appUserDTO)); + } - /** - * 通过id查询app用户表 - * @param userId id - * @return R - */ - @Operation(summary = "通过id查询", description = "通过id查询") - @GetMapping("/details/{userId}") - public R getById(@PathVariable("userId") Long userId) { - return R.ok(appUserService.selectUserVoById(userId)); - } + /** + * 通过id查询app用户表 + * + * @param userId id + * @return R + */ + @Operation(summary = "通过id查询", description = "通过id查询") + @GetMapping("/details/{userId}") + public R getById(@PathVariable("userId") Long userId) { + return R.ok(appUserService.selectUserVoById(userId)); + } - /** - * @return R - */ - @Operation(summary = "通过userName查询", description = "通过userName查询") - @GetMapping("/details") - public R getByUserName(AppUser user) { - AppUser one = appUserService.getOne(Wrappers.query(user), false); - return R.ok(one == null ? null : CommonConstants.SUCCESS); - } + /** + * @return R + */ + @Operation(summary = "通过userName查询", description = "通过userName查询") + @GetMapping("/details") + public R getByUserName(AppUser user) { + AppUser one = appUserService.getOne(Wrappers.query(user), false); + return R.ok(one == null ? null : CommonConstants.SUCCESS); + } - /** - * 新增app用户表 - * @param appUser app用户表 - * @return R - */ - @Operation(summary = "新增app用户表", description = "新增app用户表") - @SysLog("新增app用户表") - @PostMapping - @HasPermission("app_appuser_add") - public R save(@RequestBody AppUserDTO appUser) { - appUserService.saveUser(appUser); - return R.ok(); - } + /** + * 新增app用户表 + * + * @param appUser app用户表 + * @return R + */ + @Operation(summary = "新增app用户表", description = "新增app用户表") + @SysLog("新增app用户表") + @PostMapping + @HasPermission("app_appuser_add") + public R save(@RequestBody AppUserDTO appUser) { + appUserService.saveUser(appUser); + return R.ok(); + } - /** - * 修改app用户表 - * @param appUser app用户表 - * @return R - */ - @Operation(summary = "修改app用户表", description = "修改app用户表") - @SysLog("修改app用户表") - @PutMapping - @HasPermission("app_appuser_edit") - public R updateById(@RequestBody AppUserDTO appUser) { - return R.ok(appUserService.updateUser(appUser)); - } + /** + * 修改app用户表 + * + * @param appUser app用户表 + * @return R + */ + @Operation(summary = "修改app用户表", description = "修改app用户表") + @SysLog("修改app用户表") + @PutMapping + @HasPermission("app_appuser_edit") + public R updateById(@RequestBody AppUserDTO appUser) { + return R.ok(appUserService.updateUser(appUser)); + } - /** - * 通过id删除app用户表 - * @param ids userIds - * @return R - */ - @Operation(summary = "通过ids删除app用户表", description = "通过ids删除app用户表") - @SysLog("通过id删除app用户表") - @DeleteMapping - @HasPermission("app_appuser_del") - public R removeById(@RequestBody Long[] ids) { - return R.ok(appUserService.deleteAppUserByIds(ids)); - } + /** + * 通过id删除app用户表 + * + * @param ids userIds + * @return R + */ + @Operation(summary = "通过ids删除app用户表", description = "通过ids删除app用户表") + @SysLog("通过id删除app用户表") + @DeleteMapping + @HasPermission("app_appuser_del") + public R removeById(@RequestBody Long[] ids) { + return R.ok(appUserService.deleteAppUserByIds(ids)); + } - /** - * 导出excel 表格 - * @param appUser 查询条件 - * @return excel 文件流 - */ - @ResponseExcel - @GetMapping("/export") - @HasPermission("app_appuser_export") - public List export(AppUserDTO appUser) { - return appUserService.listUser(appUser); - } + /** + * 导出excel 表格 + * + * @param appUser 查询条件 + * @return excel 文件流 + */ + @ResponseExcel + @GetMapping("/export") + @HasPermission("app_appuser_export") + public List export(AppUserDTO appUser) { + return appUserService.listUser(appUser); + } - @SysLog("修改个人信息") - @PutMapping("/edit") - public R updateUserInfo(@Valid @RequestBody AppUserDTO userDto) { - if (UserTypeEnum.TOC.getStatus().equals(SecurityUtils.getUser().getUserType())) { - userDto.setUserId(SecurityUtils.getUser().getId()); - // TOC 以手机号为key - userDto.setUsername(SecurityUtils.getUser().getUsername()); - } - return appUserService.updateUserInfo(userDto); - } + @SysLog("修改个人信息") + @PutMapping("/edit") + public R updateUserInfo(@Valid @RequestBody AppUserDTO userDto) { + if (UserTypeEnum.TOC.getStatus().equals(SecurityUtils.getUser().getUserType())) { + userDto.setUserId(SecurityUtils.getUser().getId()); + // TOC 以手机号为key + userDto.setUsername(SecurityUtils.getUser().getUsername()); + } + return appUserService.updateUserInfo(userDto); + } - /** - * 导入用户 - * @param excelVOList 用户列表 - * @param bindingResult 错误信息列表 - * @return R - */ - @PostMapping("/import") - @HasPermission("app_appuser_export") - public R importUser(@RequestExcel List excelVOList, BindingResult bindingResult) { - return appUserService.importUser(excelVOList, bindingResult); - } + /** + * 导入用户 + * + * @param excelVOList 用户列表 + * @param bindingResult 错误信息列表 + * @return R + */ + @PostMapping("/import") + @HasPermission("app_appuser_export") + public R importUser(@RequestExcel List excelVOList, BindingResult bindingResult) { + return appUserService.importUser(excelVOList, bindingResult); + } - @Operation(summary = "注册app用户表", description = "注册app用户表") - @SysLog("注册app用户表") - @Inner(value = false) - @PostMapping("/register") - public R register(@RequestBody AppUserDTO appUser) { - return appUserService.registerAppUser(appUser); - } + @Operation(summary = "注册app用户表", description = "注册app用户表") + @SysLog("注册app用户表") + @Inner(value = false) + @PostMapping("/register") + public R register(@RequestBody AppUserDTO appUser) { + return appUserService.registerAppUser(appUser); + } } diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppMobileServiceImpl.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppMobileServiceImpl.java index 18a96639b..12f90baae 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppMobileServiceImpl.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppMobileServiceImpl.java @@ -20,11 +20,11 @@ package com.pig4cloud.pigx.app.service.impl; import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.toolkit.StringPool; +import com.pig4cloud.pigx.app.api.enums.AppErrorCodes; import com.pig4cloud.pigx.app.service.AppMobileService; import com.pig4cloud.pigx.common.core.constant.CacheConstants; import com.pig4cloud.pigx.common.core.constant.SecurityConstants; import com.pig4cloud.pigx.common.core.constant.enums.LoginTypeEnum; -import com.pig4cloud.pigx.common.core.exception.ErrorCodes; import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; import com.pig4cloud.pigx.common.data.cache.RedisUtils; @@ -43,24 +43,25 @@ import org.springframework.stereotype.Service; @AllArgsConstructor public class AppMobileServiceImpl implements AppMobileService { - /** - * 发送手机验证码 TODO: 调用短信网关发送验证码,测试返回前端 - * @param mobile mobile - * @return code - */ - @Override - public R sendSmsCode(String mobile) { - String codeObj = RedisUtils.get(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT + mobile); + /** + * 发送手机验证码 TODO: 调用短信网关发送验证码,测试返回前端 + * + * @param mobile mobile + * @return code + */ + @Override + public R sendSmsCode(String mobile) { + String codeObj = RedisUtils.get(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT + mobile); - if (StrUtil.isNotBlank(codeObj)) { - log.info("手机号验证码未过期:{},{}", mobile, codeObj); - return R.ok(Boolean.FALSE, MsgUtils.getMessage(ErrorCodes.SYS_APP_SMS_OFTEN)); - } + if (StrUtil.isNotBlank(codeObj)) { + log.info("手机号验证码未过期:{},{}", mobile, codeObj); + return R.ok(Boolean.FALSE, MsgUtils.getMessage(AppErrorCodes.APP_SMS_OFTEN)); + } - String code = RandomUtil.randomNumbers(Integer.parseInt(SecurityConstants.CODE_SIZE)); - log.debug("手机号生成验证码成功:{},{}", mobile, code); - RedisUtils.set(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT + mobile, code, SecurityConstants.CODE_TIME); - return R.ok(Boolean.TRUE); - } + String code = RandomUtil.randomNumbers(Integer.parseInt(SecurityConstants.CODE_SIZE)); + log.debug("手机号生成验证码成功:{},{}", mobile, code); + RedisUtils.set(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT + mobile, code, SecurityConstants.CODE_TIME); + return R.ok(Boolean.TRUE); + } } diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppRoleServiceImpl.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppRoleServiceImpl.java index b4a8293a4..8d890d0aa 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppRoleServiceImpl.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppRoleServiceImpl.java @@ -17,16 +17,20 @@ package com.pig4cloud.pigx.app.service.impl; import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pig4cloud.pigx.app.api.entity.AppPageEntity; import com.pig4cloud.pigx.app.api.entity.AppRole; +import com.pig4cloud.pigx.app.api.enums.AppErrorCodes; +import com.pig4cloud.pigx.app.api.enums.PageTypeEnums; import com.pig4cloud.pigx.app.api.vo.AppRoleExcelVO; +import com.pig4cloud.pigx.app.mapper.AppPageMapper; import com.pig4cloud.pigx.app.mapper.AppRoleMapper; import com.pig4cloud.pigx.app.service.AppRoleService; -import com.pig4cloud.pigx.common.core.exception.ErrorCodes; import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; import com.pig4cloud.pigx.common.excel.vo.ErrorMessage; -import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.validation.BindingResult; @@ -35,79 +39,97 @@ import java.util.List; import java.util.Set; /** - * app角色表 + * APP角色服务实现类 + *

+ * 提供对APP角色相关的业务操作实现。 * - * @author aeizzz - * @date 2022-12-07 09:52:03 + * @author lengleng + * @date 2025/05/29 */ @Service -@AllArgsConstructor +@RequiredArgsConstructor public class AppRoleServiceImpl extends ServiceImpl implements AppRoleService { - @Override - public List findRolesByUserId(Long userId) { - return baseMapper.listRolesByUserId(userId); - } + private final AppPageMapper appPageMapper; - /** - * 删除用户的同时,把role_menu关系删除 - * @param ids roleIds - */ - @Override - public Boolean deleteRoleByIds(Long[] ids) { - this.removeBatchByIds(CollUtil.toList(ids)); - return Boolean.TRUE; - } + private final AppRoleMapper appRoleMapper; - /** - * 导入角色 - * @param excelVOList - * @param bindingResult - * @return - */ - @Override - public R importRole(List excelVOList, BindingResult bindingResult) { - // 通用校验获取失败的数据 - List errorMessageList = (List) bindingResult.getTarget(); + @Override + public List findRolesByUserId(Long userId) { + return baseMapper.listRolesByUserId(userId); + } - // 个性化校验逻辑 - List roleList = this.list(); + /** + * 删除用户的同时,把role_menu关系删除 + * + * @param ids roleIds + */ + @Override + public Boolean deleteRoleByIds(Long[] ids) { + this.removeBatchByIds(CollUtil.toList(ids)); + return Boolean.TRUE; + } - // 执行数据插入操作 组装 RoleDto - for (AppRoleExcelVO excel : excelVOList) { - Set errorMsg = new HashSet<>(); - // 检验角色名称或者角色编码是否存在 - boolean existRole = roleList.stream() - .anyMatch(appRole -> excel.getRoleName().equals(appRole.getRoleName()) - || excel.getRoleCode().equals(appRole.getRoleCode())); + /** + * 导入角色 + * + * @param excelVOList + * @param bindingResult + * @return + */ + @Override + public R importRole(List excelVOList, BindingResult bindingResult) { + // 通用校验获取失败的数据 + List errorMessageList = (List) bindingResult.getTarget(); - if (existRole) { - errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_ROLE_NAMEORCODE_EXISTING, excel.getRoleName(), - excel.getRoleCode())); - } + // 个性化校验逻辑 + List roleList = this.list(); - // 数据合法情况 - if (CollUtil.isEmpty(errorMsg)) { - insertExcelRole(excel); - } - else { - // 数据不合法情况 - errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg)); - } - } - if (CollUtil.isNotEmpty(errorMessageList)) { - return R.failed(errorMessageList); - } - return R.ok(); + // 执行数据插入操作 组装 RoleDto + for (AppRoleExcelVO excel : excelVOList) { + Set errorMsg = new HashSet<>(); + // 检验角色名称或者角色编码是否存在 + boolean existRole = roleList.stream() + .anyMatch(appRole -> excel.getRoleName().equals(appRole.getRoleName()) + || excel.getRoleCode().equals(appRole.getRoleCode())); - } + if (existRole) { + errorMsg.add(MsgUtils.getMessage(AppErrorCodes.APP_ROLE_NAMEORCODE_EXISTING, excel.getRoleName(), + excel.getRoleCode())); + } - private void insertExcelRole(AppRoleExcelVO excel) { - AppRole appRole = new AppRole(); - appRole.setRoleName(excel.getRoleName()); - appRole.setRoleDesc(excel.getRoleDesc()); - appRole.setRoleCode(excel.getRoleCode()); - this.save(appRole); - } + // 数据合法情况 + if (CollUtil.isEmpty(errorMsg)) { + insertExcelRole(excel); + } else { + // 数据不合法情况 + errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg)); + } + } + if (CollUtil.isNotEmpty(errorMessageList)) { + return R.failed(errorMessageList); + } + return R.ok(); + + } + + /** + * 获取列表菜单对象 + * + * @return 列表菜单对象的实例 + */ + @Override + public AppPageEntity listMenu() { + return appPageMapper.selectOne(Wrappers.lambdaQuery() + .eq(AppPageEntity::getPageType, PageTypeEnums.WORKBENCH.getPageType())); + } + + private void insertExcelRole(AppRoleExcelVO excel) { + AppRole appRole = new AppRole(); + appRole.setRoleName(excel.getRoleName()); + appRole.setRoleDesc(excel.getRoleDesc()); + appRole.setRoleCode(excel.getRoleCode()); + this.save(appRole); + } } diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppUserServiceImpl.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppUserServiceImpl.java index 4381f7617..263228999 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppUserServiceImpl.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppUserServiceImpl.java @@ -29,6 +29,7 @@ import com.pig4cloud.pigx.app.api.dto.AppUserInfo; import com.pig4cloud.pigx.app.api.entity.AppRole; import com.pig4cloud.pigx.app.api.entity.AppUser; import com.pig4cloud.pigx.app.api.entity.AppUserRole; +import com.pig4cloud.pigx.app.api.enums.AppErrorCodes; import com.pig4cloud.pigx.app.api.vo.AppUserExcelVO; import com.pig4cloud.pigx.app.api.vo.AppUserVO; import com.pig4cloud.pigx.app.mapper.AppUserMapper; @@ -39,7 +40,6 @@ import com.pig4cloud.pigx.common.core.constant.CacheConstants; import com.pig4cloud.pigx.common.core.constant.CommonConstants; import com.pig4cloud.pigx.common.core.constant.enums.LoginTypeEnum; import com.pig4cloud.pigx.common.core.constant.enums.UserTypeEnum; -import com.pig4cloud.pigx.common.core.exception.ErrorCodes; import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; import com.pig4cloud.pigx.common.data.cache.RedisUtils; @@ -72,328 +72,338 @@ import java.util.Set; @Slf4j public class AppUserServiceImpl extends ServiceImpl implements AppUserService { - /** - * 密码编码器,用于密码加密 - */ - private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder(); + /** + * 密码编码器,用于密码加密 + */ + private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder(); - /** - * 用户角色关联服务 - */ - private final AppUserRoleService appUserRoleService; + /** + * 用户角色关联服务 + */ + private final AppUserRoleService appUserRoleService; - /** - * 角色服务 - */ - private final AppRoleService appRoleService; + /** + * 角色服务 + */ + private final AppRoleService appRoleService; - /** - * 缓存管理器 - */ - private final CacheManager cacheManager; + /** + * 缓存管理器 + */ + private final CacheManager cacheManager; - /** - * 更新用户信息 - * @param userDTO 用户数据传输对象 - * @return 更新结果 - */ - @Override - @CacheEvict(value = CacheConstants.USER_DETAILS_MINI, key = "#userDTO.username") - public Boolean updateUser(AppUserDTO userDTO) { - // 创建用户实体并复制属性 - AppUser appUser = new AppUser(); - BeanUtils.copyProperties(userDTO, appUser); + /** + * 更新用户信息 + * + * @param userDTO 用户数据传输对象 + * @return 更新结果 + */ + @Override + @CacheEvict(value = CacheConstants.USER_DETAILS_MINI, key = "#userDTO.username") + public Boolean updateUser(AppUserDTO userDTO) { + // 创建用户实体并复制属性 + AppUser appUser = new AppUser(); + BeanUtils.copyProperties(userDTO, appUser); - // 如果密码不为空,进行加密处理 - if (StrUtil.isNotBlank(userDTO.getPassword())) { - appUser.setPassword(ENCODER.encode(userDTO.getPassword())); - } - // 更新用户基本信息 - this.updateById(appUser); + // 如果密码不为空,进行加密处理 + if (StrUtil.isNotBlank(userDTO.getPassword())) { + appUser.setPassword(ENCODER.encode(userDTO.getPassword())); + } + // 更新用户基本信息 + this.updateById(appUser); - // 删除用户与角色的关联关系 - appUserRoleService.remove(Wrappers.lambdaQuery().eq(AppUserRole::getUserId, appUser.getUserId())); - // 重新建立用户与角色的关联关系 - userDTO.getRole().forEach(roleId -> { - AppUserRole appUserRole = new AppUserRole(); - appUserRole.setRoleId(roleId); - appUserRole.setUserId(userDTO.getUserId()); - appUserRole.insert(); - }); + // 删除用户与角色的关联关系 + appUserRoleService.remove(Wrappers.lambdaQuery().eq(AppUserRole::getUserId, appUser.getUserId())); + // 重新建立用户与角色的关联关系 + userDTO.getRole().forEach(roleId -> { + AppUserRole appUserRole = new AppUserRole(); + appUserRole.setRoleId(roleId); + appUserRole.setUserId(userDTO.getUserId()); + appUserRole.insert(); + }); - // 清除以手机号为key的用户缓存 - AppUser appUserInfo = baseMapper.selectById(userDTO.getUserId()); - cacheManager.getCache(CacheConstants.USER_DETAILS_MINI).evict(appUserInfo.getPhone()); - return Boolean.TRUE; - } + // 清除以手机号为key的用户缓存 + AppUser appUserInfo = baseMapper.selectById(userDTO.getUserId()); + cacheManager.getCache(CacheConstants.USER_DETAILS_MINI).evict(appUserInfo.getPhone()); + return Boolean.TRUE; + } - /** - * 新增用户 - * @param userDTO 用户数据传输对象 - * @return 保存结果 - */ - @Override - public Boolean saveUser(AppUserDTO userDTO) { - // 创建用户实体并复制属性 - AppUser appUser = new AppUser(); - BeanUtils.copyProperties(userDTO, appUser); - // 设置删除标志为正常状态 - appUser.setDelFlag(CommonConstants.STATUS_NORMAL); - // 对密码进行加密 - appUser.setPassword(ENCODER.encode(userDTO.getPassword())); - // 插入用户记录 - baseMapper.insert(appUser); + /** + * 新增用户 + * + * @param userDTO 用户数据传输对象 + * @return 保存结果 + */ + @Override + public Boolean saveUser(AppUserDTO userDTO) { + // 创建用户实体并复制属性 + AppUser appUser = new AppUser(); + BeanUtils.copyProperties(userDTO, appUser); + // 设置删除标志为正常状态 + appUser.setDelFlag(CommonConstants.STATUS_NORMAL); + // 对密码进行加密 + appUser.setPassword(ENCODER.encode(userDTO.getPassword())); + // 插入用户记录 + baseMapper.insert(appUser); - // 如果角色列表不为空,建立用户与角色的关联关系 - if (CollUtil.isNotEmpty(userDTO.getRole())) { - List userRoleList = userDTO.getRole().stream().map(roleId -> { - AppUserRole userRole = new AppUserRole(); - userRole.setUserId(appUser.getUserId()); - userRole.setRoleId(roleId); - return userRole; - }).toList(); - // 批量保存用户角色关联 - appUserRoleService.saveBatch(userRoleList); - } - return Boolean.TRUE; - } + // 如果角色列表不为空,建立用户与角色的关联关系 + if (CollUtil.isNotEmpty(userDTO.getRole())) { + List userRoleList = userDTO.getRole().stream().map(roleId -> { + AppUserRole userRole = new AppUserRole(); + userRole.setUserId(appUser.getUserId()); + userRole.setRoleId(roleId); + return userRole; + }).toList(); + // 批量保存用户角色关联 + appUserRoleService.saveBatch(userRoleList); + } + return Boolean.TRUE; + } - /** - * 查询全部用户(用于Excel导出) - * @param appUser 查询条件 - * @return 用户Excel视图对象列表 - */ - @Override - public List listUser(AppUserDTO appUser) { - // 查询所有用户 - List appUsers = baseMapper.selectList(null); - // 将用户实体转换为Excel视图对象 - List appUserExcelVOS = appUsers.stream().map(item -> { - AppUserExcelVO appUserExcelVO = new AppUserExcelVO(); - BeanUtils.copyProperties(item, appUserExcelVO); - return appUserExcelVO; - }).toList(); - return appUserExcelVOS; - } + /** + * 查询全部用户(用于Excel导出) + * + * @param appUser 查询条件 + * @return 用户Excel视图对象列表 + */ + @Override + public List listUser(AppUserDTO appUser) { + // 查询所有用户 + List appUsers = baseMapper.selectList(null); + // 将用户实体转换为Excel视图对象 + List appUserExcelVOS = appUsers.stream().map(item -> { + AppUserExcelVO appUserExcelVO = new AppUserExcelVO(); + BeanUtils.copyProperties(item, appUserExcelVO); + return appUserExcelVO; + }).toList(); + return appUserExcelVOS; + } - /** - * 分页查询用户(包含角色信息) - * @param page 分页参数 - * @param appUserDTO 查询条件 - * @return 分页结果 - */ - @Override - public IPage getUsersWithRolePage(Page page, AppUserDTO appUserDTO) { - return baseMapper.getUserVosPage(page, appUserDTO); - } + /** + * 分页查询用户(包含角色信息) + * + * @param page 分页参数 + * @param appUserDTO 查询条件 + * @return 分页结果 + */ + @Override + public IPage getUsersWithRolePage(Page page, AppUserDTO appUserDTO) { + return baseMapper.getUserVosPage(page, appUserDTO); + } - /** - * 查询用户详细信息(包含角色和权限) - * @param user 用户对象 - * @return 用户信息对象 - */ - @Override - public AppUserInfo findUserInfo(AppUser user) { - // 创建用户信息对象 - AppUserInfo info = new AppUserInfo(); - info.setAppUser(user); + /** + * 查询用户详细信息(包含角色和权限) + * + * @param user 用户对象 + * @return 用户信息对象 + */ + @Override + public AppUserInfo findUserInfo(AppUser user) { + // 创建用户信息对象 + AppUserInfo info = new AppUserInfo(); + info.setAppUser(user); - // 设置角色列表(ID数组) - List roleIds = appRoleService.findRolesByUserId(user.getUserId()) - .stream() - .map(AppRole::getRoleId) - .toList(); - info.setRoles(ArrayUtil.toArray(roleIds, Long.class)); + // 设置角色列表(ID数组) + List roleIds = appRoleService.findRolesByUserId(user.getUserId()) + .stream() + .map(AppRole::getRoleId) + .toList(); + info.setRoles(ArrayUtil.toArray(roleIds, Long.class)); - // 设置权限列表(暂为空) - Set permissions = new HashSet<>(); - info.setPermissions(ArrayUtil.toArray(permissions, String.class)); - return info; - } + // 设置权限列表(暂为空) + Set permissions = new HashSet<>(); + info.setPermissions(ArrayUtil.toArray(permissions, String.class)); + return info; + } - /** - * 更新用户个人信息 - * @param userDTO 用户数据传输对象 - * @return 操作结果 - */ - @Override - @CacheEvict(value = CacheConstants.USER_DETAILS_MINI, key = "#userDTO.username") - public R updateUserInfo(AppUserDTO userDTO) { - // 获取当前登录用户 - PigxUser user = SecurityUtils.getUser(); + /** + * 更新用户个人信息 + * + * @param userDTO 用户数据传输对象 + * @return 操作结果 + */ + @Override + @CacheEvict(value = CacheConstants.USER_DETAILS_MINI, key = "#userDTO.username") + public R updateUserInfo(AppUserDTO userDTO) { + // 获取当前登录用户 + PigxUser user = SecurityUtils.getUser(); - // C端客户修改手机号需要验证短信验证码 - if (UserTypeEnum.TOC.getStatus().equals(user.getUserType()) && StrUtil.isNotBlank(userDTO.getPhone())) { - // 构建验证码缓存key - String key = CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT - + userDTO.getPhone(); - // 获取缓存中的验证码 - String codeObj = RedisUtils.get(key); + // C端客户修改手机号需要验证短信验证码 + if (UserTypeEnum.TOC.getStatus().equals(user.getUserType()) && StrUtil.isNotBlank(userDTO.getPhone())) { + // 构建验证码缓存key + String key = CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT + + userDTO.getPhone(); + // 获取缓存中的验证码 + String codeObj = RedisUtils.get(key); - // 验证码不匹配,返回错误 - if (!userDTO.getMobileCode().equals(codeObj)) { - return R.failed("验证码错误"); - } - } + // 验证码不匹配,返回错误 + if (!userDTO.getMobileCode().equals(codeObj)) { + return R.failed("验证码错误"); + } + } - // 更新密码(如果提供了新密码) - if (StrUtil.isNotBlank(userDTO.getPassword())) { - userDTO.setPassword(ENCODER.encode(userDTO.getPassword())); - } + // 更新密码(如果提供了新密码) + if (StrUtil.isNotBlank(userDTO.getPassword())) { + userDTO.setPassword(ENCODER.encode(userDTO.getPassword())); + } - // 获取原用户信息并更新 - AppUser appUser = baseMapper.selectById(userDTO.getUserId()); - BeanUtils.copyProperties(userDTO, appUser); + // 获取原用户信息并更新 + AppUser appUser = baseMapper.selectById(userDTO.getUserId()); + BeanUtils.copyProperties(userDTO, appUser); - // 清除以手机号为key的用户缓存 - cacheManager.getCache(CacheConstants.USER_DETAILS_MINI).evict(user.getPhone()); - return R.ok(this.updateById(appUser)); - } + // 清除以手机号为key的用户缓存 + cacheManager.getCache(CacheConstants.USER_DETAILS_MINI).evict(user.getPhone()); + return R.ok(this.updateById(appUser)); + } - /** - * 根据用户ID查询用户视图对象 - * @param userId 用户ID - * @return 用户视图对象 - */ - @Override - public AppUserVO selectUserVoById(Long userId) { - return baseMapper.getUserVoById(userId); - } + /** + * 根据用户ID查询用户视图对象 + * + * @param userId 用户ID + * @return 用户视图对象 + */ + @Override + public AppUserVO selectUserVoById(Long userId) { + return baseMapper.getUserVoById(userId); + } - /** - * 批量删除用户 - * @param ids 用户ID数组 - * @return 删除结果 - */ - @Override - public Boolean deleteAppUserByIds(Long[] ids) { - // 获取缓存对象 - Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS_MINI); - // 清除用户缓存 - for (AppUser appUser : baseMapper.selectByIds(CollUtil.toList(ids))) { - cache.evict(appUser.getUsername()); - } + /** + * 批量删除用户 + * + * @param ids 用户ID数组 + * @return 删除结果 + */ + @Override + public Boolean deleteAppUserByIds(Long[] ids) { + // 获取缓存对象 + Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS_MINI); + // 清除用户缓存 + for (AppUser appUser : baseMapper.selectByIds(CollUtil.toList(ids))) { + cache.evict(appUser.getUsername()); + } - // 删除用户角色关联关系 - this.appUserRoleService - .remove(Wrappers.lambdaQuery().in(AppUserRole::getUserId, CollUtil.toList(ids))); + // 删除用户角色关联关系 + this.appUserRoleService + .remove(Wrappers.lambdaQuery().in(AppUserRole::getUserId, CollUtil.toList(ids))); - // 删除用户记录 - this.removeBatchByIds(CollUtil.toList(ids)); + // 删除用户记录 + this.removeBatchByIds(CollUtil.toList(ids)); - return Boolean.TRUE; - } + return Boolean.TRUE; + } - /** - * 注册APP用户 - * @param appUser 用户数据传输对象 - * @return 操作结果 - */ - @Override - public R registerAppUser(AppUserDTO appUser) { - // 检查手机号是否已被注册 - List appUserList = baseMapper - .selectList(Wrappers.lambdaQuery().eq(AppUser::getPhone, appUser.getPhone())); + /** + * 注册APP用户 + * + * @param appUser 用户数据传输对象 + * @return 操作结果 + */ + @Override + public R registerAppUser(AppUserDTO appUser) { + // 检查手机号是否已被注册 + List appUserList = baseMapper + .selectList(Wrappers.lambdaQuery().eq(AppUser::getPhone, appUser.getPhone())); - if (CollUtil.isNotEmpty(appUserList)) { - return R.failed("手机号已注册,请使用验证码直接登录"); - } + if (CollUtil.isNotEmpty(appUserList)) { + return R.failed("手机号已注册,请使用验证码直接登录"); + } - // 验证短信验证码 - String key = CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT - + appUser.getPhone(); - String codeObj = RedisUtils.get(key); + // 验证短信验证码 + String key = CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT + + appUser.getPhone(); + String codeObj = RedisUtils.get(key); - if (!appUser.getMobileCode().equals(codeObj)) { - return R.failed("验证码错误"); - } + if (!appUser.getMobileCode().equals(codeObj)) { + return R.failed("验证码错误"); + } - // 创建用户并保存 - AppUser app = new AppUser(); - BeanUtils.copyProperties(appUser, app); - appUser.setUsername(app.getPhone()); // 默认使用手机号作为用户名 - appUser.setDelFlag(CommonConstants.STATUS_NORMAL); - appUser.setPassword(ENCODER.encode(appUser.getPassword())); - baseMapper.insert(appUser); - return null; - } + // 创建用户并保存 + AppUser app = new AppUser(); + BeanUtils.copyProperties(appUser, app); + appUser.setUsername(app.getPhone()); // 默认使用手机号作为用户名 + appUser.setDelFlag(CommonConstants.STATUS_NORMAL); + appUser.setPassword(ENCODER.encode(appUser.getPassword())); + baseMapper.insert(appUser); + return null; + } - /** - * 导入用户(从Excel导入) - * @param excelVOList Excel数据列表 - * @param bindingResult 数据校验结果 - * @return 导入结果 - */ - @Override - public R importUser(List excelVOList, BindingResult bindingResult) { - // 获取通用校验失败的数据 - List errorMessageList = (List) bindingResult.getTarget(); + /** + * 导入用户(从Excel导入) + * + * @param excelVOList Excel数据列表 + * @param bindingResult 数据校验结果 + * @return 导入结果 + */ + @Override + public R importUser(List excelVOList, BindingResult bindingResult) { + // 获取通用校验失败的数据 + List errorMessageList = (List) bindingResult.getTarget(); - // 获取现有用户和角色列表,用于校验 - List userList = this.list(); - List roleList = appRoleService.list(); + // 获取现有用户和角色列表,用于校验 + List userList = this.list(); + List roleList = appRoleService.list(); - // 遍历Excel数据进行处理 - for (AppUserExcelVO excel : excelVOList) { - Set errorMsg = new HashSet<>(); + // 遍历Excel数据进行处理 + for (AppUserExcelVO excel : excelVOList) { + Set errorMsg = new HashSet<>(); - // 校验用户名是否已存在 - boolean exsitUserName = userList.stream() - .anyMatch(sysUser -> excel.getUsername().equals(sysUser.getUsername())); + // 校验用户名是否已存在 + boolean exsitUserName = userList.stream() + .anyMatch(sysUser -> excel.getUsername().equals(sysUser.getUsername())); - if (exsitUserName) { - errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_USER_USERNAME_EXISTING, excel.getUsername())); - } + if (exsitUserName) { + errorMsg.add(MsgUtils.getMessage(AppErrorCodes.APP_USER_USERNAME_EXISTING, excel.getUsername())); + } - // 校验角色名称是否合法 - List roleNameList = StrUtil.split(excel.getRoleNameList(), StrUtil.COMMA); - List roleCollList = roleList.stream() - .filter(role -> roleNameList.stream().anyMatch(name -> role.getRoleName().equals(name))) - .toList(); + // 校验角色名称是否合法 + 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())); - } + if (roleCollList.size() != roleNameList.size()) { + errorMsg.add(MsgUtils.getMessage(AppErrorCodes.APP_ROLE_ROLENAME_INEXISTENCE, excel.getRoleNameList())); + } - // 数据合法时执行插入 - if (CollUtil.isEmpty(errorMsg)) { - insertExcelUser(excel, roleCollList); - } - else { - // 记录不合法数据的错误信息 - errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg)); - } - } + // 数据合法时执行插入 + if (CollUtil.isEmpty(errorMsg)) { + insertExcelUser(excel, roleCollList); + } 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)); - } + // 存在错误信息时返回失败 + if (CollUtil.isNotEmpty(errorMessageList)) { + return R.failed(errorMessageList); + } + return R.ok(null, MsgUtils.getMessage(AppErrorCodes.APP_USER_IMPORT_SUCCEED)); + } - /** - * 插入Excel导入的用户数据 - * @param excel Excel用户数据 - * @param roleCollList 角色列表 - */ - private void insertExcelUser(AppUserExcelVO excel, List roleCollList) { - // 创建用户DTO并设置基本信息 - AppUserDTO userDTO = new AppUserDTO(); - userDTO.setUsername(excel.getUsername()); - userDTO.setPhone(excel.getPhone()); - userDTO.setNickname(excel.getNickname()); - userDTO.setName(excel.getName()); - userDTO.setEmail(excel.getEmail()); - // 批量导入时初始密码设置为手机号 - userDTO.setPassword(userDTO.getPhone()); + /** + * 插入Excel导入的用户数据 + * + * @param excel Excel用户数据 + * @param roleCollList 角色列表 + */ + private void insertExcelUser(AppUserExcelVO excel, List roleCollList) { + // 创建用户DTO并设置基本信息 + AppUserDTO userDTO = new AppUserDTO(); + userDTO.setUsername(excel.getUsername()); + userDTO.setPhone(excel.getPhone()); + userDTO.setNickname(excel.getNickname()); + userDTO.setName(excel.getName()); + userDTO.setEmail(excel.getEmail()); + // 批量导入时初始密码设置为手机号 + userDTO.setPassword(userDTO.getPhone()); - // 获取角色ID列表 - List roleIdList = roleCollList.stream().map(AppRole::getRoleId).toList(); - userDTO.setRole(roleIdList); + // 获取角色ID列表 + List roleIdList = roleCollList.stream().map(AppRole::getRoleId).toList(); + userDTO.setRole(roleIdList); - // 保存用户 - this.saveUser(userDTO); - } + // 保存用户 + this.saveUser(userDTO); + } } diff --git a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/PigxTokenEndpoint.java b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/PigxTokenEndpoint.java index 1a2798404..dc7adfdcf 100644 --- a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/PigxTokenEndpoint.java +++ b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/PigxTokenEndpoint.java @@ -26,9 +26,11 @@ import com.pig4cloud.pigx.admin.api.entity.SysTenant; import com.pig4cloud.pigx.admin.api.feign.RemoteClientDetailsService; import com.pig4cloud.pigx.admin.api.feign.RemoteTenantService; import com.pig4cloud.pigx.admin.api.vo.TokenVO; +import com.pig4cloud.pigx.auth.support.core.AuthErrorCodes; import com.pig4cloud.pigx.common.core.constant.CacheConstants; import com.pig4cloud.pigx.common.core.constant.CommonConstants; import com.pig4cloud.pigx.common.core.constant.SecurityConstants; +import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; import com.pig4cloud.pigx.common.core.util.RetOps; import com.pig4cloud.pigx.common.core.util.SpringContextHolder; @@ -48,6 +50,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.security.authentication.event.LogoutSuccessEvent; import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.security.oauth2.core.OAuth2ErrorCodes; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; @@ -111,7 +114,8 @@ public class PigxTokenEndpoint { @RequestParam(OAuth2ParameterNames.STATE) String state) { SysOauthClientDetails clientDetails = RetOps.of(clientDetailsService.getClientDetailsById(clientId)) .getData() - .orElseThrow(() -> new OAuthClientException("clientId 不合法")); + .orElseThrow(() -> new OAuthClientException(OAuth2ErrorCodes.INVALID_CLIENT, + MsgUtils.getMessage(AuthErrorCodes.AUTH_CLIENT_INVALID))); Set authorizedScopes = StringUtils.commaDelimitedListToSet(clientDetails.getScope()); // Note: XSS prevention is handled by FreeMarker template using ?html directive diff --git a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/core/AuthErrorCodes.java b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/core/AuthErrorCodes.java new file mode 100644 index 000000000..dbca42cff --- /dev/null +++ b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/core/AuthErrorCodes.java @@ -0,0 +1,18 @@ +package com.pig4cloud.pigx.auth.support.core; + +/** + * Auth 模块错误编码 + * + * @author lengleng + * @date 2022/3/30 + */ +public interface AuthErrorCodes { + + // === 验证码相关 === + String AUTH_CAPTCHA_EMPTY = "auth.captcha.empty"; + + String AUTH_CAPTCHA_INVALID = "auth.captcha.invalid"; + + // === 客户端相关 === + String AUTH_CLIENT_INVALID = "auth.client.invalid"; +} diff --git a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/filter/ValidateCodeFilter.java b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/filter/ValidateCodeFilter.java index 38cbd03f8..626a5fba3 100644 --- a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/filter/ValidateCodeFilter.java +++ b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/support/filter/ValidateCodeFilter.java @@ -12,11 +12,13 @@ import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.anji.captcha.model.vo.CaptchaVO; import com.anji.captcha.service.CaptchaService; +import com.pig4cloud.pigx.auth.support.core.AuthErrorCodes; import com.pig4cloud.pigx.common.core.constant.CacheConstants; import com.pig4cloud.pigx.common.core.constant.CommonConstants; import com.pig4cloud.pigx.common.core.constant.SecurityConstants; import com.pig4cloud.pigx.common.core.constant.enums.CaptchaFlagTypeEnum; import com.pig4cloud.pigx.common.core.exception.ValidateCodeException; +import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.SpringContextHolder; import com.pig4cloud.pigx.common.core.util.WebUtils; import com.pig4cloud.pigx.common.data.cache.RedisUtils; @@ -99,7 +101,7 @@ public class ValidateCodeFilter extends OncePerRequestFilter { String code = request.getParameter("code"); if (StrUtil.isBlank(code)) { - throw new ValidateCodeException("验证码不能为空"); + throw new ValidateCodeException(MsgUtils.getMessage(AuthErrorCodes.AUTH_CAPTCHA_EMPTY)); } String randomStr = request.getParameter("randomStr"); @@ -117,30 +119,30 @@ public class ValidateCodeFilter extends OncePerRequestFilter { vo.setCaptchaVerification(code); vo.setCaptchaType(randomStr); if (!captchaService.verification(vo).isSuccess()) { - throw new ValidateCodeException("验证码不能为空"); + throw new ValidateCodeException(MsgUtils.getMessage(AuthErrorCodes.AUTH_CAPTCHA_EMPTY)); } return; } String key = CacheConstants.DEFAULT_CODE_KEY + randomStr; if (!RedisUtils.hasKey(key)) { - throw new ValidateCodeException("验证码不合法"); + throw new ValidateCodeException(MsgUtils.getMessage(AuthErrorCodes.AUTH_CAPTCHA_INVALID)); } String codeObj = RedisUtils.get(key); if (codeObj == null) { - throw new ValidateCodeException("验证码不合法"); + throw new ValidateCodeException(MsgUtils.getMessage(AuthErrorCodes.AUTH_CAPTCHA_INVALID)); } if (StrUtil.isBlank(codeObj)) { RedisUtils.delete(key); - throw new ValidateCodeException("验证码不合法"); + throw new ValidateCodeException(MsgUtils.getMessage(AuthErrorCodes.AUTH_CAPTCHA_INVALID)); } if (!StrUtil.equals(codeObj, code)) { RedisUtils.delete(key); - throw new ValidateCodeException("验证码不合法"); + throw new ValidateCodeException(MsgUtils.getMessage(AuthErrorCodes.AUTH_CAPTCHA_INVALID)); } RedisUtils.delete(key); diff --git a/pigx-auth/src/main/resources/i18n/messages_en.properties b/pigx-auth/src/main/resources/i18n/messages_en.properties new file mode 100644 index 000000000..8b1df1611 --- /dev/null +++ b/pigx-auth/src/main/resources/i18n/messages_en.properties @@ -0,0 +1,5 @@ +# Auth Module i18n Messages - English +# Captcha related +auth.captcha.empty=Captcha cannot be empty +auth.captcha.invalid=Invalid captcha +auth.client.invalid=Invalid client \ No newline at end of file diff --git a/pigx-auth/src/main/resources/i18n/messages_zh_CN.properties b/pigx-auth/src/main/resources/i18n/messages_zh_CN.properties new file mode 100644 index 000000000..89481df74 --- /dev/null +++ b/pigx-auth/src/main/resources/i18n/messages_zh_CN.properties @@ -0,0 +1,5 @@ +# Auth \u6A21\u5757\u56FD\u9645\u5316\u6D88\u606F - \u4E2D\u6587 +# \u9A8C\u8BC1\u7801\u76F8\u5173 +auth.captcha.empty=\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A +auth.captcha.invalid=\u9A8C\u8BC1\u7801\u4E0D\u5408\u6CD5 +auth.client.invalid=\u65E0\u6548\u7684\u5BA2\u6237\u7AEF \ No newline at end of file diff --git a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/config/MessageSourceConfiguration.java b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/config/MessageSourceConfiguration.java index f68fca696..98e3eeea2 100644 --- a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/config/MessageSourceConfiguration.java +++ b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/config/MessageSourceConfiguration.java @@ -17,25 +17,149 @@ package com.pig4cloud.pigx.common.core.config; +import lombok.extern.slf4j.Slf4j; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.ReloadableResourceBundleMessageSource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.util.ResourceUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.regex.Pattern; /** - * @author lengleng - * @date 2018/11/14 + * @author Hccake + * @date 2025-12-12 *

- * 国际化配置 + * 国际化配置 - 支持多 JAR 包扫描 */ +@Slf4j @Configuration public class MessageSourceConfiguration { - @Bean - public MessageSource pigxMessageSource() { - ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); - messageSource.setBasename("classpath:i18n/messages"); - return messageSource; - } + /** + * 创建并配置国际化消息源Bean + * + * @return 配置好的消息源实例,支持通配符资源加载 + */ + @Bean + public MessageSource pigxMessageSource() { + WildcardReloadableResourceBundleMessageSource messageSource = new WildcardReloadableResourceBundleMessageSource(); + // 设置基础包名,支持通配符扫描多个 JAR + messageSource.setBasename("i18n/messages"); + messageSource.setDefaultEncoding("UTF-8"); + messageSource.setCacheSeconds(3600); + return messageSource; + } + + /** + * 支持 classpath*: 通配符的 MessageSource 实现 能够扫描所有 JAR 包中的 i18n 资源文件 + */ + @Slf4j + static class WildcardReloadableResourceBundleMessageSource extends ReloadableResourceBundleMessageSource { + private static final String PROPERTIES_SUFFIX = ".properties"; + + private final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + + private static final Pattern LOCALE_PROPERTIES_FILE_NAME_PATTERN = Pattern.compile(".*_([a-z]{2}(_[A-Z]{2})?(_[A-Z]+)?)\\.properties$"); + + public WildcardReloadableResourceBundleMessageSource() { + super.setResourceLoader(this.resolver); + } + + /** + * 计算给定bundle基名和Locale的所有文件名。会计算指定Locale、系统Locale(如果适用)和默认文件的文件名。 + * 当基名包含通配符时,会移除基名并添加通配符匹配到的资源文件路径。 + * + * @param basename bundle的基名 + * @param locale 区域设置 + * @return 要检查的文件名列表 + * @see #setFallbackToSystemLocale + * @see #calculateFilenamesForLocale + */ + @Override + protected List calculateAllFilenames(String basename, Locale locale) { + // 父类默认的方法会将 basename 也放入 filenames 列表 + List filenames = super.calculateAllFilenames(basename, locale); + // 当 basename 有匹配符时,从 filenames 中移除,否则扫描文件将抛出 Illegal char <*> 的异常 + if (containsWildcard(basename)) { + filenames.remove(basename); + try { + Resource[] resources = getResources(basename); + for (Resource resource : resources) { + String resourceUriStr = resource.getURI().toString(); + // 根据通配符匹配到的多个 basename 对应的文件添加到文件列表末尾,作为兜底匹配 + if (!LOCALE_PROPERTIES_FILE_NAME_PATTERN.matcher(resourceUriStr).matches()) { + String sourcePath = resourceUriStr.replace(PROPERTIES_SUFFIX, ""); + filenames.add(sourcePath); + } + } + } catch (IOException ex) { + log.error("读取国际化信息文件异常", ex); + } + } + return filenames; + } + + /** + * 计算指定基名和区域设置的资源文件名列表 + * + * @param basename 资源文件基名 + * @param locale 区域设置 + * @return 存在的资源文件名列表(不包含.properties后缀) + */ + @Override + protected List calculateFilenamesForLocale(String basename, Locale locale) { + // 资源文件名 + List fileNames = new ArrayList<>(); + // 获取到待匹配的国际化信息文件名集合 + List matchFilenames = super.calculateFilenamesForLocale(basename, locale); + for (String matchFilename : matchFilenames) { + try { + Resource[] resources = getResources(matchFilename); + for (Resource resource : resources) { + if (resource.exists()) { + String sourcePath = resource.getURI().toString().replace(PROPERTIES_SUFFIX, ""); + fileNames.add(sourcePath); + } + } + } catch (IOException ex) { + log.error("读取国际化信息文件异常", ex); + } + } + return fileNames; + } + + /** + * 根据资源路径模式获取资源数组 + * + * @param resourceLocationPattern 资源路径模式(支持用"."表示文件层级) + * @return 匹配的资源数组 + * @throws IOException 获取资源过程中发生IO异常时抛出 + */ + private Resource[] getResources(String resourceLocationPattern) throws IOException { + // 支持用 . 表示文件层级 + resourceLocationPattern = resourceLocationPattern.replace(".", "/"); + return this.resolver.getResources("classpath*:" + resourceLocationPattern + PROPERTIES_SUFFIX); + } + + /** + * 检查字符串是否包含通配符 + * + * @param str 要检查的字符串 + * @return 如果字符串包含通配符返回true,否则返回false + */ + private boolean containsWildcard(String str) { + if (str.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)) { + str = str.substring(ResourceUtils.CLASSPATH_URL_PREFIX.length()); + } + return str.contains("*"); + } + } } diff --git a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/exception/ErrorCodes.java b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/exception/ErrorCodes.java deleted file mode 100644 index 4e2bb402e..000000000 --- a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/exception/ErrorCodes.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.pig4cloud.pigx.common.core.exception; - -/** - * 错误编码 - * - * @author lengleng - * @date 2022/3/30 - */ -public interface ErrorCodes { - - /** - * 系统编码错误 - */ - String SYS_PARAM_CONFIG_ERROR = "sys.param.config.error"; - - /** - * 系统内置参数不能删除 - */ - String SYS_PARAM_DELETE_SYSTEM = "sys.param.delete.system"; - - /** - * 用户已存在 - */ - String SYS_USER_USERNAME_EXISTING = "sys.user.username.existing"; - - /** - * 用户已存在 - */ - String SYS_USER_PHONE_EXISTING = "sys.user.phone.existing"; - - /** - * 用户原密码错误,修改失败 - */ - String SYS_USER_UPDATE_PASSWORDERROR = "sys.user.update.passwordError"; - - /** - * 用户信息为空 - */ - String SYS_USER_USERINFO_EMPTY = "sys.user.userInfo.empty"; - - /** - * 获取当前用户信息失败 - */ - String SYS_USER_QUERY_ERROR = "sys.user.query.error"; - - String SYS_USER_IMPORT_SUCCEED = "sys.user.import.succeed"; - - /** - * 部门名称不存在 - */ - String SYS_DEPT_DEPTNAME_INEXISTENCE = "sys.dept.deptName.inexistence"; - - /** - * 岗位名称不存在 - */ - String SYS_POST_POSTNAME_INEXISTENCE = "sys.post.postName.inexistence"; - - /** - * 岗位名称或编码已经存在 - */ - String SYS_POST_NAMEORCODE_EXISTING = "sys.post.nameOrCode.existing"; - - /** - * 角色名称不存在 - */ - String SYS_ROLE_ROLENAME_INEXISTENCE = "sys.role.roleName.inexistence"; - - /** - * 角色名或角色编码已经存在 - */ - String SYS_ROLE_NAMEORCODE_EXISTING = "sys.role.nameOrCode.existing"; - - /** - * 菜单存在下级节点 删除失败 - */ - String SYS_MENU_DELETE_EXISTING = "sys.menu.delete.existing"; - - /** - * 系统内置字典不允许删除 - */ - String SYS_DICT_DELETE_SYSTEM = "sys.dict.delete.system"; - - /** - * 系统内置字典不能修改 - */ - String SYS_DICT_UPDATE_SYSTEM = "sys.dict.update.system"; - - /** - * 验证码发送频繁 - */ - String SYS_APP_SMS_OFTEN = "sys.app.sms.often"; - - /** - * 系统应用短信错误编码 - */ - String SYS_APP_SMS_ERROR = "sys.app.sms.error"; - - /** - * 手机号未注册 - */ - String SYS_APP_PHONE_UNREGISTERED = "sys.app.phone.unregistered"; - - /** - * 企微调用接口错误 - */ - String SYS_CONNECT_CP_DEPT_SYNC_ERROR = "sys.connect.cp.dept.sync.error"; - - /** - * 企微调用接口错误 - */ - String SYS_CONNECT_CP_USER_SYNC_ERROR = "sys.connect.cp.user.sync.error"; - - /** - * 用户信息为空 - */ - String APP_USER_USERINFO_EMPTY = "app.user.userInfo.empty"; - -} diff --git a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/util/MsgUtils.java b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/util/MsgUtils.java index b2869218f..3a0c0e4a9 100644 --- a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/util/MsgUtils.java +++ b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/util/MsgUtils.java @@ -2,6 +2,8 @@ package com.pig4cloud.pigx.common.core.util; import lombok.experimental.UtilityClass; import org.springframework.context.MessageSource; +import org.springframework.context.NoSuchMessageException; +import org.springframework.context.i18n.LocaleContextHolder; import java.util.Locale; @@ -14,34 +16,55 @@ import java.util.Locale; @UtilityClass public class MsgUtils { - /** - * 通过code 获取中文错误信息 - * @param code - * @return - */ - public String getMessage(String code) { - MessageSource messageSource = SpringContextHolder.getBean("pigxMessageSource"); - return messageSource.getMessage(code, null, Locale.CHINA); - } + /** + * 通过code 获取中文错误信息 + * + * @param code + * @return + */ + public String getMessage(String code) { + MessageSource messageSource = SpringContextHolder.getBean("pigxMessageSource"); + return resolveMessage(messageSource, code, null); + } - /** - * 通过code 和参数获取中文错误信息 - * @param code - * @return - */ - public String getMessage(String code, Object... objects) { - MessageSource messageSource = SpringContextHolder.getBean("pigxMessageSource"); - return messageSource.getMessage(code, objects, Locale.CHINA); - } + /** + * 通过code 和参数获取中文错误信息 + * + * @param code + * @return + */ + public String getMessage(String code, Object... objects) { + MessageSource messageSource = SpringContextHolder.getBean("pigxMessageSource"); + return resolveMessage(messageSource, code, objects); + } - /** - * security 通过code 和参数获取中文错误信息 - * @param code - * @return - */ - public String getSecurityMessage(String code, Object... objects) { - MessageSource messageSource = SpringContextHolder.getBean("securityMessageSource"); - return messageSource.getMessage(code, objects, Locale.CHINA); - } + /** + * 通过错误码和参数获取安全相关的中文错误信息 + * + * @param code 错误码 + * @param objects 格式化参数 + * @return 格式化后的错误信息 + */ + public String getSecurityMessage(String code, Object... objects) { + MessageSource messageSource = SpringContextHolder.getBean("securityMessageSource"); + return resolveMessage(messageSource, code, objects); + } + + /** + * 解析消息,根据当前区域设置获取消息,如果找不到消息则使用中国区域设置重试 + * + * @param messageSource 消息源 + * @param code 消息代码 + * @param objects 消息参数 + * @return 解析后的消息 + */ + private String resolveMessage(MessageSource messageSource, String code, Object[] objects) { + Locale locale = LocaleContextHolder.getLocale(); + try { + return messageSource.getMessage(code, objects, locale); + } catch (NoSuchMessageException ex) { + return messageSource.getMessage(code, objects, Locale.CHINA); + } + } } diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxSecurityMessageSourceConfiguration.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxSecurityMessageSourceConfiguration.java index 05b0d0ebe..595c86d22 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxSecurityMessageSourceConfiguration.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/component/PigxSecurityMessageSourceConfiguration.java @@ -22,8 +22,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.support.ReloadableResourceBundleMessageSource; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import java.util.Locale; - import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type.SERVLET; /** @@ -35,12 +33,14 @@ import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebA @ConditionalOnWebApplication(type = SERVLET) public class PigxSecurityMessageSourceConfiguration implements WebMvcConfigurer { - @Bean - public MessageSource securityMessageSource() { - ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); - messageSource.addBasenames("classpath:errors/messages"); - messageSource.setDefaultLocale(Locale.CHINA); - return messageSource; - } + @Bean + public MessageSource securityMessageSource() { + ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); + messageSource.addBasenames("classpath:errors/messages"); + //messageSource.setDefaultLocale(Locale.CHINA); 没有国际化的需求可以关闭 + messageSource.setDefaultEncoding("UTF-8"); + messageSource.setCacheSeconds(3600); + return messageSource; + } } diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/util/OAuthClientException.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/util/OAuthClientException.java index 2252012c6..133359193 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/util/OAuthClientException.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/util/OAuthClientException.java @@ -9,6 +9,14 @@ import org.springframework.security.oauth2.core.OAuth2Error; */ public class OAuthClientException extends OAuth2AuthenticationException { + public OAuthClientException(String errorCode, String description) { + super(new OAuth2Error(errorCode, description, null), description); + } + + public OAuthClientException(String errorCode, String description, Throwable cause) { + super(new OAuth2Error(errorCode, description, null), cause); + } + /** * Constructs a ScopeException with the specified message. * @param msg the detail message. diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/constant/UpmsErrorCodes.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/constant/UpmsErrorCodes.java new file mode 100644 index 000000000..9379612df --- /dev/null +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/constant/UpmsErrorCodes.java @@ -0,0 +1,97 @@ +package com.pig4cloud.pigx.admin.api.constant; + +/** + * UPMS 模块错误编码 + * + * @author lengleng + * @date 2022/3/30 + */ +public interface UpmsErrorCodes { + + // === 用户相关 === + String SYS_USER_USERNAME_EXISTING = "sys.user.username.existing"; + + String SYS_USER_PHONE_EXISTING = "sys.user.phone.existing"; + + String SYS_USER_UPDATE_PASSWORDERROR = "sys.user.update.passwordError"; + + String SYS_USER_OLD_PASSWORD_EMPTY = "sys.user.oldPassword.empty"; + + String SYS_USER_NEW_PASSWORD_EMPTY = "sys.user.newPassword.empty"; + + String SYS_USER_UNBIND_TYPE_NOT_FOUND = "sys.user.unbind.type.notFound"; + + String SYS_USER_PASSWORD_MISMATCH = "sys.user.password.mismatch"; + + String SYS_USER_PASSWORD_SAME = "sys.user.password.same"; + + String SYS_USER_USERINFO_EMPTY = "sys.user.userInfo.empty"; + + String SYS_USER_QUERY_ERROR = "sys.user.query.error"; + + String SYS_USER_IMPORT_SUCCEED = "sys.user.import.succeed"; + + // === 部门相关 === + String SYS_DEPT_DEPTNAME_INEXISTENCE = "sys.dept.deptName.inexistence"; + + String SYS_DEPT_IMPORT_SUCCEED = "sys.dept.import.succeed"; + + String SYS_DEPT_SWITCH_SUCCEED = "sys.dept.switch.succeed"; + + // === 岗位相关 === + String SYS_POST_POSTNAME_INEXISTENCE = "sys.post.postName.inexistence"; + + String SYS_POST_NAMEORCODE_EXISTING = "sys.post.nameOrCode.existing"; + + // === 角色相关 === + String SYS_ROLE_ROLENAME_INEXISTENCE = "sys.role.roleName.inexistence"; + + String SYS_ROLE_NAMEORCODE_EXISTING = "sys.role.nameOrCode.existing"; + + // === 菜单相关 === + String SYS_MENU_DELETE_EXISTING = "sys.menu.delete.existing"; + + String SYS_MENU_NOT_FOUND = "sys.menu.notFound"; + + String SYS_MENU_HOME_SET_SUCCEED = "sys.menu.home.set.succeed"; + + String SYS_MENU_HOME_CANCEL_SUCCEED = "sys.menu.home.cancel.succeed"; + + String SYS_MENU_BUTTON_CANNOT_HOME = "sys.menu.button.cannot.home"; + + // === 字典相关 === + String SYS_DICT_DELETE_SYSTEM = "sys.dict.delete.system"; + + String SYS_DICT_UPDATE_SYSTEM = "sys.dict.update.system"; + + // === 参数相关 === + String SYS_PARAM_CONFIG_ERROR = "sys.param.config.error"; + + String SYS_PARAM_DELETE_SYSTEM = "sys.param.delete.system"; + + String SYS_PARAM_ILLEGAL = "sys.param.illegal"; + + // === 消息相关 === + String SYS_MESSAGE_SMS_CHANNEL_MISSING = "sys.message.sms.channel.missing"; + + String SYS_MESSAGE_CHANNEL_MISSING = "sys.message.channel.missing"; + + // === 小程序相关 === + String SYS_MINIAPP_LOGIN_FAILED = "sys.miniapp.login.failed"; + + // === 系统缓存 === + String SYS_SYSTEM_CACHE_FETCH_ERROR = "sys.system.cache.fetch.error"; + + // === 企业微信相关 === + String SYS_CONNECT_CP_DEPT_SYNC_ERROR = "sys.connect.cp.dept.sync.error"; + + String SYS_CONNECT_CP_USER_SYNC_ERROR = "sys.connect.cp.user.sync.error"; + + // === 短信验证相关 (在 UPMS 消息和用户服务中使用) === + String SYS_APP_SMS_OFTEN = "sys.app.sms.often"; + + String SYS_APP_SMS_ERROR = "sys.app.sms.error"; + + String SYS_APP_PHONE_UNREGISTERED = "sys.app.phone.unregistered"; + +} diff --git a/pigx-upms/pigx-upms-api/src/main/resources/i18n/messages_en.properties b/pigx-upms/pigx-upms-api/src/main/resources/i18n/messages_en.properties new file mode 100644 index 000000000..b63c0595f --- /dev/null +++ b/pigx-upms/pigx-upms-api/src/main/resources/i18n/messages_en.properties @@ -0,0 +1,62 @@ +# UPMS Module i18n Messages - English + +# User related +sys.user.update.passwordError=Incorrect current password; update failed +sys.user.oldPassword.empty=Current password cannot be empty +sys.user.newPassword.empty=New password cannot be empty +sys.user.unbind.type.notFound=Unknown account type to unbind +sys.user.password.mismatch=Incorrect password +sys.user.password.same=New and old passwords cannot be the same +sys.user.query.error=Failed to fetch current user information +sys.user.import.succeed=User import succeeded; default password is the phone number +sys.user.username.existing=Username {0} already exists +sys.user.phone.existing=Phone number {0} already exists +sys.user.userInfo.empty=User information is empty for {0} + +# Department related +sys.dept.deptName.inexistence=Department name {0} does not exist +sys.dept.import.succeed=Department import succeeded +sys.dept.switch.succeed=Department switched successfully + +# Post related +sys.post.postName.inexistence=Post name {0} does not exist +sys.post.nameOrCode.existing=Post name or code already exists ({0} {1}) + +# Role related +sys.role.roleName.inexistence=Role name {0} does not exist +sys.role.nameOrCode.existing=Role name or code already exists ({0} {1}) + +# Menu related +sys.menu.delete.existing=Menu contains sub-menus; cannot delete +sys.menu.notFound=Menu does not exist +sys.menu.home.set.succeed=Homepage set successfully +sys.menu.home.cancel.succeed=Homepage removed successfully +sys.menu.button.cannot.home=Button-type menu cannot be set as homepage + +# Dictionary related +sys.dict.delete.system=Built-in dictionary items cannot be deleted +sys.dict.update.system=Built-in dictionary items cannot be modified + +# Parameter related +sys.param.config.error=System parameter configuration error: {0} +sys.param.delete.system=Built-in system parameters cannot be deleted +sys.param.illegal=Invalid parameter + +# Message related +sys.message.sms.channel.missing=Send failed; SMS channel configuration not found +sys.message.channel.missing=Send failed; channel configuration not found + +# Mini-program related +sys.miniapp.login.failed=WeChat mini-program login failed + +# System cache +sys.system.cache.fetch.error=Failed to fetch cache statistics + +# WeCom related +sys.connect.cp.dept.sync.error=Failed to fetch WeCom department list +sys.connect.cp.user.sync.error=Failed to fetch WeCom user list + +# SMS verification related (used in UPMS message and user services) +sys.app.sms.often=Verification code sent too frequently +sys.app.sms.error=Invalid verification code +sys.app.phone.unregistered=Phone number {0} is not registered diff --git a/pigx-common/pigx-common-core/src/main/resources/i18n/messages_zh_CN.properties b/pigx-upms/pigx-upms-api/src/main/resources/i18n/messages_zh_CN.properties similarity index 55% rename from pigx-common/pigx-common-core/src/main/resources/i18n/messages_zh_CN.properties rename to pigx-upms/pigx-upms-api/src/main/resources/i18n/messages_zh_CN.properties index 96cff7f7b..adf8cd6f6 100644 --- a/pigx-common/pigx-common-core/src/main/resources/i18n/messages_zh_CN.properties +++ b/pigx-upms/pigx-upms-api/src/main/resources/i18n/messages_zh_CN.properties @@ -1,22 +1,62 @@ +# UPMS 模块国际化消息 - 中文 + +# 用户相关 sys.user.update.passwordError=\u539F\u5BC6\u7801\u9519\u8BEF\uFF0C\u4FEE\u6539\u5931\u8D25 +sys.user.oldPassword.empty=\u539F\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A +sys.user.newPassword.empty=\u65B0\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A +sys.user.unbind.type.notFound=\u89E3\u7ED1\u8D26\u53F7\u7C7B\u578B\u4E0D\u5B58\u5728 +sys.user.password.mismatch=\u5BC6\u7801\u8F93\u5165\u9519\u8BEF +sys.user.password.same=\u65B0\u65E7\u5BC6\u7801\u4E0D\u80FD\u76F8\u540C sys.user.query.error=\u83B7\u53D6\u5F53\u524D\u7528\u6237\u4FE1\u606F\u5931\u8D25 sys.user.import.succeed=\u7528\u6237\u5BFC\u5165\u6210\u529F\uFF0C\u9ED8\u8BA4\u5BC6\u7801\u4E3A\u624B\u673A\u53F7 sys.user.username.existing={0} \u7528\u6237\u540D\u5DF2\u5B58\u5728 sys.user.phone.existing={0} \u624B\u673A\u53F7\u5DF2\u5B58\u5728 sys.user.userInfo.empty={0} \u7528\u6237\u4FE1\u606F\u4E3A\u7A7A + +# 部门相关 sys.dept.deptName.inexistence={0} \u90E8\u95E8\u540D\u79F0\u4E0D\u5B58\u5728 +sys.dept.import.succeed=\u90E8\u95E8\u5BFC\u5165\u6210\u529F +sys.dept.switch.succeed=\u90E8\u95E8\u5207\u6362\u6210\u529F + +# 岗位相关 sys.post.postName.inexistence={0} \u5C97\u4F4D\u540D\u79F0\u4E0D\u5B58\u5728 sys.post.nameOrCode.existing={0} {1} \u5C97\u4F4D\u540D\u6216\u5C97\u4F4D\u7F16\u7801\u5DF2\u7ECF\u5B58\u5728 + +# 角色相关 sys.role.roleName.inexistence={0} \u89D2\u8272\u540D\u79F0\u4E0D\u5B58\u5728 sys.role.nameOrCode.existing={0} {1} \u89D2\u8272\u540D\u6216\u89D2\u8272\u7F16\u7801\u5DF2\u7ECF\u5B58\u5728 -sys.param.delete.system=\u7CFB\u7EDF\u5185\u7F6E\u53C2\u6570\u4E0D\u80FD\u5220\u9664 -sys.param.config.error={0} \u7CFB\u7EDF\u53C2\u6570\u914D\u7F6E\u9519\u8BEF + +# 菜单相关 sys.menu.delete.existing=\u83DC\u5355\u542B\u6709\u4E0B\u7EA7\u4E0D\u80FD\u5220\u9664 +sys.menu.notFound=\u83DC\u5355\u4E0D\u5B58\u5728 +sys.menu.home.set.succeed=\u8BBE\u7F6E\u9996\u9875\u6210\u529F +sys.menu.home.cancel.succeed=\u53D6\u6D88\u9996\u9875\u6210\u529F +sys.menu.button.cannot.home=\u6309\u94AE\u7C7B\u578B\u4E0D\u80FD\u8BBE\u7F6E\u4E3A\u9996\u9875 + +# 字典相关 +sys.dict.delete.system=\u7CFB\u7EDF\u5185\u7F6E\u5B57\u5178\u9879\u76EE\u4E0D\u80FD\u5220\u9664 +sys.dict.update.system=\u7CFB\u7EDF\u5185\u7F6E\u5B57\u5178\u9879\u76EE\u4E0D\u80FD\u4FEE\u6539 + +# 参数相关 +sys.param.config.error={0} \u7CFB\u7EDF\u53C2\u6570\u914D\u7F6E\u9519\u8BEF +sys.param.delete.system=\u7CFB\u7EDF\u5185\u7F6E\u53C2\u6570\u4E0D\u80FD\u5220\u9664 +sys.param.illegal=\u975E\u6CD5\u53C2\u6570 + +# 消息相关 +sys.message.sms.channel.missing=\u53D1\u9001\u5931\u8D25\uFF0C\u77ED\u4FE1\u901A\u9053\u914D\u7F6E\u4E0D\u5B58\u5728 +sys.message.channel.missing=\u53D1\u9001\u5931\u8D25\uFF0C\u901A\u9053\u914D\u7F6E\u4E0D\u5B58\u5728 + +# 小程序相关 +sys.miniapp.login.failed=\u5FAE\u4FE1\u5C0F\u7A0B\u5E8F\u767B\u5F55\u5931\u8D25 + +# 系统缓存 +sys.system.cache.fetch.error=\u83B7\u53D6\u5F02\u5E38 + +# 企业微信相关 +sys.connect.cp.dept.sync.error=\u83B7\u53D6\u4F01\u4E1A\u5FAE\u4FE1\u90E8\u95E8\u5217\u8868\u5931\u8D25 +sys.connect.cp.user.sync.error=\u83B7\u53D6\u4F01\u4E1A\u5FAE\u4FE1\u7528\u6237\u5217\u8868\u5931\u8D25 + +# 短信验证相关 (在 UPMS 消息和用户服务中使用) sys.app.sms.often=\u9A8C\u8BC1\u7801\u53D1\u9001\u8FC7\u9891\u7E41 sys.app.sms.error=\u9A8C\u8BC1\u7801\u9519\u8BEF sys.app.phone.unregistered={0} \u624B\u673A\u53F7\u672A\u6CE8\u518C -sys.dict.delete.system=\u7CFB\u7EDF\u5185\u7F6E\u5B57\u5178\u9879\u76EE\u4E0D\u80FD\u5220\u9664 -sys.dict.update.system=\u7CFB\u7EDF\u5185\u7F6E\u5B57\u5178\u9879\u76EE\u4E0D\u80FD\u4FEE\u6539 -sys.connect.cp.dept.sync.error=\u83B7\u53D6\u4F01\u4E1A\u5FAE\u4FE1\u90E8\u95E8\u5217\u8868\u5931\u8D25 -sys.connect.cp.user.sync.error=\u83B7\u53D6\u4F01\u4E1A\u5FAE\u4FE1\u7528\u6237\u5217\u8868\u5931\u8D25 -app.user.userInfo.empty={0} \u7528\u6237\u4FE1\u606F\u4E3A\u7A7A diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysSystemInfoController.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysSystemInfoController.java index b4e24f8f0..080593810 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysSystemInfoController.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysSystemInfoController.java @@ -1,5 +1,7 @@ package com.pig4cloud.pigx.admin.controller; +import com.pig4cloud.pigx.admin.api.constant.UpmsErrorCodes; +import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; import com.pig4cloud.pigx.common.data.cache.RedisUtils; import io.swagger.v3.oas.annotations.security.SecurityRequirement; @@ -22,36 +24,37 @@ import java.util.*; @SecurityRequirement(name = HttpHeaders.AUTHORIZATION) public class SysSystemInfoController { - /** - * 缓存监控 - * @return R - */ - @GetMapping("/cache") - public R cache() { + /** + * 缓存监控 + * + * @return R + */ + @GetMapping("/cache") + public R cache() { Properties info = RedisUtils.execute((RedisCallback) RedisServerCommands::info); Properties commandStats = RedisUtils .execute((RedisCallback) connection -> connection.serverCommands().info("commandstats")); Object dbSize = RedisUtils.execute((RedisCallback) RedisServerCommands::dbSize); - if (commandStats == null) { - return R.failed("获取异常"); - } + if (commandStats == null) { + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_SYSTEM_CACHE_FETCH_ERROR)); + } - Map result = new HashMap<>(3); - result.put("info", info); - result.put("dbSize", dbSize); + Map result = new HashMap<>(3); + result.put("info", info); + result.put("dbSize", dbSize); - List> pieList = new ArrayList<>(); - commandStats.stringPropertyNames().forEach(key -> { - Map data = new HashMap<>(2); - String property = commandStats.getProperty(key); - data.put("name", StringUtils.removeStart(key, "cmdstat_")); - data.put("value", StringUtils.substringBetween(property, "calls=", ",usec")); - pieList.add(data); - }); + List> pieList = new ArrayList<>(); + commandStats.stringPropertyNames().forEach(key -> { + Map data = new HashMap<>(2); + String property = commandStats.getProperty(key); + data.put("name", StringUtils.removeStart(key, "cmdstat_")); + data.put("value", StringUtils.substringBetween(property, "calls=", ",usec")); + pieList.add(data); + }); - result.put("commandStats", pieList); - return R.ok(result); - } + result.put("commandStats", pieList); + return R.ok(result); + } } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/MiniAppLoginHandler.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/MiniAppLoginHandler.java index 81803fbe1..dfe5615ee 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/MiniAppLoginHandler.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/MiniAppLoginHandler.java @@ -22,6 +22,7 @@ import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.pig4cloud.pigx.admin.api.constant.UpmsErrorCodes; import com.pig4cloud.pigx.admin.api.dto.UserDTO; import com.pig4cloud.pigx.admin.api.dto.UserInfo; import com.pig4cloud.pigx.admin.api.entity.SysSocialDetails; @@ -31,6 +32,7 @@ import com.pig4cloud.pigx.admin.service.SysUserService; import com.pig4cloud.pigx.common.core.constant.SecurityConstants; import com.pig4cloud.pigx.common.core.constant.enums.LoginTypeEnum; import com.pig4cloud.pigx.common.core.exception.CheckedException; +import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -73,7 +75,7 @@ public class MiniAppLoginHandler extends AbstractLoginHandler { if (resultJsonObj.containsKey("errcode")) { log.error("微信小程序登录失败:{}", result); - throw new CheckedException("微信小程序登录失败"); + throw new CheckedException(MsgUtils.getMessage(UpmsErrorCodes.SYS_MINIAPP_LOGIN_FAILED)); } return resultJsonObj.getStr("openid"); diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/ConnectServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/ConnectServiceImpl.java index 36ad6224a..dcb067e23 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/ConnectServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/ConnectServiceImpl.java @@ -18,6 +18,7 @@ import com.dingtalk.api.response.OapiGettokenResponse; import com.dingtalk.api.response.OapiUserListidResponse; import com.dingtalk.api.response.OapiV2DepartmentListsubResponse; import com.dingtalk.api.response.OapiV2UserGetResponse; +import com.pig4cloud.pigx.admin.api.constant.UpmsErrorCodes; import com.pig4cloud.pigx.admin.api.dto.UserDTO; import com.pig4cloud.pigx.admin.api.entity.SysDept; import com.pig4cloud.pigx.admin.api.entity.SysSocialDetails; @@ -28,7 +29,6 @@ import com.pig4cloud.pigx.admin.service.SysDeptService; import com.pig4cloud.pigx.admin.service.SysUserService; import com.pig4cloud.pigx.common.core.constant.SecurityConstants; import com.pig4cloud.pigx.common.core.constant.enums.LoginTypeEnum; -import com.pig4cloud.pigx.common.core.exception.ErrorCodes; import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; import lombok.RequiredArgsConstructor; @@ -213,7 +213,7 @@ public class ConnectServiceImpl implements ConnectService { departList = wxCpService.getDepartmentService().list(null); } catch (WxErrorException e) { log.error("获取企业微信部门列表失败", e); - return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_CONNECT_CP_DEPT_SYNC_ERROR)); + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_CONNECT_CP_DEPT_SYNC_ERROR)); } if (CollUtil.isEmpty(departList)) { @@ -251,7 +251,7 @@ public class ConnectServiceImpl implements ConnectService { cpUserList = wxCpService.getUserService().listByDepartment(1L, true, 0); } catch (WxErrorException e) { log.error("获取企业微信用户列表失败", e); - return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_CONNECT_CP_USER_SYNC_ERROR)); + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_CONNECT_CP_USER_SYNC_ERROR)); } if (CollUtil.isEmpty(cpUserList)) { diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysDeptServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysDeptServiceImpl.java index 104b10713..d5fe7cf81 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysDeptServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysDeptServiceImpl.java @@ -30,12 +30,14 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.github.yulichang.wrapper.MPJLambdaWrapper; import com.pig4cloud.pigx.admin.api.constant.OrgTypeEnum; import com.pig4cloud.pigx.admin.api.constant.PostCodeEnum; +import com.pig4cloud.pigx.admin.api.constant.UpmsErrorCodes; import com.pig4cloud.pigx.admin.api.entity.*; import com.pig4cloud.pigx.admin.api.utils.DataUtil; import com.pig4cloud.pigx.admin.api.vo.DeptExcelVO; import com.pig4cloud.pigx.admin.api.vo.OrgTreeVO; import com.pig4cloud.pigx.admin.mapper.*; import com.pig4cloud.pigx.admin.service.SysDeptService; +import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; import com.pig4cloud.pigx.common.data.datascope.DataScope; import com.pig4cloud.pigx.common.excel.vo.ErrorMessage; @@ -59,329 +61,336 @@ import java.util.*; @AllArgsConstructor public class SysDeptServiceImpl extends ServiceImpl implements SysDeptService { - private final SysUserMapper userMapper; + private final SysUserMapper userMapper; - private final SysDeptMapper deptMapper; + private final SysDeptMapper deptMapper; - private final SysPostMapper postMapper; + private final SysPostMapper postMapper; - private final SysRoleMapper roleMapper; + private final SysRoleMapper roleMapper; - private final SysUserDeptMapper userDeptMapper; + private final SysUserDeptMapper userDeptMapper; - private final SysUserPostMapper userPostMapper; + private final SysUserPostMapper userPostMapper; - /** - * 删除部门 - * @param id 部门 ID - * @return 成功、失败 - */ - @Override - @Transactional(rollbackFor = Exception.class) - public Boolean removeDeptById(Long id) { - // 级联删除部门 - List idList = this.listDescendant(id).stream().map(SysDept::getDeptId).toList(); + /** + * 删除部门 + * + * @param id 部门 ID + * @return 成功、失败 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean removeDeptById(Long id) { + // 级联删除部门 + List idList = this.listDescendant(id).stream().map(SysDept::getDeptId).toList(); - Optional.ofNullable(idList).filter(CollUtil::isNotEmpty).ifPresent(this::removeByIds); + Optional.ofNullable(idList).filter(CollUtil::isNotEmpty).ifPresent(this::removeByIds); - return Boolean.TRUE; - } + return Boolean.TRUE; + } - /** - * 查询全部部门树 - * @param deptName - * @param parentId - * @return 树 部门名称 - */ - @Override - public List> selectTree(String deptName, Long parentId) { - // 查询全部部门 - List deptAllList = deptMapper - .selectList(Wrappers.lambdaQuery().like(StrUtil.isNotBlank(deptName), SysDept::getName, deptName)); - // 查询数据权限内部门 - List deptOwnIdList = deptMapper - .selectListByScope( - Wrappers.lambdaQuery().like(StrUtil.isNotBlank(deptName), SysDept::getName, deptName), - DataScope.of()) - .stream() - .map(SysDept::getDeptId) - .toList(); + /** + * 查询全部部门树 + * + * @param deptName + * @param parentId + * @return 树 部门名称 + */ + @Override + public List> selectTree(String deptName, Long parentId) { + // 查询全部部门 + List deptAllList = deptMapper + .selectList(Wrappers.lambdaQuery().like(StrUtil.isNotBlank(deptName), SysDept::getName, deptName)); + // 查询数据权限内部门 + List deptOwnIdList = deptMapper + .selectListByScope( + Wrappers.lambdaQuery().like(StrUtil.isNotBlank(deptName), SysDept::getName, deptName), + DataScope.of()) + .stream() + .map(SysDept::getDeptId) + .toList(); - // 权限内部门 - List> collect = deptAllList.stream() - .filter(dept -> dept.getDeptId().intValue() != dept.getParentId()) - .sorted(Comparator.comparingInt(SysDept::getSortOrder)) - .map(dept -> { - TreeNode treeNode = new TreeNode<>(); - treeNode.setId(dept.getDeptId()); - treeNode.setParentId(dept.getParentId()); - treeNode.setName(dept.getName()); - treeNode.setWeight(dept.getSortOrder()); - // 有权限不返回标识 - Map extra = new HashMap<>(8); - extra.put("isLock", !deptOwnIdList.contains(dept.getDeptId())); - extra.put("createTime", dept.getCreateTime()); - treeNode.setExtra(extra); - return treeNode; - }) - .toList(); + // 权限内部门 + List> collect = deptAllList.stream() + .filter(dept -> dept.getDeptId().intValue() != dept.getParentId()) + .sorted(Comparator.comparingInt(SysDept::getSortOrder)) + .map(dept -> { + TreeNode treeNode = new TreeNode<>(); + treeNode.setId(dept.getDeptId()); + treeNode.setParentId(dept.getParentId()); + treeNode.setName(dept.getName()); + treeNode.setWeight(dept.getSortOrder()); + // 有权限不返回标识 + Map extra = new HashMap<>(8); + extra.put("isLock", !deptOwnIdList.contains(dept.getDeptId())); + extra.put("createTime", dept.getCreateTime()); + treeNode.setExtra(extra); + return treeNode; + }) + .toList(); - // 模糊查询 不组装树结构 直接返回 表格方便编辑 - if (StrUtil.isNotBlank(deptName)) { - return collect.stream().map(node -> { - Tree tree = new Tree<>(); - tree.putAll(node.getExtra()); - BeanUtils.copyProperties(node, tree); - return tree; - }).toList(); - } + // 模糊查询 不组装树结构 直接返回 表格方便编辑 + if (StrUtil.isNotBlank(deptName)) { + return collect.stream().map(node -> { + Tree tree = new Tree<>(); + tree.putAll(node.getExtra()); + BeanUtils.copyProperties(node, tree); + return tree; + }).toList(); + } - return TreeUtil.build(collect, parentId == null ? 0 : parentId); - } + return TreeUtil.build(collect, parentId == null ? 0 : parentId); + } - /** - * 导出部门 - * @return - */ - @Override - public List listExcelVo() { - List list = this.list(); - List deptExcelVos = list.stream().map(item -> { - DeptExcelVO deptExcelVo = new DeptExcelVO(); - deptExcelVo.setName(item.getName()); - Optional first = this.list() - .stream() - .filter(it -> item.getParentId().equals(it.getDeptId())) - .map(SysDept::getName) - .findFirst(); - deptExcelVo.setParentName(first.orElse("根部门")); - deptExcelVo.setSortOrder(item.getSortOrder()); - return deptExcelVo; - }).toList(); - return deptExcelVos; - } + /** + * 导出部门 + * + * @return + */ + @Override + public List listExcelVo() { + List list = this.list(); + List deptExcelVos = list.stream().map(item -> { + DeptExcelVO deptExcelVo = new DeptExcelVO(); + deptExcelVo.setName(item.getName()); + Optional first = this.list() + .stream() + .filter(it -> item.getParentId().equals(it.getDeptId())) + .map(SysDept::getName) + .findFirst(); + deptExcelVo.setParentName(first.orElse("根部门")); + deptExcelVo.setSortOrder(item.getSortOrder()); + return deptExcelVo; + }).toList(); + return deptExcelVos; + } - @Override - public R importDept(List excelVOList, BindingResult bindingResult) { - List errorMessageList = (List) bindingResult.getTarget(); + @Override + public R importDept(List excelVOList, BindingResult bindingResult) { + List errorMessageList = (List) bindingResult.getTarget(); - List deptList = this.list(); - for (DeptExcelVO item : excelVOList) { - Set errorMsg = new HashSet<>(); - boolean exsitUsername = deptList.stream().anyMatch(sysDept -> item.getName().equals(sysDept.getName())); - if (exsitUsername) { - errorMsg.add("部门名称已经存在"); - } - SysDept one = this.getOne(Wrappers.lambdaQuery().eq(SysDept::getName, item.getParentName())); - if (item.getParentName().equals("根部门")) { - one = new SysDept(); - one.setDeptId(0L); - } - if (one == null) { - errorMsg.add("上级部门不存在"); - } - if (CollUtil.isEmpty(errorMsg)) { - SysDept sysDept = new SysDept(); - sysDept.setName(item.getName()); - sysDept.setParentId(one.getDeptId()); - sysDept.setSortOrder(item.getSortOrder()); - baseMapper.insert(sysDept); - } - else { - // 数据不合法情况 - errorMessageList.add(new ErrorMessage(item.getLineNum(), errorMsg)); - } - } - if (CollUtil.isNotEmpty(errorMessageList)) { - return R.failed(errorMessageList); - } - return R.ok(null, "部门导入成功"); - } + List deptList = this.list(); + for (DeptExcelVO item : excelVOList) { + Set errorMsg = new HashSet<>(); + boolean exsitUsername = deptList.stream().anyMatch(sysDept -> item.getName().equals(sysDept.getName())); + if (exsitUsername) { + errorMsg.add("部门名称已经存在"); + } + SysDept one = this.getOne(Wrappers.lambdaQuery().eq(SysDept::getName, item.getParentName())); + if (item.getParentName().equals("根部门")) { + one = new SysDept(); + one.setDeptId(0L); + } + if (one == null) { + errorMsg.add("上级部门不存在"); + } + if (CollUtil.isEmpty(errorMsg)) { + SysDept sysDept = new SysDept(); + sysDept.setName(item.getName()); + sysDept.setParentId(one.getDeptId()); + sysDept.setSortOrder(item.getSortOrder()); + baseMapper.insert(sysDept); + } else { + // 数据不合法情况 + errorMessageList.add(new ErrorMessage(item.getLineNum(), errorMsg)); + } + } + if (CollUtil.isNotEmpty(errorMessageList)) { + return R.failed(errorMessageList); + } + return R.ok(null, MsgUtils.getMessage(UpmsErrorCodes.SYS_DEPT_IMPORT_SUCCEED)); + } - /** - * 查询所有子节点 (包含当前节点) - * @param deptId 部门ID 目标部门ID - * @return ID - */ - @Override - public List listDescendant(Long deptId) { - // 查询全部部门 - List allDeptList = baseMapper.selectList(Wrappers.emptyWrapper()); + /** + * 查询所有子节点 (包含当前节点) + * + * @param deptId 部门ID 目标部门ID + * @return ID + */ + @Override + public List listDescendant(Long deptId) { + // 查询全部部门 + List allDeptList = baseMapper.selectList(Wrappers.emptyWrapper()); - // 递归查询所有子节点 - List resDeptList = new ArrayList<>(); - recursiveDept(allDeptList, deptId, resDeptList); + // 递归查询所有子节点 + List resDeptList = new ArrayList<>(); + recursiveDept(allDeptList, deptId, resDeptList); - // 添加当前节点 - resDeptList.addAll(allDeptList.stream().filter(sysDept -> deptId.equals(sysDept.getDeptId())).toList()); - return resDeptList; - } + // 添加当前节点 + resDeptList.addAll(allDeptList.stream().filter(sysDept -> deptId.equals(sysDept.getDeptId())).toList()); + return resDeptList; + } - /** - * 获取部门负责人 - *

- * 1. 根据dept 查询用户 2. 筛选用户列表中 post - * @param deptId deptId - * @return user id list - */ - @Override - public List listDeptLeader(Long deptId) { + /** + * 获取部门负责人 + *

+ * 1. 根据dept 查询用户 2. 筛选用户列表中 post + * + * @param deptId deptId + * @return user id list + */ + @Override + public List listDeptLeader(Long deptId) { - List userDeptList = userDeptMapper - .selectList(Wrappers.lambdaQuery().eq(SysUserDept::getDeptId, deptId)); - if (CollUtil.isEmpty(userDeptList)) { - return null; - } + List userDeptList = userDeptMapper + .selectList(Wrappers.lambdaQuery().eq(SysUserDept::getDeptId, deptId)); + if (CollUtil.isEmpty(userDeptList)) { + return null; + } - SysPost deptLeader = postMapper - .selectOne(Wrappers.lambdaQuery().eq(SysPost::getPostCode, PostCodeEnum.TEAM_LEADER.getCode())); - if (deptLeader == null) { - return null; - } + SysPost deptLeader = postMapper + .selectOne(Wrappers.lambdaQuery().eq(SysPost::getPostCode, PostCodeEnum.TEAM_LEADER.getCode())); + if (deptLeader == null) { + return null; + } - List userIdList = userDeptList.stream().map(SysUserDept::getUserId).toList(); - return userPostMapper.selectList(Wrappers.lambdaQuery().in(SysUserPost::getUserId, userIdList)) - .stream() - .filter(post -> Objects.equals(post.getPostId(), deptLeader.getPostId())) - .map(SysUserPost::getUserId) - .toList(); - } + List userIdList = userDeptList.stream().map(SysUserDept::getUserId).toList(); + return userPostMapper.selectList(Wrappers.lambdaQuery().in(SysUserPost::getUserId, userIdList)) + .stream() + .filter(post -> Objects.equals(post.getPostId(), deptLeader.getPostId())) + .map(SysUserPost::getUserId) + .toList(); + } - /** - * 根据父部门ID和类型获取组织树 - * @param parentDeptId 父部门ID - * @param type 类型 - * @return 组织树信息 - */ - public Map listOrgTree(Long parentDeptId, String type) { + /** + * 根据父部门ID和类型获取组织树 + * + * @param parentDeptId 父部门ID + * @param type 类型 + * @return 组织树信息 + */ + public Map listOrgTree(Long parentDeptId, String type) { - List orgs = new LinkedList<>(); + List orgs = new LinkedList<>(); - if (StrUtil.equals(type, OrgTypeEnum.ROLE.getType())) { - // 角色 - List roleList = roleMapper.selectList(Wrappers.emptyWrapper()); + if (StrUtil.equals(type, OrgTypeEnum.ROLE.getType())) { + // 角色 + List roleList = roleMapper.selectList(Wrappers.emptyWrapper()); - roleList.forEach(role -> { - OrgTreeVO orgTreeVo = new OrgTreeVO(); - orgTreeVo.setId(role.getRoleId()); - orgTreeVo.setName(role.getRoleName()); - orgTreeVo.setType(OrgTypeEnum.ROLE.getType()); - orgTreeVo.setSelected(false); - orgs.add(orgTreeVo); - }); + roleList.forEach(role -> { + OrgTreeVO orgTreeVo = new OrgTreeVO(); + orgTreeVo.setId(role.getRoleId()); + orgTreeVo.setName(role.getRoleName()); + orgTreeVo.setType(OrgTypeEnum.ROLE.getType()); + orgTreeVo.setSelected(false); + orgs.add(orgTreeVo); + }); - return Dict.create() - .set("roleList", orgs) - .set("postList", new ArrayList<>()) - .set("childDepartments", orgs) - .set("employees", new ArrayList<>()); - } + return Dict.create() + .set("roleList", orgs) + .set("postList", new ArrayList<>()) + .set("childDepartments", orgs) + .set("employees", new ArrayList<>()); + } - if (StrUtil.equals(type, OrgTypeEnum.POST.getType())) { - // 角色 - List postList = postMapper.selectList(Wrappers.emptyWrapper()); + if (StrUtil.equals(type, OrgTypeEnum.POST.getType())) { + // 角色 + List postList = postMapper.selectList(Wrappers.emptyWrapper()); - postList.forEach(post -> { - OrgTreeVO orgTreeVo = new OrgTreeVO(); - orgTreeVo.setId(post.getPostId()); - orgTreeVo.setName(post.getPostName()); - orgTreeVo.setType(OrgTypeEnum.POST.getType()); - orgTreeVo.setSelected(false); - orgs.add(orgTreeVo); - }); + postList.forEach(post -> { + OrgTreeVO orgTreeVo = new OrgTreeVO(); + orgTreeVo.setId(post.getPostId()); + orgTreeVo.setName(post.getPostName()); + orgTreeVo.setType(OrgTypeEnum.POST.getType()); + orgTreeVo.setSelected(false); + orgs.add(orgTreeVo); + }); - return Dict.create() - .set("roleList", new ArrayList<>()) - .set("postList", orgs) - .set("childDepartments", orgs) - .set("employees", new ArrayList<>()); - } + return Dict.create() + .set("roleList", new ArrayList<>()) + .set("postList", orgs) + .set("childDepartments", orgs) + .set("employees", new ArrayList<>()); + } - Dict dict = Dict.create() - .set("titleDepartments", new ArrayList<>()) - .set("roleList", new ArrayList<>()) - .set("postList", new ArrayList<>()) - .set("employees", new ArrayList<>()); + Dict dict = Dict.create() + .set("titleDepartments", new ArrayList<>()) + .set("roleList", new ArrayList<>()) + .set("postList", new ArrayList<>()) + .set("employees", new ArrayList<>()); - List deptList = this.list( - Wrappers.lambdaQuery().eq(Objects.nonNull(parentDeptId), SysDept::getParentId, parentDeptId)); + List deptList = this.list( + Wrappers.lambdaQuery().eq(Objects.nonNull(parentDeptId), SysDept::getParentId, parentDeptId)); - // 查询所有部门及员工 - List deptVoList = new ArrayList<>(); - deptList.forEach(dept -> { - OrgTreeVO orgTreeVo = new OrgTreeVO(); - orgTreeVo.setId(dept.getDeptId()); - orgTreeVo.setName(dept.getName()); - orgTreeVo.setType(OrgTypeEnum.DEPT.getType()); - orgTreeVo.setSelected(false); - deptVoList.add(orgTreeVo); - }); - dict.set("childDepartments", deptVoList); + // 查询所有部门及员工 + List deptVoList = new ArrayList<>(); + deptList.forEach(dept -> { + OrgTreeVO orgTreeVo = new OrgTreeVO(); + orgTreeVo.setId(dept.getDeptId()); + orgTreeVo.setName(dept.getName()); + orgTreeVo.setType(OrgTypeEnum.DEPT.getType()); + orgTreeVo.setSelected(false); + deptVoList.add(orgTreeVo); + }); + dict.set("childDepartments", deptVoList); - if (!StrUtil.equals(type, OrgTypeEnum.DEPT.getType())) { + if (!StrUtil.equals(type, OrgTypeEnum.DEPT.getType())) { - List userVoList = new ArrayList<>(); + List userVoList = new ArrayList<>(); - MPJLambdaWrapper userQuery = new MPJLambdaWrapper<>(); - userQuery.selectAll(SysUser.class) - .leftJoin(SysUserDept.class, SysUserDept::getUserId, SysUser::getUserId) - .eq(SysUserDept::getDeptId, parentDeptId); + MPJLambdaWrapper userQuery = new MPJLambdaWrapper<>(); + userQuery.selectAll(SysUser.class) + .leftJoin(SysUserDept.class, SysUserDept::getUserId, SysUser::getUserId) + .eq(SysUserDept::getDeptId, parentDeptId); - List userList = userMapper.selectJoinList(userQuery); + List userList = userMapper.selectJoinList(userQuery); - userList.forEach(user -> { - OrgTreeVO orgTreeVo = new OrgTreeVO(); - orgTreeVo.setId(user.getUserId()); - orgTreeVo.setName(user.getUsername()); - orgTreeVo.setType(OrgTypeEnum.USER.getType()); - orgTreeVo.setSelected(false); - orgTreeVo.setAvatar(user.getAvatar()); - userVoList.add(orgTreeVo); - }); - dict.set("employees", userVoList); - } + userList.forEach(user -> { + OrgTreeVO orgTreeVo = new OrgTreeVO(); + orgTreeVo.setId(user.getUserId()); + orgTreeVo.setName(user.getUsername()); + orgTreeVo.setType(OrgTypeEnum.USER.getType()); + orgTreeVo.setSelected(false); + orgTreeVo.setAvatar(user.getAvatar()); + userVoList.add(orgTreeVo); + }); + dict.set("employees", userVoList); + } - if (parentDeptId > 0) { - List allDept = this.list(); - List depts = DataUtil.selectParentByDept(parentDeptId, allDept); - dict.set("titleDepartments", CollUtil.reverse(depts)); - } + if (parentDeptId > 0) { + List allDept = this.list(); + List depts = DataUtil.selectParentByDept(parentDeptId, allDept); + dict.set("titleDepartments", CollUtil.reverse(depts)); + } - return dict; - } + return dict; + } - /** - * 模糊搜索用户 - * @param username 用户名/拼音/首字母 - * @return 匹配到的用户 - */ - public List getOrgTreeUser(String username) { - return userMapper.selectList(Wrappers.lambdaQuery().like(SysUser::getUsername, username)) - .stream() - .map(user -> { - OrgTreeVO orgTreeVo = new OrgTreeVO(); - orgTreeVo.setId(user.getUserId()); - orgTreeVo.setName(user.getUsername()); - orgTreeVo.setType(OrgTypeEnum.USER.getType()); - orgTreeVo.setSelected(false); - orgTreeVo.setAvatar(user.getAvatar()); - return orgTreeVo; - }) - .toList(); - } + /** + * 模糊搜索用户 + * + * @param username 用户名/拼音/首字母 + * @return 匹配到的用户 + */ + public List getOrgTreeUser(String username) { + return userMapper.selectList(Wrappers.lambdaQuery().like(SysUser::getUsername, username)) + .stream() + .map(user -> { + OrgTreeVO orgTreeVo = new OrgTreeVO(); + orgTreeVo.setId(user.getUserId()); + orgTreeVo.setName(user.getUsername()); + orgTreeVo.setType(OrgTypeEnum.USER.getType()); + orgTreeVo.setSelected(false); + orgTreeVo.setAvatar(user.getAvatar()); + return orgTreeVo; + }) + .toList(); + } - /** - * 递归查询所有子节点。 - * @param allDeptList 所有部门列表 - * @param parentId 父部门ID - * @param resDeptList 结果集合 - */ - private void recursiveDept(List allDeptList, Long parentId, List resDeptList) { - // 使用 Stream API 进行筛选和遍历 - allDeptList.stream().filter(sysDept -> sysDept.getParentId().equals(parentId)).forEach(sysDept -> { - resDeptList.add(sysDept); - recursiveDept(allDeptList, sysDept.getDeptId(), resDeptList); - }); - } + /** + * 递归查询所有子节点。 + * + * @param allDeptList 所有部门列表 + * @param parentId 父部门ID + * @param resDeptList 结果集合 + */ + private void recursiveDept(List allDeptList, Long parentId, List resDeptList) { + // 使用 Stream API 进行筛选和遍历 + allDeptList.stream().filter(sysDept -> sysDept.getParentId().equals(parentId)).forEach(sysDept -> { + resDeptList.add(sysDept); + recursiveDept(allDeptList, sysDept.getDeptId(), resDeptList); + }); + } } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysDictItemServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysDictItemServiceImpl.java index c9a1a2da0..d335b31b7 100755 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysDictItemServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysDictItemServiceImpl.java @@ -17,6 +17,7 @@ package com.pig4cloud.pigx.admin.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pig4cloud.pigx.admin.api.constant.UpmsErrorCodes; import com.pig4cloud.pigx.admin.api.entity.SysDict; import com.pig4cloud.pigx.admin.api.entity.SysDictItem; import com.pig4cloud.pigx.admin.mapper.SysDictItemMapper; @@ -24,7 +25,6 @@ import com.pig4cloud.pigx.admin.service.SysDictItemService; import com.pig4cloud.pigx.admin.service.SysDictService; import com.pig4cloud.pigx.common.core.constant.CacheConstants; import com.pig4cloud.pigx.common.core.constant.enums.DictTypeEnum; -import com.pig4cloud.pigx.common.core.exception.ErrorCodes; import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; import lombok.AllArgsConstructor; @@ -41,41 +41,43 @@ import org.springframework.stereotype.Service; @AllArgsConstructor public class SysDictItemServiceImpl extends ServiceImpl implements SysDictItemService { - private final SysDictService dictService; + private final SysDictService dictService; - /** - * 删除字典项 - * @param id 字典项ID - * @return - */ - @Override - @CacheEvict(value = CacheConstants.DICT_DETAILS, allEntries = true) - public R removeDictItem(Long id) { - // 根据ID查询字典ID - SysDictItem dictItem = this.getById(id); - SysDict dict = dictService.getById(dictItem.getDictId()); - // 系统内置 - if (DictTypeEnum.SYSTEM.getType().equals(dict.getSystemFlag())) { - return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_DICT_DELETE_SYSTEM)); - } - return R.ok(this.removeById(id)); - } + /** + * 删除字典项 + * + * @param id 字典项ID + * @return + */ + @Override + @CacheEvict(value = CacheConstants.DICT_DETAILS, allEntries = true) + public R removeDictItem(Long id) { + // 根据ID查询字典ID + SysDictItem dictItem = this.getById(id); + SysDict dict = dictService.getById(dictItem.getDictId()); + // 系统内置 + if (DictTypeEnum.SYSTEM.getType().equals(dict.getSystemFlag())) { + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_DICT_DELETE_SYSTEM)); + } + return R.ok(this.removeById(id)); + } - /** - * 更新字典项 - * @param item 字典项 - * @return - */ - @Override - @CacheEvict(value = CacheConstants.DICT_DETAILS, key = "#item.dictType") - public R updateDictItem(SysDictItem item) { - // 查询字典 - SysDict dict = dictService.getById(item.getDictId()); - // 系统内置 - if (DictTypeEnum.SYSTEM.getType().equals(dict.getSystemFlag())) { - return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_DICT_UPDATE_SYSTEM)); - } - return R.ok(this.updateById(item)); - } + /** + * 更新字典项 + * + * @param item 字典项 + * @return + */ + @Override + @CacheEvict(value = CacheConstants.DICT_DETAILS, key = "#item.dictType") + public R updateDictItem(SysDictItem item) { + // 查询字典 + SysDict dict = dictService.getById(item.getDictId()); + // 系统内置 + if (DictTypeEnum.SYSTEM.getType().equals(dict.getSystemFlag())) { + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_DICT_UPDATE_SYSTEM)); + } + return R.ok(this.updateById(item)); + } } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysDictServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysDictServiceImpl.java index 49e2e6602..975672866 100755 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysDictServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysDictServiceImpl.java @@ -19,6 +19,7 @@ package com.pig4cloud.pigx.admin.service.impl; import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pig4cloud.pigx.admin.api.constant.UpmsErrorCodes; import com.pig4cloud.pigx.admin.api.entity.SysDict; import com.pig4cloud.pigx.admin.api.entity.SysDictItem; import com.pig4cloud.pigx.admin.mapper.SysDictItemMapper; @@ -26,7 +27,6 @@ import com.pig4cloud.pigx.admin.mapper.SysDictMapper; import com.pig4cloud.pigx.admin.service.SysDictService; import com.pig4cloud.pigx.common.core.constant.CacheConstants; import com.pig4cloud.pigx.common.core.constant.enums.DictTypeEnum; -import com.pig4cloud.pigx.common.core.exception.ErrorCodes; import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; import lombok.AllArgsConstructor; @@ -46,55 +46,58 @@ import java.util.List; @AllArgsConstructor public class SysDictServiceImpl extends ServiceImpl implements SysDictService { - private final SysDictItemMapper dictItemMapper; + private final SysDictItemMapper dictItemMapper; - /** - * 根据ID 删除字典 - * @param ids 字典ID 列表 - * @return - */ - @Override - @Transactional(rollbackFor = Exception.class) - @CacheEvict(value = CacheConstants.DICT_DETAILS, allEntries = true) - public R removeDictByIds(Long[] ids) { + /** + * 根据ID 删除字典 + * + * @param ids 字典ID 列表 + * @return + */ + @Override + @Transactional(rollbackFor = Exception.class) + @CacheEvict(value = CacheConstants.DICT_DETAILS, allEntries = true) + public R removeDictByIds(Long[] ids) { - List dictIdList = baseMapper.selectByIds(CollUtil.toList(ids)) - .stream() - .filter(sysDict -> !sysDict.getSystemFlag().equals(DictTypeEnum.SYSTEM.getType()))// 系统内置类型不删除 - .map(SysDict::getId) + List dictIdList = baseMapper.selectByIds(CollUtil.toList(ids)) + .stream() + .filter(sysDict -> !sysDict.getSystemFlag().equals(DictTypeEnum.SYSTEM.getType()))// 系统内置类型不删除 + .map(SysDict::getId) .toList(); - baseMapper.deleteByIds(dictIdList); + baseMapper.deleteByIds(dictIdList); - dictItemMapper.delete(Wrappers.lambdaQuery().in(SysDictItem::getDictId, dictIdList)); - return R.ok(); - } + dictItemMapper.delete(Wrappers.lambdaQuery().in(SysDictItem::getDictId, dictIdList)); + return R.ok(); + } - /** - * 更新字典 - * @param dict 字典 - * @return - */ - @Override - @CacheEvict(value = CacheConstants.DICT_DETAILS, key = "#dict.dictType") - public R updateDict(SysDict dict) { - SysDict sysDict = this.getById(dict.getId()); - // 系统内置 - if (DictTypeEnum.SYSTEM.getType().equals(sysDict.getSystemFlag())) { - return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_DICT_UPDATE_SYSTEM)); - } - this.updateById(dict); - return R.ok(dict); - } + /** + * 更新字典 + * + * @param dict 字典 + * @return + */ + @Override + @CacheEvict(value = CacheConstants.DICT_DETAILS, key = "#dict.dictType") + public R updateDict(SysDict dict) { + SysDict sysDict = this.getById(dict.getId()); + // 系统内置 + if (DictTypeEnum.SYSTEM.getType().equals(sysDict.getSystemFlag())) { + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_DICT_UPDATE_SYSTEM)); + } + this.updateById(dict); + return R.ok(dict); + } - /** - * 同步缓存 (清空缓存) - * @return R - */ - @Override - @CacheEvict(value = CacheConstants.DICT_DETAILS, allEntries = true) - public R syncDictCache() { - return R.ok(); - } + /** + * 同步缓存 (清空缓存) + * + * @return R + */ + @Override + @CacheEvict(value = CacheConstants.DICT_DETAILS, allEntries = true) + public R syncDictCache() { + return R.ok(); + } } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMenuServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMenuServiceImpl.java index 119db01bb..a610b547a 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMenuServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMenuServiceImpl.java @@ -27,6 +27,7 @@ import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pig4cloud.pigx.admin.api.constant.UpmsErrorCodes; import com.pig4cloud.pigx.admin.api.entity.SysI18nEntity; import com.pig4cloud.pigx.admin.api.entity.SysMenu; import com.pig4cloud.pigx.admin.api.entity.SysRoleMenu; @@ -38,7 +39,6 @@ import com.pig4cloud.pigx.common.core.constant.CacheConstants; import com.pig4cloud.pigx.common.core.constant.CommonConstants; import com.pig4cloud.pigx.common.core.constant.enums.MenuTypeEnum; import com.pig4cloud.pigx.common.core.constant.enums.YesNoEnum; -import com.pig4cloud.pigx.common.core.exception.ErrorCodes; import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; import com.pig4cloud.pigx.common.security.util.SecurityUtils; @@ -85,7 +85,7 @@ public class SysMenuServiceImpl extends ServiceImpl impl // 1. 检查菜单是否存在 SysMenu menu = this.getById(id); if (menu == null) { - return R.failed("菜单不存在"); + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_MENU_NOT_FOUND)); } // 2. 递归收集当前菜单及所有子孙菜单的ID @@ -103,7 +103,8 @@ public class SysMenuServiceImpl extends ServiceImpl impl /** * 递归收集菜单ID及其所有子孙菜单ID - * @param menuId 菜单ID + * + * @param menuId 菜单ID * @param menuIds 收集结果列表 */ private void collectMenuIds(Long menuId, List menuIds) { @@ -232,10 +233,10 @@ public class SysMenuServiceImpl extends ServiceImpl impl // 查询菜单是否存在且为菜单类型(非按钮) SysMenu menu = this.getById(menuId); if (menu == null) { - return R.failed("菜单不存在"); + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_MENU_NOT_FOUND)); } if (MenuTypeEnum.BUTTON.getType().equals(menu.getMenuType())) { - return R.failed("按钮类型不能设置为首页"); + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_MENU_BUTTON_CANNOT_HOME)); } // Toggle 模式:如果已是首页则取消,否则设置为首页 @@ -243,13 +244,13 @@ public class SysMenuServiceImpl extends ServiceImpl impl // 已是首页,取消首页设置 menu.setIsHomePage(YesNoEnum.NO.getCode()); this.updateById(menu); - return R.ok(Boolean.TRUE, "取消首页成功"); + return R.ok(Boolean.TRUE, MsgUtils.getMessage(UpmsErrorCodes.SYS_MENU_HOME_CANCEL_SUCCEED)); } else { // 不是首页,先将所有菜单的首页标记重置为0,再设置当前菜单为首页 this.update(Wrappers.lambdaUpdate().set(SysMenu::getIsHomePage, YesNoEnum.NO.getCode())); menu.setIsHomePage(YesNoEnum.YES.getCode()); this.updateById(menu); - return R.ok(Boolean.TRUE, "设置首页成功"); + return R.ok(Boolean.TRUE, MsgUtils.getMessage(UpmsErrorCodes.SYS_MENU_HOME_SET_SUCCEED)); } } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMessageServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMessageServiceImpl.java index b48263765..76da753d9 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMessageServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMessageServiceImpl.java @@ -23,6 +23,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.github.yulichang.wrapper.MPJLambdaWrapper; import com.pig4cloud.pigx.admin.api.constant.SmsBizCodeEnum; import com.pig4cloud.pigx.admin.api.constant.SystemConfigTypeEnum; +import com.pig4cloud.pigx.admin.api.constant.UpmsErrorCodes; import com.pig4cloud.pigx.admin.api.dto.MessageEmailDTO; import com.pig4cloud.pigx.admin.api.dto.MessageHookDTO; import com.pig4cloud.pigx.admin.api.dto.MessageSmsDTO; @@ -39,7 +40,6 @@ import com.pig4cloud.pigx.common.core.constant.SecurityConstants; import com.pig4cloud.pigx.common.core.constant.enums.LoginTypeEnum; import com.pig4cloud.pigx.common.core.constant.enums.YesNoEnum; import com.pig4cloud.pigx.common.core.exception.CheckedException; -import com.pig4cloud.pigx.common.core.exception.ErrorCodes; import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; import com.pig4cloud.pigx.common.data.cache.RedisUtils; @@ -284,7 +284,7 @@ public class SysMessageServiceImpl extends ServiceImpl new CheckedException("发送失败,短信通道配置不存在")); + .orElseThrow(() -> new CheckedException( + MsgUtils.getMessage(UpmsErrorCodes.SYS_MESSAGE_SMS_CHANNEL_MISSING))); BaseConfig baseConfig = (BaseConfig) JSONUtil.toBean(configEntity.getConfigValue(), targetClass); // 如果渠道扩展参数不为空则增加特性化参数 @@ -388,7 +389,7 @@ public class SysMessageServiceImpl extends ServiceImpl implements SysPostService { - /** - * 导入岗位 - * @param excelVOList 岗位列表 - * @param bindingResult 错误信息列表 - * @return ok fail - */ - @Override - public R importPost(List excelVOList, BindingResult bindingResult) { - // 通用校验获取失败的数据 - List errorMessageList = (List) bindingResult.getTarget(); + /** + * 导入岗位 + * + * @param excelVOList 岗位列表 + * @param bindingResult 错误信息列表 + * @return ok fail + */ + @Override + public R importPost(List excelVOList, BindingResult bindingResult) { + // 通用校验获取失败的数据 + List errorMessageList = (List) bindingResult.getTarget(); - // 个性化校验逻辑 - List postList = this.list(); + // 个性化校验逻辑 + List postList = this.list(); - // 执行数据插入操作 组装 PostDto - for (PostExcelVO excel : excelVOList) { - Set errorMsg = new HashSet<>(); - // 检验岗位名称或者岗位编码是否存在 - boolean existPost = postList.stream() - .anyMatch(post -> excel.getPostName().equals(post.getPostName()) - || excel.getPostCode().equals(post.getPostCode())); + // 执行数据插入操作 组装 PostDto + for (PostExcelVO excel : excelVOList) { + Set errorMsg = new HashSet<>(); + // 检验岗位名称或者岗位编码是否存在 + boolean existPost = postList.stream() + .anyMatch(post -> excel.getPostName().equals(post.getPostName()) + || excel.getPostCode().equals(post.getPostCode())); - if (existPost) { - errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_POST_NAMEORCODE_EXISTING, excel.getPostName(), - excel.getPostCode())); - } + if (existPost) { + errorMsg.add(MsgUtils.getMessage(UpmsErrorCodes.SYS_POST_NAMEORCODE_EXISTING, excel.getPostName(), + excel.getPostCode())); + } - // 数据合法情况 - if (CollUtil.isEmpty(errorMsg)) { - insertExcelPost(excel); - } - else { - // 数据不合法 - errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg)); - } - } - if (CollUtil.isNotEmpty(errorMessageList)) { - return R.failed(errorMessageList); - } - return R.ok(); - } + // 数据合法情况 + if (CollUtil.isEmpty(errorMsg)) { + insertExcelPost(excel); + } else { + // 数据不合法 + errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg)); + } + } + if (CollUtil.isNotEmpty(errorMessageList)) { + return R.failed(errorMessageList); + } + return R.ok(); + } - /** - * 导出excel 表格 - * @return - */ - @Override - public List listPost(SysPost query, Long[] ids) { - List postList = this - .list(Wrappers.lambdaQuery(query).in(ArrayUtil.isNotEmpty(ids), SysPost::getPostId, CollUtil.toList(ids))); - // 转换成execl 对象输出 - return postList.stream().map(post -> { - PostExcelVO postExcelVO = new PostExcelVO(); - BeanUtil.copyProperties(post, postExcelVO); - return postExcelVO; + /** + * 导出excel 表格 + * + * @return + */ + @Override + public List listPost(SysPost query, Long[] ids) { + List postList = this + .list(Wrappers.lambdaQuery(query).in(ArrayUtil.isNotEmpty(ids), SysPost::getPostId, CollUtil.toList(ids))); + // 转换成execl 对象输出 + return postList.stream().map(post -> { + PostExcelVO postExcelVO = new PostExcelVO(); + BeanUtil.copyProperties(post, postExcelVO); + return postExcelVO; }).toList(); - } + } - /** - * 插入excel Post - */ - private void insertExcelPost(PostExcelVO excel) { - SysPost sysPost = new SysPost(); - BeanUtil.copyProperties(excel, sysPost); - this.save(sysPost); - } + /** + * 插入excel Post + */ + private void insertExcelPost(PostExcelVO excel) { + SysPost sysPost = new SysPost(); + BeanUtil.copyProperties(excel, sysPost); + this.save(sysPost); + } } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysPublicParamServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysPublicParamServiceImpl.java index 2fb01860e..541498234 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysPublicParamServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysPublicParamServiceImpl.java @@ -20,12 +20,12 @@ package com.pig4cloud.pigx.admin.service.impl; import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pig4cloud.pigx.admin.api.constant.UpmsErrorCodes; import com.pig4cloud.pigx.admin.api.entity.SysPublicParam; import com.pig4cloud.pigx.admin.mapper.SysPublicParamMapper; import com.pig4cloud.pigx.admin.service.SysPublicParamService; import com.pig4cloud.pigx.common.core.constant.CacheConstants; import com.pig4cloud.pigx.common.core.constant.enums.DictTypeEnum; -import com.pig4cloud.pigx.common.core.exception.ErrorCodes; import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; import lombok.AllArgsConstructor; @@ -46,74 +46,78 @@ import java.util.Map; @Service @AllArgsConstructor public class SysPublicParamServiceImpl extends ServiceImpl - implements SysPublicParamService { + implements SysPublicParamService { - @Override - @Cacheable(value = CacheConstants.PARAMS_DETAILS, key = "#publicKey", unless = "#result == null ") - public String getSysPublicParamKeyToValue(String publicKey) { - SysPublicParam sysPublicParam = this.baseMapper - .selectOne(Wrappers.lambdaQuery().eq(SysPublicParam::getPublicKey, publicKey)); + @Override + @Cacheable(value = CacheConstants.PARAMS_DETAILS, key = "#publicKey", unless = "#result == null ") + public String getSysPublicParamKeyToValue(String publicKey) { + SysPublicParam sysPublicParam = this.baseMapper + .selectOne(Wrappers.lambdaQuery().eq(SysPublicParam::getPublicKey, publicKey)); - if (sysPublicParam != null) { - return sysPublicParam.getPublicValue(); - } - return null; - } + if (sysPublicParam != null) { + return sysPublicParam.getPublicValue(); + } + return null; + } - /** - * 通过key查询公共参数指定值 - * @param keys 参数列表 - * @return Map - */ - @Override - public Map getSysPublicParamsKeyToValue(String[] keys) { - List paramList = this.baseMapper - .selectList(Wrappers.lambdaQuery().in(SysPublicParam::getPublicKey, CollUtil.toList(keys))); - Map result = new HashMap<>(8); - paramList.forEach(param -> result.put(param.getPublicKey(), param.getPublicValue())); - return result; - } + /** + * 通过key查询公共参数指定值 + * + * @param keys 参数列表 + * @return Map + */ + @Override + public Map getSysPublicParamsKeyToValue(String[] keys) { + List paramList = this.baseMapper + .selectList(Wrappers.lambdaQuery().in(SysPublicParam::getPublicKey, CollUtil.toList(keys))); + Map result = new HashMap<>(8); + paramList.forEach(param -> result.put(param.getPublicKey(), param.getPublicValue())); + return result; + } - /** - * 更新参数 - * @param sysPublicParam - * @return - */ - @Override - @CacheEvict(value = CacheConstants.PARAMS_DETAILS, key = "#sysPublicParam.publicKey") - public R updateParam(SysPublicParam sysPublicParam) { - SysPublicParam param = this.getById(sysPublicParam.getPublicId()); - // 系统内置 - if (DictTypeEnum.SYSTEM.getType().equals(param.getSystemFlag())) { - return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_PARAM_DELETE_SYSTEM)); - } - return R.ok(this.updateById(sysPublicParam)); - } + /** + * 更新参数 + * + * @param sysPublicParam + * @return + */ + @Override + @CacheEvict(value = CacheConstants.PARAMS_DETAILS, key = "#sysPublicParam.publicKey") + public R updateParam(SysPublicParam sysPublicParam) { + SysPublicParam param = this.getById(sysPublicParam.getPublicId()); + // 系统内置 + if (DictTypeEnum.SYSTEM.getType().equals(param.getSystemFlag())) { + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_PARAM_DELETE_SYSTEM)); + } + return R.ok(this.updateById(sysPublicParam)); + } - /** - * 删除参数 - * @param publicIds 参数ID列表 - * @return - */ - @Override - @CacheEvict(value = CacheConstants.PARAMS_DETAILS, allEntries = true) - public R removeParamByIds(Long[] publicIds) { - List idList = this.baseMapper.selectByIds(CollUtil.toList(publicIds)) - .stream() - .filter(p -> !p.getSystemFlag().equals(DictTypeEnum.SYSTEM.getType()))// 系统内置的跳过不能删除 - .map(SysPublicParam::getPublicId) + /** + * 删除参数 + * + * @param publicIds 参数ID列表 + * @return + */ + @Override + @CacheEvict(value = CacheConstants.PARAMS_DETAILS, allEntries = true) + public R removeParamByIds(Long[] publicIds) { + List idList = this.baseMapper.selectByIds(CollUtil.toList(publicIds)) + .stream() + .filter(p -> !p.getSystemFlag().equals(DictTypeEnum.SYSTEM.getType()))// 系统内置的跳过不能删除 + .map(SysPublicParam::getPublicId) .toList(); - return R.ok(this.removeBatchByIds(idList)); - } + return R.ok(this.removeBatchByIds(idList)); + } - /** - * 同步缓存 - * @return R - */ - @Override - @CacheEvict(value = CacheConstants.PARAMS_DETAILS, allEntries = true) - public R syncParamCache() { - return R.ok(); - } + /** + * 同步缓存 + * + * @return R + */ + @Override + @CacheEvict(value = CacheConstants.PARAMS_DETAILS, allEntries = true) + public R syncParamCache() { + return R.ok(); + } } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysRoleServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysRoleServiceImpl.java index 0ea273cb3..090308601 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysRoleServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysRoleServiceImpl.java @@ -24,6 +24,7 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ArrayUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pig4cloud.pigx.admin.api.constant.UpmsErrorCodes; import com.pig4cloud.pigx.admin.api.entity.SysRole; import com.pig4cloud.pigx.admin.api.entity.SysRoleMenu; import com.pig4cloud.pigx.admin.api.vo.RoleExcelVO; @@ -32,7 +33,6 @@ import com.pig4cloud.pigx.admin.mapper.SysRoleMapper; import com.pig4cloud.pigx.admin.service.SysRoleMenuService; import com.pig4cloud.pigx.admin.service.SysRoleService; import com.pig4cloud.pigx.common.core.constant.CacheConstants; -import com.pig4cloud.pigx.common.core.exception.ErrorCodes; import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder; @@ -59,120 +59,125 @@ import java.util.Set; @AllArgsConstructor public class SysRoleServiceImpl extends ServiceImpl implements SysRoleService { - private SysRoleMenuService roleMenuService; + private SysRoleMenuService roleMenuService; - /** - * 通过用户ID查询角色信息 - * @param userId 用户ID - * @return 角色信息列表 - */ - @Override - public List findRolesByUserId(Long userId) { - return baseMapper.listRolesByUserId(userId, TenantContextHolder.getTenantId()); - } + /** + * 通过用户ID查询角色信息 + * + * @param userId 用户ID + * @return 角色信息列表 + */ + @Override + public List findRolesByUserId(Long userId) { + return baseMapper.listRolesByUserId(userId, TenantContextHolder.getTenantId()); + } - /** - * 根据角色ID 查询角色列表,注意缓存删除 - * @param roleIdList 角色ID列表 - * @param key 缓存key - * @return - */ - @Override - @Cacheable(value = CacheConstants.ROLE_DETAILS, key = "#key", unless = "#result.isEmpty()") - public List findRolesByRoleIds(List roleIdList, String key) { - return baseMapper.selectByIds(roleIdList); - } + /** + * 根据角色ID 查询角色列表,注意缓存删除 + * + * @param roleIdList 角色ID列表 + * @param key 缓存key + * @return + */ + @Override + @Cacheable(value = CacheConstants.ROLE_DETAILS, key = "#key", unless = "#result.isEmpty()") + public List findRolesByRoleIds(List roleIdList, String key) { + return baseMapper.selectByIds(roleIdList); + } - /** - * 通过角色ID,删除角色,并清空角色菜单缓存 - * @param ids - * @return - */ - @Override - @Transactional(rollbackFor = Exception.class) - public Boolean removeRoleByIds(Long[] ids) { - roleMenuService - .remove(Wrappers.update().lambda().in(SysRoleMenu::getRoleId, CollUtil.toList(ids))); - return this.removeBatchByIds(CollUtil.toList(ids)); - } + /** + * 通过角色ID,删除角色,并清空角色菜单缓存 + * + * @param ids + * @return + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean removeRoleByIds(Long[] ids) { + roleMenuService + .remove(Wrappers.update().lambda().in(SysRoleMenu::getRoleId, CollUtil.toList(ids))); + return this.removeBatchByIds(CollUtil.toList(ids)); + } - /** - * 根据角色菜单列表 - * @param roleVo 角色&菜单列表 - * @return - */ - @Override - public Boolean updateRoleMenus(RoleMenuVO roleVo) { - return roleMenuService.saveRoleMenus(roleVo.getRoleId(), roleVo.getMenuIds()); - } + /** + * 根据角色菜单列表 + * + * @param roleVo 角色&菜单列表 + * @return + */ + @Override + public Boolean updateRoleMenus(RoleMenuVO roleVo) { + return roleMenuService.saveRoleMenus(roleVo.getRoleId(), roleVo.getMenuIds()); + } - /** - * 导入角色 - * @param excelVOList 角色列表 - * @param bindingResult 错误信息列表 - * @return ok fail - */ - @Override - public R importRole(List excelVOList, BindingResult bindingResult) { - // 通用校验获取失败的数据 - List errorMessageList = (List) bindingResult.getTarget(); + /** + * 导入角色 + * + * @param excelVOList 角色列表 + * @param bindingResult 错误信息列表 + * @return ok fail + */ + @Override + public R importRole(List excelVOList, BindingResult bindingResult) { + // 通用校验获取失败的数据 + List errorMessageList = (List) bindingResult.getTarget(); - // 个性化校验逻辑 - List roleList = this.list(); + // 个性化校验逻辑 + List roleList = this.list(); - // 执行数据插入操作 组装 RoleDto - for (RoleExcelVO excel : excelVOList) { - Set errorMsg = new HashSet<>(); - // 检验角色名称或者角色编码是否存在 - boolean existRole = roleList.stream() - .anyMatch(sysRole -> excel.getRoleName().equals(sysRole.getRoleName()) - || excel.getRoleCode().equals(sysRole.getRoleCode())); + // 执行数据插入操作 组装 RoleDto + for (RoleExcelVO excel : excelVOList) { + Set errorMsg = new HashSet<>(); + // 检验角色名称或者角色编码是否存在 + boolean existRole = roleList.stream() + .anyMatch(sysRole -> excel.getRoleName().equals(sysRole.getRoleName()) + || excel.getRoleCode().equals(sysRole.getRoleCode())); - if (existRole) { - errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_ROLE_NAMEORCODE_EXISTING, excel.getRoleName(), - excel.getRoleCode())); - } + if (existRole) { + errorMsg.add(MsgUtils.getMessage(UpmsErrorCodes.SYS_ROLE_NAMEORCODE_EXISTING, excel.getRoleName(), + excel.getRoleCode())); + } - // 数据合法情况 - if (CollUtil.isEmpty(errorMsg)) { - insertExcelRole(excel); - } - else { - // 数据不合法情况 - errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg)); - } - } - if (CollUtil.isNotEmpty(errorMessageList)) { - return R.failed(errorMessageList); - } - return R.ok(); - } + // 数据合法情况 + if (CollUtil.isEmpty(errorMsg)) { + insertExcelRole(excel); + } else { + // 数据不合法情况 + errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg)); + } + } + if (CollUtil.isNotEmpty(errorMessageList)) { + return R.failed(errorMessageList); + } + return R.ok(); + } - /** - * 查询全部的角色 - * @return list - */ - @Override - public List listRole(SysRole sysRole, Long[] ids) { + /** + * 查询全部的角色 + * + * @return list + */ + @Override + public List listRole(SysRole sysRole, Long[] ids) { List roleList = this.list( Wrappers.lambdaQuery(sysRole).in(ArrayUtil.isNotEmpty(ids), SysRole::getRoleId, CollUtil.toList(ids))); - // 转换成execl 对象输出 - return roleList.stream().map(role -> { - RoleExcelVO roleExcelVO = new RoleExcelVO(); - BeanUtil.copyProperties(role, roleExcelVO); - return roleExcelVO; + // 转换成execl 对象输出 + return roleList.stream().map(role -> { + RoleExcelVO roleExcelVO = new RoleExcelVO(); + BeanUtil.copyProperties(role, roleExcelVO); + return roleExcelVO; }).toList(); - } + } - /** - * 插入excel Role - */ - private void insertExcelRole(RoleExcelVO excel) { - SysRole sysRole = new SysRole(); - sysRole.setRoleName(excel.getRoleName()); - sysRole.setRoleDesc(excel.getRoleDesc()); - sysRole.setRoleCode(excel.getRoleCode()); - this.save(sysRole); - } + /** + * 插入excel Role + */ + private void insertExcelRole(RoleExcelVO excel) { + SysRole sysRole = new SysRole(); + sysRole.setRoleName(excel.getRoleName()); + sysRole.setRoleDesc(excel.getRoleDesc()); + sysRole.setRoleCode(excel.getRoleCode()); + this.save(sysRole); + } } 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 bd36cb265..29c01d4e8 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 @@ -28,6 +28,7 @@ import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.pig4cloud.pigx.admin.api.constant.UpmsErrorCodes; import com.pig4cloud.pigx.admin.api.constant.UserStateEnum; import com.pig4cloud.pigx.admin.api.dto.RegisterUserDTO; import com.pig4cloud.pigx.admin.api.dto.UserDTO; @@ -41,7 +42,6 @@ import com.pig4cloud.pigx.common.audit.annotation.Audit; import com.pig4cloud.pigx.common.core.constant.CacheConstants; import com.pig4cloud.pigx.common.core.constant.CommonConstants; import com.pig4cloud.pigx.common.core.constant.enums.LoginTypeEnum; -import com.pig4cloud.pigx.common.core.exception.ErrorCodes; import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; import com.pig4cloud.pigx.common.data.cache.RedisUtils; @@ -184,7 +184,7 @@ public class SysUserServiceImpl extends ServiceImpl impl UserVO dbUser = baseMapper.getUserVo(userDTO); if (dbUser == null) { - return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_USER_USERINFO_EMPTY, userDTO.getUsername())); + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_USER_USERINFO_EMPTY, userDTO.getUsername())); } // 设置角色列表 (ID) @@ -278,7 +278,7 @@ public class SysUserServiceImpl extends ServiceImpl impl Object codeObj = RedisUtils.get(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.SMS.getType() + StringPool.AT + userDto.getPhone()); if (!StrUtil.equals(userDto.getCode(), codeObj.toString())) { - return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_APP_SMS_ERROR)); + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_APP_SMS_ERROR)); } } @@ -444,13 +444,13 @@ public class SysUserServiceImpl extends ServiceImpl impl .anyMatch(sysUser -> excel.getUsername().equals(sysUser.getUsername())); if (exsitUserName) { - errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_USER_USERNAME_EXISTING, excel.getUsername())); + errorMsg.add(MsgUtils.getMessage(UpmsErrorCodes.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())); + errorMsg.add(MsgUtils.getMessage(UpmsErrorCodes.SYS_USER_PHONE_EXISTING, excel.getPhone())); } // 判断输入的部门名称列表是否合法 @@ -458,7 +458,7 @@ public class SysUserServiceImpl extends ServiceImpl impl .filter(dept -> excel.getDeptNameList().equals(dept.getName())) .findFirst(); if (deptOptional.isEmpty()) { - errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_DEPT_DEPTNAME_INEXISTENCE, excel.getDeptNameList())); + errorMsg.add(MsgUtils.getMessage(UpmsErrorCodes.SYS_DEPT_DEPTNAME_INEXISTENCE, excel.getDeptNameList())); } // 判断输入的角色名称列表是否合法 @@ -468,7 +468,7 @@ public class SysUserServiceImpl extends ServiceImpl impl .toList(); if (roleCollList.size() != roleNameList.size()) { - errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_ROLE_ROLENAME_INEXISTENCE, excel.getRoleNameList())); + errorMsg.add(MsgUtils.getMessage(UpmsErrorCodes.SYS_ROLE_ROLENAME_INEXISTENCE, excel.getRoleNameList())); } // 判断输入的部门名称列表是否合法 @@ -478,7 +478,7 @@ public class SysUserServiceImpl extends ServiceImpl impl .toList(); if (postCollList.size() != postNameList.size()) { - errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_POST_POSTNAME_INEXISTENCE, excel.getPostNameList())); + errorMsg.add(MsgUtils.getMessage(UpmsErrorCodes.SYS_POST_POSTNAME_INEXISTENCE, excel.getPostNameList())); } // 数据合法情况 @@ -494,7 +494,7 @@ public class SysUserServiceImpl extends ServiceImpl impl if (CollUtil.isNotEmpty(errorMessageList)) { return R.failed(errorMessageList); } - return R.ok(null, MsgUtils.getMessage(ErrorCodes.SYS_USER_IMPORT_SUCCEED)); + return R.ok(null, MsgUtils.getMessage(UpmsErrorCodes.SYS_USER_IMPORT_SUCCEED)); } /** @@ -537,7 +537,7 @@ public class SysUserServiceImpl extends ServiceImpl impl 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()); + String message = MsgUtils.getMessage(UpmsErrorCodes.SYS_USER_USERNAME_EXISTING, userDto.getUsername()); return R.failed(message); } @@ -545,7 +545,7 @@ public class SysUserServiceImpl extends ServiceImpl impl 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()); + String message = MsgUtils.getMessage(UpmsErrorCodes.SYS_USER_PHONE_EXISTING, userDto.getPhone()); return R.failed(message); } @@ -585,16 +585,16 @@ public class SysUserServiceImpl extends ServiceImpl impl public R changePassword(UserDTO userDto) { SysUser user = baseMapper.selectById(SecurityUtils.getUser().getId()); if (StrUtil.isEmpty(userDto.getPassword())) { - return R.failed("原密码不能为空"); + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_USER_OLD_PASSWORD_EMPTY)); } if (!ENCODER.matches(userDto.getPassword(), user.getPassword())) { log.info("原密码错误,修改个人信息失败:{}", userDto.getUsername()); - return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_USER_UPDATE_PASSWORDERROR)); + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_USER_UPDATE_PASSWORDERROR)); } if (StrUtil.isEmpty(userDto.getNewpassword1())) { - return R.failed("新密码不能为空"); + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_USER_NEW_PASSWORD_EMPTY)); } String password = ENCODER.encode(userDto.getNewpassword1()); @@ -639,7 +639,7 @@ public class SysUserServiceImpl extends ServiceImpl impl } if (Objects.isNull(wrapper)) { - return R.failed("解绑账号类型不存在"); + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_USER_UNBIND_TYPE_NOT_FOUND)); } this.update(wrapper); return R.ok(); @@ -660,7 +660,7 @@ public class SysUserServiceImpl extends ServiceImpl impl if (!ENCODER.matches(password, sysUser.getPassword())) { log.info("原密码错误"); - return R.failed("密码输入错误"); + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_USER_PASSWORD_MISMATCH)); } else { return R.ok(); } @@ -683,7 +683,7 @@ public class SysUserServiceImpl extends ServiceImpl impl // 新密码校验 if (StrUtil.equals(userDto.getPassword(), userDto.getNewpassword1())) { - return R.failed("新旧密码不能相同"); + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_USER_PASSWORD_SAME)); } // 重置密码 @@ -706,13 +706,13 @@ public class SysUserServiceImpl extends ServiceImpl impl @Override public R forgetUserPassword(RegisterUserDTO userDto, String code) { if (StrUtil.isBlank(userDto.getPhone())) { - return R.failed("非法参数"); + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_PARAM_ILLEGAL)); } String codeObj = RedisUtils .get(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.SMS.getType() + StringPool.AT + userDto.getPhone()); if (!StrUtil.equals(codeObj, code)) { - return R.failed("验证码错误"); + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_APP_SMS_ERROR)); } TenantBroker.noneAs(() -> { @@ -812,7 +812,7 @@ public class SysUserServiceImpl extends ServiceImpl impl // 2. 清除用户缓存,触发权限重新计算 cacheManager.getCache(CacheConstants.USER_DETAILS).evict(username); - return R.ok(true, "部门切换成功"); + return R.ok(true, MsgUtils.getMessage(UpmsErrorCodes.SYS_DEPT_SWITCH_SUCCEED)); } } From aaab8956550b44f847857815b069295a6f4f205c Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Thu, 18 Dec 2025 17:02:53 +0800 Subject: [PATCH 33/44] =?UTF-8?q?fix(pom):=20=E6=9B=B4=E6=96=B0spring-clou?= =?UTF-8?q?d=E7=89=88=E6=9C=AC=E8=87=B32025.0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2b16b5037..29dd7fca0 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ 3.5.8 - 2025.0.0 + 2025.0.1 2025.0.0.0 UTF-8 17 From 77ebb4fe3712269137aa1f32a9eef9743bee59f1 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Fri, 19 Dec 2025 10:27:20 +0800 Subject: [PATCH 34/44] fix(pom): update spring-boot and dynamic-ds versions --- pigx-common/pigx-common-bom/pom.xml | 14 +++++++++++--- pom.xml | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/pigx-common/pigx-common-bom/pom.xml b/pigx-common/pigx-common-bom/pom.xml index e83b979b9..540c4995c 100644 --- a/pigx-common/pigx-common-bom/pom.xml +++ b/pigx-common/pigx-common-bom/pom.xml @@ -12,9 +12,9 @@ 5.11.0 - 3.5.14 - 1.5.4 - 4.3.1 + 3.5.15 + 1.5.5 + 4.5.0 3.1.0 1.2.23 5.8.42 @@ -26,6 +26,8 @@ 3.0.5 2.8.5 2.2.28 + + 2.5.2 4.7.8-20251028.120007 2.9.12.1-17 3.0.3 @@ -479,6 +481,12 @@ nacos-client ${nacos.client.version} + + + org.quartz-scheduler + quartz + ${quartz.version} + org.apache.logging.log4j diff --git a/pom.xml b/pom.xml index 29dd7fca0..dc6615e1c 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ - 3.5.8 + 3.5.9 2025.0.1 2025.0.0.0 UTF-8 From bfd64bbd478b043d4594a863a32b5405858dc540 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Fri, 19 Dec 2025 17:29:10 +0800 Subject: [PATCH 35/44] =?UTF-8?q?feat(pay):=20=E5=8D=87=E7=BA=A7yungouos-p?= =?UTF-8?q?ay-sdk=E7=89=88=E6=9C=AC=E5=B9=B6=E4=BC=98=E5=8C=96=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将yungouos-pay-sdk版本从2.0.37升级至2.0.39 - 排除hutool-all依赖以避免与系统已有依赖冲突 - 在YungouosMergePayOrderHandler中新增pay参数配置 --- pigx-visual/pigx-pay-platform/pom.xml | 9 ++++++++- .../pay/handler/impl/YungouosMergePayOrderHandler.java | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/pigx-visual/pigx-pay-platform/pom.xml b/pigx-visual/pigx-pay-platform/pom.xml index a7c0b0f22..2067dd067 100755 --- a/pigx-visual/pigx-pay-platform/pom.xml +++ b/pigx-visual/pigx-pay-platform/pom.xml @@ -14,7 +14,7 @@ 支付系统 - 2.0.37 + 2.0.39 @@ -113,6 +113,13 @@ com.yungouos.pay yungouos-pay-sdk ${yungouos.version} + + + + hutool-all + cn.hutool + + diff --git a/pigx-visual/pigx-pay-platform/src/main/java/com/pig4cloud/pigx/pay/handler/impl/YungouosMergePayOrderHandler.java b/pigx-visual/pigx-pay-platform/src/main/java/com/pig4cloud/pigx/pay/handler/impl/YungouosMergePayOrderHandler.java index b5f066a58..23d3cdc63 100644 --- a/pigx-visual/pigx-pay-platform/src/main/java/com/pig4cloud/pigx/pay/handler/impl/YungouosMergePayOrderHandler.java +++ b/pigx-visual/pigx-pay-platform/src/main/java/com/pig4cloud/pigx/pay/handler/impl/YungouosMergePayOrderHandler.java @@ -100,7 +100,7 @@ public class YungouosMergePayOrderHandler extends AbstractPayOrderHandler { return MergePay.nativePay(String.valueOf(tradeOrder.getOrderId()), money, channel.getChannelMchId(), tradeOrder.getBody(), "1", TenantContextHolder.getTenantId().toString(), String.format("%s/api/%s/notify/merge/callbak", ChannelPayApiConfigKit.get().getNotifyUrl(), WebUtils.isMicro() ? "pay" : "admin"), - ChannelPayApiConfigKit.get().getReturnUrl(), "", "", "", null, key); + ChannelPayApiConfigKit.get().getReturnUrl(), "", "", "0", "pay", null, key); } /** From 6743d8d7ab397f14503d824ff4c59cbea0d84eb4 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Sun, 28 Dec 2025 16:33:51 +0800 Subject: [PATCH 36/44] =?UTF-8?q?feat(version):=20=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E7=89=88=E6=9C=AC=E8=87=B35.11.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新所有模块的pom.xml中version字段 - 同步更新CLAUDE.md文档中的版本信息 --- CLAUDE.md | 6 +- pigx-app-server/pigx-app-server-api/pom.xml | 2 +- pigx-app-server/pigx-app-server-biz/pom.xml | 2 +- .../app/service/impl/AppRoleServiceImpl.java | 11 ---- pigx-app-server/pom.xml | 2 +- pigx-auth/pom.xml | 2 +- pigx-boot/pom.xml | 2 +- pigx-common/pigx-common-audit/pom.xml | 2 +- pigx-common/pigx-common-core/pom.xml | 2 +- pigx-common/pigx-common-data/pom.xml | 2 +- pigx-common/pigx-common-datasource/pom.xml | 2 +- pigx-common/pigx-common-encrypt-api/pom.xml | 2 +- pigx-common/pigx-common-excel/pom.xml | 2 +- pigx-common/pigx-common-feign/pom.xml | 2 +- pigx-common/pigx-common-gateway/pom.xml | 2 +- pigx-common/pigx-common-gray/pom.xml | 2 +- pigx-common/pigx-common-idempotent/pom.xml | 2 +- pigx-common/pigx-common-job/pom.xml | 2 +- pigx-common/pigx-common-log/pom.xml | 2 +- pigx-common/pigx-common-oss/pom.xml | 2 +- pigx-common/pigx-common-seata/pom.xml | 2 +- pigx-common/pigx-common-security/pom.xml | 2 +- pigx-common/pigx-common-sensitive/pom.xml | 2 +- pigx-common/pigx-common-sentinel/pom.xml | 2 +- pigx-common/pigx-common-sequence/pom.xml | 2 +- pigx-common/pigx-common-sse/pom.xml | 2 +- pigx-common/pigx-common-swagger/pom.xml | 2 +- pigx-common/pigx-common-websocket/pom.xml | 2 +- pigx-common/pigx-common-xss/pom.xml | 2 +- pigx-common/pom.xml | 2 +- pigx-flow/pigx-flow-api/pom.xml | 2 +- pigx-flow/pigx-flow-biz/pom.xml | 2 +- pigx-flow/pom.xml | 2 +- pigx-gateway/pom.xml | 2 +- pigx-register/pom.xml | 2 +- pigx-upms/pigx-upms-api/pom.xml | 2 +- pigx-upms/pigx-upms-biz/pom.xml | 2 +- .../service/impl/SysMenuServiceImpl.java | 57 ------------------- pigx-upms/pom.xml | 2 +- pigx-visual/pigx-codegen/pom.xml | 2 +- pigx-visual/pigx-daemon-elastic-job/pom.xml | 2 +- pigx-visual/pigx-daemon-quartz/pom.xml | 2 +- pigx-visual/pigx-jimu-platform/pom.xml | 2 +- pigx-visual/pigx-monitor/pom.xml | 2 +- pigx-visual/pigx-mp-platform/pom.xml | 2 +- pigx-visual/pigx-pay-platform/pom.xml | 2 +- pigx-visual/pigx-report-platform/pom.xml | 2 +- pigx-visual/pigx-xxl-job-admin/pom.xml | 2 +- pigx-visual/pom.xml | 2 +- pom.xml | 2 +- 50 files changed, 50 insertions(+), 118 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index c35ebd5bc..d8339b17c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,7 +5,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview PigX is a proprietary enterprise-grade microservices platform built on Spring Cloud Alibaba, focusing on AI knowledge -management, document processing, and intelligent Q&A capabilities. The current version is 5.10.0. +management, document processing, and intelligent Q&A capabilities. The current version is 5.11.0. **Important**: This is NOT an open-source project. All rights reserved by the author. Commercial use requires proper licensing. @@ -13,8 +13,8 @@ licensing. ## Technology Stack - **Java Version**: Java 17 -- **Spring Boot**: 3.5.6 -- **Spring Cloud**: 2025.0.0 +- **Spring Boot**: 3.5.9 +- **Spring Cloud**: 2025.0.1 - **Spring Cloud Alibaba**: 2023.0.3.3 - **AI/ML Libraries**: - LangChain4j: 1.6.0 diff --git a/pigx-app-server/pigx-app-server-api/pom.xml b/pigx-app-server/pigx-app-server-api/pom.xml index ab037f89b..547542226 100644 --- a/pigx-app-server/pigx-app-server-api/pom.xml +++ b/pigx-app-server/pigx-app-server-api/pom.xml @@ -7,7 +7,7 @@ com.pig4cloud pigx-app-server - 5.10.0 + 5.11.0 pigx-app-server-api diff --git a/pigx-app-server/pigx-app-server-biz/pom.xml b/pigx-app-server/pigx-app-server-biz/pom.xml index a41acf635..688e4856e 100644 --- a/pigx-app-server/pigx-app-server-biz/pom.xml +++ b/pigx-app-server/pigx-app-server-biz/pom.xml @@ -7,7 +7,7 @@ com.pig4cloud pigx-app-server - 5.10.0 + 5.11.0 pigx-app-server-biz diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppRoleServiceImpl.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppRoleServiceImpl.java index 8d890d0aa..f42186925 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppRoleServiceImpl.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppRoleServiceImpl.java @@ -113,17 +113,6 @@ public class AppRoleServiceImpl extends ServiceImpl impl } - /** - * 获取列表菜单对象 - * - * @return 列表菜单对象的实例 - */ - @Override - public AppPageEntity listMenu() { - return appPageMapper.selectOne(Wrappers.lambdaQuery() - .eq(AppPageEntity::getPageType, PageTypeEnums.WORKBENCH.getPageType())); - } - private void insertExcelRole(AppRoleExcelVO excel) { AppRole appRole = new AppRole(); appRole.setRoleName(excel.getRoleName()); diff --git a/pigx-app-server/pom.xml b/pigx-app-server/pom.xml index 0ccef98d8..82bc1be07 100644 --- a/pigx-app-server/pom.xml +++ b/pigx-app-server/pom.xml @@ -7,7 +7,7 @@ com.pig4cloud pigx - 5.10.0 + 5.11.0 pigx-app-server diff --git a/pigx-auth/pom.xml b/pigx-auth/pom.xml index 58252fbd3..de59ffac6 100644 --- a/pigx-auth/pom.xml +++ b/pigx-auth/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx - 5.10.0 + 5.11.0 pigx-auth diff --git a/pigx-boot/pom.xml b/pigx-boot/pom.xml index 4ab5c1ec5..6fda20da6 100755 --- a/pigx-boot/pom.xml +++ b/pigx-boot/pom.xml @@ -21,7 +21,7 @@ com.pig4cloud pigx - 5.10.0 + 5.11.0 pigx-boot diff --git a/pigx-common/pigx-common-audit/pom.xml b/pigx-common/pigx-common-audit/pom.xml index 8e9d0d524..f6f3e169b 100644 --- a/pigx-common/pigx-common-audit/pom.xml +++ b/pigx-common/pigx-common-audit/pom.xml @@ -5,7 +5,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 pigx-common-audit diff --git a/pigx-common/pigx-common-core/pom.xml b/pigx-common/pigx-common-core/pom.xml index cc8e456fb..94ac332c1 100644 --- a/pigx-common/pigx-common-core/pom.xml +++ b/pigx-common/pigx-common-core/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 pigx-common-core diff --git a/pigx-common/pigx-common-data/pom.xml b/pigx-common/pigx-common-data/pom.xml index 737e5de70..80f0b85f6 100644 --- a/pigx-common/pigx-common-data/pom.xml +++ b/pigx-common/pigx-common-data/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 pigx-common-data diff --git a/pigx-common/pigx-common-datasource/pom.xml b/pigx-common/pigx-common-datasource/pom.xml index 616bec454..ae5f80982 100644 --- a/pigx-common/pigx-common-datasource/pom.xml +++ b/pigx-common/pigx-common-datasource/pom.xml @@ -5,7 +5,7 @@ pigx-common com.pig4cloud - 5.10.0 + 5.11.0 4.0.0 diff --git a/pigx-common/pigx-common-encrypt-api/pom.xml b/pigx-common/pigx-common-encrypt-api/pom.xml index afc96155e..3b7ebf0e7 100644 --- a/pigx-common/pigx-common-encrypt-api/pom.xml +++ b/pigx-common/pigx-common-encrypt-api/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 pigx-common-encrypt-api diff --git a/pigx-common/pigx-common-excel/pom.xml b/pigx-common/pigx-common-excel/pom.xml index d490284af..e1b17ebb7 100644 --- a/pigx-common/pigx-common-excel/pom.xml +++ b/pigx-common/pigx-common-excel/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 pigx-common-excel diff --git a/pigx-common/pigx-common-feign/pom.xml b/pigx-common/pigx-common-feign/pom.xml index 789e466f0..e6b0bccb4 100755 --- a/pigx-common/pigx-common-feign/pom.xml +++ b/pigx-common/pigx-common-feign/pom.xml @@ -5,7 +5,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 4.0.0 diff --git a/pigx-common/pigx-common-gateway/pom.xml b/pigx-common/pigx-common-gateway/pom.xml index de28acde7..ed2305113 100644 --- a/pigx-common/pigx-common-gateway/pom.xml +++ b/pigx-common/pigx-common-gateway/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 pigx-common-gateway diff --git a/pigx-common/pigx-common-gray/pom.xml b/pigx-common/pigx-common-gray/pom.xml index cac84ba26..39d41da0f 100644 --- a/pigx-common/pigx-common-gray/pom.xml +++ b/pigx-common/pigx-common-gray/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 pigx-common-gray diff --git a/pigx-common/pigx-common-idempotent/pom.xml b/pigx-common/pigx-common-idempotent/pom.xml index 2766d148d..fe687729b 100644 --- a/pigx-common/pigx-common-idempotent/pom.xml +++ b/pigx-common/pigx-common-idempotent/pom.xml @@ -5,7 +5,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 pigx-common-idempotent diff --git a/pigx-common/pigx-common-job/pom.xml b/pigx-common/pigx-common-job/pom.xml index dbc5ed64d..83a38e1ca 100644 --- a/pigx-common/pigx-common-job/pom.xml +++ b/pigx-common/pigx-common-job/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 pigx-common-job diff --git a/pigx-common/pigx-common-log/pom.xml b/pigx-common/pigx-common-log/pom.xml index d4600b60b..fb735556d 100644 --- a/pigx-common/pigx-common-log/pom.xml +++ b/pigx-common/pigx-common-log/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 pigx-common-log diff --git a/pigx-common/pigx-common-oss/pom.xml b/pigx-common/pigx-common-oss/pom.xml index 1456f653f..0881863b9 100755 --- a/pigx-common/pigx-common-oss/pom.xml +++ b/pigx-common/pigx-common-oss/pom.xml @@ -6,7 +6,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 pigx-common-oss diff --git a/pigx-common/pigx-common-seata/pom.xml b/pigx-common/pigx-common-seata/pom.xml index f434900f0..306b5adf5 100644 --- a/pigx-common/pigx-common-seata/pom.xml +++ b/pigx-common/pigx-common-seata/pom.xml @@ -23,7 +23,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 pigx-common-seata diff --git a/pigx-common/pigx-common-security/pom.xml b/pigx-common/pigx-common-security/pom.xml index e5cb71106..b70d10845 100644 --- a/pigx-common/pigx-common-security/pom.xml +++ b/pigx-common/pigx-common-security/pom.xml @@ -23,7 +23,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 pigx-common-security diff --git a/pigx-common/pigx-common-sensitive/pom.xml b/pigx-common/pigx-common-sensitive/pom.xml index eaf6cdead..483d02361 100644 --- a/pigx-common/pigx-common-sensitive/pom.xml +++ b/pigx-common/pigx-common-sensitive/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 pigx-common-sensitive diff --git a/pigx-common/pigx-common-sentinel/pom.xml b/pigx-common/pigx-common-sentinel/pom.xml index 267b139c9..ccb7a4cbd 100755 --- a/pigx-common/pigx-common-sentinel/pom.xml +++ b/pigx-common/pigx-common-sentinel/pom.xml @@ -5,7 +5,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 4.0.0 diff --git a/pigx-common/pigx-common-sequence/pom.xml b/pigx-common/pigx-common-sequence/pom.xml index 076ecc905..c551ee732 100755 --- a/pigx-common/pigx-common-sequence/pom.xml +++ b/pigx-common/pigx-common-sequence/pom.xml @@ -6,7 +6,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 pigx-common-sequence diff --git a/pigx-common/pigx-common-sse/pom.xml b/pigx-common/pigx-common-sse/pom.xml index a1d205b82..f2dd490ca 100755 --- a/pigx-common/pigx-common-sse/pom.xml +++ b/pigx-common/pigx-common-sse/pom.xml @@ -6,7 +6,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 pigx-common-sse diff --git a/pigx-common/pigx-common-swagger/pom.xml b/pigx-common/pigx-common-swagger/pom.xml index 6629a6c8c..a00afc1b2 100644 --- a/pigx-common/pigx-common-swagger/pom.xml +++ b/pigx-common/pigx-common-swagger/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 pigx-common-swagger diff --git a/pigx-common/pigx-common-websocket/pom.xml b/pigx-common/pigx-common-websocket/pom.xml index 86549a8cb..eebcb5151 100755 --- a/pigx-common/pigx-common-websocket/pom.xml +++ b/pigx-common/pigx-common-websocket/pom.xml @@ -6,7 +6,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 pigx-common-websocket diff --git a/pigx-common/pigx-common-xss/pom.xml b/pigx-common/pigx-common-xss/pom.xml index 16cd3a283..bd5de6c8c 100755 --- a/pigx-common/pigx-common-xss/pom.xml +++ b/pigx-common/pigx-common-xss/pom.xml @@ -6,7 +6,7 @@ com.pig4cloud pigx-common - 5.10.0 + 5.11.0 pigx-common-xss diff --git a/pigx-common/pom.xml b/pigx-common/pom.xml index cce0e89e9..44b178c50 100644 --- a/pigx-common/pom.xml +++ b/pigx-common/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx - 5.10.0 + 5.11.0 pigx-common diff --git a/pigx-flow/pigx-flow-api/pom.xml b/pigx-flow/pigx-flow-api/pom.xml index c9d444cc1..0ddeb5fdf 100644 --- a/pigx-flow/pigx-flow-api/pom.xml +++ b/pigx-flow/pigx-flow-api/pom.xml @@ -7,7 +7,7 @@ com.pig4cloud pigx-flow - 5.10.0 + 5.11.0 pigx-flow-api diff --git a/pigx-flow/pigx-flow-biz/pom.xml b/pigx-flow/pigx-flow-biz/pom.xml index 80f975ce0..391972520 100644 --- a/pigx-flow/pigx-flow-biz/pom.xml +++ b/pigx-flow/pigx-flow-biz/pom.xml @@ -7,7 +7,7 @@ com.pig4cloud pigx-flow - 5.10.0 + 5.11.0 pigx-flow-biz diff --git a/pigx-flow/pom.xml b/pigx-flow/pom.xml index d04f2cd52..6fc001826 100644 --- a/pigx-flow/pom.xml +++ b/pigx-flow/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx - 5.10.0 + 5.11.0 pigx-flow diff --git a/pigx-gateway/pom.xml b/pigx-gateway/pom.xml index 94a227e57..2330547ed 100644 --- a/pigx-gateway/pom.xml +++ b/pigx-gateway/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx - 5.10.0 + 5.11.0 pigx-gateway diff --git a/pigx-register/pom.xml b/pigx-register/pom.xml index 3925ce774..548197db9 100644 --- a/pigx-register/pom.xml +++ b/pigx-register/pom.xml @@ -18,7 +18,7 @@ com.pig4cloud pigx - 5.10.0 + 5.11.0 pigx-register diff --git a/pigx-upms/pigx-upms-api/pom.xml b/pigx-upms/pigx-upms-api/pom.xml index 63d568981..f08cfc8a1 100644 --- a/pigx-upms/pigx-upms-api/pom.xml +++ b/pigx-upms/pigx-upms-api/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx-upms - 5.10.0 + 5.11.0 pigx-upms-api diff --git a/pigx-upms/pigx-upms-biz/pom.xml b/pigx-upms/pigx-upms-biz/pom.xml index dc390cb32..b8314bfef 100644 --- a/pigx-upms/pigx-upms-biz/pom.xml +++ b/pigx-upms/pigx-upms-biz/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx-upms - 5.10.0 + 5.11.0 pigx-upms-biz diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMenuServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMenuServiceImpl.java index a610b547a..176f25594 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMenuServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMenuServiceImpl.java @@ -186,7 +186,6 @@ public class SysMenuServiceImpl extends ServiceImpl impl extra.put("menuType", menu.getMenuType()); extra.put("permission", menu.getPermission()); extra.put("sortOrder", menu.getSortOrder()); - extra.put("isHomePage", menu.getIsHomePage()); // 适配 vue3 Map meta = new HashMap<>(); @@ -220,60 +219,4 @@ public class SysMenuServiceImpl extends ServiceImpl impl }; } - /** - * 设置首页菜单 - * - * @param menuId 菜单ID - * @return 是否成功 - */ - @Override - @Transactional(rollbackFor = Exception.class) - @CacheEvict(value = CacheConstants.MENU_DETAILS, allEntries = true) - public R setHomePage(Long menuId) { - // 查询菜单是否存在且为菜单类型(非按钮) - SysMenu menu = this.getById(menuId); - if (menu == null) { - return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_MENU_NOT_FOUND)); - } - if (MenuTypeEnum.BUTTON.getType().equals(menu.getMenuType())) { - return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_MENU_BUTTON_CANNOT_HOME)); - } - - // Toggle 模式:如果已是首页则取消,否则设置为首页 - if (YesNoEnum.YES.getCode().equals(menu.getIsHomePage())) { - // 已是首页,取消首页设置 - menu.setIsHomePage(YesNoEnum.NO.getCode()); - this.updateById(menu); - return R.ok(Boolean.TRUE, MsgUtils.getMessage(UpmsErrorCodes.SYS_MENU_HOME_CANCEL_SUCCEED)); - } else { - // 不是首页,先将所有菜单的首页标记重置为0,再设置当前菜单为首页 - this.update(Wrappers.lambdaUpdate().set(SysMenu::getIsHomePage, YesNoEnum.NO.getCode())); - menu.setIsHomePage(YesNoEnum.YES.getCode()); - this.updateById(menu); - return R.ok(Boolean.TRUE, MsgUtils.getMessage(UpmsErrorCodes.SYS_MENU_HOME_SET_SUCCEED)); - } - } - - /** - * 获取首页菜单 - * 根据当前用户的角色ID查询其有权限访问的首页菜单 - * - * @return 首页菜单 - */ - @Override - public SysMenu getHomePage() { - // 获取当前用户的角色ID列表 - List roleIds = SecurityUtils.getRoleIds(); - if (CollUtil.isEmpty(roleIds)) { - return null; - } - - // 获取用户所有角色对应的菜单(直接调用mapper避免self-invocation导致缓存失效) - Set userMenus = new HashSet<>(); - roleIds.forEach(roleId -> userMenus.addAll(baseMapper.listMenusByRoleId(roleId))); - - // 在用户有权限的菜单中查找首页菜单 - return userMenus.stream().filter(menu -> YesNoEnum.YES.getCode().equals(menu.getIsHomePage())).findFirst().orElse(null); - } - } diff --git a/pigx-upms/pom.xml b/pigx-upms/pom.xml index 9e90efd78..41b2ecd2b 100644 --- a/pigx-upms/pom.xml +++ b/pigx-upms/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx - 5.10.0 + 5.11.0 pigx-upms diff --git a/pigx-visual/pigx-codegen/pom.xml b/pigx-visual/pigx-codegen/pom.xml index 7a8c4a9dc..fd1ddddad 100755 --- a/pigx-visual/pigx-codegen/pom.xml +++ b/pigx-visual/pigx-codegen/pom.xml @@ -6,7 +6,7 @@ com.pig4cloud pigx-visual - 5.10.0 + 5.11.0 pigx-codegen diff --git a/pigx-visual/pigx-daemon-elastic-job/pom.xml b/pigx-visual/pigx-daemon-elastic-job/pom.xml index 9554d86f3..b117ccb0e 100644 --- a/pigx-visual/pigx-daemon-elastic-job/pom.xml +++ b/pigx-visual/pigx-daemon-elastic-job/pom.xml @@ -23,7 +23,7 @@ com.pig4cloud pigx-visual - 5.10.0 + 5.11.0 pigx-daemon-elastic-job diff --git a/pigx-visual/pigx-daemon-quartz/pom.xml b/pigx-visual/pigx-daemon-quartz/pom.xml index 95b57bb27..584a8cad2 100644 --- a/pigx-visual/pigx-daemon-quartz/pom.xml +++ b/pigx-visual/pigx-daemon-quartz/pom.xml @@ -5,7 +5,7 @@ com.pig4cloud pigx-visual - 5.10.0 + 5.11.0 4.0.0 diff --git a/pigx-visual/pigx-jimu-platform/pom.xml b/pigx-visual/pigx-jimu-platform/pom.xml index 506e48bb2..4caec67ff 100755 --- a/pigx-visual/pigx-jimu-platform/pom.xml +++ b/pigx-visual/pigx-jimu-platform/pom.xml @@ -5,7 +5,7 @@ com.pig4cloud pigx-visual - 5.10.0 + 5.11.0 pigx-jimu-platform diff --git a/pigx-visual/pigx-monitor/pom.xml b/pigx-visual/pigx-monitor/pom.xml index bdfd9173c..dbe4688ad 100644 --- a/pigx-visual/pigx-monitor/pom.xml +++ b/pigx-visual/pigx-monitor/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx-visual - 5.10.0 + 5.11.0 pigx-monitor diff --git a/pigx-visual/pigx-mp-platform/pom.xml b/pigx-visual/pigx-mp-platform/pom.xml index 6f386cd96..67f241cd5 100755 --- a/pigx-visual/pigx-mp-platform/pom.xml +++ b/pigx-visual/pigx-mp-platform/pom.xml @@ -6,7 +6,7 @@ com.pig4cloud pigx-visual - 5.10.0 + 5.11.0 pigx-mp-platform diff --git a/pigx-visual/pigx-pay-platform/pom.xml b/pigx-visual/pigx-pay-platform/pom.xml index 2067dd067..bdd34e146 100755 --- a/pigx-visual/pigx-pay-platform/pom.xml +++ b/pigx-visual/pigx-pay-platform/pom.xml @@ -6,7 +6,7 @@ com.pig4cloud pigx-visual - 5.10.0 + 5.11.0 pigx-pay-platform diff --git a/pigx-visual/pigx-report-platform/pom.xml b/pigx-visual/pigx-report-platform/pom.xml index 64cb5ee35..cd315ef9e 100644 --- a/pigx-visual/pigx-report-platform/pom.xml +++ b/pigx-visual/pigx-report-platform/pom.xml @@ -5,7 +5,7 @@ com.pig4cloud pigx-visual - 5.10.0 + 5.11.0 pigx-report-platform diff --git a/pigx-visual/pigx-xxl-job-admin/pom.xml b/pigx-visual/pigx-xxl-job-admin/pom.xml index 525ead7aa..b6d15f7c4 100644 --- a/pigx-visual/pigx-xxl-job-admin/pom.xml +++ b/pigx-visual/pigx-xxl-job-admin/pom.xml @@ -4,7 +4,7 @@ com.pig4cloud pigx-visual - 5.10.0 + 5.11.0 pigx-xxl-job-admin diff --git a/pigx-visual/pom.xml b/pigx-visual/pom.xml index 7c6de9915..5f4e21589 100644 --- a/pigx-visual/pom.xml +++ b/pigx-visual/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx - 5.10.0 + 5.11.0 pigx-visual diff --git a/pom.xml b/pom.xml index dc6615e1c..a733af733 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ com.pig4cloud pigx - 5.10.0 + 5.11.0 ${project.artifactId} pom From 5a324b75c9636450aacb838dad0cbdeb93ff08e6 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Sun, 28 Dec 2025 16:38:52 +0800 Subject: [PATCH 37/44] =?UTF-8?q?refactor(build):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=A4=9A=E4=B8=AA=E6=A8=A1=E5=9D=97=E4=B8=ADspring-boot-maven-?= =?UTF-8?q?plugin=E7=9A=84CLASSIC=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 统一清理pigx-flow/pigx-app-server等模块的冗余loaderImplementation配置 - 简化各模块pom.xml中的spring-boot-maven-plugin配置项 --- pigx-app-server/pigx-app-server-biz/pom.xml | 3 --- pigx-auth/pom.xml | 3 --- pigx-flow/pigx-flow-biz/pom.xml | 3 --- pigx-upms/pigx-upms-biz/pom.xml | 3 --- pigx-visual/pigx-codegen/pom.xml | 3 --- pigx-visual/pigx-daemon-quartz/pom.xml | 3 --- pigx-visual/pigx-mp-platform/pom.xml | 3 --- pigx-visual/pigx-pay-platform/pom.xml | 3 --- 8 files changed, 24 deletions(-) diff --git a/pigx-app-server/pigx-app-server-biz/pom.xml b/pigx-app-server/pigx-app-server-biz/pom.xml index 688e4856e..e68ea2d37 100644 --- a/pigx-app-server/pigx-app-server-biz/pom.xml +++ b/pigx-app-server/pigx-app-server-biz/pom.xml @@ -112,9 +112,6 @@ repackage - - CLASSIC - diff --git a/pigx-auth/pom.xml b/pigx-auth/pom.xml index de59ffac6..2437f7170 100644 --- a/pigx-auth/pom.xml +++ b/pigx-auth/pom.xml @@ -128,9 +128,6 @@ repackage - - CLASSIC - diff --git a/pigx-flow/pigx-flow-biz/pom.xml b/pigx-flow/pigx-flow-biz/pom.xml index 391972520..eec6efa3c 100644 --- a/pigx-flow/pigx-flow-biz/pom.xml +++ b/pigx-flow/pigx-flow-biz/pom.xml @@ -150,9 +150,6 @@ repackage - - CLASSIC - diff --git a/pigx-upms/pigx-upms-biz/pom.xml b/pigx-upms/pigx-upms-biz/pom.xml index b8314bfef..4980133ae 100644 --- a/pigx-upms/pigx-upms-biz/pom.xml +++ b/pigx-upms/pigx-upms-biz/pom.xml @@ -216,9 +216,6 @@ repackage - - CLASSIC - diff --git a/pigx-visual/pigx-codegen/pom.xml b/pigx-visual/pigx-codegen/pom.xml index fd1ddddad..8858343d0 100755 --- a/pigx-visual/pigx-codegen/pom.xml +++ b/pigx-visual/pigx-codegen/pom.xml @@ -199,9 +199,6 @@ repackage - - CLASSIC - diff --git a/pigx-visual/pigx-daemon-quartz/pom.xml b/pigx-visual/pigx-daemon-quartz/pom.xml index 584a8cad2..54533f7d1 100644 --- a/pigx-visual/pigx-daemon-quartz/pom.xml +++ b/pigx-visual/pigx-daemon-quartz/pom.xml @@ -123,9 +123,6 @@ repackage - - CLASSIC - diff --git a/pigx-visual/pigx-mp-platform/pom.xml b/pigx-visual/pigx-mp-platform/pom.xml index 67f241cd5..78a55365b 100755 --- a/pigx-visual/pigx-mp-platform/pom.xml +++ b/pigx-visual/pigx-mp-platform/pom.xml @@ -141,9 +141,6 @@ repackage - - CLASSIC - diff --git a/pigx-visual/pigx-pay-platform/pom.xml b/pigx-visual/pigx-pay-platform/pom.xml index bdd34e146..7b27b6f7f 100755 --- a/pigx-visual/pigx-pay-platform/pom.xml +++ b/pigx-visual/pigx-pay-platform/pom.xml @@ -153,9 +153,6 @@ repackage - - CLASSIC - From e959811fa2c9be8ea8604a2b6fafa4bc06f894de Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Mon, 29 Dec 2025 14:53:41 +0800 Subject: [PATCH 38/44] =?UTF-8?q?feat(codegen):=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=8A=A8=E6=80=81=E6=95=B0=E6=8D=AE=E6=BA=90=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在GenTableController的export方法中添加DynamicDataSourceContextHolder支持 - 实现根据table参数动态切换数据源功能 --- .../pig4cloud/pigx/codegen/controller/GenTableController.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pigx-visual/pigx-codegen/src/main/java/com/pig4cloud/pigx/codegen/controller/GenTableController.java b/pigx-visual/pigx-codegen/src/main/java/com/pig4cloud/pigx/codegen/controller/GenTableController.java index 3da6ac9b2..f0cf9f628 100644 --- a/pigx-visual/pigx-codegen/src/main/java/com/pig4cloud/pigx/codegen/controller/GenTableController.java +++ b/pigx-visual/pigx-codegen/src/main/java/com/pig4cloud/pigx/codegen/controller/GenTableController.java @@ -17,6 +17,7 @@ package com.pig4cloud.pigx.codegen.controller; +import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.pig4cloud.pigx.codegen.entity.GenTable; @@ -179,6 +180,8 @@ public class GenTableController { @ResponseExcel @GetMapping("/export") public List export(GenTable table) { + // 切换至对应数据源 + DynamicDataSourceContextHolder.push(table.getDsName()); return tableService.list(Wrappers.query(table)); } From 13d17923e47d650521ad6a641da3a419485673a5 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Tue, 30 Dec 2025 10:46:02 +0800 Subject: [PATCH 39/44] =?UTF-8?q?feat(upms):=20=E5=A2=9E=E5=BC=BA=E7=9F=AD?= =?UTF-8?q?=E4=BF=A1=E9=AA=8C=E8=AF=81=E7=A0=81=E6=B3=A8=E5=86=8C=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=92=8C=E5=AE=89=E5=85=A8=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在RegisterUserDTO中新增短信验证码字段 - 用户注册时增加短信验证码校验逻辑 - 合并短信验证码发送接口为单一API - 增加IP频率限制防止短信轰炸攻击 - 优化验证码校验失败的错误返回类型 --- .../pigx/admin/api/dto/RegisterUserDTO.java | 5 +++ .../controller/SysMessageController.java | 34 +++++++------------ .../service/impl/SysMessageServiceImpl.java | 21 ++++++++++-- .../service/impl/SysUserServiceImpl.java | 10 ++++++ 4 files changed, 46 insertions(+), 24 deletions(-) diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/RegisterUserDTO.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/RegisterUserDTO.java index fb2b73abe..9101f8a9a 100644 --- a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/RegisterUserDTO.java +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/RegisterUserDTO.java @@ -36,4 +36,9 @@ public class RegisterUserDTO { */ private Long tenantId; + /** + * 短信验证码 + */ + private String code; + } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysMessageController.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysMessageController.java index 978211887..79a2a0f67 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysMessageController.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysMessageController.java @@ -182,26 +182,16 @@ public class SysMessageController { } /** - * 发送短信(验证码) + * 发送短信验证码 * - * @param mobile 手机号 (需要注册,对外提供,安全性第一) - * @return {@link R } + * @param mobile 手机号 + * @param registered 是否已注册,默认true + * @return 发送结果 */ @Inner(value = false) @GetMapping("/send/smsCode") - public R sendSmsCode(@RequestParam String mobile) { - return sysMessageService.sendSmsCode(mobile, true); - } - - /** - * 发送内部短信验证码 - * - * @param mobile 手机号码 (系统内不一定存在) - * @return 发送结果 - */ - @GetMapping("/send/smsInnerCode") - public R sendSmsInnerCode(@RequestParam String mobile) { - return sysMessageService.sendSmsCode(mobile, false); + public R sendSmsCode(@RequestParam String mobile, @RequestParam(required = false, defaultValue = "true") boolean registered) { + return sysMessageService.sendSmsCode(mobile, registered); } /** @@ -234,12 +224,12 @@ public class SysMessageController { */ @PostMapping("/send/hook") public R sendWebhook(@Valid @RequestBody MessageHookDTO messageHookDTO) { - return sysMessageService.sendHook(messageHookDTO); - } + return sysMessageService.sendHook(messageHookDTO); + } - @GetMapping("/list/hook") - public R listHookBizCode(MessageHookDTO messageHookDTO) { - return sysMessageService.listHookBizCode(messageHookDTO); - } + @GetMapping("/list/hook") + public R listHookBizCode(MessageHookDTO messageHookDTO) { + return sysMessageService.listHookBizCode(messageHookDTO); + } } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMessageServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMessageServiceImpl.java index 76da753d9..a0dbb1660 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMessageServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysMessageServiceImpl.java @@ -42,7 +42,9 @@ import com.pig4cloud.pigx.common.core.constant.enums.YesNoEnum; import com.pig4cloud.pigx.common.core.exception.CheckedException; import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; +import com.pig4cloud.pigx.common.core.util.WebUtils; import com.pig4cloud.pigx.common.data.cache.RedisUtils; +import com.pig4cloud.pigx.common.data.resolver.ParamResolver; import com.pig4cloud.pigx.common.data.tenant.TenantBroker; import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder; import com.pig4cloud.pigx.common.file.core.FileTemplate; @@ -279,12 +281,27 @@ public class SysMessageServiceImpl extends ServiceImpl sendSmsCode(String mobile, boolean registered) { + // IP 限制校验,单位时间内同一IP发送次数限制 + String clientIp = WebUtils.getIP(); + String ipLimitKey = CacheConstants.DEFAULT_CODE_KEY + "IP_LIMIT:" + clientIp; + if (StrUtil.isNotBlank(clientIp)) { + Long maxTimes = ParamResolver.getLong("SMS_IP_LIMIT_TIMES", 5L); + Long ipSendTimes = RedisUtils.increment(ipLimitKey, 1L); + // 设置过期时间 + RedisUtils.expire(ipLimitKey, SecurityConstants.CODE_TIME); + + if (ipSendTimes > maxTimes) { + log.info("IP发送验证码过于频繁:{}, 已发送{}次", clientIp, ipSendTimes); + return R.failed(Boolean.FALSE, MsgUtils.getMessage(UpmsErrorCodes.SYS_APP_SMS_OFTEN)); + } + } + List userList = TenantBroker .noneAs(() -> userMapper.selectList(Wrappers.query().lambda().eq(SysUser::getPhone, mobile))); if (registered && CollUtil.isEmpty(userList)) { log.info("手机号未注册:{}", mobile); - return R.ok(Boolean.FALSE, MsgUtils.getMessage(UpmsErrorCodes.SYS_APP_PHONE_UNREGISTERED, mobile)); + return R.failed(Boolean.FALSE, MsgUtils.getMessage(UpmsErrorCodes.SYS_APP_PHONE_UNREGISTERED, mobile)); } String codeObj = RedisUtils @@ -292,7 +309,7 @@ public class SysMessageServiceImpl extends ServiceImpl impl @Override @Transactional(rollbackFor = Exception.class) public R registerUser(RegisterUserDTO userDto) { + // 校验短信验证码 + if (StrUtil.isBlank(userDto.getCode())) { + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_PARAM_ILLEGAL)); + } + String codeObj = RedisUtils + .get(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.SMS.getType() + StringPool.AT + userDto.getPhone()); + if (!StrUtil.equals(codeObj, userDto.getCode())) { + return R.failed(MsgUtils.getMessage(UpmsErrorCodes.SYS_APP_SMS_ERROR)); + } + // 判断用户名是否存在 boolean usernameExists = TenantBroker .noneAs(() -> this.exists(Wrappers.lambdaQuery().eq(SysUser::getUsername, userDto.getUsername()))); From 85a1d609e354728670fb354ca8e8bf1e72573df6 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Tue, 30 Dec 2025 14:49:47 +0800 Subject: [PATCH 40/44] =?UTF-8?q?feat(tenant):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=A7=9F=E6=88=B7=E4=B8=8A=E4=B8=8B=E6=96=87=E8=BF=87=E6=BB=A4?= =?UTF-8?q?=E5=99=A8=E7=9A=84=E6=8F=8F=E8=BF=B0=E5=92=8C=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E9=A1=BA=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pigx/common/data/tenant/TenantContextHolderFilter.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantContextHolderFilter.java b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantContextHolderFilter.java index 45bbcee16..aa125f28e 100644 --- a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantContextHolderFilter.java +++ b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantContextHolderFilter.java @@ -48,13 +48,15 @@ import java.util.List; import java.util.Objects; /** + * 租户上下文过滤器:处理租户ID的提取、验证和上下文设置 + * * @author lengleng - * @date 2018/9/13 + * @date 2025/12/30 */ @Slf4j +@Order(-104) // OrderedRequestContextFilter 之后执行 @Component @RequiredArgsConstructor -@Order(Ordered.HIGHEST_PRECEDENCE) public class TenantContextHolderFilter extends GenericFilterBean { private final static String UNDEFINED_STR = "undefined"; From 36620c05ad513d24c38be41905c913ea8b88de09 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Tue, 30 Dec 2025 19:16:57 +0800 Subject: [PATCH 41/44] =?UTF-8?q?feat(upms):=20=E7=A7=BB=E9=99=A4DingTalk?= =?UTF-8?q?=20SDK=E4=B8=AD=E5=86=B2=E7=AA=81=E7=9A=84mail=E4=BE=9D?= =?UTF-8?q?=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在pom.xml中为alibaba-dingtalk-service-sdk添加exclusions配置 - 排除javax.mail依赖以避免与其他模块的版本冲突 --- pigx-upms/pigx-upms-biz/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pigx-upms/pigx-upms-biz/pom.xml b/pigx-upms/pigx-upms-biz/pom.xml index 4980133ae..ea8bf351a 100644 --- a/pigx-upms/pigx-upms-biz/pom.xml +++ b/pigx-upms/pigx-upms-biz/pom.xml @@ -148,6 +148,12 @@ com.aliyun alibaba-dingtalk-service-sdk ${dingtalk.old.version} + + + mail + javax.mail + + From 7bdf9e1d8cb24dc0430d44ca5f32b0fbab10e7af Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Tue, 30 Dec 2025 21:38:53 +0800 Subject: [PATCH 42/44] =?UTF-8?q?feat(upms):=20=E5=A2=9E=E5=BC=BA=E7=A7=9F?= =?UTF-8?q?=E6=88=B7=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=E5=B9=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E7=94=A8=E6=88=B7=E7=A7=9F=E6=88=B7=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在UserInfo DTO中新增tenantName字段用于返回租户名称 - 重构SysTenantService.getOrUpdateTenant方法返回完整租户对象 - 更新用户登录逻辑以包含租户名称信息 - 修改默认租户名称为PIGX ADMIN - 优化租户切换逻辑确保缓存一致性 --- db/0pigxx_boot.sql | 2 +- db/2pigxx.sql | 2 +- .../pigx/admin/api/dto/UserInfo.java | 32 +- .../pigx/admin/service/SysTenantService.java | 7 +- .../service/impl/SysTenantServiceImpl.java | 897 +++++++++--------- .../service/impl/SysUserServiceImpl.java | 7 +- 6 files changed, 494 insertions(+), 453 deletions(-) diff --git a/db/0pigxx_boot.sql b/db/0pigxx_boot.sql index c26bde9ce..4ddc85000 100644 --- a/db/0pigxx_boot.sql +++ b/db/0pigxx_boot.sql @@ -1311,7 +1311,7 @@ CREATE TABLE `sys_tenant` ( -- Records of sys_tenant -- ---------------------------- BEGIN; -INSERT INTO `sys_tenant` VALUES (1, '北京分公司', '1', '', NULL, NULL, NULL, NULL, NULL, '2019-05-15 00:00:00', '2099-12-31 00:00:00', '0', '0', '', 'admin', '2019-05-15 15:44:57', '2023-07-30 14:52:57', 1642752536722997250); +INSERT INTO `sys_tenant` VALUES (1, 'PIGX ADMIN', '1', '', NULL, NULL, NULL, NULL, NULL, '2019-05-15 00:00:00', '2099-12-31 00:00:00', '0', '0', '', 'admin', '2019-05-15 15:44:57', '2023-07-30 14:52:57', 1642752536722997250); COMMIT; diff --git a/db/2pigxx.sql b/db/2pigxx.sql index f369f28b8..b7e8084f9 100644 --- a/db/2pigxx.sql +++ b/db/2pigxx.sql @@ -1295,7 +1295,7 @@ CREATE TABLE `sys_tenant` ( -- Records of sys_tenant -- ---------------------------- BEGIN; -INSERT INTO `sys_tenant` VALUES (1, '北京分公司', '1', '', NULL, NULL, NULL, NULL, NULL, '2019-05-15 00:00:00', '2099-12-31 00:00:00', '0', '0', '', 'admin', '2019-05-15 15:44:57', '2023-07-30 14:52:57', 1642752536722997250); +INSERT INTO `sys_tenant` VALUES (1, 'PIGX ADMIN', '1', '', NULL, NULL, NULL, NULL, NULL, '2019-05-15 00:00:00', '2099-12-31 00:00:00', '0', '0', '', 'admin', '2019-05-15 15:44:57', '2023-07-30 14:52:57', 1642752536722997250); COMMIT; diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/UserInfo.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/UserInfo.java index d329cacdb..759200713 100644 --- a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/UserInfo.java +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/UserInfo.java @@ -40,22 +40,24 @@ import java.util.List; @EqualsAndHashCode(callSuper = true) public class UserInfo extends UserVO implements Serializable { - /** - * 密码 - */ - @JsonIgnore(value = false) - private String password; + /** + * 密码 + */ + @JsonIgnore(value = false) + private String password; - /** - * 随机盐 - */ - @JsonIgnore(value = false) - private String salt; + /** + * 随机盐 + */ + @JsonIgnore(value = false) + private String salt; - /** - * 权限标识集合 - */ - @Schema(description = "权限标识集合") - private List permissions = new ArrayList<>(); + /** + * 权限标识集合 + */ + @Schema(description = "权限标识集合") + private List permissions = new ArrayList<>(); + @Schema(description = "租户名称") + private String tenantName; } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysTenantService.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysTenantService.java index 4d0c0fea4..ed0c0e045 100755 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysTenantService.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysTenantService.java @@ -114,8 +114,11 @@ public interface SysTenantService extends IService { R updateUserTenant(UserDTO userDto); /** - * 检查或更新租户信息 + * 获取或更新当前用户的租户信息 + *

+ * 如果当前租户已过期或无效,则自动切换到用户的其他有效租户 + * @return 当前有效的租户对象,如果无有效租户则返回null */ - Long getOrUpdateTenant(); + SysTenant getOrUpdateTenant(); } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysTenantServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysTenantServiceImpl.java index 4c64aaae1..a18d4aa93 100755 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysTenantServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysTenantServiceImpl.java @@ -67,475 +67,510 @@ import java.util.*; @AllArgsConstructor public class SysTenantServiceImpl extends ServiceImpl implements SysTenantService { - private final SysOauthClientDetailsService clientServices; + private final SysOauthClientDetailsService clientServices; - private final SysRoleMenuMapper roleMenuMapper; + private final SysRoleMenuMapper roleMenuMapper; - private final SysDictItemService dictItemService; + private final SysDictItemService dictItemService; - private final SysPublicParamService paramService; + private final SysPublicParamService paramService; - private final SysRoleService roleService; + private final SysRoleService roleService; - private final SysMenuService menuService; + private final SysMenuService menuService; - private final SysDeptService deptService; + private final SysDeptService deptService; - private final SysDictService dictService; + private final SysDictService dictService; - private final SysUserMapper sysUserMapper; + private final SysUserMapper sysUserMapper; - private final SysTenantUserMapper sysTenantUserMapper; + private final SysTenantUserMapper sysTenantUserMapper; - private final SysUserPostMapper sysUserPostMapper; + private final SysUserPostMapper sysUserPostMapper; - private final SysUserRoleMapper sysUserRoleMapper; + private final SysUserRoleMapper sysUserRoleMapper; - private final SysUserDeptMapper sysUserDeptMapper; + private final SysUserDeptMapper sysUserDeptMapper; - private final SysRoleMapper sysRoleMapper; + private final SysRoleMapper sysRoleMapper; - private final SysDeptMapper sysDeptMapper; + private final SysDeptMapper sysDeptMapper; - private final SysPostMapper sysPostMapper; + private final SysPostMapper sysPostMapper; - private final CacheManager cacheManager; + private final CacheManager cacheManager; - /** - * 获取正常状态租户 - *

- * 1. 状态正常 2. 开始时间小于等于当前时间 3. 结束时间大于等于当前时间 - * @return - */ - @Override - @Cacheable(value = CacheConstants.TENANT_DETAILS) - public List getNormalTenant() { - return baseMapper - .selectList(Wrappers.lambdaQuery().eq(SysTenant::getStatus, TenantStateEnum.NORMAL.getCode())); - } + /** + * 获取正常状态租户 + *

+ * 1. 状态正常 2. 开始时间小于等于当前时间 3. 结束时间大于等于当前时间 + * + * @return + */ + @Override + @Cacheable(value = CacheConstants.TENANT_DETAILS) + public List getNormalTenant() { + return baseMapper + .selectList(Wrappers.lambdaQuery().eq(SysTenant::getStatus, TenantStateEnum.NORMAL.getCode())); + } - /** - * 保存租户 - *

- * 1. 保存租户 2. 初始化权限相关表 - sys_user - sys_role - sys_menu - sys_user_role - - * sys_role_menu - sys_dict - sys_dict_item - sys_client_details - sys_public_params - * @param sysTenant 租户实体 - * @return - */ - @Override - @Transactional(rollbackFor = Exception.class) - @CacheEvict(value = CacheConstants.TENANT_DETAILS) - public Boolean saveTenant(SysTenant sysTenant) { - this.save(sysTenant); - // 查询系统默认租户配置参数 - TenantDefaultConfig tenantDefault = new TenantDefaultConfig(); + /** + * 保存租户 + *

+ * 1. 保存租户 2. 初始化权限相关表 - sys_user - sys_role - sys_menu - sys_user_role - + * sys_role_menu - sys_dict - sys_dict_item - sys_client_details - sys_public_params + * + * @param sysTenant 租户实体 + * @return + */ + @Override + @Transactional(rollbackFor = Exception.class) + @CacheEvict(value = CacheConstants.TENANT_DETAILS) + public Boolean saveTenant(SysTenant sysTenant) { + this.save(sysTenant); + // 查询系统默认租户配置参数 + TenantDefaultConfig tenantDefault = new TenantDefaultConfig(); - List dictList = new ArrayList<>(32); - List dictIdList = new ArrayList<>(32); - List dictItemList = new ArrayList<>(64); - List menuList = new ArrayList<>(128); - List clientDetailsList = new ArrayList<>(16); - List publicParamList = new ArrayList<>(64); + List dictList = new ArrayList<>(32); + List dictIdList = new ArrayList<>(32); + List dictItemList = new ArrayList<>(64); + List menuList = new ArrayList<>(128); + List clientDetailsList = new ArrayList<>(16); + List publicParamList = new ArrayList<>(64); - TenantBroker.runAs(tenantDefault.getTenantDefaultId(), (id) -> { - // 查询系统内置字典 - dictList.addAll(dictService.list()); - // 查询系统内置字典项目 - dictIdList.addAll(dictList.stream().map(SysDict::getId).toList()); - dictItemList.addAll( - dictItemService.list(Wrappers.lambdaQuery().in(SysDictItem::getDictId, dictIdList))); - List newMenuList = menuService.list(Wrappers.lambdaQuery() - .in(SysMenu::getMenuId, StrUtil.split(sysTenant.getMenuId(), CharUtil.COMMA))); - // 查询当前租户菜单 - menuList.addAll(newMenuList); - // 查询客户端配置 - clientDetailsList.addAll(clientServices.list()); - // 查询系统参数配置 - publicParamList.addAll(paramService.list()); - }); + TenantBroker.runAs(tenantDefault.getTenantDefaultId(), (id) -> { + // 查询系统内置字典 + dictList.addAll(dictService.list()); + // 查询系统内置字典项目 + dictIdList.addAll(dictList.stream().map(SysDict::getId).toList()); + dictItemList.addAll( + dictItemService.list(Wrappers.lambdaQuery().in(SysDictItem::getDictId, dictIdList))); + List newMenuList = menuService.list(Wrappers.lambdaQuery() + .in(SysMenu::getMenuId, + StrUtil.split(sysTenant.getMenuId(), CharUtil.COMMA).stream().map(Long::parseLong).toList())); + // 查询当前租户菜单 + menuList.addAll(newMenuList); + // 查询客户端配置 + clientDetailsList.addAll(clientServices.list()); + // 查询系统参数配置 + publicParamList.addAll(paramService.list()); + }); - // 保证插入租户为新的租户 - TenantBroker.applyAs(sysTenant.getId(), (id -> { - // 插入部门 - SysDept dept = new SysDept(); - dept.setName(tenantDefault.getTenantDefaultDeptname()); - dept.setParentId(0L); - deptService.save(dept); + // 保证插入租户为新的租户 + TenantBroker.applyAs(sysTenant.getId(), (id -> { + // 插入部门 + SysDept dept = new SysDept(); + dept.setName(tenantDefault.getTenantDefaultDeptname()); + dept.setParentId(0L); + dept.setSortOrder(0); + deptService.save(dept); - // 构造普通用户角色 - SysRole roleDefault = new SysRole(); - roleDefault.setRoleCode(tenantDefault.getUserDefaultRolecode()); - roleDefault.setRoleName(tenantDefault.getUserDefaultRolename()); - roleDefault.setDsType(DataScopeTypeEnum.SELF_LEVEL.getType()); - roleService.save(roleDefault); + // 构造普通用户角色 + SysRole roleDefault = new SysRole(); + roleDefault.setRoleCode(tenantDefault.getUserDefaultRolecode()); + roleDefault.setRoleName(tenantDefault.getUserDefaultRolename()); + roleDefault.setDsType(DataScopeTypeEnum.SELF_LEVEL.getType()); + roleService.save(roleDefault); - // 构造新角色 管理员角色 - SysRole role = new SysRole(); - role.setRoleCode(tenantDefault.getTenantDefaultRolecode()); - role.setRoleName(tenantDefault.getTenantDefaultRolename()); - role.setDsType(DataScopeTypeEnum.ALL.getType()); - roleService.save(role); - // 插入新的菜单 - saveTenantMenu(menuList, CommonConstants.MENU_TREE_ROOT_ID, CommonConstants.MENU_TREE_ROOT_ID); + // 构造新角色 管理员角色 + SysRole role = new SysRole(); + role.setRoleCode(tenantDefault.getTenantDefaultRolecode()); + role.setRoleName(tenantDefault.getTenantDefaultRolename()); + role.setDsType(DataScopeTypeEnum.ALL.getType()); + roleService.save(role); + // 插入新的菜单 + saveTenantMenu(menuList, CommonConstants.MENU_TREE_ROOT_ID, CommonConstants.MENU_TREE_ROOT_ID); - // 重新查询出所有的菜单关联角色 - List list = menuService.list(); + // 重新查询出所有的菜单关联角色 + List list = menuService.list(); - // 查询全部菜单,构造角色菜单关系 - List roleMenuList = list.stream().map(menu -> { - SysRoleMenu roleMenu = new SysRoleMenu(); - roleMenu.setRoleId(role.getRoleId()); - roleMenu.setMenuId(menu.getMenuId()); - return roleMenu; - }).toList(); - roleMenuList.forEach(roleMenuMapper::insert); - // 插入系统字典 - dictService.saveBatch(dictList.stream().peek(d -> d.setId(null)).toList()); - // 处理字典项最新关联的字典ID - List itemList = dictList.stream() - .flatMap(dict -> dictItemList.stream() - .filter(item -> item.getDictType().equals(dict.getDictType())) - .peek(item -> { - item.setDictId(dict.getId()); - item.setId(null); - })) - .toList(); + // 查询全部菜单,构造角色菜单关系 + List roleMenuList = list.stream().map(menu -> { + SysRoleMenu roleMenu = new SysRoleMenu(); + roleMenu.setRoleId(role.getRoleId()); + roleMenu.setMenuId(menu.getMenuId()); + return roleMenu; + }).toList(); + roleMenuList.forEach(roleMenuMapper::insert); + // 插入系统字典 + dictService.saveBatch(dictList.stream().peek(d -> d.setId(null)).toList()); + // 处理字典项最新关联的字典ID + List itemList = dictList.stream() + .flatMap(dict -> dictItemList.stream() + .filter(item -> item.getDictType().equals(dict.getDictType())) + .peek(item -> { + item.setDictId(dict.getId()); + item.setId(null); + })) + .toList(); - // 插入客户端 - clientServices.saveBatch(clientDetailsList.stream().peek(d -> d.setId(null)).toList()); - // 插入系统配置 - paramService.saveBatch(publicParamList.stream().peek(d -> d.setPublicId(null)).toList()); - return dictItemService.saveBatch(itemList); - })); + // 插入客户端 + clientServices.saveBatch(clientDetailsList.stream().peek(d -> d.setId(null)).toList()); + // 插入系统配置 + paramService.saveBatch(publicParamList.stream().peek(d -> d.setPublicId(null)).toList()); + return dictItemService.saveBatch(itemList); + })); - SpringContextHolder.publishEvent(new ClientDetailsInitRunner.ClientDetailsInitEvent(sysTenant)); + SpringContextHolder.publishEvent(new ClientDetailsInitRunner.ClientDetailsInitEvent(sysTenant)); - return Boolean.TRUE; - } + return Boolean.TRUE; + } - /** - * 修改租户 - * @param tenantDTO 新旧套餐数据 - * @return true/false - */ - @Override - @Transactional(rollbackFor = Exception.class) - @CacheEvict(value = CacheConstants.TENANT_DETAILS, allEntries = true) - public Boolean updateTenant(SysTenant tenantDTO) { - SysTenant oldTenant = baseMapper.selectById(tenantDTO.getId()); - List newMenuIdList = StrUtil.split(tenantDTO.getMenuId(), CharUtil.COMMA); - List oldMenuIdList = StrUtil.split(oldTenant.getMenuId(), CharUtil.COMMA); + /** + * 修改租户 + * + * @param tenantDTO 新旧套餐数据 + * @return true/false + */ + @Override + @Transactional(rollbackFor = Exception.class) + @CacheEvict(value = CacheConstants.TENANT_DETAILS, allEntries = true) + public Boolean updateTenant(SysTenant tenantDTO) { + SysTenant oldTenant = baseMapper.selectById(tenantDTO.getId()); + List newMenuIdList = StrUtil.split(tenantDTO.getMenuId(), CharUtil.COMMA); + List oldMenuIdList = StrUtil.split(oldTenant.getMenuId(), CharUtil.COMMA); - // 更新租户信息 - updateById(tenantDTO); + // 更新租户信息 + updateById(tenantDTO); - // 如果没有修改租户套餐 - TenantDefaultConfig tenantDefault = new TenantDefaultConfig(); - List sysMenuList = TenantBroker.applyAs(tenantDefault.getTenantDefaultId(), id -> menuService.list()); - if (tenantDefault.getTenantDefaultId().equals(tenantDTO.getId())) { - return Boolean.TRUE; - } + // 如果没有修改租户套餐 + TenantDefaultConfig tenantDefault = new TenantDefaultConfig(); + List sysMenuList = TenantBroker.applyAs(tenantDefault.getTenantDefaultId(), id -> menuService.list()); + if (tenantDefault.getTenantDefaultId().equals(tenantDTO.getId())) { + return Boolean.TRUE; + } - // 比较新旧套餐是否一致 - if (CollUtil.isEqualList(newMenuIdList, oldMenuIdList)) { - return Boolean.TRUE; - } + // 比较新旧套餐是否一致 + if (CollUtil.isEqualList(newMenuIdList, oldMenuIdList)) { + return Boolean.TRUE; + } - // 0. 计算两个差集,差集表示变化的功能项 - Collection disjunctionList = CollUtil.disjunction(newMenuIdList, oldMenuIdList); - // 1. 如果旧套餐包含差集元素则删除 - CollUtil.intersection(oldMenuIdList, disjunctionList) - .forEach(menuId -> TenantBroker.runAs(tenantDTO.getId(), (tenantId -> { - // 查询租户菜单元信息 - SysMenu menu = CollUtil.findOneByField(sysMenuList, SysMenu.Fields.menuId, Long.parseLong(menuId)); + // 0. 计算两个差集,差集表示变化的功能项 + Collection disjunctionList = CollUtil.disjunction(newMenuIdList, oldMenuIdList); + // 1. 如果旧套餐包含差集元素则删除 + CollUtil.intersection(oldMenuIdList, disjunctionList) + .forEach(menuId -> TenantBroker.runAs(tenantDTO.getId(), (tenantId -> { + // 查询租户菜单元信息 + SysMenu menu = CollUtil.findOneByField(sysMenuList, SysMenu.Fields.menuId, Long.parseLong(menuId)); - // 根据path 或者 permisson 删除目标租户菜单 - menuService.remove(Wrappers.lambdaQuery() - .eq(StrUtil.isNotBlank(menu.getPath()), SysMenu::getPath, menu.getPath()) - .or() - .eq(StrUtil.isNotBlank(menu.getPermission()), SysMenu::getPermission, menu.getPermission())); - }))); + if (Objects.isNull(menu)) { + return; + } - // 2. 如果旧套餐不包含差集元素则新增 - List newTenantMenuIdList = CollUtil.subtract(disjunctionList, oldMenuIdList) - .stream() - .map(menuId -> TenantBroker.applyAs(tenantDTO.getId(), (tenantId -> { - // 查询租户菜单元信息 - SysMenu menu = CollUtil.findOneByField(sysMenuList, SysMenu.Fields.menuId, Long.parseLong(menuId)); + // 根据path 或者 permisson 删除目标租户菜单 + menuService.remove(Wrappers.lambdaQuery() + .eq(StrUtil.isNotBlank(menu.getPath()), SysMenu::getPath, menu.getPath()) + .or() + .eq(StrUtil.isNotBlank(menu.getPermission()), SysMenu::getPermission, menu.getPermission())); + }))); - // 新增租户菜单,但未维护上下级关系 - SysMenu newMenu = new SysMenu(); - BeanUtils.copyProperties(menu, newMenu, SysMenu.Fields.menuId); - menuService.save(newMenu); - return newMenu; - }))) - .toList(); + // 2. 如果旧套餐不包含差集元素则新增 + List newTenantMenuIdList = CollUtil.subtract(disjunctionList, oldMenuIdList) + .stream() + .map(menuId -> TenantBroker.applyAs(tenantDTO.getId(), (tenantId -> { + // 查询租户菜单元信息 + SysMenu menu = CollUtil.findOneByField(sysMenuList, SysMenu.Fields.menuId, Long.parseLong(menuId)); - // 3. 更新新增菜单上下级关系 - for (SysMenu tenantMenu : newTenantMenuIdList) { - // 查询租户菜单父元信息 - SysMenu parentMenu = CollUtil.findOneByField(sysMenuList, SysMenu.Fields.menuId, tenantMenu.getParentId()); - // 查询目标租户父菜单元信息 - TenantBroker.runAs(tenantDTO.getId(), tenantId -> { - // 如何菜单父节点是根节点则直接插入,不要查询父节点判断 - if (Objects.equals(tenantMenu.getParentId(), CommonConstants.MENU_TREE_ROOT_ID)) { - menuService.updateById(tenantMenu); - return; - } + // 新增租户菜单,但未维护上下级关系 + SysMenu newMenu = new SysMenu(); + BeanUtils.copyProperties(menu, newMenu, SysMenu.Fields.menuId); + menuService.save(newMenu); + return newMenu; + }))) + .toList(); - // 根据path 或者 permisson 查询目标父菜单 - SysMenu tenantParentMenu = menuService.getOne(Wrappers.lambdaQuery() - .eq(StrUtil.isNotBlank(parentMenu.getPath()), SysMenu::getPath, parentMenu.getPath()) - .or() - .eq(StrUtil.isNotBlank(parentMenu.getPermission()), SysMenu::getPermission, - parentMenu.getPermission()), - false); + // 3. 更新新增菜单上下级关系 + for (SysMenu tenantMenu : newTenantMenuIdList) { + // 查询租户菜单父元信息 + SysMenu parentMenu = CollUtil.findOneByField(sysMenuList, SysMenu.Fields.menuId, tenantMenu.getParentId()); + // 查询目标租户父菜单元信息 + TenantBroker.runAs(tenantDTO.getId(), tenantId -> { + // 如何菜单父节点是根节点则直接插入,不要查询父节点判断 + if (Objects.equals(tenantMenu.getParentId(), CommonConstants.MENU_TREE_ROOT_ID)) { + menuService.updateById(tenantMenu); + return; + } - tenantMenu.setParentId(tenantParentMenu.getMenuId()); - menuService.updateById(tenantMenu); - }); - } + // 根据path 或者 permisson 查询目标父菜单 + SysMenu tenantParentMenu = menuService.getOne(Wrappers.lambdaQuery() + .eq(StrUtil.isNotBlank(parentMenu.getPath()), SysMenu::getPath, parentMenu.getPath()) + .or() + .eq(StrUtil.isNotBlank(parentMenu.getPermission()), SysMenu::getPermission, + parentMenu.getPermission()), + false); - // 清空指定租户的菜单权限缓存 - Set keys = RedisUtils.keys(String.format("%s:%s::*", tenantDTO.getId(), CacheConstants.MENU_DETAILS)); - if (CollUtil.isNotEmpty(keys)) { - RedisUtils.delete(keys.toArray(new String[0])); - } - return Boolean.TRUE; - } + tenantMenu.setParentId(tenantParentMenu.getMenuId()); + menuService.updateById(tenantMenu); + }); + } - /** - * 获取用户所属租户列表 - * @return 用户所属租户列表 - */ - @Override - public List getUserTenant() { - MPJLambdaWrapper wrapper = new MPJLambdaWrapper().selectAll(SysTenant.class) - .leftJoin(SysTenantUser.class, SysTenantUser::getTenantId, SysTenant::getId) - .eq(SysTenantUser::getUserId, SecurityUtils.getUser().getId()) - .eq(SysTenant::getStatus, TenantStateEnum.NORMAL.getCode()); + // 清空指定租户的菜单权限缓存 + Set keys = RedisUtils.keys(String.format("%s:%s::*", tenantDTO.getId(), CacheConstants.MENU_DETAILS)); + if (CollUtil.isNotEmpty(keys)) { + RedisUtils.delete(keys.toArray(new String[0])); + } + return Boolean.TRUE; + } - return baseMapper.selectJoinList(wrapper) - .stream() - .filter(tenant -> tenant.getStartTime().isBefore(LocalDateTime.now())) - .filter(tenant -> tenant.getEndTime().isAfter(LocalDateTime.now())) - .toList(); - } + /** + * 获取用户所属租户列表 + * + * @return 用户所属租户列表 + */ + @Override + public List getUserTenant() { + MPJLambdaWrapper wrapper = new MPJLambdaWrapper().selectAll(SysTenant.class) + .leftJoin(SysTenantUser.class, SysTenantUser::getTenantId, SysTenant::getId) + .eq(SysTenantUser::getUserId, SecurityUtils.getUser().getId()) + .eq(SysTenant::getStatus, TenantStateEnum.NORMAL.getCode()); - /** - * 获取用户租户分页信息 - * @param page 分页参数 - * @param userDTO 用户信息 - * @return 用户租户分页结果 - */ - @Override - public Page getUserTenantPage(Page page, UserDTO userDTO) { - MPJLambdaWrapper wrapper = new MPJLambdaWrapper().selectAll(SysUser.class) - .innerJoin(SysTenantUser.class, SysTenantUser::getUserId, SysUser::getUserId) - .like(StrUtil.isNotBlank(userDTO.getUsername()), SysUser::getUsername, userDTO.getUsername()) - .like(StrUtil.isNotBlank(userDTO.getPhone()), SysUser::getPhone, userDTO.getPhone()) - .like(StrUtil.isNotBlank(userDTO.getEmail()), SysUser::getEmail, userDTO.getEmail()) - .eq(Objects.nonNull(userDTO.getTenantId()), SysTenantUser::getTenantId, userDTO.getTenantId()); - return TenantBroker.noneAs(() -> sysUserMapper.selectJoinPage(page, wrapper)); - } + return baseMapper.selectJoinList(wrapper) + .stream() + .filter(tenant -> tenant.getStartTime().isBefore(LocalDateTime.now())) + .filter(tenant -> tenant.getEndTime().isAfter(LocalDateTime.now())) + .toList(); + } - /** - * 移除租户用户 - * @param tenantUserDTO 租户用户信息 - * @return 是否移除成功 - */ - @Override - public Boolean removeTenantUser(SysTenantUserDTO tenantUserDTO) { - ArrayList userIdList = CollUtil.toList(tenantUserDTO.getUserIds()); + /** + * 获取用户租户分页信息 + * + * @param page 分页参数 + * @param userDTO 用户信息 + * @return 用户租户分页结果 + */ + @Override + public Page getUserTenantPage(Page page, UserDTO userDTO) { + MPJLambdaWrapper wrapper = new MPJLambdaWrapper().selectAll(SysUser.class) + .innerJoin(SysTenantUser.class, SysTenantUser::getUserId, SysUser::getUserId) + .like(StrUtil.isNotBlank(userDTO.getUsername()), SysUser::getUsername, userDTO.getUsername()) + .like(StrUtil.isNotBlank(userDTO.getPhone()), SysUser::getPhone, userDTO.getPhone()) + .like(StrUtil.isNotBlank(userDTO.getEmail()), SysUser::getEmail, userDTO.getEmail()) + .eq(Objects.nonNull(userDTO.getTenantId()), SysTenantUser::getTenantId, userDTO.getTenantId()); + return TenantBroker.noneAs(() -> sysUserMapper.selectJoinPage(page, wrapper)); + } - // 删除租户用户关系 - sysTenantUserMapper.delete(Wrappers.lambdaQuery() - .eq(SysTenantUser::getTenantId, tenantUserDTO.getTenantId()) - .in(SysTenantUser::getUserId, userIdList)); + /** + * 移除租户用户 + * + * @param tenantUserDTO 租户用户信息 + * @return 是否移除成功 + */ + @Override + public Boolean removeTenantUser(SysTenantUserDTO tenantUserDTO) { + ArrayList userIdList = CollUtil.toList(tenantUserDTO.getUserIds()); - TenantBroker.runAs(tenantUserDTO.getTenantId(), (tenantId -> { - // 删除用户岗位 - sysUserPostMapper.delete(Wrappers.lambdaQuery().in(SysUserPost::getUserId, userIdList)); - // 删除用户角色 - sysUserRoleMapper.delete(Wrappers.lambdaQuery().in(SysUserRole::getUserId, userIdList)); - // 删除用户部门 - sysUserDeptMapper.delete(Wrappers.lambdaQuery().in(SysUserDept::getUserId, userIdList)); - })); - return Boolean.TRUE; - } + // 删除租户用户关系 + sysTenantUserMapper.delete(Wrappers.lambdaQuery() + .eq(SysTenantUser::getTenantId, tenantUserDTO.getTenantId()) + .in(SysTenantUser::getUserId, userIdList)); - /** - * 根据用户信息查询租户用户列表 - * @param userDTO 用户信息传输对象 - * @return 租户用户列表 - */ - @Override - public List listTenantUser(UserDTO userDTO) { - return TenantBroker.noneAs(() -> sysUserMapper.selectList(Wrappers.lambdaQuery() - .ne(SysUser::getTenantId, userDTO.getTenantId()) - .like(StrUtil.isNotBlank(userDTO.getUsername()), SysUser::getUsername, userDTO.getUsername()))); - } + TenantBroker.runAs(tenantUserDTO.getTenantId(), (tenantId -> { + // 删除用户岗位 + sysUserPostMapper.delete(Wrappers.lambdaQuery().in(SysUserPost::getUserId, userIdList)); + // 删除用户角色 + sysUserRoleMapper.delete(Wrappers.lambdaQuery().in(SysUserRole::getUserId, userIdList)); + // 删除用户部门 + sysUserDeptMapper.delete(Wrappers.lambdaQuery().in(SysUserDept::getUserId, userIdList)); + })); + return Boolean.TRUE; + } - /** - * 保存租户用户信息 - * @param tenantUserDTO 租户用户信息DTO - * @return 保存是否成功 - */ - @Override - @Transactional(rollbackFor = Exception.class) - public Boolean saveTenantUser(SysTenantUserDTO tenantUserDTO) { - TenantBroker.runAs(tenantUserDTO.getTenantId(), (tenantId -> { - for (Long userId : tenantUserDTO.getUserIds()) { - List sysTenantUsers = sysTenantUserMapper - .selectList(Wrappers.lambdaQuery() - .eq(SysTenantUser::getTenantId, tenantId) - .eq(SysTenantUser::getUserId, userId)); - if (CollUtil.isEmpty(sysTenantUsers)) { - // 插入租户用户关系 - SysTenantUser sysTenantUser = new SysTenantUser(); - sysTenantUser.setUserId(userId); - sysTenantUser.setTenantId(tenantId); - sysTenantUserMapper.insert(sysTenantUser); - } + /** + * 根据用户信息查询租户用户列表 + * + * @param userDTO 用户信息传输对象 + * @return 租户用户列表 + */ + @Override + public List listTenantUser(UserDTO userDTO) { + return TenantBroker.noneAs(() -> sysUserMapper.selectList(Wrappers.lambdaQuery() + .like(StrUtil.isNotBlank(userDTO.getUsername()), SysUser::getUsername, userDTO.getUsername()))); + } - List userRoleList = sysUserRoleMapper.selectList(Wrappers.lambdaQuery() - .eq(SysUserRole::getUserId, userId) - .eq(SysUserRole::getRoleId, tenantUserDTO.getRoleId())); - if (CollUtil.isEmpty(userRoleList)) { - // 插入用户角色关系 - SysUserRole sysUserRole = new SysUserRole(); - sysUserRole.setUserId(userId); - sysUserRole.setRoleId(tenantUserDTO.getRoleId()); - sysUserRoleMapper.insert(sysUserRole); - } + /** + * 保存租户用户信息 + * + * @param tenantUserDTO 租户用户信息DTO + * @return 保存是否成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean saveTenantUser(SysTenantUserDTO tenantUserDTO) { + TenantBroker.runAs(tenantUserDTO.getTenantId(), (tenantId -> { + for (Long userId : tenantUserDTO.getUserIds()) { + List sysTenantUsers = sysTenantUserMapper + .selectList(Wrappers.lambdaQuery() + .eq(SysTenantUser::getTenantId, tenantId) + .eq(SysTenantUser::getUserId, userId)); + if (CollUtil.isEmpty(sysTenantUsers)) { + // 插入租户用户关系 + SysTenantUser sysTenantUser = new SysTenantUser(); + sysTenantUser.setUserId(userId); + sysTenantUser.setTenantId(tenantId); + sysTenantUserMapper.insert(sysTenantUser); + } - List userDeptList = sysUserDeptMapper.selectList(Wrappers.lambdaQuery() - .eq(SysUserDept::getUserId, userId) - .eq(SysUserDept::getDeptId, tenantUserDTO.getDeptId())); + List userRoleList = sysUserRoleMapper.selectList(Wrappers.lambdaQuery() + .eq(SysUserRole::getUserId, userId) + .eq(SysUserRole::getRoleId, tenantUserDTO.getRoleId())); + if (CollUtil.isEmpty(userRoleList)) { + // 插入用户角色关系 + SysUserRole sysUserRole = new SysUserRole(); + sysUserRole.setUserId(userId); + sysUserRole.setRoleId(tenantUserDTO.getRoleId()); + sysUserRoleMapper.insert(sysUserRole); + } - if (CollUtil.isEmpty(userDeptList)) { - // 插入用户部门关系 - SysUserDept sysUserDept = new SysUserDept(); - sysUserDept.setUserId(userId); - sysUserDept.setDeptId(tenantUserDTO.getDeptId()); - sysUserDeptMapper.insert(sysUserDept); - } + List userDeptList = sysUserDeptMapper.selectList(Wrappers.lambdaQuery() + .eq(SysUserDept::getUserId, userId) + .eq(SysUserDept::getDeptId, tenantUserDTO.getDeptId())); - List userPostList = sysUserPostMapper.selectList(Wrappers.lambdaQuery() - .eq(SysUserPost::getUserId, userId) - .eq(SysUserPost::getPostId, tenantUserDTO.getPostId())); + if (CollUtil.isEmpty(userDeptList)) { + // 插入用户部门关系 + SysUserDept sysUserDept = new SysUserDept(); + sysUserDept.setUserId(userId); + sysUserDept.setDeptId(tenantUserDTO.getDeptId()); + sysUserDeptMapper.insert(sysUserDept); + } - if (CollUtil.isEmpty(userPostList) && Objects.nonNull(tenantUserDTO.getPostId())) { - // 插入用户岗位关系 - SysUserPost sysUserPost = new SysUserPost(); - sysUserPost.setUserId(userId); - sysUserPost.setPostId(tenantUserDTO.getPostId()); - sysUserPostMapper.insert(sysUserPost); - } - } - })); - return Boolean.TRUE; - } + List userPostList = sysUserPostMapper.selectList(Wrappers.lambdaQuery() + .eq(SysUserPost::getUserId, userId) + .eq(SysUserPost::getPostId, tenantUserDTO.getPostId())); - /** - * 获取租户角色列表 - * @param userDTO 用户信息 - * @return 租户角色列表 - */ - @Override - public Map listTenantOrg(UserDTO userDTO) { - return TenantBroker.applyAs(userDTO.getTenantId(), tenantId -> { - // 过滤有权限配置的菜单 - List sysRoles = sysRoleMapper.selectList(Wrappers.lambdaQuery()) - .stream() - .filter(role -> roleMenuMapper - .exists(Wrappers.lambdaQuery().eq(SysRoleMenu::getRoleId, role.getRoleId()))) - .toList(); - List sysDepts = sysDeptMapper.selectList(Wrappers.lambdaQuery()); - List sysPosts = sysPostMapper.selectList(Wrappers.lambdaQuery()); - return Map.of("sysRoles", sysRoles, "sysDepts", sysDepts, "sysPosts", sysPosts); - }); - } + if (CollUtil.isEmpty(userPostList) && Objects.nonNull(tenantUserDTO.getPostId())) { + // 插入用户岗位关系 + SysUserPost sysUserPost = new SysUserPost(); + sysUserPost.setUserId(userId); + sysUserPost.setPostId(tenantUserDTO.getPostId()); + sysUserPostMapper.insert(sysUserPost); + } + } + })); + return Boolean.TRUE; + } - /** - * 移除租户 - * @param ids 租户ID列表 - * @return 是否移除成功 - */ - @Override - @CacheEvict(value = CacheConstants.TENANT_DETAILS, allEntries = true) - public Boolean removeTenant(Long[] ids) { - baseMapper.deleteByIds(CollUtil.toList(ids)); - sysTenantUserMapper - .delete(Wrappers.lambdaQuery().in(SysTenantUser::getTenantId, CollUtil.toList(ids))); - return Boolean.TRUE; - } + /** + * 获取租户角色列表 + * + * @param userDTO 用户信息 + * @return 租户角色列表 + */ + @Override + public Map listTenantOrg(UserDTO userDTO) { + return TenantBroker.applyAs(userDTO.getTenantId(), tenantId -> { + // 过滤有权限配置的菜单 + List sysRoles = sysRoleMapper.selectList(Wrappers.lambdaQuery()) + .stream() + .filter(role -> roleMenuMapper + .exists(Wrappers.lambdaQuery().eq(SysRoleMenu::getRoleId, role.getRoleId()))) + .toList(); + List sysDepts = sysDeptMapper.selectList(Wrappers.lambdaQuery()); + List sysPosts = sysPostMapper.selectList(Wrappers.lambdaQuery()); + return Map.of("sysRoles", sysRoles, "sysDepts", sysDepts, "sysPosts", sysPosts); + }); + } - /** - * 更新用户租户信息 - * @param userDto 用户数据传输对象 - * @return 更新结果 - */ - @Override - @CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username") - public R updateUserTenant(UserDTO userDto) { - return R.ok(TenantBroker.noneAs(() -> sysUserMapper.update(Wrappers.lambdaUpdate() - .set(SysUser::getTenantId, userDto.getTenantId()) - .eq(SysUser::getUserId, userDto.getUserId())))); - } + /** + * 移除租户 + * + * @param ids 租户ID列表 + * @return 是否移除成功 + */ + @Override + @CacheEvict(value = CacheConstants.TENANT_DETAILS, allEntries = true) + public Boolean removeTenant(Long[] ids) { + baseMapper.deleteByIds(CollUtil.toList(ids)); + sysTenantUserMapper + .delete(Wrappers.lambdaQuery().in(SysTenantUser::getTenantId, CollUtil.toList(ids))); + return Boolean.TRUE; + } - /** - * 保存新的租户菜单,维护成新的菜单 - * @param menuList 菜单列表 - * @param originParentId 原始上级 - * @param targetParentId 目标上级 - */ - private void saveTenantMenu(List menuList, Long originParentId, Long targetParentId) { - menuList.stream().filter(menu -> menu.getParentId().equals(originParentId)).forEach(menu -> { - // 保存菜单原始menuId, 方便查询子节点使用 - Long originMenuId = menu.getMenuId(); - menu.setMenuId(null); - menu.setParentId(targetParentId); - menuService.save(menu); - // 查找此节点的子节点,然后子节点的重新插入父节点更改为新的menuId - saveTenantMenu(menuList, originMenuId, menu.getMenuId()); - }); - } + /** + * 更新用户租户信息 + * + * @param userDto 用户数据传输对象 + * @return 更新结果 + */ + @Override + @CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username") + public R updateUserTenant(UserDTO userDto) { + return R.ok(TenantBroker.noneAs(() -> sysUserMapper.update(Wrappers.lambdaUpdate() + .set(SysUser::getTenantId, userDto.getTenantId()) + .eq(SysUser::getUserId, userDto.getUserId())))); + } - /** - * 获取或更新当前用户的租户信息 - * @return 更新后的租户ID,如果无需更新则返回null - */ - @Override - public Long getOrUpdateTenant() { - PigxUser loginUser = SecurityUtils.getUser(); - boolean match = this.getNormalTenant() - .stream() - .filter(tenant -> tenant.getStartTime().isBefore(LocalDateTime.now())) - .filter(tenant -> tenant.getEndTime().isAfter(LocalDateTime.now())) - .anyMatch(tenant -> tenant.getId().equals(loginUser.getTenantId())); + /** + * 保存新的租户菜单,维护成新的菜单 + * + * @param menuList 菜单列表 + * @param originParentId 原始上级 + * @param targetParentId 目标上级 + */ + private void saveTenantMenu(List menuList, Long originParentId, Long targetParentId) { + menuList.stream().filter(menu -> menu.getParentId().equals(originParentId)).forEach(menu -> { + // 保存菜单原始menuId, 方便查询子节点使用 + Long originMenuId = menu.getMenuId(); + menu.setMenuId(null); + menu.setParentId(targetParentId); + menuService.save(menu); + // 查找此节点的子节点,然后子节点的重新插入父节点更改为新的menuId + saveTenantMenu(menuList, originMenuId, menu.getMenuId()); + }); + } - if (!match) { - SysTenantUser tenantUser = sysTenantUserMapper.selectOne(Wrappers.lambdaQuery() - .eq(SysTenantUser::getUserId, SecurityUtils.getUser().getId()) - .ne(SysTenantUser::getTenantId, loginUser.getTenantId()), false); - // 如果当前用户没有租户信息,则不进行任何操作 - if (Objects.isNull(tenantUser)) { - return null; - } + /** + * 获取或更新当前用户的租户信息 + *

+ * 如果当前租户已过期或无效,则自动切换到用户的其他有效租户 + * + * @return 当前有效的租户对象,如果无有效租户则返回null + */ + @Override + public SysTenant getOrUpdateTenant() { + PigxUser loginUser = SecurityUtils.getUser(); + // 通过代理对象调用以确保 @Cacheable 生效,避免自调用导致缓存失效 + List normalTenantList = SpringContextHolder.getBean(SysTenantService.class) + .getNormalTenant() + .stream() + .filter(tenant -> tenant.getStartTime().isBefore(LocalDateTime.now())) + .filter(tenant -> tenant.getEndTime().isAfter(LocalDateTime.now())) + .toList(); - // 切换至状态正常的租户 - UserDTO userDTO = new UserDTO(); - userDTO.setUserId(loginUser.getId()); - userDTO.setTenantId(tenantUser.getTenantId()); - userDTO.setUsername(loginUser.getUsername()); - updateUserTenant(userDTO); - cacheManager.getCache(CacheConstants.USER_DETAILS).evict(loginUser.getUsername()); - return tenantUser.getTenantId(); - } + boolean match = normalTenantList.stream() + .anyMatch(tenant -> tenant.getId().equals(loginUser.getTenantId())); - return null; - } + if (match) { + // 当前租户有效,返回当前租户对象 + return normalTenantList.stream() + .filter(tenant -> tenant.getId().equals(loginUser.getTenantId())) + .findFirst() + .orElse(null); + } + + SysTenantUser tenantUser = sysTenantUserMapper.selectOne(Wrappers.lambdaQuery() + .eq(SysTenantUser::getUserId, SecurityUtils.getUser().getId()) + .ne(SysTenantUser::getTenantId, loginUser.getTenantId()), false); + // 如果当前用户没有租户信息,则不进行任何操作 + if (Objects.isNull(tenantUser)) { + return null; + } + + // 切换至状态正常的租户 + UserDTO userDTO = new UserDTO(); + userDTO.setUserId(loginUser.getId()); + userDTO.setTenantId(tenantUser.getTenantId()); + userDTO.setUsername(loginUser.getUsername()); + updateUserTenant(userDTO); + cacheManager.getCache(CacheConstants.USER_DETAILS).evict(loginUser.getUsername()); + + // 返回切换后的租户对象 + return normalTenantList.stream() + .filter(tenant -> tenant.getId().equals(tenantUser.getTenantId())) + .findFirst() + .orElseGet(() -> baseMapper.selectById(tenantUser.getTenantId())); + + } } @@ -545,36 +580,36 @@ public class SysTenantServiceImpl extends ServiceImpl args = Arrays.stream(ReflectUtil.getFields(this.getClass())) - .map(Field::getName) - .map(StrUtil::toUnderlineCase) - .map(String::toUpperCase) - .toList(); - Map paramsMap = ParamResolver.getMap(args.toArray(new String[] {})); - for (Field field : ReflectUtil.getFields(this.getClass())) { - String key = StrUtil.toUnderlineCase(field.getName()).toUpperCase(); - if (paramsMap.containsKey(key)) { - field.set(this, paramsMap.get(key)); - } - } - } + @SneakyThrows + public TenantDefaultConfig() { + List args = Arrays.stream(ReflectUtil.getFields(this.getClass())) + .map(Field::getName) + .map(StrUtil::toUnderlineCase) + .map(String::toUpperCase) + .toList(); + Map paramsMap = ParamResolver.getMap(args.toArray(new String[]{})); + for (Field field : ReflectUtil.getFields(this.getClass())) { + String key = StrUtil.toUnderlineCase(field.getName()).toUpperCase(); + if (paramsMap.containsKey(key)) { + field.set(this, paramsMap.get(key)); + } + } + } } 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 bd1599d0b..ef2b2ed11 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 @@ -202,9 +202,10 @@ public class SysUserServiceImpl extends ServiceImpl impl // 如果是登录成功以后查询租户信息并进行校验 if (Objects.nonNull(SecurityUtils.getUser())) { - Long updateTenantId = sysTenantService.getOrUpdateTenant(); - if (Objects.nonNull(updateTenantId)) { - userInfo.setTenantId(updateTenantId); + SysTenant updateTenant = sysTenantService.getOrUpdateTenant(); + if (Objects.nonNull(updateTenant)) { + userInfo.setTenantId(updateTenant.getId()); + userInfo.setTenantName(updateTenant.getName()); } } From 25e16f5dd6d9d60d923875980e3bcd524a1fc328 Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Thu, 1 Jan 2026 08:52:16 +0800 Subject: [PATCH 43/44] =?UTF-8?q?feat(upms):=20=E4=BC=98=E5=8C=96=E5=AF=86?= =?UTF-8?q?=E7=A0=81=E9=AA=8C=E8=AF=81=E9=80=BB=E8=BE=91=EF=BC=8C=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E5=B8=83=E5=B0=94=E5=80=BC=E4=BB=A5=E6=8C=87=E7=A4=BA?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=BB=93=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/service/impl/SysUserServiceImpl.java | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) 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 ef2b2ed11..691ed7e94 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 @@ -664,17 +664,10 @@ public class SysUserServiceImpl extends ServiceImpl impl * @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(MsgUtils.getMessage(UpmsErrorCodes.SYS_USER_PASSWORD_MISMATCH)); - } else { - return R.ok(); - } + public R checkPassword(String username, String password) { + SysUser sysUser = this.getOne(Wrappers.lambdaQuery().eq(SysUser::getUsername, username)); + boolean matches = ENCODER.matches(password, sysUser.getPassword()); + return matches ? R.ok(true) : R.ok(false, MsgUtils.getMessage(UpmsErrorCodes.SYS_USER_PASSWORD_MISMATCH)); } /** From c36ef5fdf5eca99471616507aac3d62167ca49bb Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Sat, 3 Jan 2026 22:55:26 +0800 Subject: [PATCH 44/44] =?UTF-8?q?refactor(copyright):=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E7=89=88=E6=9D=83=E5=B9=B4=E4=BB=BD=E8=87=B3=202026?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 统一更新所有源文件、LICENSE、pom.xml 等的版权年份至 2026 --- LICENSE | 2 +- .../main/java/com/pig4cloud/pigx/app/api/dto/AppUserInfo.java | 2 +- .../main/java/com/pig4cloud/pigx/app/api/entity/AppRole.java | 2 +- .../com/pig4cloud/pigx/app/api/entity/AppSocialDetails.java | 2 +- .../main/java/com/pig4cloud/pigx/app/api/entity/AppUser.java | 2 +- .../java/com/pig4cloud/pigx/app/api/entity/AppUserRole.java | 2 +- .../com/pig4cloud/pigx/app/api/feign/RemoteAppUserService.java | 2 +- .../main/java/com/pig4cloud/pigx/app/api/vo/AppRoleExcelVO.java | 2 +- .../src/main/java/com/pig4cloud/pigx/app/api/vo/AppRoleVO.java | 2 +- .../com/pig4cloud/pigx/app/controller/AppMobileController.java | 2 +- .../com/pig4cloud/pigx/app/controller/AppRoleController.java | 2 +- .../pigx/app/controller/AppSocialDetailsController.java | 2 +- .../com/pig4cloud/pigx/app/controller/AppUserController.java | 2 +- .../pig4cloud/pigx/app/controller/AppUserRoleController.java | 2 +- .../com/pig4cloud/pigx/app/handler/AbstractLoginHandler.java | 2 +- .../main/java/com/pig4cloud/pigx/app/handler/LoginHandler.java | 2 +- .../com/pig4cloud/pigx/app/handler/MiniAppLoginHandler.java | 2 +- .../java/com/pig4cloud/pigx/app/handler/SmsLoginHandler.java | 2 +- .../main/java/com/pig4cloud/pigx/app/mapper/AppRoleMapper.java | 2 +- .../com/pig4cloud/pigx/app/mapper/AppSocialDetailsMapper.java | 2 +- .../main/java/com/pig4cloud/pigx/app/mapper/AppUserMapper.java | 2 +- .../java/com/pig4cloud/pigx/app/mapper/AppUserRoleMapper.java | 2 +- .../java/com/pig4cloud/pigx/app/service/AppMobileService.java | 2 +- .../java/com/pig4cloud/pigx/app/service/AppRoleService.java | 2 +- .../com/pig4cloud/pigx/app/service/AppSocialDetailsService.java | 2 +- .../java/com/pig4cloud/pigx/app/service/AppUserRoleService.java | 2 +- .../java/com/pig4cloud/pigx/app/service/AppUserService.java | 2 +- .../pig4cloud/pigx/app/service/impl/AppMobileServiceImpl.java | 2 +- .../com/pig4cloud/pigx/app/service/impl/AppRoleServiceImpl.java | 2 +- .../pigx/app/service/impl/AppSocialDetailsServiceImpl.java | 2 +- .../pig4cloud/pigx/app/service/impl/AppUserRoleServiceImpl.java | 2 +- .../com/pig4cloud/pigx/app/service/impl/AppUserServiceImpl.java | 2 +- .../src/main/resources/mapper/AppRoleMapper.xml | 2 +- .../src/main/resources/mapper/AppUserMapper.xml | 2 +- .../src/main/resources/mapper/AppUserRoleMapper.xml | 2 +- pigx-auth/pom.xml | 2 +- pigx-auth/src/main/resources/logback-spring.xml | 2 +- pigx-boot/src/main/resources/logback-spring.xml | 2 +- pigx-common/pigx-common-core/pom.xml | 2 +- .../pigx/common/core/config/MessageSourceConfiguration.java | 2 +- .../pigx/common/core/config/WebCommonConfiguration.java | 2 +- .../pig4cloud/pigx/common/core/constant/CommonConstants.java | 2 +- .../pig4cloud/pigx/common/core/constant/SecurityConstants.java | 2 +- .../pigx/common/core/constant/ServiceNameConstants.java | 2 +- .../pigx/common/core/constant/enums/LoginTypeEnum.java | 2 +- .../pigx/common/core/constant/enums/ProcessStatusEnum.java | 2 +- .../pigx/common/core/constant/enums/ResourceTypeEnum.java | 2 +- .../pigx/common/core/constant/enums/TaskStatusEnum.java | 2 +- .../pig4cloud/pigx/common/core/exception/CheckedException.java | 2 +- .../pigx/common/core/exception/ValidateCodeException.java | 2 +- .../java/com/pig4cloud/pigx/common/core/util/ClassUtils.java | 2 +- .../src/main/java/com/pig4cloud/pigx/common/core/util/R.java | 2 +- .../main/java/com/pig4cloud/pigx/common/core/util/RetOps.java | 2 +- .../pig4cloud/pigx/common/core/util/SpringContextHolder.java | 2 +- .../main/java/com/pig4cloud/pigx/common/core/util/WebUtils.java | 2 +- pigx-common/pigx-common-data/pom.xml | 2 +- .../pig4cloud/pigx/common/data/cache/RedisAutoCacheManager.java | 2 +- .../pigx/common/data/cache/RedisCacheAutoConfiguration.java | 2 +- .../pigx/common/data/cache/RedisCacheManagerConfiguration.java | 2 +- .../pigx/common/data/cache/RedisTemplateConfiguration.java | 2 +- .../com/pig4cloud/pigx/common/data/datascope/DataScope.java | 2 +- .../pig4cloud/pigx/common/data/datascope/DataScopeFuncEnum.java | 2 +- .../pig4cloud/pigx/common/data/datascope/DataScopeHandle.java | 2 +- .../pigx/common/data/datascope/DataScopeLogicEnum.java | 2 +- .../pig4cloud/pigx/common/data/datascope/DataScopeTypeEnum.java | 2 +- .../pigx/common/data/datascope/PigxDefaultDataScopeHandle.java | 2 +- .../pigx/common/data/mybatis/MybatisPlusConfiguration.java | 2 +- .../pigx/common/data/resolver/SqlFilterArgumentResolver.java | 2 +- .../pigx/common/data/tenant/PigxFeignTenantInterceptor.java | 2 +- .../pigx/common/data/tenant/PigxTenantConfiguration.java | 2 +- .../pig4cloud/pigx/common/data/tenant/PigxTenantHandler.java | 2 +- .../pig4cloud/pigx/common/data/tenant/TenantContextHolder.java | 2 +- .../pigx/common/data/tenant/TenantContextHolderFilter.java | 2 +- .../common/datasource/DynamicDataSourceAutoConfiguration.java | 2 +- .../common/datasource/config/JdbcDynamicDataSourceProvider.java | 2 +- .../pigx/common/datasource/config/LastParamDsProcessor.java | 2 +- .../pigx/common/datasource/config/MasterDataSourceProvider.java | 2 +- pigx-common/pigx-common-encrypt-api/pom.xml | 2 +- .../common/api/encrypt/core/DecryptQueryParametersFilter.java | 2 +- pigx-common/pigx-common-excel/pom.xml | 2 +- .../pig4cloud/pigx/common/feign/PigxFeignAutoConfiguration.java | 2 +- .../pigx/common/feign/annotation/EnablePigxFeignClients.java | 2 +- .../cloud/openfeign/PigxFeignClientsRegistrar.java | 2 +- pigx-common/pigx-common-gateway/pom.xml | 2 +- .../pigx/common/gateway/annotation/EnablePigxDynamicRoute.java | 2 +- .../gateway/configuration/DynamicRouteAutoConfiguration.java | 2 +- .../configuration/GrayLoadBalancerClientConfiguration.java | 2 +- .../gateway/filter/GrayReactiveLoadBalancerClientFilter.java | 2 +- .../pig4cloud/pigx/common/gateway/rule/GrayLoadBalancer.java | 2 +- .../pigx/common/gateway/rule/VersionGrayLoadBalancer.java | 2 +- .../pigx/common/gateway/support/DynamicRouteInitEvent.java | 2 +- .../pigx/common/gateway/support/RedisRouteDefinitionWriter.java | 2 +- .../pig4cloud/pigx/common/gateway/support/RouteCacheHolder.java | 2 +- .../com/pig4cloud/pigx/common/gateway/vo/RouteDefinitionVo.java | 2 +- pigx-common/pigx-common-gray/pom.xml | 2 +- .../pigx/common/gray/GrayLoadBalancerAutoConfiguration.java | 2 +- .../pigx/common/gray/feign/GrayFeignRequestInterceptor.java | 2 +- .../pigx/common/gray/support/NonWebVersionContextHolder.java | 2 +- pigx-common/pigx-common-job/pom.xml | 2 +- .../pig4cloud/pigx/common/job/annotation/EnablePigxXxlJob.java | 2 +- pigx-common/pigx-common-log/pom.xml | 2 +- .../com/pig4cloud/pigx/common/log/LogAutoConfiguration.java | 2 +- .../java/com/pig4cloud/pigx/common/log/annotation/SysLog.java | 2 +- .../com/pig4cloud/pigx/common/file/FileAutoConfiguration.java | 2 +- .../com/pig4cloud/pigx/common/file/core/FileProperties.java | 2 +- .../pigx/common/file/local/LocalFileAutoConfiguration.java | 2 +- .../pig4cloud/pigx/common/file/local/LocalFileProperties.java | 2 +- .../pig4cloud/pigx/common/file/oss/OssAutoConfiguration.java | 2 +- .../java/com/pig4cloud/pigx/common/file/oss/OssProperties.java | 2 +- .../com/pig4cloud/pigx/common/file/oss/http/OssEndpoint.java | 2 +- .../com/pig4cloud/pigx/common/file/oss/service/OssTemplate.java | 2 +- pigx-common/pigx-common-seata/pom.xml | 2 +- pigx-common/pigx-common-security/pom.xml | 2 +- .../security/handler/FormAuthenticationFailureHandler.java | 2 +- .../security/service/PigxDefaultUserDetailsServiceImpl.java | 2 +- .../com/pig4cloud/pigx/common/security/service/PigxUser.java | 2 +- .../pigx/common/security/service/PigxUserDetailsService.java | 2 +- .../java/com/pig4cloud/pigx/common/security/util/AuthUtils.java | 2 +- .../pigx/common/security/util/NonWebTokenContextHolder.java | 2 +- .../com/pig4cloud/pigx/common/security/util/SecurityUtils.java | 2 +- .../src/main/resources/errors/messages_zh_CN.properties | 2 +- pigx-common/pigx-common-sensitive/pom.xml | 2 +- .../pig4cloud/pigx/common/sensitive/annotation/Sensitive.java | 2 +- .../pigx/common/sensitive/core/SensitiveSerialize.java | 2 +- .../pig4cloud/pigx/common/sensitive/core/SensitiveTypeEnum.java | 2 +- .../pig4cloud/pigx/common/sensitive/util/DesensitizedUtils.java | 2 +- .../pigx/common/sentinel/PigxSentinelAutoConfiguration.java | 2 +- .../pigx/common/sentinel/handle/GlobalBizExceptionHandler.java | 2 +- .../pigx/common/sentinel/handle/PigxUrlBlockHandler.java | 2 +- .../common/sentinel/parser/PigxHeaderRequestOriginParser.java | 2 +- .../pigx/common/sequence/SequenceAutoConfiguration.java | 2 +- pigx-common/pigx-common-swagger/pom.xml | 2 +- .../pig4cloud/pigx/common/swagger/config/OpenAPIDefinition.java | 2 +- .../pigx/common/swagger/support/SwaggerProperties.java | 2 +- pigx-common/pom.xml | 2 +- pigx-flow/pom.xml | 2 +- pigx-gateway/pom.xml | 2 +- .../java/com/pig4cloud/pigx/gateway/PigxGatewayApplication.java | 2 +- .../pig4cloud/pigx/gateway/config/RateLimiterConfiguration.java | 2 +- .../pig4cloud/pigx/gateway/filter/HttpBasicGatewayFilter.java | 2 +- .../pig4cloud/pigx/gateway/filter/PigxRequestGlobalFilter.java | 2 +- .../com/pig4cloud/pigx/gateway/filter/PreviewGatewayFilter.java | 2 +- pigx-gateway/src/main/resources/logback-spring.xml | 2 +- pigx-register/src/main/resources/logback-spring.xml | 2 +- pigx-upms/pigx-upms-api/pom.xml | 2 +- .../src/main/java/com/pig4cloud/pigx/admin/api/dto/UserDTO.java | 2 +- .../main/java/com/pig4cloud/pigx/admin/api/dto/UserInfo.java | 2 +- .../java/com/pig4cloud/pigx/admin/api/entity/SysAuditLog.java | 2 +- .../main/java/com/pig4cloud/pigx/admin/api/entity/SysDept.java | 2 +- .../com/pig4cloud/pigx/admin/api/entity/SysDeptRelation.java | 2 +- .../main/java/com/pig4cloud/pigx/admin/api/entity/SysDict.java | 2 +- .../java/com/pig4cloud/pigx/admin/api/entity/SysDictItem.java | 2 +- .../main/java/com/pig4cloud/pigx/admin/api/entity/SysFile.java | 2 +- .../java/com/pig4cloud/pigx/admin/api/entity/SysI18nEntity.java | 2 +- .../main/java/com/pig4cloud/pigx/admin/api/entity/SysLog.java | 2 +- .../main/java/com/pig4cloud/pigx/admin/api/entity/SysMenu.java | 2 +- .../pig4cloud/pigx/admin/api/entity/SysOauthClientDetails.java | 2 +- .../main/java/com/pig4cloud/pigx/admin/api/entity/SysPost.java | 2 +- .../com/pig4cloud/pigx/admin/api/entity/SysPublicParam.java | 2 +- .../main/java/com/pig4cloud/pigx/admin/api/entity/SysRole.java | 2 +- .../java/com/pig4cloud/pigx/admin/api/entity/SysRoleMenu.java | 2 +- .../java/com/pig4cloud/pigx/admin/api/entity/SysRouteConf.java | 2 +- .../com/pig4cloud/pigx/admin/api/entity/SysScheduleEntity.java | 2 +- .../com/pig4cloud/pigx/admin/api/entity/SysSocialDetails.java | 2 +- .../java/com/pig4cloud/pigx/admin/api/entity/SysTenant.java | 2 +- .../java/com/pig4cloud/pigx/admin/api/entity/SysTenantUser.java | 2 +- .../main/java/com/pig4cloud/pigx/admin/api/entity/SysUser.java | 2 +- .../java/com/pig4cloud/pigx/admin/api/entity/SysUserRole.java | 2 +- .../pig4cloud/pigx/admin/api/feign/RemoteAuditLogService.java | 2 +- .../pigx/admin/api/feign/RemoteClientDetailsService.java | 2 +- .../pig4cloud/pigx/admin/api/feign/RemoteDataScopeService.java | 2 +- .../com/pig4cloud/pigx/admin/api/feign/RemoteDeptService.java | 2 +- .../com/pig4cloud/pigx/admin/api/feign/RemoteLogService.java | 2 +- .../com/pig4cloud/pigx/admin/api/feign/RemoteMenuService.java | 2 +- .../pig4cloud/pigx/admin/api/feign/RemoteMessageService.java | 2 +- .../com/pig4cloud/pigx/admin/api/feign/RemoteRoleService.java | 2 +- .../pig4cloud/pigx/admin/api/feign/RemoteRouteConfService.java | 2 +- .../com/pig4cloud/pigx/admin/api/feign/RemoteTenantService.java | 2 +- .../com/pig4cloud/pigx/admin/api/feign/RemoteTokenService.java | 2 +- .../com/pig4cloud/pigx/admin/api/feign/RemoteUserService.java | 2 +- .../src/main/java/com/pig4cloud/pigx/admin/api/vo/PreLogVO.java | 2 +- .../main/java/com/pig4cloud/pigx/admin/api/vo/RoleMenuVO.java | 2 +- .../src/main/java/com/pig4cloud/pigx/admin/api/vo/RoleVO.java | 2 +- .../src/main/java/com/pig4cloud/pigx/admin/api/vo/UserVO.java | 2 +- pigx-upms/pigx-upms-biz/pom.xml | 2 +- .../java/com/pig4cloud/pigx/admin/PigxAdminApplication.java | 2 +- .../com/pig4cloud/pigx/admin/config/DynamicRouteInitRunner.java | 2 +- .../pig4cloud/pigx/admin/controller/SysAuditLogController.java | 2 +- .../pig4cloud/pigx/admin/controller/SysClientController.java | 2 +- .../com/pig4cloud/pigx/admin/controller/SysDeptController.java | 2 +- .../com/pig4cloud/pigx/admin/controller/SysDictController.java | 2 +- .../com/pig4cloud/pigx/admin/controller/SysFileController.java | 2 +- .../com/pig4cloud/pigx/admin/controller/SysI18nController.java | 2 +- .../com/pig4cloud/pigx/admin/controller/SysLogController.java | 2 +- .../com/pig4cloud/pigx/admin/controller/SysMenuController.java | 2 +- .../com/pig4cloud/pigx/admin/controller/SysPostController.java | 2 +- .../pigx/admin/controller/SysPublicParamController.java | 2 +- .../com/pig4cloud/pigx/admin/controller/SysRoleController.java | 2 +- .../pig4cloud/pigx/admin/controller/SysRouteConfController.java | 2 +- .../pig4cloud/pigx/admin/controller/SysScheduleController.java | 2 +- .../pigx/admin/controller/SysSocialDetailsController.java | 2 +- .../pig4cloud/pigx/admin/controller/SysTenantController.java | 2 +- .../com/pig4cloud/pigx/admin/controller/SysTokenController.java | 2 +- .../com/pig4cloud/pigx/admin/controller/SysUserController.java | 2 +- .../com/pig4cloud/pigx/admin/handler/AbstractLoginHandler.java | 2 +- .../java/com/pig4cloud/pigx/admin/handler/CasLoginHandler.java | 2 +- .../com/pig4cloud/pigx/admin/handler/DingTalkLoginHandler.java | 2 +- .../com/pig4cloud/pigx/admin/handler/GiteeLoginHandler.java | 2 +- .../java/com/pig4cloud/pigx/admin/handler/LoginHandler.java | 2 +- .../com/pig4cloud/pigx/admin/handler/MiniAppLoginHandler.java | 2 +- .../com/pig4cloud/pigx/admin/handler/OscChinaLoginHandler.java | 2 +- .../java/com/pig4cloud/pigx/admin/handler/SmsLoginHandler.java | 2 +- .../com/pig4cloud/pigx/admin/handler/WeChatCPLoginHandler.java | 2 +- .../com/pig4cloud/pigx/admin/handler/WeChatLoginHandler.java | 2 +- .../java/com/pig4cloud/pigx/admin/mapper/SysAuditLogMapper.java | 2 +- .../java/com/pig4cloud/pigx/admin/mapper/SysDeptMapper.java | 2 +- .../java/com/pig4cloud/pigx/admin/mapper/SysDictItemMapper.java | 2 +- .../java/com/pig4cloud/pigx/admin/mapper/SysDictMapper.java | 2 +- .../com/pig4cloud/pigx/admin/mapper/SysFileGroupMapper.java | 2 +- .../java/com/pig4cloud/pigx/admin/mapper/SysFileMapper.java | 2 +- .../java/com/pig4cloud/pigx/admin/mapper/SysI18nMapper.java | 2 +- .../main/java/com/pig4cloud/pigx/admin/mapper/SysLogMapper.java | 2 +- .../java/com/pig4cloud/pigx/admin/mapper/SysMenuMapper.java | 2 +- .../pigx/admin/mapper/SysOauthClientDetailsMapper.java | 2 +- .../java/com/pig4cloud/pigx/admin/mapper/SysPostMapper.java | 2 +- .../com/pig4cloud/pigx/admin/mapper/SysPublicParamMapper.java | 2 +- .../java/com/pig4cloud/pigx/admin/mapper/SysRoleMapper.java | 2 +- .../java/com/pig4cloud/pigx/admin/mapper/SysRoleMenuMapper.java | 2 +- .../com/pig4cloud/pigx/admin/mapper/SysRouteConfMapper.java | 2 +- .../java/com/pig4cloud/pigx/admin/mapper/SysScheduleMapper.java | 2 +- .../com/pig4cloud/pigx/admin/mapper/SysSocialDetailsMapper.java | 2 +- .../java/com/pig4cloud/pigx/admin/mapper/SysTenantMapper.java | 2 +- .../com/pig4cloud/pigx/admin/mapper/SysTenantUserMapper.java | 2 +- .../java/com/pig4cloud/pigx/admin/mapper/SysUserMapper.java | 2 +- .../java/com/pig4cloud/pigx/admin/mapper/SysUserRoleMapper.java | 2 +- .../com/pig4cloud/pigx/admin/service/SysAuditLogService.java | 2 +- .../java/com/pig4cloud/pigx/admin/service/SysDeptService.java | 2 +- .../com/pig4cloud/pigx/admin/service/SysDictItemService.java | 2 +- .../java/com/pig4cloud/pigx/admin/service/SysDictService.java | 2 +- .../java/com/pig4cloud/pigx/admin/service/SysFileService.java | 2 +- .../java/com/pig4cloud/pigx/admin/service/SysLogService.java | 2 +- .../java/com/pig4cloud/pigx/admin/service/SysMenuService.java | 2 +- .../pigx/admin/service/SysOauthClientDetailsService.java | 2 +- .../java/com/pig4cloud/pigx/admin/service/SysPostService.java | 2 +- .../com/pig4cloud/pigx/admin/service/SysPublicParamService.java | 2 +- .../com/pig4cloud/pigx/admin/service/SysRoleMenuService.java | 2 +- .../java/com/pig4cloud/pigx/admin/service/SysRoleService.java | 2 +- .../com/pig4cloud/pigx/admin/service/SysRouteConfService.java | 2 +- .../com/pig4cloud/pigx/admin/service/SysScheduleService.java | 2 +- .../pig4cloud/pigx/admin/service/SysSocialDetailsService.java | 2 +- .../java/com/pig4cloud/pigx/admin/service/SysTenantService.java | 2 +- .../com/pig4cloud/pigx/admin/service/SysUserRoleService.java | 2 +- .../java/com/pig4cloud/pigx/admin/service/SysUserService.java | 2 +- .../pigx/admin/service/impl/SysAuditLogServiceImpl.java | 2 +- .../pig4cloud/pigx/admin/service/impl/SysDeptServiceImpl.java | 2 +- .../pigx/admin/service/impl/SysDictItemServiceImpl.java | 2 +- .../pig4cloud/pigx/admin/service/impl/SysDictServiceImpl.java | 2 +- .../pig4cloud/pigx/admin/service/impl/SysFileServiceImpl.java | 2 +- .../pig4cloud/pigx/admin/service/impl/SysI18nServiceImpl.java | 2 +- .../pig4cloud/pigx/admin/service/impl/SysLogServiceImpl.java | 2 +- .../pig4cloud/pigx/admin/service/impl/SysMenuServiceImpl.java | 2 +- .../admin/service/impl/SysOauthClientDetailsServiceImpl.java | 2 +- .../pig4cloud/pigx/admin/service/impl/SysPostServiceImpl.java | 2 +- .../pigx/admin/service/impl/SysPublicParamServiceImpl.java | 2 +- .../pigx/admin/service/impl/SysRoleMenuServiceImpl.java | 2 +- .../pig4cloud/pigx/admin/service/impl/SysRoleServiceImpl.java | 2 +- .../pigx/admin/service/impl/SysRouteConfServiceImpl.java | 2 +- .../pigx/admin/service/impl/SysScheduleServiceImpl.java | 2 +- .../pigx/admin/service/impl/SysSocialDetailsServiceImpl.java | 2 +- .../pig4cloud/pigx/admin/service/impl/SysTenantServiceImpl.java | 2 +- .../pigx/admin/service/impl/SysUserRoleServiceImpl.java | 2 +- .../pig4cloud/pigx/admin/service/impl/SysUserServiceImpl.java | 2 +- .../pigx-upms-biz/src/main/resources/mapper/SysDeptMapper.xml | 2 +- .../pigx-upms-biz/src/main/resources/mapper/SysLogMapper.xml | 2 +- .../pigx-upms-biz/src/main/resources/mapper/SysMenuMapper.xml | 2 +- .../pigx-upms-biz/src/main/resources/mapper/SysPostMapper.xml | 2 +- .../pigx-upms-biz/src/main/resources/mapper/SysRoleMapper.xml | 2 +- pigx-upms/pom.xml | 2 +- .../java/com/pig4cloud/pigx/codegen/PigxCodeGenApplication.java | 2 +- .../pigx/codegen/controller/GenCreateTableController.java | 2 +- .../pig4cloud/pigx/codegen/controller/GenDsConfController.java | 2 +- .../pigx/codegen/controller/GenFieldTypeController.java | 2 +- .../pigx/codegen/controller/GenFormConfController.java | 2 +- .../pig4cloud/pigx/codegen/controller/GenGroupController.java | 2 +- .../pig4cloud/pigx/codegen/controller/GenTableController.java | 2 +- .../pigx/codegen/controller/GenTemplateController.java | 2 +- .../pigx/codegen/controller/GenTemplateGroupController.java | 2 +- .../pig4cloud/pigx/codegen/controller/GeneratorController.java | 2 +- .../java/com/pig4cloud/pigx/codegen/entity/ColumnEntity.java | 2 +- .../main/java/com/pig4cloud/pigx/codegen/entity/GenConfig.java | 2 +- .../java/com/pig4cloud/pigx/codegen/entity/GenCreateTable.java | 2 +- .../com/pig4cloud/pigx/codegen/entity/GenDatasourceConf.java | 2 +- .../java/com/pig4cloud/pigx/codegen/entity/GenFieldType.java | 2 +- .../java/com/pig4cloud/pigx/codegen/entity/GenFormConf.java | 2 +- .../java/com/pig4cloud/pigx/codegen/entity/GenGroupEntity.java | 2 +- .../main/java/com/pig4cloud/pigx/codegen/entity/GenTable.java | 2 +- .../com/pig4cloud/pigx/codegen/entity/GenTableColumnEntity.java | 2 +- .../com/pig4cloud/pigx/codegen/entity/GenTemplateEntity.java | 2 +- .../pig4cloud/pigx/codegen/entity/GenTemplateGroupEntity.java | 2 +- .../java/com/pig4cloud/pigx/codegen/entity/TableEntity.java | 2 +- .../com/pig4cloud/pigx/codegen/mapper/GenCreateTableMapper.java | 2 +- .../pig4cloud/pigx/codegen/mapper/GenDatasourceConfMapper.java | 2 +- .../com/pig4cloud/pigx/codegen/mapper/GenDynamicMapper.java | 2 +- .../com/pig4cloud/pigx/codegen/mapper/GenFieldTypeMapper.java | 2 +- .../com/pig4cloud/pigx/codegen/mapper/GenFormConfMapper.java | 2 +- .../java/com/pig4cloud/pigx/codegen/mapper/GenGroupMapper.java | 2 +- .../com/pig4cloud/pigx/codegen/mapper/GenTableColumnMapper.java | 2 +- .../java/com/pig4cloud/pigx/codegen/mapper/GenTableMapper.java | 2 +- .../pig4cloud/pigx/codegen/mapper/GenTemplateGroupMapper.java | 2 +- .../com/pig4cloud/pigx/codegen/mapper/GenTemplateMapper.java | 2 +- .../pig4cloud/pigx/codegen/service/GenCreateTableService.java | 2 +- .../pigx/codegen/service/GenDatasourceConfService.java | 2 +- .../com/pig4cloud/pigx/codegen/service/GenFieldTypeService.java | 2 +- .../com/pig4cloud/pigx/codegen/service/GenFormConfService.java | 2 +- .../com/pig4cloud/pigx/codegen/service/GenGroupService.java | 2 +- .../pig4cloud/pigx/codegen/service/GenTableColumnService.java | 2 +- .../com/pig4cloud/pigx/codegen/service/GenTableService.java | 2 +- .../pig4cloud/pigx/codegen/service/GenTemplateGroupService.java | 2 +- .../com/pig4cloud/pigx/codegen/service/GenTemplateService.java | 2 +- .../com/pig4cloud/pigx/codegen/service/GeneratorService.java | 2 +- .../pigx/codegen/service/impl/CreateTableServiceImpl.java | 2 +- .../pigx/codegen/service/impl/GenDatasourceConfServiceImpl.java | 2 +- .../pigx/codegen/service/impl/GenFieldTypeServiceImpl.java | 2 +- .../pigx/codegen/service/impl/GenFormConfServiceImpl.java | 2 +- .../pigx/codegen/service/impl/GenGroupServiceImpl.java | 2 +- .../pigx/codegen/service/impl/GenTableServiceImpl.java | 2 +- .../pigx/codegen/service/impl/GenTemplateGroupServiceImpl.java | 2 +- .../pigx/codegen/service/impl/GenTemplateServiceImpl.java | 2 +- .../pigx/codegen/service/impl/GeneratorServiceImpl.java | 2 +- .../pig4cloud/pigx/codegen/util/table/CreateTableHandler.java | 2 +- .../pig4cloud/pigx/codegen/util/table/enums/TableModelEnum.java | 2 +- .../com/pig4cloud/pigx/codegen/util/table/model/TableInfo.java | 2 +- .../com/pig4cloud/pigx/codegen/util/vo/GenCreateTableVO.java | 2 +- pigx-visual/pigx-codegen/src/main/resources/logback-spring.xml | 2 +- .../src/main/resources/mapper/GenFieldTypeMapper.xml | 2 +- .../src/main/resources/mapper/GenFormConfMapper.xml | 2 +- .../pigx-codegen/src/main/resources/mapper/GenTableMapper.xml | 2 +- pigx-visual/pigx-daemon-elastic-job/pom.xml | 2 +- .../pigx/daemon/elastic/PigxDaemonElasticJobApplication.java | 2 +- .../java/com/pig4cloud/pigx/daemon/elastic/job/DemoJob.java | 2 +- .../src/main/resources/logback-spring.xml | 2 +- .../daemon/quartz/config/AutowireCapableBeanJobFactory.java | 2 +- .../pig4cloud/pigx/daemon/quartz/config/PigxInitQuartzJob.java | 2 +- .../pig4cloud/pigx/daemon/quartz/config/PigxQuartzConfig.java | 2 +- .../pigx/daemon/quartz/config/PigxQuartzCustomizerConfig.java | 2 +- .../pig4cloud/pigx/daemon/quartz/config/PigxQuartzFactory.java | 2 +- .../pigx/daemon/quartz/config/PigxQuartzInvokeFactory.java | 2 +- .../pigx/daemon/quartz/constants/JobTypeQuartzEnum.java | 2 +- .../pig4cloud/pigx/daemon/quartz/constants/PigxQuartzEnum.java | 2 +- .../pigx/daemon/quartz/controller/SysJobController.java | 2 +- .../pigx/daemon/quartz/controller/SysJobLogController.java | 2 +- .../java/com/pig4cloud/pigx/daemon/quartz/entity/SysJob.java | 2 +- .../java/com/pig4cloud/pigx/daemon/quartz/entity/SysJobLog.java | 2 +- .../com/pig4cloud/pigx/daemon/quartz/event/SysJobEvent.java | 2 +- .../com/pig4cloud/pigx/daemon/quartz/event/SysJobListener.java | 2 +- .../com/pig4cloud/pigx/daemon/quartz/event/SysJobLogEvent.java | 2 +- .../pig4cloud/pigx/daemon/quartz/event/SysJobLogListener.java | 2 +- .../pig4cloud/pigx/daemon/quartz/exception/TaskException.java | 2 +- .../pig4cloud/pigx/daemon/quartz/mapper/SysJobLogMapper.java | 2 +- .../com/pig4cloud/pigx/daemon/quartz/mapper/SysJobMapper.java | 2 +- .../pig4cloud/pigx/daemon/quartz/service/SysJobLogService.java | 2 +- .../com/pig4cloud/pigx/daemon/quartz/service/SysJobService.java | 2 +- .../pigx/daemon/quartz/service/impl/SysJobLogServiceImpl.java | 2 +- .../pigx/daemon/quartz/service/impl/SysJobServiceImpl.java | 2 +- .../pig4cloud/pigx/daemon/quartz/task/SpringBeanTaskDemo.java | 2 +- .../java/com/pig4cloud/pigx/daemon/quartz/util/ITaskInvok.java | 2 +- .../com/pig4cloud/pigx/daemon/quartz/util/JarTaskInvok.java | 2 +- .../pig4cloud/pigx/daemon/quartz/util/JavaClassTaskInvok.java | 2 +- .../pig4cloud/pigx/daemon/quartz/util/JobSecurityValidator.java | 2 +- .../com/pig4cloud/pigx/daemon/quartz/util/RestTaskInvok.java | 2 +- .../pig4cloud/pigx/daemon/quartz/util/SpringBeanTaskInvok.java | 2 +- .../com/pig4cloud/pigx/daemon/quartz/util/TaskInvokUtil.java | 2 +- .../java/com/pig4cloud/pigx/daemon/quartz/util/TaskUtil.java | 2 +- .../main/java/com/pig4cloud/pigx/bi/PigxJimuApplication.java | 2 +- .../pigx-jimu-platform/src/main/resources/logback-spring.xml | 2 +- pigx-visual/pigx-monitor/pom.xml | 2 +- .../java/com/pig4cloud/pigx/monitor/PigxMonitorApplication.java | 2 +- pigx-visual/pigx-monitor/src/main/resources/logback-spring.xml | 2 +- .../java/com/pig4cloud/pigx/mp/PigxMpPlatformApplication.java | 2 +- .../java/com/pig4cloud/pigx/mp/config/WxMpContextHolder.java | 2 +- .../com/pig4cloud/pigx/mp/config/WxMpInRedisConfigStorage.java | 2 +- .../com/pig4cloud/pigx/mp/controller/WxAccountController.java | 2 +- .../pig4cloud/pigx/mp/controller/WxAccountFansController.java | 2 +- .../com/pig4cloud/pigx/mp/controller/WxFansMsgController.java | 2 +- .../java/com/pig4cloud/pigx/mp/controller/WxMenuController.java | 2 +- .../src/main/java/com/pig4cloud/pigx/mp/entity/WxAccount.java | 2 +- .../main/java/com/pig4cloud/pigx/mp/entity/WxAccountFans.java | 2 +- .../main/java/com/pig4cloud/pigx/mp/entity/WxFansMsgRes.java | 2 +- .../src/main/java/com/pig4cloud/pigx/mp/entity/WxMpMenu.java | 2 +- .../java/com/pig4cloud/pigx/mp/mapper/WxAccountFansMapper.java | 2 +- .../main/java/com/pig4cloud/pigx/mp/mapper/WxAccountMapper.java | 2 +- .../java/com/pig4cloud/pigx/mp/mapper/WxAccountTagMapper.java | 2 +- .../java/com/pig4cloud/pigx/mp/mapper/WxFansMsgResMapper.java | 2 +- .../main/java/com/pig4cloud/pigx/mp/mapper/WxMenuMapper.java | 2 +- .../com/pig4cloud/pigx/mp/service/WxAccountFansService.java | 2 +- .../java/com/pig4cloud/pigx/mp/service/WxAccountService.java | 2 +- .../main/java/com/pig4cloud/pigx/mp/service/WxMenuService.java | 2 +- .../pigx/mp/service/impl/WxAccountFansServiceImpl.java | 2 +- .../pig4cloud/pigx/mp/service/impl/WxAccountServiceImpl.java | 2 +- .../com/pig4cloud/pigx/mp/service/impl/WxMenuServiceImpl.java | 2 +- .../pigx-mp-platform/src/main/resources/logback-spring.xml | 2 +- .../src/main/resources/mapper/WxAccountFansMapper.xml | 2 +- .../src/main/resources/mapper/WxAccountMapper.xml | 2 +- .../pigx-mp-platform/src/main/resources/mapper/WxMenuMapper.xml | 2 +- .../java/com/pig4cloud/pigx/pay/PigxPayPlatformApplication.java | 2 +- .../com/pig4cloud/pigx/pay/controller/PayChannelController.java | 2 +- .../pig4cloud/pigx/pay/controller/PayGoodsOrderController.java | 2 +- .../pigx/pay/controller/PayNotifyRecordController.java | 2 +- .../pig4cloud/pigx/pay/controller/PayRefundOrderController.java | 2 +- .../pig4cloud/pigx/pay/controller/PayTradeOrderController.java | 2 +- .../src/main/java/com/pig4cloud/pigx/pay/entity/PayChannel.java | 2 +- .../main/java/com/pig4cloud/pigx/pay/entity/PayGoodsOrder.java | 2 +- .../java/com/pig4cloud/pigx/pay/entity/PayNotifyRecord.java | 2 +- .../main/java/com/pig4cloud/pigx/pay/entity/PayRefundOrder.java | 2 +- .../main/java/com/pig4cloud/pigx/pay/entity/PayTradeOrder.java | 2 +- .../pigx/pay/handler/MessageDuplicateCheckerHandler.java | 2 +- .../com/pig4cloud/pigx/pay/handler/PayNotifyCallbakHandler.java | 2 +- .../pigx/pay/handler/impl/AbstractPayNotifyCallbakHandler.java | 2 +- .../pigx/pay/handler/impl/AbstractPayOrderHandler.java | 2 +- .../pigx/pay/handler/impl/AlipayPayNotifyCallbackHandler.java | 2 +- .../pigx/pay/handler/impl/AlipayWapPayOrderHandler.java | 2 +- .../pay/handler/impl/MessageRedisDuplicateCheckerHandler.java | 2 +- .../pigx/pay/handler/impl/WeChatMpPayOrderHandler.java | 2 +- .../pigx/pay/handler/impl/WeChatPayNotifyCallbackHandler.java | 2 +- .../java/com/pig4cloud/pigx/pay/mapper/PayChannelMapper.java | 2 +- .../java/com/pig4cloud/pigx/pay/mapper/PayGoodsOrderMapper.java | 2 +- .../com/pig4cloud/pigx/pay/mapper/PayNotifyRecordMapper.java | 2 +- .../com/pig4cloud/pigx/pay/mapper/PayRefundOrderMapper.java | 2 +- .../java/com/pig4cloud/pigx/pay/mapper/PayTradeOrderMapper.java | 2 +- .../java/com/pig4cloud/pigx/pay/service/PayChannelService.java | 2 +- .../com/pig4cloud/pigx/pay/service/PayGoodsOrderService.java | 2 +- .../com/pig4cloud/pigx/pay/service/PayNotifyRecordService.java | 2 +- .../com/pig4cloud/pigx/pay/service/PayRefundOrderService.java | 2 +- .../com/pig4cloud/pigx/pay/service/PayTradeOrderService.java | 2 +- .../pig4cloud/pigx/pay/service/impl/PayChannelServiceImpl.java | 2 +- .../pigx/pay/service/impl/PayGoodsOrderServiceImpl.java | 2 +- .../pigx/pay/service/impl/PayNotifyRecordServiceImpl.java | 2 +- .../pigx/pay/service/impl/PayRefundOrderServiceImpl.java | 2 +- .../pigx/pay/service/impl/PayTradeOrderServiceImpl.java | 2 +- .../src/main/resources/mapper/PayChannelMapper.xml | 2 +- .../src/main/resources/mapper/PayGoodsOrderMapper.xml | 2 +- .../src/main/resources/mapper/PayNotifyRecordMapper.xml | 2 +- .../src/main/resources/mapper/PayRefundOrderMapper.xml | 2 +- .../src/main/resources/mapper/PayTradeOrderMapper.xml | 2 +- pigx-visual/pom.xml | 2 +- pom.xml | 2 +- 446 files changed, 446 insertions(+), 446 deletions(-) diff --git a/LICENSE b/LICENSE index a3640ace6..61149cbcb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ - Copyright (c) 2018-2025, lengleng All rights reserved. + Copyright (c) 2018-2026, lengleng All rights reserved. 协议和授权 pigX并非一个开源软件,作者保留全部的权利。 diff --git a/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/dto/AppUserInfo.java b/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/dto/AppUserInfo.java index 68fd258bf..45ac2f48e 100644 --- a/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/dto/AppUserInfo.java +++ b/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/dto/AppUserInfo.java @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/entity/AppRole.java b/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/entity/AppRole.java index ec63649a6..df89ff0be 100644 --- a/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/entity/AppRole.java +++ b/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/entity/AppRole.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/entity/AppSocialDetails.java b/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/entity/AppSocialDetails.java index d5cba8300..c56e33c48 100644 --- a/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/entity/AppSocialDetails.java +++ b/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/entity/AppSocialDetails.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/entity/AppUser.java b/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/entity/AppUser.java index e19428144..98ea669e7 100644 --- a/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/entity/AppUser.java +++ b/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/entity/AppUser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/entity/AppUserRole.java b/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/entity/AppUserRole.java index 1de90b88d..d560c0731 100644 --- a/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/entity/AppUserRole.java +++ b/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/entity/AppUserRole.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/feign/RemoteAppUserService.java b/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/feign/RemoteAppUserService.java index efa4bdfe4..7605a9e56 100644 --- a/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/feign/RemoteAppUserService.java +++ b/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/feign/RemoteAppUserService.java @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/vo/AppRoleExcelVO.java b/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/vo/AppRoleExcelVO.java index 436d9e305..e642897e0 100644 --- a/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/vo/AppRoleExcelVO.java +++ b/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/vo/AppRoleExcelVO.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/vo/AppRoleVO.java b/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/vo/AppRoleVO.java index 89532b06d..bc2b70ece 100644 --- a/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/vo/AppRoleVO.java +++ b/pigx-app-server/pigx-app-server-api/src/main/java/com/pig4cloud/pigx/app/api/vo/AppRoleVO.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppMobileController.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppMobileController.java index 9b93c5ebd..8612179b9 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppMobileController.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppMobileController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppRoleController.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppRoleController.java index fbac78ae6..d4afe10be 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppRoleController.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppRoleController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppSocialDetailsController.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppSocialDetailsController.java index dc8d6c5ea..c3d207be7 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppSocialDetailsController.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppSocialDetailsController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppUserController.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppUserController.java index 73eb55977..a9e5f0cd7 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppUserController.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppUserController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppUserRoleController.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppUserRoleController.java index de1945868..b3f1693ab 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppUserRoleController.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppUserRoleController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/handler/AbstractLoginHandler.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/handler/AbstractLoginHandler.java index 2b2aff986..fd2503be8 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/handler/AbstractLoginHandler.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/handler/AbstractLoginHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/handler/LoginHandler.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/handler/LoginHandler.java index 1137d8f65..e9716b7ab 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/handler/LoginHandler.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/handler/LoginHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/handler/MiniAppLoginHandler.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/handler/MiniAppLoginHandler.java index 29bc6aeaa..737518a1e 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/handler/MiniAppLoginHandler.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/handler/MiniAppLoginHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/handler/SmsLoginHandler.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/handler/SmsLoginHandler.java index 321361bbf..2b4e26a50 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/handler/SmsLoginHandler.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/handler/SmsLoginHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/mapper/AppRoleMapper.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/mapper/AppRoleMapper.java index 9173e7f67..4c1bbaa47 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/mapper/AppRoleMapper.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/mapper/AppRoleMapper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/mapper/AppSocialDetailsMapper.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/mapper/AppSocialDetailsMapper.java index 0dc07fa1d..c5db03aa8 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/mapper/AppSocialDetailsMapper.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/mapper/AppSocialDetailsMapper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/mapper/AppUserMapper.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/mapper/AppUserMapper.java index 37a878e82..4a1284aa9 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/mapper/AppUserMapper.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/mapper/AppUserMapper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/mapper/AppUserRoleMapper.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/mapper/AppUserRoleMapper.java index a789cb6dd..e3a7445f1 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/mapper/AppUserRoleMapper.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/mapper/AppUserRoleMapper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppMobileService.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppMobileService.java index 702efb32a..dec7d91b8 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppMobileService.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppMobileService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppRoleService.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppRoleService.java index a1cb464d4..755faa03a 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppRoleService.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppRoleService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppSocialDetailsService.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppSocialDetailsService.java index 23085ae4c..cd9d142af 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppSocialDetailsService.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppSocialDetailsService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppUserRoleService.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppUserRoleService.java index 23c941411..13c7b85af 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppUserRoleService.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppUserRoleService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppUserService.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppUserService.java index 66655130a..2ddef6fcb 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppUserService.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppUserService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppMobileServiceImpl.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppMobileServiceImpl.java index 12f90baae..3a522ccc5 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppMobileServiceImpl.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppMobileServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppRoleServiceImpl.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppRoleServiceImpl.java index f42186925..7d620e75d 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppRoleServiceImpl.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppRoleServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppSocialDetailsServiceImpl.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppSocialDetailsServiceImpl.java index fdfec8740..47b9bd033 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppSocialDetailsServiceImpl.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppSocialDetailsServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppUserRoleServiceImpl.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppUserRoleServiceImpl.java index bc31adebc..0eeab146e 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppUserRoleServiceImpl.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppUserRoleServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppUserServiceImpl.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppUserServiceImpl.java index 263228999..1ecccb968 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppUserServiceImpl.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppUserServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2025, lengleng All rights reserved. + * Copyright (c) 2018-2026, lengleng All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/pigx-app-server/pigx-app-server-biz/src/main/resources/mapper/AppRoleMapper.xml b/pigx-app-server/pigx-app-server-biz/src/main/resources/mapper/AppRoleMapper.xml index 0da7d0018..e00f6c9e0 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/resources/mapper/AppRoleMapper.xml +++ b/pigx-app-server/pigx-app-server-biz/src/main/resources/mapper/AppRoleMapper.xml @@ -2,7 +2,7 @@