From 10d4d6104c366bc54440508d3c8f0a30770e3dd6 Mon Sep 17 00:00:00 2001 From: hongawen <83944980@qq.com> Date: Tue, 24 Mar 2026 14:13:45 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../filter/TokenAuthenticationFilter.java | 6 +- .../security/TokenAuthenticationFilter.java | 49 +++++--- .../system/enums/ErrorCodeConstants.java | 6 +- .../system/enums/permission/RoleCodeEnum.java | 4 + .../admin/permission/MenuController.java | 2 +- .../admin/permission/RoleController.java | 3 +- .../dal/mysql/permission/RoleMapper.java | 20 ++- .../oauth2/OAuth2TokenServiceImpl.java | 6 +- .../service/permission/RoleServiceImpl.java | 116 ++++++++---------- 9 files changed, 117 insertions(+), 95 deletions(-) diff --git a/rdms-framework/rdms-spring-boot-starter-security/src/main/java/com/njcn/rdms/framework/security/core/filter/TokenAuthenticationFilter.java b/rdms-framework/rdms-spring-boot-starter-security/src/main/java/com/njcn/rdms/framework/security/core/filter/TokenAuthenticationFilter.java index 7c5737b..f39a00d 100644 --- a/rdms-framework/rdms-spring-boot-starter-security/src/main/java/com/njcn/rdms/framework/security/core/filter/TokenAuthenticationFilter.java +++ b/rdms-framework/rdms-spring-boot-starter-security/src/main/java/com/njcn/rdms/framework/security/core/filter/TokenAuthenticationFilter.java @@ -5,6 +5,7 @@ import cn.hutool.core.util.StrUtil; import com.njcn.rdms.framework.common.biz.system.oauth2.OAuth2TokenCommonApi; import com.njcn.rdms.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO; import com.njcn.rdms.framework.common.exception.ServiceException; +import com.njcn.rdms.framework.common.exception.enums.GlobalErrorCodeConstants; import com.njcn.rdms.framework.common.pojo.CommonResult; import com.njcn.rdms.framework.common.util.json.JsonUtils; import com.njcn.rdms.framework.common.util.servlet.ServletUtils; @@ -79,7 +80,10 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { .setScopes(accessToken.getScopes()) .setExpiresTime(accessToken.getExpiresTime()); } catch (ServiceException serviceException) { - return null; + if (ObjectUtil.equal(serviceException.getCode(), GlobalErrorCodeConstants.UNAUTHORIZED.getCode())) { + return null; + } + throw serviceException; } } diff --git a/rdms-gateway/src/main/java/com/njcn/rdms/gateway/filter/security/TokenAuthenticationFilter.java b/rdms-gateway/src/main/java/com/njcn/rdms/gateway/filter/security/TokenAuthenticationFilter.java index ee20dfa..b8c5380 100644 --- a/rdms-gateway/src/main/java/com/njcn/rdms/gateway/filter/security/TokenAuthenticationFilter.java +++ b/rdms-gateway/src/main/java/com/njcn/rdms/gateway/filter/security/TokenAuthenticationFilter.java @@ -6,15 +6,18 @@ import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.njcn.rdms.framework.common.biz.system.oauth2.OAuth2TokenCommonApi; import com.njcn.rdms.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO; +import com.njcn.rdms.framework.common.exception.ServiceException; +import com.njcn.rdms.framework.common.exception.enums.GlobalErrorCodeConstants; import com.njcn.rdms.framework.common.pojo.CommonResult; import com.njcn.rdms.framework.common.util.date.LocalDateTimeUtils; import com.njcn.rdms.framework.common.util.json.JsonUtils; import com.njcn.rdms.gateway.util.SecurityFrameworkUtils; +import com.njcn.rdms.gateway.util.WebFrameworkUtils; +import com.njcn.rdms.module.system.enums.ErrorCodeConstants; import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.server.ServerWebExchange; @@ -42,7 +45,7 @@ public class TokenAuthenticationFilter implements GlobalFilter, Ordered { @Override public LoginUser load(String token) { String body = checkAccessToken(token).block(); - return buildUser(body); + return buildUser(body, token); } }); @@ -61,29 +64,36 @@ public class TokenAuthenticationFilter implements GlobalFilter, Ordered { } ServerWebExchange finalExchange = exchange; - return getLoginUser(token).defaultIfEmpty(LOGIN_USER_EMPTY).flatMap(user -> { - if (user == LOGIN_USER_EMPTY - || user.getExpiresTime() == null - || LocalDateTimeUtils.beforeNow(user.getExpiresTime())) { - return chain.filter(finalExchange); - } + return getLoginUser(token) + .flatMap(user -> { + if (user.getExpiresTime() == null) { + return chain.filter(finalExchange); + } + if (LocalDateTimeUtils.beforeNow(user.getExpiresTime())) { + loginUserCache.invalidate(token); + return WebFrameworkUtils.writeJSON(finalExchange, + CommonResult.error(ErrorCodeConstants.OAUTH2_ACCESS_TOKEN_EXPIRE)); + } - SecurityFrameworkUtils.setLoginUser(finalExchange, user); - ServerWebExchange newExchange = finalExchange.mutate() - .request(builder -> SecurityFrameworkUtils.setLoginUserHeader(builder, user)) - .build(); - return chain.filter(newExchange); - }); + SecurityFrameworkUtils.setLoginUser(finalExchange, user); + ServerWebExchange newExchange = finalExchange.mutate() + .request(builder -> SecurityFrameworkUtils.setLoginUserHeader(builder, user)) + .build(); + return chain.filter(newExchange); + }) + .switchIfEmpty(Mono.defer(() -> chain.filter(finalExchange))) + .onErrorResume(ServiceException.class, ex -> + WebFrameworkUtils.writeJSON(finalExchange, CommonResult.error(ex))); } private Mono getLoginUser(String token) { LoginUser localUser = loginUserCache.getIfPresent(token); if (localUser != null) { - return Mono.just(localUser); + return localUser == LOGIN_USER_EMPTY ? Mono.empty() : Mono.just(localUser); } return checkAccessToken(token).flatMap((Function>) body -> { - LoginUser remoteUser = buildUser(body); + LoginUser remoteUser = buildUser(body, token); if (remoteUser != null) { loginUserCache.put(token, remoteUser); return Mono.just(remoteUser); @@ -99,16 +109,17 @@ public class TokenAuthenticationFilter implements GlobalFilter, Ordered { .bodyToMono(String.class); } - private LoginUser buildUser(String body) { + private LoginUser buildUser(String body, String token) { CommonResult result = JsonUtils.parseObject(body, CHECK_RESULT_TYPE_REFERENCE); if (result == null) { return null; } if (result.isError()) { - if (Objects.equals(result.getCode(), HttpStatus.UNAUTHORIZED.value())) { + if (Objects.equals(result.getCode(), GlobalErrorCodeConstants.UNAUTHORIZED.getCode())) { + loginUserCache.put(token, LOGIN_USER_EMPTY); return LOGIN_USER_EMPTY; } - return null; + throw new ServiceException(result.getCode(), result.getMsg()); } OAuth2AccessTokenCheckRespDTO tokenInfo = result.getData(); diff --git a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/ErrorCodeConstants.java b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/ErrorCodeConstants.java index a95198d..6a37b68 100644 --- a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/ErrorCodeConstants.java +++ b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/ErrorCodeConstants.java @@ -36,7 +36,7 @@ public interface ErrorCodeConstants { ErrorCode ROLE_NOT_EXISTS = new ErrorCode(1_002_002_000, "角色不存在"); ErrorCode ROLE_NAME_DUPLICATE = new ErrorCode(1_002_002_001, "已经存在名为【{}】的角色"); ErrorCode ROLE_CODE_DUPLICATE = new ErrorCode(1_002_002_002, "已经存在标识为【{}】的角色"); - ErrorCode ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE = new ErrorCode(1_002_002_003, "不能操作类型为系统内置的角色"); + ErrorCode ROLE_CAN_NOT_DELETE_SYSTEM_TYPE_ROLE = new ErrorCode(1_002_002_003, "不能删除类型为系统内置的角色"); ErrorCode ROLE_IS_DISABLE = new ErrorCode(1_002_002_004, "名字为【{}】的角色已被禁用"); ErrorCode ROLE_ADMIN_CODE_ERROR = new ErrorCode(1_002_002_005, "标识【{}】不能使用"); @@ -109,6 +109,10 @@ public interface ErrorCodeConstants { ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1_002_022_000, "code 不存在"); ErrorCode OAUTH2_CODE_EXPIRE = new ErrorCode(1_002_022_001, "code 已过期"); + // ========== OAuth2 Token 1-002-023-000 ========= + ErrorCode OAUTH2_ACCESS_TOKEN_EXPIRE = new ErrorCode(1_002_023_000, "访问令牌已过期"); + ErrorCode OAUTH2_REFRESH_TOKEN_EXPIRE = new ErrorCode(1_002_023_001, "刷新令牌已过期"); + // ========== 站内信模版 1-002-026-000 ========== ErrorCode NOTIFY_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_026_000, "站内信模版不存在"); diff --git a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/permission/RoleCodeEnum.java b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/permission/RoleCodeEnum.java index 67d647e..56791d7 100644 --- a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/permission/RoleCodeEnum.java +++ b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/permission/RoleCodeEnum.java @@ -18,4 +18,8 @@ public enum RoleCodeEnum { return ObjectUtils.equalsAny(code, SUPER_ADMIN.getCode()); } + public static boolean isBuiltIn(String code) { + return ObjectUtils.equalsAny(code, SUPER_ADMIN.getCode(), CRM_ADMIN.getCode()); + } + } diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/permission/MenuController.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/permission/MenuController.java index 138590f..5384dcf 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/permission/MenuController.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/permission/MenuController.java @@ -82,7 +82,7 @@ public class MenuController { return success(BeanUtils.toBean(list, MenuRespVO.class)); } - @GetMapping({"/list-all-simple", "simple-list"}) + @GetMapping("/simple-list") @Operation(summary = "获取菜单精简信息列表", description = "只包含已启用的菜单,用于【角色分配菜单】功能的选项") public CommonResult> getSimpleMenuList() { List list = menuService.getMenuList(new MenuListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus())); diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/permission/RoleController.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/permission/RoleController.java index 25c0452..3423788 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/permission/RoleController.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/permission/RoleController.java @@ -84,11 +84,12 @@ public class RoleController { @Operation(summary = "获得角色分页") @PreAuthorize("@ss.hasPermission('system:role:query')") public CommonResult> getRolePage(RolePageReqVO pageReqVO) { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); PageResult pageResult = roleService.getRolePage(pageReqVO); return success(BeanUtils.toBean(pageResult, RoleRespVO.class)); } - @GetMapping({"/list-all-simple", "/simple-list"}) + @GetMapping("/simple-list") @Operation(summary = "获取角色精简信息列表", description = "只包含被开启的角色,主要用于前端的下拉选项") public CommonResult> getSimpleRoleList() { List list = roleService.getRoleListByStatus(singleton(CommonStatusEnum.ENABLE.getStatus())); diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/mysql/permission/RoleMapper.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/mysql/permission/RoleMapper.java index be6bfd7..00bd154 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/mysql/permission/RoleMapper.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/dal/mysql/permission/RoleMapper.java @@ -8,6 +8,7 @@ import com.njcn.rdms.module.system.controller.admin.permission.vo.role.RolePageR import com.njcn.rdms.module.system.dal.dataobject.permission.RoleDO; import org.apache.ibatis.annotations.Mapper; import org.springframework.lang.Nullable; +import org.springframework.util.StringUtils; import java.util.Collection; import java.util.List; @@ -16,12 +17,21 @@ import java.util.List; public interface RoleMapper extends BaseMapperX { default PageResult selectPage(RolePageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(RoleDO::getName, reqVO.getName()) - .likeIfPresent(RoleDO::getCode, reqVO.getCode()) - .eqIfPresent(RoleDO::getStatus, reqVO.getStatus()) + LambdaQueryWrapperX queryWrapper = new LambdaQueryWrapperX<>(); + boolean hasName = StringUtils.hasText(reqVO.getName()); + boolean hasCode = StringUtils.hasText(reqVO.getCode()); + if (hasName && hasCode) { + queryWrapper.and(wrapper -> wrapper.like(RoleDO::getName, reqVO.getName()) + .or() + .like(RoleDO::getCode, reqVO.getCode())); + } else { + queryWrapper.likeIfPresent(RoleDO::getName, reqVO.getName()) + .likeIfPresent(RoleDO::getCode, reqVO.getCode()); + } + queryWrapper.eqIfPresent(RoleDO::getStatus, reqVO.getStatus()) .betweenIfPresent(BaseDO::getCreateTime, reqVO.getCreateTime()) - .orderByAsc(RoleDO::getSort)); + .orderByAsc(RoleDO::getSort); + return selectPage(reqVO, queryWrapper); } default RoleDO selectByName(String name) { diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/oauth2/OAuth2TokenServiceImpl.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/oauth2/OAuth2TokenServiceImpl.java index cd0584a..d453aa6 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/oauth2/OAuth2TokenServiceImpl.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/oauth2/OAuth2TokenServiceImpl.java @@ -22,6 +22,7 @@ import com.njcn.rdms.module.system.dal.dataobject.user.AdminUserDO; import com.njcn.rdms.module.system.dal.mysql.oauth2.OAuth2AccessTokenMapper; import com.njcn.rdms.module.system.dal.mysql.oauth2.OAuth2RefreshTokenMapper; import com.njcn.rdms.module.system.dal.redis.oauth2.OAuth2AccessTokenRedisDAO; +import com.njcn.rdms.module.system.enums.ErrorCodeConstants; import com.njcn.rdms.module.system.service.user.AdminUserService; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; @@ -33,6 +34,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.exception0; import static com.njcn.rdms.framework.common.util.collection.CollectionUtils.convertSet; @@ -93,7 +95,7 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService { // 已过期的情况下,删除刷新令牌 if (DateUtils.isExpired(refreshTokenDO.getExpiresTime())) { oauth2RefreshTokenMapper.deleteById(refreshTokenDO.getId()); - throw exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "刷新令牌已过期"); + throw exception(ErrorCodeConstants.OAUTH2_REFRESH_TOKEN_EXPIRE); } // 创建访问令牌 @@ -134,7 +136,7 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService { throw exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "访问令牌不存在"); } if (DateUtils.isExpired(accessTokenDO.getExpiresTime())) { - throw exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "访问令牌已过期"); + throw exception(ErrorCodeConstants.OAUTH2_ACCESS_TOKEN_EXPIRE); } return accessTokenDO; } diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/permission/RoleServiceImpl.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/permission/RoleServiceImpl.java index 0f03a38..b5f2661 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/permission/RoleServiceImpl.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/permission/RoleServiceImpl.java @@ -5,6 +5,10 @@ import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.extra.spring.SpringUtil; +import com.google.common.annotations.VisibleForTesting; +import com.mzt.logapi.context.LogRecordContext; +import com.mzt.logapi.service.impl.DiffParseFunction; +import com.mzt.logapi.starter.annotation.LogRecord; import com.njcn.rdms.framework.common.enums.CommonStatusEnum; import com.njcn.rdms.framework.common.pojo.PageResult; import com.njcn.rdms.framework.common.util.collection.CollectionUtils; @@ -16,10 +20,6 @@ import com.njcn.rdms.module.system.dal.mysql.permission.RoleMapper; import com.njcn.rdms.module.system.dal.redis.RedisKeyConstants; import com.njcn.rdms.module.system.enums.permission.RoleCodeEnum; import com.njcn.rdms.module.system.enums.permission.RoleTypeEnum; -import com.google.common.annotations.VisibleForTesting; -import com.mzt.logapi.context.LogRecordContext; -import com.mzt.logapi.service.impl.DiffParseFunction; -import com.mzt.logapi.starter.annotation.LogRecord; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.cache.annotation.CacheEvict; @@ -35,14 +35,20 @@ import java.util.Map; import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.njcn.rdms.framework.common.util.collection.CollectionUtils.convertMap; -import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.*; -import static com.njcn.rdms.module.system.enums.LogRecordConstants.*; +import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.ROLE_ADMIN_CODE_ERROR; +import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.ROLE_CAN_NOT_DELETE_SYSTEM_TYPE_ROLE; +import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.ROLE_CODE_DUPLICATE; +import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.ROLE_IS_DISABLE; +import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.ROLE_NAME_DUPLICATE; +import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.ROLE_NOT_EXISTS; +import static com.njcn.rdms.module.system.enums.LogRecordConstants.SYSTEM_ROLE_CREATE_SUB_TYPE; +import static com.njcn.rdms.module.system.enums.LogRecordConstants.SYSTEM_ROLE_CREATE_SUCCESS; +import static com.njcn.rdms.module.system.enums.LogRecordConstants.SYSTEM_ROLE_DELETE_SUB_TYPE; +import static com.njcn.rdms.module.system.enums.LogRecordConstants.SYSTEM_ROLE_DELETE_SUCCESS; +import static com.njcn.rdms.module.system.enums.LogRecordConstants.SYSTEM_ROLE_TYPE; +import static com.njcn.rdms.module.system.enums.LogRecordConstants.SYSTEM_ROLE_UPDATE_SUB_TYPE; +import static com.njcn.rdms.module.system.enums.LogRecordConstants.SYSTEM_ROLE_UPDATE_SUCCESS; -/** - * 角色 Service 实现类 - * - * @author hongawen - */ @Service @Slf4j public class RoleServiceImpl implements RoleService { @@ -58,16 +64,13 @@ public class RoleServiceImpl implements RoleService { @LogRecord(type = SYSTEM_ROLE_TYPE, subType = SYSTEM_ROLE_CREATE_SUB_TYPE, bizNo = "{{#role.id}}", success = SYSTEM_ROLE_CREATE_SUCCESS) public Long createRole(RoleSaveReqVO createReqVO, Integer type) { - // 1. 校验角色 validateRoleDuplicate(createReqVO.getName(), createReqVO.getCode(), null); - // 2. 插入到数据库 RoleDO role = BeanUtils.toBean(createReqVO, RoleDO.class) .setType(ObjectUtil.defaultIfNull(type, RoleTypeEnum.CUSTOM.getType())) .setStatus(ObjUtil.defaultIfNull(createReqVO.getStatus(), CommonStatusEnum.ENABLE.getStatus())); roleMapper.insert(role); - // 3. 记录操作日志上下文 LogRecordContext.putVariable("role", role); return role.getId(); } @@ -77,16 +80,16 @@ public class RoleServiceImpl implements RoleService { @LogRecord(type = SYSTEM_ROLE_TYPE, subType = SYSTEM_ROLE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", success = SYSTEM_ROLE_UPDATE_SUCCESS) public void updateRole(RoleSaveReqVO updateReqVO) { - // 1.1 校验是否可以更新 - RoleDO role = validateRoleForUpdate(updateReqVO.getId()); - // 1.2 校验角色的唯一字段是否重复 - validateRoleDuplicate(updateReqVO.getName(), updateReqVO.getCode(), updateReqVO.getId()); + RoleDO role = validateRoleExists(updateReqVO.getId()); + String effectiveCode = shouldPreserveBuiltInCode(role) ? role.getCode() : updateReqVO.getCode(); + validateRoleDuplicate(updateReqVO.getName(), effectiveCode, updateReqVO.getId()); - // 2. 更新到数据库 RoleDO updateObj = BeanUtils.toBean(updateReqVO, RoleDO.class); + if (shouldPreserveBuiltInCode(role)) { + updateObj.setCode(role.getCode()); + } roleMapper.updateById(updateObj); - // 3. 记录操作日志上下文 LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(role, RoleSaveReqVO.class)); LogRecordContext.putVariable("role", role); } @@ -97,15 +100,11 @@ public class RoleServiceImpl implements RoleService { @LogRecord(type = SYSTEM_ROLE_TYPE, subType = SYSTEM_ROLE_DELETE_SUB_TYPE, bizNo = "{{#id}}", success = SYSTEM_ROLE_DELETE_SUCCESS) public void deleteRole(Long id) { - // 1. 校验是否可以更新 - RoleDO role = validateRoleForUpdate(id); + RoleDO role = validateRoleForDelete(id); - // 2.1 标记删除 roleMapper.deleteById(id); - // 2.2 删除相关数据 permissionService.processRoleDeleted(id); - // 3. 记录操作日志上下文 LogRecordContext.putVariable("role", role); } @@ -113,78 +112,73 @@ public class RoleServiceImpl implements RoleService { @Transactional(rollbackFor = Exception.class) @CacheEvict(value = RedisKeyConstants.ROLE, allEntries = true) public void deleteRoleList(List ids) { - // 1. 校验是否可以删除 - ids.forEach(this::validateRoleForUpdate); + ids.forEach(this::validateRoleForDelete); - // 2.1 标记删除 roleMapper.deleteByIds(ids); - // 2.2 删除相关数据 - ids.forEach(id -> permissionService.processRoleDeleted(id)); + ids.forEach(permissionService::processRoleDeleted); } - /** - * 校验角色的唯一字段是否重复 - * - * 1. 是否存在相同名字的角色 - * 2. 是否存在相同编码的角色 - * - * @param name 角色名字 - * @param code 角色额编码 - * @param id 角色编号 - */ @VisibleForTesting void validateRoleDuplicate(String name, String code, Long id) { - // 0. 超级管理员,不允许创建 - if (RoleCodeEnum.isSuperAdmin(code)) { - throw exception(ROLE_ADMIN_CODE_ERROR, code); + if (RoleCodeEnum.isBuiltIn(code)) { + if (id == null) { + throw exception(ROLE_ADMIN_CODE_ERROR, code); + } + RoleDO currentRole = roleMapper.selectById(id); + if (currentRole == null || !ObjUtil.equal(currentRole.getCode(), code)) { + throw exception(ROLE_ADMIN_CODE_ERROR, code); + } } - // 1. 该 name 名字被其它角色所使用 + RoleDO role = roleMapper.selectByName(name); if (role != null && !role.getId().equals(id)) { throw exception(ROLE_NAME_DUPLICATE, name); } - // 2. 是否存在相同编码的角色 + if (!StringUtils.hasText(code)) { return; } - // 该 code 编码被其它角色所使用 role = roleMapper.selectByCode(code); if (role != null && !role.getId().equals(id)) { throw exception(ROLE_CODE_DUPLICATE, code); } } - /** - * 校验角色是否可以被更新 - * - * @param id 角色编号 - */ @VisibleForTesting - RoleDO validateRoleForUpdate(Long id) { + RoleDO validateRoleExists(Long id) { RoleDO role = roleMapper.selectById(id); if (role == null) { throw exception(ROLE_NOT_EXISTS); } - // 内置角色,不允许删除 + return role; + } + + @VisibleForTesting + RoleDO validateRoleForDelete(Long id) { + RoleDO role = validateRoleExists(id); if (RoleTypeEnum.SYSTEM.getType().equals(role.getType())) { - throw exception(ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE); + throw exception(ROLE_CAN_NOT_DELETE_SYSTEM_TYPE_ROLE); } return role; } + private boolean shouldPreserveBuiltInCode(RoleDO role) { + return role != null + && RoleTypeEnum.SYSTEM.getType().equals(role.getType()) + && RoleCodeEnum.isBuiltIn(role.getCode()); + } + @Override public RoleDO getRole(Long id) { return roleMapper.selectById(id); } @Override - @Cacheable(value = RedisKeyConstants.ROLE, key = "#id", - unless = "#result == null") + @Cacheable(value = RedisKeyConstants.ROLE, key = "#id", unless = "#result == null") public RoleDO getRoleFromCache(Long id) { return roleMapper.selectById(id); } - @Override public List getRoleListByStatus(Collection statuses) { return roleMapper.selectListByStatus(statuses); @@ -208,7 +202,6 @@ public class RoleServiceImpl implements RoleService { if (CollectionUtil.isEmpty(ids)) { return Collections.emptyList(); } - // 这里采用 for 循环从缓存中获取,主要考虑 Spring CacheManager 无法批量操作的问题 RoleServiceImpl self = getSelf(); return CollectionUtils.convertList(ids, self::getRoleFromCache); } @@ -235,10 +228,8 @@ public class RoleServiceImpl implements RoleService { if (CollUtil.isEmpty(ids)) { return; } - // 获得角色信息 List roles = roleMapper.selectByIds(ids); Map roleMap = convertMap(roles, RoleDO::getId); - // 校验 ids.forEach(id -> { RoleDO role = roleMap.get(id); if (role == null) { @@ -250,11 +241,6 @@ public class RoleServiceImpl implements RoleService { }); } - /** - * 获得自身的代理对象,解决 AOP 生效问题 - * - * @return 自己 - */ private RoleServiceImpl getSelf() { return SpringUtil.getBean(getClass()); }