2 Commits

  1. 108
      src/main/java/com/example/demo/RabbitMQ/LogAspect.java
  2. 39
      src/main/java/com/example/demo/RabbitMQ/LogConsumer.java
  3. 28
      src/main/java/com/example/demo/Util/SecurityUtils.java
  4. 48
      src/main/java/com/example/demo/config/RabbitMQConfig.java
  5. 15
      src/main/java/com/example/demo/config/interfac/Log.java
  6. 2
      src/main/java/com/example/demo/controller/coin/GoldDetailController.java
  7. 21
      src/main/java/com/example/demo/domain/DTO/OperationLogDTO.java
  8. 30
      src/main/java/com/example/demo/domain/entity/Log.java
  9. 25
      src/main/java/com/example/demo/domain/entity/OperationLog.java
  10. 1
      src/main/java/com/example/demo/domain/vo/coin/Page.java
  11. 13
      src/main/java/com/example/demo/mapper/coin/OperationLogMapper.java
  12. 15
      src/main/resources/application-test.yml

108
src/main/java/com/example/demo/RabbitMQ/LogAspect.java

@ -0,0 +1,108 @@
package com.example.demo.RabbitMQ;
import com.example.demo.Util.SecurityUtils;
import com.example.demo.config.RabbitMQConfig;
import com.example.demo.config.interfac.Log;
import com.example.demo.domain.DTO.OperationLogDTO;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.time.LocalDateTime;
// com.example.demo.aspect.LogAspect.java
@Aspect
@Component
@Slf4j
public class LogAspect {
@Autowired
private RabbitTemplate rabbitTemplate;
@Around("@annotation(com.example.demo.config.interfac.Log)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String methodName = signature.getName();
String className = signature.getDeclaringTypeName();
Object[] args = joinPoint.getArgs();
Log logAnnotation = signature.getMethod().getAnnotation(Log.class);
String action = logAnnotation.value();
// 使用 Spring Security 获取真实用户
String username = SecurityUtils.getCurrentUsername();
Integer userId = SecurityUtils.getCurrentUserId();
String ip = getClientIp();
ObjectMapper mapper = new ObjectMapper();
String argsJson = "[]";
try {
argsJson = mapper.writeValueAsString(args);
} catch (Exception e) {
argsJson = "serialize failed";
}
Object result;
try {
result = joinPoint.proceed();
} catch (Exception e) {
log.error("方法执行异常: {}", e.getMessage());
throw e;
}
long duration = System.currentTimeMillis() - startTime;
// 构造日志消息 DTO
OperationLogDTO logDTO = new OperationLogDTO();
logDTO.setUserId(userId);
logDTO.setUsername(username);
logDTO.setAction(action);
logDTO.setIp(ip);
logDTO.setMethod(className + "." + methodName);
logDTO.setArgs(argsJson);
logDTO.setCreateTime(LocalDateTime.now());
System.out.println(logDTO);
// 发送消息到 RabbitMQ不等待
try {
rabbitTemplate.convertAndSend(RabbitMQConfig.LOG_EXCHANGE, "log.save", logDTO);
log.info("📩 日志消息已发送到 RabbitMQ: {}", action);
} catch (Exception e) {
log.error("发送日志消息到 RabbitMQ 失败", e);
}
log.info("✅ AOP 拦截完成: {} 执行 [{}] 耗时 {}ms", username, action, duration);
return result;
}
private String getClientIp() {
try {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip.split(",")[0].trim(); // 多代理时取第一个
} catch (Exception e) {
return "unknown";
}
}
}

39
src/main/java/com/example/demo/RabbitMQ/LogConsumer.java

@ -0,0 +1,39 @@
package com.example.demo.RabbitMQ;
import com.example.demo.config.RabbitMQConfig;
import com.example.demo.domain.DTO.OperationLogDTO;
import com.example.demo.domain.entity.OperationLog;
import com.example.demo.mapper.coin.OperationLogMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
// com.example.demo.consumer.LogConsumer.java
@Component
@Slf4j
public class LogConsumer {
@Autowired
private OperationLogMapper operationLogMapper;
@RabbitListener(queues = RabbitMQConfig.LOG_QUEUE)
public void consumeLog(OperationLogDTO logDTO) {
try {
OperationLog log = new OperationLog();
log.setUserId(logDTO.getUserId());
log.setUsername(logDTO.getUsername());
log.setAction(logDTO.getAction());
log.setIp(logDTO.getIp());
log.setMethod(logDTO.getMethod());
log.setArgs(logDTO.getArgs());
log.setCreateTime(logDTO.getCreateTime());
System.out.println("consumer" + log);
operationLogMapper.insertLog(log);
} catch (Exception e) {
log.error("持久化日志失败", e);
// 可以重试或记录到文件
}
}
}

28
src/main/java/com/example/demo/Util/SecurityUtils.java

@ -0,0 +1,28 @@
package com.example.demo.Util;
import com.example.demo.domain.entity.Admin;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
// com.example.demo.utils.SecurityUtils.java
public class SecurityUtils {
public static String getCurrentUsername() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return "anonymous";
}
return authentication.getName();
}
public static Integer getCurrentUserId() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return null;
}
if (authentication.getPrincipal() instanceof Admin userDetails) {
return userDetails.getId();
}
return null;
}
}

48
src/main/java/com/example/demo/config/RabbitMQConfig.java

@ -0,0 +1,48 @@
package com.example.demo.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
public static final String LOG_QUEUE = "operation_log_queue";
public static final String LOG_EXCHANGE = "operation_log_exchange";
@Bean
public Queue logQueue() {
return new Queue(LOG_QUEUE, true);
}
@Bean
public TopicExchange logExchange() {
return new TopicExchange(LOG_EXCHANGE);
}
@Bean
public Binding binding() {
return BindingBuilder.bind(logQueue())
.to(logExchange())
.with("log.*");
}
@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
template.setMessageConverter(messageConverter());
return template;
}
}

15
src/main/java/com/example/demo/config/interfac/Log.java

@ -0,0 +1,15 @@
package com.example.demo.config.interfac;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.Documented;
// com.example.demo.annotation.Log.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
String value() default ""; // 描述操作 "修改用户"
}

2
src/main/java/com/example/demo/controller/coin/GoldDetailController.java

@ -3,6 +3,7 @@ package com.example.demo.controller.coin;
import com.example.demo.Util.BusinessException; import com.example.demo.Util.BusinessException;
import com.example.demo.Util.JWTUtil; import com.example.demo.Util.JWTUtil;
import com.example.demo.Util.RedisLockUtil; import com.example.demo.Util.RedisLockUtil;
import com.example.demo.config.interfac.Log;
import com.example.demo.domain.DTO.GoldDetailDTO; import com.example.demo.domain.DTO.GoldDetailDTO;
import com.example.demo.domain.DTO.GoldUserDTO; import com.example.demo.domain.DTO.GoldUserDTO;
import com.example.demo.domain.entity.Admin; import com.example.demo.domain.entity.Admin;
@ -54,6 +55,7 @@ public class GoldDetailController {
@Autowired @Autowired
MarketService marketService; MarketService marketService;
@Log("获取金币明细")
@PostMapping("/getGoldDetail") @PostMapping("/getGoldDetail")
public Result getGoldDetail(@RequestBody Page page) throws Exception { public Result getGoldDetail(@RequestBody Page page) throws Exception {

21
src/main/java/com/example/demo/domain/DTO/OperationLogDTO.java

@ -0,0 +1,21 @@
package com.example.demo.domain.DTO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OperationLogDTO{
private Integer userId;
private String username;
private String action;
private String ip;
private String method;
private String args;
private LocalDateTime createTime;
}

30
src/main/java/com/example/demo/domain/entity/Log.java

@ -1,30 +0,0 @@
package com.example.demo.domain.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
@Data
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class Log implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id; // 日志id
private String ip; // ip地址
private Integer adminId; // 用户id
private String method; // 方法名
private String operation; // 操作
private Object params; // 参数
private String path; // 请求url
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private Date createTime; // 创建时间操作时间
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private Date updateTime; // 更新时间
}

25
src/main/java/com/example/demo/domain/entity/OperationLog.java

@ -0,0 +1,25 @@
package com.example.demo.domain.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
public class OperationLog {
@Id
private Integer id;
private Integer userId;
private String username;
private String action; // 操作描述
private String ip;
private String method; // 调用的方法名
private String args; // 参数JSON字符串
private LocalDateTime createTime = LocalDateTime.now();
}

1
src/main/java/com/example/demo/domain/vo/coin/Page.java

@ -18,7 +18,6 @@ import lombok.NoArgsConstructor;
public class Page { public class Page {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private String token;
private Integer pageNum; private Integer pageNum;
private Integer pageSize; private Integer pageSize;
private GoldDetail goldDetail; private GoldDetail goldDetail;

13
src/main/java/com/example/demo/mapper/coin/OperationLogMapper.java

@ -0,0 +1,13 @@
package com.example.demo.mapper.coin;// com.example.demo.mapper.OperationLogMapper.java
import com.example.demo.domain.entity.OperationLog;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface OperationLogMapper {
@Insert("INSERT INTO operation_log (user_id, username, action, ip, method, args, create_time) " +
"VALUES (#{userId}, #{username}, #{action}, #{ip}, #{method}, #{args}, NOW())")
void insertLog(OperationLog log);
}

15
src/main/resources/application-test.yml

@ -64,6 +64,17 @@ spring:
max-file-size: 10MB max-file-size: 10MB
max-request-size: 10MB max-request-size: 10MB
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
listener:
simple:
trusted-packages: com.example.demo.domain.DTO
data: data:
redis: redis:
database: 0 database: 0
@ -76,10 +87,6 @@ spring:
max-active: 20 max-active: 20
max-wait: -1 max-wait: -1
max-idle: 5 max-idle: 5
http://192:
168:
1:
50:8081:
file: file:
upload: upload:
url: http://39.101.133.168:8828/hljw/api/aws/upload url: http://39.101.133.168:8828/hljw/api/aws/upload

Loading…
Cancel
Save