package com.iailab.module.system.service.permission; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.extra.spring.SpringUtil; import com.iailab.framework.common.enums.CommonStatusEnum; import com.iailab.framework.common.pojo.PageResult; import com.iailab.framework.common.util.collection.CollectionUtils; import com.iailab.framework.common.util.object.BeanUtils; import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX; import com.iailab.module.system.controller.admin.permission.vo.role.RolePageReqVO; import com.iailab.module.system.controller.admin.permission.vo.role.RoleSaveReqVO; import com.iailab.module.system.dal.dataobject.permission.RoleDO; import com.iailab.module.system.dal.mysql.permission.RoleMapper; import com.iailab.module.system.dal.redis.RedisKeyConstants; import com.iailab.module.system.enums.permission.DataScopeEnum; import com.iailab.module.system.enums.permission.RoleCodeEnum; import com.iailab.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 lombok.extern.slf4j.Slf4j; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import javax.annotation.Resource; import java.util.*; import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.iailab.framework.common.util.collection.CollectionUtils.convertMap; import static com.iailab.module.system.enums.ErrorCodeConstants.*; import static com.iailab.module.system.enums.LogRecordConstants.*; /** * 角色 Service 实现类 * * @author iailab */ @Service @Slf4j public class RoleServiceImpl implements RoleService { @Resource private PermissionService permissionService; @Resource private RoleMapper roleMapper; @Override @Transactional(rollbackFor = Exception.class) @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(CommonStatusEnum.ENABLE.getStatus()) .setDataScope(DataScopeEnum.ALL.getScope()); // 默认å¯æŸ¥çœ‹æ‰€æœ‰æ•°æ®ã€‚åŽŸå› æ˜¯ï¼Œå¯èƒ½ä¸€äº›é¡¹ç›®ä¸éœ€è¦é¡¹ç›®æƒé™ roleMapper.insert(role); // 3. 记录æ“作日志上下文 LogRecordContext.putVariable("role", role); return role.getId(); } @Override @CacheEvict(value = RedisKeyConstants.ROLE, key = "#updateReqVO.id") @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()); // 2. 更新到数æ®åº“ RoleDO updateObj = BeanUtils.toBean(updateReqVO, RoleDO.class); roleMapper.updateById(updateObj); // 3. 记录æ“作日志上下文 LogRecordContext.putVariable("role", role); } @Override @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id") public void updateRoleDataScope(Long id, Integer dataScope, Set<Long> dataScopeDeptIds) { // æ ¡éªŒæ˜¯å¦å¯ä»¥æ›´æ–° validateRoleForUpdate(id); // æ›´æ–°æ•°æ®èŒƒå›´ RoleDO updateObject = new RoleDO(); updateObject.setId(id); updateObject.setDataScope(dataScope); updateObject.setDataScopeDeptIds(dataScopeDeptIds); roleMapper.updateById(updateObject); } @Override @Transactional(rollbackFor = Exception.class) @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id") @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); // 2.1 æ ‡è®°åˆ é™¤ roleMapper.deleteById(id); // 2.2 åˆ é™¤ç›¸å…³æ•°æ® permissionService.processRoleDeleted(id); // 3. 记录æ“作日志上下文 LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(role, RoleSaveReqVO.class)); LogRecordContext.putVariable("role", role); } /** * æ ¡éªŒè§’è‰²çš„å”¯ä¸€å—段是å¦é‡å¤ * * 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); } // 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 role = roleMapper.selectById(id); if (role == null) { throw exception(ROLE_NOT_EXISTS); } // 内置角色,ä¸å…è®¸åˆ é™¤ if (RoleTypeEnum.SYSTEM.getType().equals(role.getType())) { throw exception(ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE); } return role; } @Override public RoleDO getRole(Long id) { return roleMapper.selectById(id); } @Override @Cacheable(value = RedisKeyConstants.ROLE, key = "#id", unless = "#result == null") public RoleDO getRoleFromCache(Long id) { return roleMapper.selectById(id); } @Override public List<RoleDO> getRoleListByStatus(Collection<Integer> statuses) { return roleMapper.selectListByStatus(statuses); } @Override public List<RoleDO> getRoleList() { return roleMapper.selectList(); } @Override public List<RoleDO> getRoleList(Collection<Long> ids) { if (CollectionUtil.isEmpty(ids)) { return Collections.emptyList(); } return roleMapper.selectBatchIds(ids); } @Override public List<RoleDO> getRoleListFromCache(Collection<Long> ids) { if (CollectionUtil.isEmpty(ids)) { return Collections.emptyList(); } // 这里采用 for 循环从缓å˜ä¸èŽ·å–,主è¦è€ƒè™‘ Spring CacheManager æ— æ³•æ‰¹é‡æ“作的问题 RoleServiceImpl self = getSelf(); return CollectionUtils.convertList(ids, self::getRoleFromCache); } @Override public PageResult<RoleDO> getRolePage(RolePageReqVO reqVO) { return roleMapper.selectPage(reqVO); } @Override public boolean hasAnySuperAdmin(Collection<Long> ids) { if (CollectionUtil.isEmpty(ids)) { return false; } RoleServiceImpl self = getSelf(); return ids.stream().anyMatch(id -> { RoleDO role = self.getRoleFromCache(id); return role != null && RoleCodeEnum.isSuperAdmin(role.getCode()); }); } @Override public void validateRoleList(Collection<Long> ids) { if (CollUtil.isEmpty(ids)) { return; } // èŽ·å¾—è§’è‰²ä¿¡æ¯ List<RoleDO> roles = roleMapper.selectBatchIds(ids); Map<Long, RoleDO> roleMap = convertMap(roles, RoleDO::getId); // æ ¡éªŒ ids.forEach(id -> { RoleDO role = roleMap.get(id); if (role == null) { throw exception(ROLE_NOT_EXISTS); } if (!CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())) { throw exception(ROLE_IS_DISABLE, role.getName()); } }); } @Override public RoleDO getRoleByName(String name) { return roleMapper.selectOne(new LambdaQueryWrapperX<RoleDO>().eq(RoleDO::getName, name)); } @Override public void insert(RoleDO role) { roleMapper.insert(role); } /** * 获得自身的代ç†å¯¹è±¡ï¼Œè§£å†³ AOP 生效问题 * * @return 自己 */ private RoleServiceImpl getSelf() { return SpringUtil.getBean(getClass()); } /** * 查询租户管ç†å‘˜ */ public RoleDO getTenantAdminRole(Long tenantId) { RoleDO roleDO = roleMapper.selectOne(new LambdaQueryWrapperX<RoleDO>().eq(RoleDO::getType, 1L).eq(RoleDO::getTenantId, tenantId)); return roleDO; } }