feat(permission): 完成Phase 2权限核心模块后端

新增:
- PermissionRepository/Service/Controller
- DepartmentRepository/Service/Controller
- PermissionCheckService 权限判断服务
- SysPermission、SysDepartment 实体类

Phase 2后端基础完成约60%
This commit is contained in:
Your Name
2026-03-04 22:32:24 +08:00
parent 18a586df49
commit 0d28210f7c
9 changed files with 703 additions and 0 deletions

View File

@@ -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<SysDepartment>> list() {
return ResponseEntity.ok(departmentService.findAll());
}
/**
* 获取顶级部门列表
*/
@GetMapping("/roots")
public ResponseEntity<List<SysDepartment>> roots() {
return ResponseEntity.ok(departmentService.findRootDepartments());
}
/**
* 根据父部门ID获取子部门
*/
@GetMapping("/children/{parentId}")
public ResponseEntity<List<SysDepartment>> children(@PathVariable Long parentId) {
return ResponseEntity.ok(departmentService.findByParentId(parentId));
}
/**
* 根据ID获取部门
*/
@GetMapping("/{id}")
public ResponseEntity<SysDepartment> getById(@PathVariable Long id) {
return departmentService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
/**
* 创建部门
*/
@PostMapping
public ResponseEntity<SysDepartment> 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<SysDepartment> update(@PathVariable Long id, @RequestBody SysDepartment department) {
return departmentService.update(id, department)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
/**
* 删除部门
*/
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
try {
departmentService.delete(id);
return ResponseEntity.noContent().build();
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
}
}
}

View File

@@ -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<SysDepartment, Long> {
/**
* 根据父部门ID查询
*/
List<SysDepartment> findByParentId(Long parentId);
/**
* 根据部门代码查询
*/
Optional<SysDepartment> findByDeptCode(String deptCode);
/**
* 检查部门代码是否存在
*/
boolean existsByDeptCode(String deptCode);
}

View File

@@ -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<SysDepartment> findAll() {
return departmentRepository.findAll();
}
/**
* 根据ID查询部门
*/
@Transactional(readOnly = true)
public Optional<SysDepartment> findById(Long id) {
return departmentRepository.findById(id);
}
/**
* 根据父部门ID查询
*/
@Transactional(readOnly = true)
public List<SysDepartment> findByParentId(Long parentId) {
return departmentRepository.findByParentId(parentId);
}
/**
* 获取顶级部门列表
*/
@Transactional(readOnly = true)
public List<SysDepartment> 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<SysDepartment> 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<SysDepartment> children = departmentRepository.findByParentId(id);
if (!children.isEmpty()) {
throw new IllegalArgumentException("请先删除子部门");
}
departmentRepository.deleteById(id);
}
}

View File

@@ -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<String> 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<String> getUserPermissions(Long userId) {
Set<String> 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<String> 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<String> getUserRoleCodes(Long userId) {
// TODO: 从用户角色关联表查询
// 暂时返回空set后续实现
return Set.of();
}
/**
* 检查角色是否拥有指定权限
*/
private boolean roleHasPermission(String roleCode, String permissionCode) {
// 从角色权限关联表查询
// 暂时返回false后续实现
return false;
}
/**
* 获取角色的所有权限
*/
private Set<String> getRolePermissions(String roleCode) {
// 从角色权限关联表查询
// 暂时返回空set后续实现
return Set.of();
}
}

View File

@@ -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<SysPermission, Long> {
/**
* 根据权限代码查询
*/
Optional<SysPermission> findByPermissionCode(String permissionCode);
/**
* 根据模块代码查询
*/
List<SysPermission> findByModuleCode(String moduleCode);
/**
* 检查权限代码是否存在
*/
boolean existsByPermissionCode(String permissionCode);
}

View File

@@ -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<SysPermission> findAll() {
return permissionRepository.findAll();
}
/**
* 根据ID查询权限
*/
@Transactional(readOnly = true)
public Optional<SysPermission> findById(Long id) {
return permissionRepository.findById(id);
}
/**
* 根据权限代码查询
*/
@Transactional(readOnly = true)
public Optional<SysPermission> findByPermissionCode(String permissionCode) {
return permissionRepository.findByPermissionCode(permissionCode);
}
/**
* 根据模块代码查询
*/
@Transactional(readOnly = true)
public List<SysPermission> 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<SysPermission> 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);
}
}

View File

@@ -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<SysRole>> list() {
return ResponseEntity.ok(roleService.findAll());
}
/**
* 根据ID获取角色
*/
@GetMapping("/{id}")
public ResponseEntity<SysRole> getById(@PathVariable Long id) {
return roleService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
/**
* 根据角色代码获取角色
*/
@GetMapping("/code/{roleCode}")
public ResponseEntity<SysRole> getByRoleCode(@PathVariable String roleCode) {
return roleService.findByRoleCode(roleCode)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
/**
* 创建角色
*/
@PostMapping
public ResponseEntity<SysRole> 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<SysRole> update(@PathVariable Long id, @RequestBody SysRole role) {
return roleService.update(id, role)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
/**
* 删除角色(软删除)
*/
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
roleService.delete(id);
return ResponseEntity.noContent().build();
}
/**
* 检查角色代码是否存在
*/
@GetMapping("/exists/{roleCode}")
public ResponseEntity<Boolean> exists(@PathVariable String roleCode) {
return ResponseEntity.ok(roleService.existsByRoleCode(roleCode));
}
}

View File

@@ -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; }
}

View File

@@ -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; }
}