fix: 修复日志功能
This commit is contained in:
10
.claude/settings.local.json
Normal file
10
.claude/settings.local.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(cat:*)",
|
||||
"Bash(find ./aioj-backend-common -path \"*common-log*pom.xml\" -exec cat {} ;)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
}
|
||||
}
|
||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -15,7 +15,7 @@
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="corretto-17" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="zulu-17" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
86
CLAUDE.md
Normal file
86
CLAUDE.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is an AI-integrated OJ (Online Judge) judging system with a microservices architecture. The system allows users to submit code for programming problems, which is then judged automatically. It also includes AI features to assist with problem-solving and code evaluation.
|
||||
|
||||
## Architecture
|
||||
|
||||
The project follows a microservices architecture pattern built with Spring Boot and Spring Cloud Alibaba:
|
||||
|
||||
### Core Modules:
|
||||
|
||||
1. **aioj-backend-common**: Common utilities and dependencies shared across all modules.
|
||||
2. **aioj-backend-gateway**: API gateway that routes requests to the appropriate microservices.
|
||||
3. **aioj-backend-judge-service**: Handles code submission, compilation, and judging processes.
|
||||
4. **aioj-backend-user-service**: Manages user accounts, authentication, and authorization.
|
||||
5. **aioj-backend-question-service**: Manages programming problems and test cases.
|
||||
6. **aioj-backend-ai-service**: Provides AI-assisted features such as code analysis and problem-solving.
|
||||
7. **aioj-backend-auth**: Manages authentication and token issuance.
|
||||
8. **aioj-backend-upms**: User Management System for administrative operations.
|
||||
|
||||
## Database
|
||||
|
||||
The system uses SQL databases. Database scripts can be found in the `db/` directory.
|
||||
|
||||
## Build System
|
||||
|
||||
The project uses Maven for build and dependency management.
|
||||
|
||||
### Common Commands:
|
||||
|
||||
- Compile the entire project:
|
||||
```bash
|
||||
mvn clean compile
|
||||
```
|
||||
|
||||
- Compile a specific module:
|
||||
```bash
|
||||
mvn clean compile -pl <module-name>
|
||||
```
|
||||
|
||||
- Package the entire project:
|
||||
```bash
|
||||
mvn clean package
|
||||
```
|
||||
|
||||
- Package a specific module:
|
||||
```bash
|
||||
mvn clean package -pl <module-name>
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
### Environment Profiles
|
||||
|
||||
The project supports different environments (dev, test, prod) with corresponding configuration files:
|
||||
|
||||
- Development: application-dev.yml
|
||||
- Test: application-test.yml
|
||||
- Production: application-prod.yml
|
||||
|
||||
### Running a Service
|
||||
|
||||
To run a specific microservice, use the Spring Boot Maven plugin:
|
||||
|
||||
```bash
|
||||
cd <module-name>
|
||||
mvn spring-boot:run
|
||||
```
|
||||
|
||||
Or run the built JAR file:
|
||||
|
||||
```bash
|
||||
cd <module-name>/target
|
||||
java -jar <module-name>-<version>.jar
|
||||
```
|
||||
|
||||
## Technologies Used
|
||||
|
||||
- **Java 17**: Programming language
|
||||
- **Spring Boot 3.5.7**: Framework for building microservices
|
||||
- **Spring Cloud Alibaba 2025.0.0.0**: Microservices ecosystem
|
||||
- **Maven**: Build tool
|
||||
- **Lombok**: Java library to reduce boilerplate code
|
||||
@@ -26,7 +26,20 @@
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--server-api-->
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-extra</artifactId>
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package cn.meowrain.aioj.backend.framework.core.utils;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class SpringContextHolder implements ApplicationContextAware, EnvironmentAware, DisposableBean {
|
||||
private static ApplicationContext applicationContext = null;
|
||||
|
||||
private static Environment environment = null;
|
||||
/**
|
||||
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
|
||||
*/
|
||||
public static <T> T getBean(String name) {
|
||||
return (T) applicationContext.getBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
|
||||
*/
|
||||
public static <T> T getBean(Class<T> requiredType) {
|
||||
return applicationContext.getBean(requiredType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void destroy() throws Exception {
|
||||
clearHolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
SpringContextHolder.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
SpringContextHolder.environment = environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除SpringContextHolder中的ApplicationContext为Null.
|
||||
*/
|
||||
public static void clearHolder() {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
|
||||
}
|
||||
applicationContext = null;
|
||||
}
|
||||
/**
|
||||
* 发布事件
|
||||
* @param event
|
||||
*/
|
||||
public static void publishEvent(ApplicationEvent event) {
|
||||
if (applicationContext == null) {
|
||||
return;
|
||||
}
|
||||
applicationContext.publishEvent(event);
|
||||
}
|
||||
/**
|
||||
* 是否是微服务
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean isMicro() {
|
||||
return environment.getProperty("spring.cloud.nacos.discovery.enabled", Boolean.class, true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,23 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!--安全依赖获取上下文信息-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-oauth2-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-extra</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-http</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.meowrain</groupId>
|
||||
<artifactId>aioj-backend-common-core</artifactId>
|
||||
|
||||
@@ -3,6 +3,8 @@ package cn.meowrain.aioj.backend.framework.log;
|
||||
import cn.meowrain.aioj.backend.framework.log.aspect.SysLogAspect;
|
||||
import cn.meowrain.aioj.backend.framework.log.config.AIOJLogPropertiesConfiguration;
|
||||
import cn.meowrain.aioj.backend.framework.log.event.SysLogListener;
|
||||
import cn.meowrain.aioj.backend.upms.api.feign.RemoteLogService;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -19,6 +21,7 @@ public class LogAutoConfiguration {
|
||||
* 创建并返回SysLogListener的Bean实例
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnBean(RemoteLogService.class)
|
||||
public SysLogListener sysLogListener(AIOJLogPropertiesConfiguration logProperties,
|
||||
RemoteLogService remoteLogService) {
|
||||
return new SysLogListener(remoteLogService, logProperties);
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
package cn.meowrain.aioj.backend.framework.log.aspect;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.meowrain.aioj.backend.framework.core.utils.SpringContextHolder;
|
||||
import cn.meowrain.aioj.backend.framework.log.annotation.SysLog;
|
||||
import cn.meowrain.aioj.backend.framework.log.enums.LogTypeEnum;
|
||||
import cn.meowrain.aioj.backend.framework.log.event.SysLogEvent;
|
||||
import cn.meowrain.aioj.backend.framework.log.event.SysLogEventSource;
|
||||
import cn.meowrain.aioj.backend.framework.log.utils.SysLogUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
@@ -15,19 +20,60 @@ import org.springframework.expression.EvaluationContext;
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class SysLogAspect {
|
||||
|
||||
/**
|
||||
* 环绕通知方法,用于处理系统日志记录
|
||||
* @param point 连接点对象
|
||||
* @param sysLog 系统日志注解
|
||||
* @return 目标方法执行结果
|
||||
* @throws Throwable 目标方法执行可能抛出的异常
|
||||
*/
|
||||
@Around("@annotation(sysLog)")
|
||||
public Object around(ProceedingJoinPoint joinPoint, SysLog sysLog) throws Throwable {
|
||||
String strClassName = joinPoint.getTarget().getClass().getName();
|
||||
String strMethodName = joinPoint.getSignature().getName();
|
||||
@SneakyThrows
|
||||
public Object around(ProceedingJoinPoint point,SysLog sysLog) {
|
||||
String strClassName = point.getTarget().getClass().getName();
|
||||
String strMethodName = point.getSignature().getName();
|
||||
log.debug("[类名]:{},[方法]:{}", strClassName, strMethodName);
|
||||
|
||||
String value = sysLog.value();
|
||||
String expression = sysLog.expression();
|
||||
// 当前表达式存在 SPEL,会覆盖 value 的值
|
||||
if (StrUtil.isNotBlank(expression)) {
|
||||
// 解析SPEL
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
EvaluationContext context = SysLogUtils.getContext(joinPoint.getArgs(), signature.getMethod());
|
||||
MethodSignature signature = (MethodSignature) point.getSignature();
|
||||
EvaluationContext context = SysLogUtils.getContext(point.getArgs(), signature.getMethod());
|
||||
try {
|
||||
value = SysLogUtils.getValue(context, expression, String.class);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// SPEL 表达式异常,获取 value 的值
|
||||
log.error("@SysLog 解析SPEL {} 异常", expression);
|
||||
}
|
||||
}
|
||||
|
||||
SysLogEventSource logVo = SysLogUtils.getSysLog();
|
||||
logVo.setTitle(value);
|
||||
// 获取请求body参数
|
||||
if (StrUtil.isBlank(logVo.getParams())) {
|
||||
logVo.setBody(point.getArgs());
|
||||
}
|
||||
// 发送异步日志事件
|
||||
Long startTime = System.currentTimeMillis();
|
||||
Object obj;
|
||||
|
||||
try {
|
||||
obj = point.proceed();
|
||||
}
|
||||
catch (Exception e) {
|
||||
logVo.setLogType(LogTypeEnum.ERROR.getType());
|
||||
logVo.setException(e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
Long endTime = System.currentTimeMillis();
|
||||
logVo.setTime(endTime - startTime);
|
||||
SpringContextHolder.publishEvent(new SysLogEvent(logVo));
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cn.meowrain.aioj.backend.framework.log.event;
|
||||
|
||||
import cn.meowrain.aioj.backend.framework.log.annotation.SysLog;
|
||||
|
||||
import cn.meowrain.aioj.backend.upms.api.entity.SysLog;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
package cn.meowrain.aioj.backend.framework.log.event;
|
||||
|
||||
import cn.meowrain.aioj.backend.upms.api.entity.SysLog;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 系统
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class SysLogEventSource extends SysLogEvent implements Serializable {
|
||||
public class SysLogEventSource extends SysLog {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -1,11 +1,101 @@
|
||||
package cn.meowrain.aioj.backend.framework.log.utils;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.extra.servlet.JakartaServletUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.meowrain.aioj.backend.framework.core.utils.SpringContextHolder;
|
||||
import cn.meowrain.aioj.backend.framework.log.config.AIOJLogPropertiesConfiguration;
|
||||
import cn.meowrain.aioj.backend.framework.log.enums.LogTypeEnum;
|
||||
import cn.meowrain.aioj.backend.framework.log.event.SysLogEventSource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.core.StandardReflectionParameterNameDiscoverer;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class SysLogUtils {
|
||||
|
||||
public static SysLogEventSource getSysLog() {
|
||||
|
||||
/**
|
||||
* 获取系统日志事件源
|
||||
* @return 系统日志事件源对象
|
||||
*/
|
||||
public static SysLogEventSource getSysLog() {
|
||||
HttpServletRequest request = ((ServletRequestAttributes) Objects
|
||||
.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
|
||||
SysLogEventSource sysLog = new SysLogEventSource();
|
||||
sysLog.setLogType(LogTypeEnum.NORMAL.getType());
|
||||
sysLog.setRequestUri(URLUtil.getPath(request.getRequestURI()));
|
||||
sysLog.setMethod(request.getMethod());
|
||||
sysLog.setRemoteAddr(JakartaServletUtil.getClientIP(request));
|
||||
sysLog.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
|
||||
sysLog.setCreateBy(getUsername());
|
||||
sysLog.setServiceId(SpringUtil.getProperty("spring.application.name"));
|
||||
|
||||
// get 参数脱敏
|
||||
AIOJLogPropertiesConfiguration logProperties = SpringContextHolder.getBean(AIOJLogPropertiesConfiguration.class);
|
||||
Map<String, String[]> paramsMap = MapUtil.removeAny(new HashMap<>(request.getParameterMap()),
|
||||
ArrayUtil.toArray(logProperties.getExcludeFields(), String.class));
|
||||
sysLog.setParams(HttpUtil.toParams(paramsMap));
|
||||
return sysLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户名称
|
||||
* @return username
|
||||
*/
|
||||
private static String getUsername() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication == null) {
|
||||
return null;
|
||||
}
|
||||
return authentication.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取spel 定义的参数值
|
||||
* @param context 参数容器
|
||||
* @param key key
|
||||
* @param clazz 需要返回的类型
|
||||
* @param <T> 返回泛型
|
||||
* @return 参数值
|
||||
*/
|
||||
public static <T> T getValue(EvaluationContext context, String key, Class<T> clazz) {
|
||||
SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
|
||||
Expression expression = spelExpressionParser.parseExpression(key);
|
||||
return expression.getValue(context, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取参数容器
|
||||
* @param arguments 方法的参数列表
|
||||
* @param signatureMethod 被执行的方法体
|
||||
* @return 装载参数的容器
|
||||
*/
|
||||
public static EvaluationContext getContext(Object[] arguments, Method signatureMethod) {
|
||||
String[] parameterNames = new StandardReflectionParameterNameDiscoverer().getParameterNames(signatureMethod);
|
||||
EvaluationContext context = new StandardEvaluationContext();
|
||||
if (parameterNames == null) {
|
||||
return context;
|
||||
}
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
context.setVariable(parameterNames[i], arguments[i]);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,5 +34,10 @@
|
||||
<artifactId>aioj-backend-common-feign</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.meowrain</groupId>
|
||||
<artifactId>aioj-backend-upms-api</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -32,6 +32,10 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-crypto</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -66,5 +70,11 @@
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.meowrain</groupId>
|
||||
<artifactId>aioj-backend-common-core</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,8 +1,8 @@
|
||||
package cn.meowrain.aioj.backend.userservice.dto.chains;
|
||||
|
||||
import cn.meowrain.aioj.backend.framework.designpattern.chains.AbstractChianHandler;
|
||||
import cn.meowrain.aioj.backend.framework.errorcode.ErrorCode;
|
||||
import cn.meowrain.aioj.backend.framework.exception.ClientException;
|
||||
import cn.meowrain.aioj.backend.framework.core.designpattern.chains.AbstractChianHandler;
|
||||
import cn.meowrain.aioj.backend.framework.core.errorcode.ErrorCode;
|
||||
import cn.meowrain.aioj.backend.framework.core.exception.ClientException;
|
||||
import cn.meowrain.aioj.backend.userservice.common.enums.ChainMarkEnums;
|
||||
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequestDTO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
Reference in New Issue
Block a user