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

@@ -1,8 +1,16 @@
package cn.meowrain.aioj.backend.framework.feign;
import cn.meowrain.aioj.backend.framework.feign.interceptor.TokenRelayRequestInterceptor;
import feign.RequestInterceptor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
@AutoConfiguration
public class FeignAutoConfiguration {
@Bean
public RequestInterceptor tokenRelayRequestInterceptor() {
return new TokenRelayRequestInterceptor();
}
}

View File

@@ -0,0 +1,60 @@
package cn.meowrain.aioj.backend.framework.feign.interceptor;
import cn.meowrain.aioj.backend.framework.feign.annotation.NoToken;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.lang.reflect.Method;
/**
* Feign 调用时透传 Authorization 头
*/
public class TokenRelayRequestInterceptor implements RequestInterceptor {
private static final String HEADER_NAME = "Authorization";
private static final String TOKEN_PREFIX = "Bearer ";
@Override
public void apply(RequestTemplate template) {
if (isNoToken(template) || template.headers().containsKey(HEADER_NAME)) {
return;
}
String authorization = resolveAuthorization();
if (StringUtils.hasText(authorization)) {
template.header(HEADER_NAME, authorization);
}
}
private boolean isNoToken(RequestTemplate template) {
if (template.methodMetadata() == null) {
return false;
}
Method method = template.methodMetadata().method();
return method != null && (method.isAnnotationPresent(NoToken.class)
|| method.getDeclaringClass().isAnnotationPresent(NoToken.class));
}
private String resolveAuthorization() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
return request.getHeader(HEADER_NAME);
}
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.getDetails() instanceof String token
&& StringUtils.hasText(token)) {
return token.startsWith(TOKEN_PREFIX) ? token : TOKEN_PREFIX + token;
}
return null;
}
}