2 Commits
12ee14a62a
...
6383d3d9b5
Author | SHA1 | Message | Date |
---|---|---|---|
|
6383d3d9b5 |
Merge remote-tracking branch 'origin/milestone-20250727-金币重构三期' into milestone-20250727-金币重构三期
|
3 days ago |
|
a013bce6c1 |
8.19操作日志写入数据库完毕
|
3 days ago |
12 changed files with 310 additions and 35 deletions
-
108src/main/java/com/example/demo/RabbitMQ/LogAspect.java
-
39src/main/java/com/example/demo/RabbitMQ/LogConsumer.java
-
28src/main/java/com/example/demo/Util/SecurityUtils.java
-
48src/main/java/com/example/demo/config/RabbitMQConfig.java
-
15src/main/java/com/example/demo/config/interfac/Log.java
-
2src/main/java/com/example/demo/controller/coin/GoldDetailController.java
-
21src/main/java/com/example/demo/domain/DTO/OperationLogDTO.java
-
30src/main/java/com/example/demo/domain/entity/Log.java
-
25src/main/java/com/example/demo/domain/entity/OperationLog.java
-
1src/main/java/com/example/demo/domain/vo/coin/Page.java
-
13src/main/java/com/example/demo/mapper/coin/OperationLogMapper.java
-
15src/main/resources/application-test.yml
@ -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"; |
|||
} |
|||
} |
|||
} |
@ -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); |
|||
// 可以重试或记录到文件 |
|||
} |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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 ""; // 描述操作,如 "修改用户" |
|||
} |
@ -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; |
|||
} |
@ -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; // 更新时间 |
|||
} |
@ -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(); |
|||
|
|||
} |
@ -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); |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue