提交 cc739885 authored 作者: 黄承天's avatar 黄承天

fix(log):日志模块有关日志生成的初始功能完成:AOP自动拦截保存日志/调用方法保存日志

上级 b0dbdee9
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>keystone</artifactId>
<groupId>org.matrix</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>kt-log</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.matrix</groupId>
<artifactId>kt-base</artifactId>
</dependency>
<dependency>
<groupId>org.matrix</groupId>
<artifactId>kt-user</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package org.matrix.log;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(basePackages = {"org.matrix.local","org.matrix.remote","org.matrix"})
public class LogApplication {
public static void main(String[] args) {
SpringApplication.run(LogApplication.class, args);
}
}
package org.matrix.log.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ForLog {
}
package org.matrix.log.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.Date;
import java.util.List;
/**
* @author C
* 日志实体类
* 使用MongoDB存储
*/
@Accessors(chain = true)
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(value = "Log对象", description = "日志")
@Document
public class Log {
@ApiModelProperty(value = "主键")
private Long id;
@ApiModelProperty(value = "所属项目id", position = 1)
private Long projectId;
@ApiModelProperty(value = "所属用户id", position = 2)
private Long userId;
@ApiModelProperty(value = "所属数据id", position = 3)
private Long dataId;
@ApiModelProperty(value = "所属数据类型 等同于其java类型", position = 4)
private String dataType;
@ApiModelProperty(value = "唯一标识id", position = 5)
private Long uniqueId;
@ApiModelProperty(value = "时间", position = 6)
private Date time;
@ApiModelProperty(value = "日志内容", position = 7)
private List<String> contents;
}
package org.matrix.log.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum Operate {
INSERT("INSERT", "数据保存"),
UPDATE("UPDATE", "数据更新"),
DELETE("DELETE", "数据删除");
private String name;
private String text;
}
package org.matrix.log.service;
import com.beust.jcommander.internal.Lists;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.matrix.entity.BaseEntity;
import org.matrix.local.service.impl.UserService;
import org.matrix.log.annotation.ForLog;
import org.matrix.log.entity.Log;
import org.matrix.log.enums.Operate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import sun.reflect.generics.tree.ClassSignature;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
* @author C
* Log日志制造者
* 有关日志的所有生成方式都在此实现
* 包括:AOP/API
*/
@Slf4j
@Aspect
@Service
public class LogCreator {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 保存一条日志
*
* @param uniqueId 唯一标识id 用于区分该条日志内容属于哪次执行操作
* @param projectId 所属项目id
* @param dataId 所属数据id
* @param dataType 所属数据类型
* @param content 日志内容
*/
public void saveForTest(Long uniqueId, Long projectId, Long dataId, String dataType, String content) {
Query query = Query.query(Criteria.where("uniqueId").is(uniqueId))
.addCriteria(Criteria.where("projectId").is(projectId))
.addCriteria(Criteria.where("dataId").is(dataId))
.addCriteria(Criteria.where("dataType").is(dataType));
Log original = mongoTemplate.findOne(query, Log.class);
if (Objects.nonNull(original)) {
List<String> contents = original.getContents();
contents.add(content);
mongoTemplate.upsert(query, Update.update("contents", contents), Log.class);
} else {
Log log = log(uniqueId, projectId, dataId, dataType, content);
mongoTemplate.insert(log);
}
}
//--------------------------------------|AOP|---------------------------------------------------
@After("execution(* com.baomidou.mybatisplus.core.mapper.BaseMapper.insert(Object))")
private void insert(JoinPoint joinPoint) {
boolean isForLog = isForLog(actualClassType(joinPoint));
if (isForLog) {
Log log = log(joinPoint, Operate.INSERT);
mongoTemplate.insert(log);
}
}
@After("execution(* com.baomidou.mybatisplus.core.mapper.BaseMapper.deleteById(Object))")
private void deleteById(JoinPoint joinPoint) {
boolean isForLog = isForLog(actualClassType(joinPoint));
if (isForLog) {
Log log = log(joinPoint, Operate.DELETE);
mongoTemplate.insert(log);
}
}
@After("execution(* com.baomidou.mybatisplus.core.mapper.BaseMapper.updateById(Object))")
private void updateById(JoinPoint joinPoint) {
boolean isForLog = isForLog(actualClassType(joinPoint));
if (isForLog) {
Log log = log(joinPoint, Operate.UPDATE);
mongoTemplate.insert(log);
}
}
//------------------------------------|PRIVATE|-------------------------------------------------
/**
* 转换Log对象
*
* @param joinPoint 切点对象
* @param operate 操作类型枚举
* @return Log对象
*/
private Log log(JoinPoint joinPoint, Operate operate) {
Object data = joinPoint.getArgs()[0];
Long userId = UserService.findNowUserId();
Long projectId = getDataProjectId(data);
Long dataId = getDataId(data);
String dataType = actualClassType(joinPoint);
List<String> contents = Lists.newArrayList(buildContents(data, operate));
return new Log()
.setId(dataId)
.setTime(new Date())
.setUserId(userId)
.setProjectId(projectId)
.setDataId(dataId)
.setDataType(dataType)
.setContents(contents);
}
/**
* 转换Log对象
*
* @param data log所属对象 例如被保存的User对象/被执行的Case对象
* @param content log内容
* @return Log对象
*/
private Log log(Object data, String content) {
Long userId = UserService.findNowUserId();
Long projectId = getDataProjectId(data);
Long dataId = getDataId(data);
String dataType = data.getClass().getTypeName();
return new Log()
.setId(dataId)
.setTime(new Date())
.setUserId(userId)
.setProjectId(projectId)
.setDataId(dataId)
.setDataType(dataType)
.setContents(Lists.newArrayList(content));
}
/**
* 转换Log对象
*
* @param uniqueId 唯一标识id 用于区分该条日志内容属于哪次执行操作
* @param projectId 所属项目id
* @param dataId 所属数据id
* @param dataType 所属数据类型
* @return Log对象
*/
private Log log(Long uniqueId, Long projectId, Long dataId, String dataType, String content) {
Long userId = UserService.findNowUserId();
return new Log()
.setId(dataId)
.setTime(new Date())
.setUserId(userId)
.setProjectId(projectId)
.setUniqueId(uniqueId)
.setDataId(dataId)
.setDataType(dataType)
.setContents(Lists.newArrayList(content));
}
/**
* 获取切点所属类的泛型名称
*
* @param joinPoint 切点对象
* @return 泛型名称
* 例如:
* 切点类是BaseMapper<User>
* 则返回org.matrix.local.entity.User
*/
private String actualClassType(JoinPoint joinPoint) {
//获取切点的标志
ClassSignature signature = (ClassSignature) joinPoint.getSignature();
//获取切点的参数(数组)
Type[] genericInterfaces = signature.getClass().getGenericInterfaces();
//取第1个为例
ParameterizedType genericParameterType = (ParameterizedType) genericInterfaces[0];
//获取到泛型(数组)
Type[] actualTypeArguments = genericParameterType.getActualTypeArguments();
return actualTypeArguments[0].getTypeName();
}
/**
* 该数据类型是否需要记日志
* 并非所有类型都需要被记日志
* 例如一些最下级的数据类型
*
* @param type 具体数据类型字符串 例如:org.matrix.local.entity.User
* @return bool结果
*/
private boolean isForLog(String type) {
try {
Class<?> clz = Class.forName(type);
return clz.isAnnotationPresent(ForLog.class);
} catch (ClassNotFoundException e) {
log.warn(e.getMessage());
return false;
}
}
private final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 制造一条日志内容字符串
*/
private String buildContents(Object data, Operate operate) {
String format = "%s %s [%s] ";
String type = data.getClass().getName();
String time = simpleDateFormat.format(new Date());
return String.format(format, time, operate.getName(), type, operate.getText());
}
/**
* 获取对象的projectId
*
* @param data 对象
* @return 对象的projectId
*/
private Long getDataProjectId(Object data) {
try {
Class<?> dataClass = data.getClass();
Field projectIdField = dataClass.getDeclaredField("projectId");
projectIdField.setAccessible(true);
return (Long) projectIdField.get(data);
} catch (Exception ignored) {
return 0L;
}
}
/**
* 获取对象的id
*
* @param data 对象
* @return 对象的id
*/
private Long getDataId(Object data) {
if (data instanceof Long) {
return (Long) data;
} else if (data instanceof BaseEntity) {
BaseEntity baseEntity = (BaseEntity) data;
return baseEntity.getId();
} else {
try {
Class<?> dataClass = data.getClass();
Field idField = dataClass.getDeclaredField("id");
idField.setAccessible(true);
return (Long) idField.get(data);
} catch (Exception ignored) {
return 0L;
}
}
}
}
server:
port: 8765
spring:
application:
name: keystone
data:
mongodb:
uri: mongodb://192.168.100.247:27017/key_stone
datasource:
dynamic:
primary: master
strict: true
datasource:
master:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.100.247:3306/key_stone?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: root
password: 123456
zentao:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.100.247:3306/zentao?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC&&zeroDateTimeBehavior=convertToNull
username: root
password: 123456
mybatis-plus:
type-enums-package: org.matrix.enums
baseJsPath: syntaxCheck.js
package org.matrix.log;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.junit.jupiter.api.Test;
import org.matrix.local.entity.User;
import org.matrix.local.mapper.UserMapper;
import org.matrix.local.service.impl.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
@SpringBootTest
class LogApplicationTests {
@Autowired
UserService userService;
@Test
void contextLoads() {
userService.create(new User().setAccount("test").setName("test").setPassword("test"));
}
public static void main(String[] args) {
Type[] genericInterfaces = UserMapper.class
.getGenericInterfaces();
//取第1个为例
ParameterizedType genericParameterType = (ParameterizedType) genericInterfaces[0];
//获取到泛型(数组)
Type[] actualTypeArguments = genericParameterType.getActualTypeArguments();
System.out.println(actualTypeArguments[0].getClass().getName());
System.out.println(actualTypeArguments[0].getTypeName());
}
}
......@@ -23,6 +23,7 @@
<module>kt-user</module>
<module>kt-database</module>
<module>kt-pub-service</module>
<module>kt-log</module>
</modules>
<properties>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论