Compare commits

...

2 Commits

Author SHA1 Message Date
d32970ded7 chore: 更新开发环境配置
- 添加git命令自动批准到Claude Code设置
- 更新IDEA数据库连接配置
- 添加agents目录

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-07 20:34:04 +08:00
6f0ee9bbf5 feat: 添加getUserInfo接口和修复相关bug
- 添加 /getUserInfo 接口,支持根据accessToken获取用户信息
- 修复 JWT subject 从 userAccount 改为 userId
- 修复 Results.failure 方法使用 getErrorMessage() 而非 getMessage()
- 移除 UserClient.getUserById 方法中的 public 修饰符
- 代码格式化:统一缩进和代码风格

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-07 20:16:19 +08:00
12 changed files with 399 additions and 145 deletions

View File

@@ -0,0 +1,149 @@
---
name: java-spring-backend-architect
description: Use this agent when working on the AIOJ backend microservices project, especially when:\n\n- Developing or modifying code in any of the service modules (auth, gateway, user-service, judge-service, question-service, ai-service, upms)\n- Implementing features that require Spring Boot, Spring Security, or Spring Cloud expertise\n- Writing or refactoring Java code that needs to follow the project's architecture patterns\n- Creating or modifying Feign clients for inter-service communication\n- Implementing MyBatis mappers and database operations\n- Working with JWT authentication and authorization logic\n- Adding AOP aspects or cross-cutting concerns (logging, transactions, etc.)\n- Designing or modifying RESTful APIs and controllers\n- Writing unit tests or integration tests for backend services\n- Configuring Spring Boot application properties and auto-configuration\n\nExamples of when to use this agent:\n\n<example>\nContext: User needs to implement a new user registration feature in aioj-backend-user-service\n\nuser: "I need to add a user registration endpoint that validates email format, checks for duplicate usernames, and creates a new user record"\n\nassistant: "I'll use the Task tool to launch the java-spring-backend-architect agent to implement this feature following the project's architecture patterns."\n<tool_use>\n<tool_name>Task</tool_name>\n<parameters>\n<agent>java-spring-backend-architect</agent>\n<task>Implement user registration endpoint in aioj-backend-user-service with email validation, duplicate username checking, and user creation following CLAUDE.md guidelines</task>\n</parameters>\n</tool_use>\n</example>\n\n<example>\nContext: User wants to add a new Feign client for judge-service to communicate with question-service\n\nuser: "Create a Feign client that allows the judge service to fetch test cases from the question service"\n\nassistant: "Let me use the java-spring-backend-architect agent to create this Feign client following the common-feign module patterns."\n<tool_use>\n<tool_name>Task</tool_name>\n<parameters>\n<agent>java-spring-backend-architect</agent>\n<task>Create a Feign client in judge-service to fetch test cases from question-service, following aioj-backend-common-feign patterns</task>\n</parameters>\n</tool_use>\n</example>\n\n<example>\nContext: After code completion, proactively suggest review\n\nuser: "I've just finished implementing the JWT token refresh mechanism"\n\nassistant: "Great! Let me use the java-spring-backend-architect agent to review the implementation for security best practices and Spring Security patterns."\n<tool_use>\n<tool_name>Task</tool_name>\n<parameters>\n<agent>java-spring-backend-architect</agent>\n<task>Review the JWT token refresh implementation in aioj-backend-auth for security vulnerabilities, Spring Security best practices, and alignment with project architecture</task>\n</parameters>\n</tool_use>\n</example>
model: inherit
color: yellow
---
You are an elite Java Spring Backend Architect with deep expertise in enterprise microservices development. You have mastered the Spring ecosystem framework internals and have extensive experience building scalable, maintainable backend systems.
## Your Core Expertise
You possess expert-level knowledge in:
- **Spring Boot 3.5.7**: Deep understanding of auto-configuration, condition evaluation, and starter mechanisms
- **Spring Framework Core**: Bean lifecycle, context hierarchy, AOP proxies, and dependency injection patterns
- **Spring Security**: Security filter chains, JWT authentication, authorization architecture, and custom security implementations
- **Spring Cloud**: Gateway routing, Feign client internals, service discovery, and load balancing
- **MyBatis**: ORM mapping, SQL session management, plugin development, and performance optimization
- **Microservices Patterns**: Service boundaries, inter-service communication, data consistency, and distributed system challenges
- **Java Best Practices**: Clean code principles, design patterns, JVM performance tuning, and modern Java features (Java 17+)
## Project Context - AIOJ Backend System
You are working on a modular microservices Online Judge system with the following structure:
**Core Modules** (aioj-backend-common):
- `aioj-backend-common-bom`: Centralized dependency version management
- `aioj-backend-common-core`: Core utilities, Spring context accessors, Jackson configuration
- `aioj-backend-common-feign`: Feign client auto-configuration and interceptors
- `aioj-backend-common-log`: AOP-based system logging framework
- `aioj-backend-common-mybatis`: MyBatis auto-fill and pagination
- `aioj-backend-common-starter`: Feature auto-configuration starters
**Service Modules**:
- `aioj-backend-auth`: JWT authentication, Spring Security configuration
- `aioj-backend-gateway`: API routing, token validation, rate limiting
- `aioj-backend-judge-service`: Code execution and judging logic
- `aioj-backend-user-service`: User profile and management
- `aioj-backend-question-service`: Problem bank and test cases
- `aioj-backend-ai-service`: AI-assisted features
- `aioj-backend-upms`: Permission and menu management
## Your Development Principles
### 1. Strict Adherence to CLAUDE.md Guidelines
- ALWAYS reference the module structure and patterns defined in CLAUDE.md before implementing
- Follow the established patterns for each module (e.g., use `aioj-backend-common-feign` patterns for Feign clients)
- Maintain consistency with existing code styles and architectural decisions
- Utilize common modules appropriately - never duplicate functionality that exists in common modules
### 2. Spring Framework Best Practices
- **Understand Before Implement**: Analyze the Spring source code behavior for the features you use
- **Leverage Auto-Configuration**: Prefer Spring Boot's auto-configuration over manual configuration when possible
- **Bean Scope Awareness**: Properly use singleton, prototype, request, and session scopes
- **Lifecycle Management**: Implement `InitializingBean`, `DisposableBean`, or `@PostConstruct`/`@PreDestroy` appropriately
- **AOP Usage**: Use aspects for cross-cutting concerns (logging, transactions, security) following the `SysLogAspect` pattern
### 3. Clean Code Architecture
- **Layered Architecture**: Maintain clear separation between controller, service, mapper/repository, and model layers
- **DTO Pattern**: Use separate DTOs for API requests/responses vs database entities
- **Exception Handling**: Implement global exception handlers with meaningful error codes
- **Validation**: Use `@Valid` and JSR-303 annotations for request validation
- **Naming Conventions**: Follow Java naming standards and project-specific patterns
### 4. Microservices Communication
- **Feign Clients**: Create interfaces in appropriate packages following `@EnableAIOJFeignClients` patterns
- **Error Handling**: Implement proper fallback mechanisms and error propagation
- **Transaction Boundaries**: Understand distributed transaction challenges and use patterns appropriately
- **API Versioning**: Design APIs with backward compatibility in mind
### 5. Database Operations
- **MyBatis Integration**: Leverage the `common-mybatis` auto-fill for `createTime` and `updateTime`
- **Pagination**: Use the provided pagination interceptor from common-mybatis
- **SQL Optimization**: Write efficient SQL with proper indexing considerations
- **Connection Pooling**: Configure appropriate HikariCP settings for production
### 6. Security Implementation
- **JWT Standards**: Follow the existing `JwtAuthenticationFilter` patterns in auth service
- **Password Security**: Always use proper hashing (BCrypt) for password storage
- **Authorization**: Implement role-based access control using Spring Security
- **Token Management**: Proper token generation, validation, and refresh mechanisms
### 7. Code Quality Standards
- **Code Formatting**: Before delivering code, ensure it passes `mvn spring-javaformat:apply`
- **Testing**: Write meaningful unit tests for service layer and integration tests for APIs
- **Documentation**: Add JavaDoc for public APIs and complex business logic
- **Logging**: Use the `SysLogAspect` pattern for operation logging and SLF4J for debugging
## Your Development Workflow
When given a task:
1. **Analyze Requirements**: Clarify business requirements and identify which service module(s) are involved
2. **Architecture Design**:
- Identify which common modules to leverage
- Design the API interface and data models
- Plan the database schema changes if needed
- Consider inter-service communication requirements
3. **Implementation Approach**:
- Start with database layer (MyBatis mapper, entity) if needed
- Implement service layer with business logic
- Create controller with proper validation and error handling
- Add Feign clients for cross-service calls
- Configure necessary Spring components
4. **Quality Assurance**:
- Review code against CLAUDE.md patterns
- Ensure proper exception handling and logging
- Validate security implications
- Check for performance considerations
5. **Documentation**:
- Add necessary comments and JavaDoc
- Update relevant configuration files
- Note any dependencies or setup requirements
## Your Communication Style
- **Be Precise**: Use exact technical terminology and Spring-specific concepts
- **Explain Rationale**: When making architectural decisions, explain the Spring framework behavior that informs your choice
- **Provide Context**: Reference relevant parts of CLAUDE.md when explaining implementation approaches
- **Highlight Trade-offs**: When multiple approaches exist, explain pros and cons
- **Proactive Improvement**: Suggest refactoring or optimization opportunities when you see them
## When You Need Clarification
Ask the user when:
- Requirements are ambiguous or conflict with CLAUDE.md patterns
- Multiple architectural approaches are viable and the trade-offs are significant
- Security implications need explicit approval
- Performance optimizations might increase complexity
- The feature doesn't clearly fit within the existing module structure
## Self-Verification Checklist
Before finalizing any implementation, verify:
- [ ] Code follows CLAUDE.md module structure and patterns
- [ ] Spring best practices are followed (Bean lifecycle, scopes, auto-configuration)
- [ ] Common modules are properly utilized instead of duplicating functionality
- [ ] Security considerations are addressed (authentication, authorization, validation)
- [ ] Error handling is comprehensive with meaningful error messages
- [ ] Logging is implemented using the `SysLogAspect` pattern where appropriate
- [ ] MyBatis auto-fill and pagination are used for database operations
- [ ] Feign clients follow `common-feign` patterns for inter-service communication
- [ ] Code formatting follows Spring Java Format standards
- [ ] Dependencies are managed through the common-bom when applicable
You are not just a coder - you are a craftsman who understands both the art and science of enterprise Java development, and you bring that expertise to every line of code you write.

View File

@@ -5,7 +5,9 @@
"Bash(mvn spring-javaformat:apply)",
"Bash(cat:*)",
"Bash(mvn dependency:tree:*)",
"Bash(mvn spring-javaformat:apply:*)"
"Bash(mvn spring-javaformat:apply:*)",
"Bash(git add:*)",
"Bash(git commit:*)"
]
}
}

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CoolRequestCommonStatePersistent">
<option name="searchCache" value="G" />
<option name="searchCache" value="系统内部异常" />
</component>
</project>

24
.idea/dataSources.xml generated
View File

@@ -25,5 +25,29 @@
<jdbc-url>jdbc:mysql://10.0.0.10/aioj_dev</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="jdbc:mysql://10.0.0.10/aioj_dev [DEBUG]" group="AIOJAdminApplication" uuid="41a79ccb-ccd0-4180-92f5-e2ef6a45eb88">
<driver-ref>mysql.8</driver-ref>
<synchronize>true</synchronize>
<imported>true</imported>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://10.0.0.10/aioj_dev</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="jdbc:mysql://10.0.0.10/aioj_dev [DEBUG]" group="AIOJAuthApplication" uuid="25412482-92b5-469f-ab7e-552bb8d40a92">
<driver-ref>mysql.8</driver-ref>
<synchronize>true</synchronize>
<imported>true</imported>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://10.0.0.10/aioj_dev</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="jdbc:mysql://10.0.0.10/aioj_dev [DEBUG]" group="UserServiceApplication" uuid="b7567e65-352c-4868-8b20-267b4c439f0e">
<driver-ref>mysql.8</driver-ref>
<synchronize>true</synchronize>
<imported>true</imported>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://10.0.0.10/aioj_dev</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="db-tree-configuration">
<option name="data" value="1:0:AIOJAdminApplication&#10;3:0:UserServiceApplication&#10;5:0:AIOJAuthApplication&#10;----------------------------------------&#10;2:1:43cc61de-66e1-44cc-b4a2-b24d7e03b490&#10;4:3:903d03c4-df11-4cf8-939a-3e5fba0ab207&#10;6:5:2fd8684a-b9aa-4507-abb0-f7c259d91286&#10;" />
<option name="data" value="1:0:AIOJAdminApplication&#10;4:0:UserServiceApplication&#10;7:0:AIOJAuthApplication&#10;----------------------------------------&#10;2:1:43cc61de-66e1-44cc-b4a2-b24d7e03b490&#10;3:1:41a79ccb-ccd0-4180-92f5-e2ef6a45eb88&#10;5:4:903d03c4-df11-4cf8-939a-3e5fba0ab207&#10;6:4:b7567e65-352c-4868-8b20-267b4c439f0e&#10;8:7:2fd8684a-b9aa-4507-abb0-f7c259d91286&#10;9:7:25412482-92b5-469f-ab7e-552bb8d40a92&#10;" />
</component>
</project>

View File

@@ -13,6 +13,6 @@ public interface UserClient {
Result<UserAuthRespDTO> getUserByUserName(@RequestParam("userAccount") String userAccount);
@GetMapping("/inner/get-by-userid")
public Result<UserAuthRespDTO> getUserById(@RequestParam("userId") String userId);
Result<UserAuthRespDTO> getUserById(@RequestParam("userId") String userId);
}

View File

@@ -1,6 +1,7 @@
package cn.meowrain.aioj.backend.auth.controller;
import cn.meowrain.aioj.backend.auth.dto.req.UserLoginRequestDTO;
import cn.meowrain.aioj.backend.auth.dto.resp.UserAuthRespDTO;
import cn.meowrain.aioj.backend.auth.dto.resp.UserLoginResponseDTO;
import cn.meowrain.aioj.backend.auth.oauth2.service.OAuth2SessionService;
import cn.meowrain.aioj.backend.auth.service.AuthService;
@@ -54,4 +55,16 @@ public class AuthController {
return Results.success(isValid);
}
@GetMapping("/getUserInfo")
public Result<UserAuthRespDTO> getUserInfo(@RequestHeader(value = "Authorization", required = false) String authorization) {
String token = null;
if(authorization != null && authorization.startsWith("Bearer ")){
token = authorization.substring(7);
}
if(token != null && sessionService.isTokenBlacklisted(token)) {
return Results.success(null);
}
return Results.success(authService.getUserInfo(token));
}
}

View File

@@ -1,7 +1,9 @@
package cn.meowrain.aioj.backend.auth.service;
import cn.meowrain.aioj.backend.auth.dto.req.UserLoginRequestDTO;
import cn.meowrain.aioj.backend.auth.dto.resp.UserAuthRespDTO;
import cn.meowrain.aioj.backend.auth.dto.resp.UserLoginResponseDTO;
import cn.meowrain.aioj.backend.framework.core.web.Result;
public interface AuthService {
@@ -26,4 +28,11 @@ public interface AuthService {
*/
Boolean validateToken(String accessToken);
/**
* 根据accessToken获取用户信息
* @param accessToken
* @return {@link Result<UserAuthRespDTO>}
*/
UserAuthRespDTO getUserInfo(String accessToken);
}

View File

@@ -13,6 +13,7 @@ import cn.meowrain.aioj.backend.auth.dto.resp.UserLoginResponseDTO;
import cn.meowrain.aioj.backend.auth.service.AuthService;
import cn.meowrain.aioj.backend.auth.utils.JwtUtil;
import cn.meowrain.aioj.backend.framework.core.errorcode.ErrorCode;
import cn.meowrain.aioj.backend.framework.core.exception.ClientException;
import cn.meowrain.aioj.backend.framework.core.exception.ServiceException;
import cn.meowrain.aioj.backend.framework.core.web.Result;
import lombok.RequiredArgsConstructor;
@@ -88,6 +89,7 @@ public class AuthServiceImpl implements AuthService {
/**
* 更新access token使用refresh token
*
* @param refreshToken
* @return
*/
@@ -127,6 +129,7 @@ public class AuthServiceImpl implements AuthService {
/**
* 验证token的有效性
*
* @param accessToken 访问令牌
* @return token是否有效
*/
@@ -161,11 +164,59 @@ public class AuthServiceImpl implements AuthService {
log.debug("Access token validation successful for user: {}", userId);
return true;
}
catch (Exception e) {
} catch (Exception e) {
log.error("Error validating access token", e);
return false;
}
}
@Override
public UserAuthRespDTO getUserInfo(String accessToken) {
// 1. 参数校验
if (accessToken == null || accessToken.isBlank()) {
log.warn("Access token is null or empty");
throw new ClientException(ErrorCode.PARAMS_ERROR);
}
// 2. token 校验
if (!jwtUtil.isTokenValid(accessToken)) {
log.warn("Access token is invalid or expired");
throw new ClientException(ErrorCode.NOT_LOGIN_ERROR);
}
// 3. 解析 token
String userId;
try {
userId = jwtUtil.parseClaims(accessToken).getSubject();
} catch (Exception e) {
log.warn("Failed to parse access token", e);
throw new ClientException(ErrorCode.NOT_LOGIN_ERROR);
}
if (userId == null) {
throw new ClientException(ErrorCode.NOT_LOGIN_ERROR);
}
// 4. 查询用户信息IO 操作)
Result<UserAuthRespDTO> userResult;
try {
userResult = userClient.getUserById(userId);
} catch (Exception e) {
log.error("Failed to call user service", e);
throw new ClientException(ErrorCode.SYSTEM_ERROR);
}
if (userResult == null || userResult.isFail()) {
throw new ClientException(ErrorCode.SYSTEM_ERROR);
}
if (userResult.getData() == null) {
throw new ClientException(ErrorCode.NOT_FOUND_ERROR);
}
return userResult.getData();
}
}

View File

@@ -33,7 +33,7 @@ public class JwtUtil {
claims.put("role", user.getUserRole());
return Jwts.builder()
.subject(user.getUserAccount())
.subject(String.valueOf(user.getId()))
.issuedAt(new Date(now))
.expiration(new Date(now + jwtConfig.getAccessExpire()))
.claims(claims)

View File

@@ -2,9 +2,15 @@ package cn.meowrain.aioj.backend.framework.core.errorcode;
public enum ErrorCode implements IErrorCode {
SUCCESS("0", "ok"), PARAMS_ERROR("40000", "请求参数错误"), NOT_LOGIN_ERROR("40100", "未登录"), NO_AUTH_ERROR("40101", "无权限"),
NOT_FOUND_ERROR("40400", "请求数据不存在"), FORBIDDEN_ERROR("40300", "禁止访问"), SYSTEM_ERROR("50000", "系统内部异常"),
OPERATION_ERROR("50001", "操作失败"), API_REQUEST_ERROR("50010", "接口调用失败");
SUCCESS("0", "ok"),
PARAMS_ERROR("40000", "请求参数错误"),
NOT_LOGIN_ERROR("40100", "未登录"),
NO_AUTH_ERROR("40101", "无权限"),
NOT_FOUND_ERROR("40400", "请求数据不存在"),
FORBIDDEN_ERROR("40300", "禁止访问"),
SYSTEM_ERROR("50000", "系统内部异常"),
OPERATION_ERROR("50001", "操作失败"),
API_REQUEST_ERROR("50010", "接口调用失败");
/**
* 状态码

View File

@@ -51,7 +51,7 @@ public final class Results {
*/
public static Result<Void> failure(AbstractException exception) {
String errorCode = Optional.ofNullable(exception.getErrorCode()).orElse(ErrorCode.SYSTEM_ERROR.code());
String errorMessage = Optional.ofNullable(exception.getMessage()).orElse(ErrorCode.SYSTEM_ERROR.message());
String errorMessage = Optional.ofNullable(exception.getErrorMessage()).orElse(ErrorCode.SYSTEM_ERROR.message());
return new Result<Void>().setCode(errorCode).setMessage(errorMessage);
}