feat: 添加管理员权限检查功能和Maven打包配置优化

主要更新:

1. 新增管理员权限检查功能
   - 添加 UserRoleEnum 枚举类统一管理用户角色(USER, ADMIN, BAN)
   - 改进 ContextHolderUtils.isAdmin() 方法,支持不区分大小写的角色比较
   - 更新 UserServiceImpl 使用枚举常量代替硬编码字符串
   - 新增管理员权限使用指南文档 (docs/admin-permission-guide.md)

2. 修复Maven打包配置
   - 配置根POM的spring-boot-maven-plugin默认跳过repackage
   - 为所有服务模块启用repackage,确保可以打包为可执行JAR
   - 修复公共库模块打包失败的问题
   - 涉及服务:gateway, auth, user-service, question-service, file-service, blog-service, upms-biz, ai-service

3. 更新项目文档
   - README.md:添加详细的打包说明、首次克隆准备工作、服务启动顺序等
   - CLAUDE.md:更新项目架构说明和开发指南

4. 重构题目服务责任链结构
   - 将责任链类按功能分类到 question/ 和 submit/ 子目录
   - 新增 QuestionSubmitJudgeInfoEnum 和相关查询功能
   - 改进题目提交服务的实现

5. 其他改进
   - 添加 Feign Token 中继拦截器
   - 更新 AsyncConfig 配置
   - 优化 Jackson 和 Security 配置
This commit is contained in:
2026-01-28 23:01:48 +08:00
parent 67825a8c5c
commit 1945cc2fb1
52 changed files with 1561 additions and 89 deletions

View File

@@ -0,0 +1,252 @@
# 管理员权限检查功能使用指南
## 功能位置
全局管理员权限检查功能已在 `aioj-backend-common-core` 模块中实现,所有服务模块都可以直接使用。
## 核心类
### 1. UserRoleEnum角色枚举
**位置**: `cn.meowrain.aioj.backend.framework.core.enums.UserRoleEnum`
定义了系统中的所有用户角色:
- `USER` - 普通用户code: "user"
- `ADMIN` - 管理员code: "admin"
- `BAN` - 封禁用户code: "ban"
### 2. ContextHolderUtils上下文工具类
**位置**: `cn.meowrain.aioj.backend.framework.core.utils.ContextHolderUtils`
提供了获取当前登录用户信息的工具方法。
## 使用方法
### 1. 检查当前用户是否为管理员
```java
import cn.meowrain.aioj.backend.framework.core.utils.ContextHolderUtils;
// 在任何需要检查管理员权限的地方
if (ContextHolderUtils.isAdmin()) {
// 管理员才能执行的操作
log.info("当前用户是管理员");
} else {
// 非管理员的处理逻辑
throw new ClientException("无权限执行此操作");
}
```
### 2. 获取当前用户ID
```java
// 获取当前登录用户ID未登录会抛出异常
Long userId = ContextHolderUtils.getCurrentUserId();
// 获取当前用户ID未登录返回null
Long userId = ContextHolderUtils.getCurrentUserIdOrNull();
```
### 3. 获取当前用户角色
```java
// 获取当前用户角色字符串
String role = ContextHolderUtils.getCurrentUserRole();
// 使用枚举进行角色判断
if (UserRoleEnum.isAdmin(role)) {
// 管理员逻辑
}
```
### 4. 检查用户是否已登录
```java
if (ContextHolderUtils.isAuthenticated()) {
// 用户已登录
}
```
## 在Controller中使用示例
### 示例1限制删除操作仅管理员可用
```java
@DeleteMapping("/{id}")
@Operation(summary = "删除题目")
public Result<Void> deleteQuestion(@PathVariable Long id) {
// 检查管理员权限
if (!ContextHolderUtils.isAdmin()) {
throw new ClientException("只有管理员才能删除题目");
}
questionService.deleteById(id);
return Result.success();
}
```
### 示例2根据角色返回不同数据
```java
@GetMapping("/list")
@Operation(summary = "获取题目列表")
public Result<List<QuestionDTO>> listQuestions() {
boolean isAdmin = ContextHolderUtils.isAdmin();
if (isAdmin) {
// 管理员可以看到所有题目(包括未发布的)
return Result.success(questionService.listAll());
} else {
// 普通用户只能看到已发布的题目
return Result.success(questionService.listPublished());
}
}
```
### 示例3限制更新操作仅作者或管理员
```java
@PutMapping("/{id}")
@Operation(summary = "更新题目")
public Result<Void> updateQuestion(
@PathVariable Long id,
@RequestBody QuestionUpdateDTO request) {
Question question = questionService.getById(id);
Long currentUserId = ContextHolderUtils.getCurrentUserId();
boolean isAdmin = ContextHolderUtils.isAdmin();
// 只有题目创建者或管理员可以更新
if (!isAdmin && !question.getCreatorId().equals(currentUserId)) {
throw new ClientException("无权限修改此题目");
}
questionService.updateQuestion(id, request);
return Result.success();
}
```
## 在Service层使用示例
```java
@Service
public class QuestionServiceImpl implements QuestionService {
@Override
public void deleteQuestion(Long id) {
// 在Service层也可以进行权限检查
if (!ContextHolderUtils.isAdmin()) {
throw new ServiceException("权限不足");
}
// 执行删除逻辑
this.removeById(id);
}
}
```
## 使用Spring Security注解推荐
除了手动检查还可以使用Spring Security的注解
```java
import org.springframework.security.access.prepost.PreAuthorize;
@DeleteMapping("/{id}")
@PreAuthorize("hasRole('admin')") // 注意:角色名会自动转换为小写
@Operation(summary = "删除题目")
public Result<Void> deleteQuestion(@PathVariable Long id) {
questionService.deleteById(id);
return Result.success();
}
```
**注意**:使用 `@PreAuthorize` 需要在配置类上启用方法级安全:
```java
@Configuration
@EnableMethodSecurity // Spring Boot 3.x
public class SecurityConfig {
// ...
}
```
## 角色枚举使用
在需要设置或比较角色时,使用枚举常量:
```java
import cn.meowrain.aioj.backend.framework.core.enums.UserRoleEnum;
// 设置用户角色
user.setUserRole(UserRoleEnum.ADMIN.getCode());
// 判断角色
if (UserRoleEnum.isAdmin(user.getUserRole())) {
// 管理员逻辑
}
// 根据代码获取枚举
UserRoleEnum role = UserRoleEnum.fromCode("admin");
if (role == UserRoleEnum.ADMIN) {
// 管理员逻辑
}
```
## 注意事项
1. **大小写不敏感**:角色比较不区分大小写,"admin"、"Admin"、"ADMIN" 都会被识别为管理员
2. **异常处理**`getCurrentUserId()``getCurrentUserRole()` 在用户未登录时会抛出 `IllegalStateException`
3. **安全性**建议在Controller和Service层都进行权限检查实现多层防护
4. **JWT依赖**此功能依赖JWT认证确保请求头中包含有效的JWT token
## 完整示例题目管理Controller
```java
@RestController
@RequestMapping("/api/question")
@RequiredArgsConstructor
public class QuestionController {
private final QuestionService questionService;
@PostMapping
@Operation(summary = "创建题目(仅管理员)")
public Result<Long> createQuestion(@RequestBody QuestionCreateDTO request) {
if (!ContextHolderUtils.isAdmin()) {
throw new ClientException("只有管理员才能创建题目");
}
Long questionId = questionService.create(request);
return Result.success(questionId);
}
@PutMapping("/{id}")
@Operation(summary = "更新题目(仅管理员)")
public Result<Void> updateQuestion(
@PathVariable Long id,
@RequestBody QuestionUpdateDTO request) {
if (!ContextHolderUtils.isAdmin()) {
throw new ClientException("只有管理员才能更新题目");
}
questionService.update(id, request);
return Result.success();
}
@DeleteMapping("/{id}")
@Operation(summary = "删除题目(仅管理员)")
public Result<Void> deleteQuestion(@PathVariable Long id) {
if (!ContextHolderUtils.isAdmin()) {
throw new ClientException("只有管理员才能删除题目");
}
questionService.deleteById(id);
return Result.success();
}
@GetMapping("/{id}")
@Operation(summary = "获取题目详情")
public Result<QuestionDTO> getQuestion(@PathVariable Long id) {
// 普通用户和管理员都可以查看
QuestionDTO question = questionService.getById(id);
return Result.success(question);
}
}
```