package com.aisi.template.service.impl; import com.aisi.template.domain.dto.RoleDto; import com.aisi.template.domain.dto.RoleQueryDto; import com.aisi.template.domain.entity.SysPermission; import com.aisi.template.domain.entity.SysRole; import com.aisi.template.domain.dto.PageResult; import com.aisi.template.domain.vo.PermissionVo; import com.aisi.template.domain.vo.RoleVo; import com.aisi.template.exception.BusinessException; import com.aisi.template.repository.SysPermissionRepository; import com.aisi.template.repository.SysRoleRepository; import com.aisi.template.service.SysRoleService; import jakarta.persistence.criteria.Predicate; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * 系统角色服务实现类 * 提供角色的 CRUD 操作、权限分配、查询等功能 * * 主要功能: * 1. 角色基本操作:创建、更新、删除、查询 * 2. 权限管理:为角色分配权限、获取角色权限 * 3. 分页查询:支持条件查询和分页 * * @author Claude * @since 2024-04-09 */ @Service @RequiredArgsConstructor public class SysRoleServiceImpl implements SysRoleService { /** * 角色数据访问接口 * 用于角色的数据库操作 */ private final SysRoleRepository roleRepository; /** * 权限数据访问接口 * 用于权限的数据库操作 */ private final SysPermissionRepository permissionRepository; /** * 创建角色 * 步骤: * 1. 校验角色编码是否已存在 * 2. 构建角色实体对象 * 3. 保存到数据库 * * @param roleDto 角色数据传输对象 * @return 创建的角色视图对象 * @throws BusinessException 当角色编码已存在时抛出异常 */ @Override @Transactional(rollbackFor = Exception.class) public RoleVo create(RoleDto roleDto) { // 1. 检查角色编码是否已存在 roleRepository.findByRoleCode(roleDto.getRoleCode()).ifPresent(role -> { throw new BusinessException("角色编码已存在: " + role.getRoleCode()); }); // 2. 构建角色实体 SysRole role = new SysRole(); role.setRoleCode(roleDto.getRoleCode()); role.setRoleName(roleDto.getRoleName()); role.setDescription(roleDto.getDescription()); role.setSortOrder(roleDto.getSortOrder()); role.setStatus(roleDto.getStatus()); // 3. 保存角色到数据库 SysRole savedRole = roleRepository.save(role); return convertToVo(savedRole); } /** * 更新角色 * 步骤: * 1. 检查角色是否存在 * 2. 如果修改了角色编码,检查新编码是否已被使用 * 3. 更新角色信息 * * @param id 角色ID * @param roleDto 角色数据传输对象 * @return 更新后的角色视图对象 * @throws BusinessException 当角色不存在或编码冲突时抛出异常 */ @Override @Transactional(rollbackFor = Exception.class) public RoleVo update(Long id, RoleDto roleDto) { // 1. 查询角色是否存在 SysRole role = roleRepository.findById(id) .orElseThrow(() -> new BusinessException("角色不存在: " + id)); // 2. 如果修改了角色编码,检查新编码是否已被使用 if (!role.getRoleCode().equals(roleDto.getRoleCode())) { roleRepository.findByRoleCode(roleDto.getRoleCode()).ifPresent(r -> { throw new BusinessException("角色编码已存在: " + r.getRoleCode()); }); } // 3. 更新角色信息 role.setRoleCode(roleDto.getRoleCode()); role.setRoleName(roleDto.getRoleName()); role.setDescription(roleDto.getDescription()); role.setSortOrder(roleDto.getSortOrder()); role.setStatus(roleDto.getStatus()); // 4. 保存更新 SysRole savedRole = roleRepository.save(role); return convertToVo(savedRole); } /** * 删除角色 * 步骤: * 1. 检查角色是否存在 * 2. 删除角色(关联的权限关系会自动级联删除) * * 注意:如果角色已分配给用户,需要先解除关联 * * @param id 角色ID * @throws BusinessException 当角色不存在时抛出异常 */ @Override @Transactional(rollbackFor = Exception.class) public void delete(Long id) { // 1. 检查角色是否存在 if (!roleRepository.existsById(id)) { throw new BusinessException("角色不存在: " + id); } // 2. 删除角色 roleRepository.deleteById(id); } /** * 根据ID获取角色 * 步骤: * 1. 查询角色基本信息 * 2. 使用 JOIN FETCH 一次性加载关联的权限(避免 N+1 问题) * 3. 转换为视图对象返回 * * @param id 角色ID * @return 角色视图对象 * @throws BusinessException 当角色不存在时抛出异常 */ @Override public RoleVo getById(Long id) { // 1. 查询角色(使用 JOIN FETCH 避免懒加载问题) SysRole role = roleRepository.findByIdWithPermissions(id) .orElseThrow(() -> new BusinessException("角色不存在: " + id)); // 2. 转换为视图对象 return convertToVo(role); } /** * 获取所有角色 * 步骤: * 1. 查询所有角色 * 2. 使用 JOIN FETCH 预加载权限 * 3. 转换为视图对象列表 * * @return 角色视图对象列表 */ @Override public List getAllRoles() { // 1. 查询所有角色(使用 JOIN FETCH 避免懒加载问题) List roles = roleRepository.findAllWithPermissions(); // 2. 转换为视图对象列表 return roles.stream() .map(this::convertToVo) .collect(Collectors.toList()); } /** * 分页查询角色 * 步骤: * 1. 构建动态查询条件(支持角色编码、名称、状态) * 2. 执行分页查询 * 3. 转换结果为视图对象 * * @param queryDto 查询条件对象 * @param page 页码(从 0 开始) * @param size 每页大小 * @return 分页结果 */ @Override public PageResult queryRoles(RoleQueryDto queryDto, int page, int size) { // 1. 构建动态查询条件 Specification spec = (root, query, cb) -> { List predicates = new ArrayList<>(); // 1.1 角色编码模糊查询 if (queryDto.getRoleCode() != null && !queryDto.getRoleCode().isEmpty()) { predicates.add(cb.like(root.get("roleCode"), "%" + queryDto.getRoleCode() + "%")); } // 1.2 角色名称模糊查询 if (queryDto.getRoleName() != null && !queryDto.getRoleName().isEmpty()) { predicates.add(cb.like(root.get("roleName"), "%" + queryDto.getRoleName() + "%")); } // 1.3 状态精确查询 if (queryDto.getStatus() != null) { predicates.add(cb.equal(root.get("status"), queryDto.getStatus())); } return cb.and(predicates.toArray(new Predicate[0])); }; // 2. 执行分页查询 Page rolePage = roleRepository.findAll(spec, PageRequest.of(page, size)); // 3. 转换为分页结果 return PageResult.of( rolePage.getContent().stream().map(this::convertToVo).collect(Collectors.toList()), rolePage.getTotalElements(), rolePage.getNumber(), rolePage.getSize() ); } /** * 为角色分配权限 * 步骤: * 1. 查询角色是否存在 * 2. 根据权限ID列表查询所有权限 * 3. 清空角色原有的权限 * 4. 添加新的权限 * 5. 保存更新 * * @param roleId 角色ID * @param permissionIds 权限ID列表 * @throws BusinessException 当角色或权限不存在时抛出异常 */ @Override @Transactional(rollbackFor = Exception.class) public void assignPermissions(Long roleId, List permissionIds) { // 1. 检查角色是否存在 SysRole role = roleRepository.findById(roleId) .orElseThrow(() -> new BusinessException("角色不存在: " + roleId)); // 2. 查询所有权限(如果权限不存在会抛出异常) Set permissions = permissionIds.stream() .map(permissionId -> permissionRepository.findById(permissionId) .orElseThrow(() -> new BusinessException("权限不存在: " + permissionId))) .collect(Collectors.toSet()); // 3. 清空原有权限并添加新权限 role.getPermissions().clear(); role.getPermissions().addAll(permissions); // 4. 保存更新 roleRepository.save(role); } /** * 获取角色的权限ID列表 * 步骤: * 1. 查询角色(使用 JOIN FETCH 加载权限) * 2. 提取所有权限的ID * * @param roleId 角色ID * @return 权限ID列表 * @throws BusinessException 当角色不存在时抛出异常 */ @Override public List getRolePermissionIds(Long roleId) { // 1. 查询角色及权限 SysRole role = roleRepository.findByIdWithPermissions(roleId) .orElseThrow(() -> new BusinessException("角色不存在: " + roleId)); // 2. 提取权限ID列表 return role.getPermissions().stream() .map(SysPermission::getId) .collect(Collectors.toList()); } /** * 将角色实体转换为视图对象 * 步骤: * 1. 复制基本信息 * 2. 转换权限列表 * * @param role 角色实体 * @return 角色视图对象 */ private RoleVo convertToVo(SysRole role) { RoleVo vo = new RoleVo(); vo.setId(role.getId()); vo.setRoleCode(role.getRoleCode()); vo.setRoleName(role.getRoleName()); vo.setDescription(role.getDescription()); vo.setSortOrder(role.getSortOrder()); vo.setStatus(role.getStatus()); vo.setCreatedAt(role.getCreatedAt()); vo.setUpdatedAt(role.getUpdatedAt()); // 1. 如果有权限,转换为视图对象 if (role.getPermissions() != null && !role.getPermissions().isEmpty()) { Set permissionVos = role.getPermissions().stream() .map(this::convertToPermissionVo) .collect(Collectors.toSet()); vo.setPermissions(permissionVos); } return vo; } /** * 将权限实体转换为视图对象 * * @param permission 权限实体 * @return 权限视图对象 */ private PermissionVo convertToPermissionVo(SysPermission permission) { PermissionVo vo = new PermissionVo(); vo.setId(permission.getId()); vo.setPermissionCode(permission.getPermissionCode()); vo.setPermissionName(permission.getPermissionName()); vo.setResource(permission.getResource()); vo.setAction(permission.getAction()); vo.setDescription(permission.getDescription()); vo.setStatus(permission.getStatus()); vo.setCreatedAt(permission.getCreatedAt()); vo.setUpdatedAt(permission.getUpdatedAt()); return vo; } }