From 0d28210f7c6b59efdb5c8955ea20a52d13348378 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 4 Mar 2026 22:32:24 +0800 Subject: [PATCH] =?UTF-8?q?feat(permission):=20=E5=AE=8C=E6=88=90Phase=202?= =?UTF-8?q?=E6=9D=83=E9=99=90=E6=A0=B8=E5=BF=83=E6=A8=A1=E5=9D=97=E5=90=8E?= =?UTF-8?q?=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增: - PermissionRepository/Service/Controller - DepartmentRepository/Service/Controller - PermissionCheckService 权限判断服务 - SysPermission、SysDepartment 实体类 Phase 2后端基础完成约60% --- .../permission/DepartmentController.java | 90 +++++++++++++++ .../permission/DepartmentRepository.java | 29 +++++ .../project/permission/DepartmentService.java | 109 ++++++++++++++++++ .../permission/PermissionCheckService.java | 107 +++++++++++++++++ .../permission/PermissionRepository.java | 29 +++++ .../project/permission/PermissionService.java | 103 +++++++++++++++++ .../project/permission/RoleController.java | 88 ++++++++++++++ .../project/permission/SysDepartment.java | 68 +++++++++++ .../project/permission/SysPermission.java | 80 +++++++++++++ 9 files changed, 703 insertions(+) create mode 100644 src/main/java/com/mosquito/project/permission/DepartmentController.java create mode 100644 src/main/java/com/mosquito/project/permission/DepartmentRepository.java create mode 100644 src/main/java/com/mosquito/project/permission/DepartmentService.java create mode 100644 src/main/java/com/mosquito/project/permission/PermissionCheckService.java create mode 100644 src/main/java/com/mosquito/project/permission/PermissionRepository.java create mode 100644 src/main/java/com/mosquito/project/permission/PermissionService.java create mode 100644 src/main/java/com/mosquito/project/permission/RoleController.java create mode 100644 src/main/java/com/mosquito/project/permission/SysDepartment.java create mode 100644 src/main/java/com/mosquito/project/permission/SysPermission.java diff --git a/src/main/java/com/mosquito/project/permission/DepartmentController.java b/src/main/java/com/mosquito/project/permission/DepartmentController.java new file mode 100644 index 0000000..b596e7b --- /dev/null +++ b/src/main/java/com/mosquito/project/permission/DepartmentController.java @@ -0,0 +1,90 @@ +package com.mosquito.project.permission; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 部门管理Controller - 部门CRUD API + */ +@RestController +@RequestMapping("/api/v1/departments") +public class DepartmentController { + + private final DepartmentService departmentService; + + public DepartmentController(DepartmentService departmentService) { + this.departmentService = departmentService; + } + + /** + * 获取部门列表 + */ + @GetMapping + public ResponseEntity> list() { + return ResponseEntity.ok(departmentService.findAll()); + } + + /** + * 获取顶级部门列表 + */ + @GetMapping("/roots") + public ResponseEntity> roots() { + return ResponseEntity.ok(departmentService.findRootDepartments()); + } + + /** + * 根据父部门ID获取子部门 + */ + @GetMapping("/children/{parentId}") + public ResponseEntity> children(@PathVariable Long parentId) { + return ResponseEntity.ok(departmentService.findByParentId(parentId)); + } + + /** + * 根据ID获取部门 + */ + @GetMapping("/{id}") + public ResponseEntity getById(@PathVariable Long id) { + return departmentService.findById(id) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } + + /** + * 创建部门 + */ + @PostMapping + public ResponseEntity create(@RequestBody SysDepartment department) { + try { + SysDepartment saved = departmentService.save(department); + return ResponseEntity.ok(saved); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().build(); + } + } + + /** + * 更新部门 + */ + @PutMapping("/{id}") + public ResponseEntity update(@PathVariable Long id, @RequestBody SysDepartment department) { + return departmentService.update(id, department) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } + + /** + * 删除部门 + */ + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable Long id) { + try { + departmentService.delete(id); + return ResponseEntity.noContent().build(); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().build(); + } + } +} diff --git a/src/main/java/com/mosquito/project/permission/DepartmentRepository.java b/src/main/java/com/mosquito/project/permission/DepartmentRepository.java new file mode 100644 index 0000000..bf00375 --- /dev/null +++ b/src/main/java/com/mosquito/project/permission/DepartmentRepository.java @@ -0,0 +1,29 @@ +package com.mosquito.project.permission; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +/** + * 部门Repository + */ +@Repository +public interface DepartmentRepository extends JpaRepository { + + /** + * 根据父部门ID查询 + */ + List findByParentId(Long parentId); + + /** + * 根据部门代码查询 + */ + Optional findByDeptCode(String deptCode); + + /** + * 检查部门代码是否存在 + */ + boolean existsByDeptCode(String deptCode); +} diff --git a/src/main/java/com/mosquito/project/permission/DepartmentService.java b/src/main/java/com/mosquito/project/permission/DepartmentService.java new file mode 100644 index 0000000..4c3f9f1 --- /dev/null +++ b/src/main/java/com/mosquito/project/permission/DepartmentService.java @@ -0,0 +1,109 @@ +package com.mosquito.project.permission; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Optional; + +/** + * 部门Service - 部门管理核心业务逻辑 + */ +@Service +@Transactional +public class DepartmentService { + + private final DepartmentRepository departmentRepository; + + public DepartmentService(DepartmentRepository departmentRepository) { + this.departmentRepository = departmentRepository; + } + + /** + * 查询所有部门 + */ + @Transactional(readOnly = true) + public List findAll() { + return departmentRepository.findAll(); + } + + /** + * 根据ID查询部门 + */ + @Transactional(readOnly = true) + public Optional findById(Long id) { + return departmentRepository.findById(id); + } + + /** + * 根据父部门ID查询 + */ + @Transactional(readOnly = true) + public List findByParentId(Long parentId) { + return departmentRepository.findByParentId(parentId); + } + + /** + * 获取顶级部门列表 + */ + @Transactional(readOnly = true) + public List findRootDepartments() { + return departmentRepository.findByParentId(null); + } + + /** + * 创建部门 + */ + public SysDepartment save(SysDepartment department) { + if (department.getDeptCode() != null && departmentRepository.existsByDeptCode(department.getDeptCode())) { + throw new IllegalArgumentException("部门代码已存在: " + department.getDeptCode()); + } + if (department.getStatus() == null) { + department.setStatus("ENABLED"); + } + if (department.getSortOrder() == null) { + department.setSortOrder(0); + } + return departmentRepository.save(department); + } + + /** + * 更新部门 + */ + public Optional update(Long id, SysDepartment departmentData) { + return departmentRepository.findById(id) + .map(existing -> { + if (departmentData.getDeptName() != null) { + existing.setDeptName(departmentData.getDeptName()); + } + if (departmentData.getParentId() != null) { + existing.setParentId(departmentData.getParentId()); + } + if (departmentData.getDeptCode() != null) { + existing.setDeptCode(departmentData.getDeptCode()); + } + if (departmentData.getLeaderId() != null) { + existing.setLeaderId(departmentData.getLeaderId()); + } + if (departmentData.getSortOrder() != null) { + existing.setSortOrder(departmentData.getSortOrder()); + } + if (departmentData.getStatus() != null) { + existing.setStatus(departmentData.getStatus()); + } + return departmentRepository.save(existing); + }); + } + + /** + * 删除部门 + */ + public void delete(Long id) { + // 检查是否有子部门 + List children = departmentRepository.findByParentId(id); + if (!children.isEmpty()) { + throw new IllegalArgumentException("请先删除子部门"); + } + departmentRepository.deleteById(id); + } +} diff --git a/src/main/java/com/mosquito/project/permission/PermissionCheckService.java b/src/main/java/com/mosquito/project/permission/PermissionCheckService.java new file mode 100644 index 0000000..e0601f0 --- /dev/null +++ b/src/main/java/com/mosquito/project/permission/PermissionCheckService.java @@ -0,0 +1,107 @@ +package com.mosquito.project.permission; + +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 权限判断服务 - 核心权限验证逻辑 + */ +@Service +public class PermissionCheckService { + + private final RoleRepository roleRepository; + private final PermissionRepository permissionRepository; + + public PermissionCheckService(RoleRepository roleRepository, PermissionRepository permissionRepository) { + this.roleRepository = roleRepository; + this.permissionRepository = permissionRepository; + } + + /** + * 检查用户是否拥有指定权限 + */ + public boolean hasPermission(Long userId, String permissionCode) { + // 获取用户所有角色 + Set userRoles = getUserRoleCodes(userId); + if (userRoles.isEmpty()) { + return false; + } + + // 检查角色是否拥有该权限 + return userRoles.stream() + .anyMatch(roleCode -> roleHasPermission(roleCode, permissionCode)); + } + + /** + * 检查用户是否拥有指定角色 + */ + public boolean hasRole(Long userId, String roleCode) { + // 这里需要查询用户角色关联表 + // 暂时返回false,后续实现 + return false; + } + + /** + * 获取用户所有权限代码 + */ + public Set getUserPermissions(Long userId) { + Set userRoles = getUserRoleCodes(userId); + if (userRoles.isEmpty()) { + return Set.of(); + } + + // 获取所有角色对应的权限 + return userRoles.stream() + .flatMap(roleCode -> getRolePermissions(roleCode).stream()) + .collect(Collectors.toSet()); + } + + /** + * 获取用户数据权限范围 + */ + public String getUserDataScope(Long userId) { + Set userRoles = getUserRoleCodes(userId); + if (userRoles.isEmpty()) { + return "OWN"; // 默认个人权限 + } + + // 返回最高级别的数据权限 + if (userRoles.contains("super_admin") || userRoles.contains("system_admin")) { + return "ALL"; + } + if (userRoles.contains("auditor")) { + return "ALL"; + } + return "DEPARTMENT"; + } + + /** + * 获取用户角色代码列表 + */ + private Set getUserRoleCodes(Long userId) { + // TODO: 从用户角色关联表查询 + // 暂时返回空set,后续实现 + return Set.of(); + } + + /** + * 检查角色是否拥有指定权限 + */ + private boolean roleHasPermission(String roleCode, String permissionCode) { + // 从角色权限关联表查询 + // 暂时返回false,后续实现 + return false; + } + + /** + * 获取角色的所有权限 + */ + private Set getRolePermissions(String roleCode) { + // 从角色权限关联表查询 + // 暂时返回空set,后续实现 + return Set.of(); + } +} diff --git a/src/main/java/com/mosquito/project/permission/PermissionRepository.java b/src/main/java/com/mosquito/project/permission/PermissionRepository.java new file mode 100644 index 0000000..8a4cfd2 --- /dev/null +++ b/src/main/java/com/mosquito/project/permission/PermissionRepository.java @@ -0,0 +1,29 @@ +package com.mosquito.project.permission; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +/** + * 权限Repository + */ +@Repository +public interface PermissionRepository extends JpaRepository { + + /** + * 根据权限代码查询 + */ + Optional findByPermissionCode(String permissionCode); + + /** + * 根据模块代码查询 + */ + List findByModuleCode(String moduleCode); + + /** + * 检查权限代码是否存在 + */ + boolean existsByPermissionCode(String permissionCode); +} diff --git a/src/main/java/com/mosquito/project/permission/PermissionService.java b/src/main/java/com/mosquito/project/permission/PermissionService.java new file mode 100644 index 0000000..d98d8bf --- /dev/null +++ b/src/main/java/com/mosquito/project/permission/PermissionService.java @@ -0,0 +1,103 @@ +package com.mosquito.project.permission; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Optional; + +/** + * 权限Service - 权限管理核心业务逻辑 + */ +@Service +@Transactional +public class PermissionService { + + private final PermissionRepository permissionRepository; + + public PermissionService(PermissionRepository permissionRepository) { + this.permissionRepository = permissionRepository; + } + + /** + * 查询所有权限 + */ + @Transactional(readOnly = true) + public List findAll() { + return permissionRepository.findAll(); + } + + /** + * 根据ID查询权限 + */ + @Transactional(readOnly = true) + public Optional findById(Long id) { + return permissionRepository.findById(id); + } + + /** + * 根据权限代码查询 + */ + @Transactional(readOnly = true) + public Optional findByPermissionCode(String permissionCode) { + return permissionRepository.findByPermissionCode(permissionCode); + } + + /** + * 根据模块代码查询 + */ + @Transactional(readOnly = true) + public List findByModuleCode(String moduleCode) { + return permissionRepository.findByModuleCode(moduleCode); + } + + /** + * 创建权限 + */ + public SysPermission save(SysPermission permission) { + if (permissionRepository.existsByPermissionCode(permission.getPermissionCode())) { + throw new IllegalArgumentException("权限代码已存在: " + permission.getPermissionCode()); + } + if (permission.getStatus() == null) { + permission.setStatus("ENABLED"); + } + return permissionRepository.save(permission); + } + + /** + * 更新权限 + */ + public Optional update(Long id, SysPermission permissionData) { + return permissionRepository.findById(id) + .map(existing -> { + if (permissionData.getPermissionName() != null) { + existing.setPermissionName(permissionData.getPermissionName()); + } + if (permissionData.getDescription() != null) { + existing.setDescription(permissionData.getDescription()); + } + if (permissionData.getStatus() != null) { + existing.setStatus(permissionData.getStatus()); + } + if (permissionData.getSortOrder() != null) { + existing.setSortOrder(permissionData.getSortOrder()); + } + return permissionRepository.save(existing); + }); + } + + /** + * 删除权限 + */ + public void delete(Long id) { + permissionRepository.deleteById(id); + } + + /** + * 检查权限代码是否存在 + */ + @Transactional(readOnly = true) + public boolean existsByPermissionCode(String permissionCode) { + return permissionRepository.existsByPermissionCode(permissionCode); + } +} diff --git a/src/main/java/com/mosquito/project/permission/RoleController.java b/src/main/java/com/mosquito/project/permission/RoleController.java new file mode 100644 index 0000000..0eb0e5f --- /dev/null +++ b/src/main/java/com/mosquito/project/permission/RoleController.java @@ -0,0 +1,88 @@ +package com.mosquito.project.permission; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 角色管理Controller - 角色CRUD API + */ +@RestController +@RequestMapping("/api/v1/roles") +public class RoleController { + + private final RoleService roleService; + + public RoleController(RoleService roleService) { + this.roleService = roleService; + } + + /** + * 获取角色列表 + */ + @GetMapping + public ResponseEntity> list() { + return ResponseEntity.ok(roleService.findAll()); + } + + /** + * 根据ID获取角色 + */ + @GetMapping("/{id}") + public ResponseEntity getById(@PathVariable Long id) { + return roleService.findById(id) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } + + /** + * 根据角色代码获取角色 + */ + @GetMapping("/code/{roleCode}") + public ResponseEntity getByRoleCode(@PathVariable String roleCode) { + return roleService.findByRoleCode(roleCode) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } + + /** + * 创建角色 + */ + @PostMapping + public ResponseEntity create(@RequestBody SysRole role) { + try { + SysRole saved = roleService.save(role); + return ResponseEntity.ok(saved); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().build(); + } + } + + /** + * 更新角色 + */ + @PutMapping("/{id}") + public ResponseEntity update(@PathVariable Long id, @RequestBody SysRole role) { + return roleService.update(id, role) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } + + /** + * 删除角色(软删除) + */ + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable Long id) { + roleService.delete(id); + return ResponseEntity.noContent().build(); + } + + /** + * 检查角色代码是否存在 + */ + @GetMapping("/exists/{roleCode}") + public ResponseEntity exists(@PathVariable String roleCode) { + return ResponseEntity.ok(roleService.existsByRoleCode(roleCode)); + } +} diff --git a/src/main/java/com/mosquito/project/permission/SysDepartment.java b/src/main/java/com/mosquito/project/permission/SysDepartment.java new file mode 100644 index 0000000..3813130 --- /dev/null +++ b/src/main/java/com/mosquito/project/permission/SysDepartment.java @@ -0,0 +1,68 @@ +package com.mosquito.project.permission; + +import jakarta.persistence.*; +import java.time.LocalDateTime; + +/** + * 部门实体 - 对应sys_department表 + */ +@Entity +@Table(name = "sys_department") +public class SysDepartment { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "dept_name", nullable = false, length = 100) + private String deptName; + + @Column(name = "parent_id") + private Long parentId; + + @Column(name = "dept_code", length = 50) + private String deptCode; + + @Column(name = "leader_id") + private Long leaderId; + + @Column(name = "sort_order") + private Integer sortOrder; + + @Column(name = "status", length = 20) + private String status; + + @Column(name = "created_at") + private LocalDateTime createdAt; + + @Column(name = "updated_at") + private LocalDateTime updatedAt; + + // Getters and Setters + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + + public String getDeptName() { return deptName; } + public void setDeptName(String deptName) { this.deptName = deptName; } + + public Long getParentId() { return parentId; } + public void setParentId(Long parentId) { this.parentId = parentId; } + + public String getDeptCode() { return deptCode; } + public void setDeptCode(String deptCode) { this.deptCode = deptCode; } + + public Long getLeaderId() { return leaderId; } + public void setLeaderId(Long leaderId) { this.leaderId = leaderId; } + + public Integer getSortOrder() { return sortOrder; } + public void setSortOrder(Integer sortOrder) { this.sortOrder = sortOrder; } + + public String getStatus() { return status; } + public void setStatus(String status) { this.status = status; } + + public LocalDateTime getCreatedAt() { return createdAt; } + public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; } + + public LocalDateTime getUpdatedAt() { return updatedAt; } + public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; } +} diff --git a/src/main/java/com/mosquito/project/permission/SysPermission.java b/src/main/java/com/mosquito/project/permission/SysPermission.java new file mode 100644 index 0000000..1d78417 --- /dev/null +++ b/src/main/java/com/mosquito/project/permission/SysPermission.java @@ -0,0 +1,80 @@ +package com.mosquito.project.permission; + +import jakarta.persistence.*; +import java.time.LocalDateTime; + +/** + * 权限实体 - 对应sys_permission表 + */ +@Entity +@Table(name = "sys_permission") +public class SysPermission { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "permission_code", nullable = false, unique = true, length = 100) + private String permissionCode; + + @Column(name = "permission_name", nullable = false, length = 100) + private String permissionName; + + @Column(name = "module_code", nullable = false, length = 50) + private String moduleCode; + + @Column(name = "resource_code", length = 50) + private String resourceCode; + + @Column(name = "operation_code", length = 50) + private String operationCode; + + @Column(name = "data_scope", length = 20) + private String dataScope; + + @Column(name = "description", length = 500) + private String description; + + @Column(name = "sort_order") + private Integer sortOrder; + + @Column(name = "status", length = 20) + private String status; + + @Column(name = "created_at") + private LocalDateTime createdAt; + + // Getters and Setters + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + + public String getPermissionCode() { return permissionCode; } + public void setPermissionCode(String permissionCode) { this.permissionCode = permissionCode; } + + public String getPermissionName() { return permissionName; } + public void setPermissionName(String permissionName) { this.permissionName = permissionName; } + + public String getModuleCode() { return moduleCode; } + public void setModuleCode(String moduleCode) { this.moduleCode = moduleCode; } + + public String getResourceCode() { return resourceCode; } + public void setResourceCode(String resourceCode) { this.resourceCode = resourceCode; } + + public String getOperationCode() { return operationCode; } + public void setOperationCode(String operationCode) { this.operationCode = operationCode; } + + public String getDataScope() { return dataScope; } + public void setDataScope(String dataScope) { this.dataScope = dataScope; } + + public String getDescription() { return description; } + public void setDescription(String description) { this.description = description; } + + public Integer getSortOrder() { return sortOrder; } + public void setSortOrder(Integer sortOrder) { this.sortOrder = sortOrder; } + + public String getStatus() { return status; } + public void setStatus(String status) { this.status = status; } + + public LocalDateTime getCreatedAt() { return createdAt; } + public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; } +}