目标

记录实体类属性的变化

1
2
3
4
5
6
7
8
9
10
package com.scjt.jypm.project.annotation;

public enum CompareType {
DEFAULT, // 默认直接比较
USER_NICKNAME, // 根据ID查找用户昵称
BOOLEAN_TO_TEXT, // 0/1 转换为 否/是
DICTIONARY, // 字典类型比较
DATE // 时间格式化比较
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.scjt.jypm.project.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface LogField {
String name(); // 字段名称
CompareType type() default CompareType.DEFAULT; // 比较类型
String dictType() default ""; // 字典类型(仅 DICTIONARY 类型使用)
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package com.scjt.jypm.project.util;

import com.scjt.jypm.project.annotation.CompareType;
import com.scjt.jypm.project.annotation.LogField;
import com.scjt.jypm.system.mapper.SysDictTypeMapper;
import com.scjt.jypm.system.mapper.SysUserMapper;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

public class LogHelper {

private final SysUserMapper sysUserMapper;
private final SysDictTypeMapper sysDictTypeMapper;

public LogHelper(SysUserMapper sysUserMapper, SysDictTypeMapper sysDictTypeMapper) {
this.sysUserMapper = sysUserMapper;
this.sysDictTypeMapper = sysDictTypeMapper;
}

private String formatValue(Object value, CompareType type, String dictType) {
if (value == null) {
return "空";
}
switch (type) {
case USER_NICKNAME:
return getUserNickname(String.valueOf(value));
case BOOLEAN_TO_TEXT:
return "1".equals(value) ? "是" : "否";
case DICTIONARY:
return getDictName(dictType, String.valueOf(value));
case DATE:
return value.toString(); // 直接调用 toString
default:
return value.toString();
}
}

private String getUserNickname(String userId) {
return Optional.ofNullable(userId)
.flatMap(id -> Optional.ofNullable(sysUserMapper.getNickNameById(id)))
.orElse("未知用户");
}

private String getDictName(String dictType, String value) {
return Optional.ofNullable(value)
.flatMap(l -> Optional.ofNullable(sysDictTypeMapper.selectDictTypeByTypeAndValue(dictType,value).getDictLabel()))
.orElse("空");
}

public <T> List<String> compareObjects(T oldData, T newData) throws IllegalAccessException {
List<String> changeLog = new ArrayList<>();
Class<?> clazz = oldData.getClass();

for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(LogField.class)) {
field.setAccessible(true);
LogField logField = field.getAnnotation(LogField.class);
String fieldName = logField.name();
CompareType type = logField.type();
String dictType = logField.dictType();
Object oldValue = field.get(oldData);
Object newValue = field.get(newData);

String formattedOldValue = formatValue(oldValue, type, dictType);
String formattedNewValue = formatValue(newValue, type, dictType);

if (!Objects.equals(formattedOldValue, formattedNewValue)) {
changeLog.add(String.format("[%s] 从 %s 修改为 %s", fieldName,
defaultIfEmpty(formattedOldValue), defaultIfEmpty(formattedNewValue)));
}
}
}
return changeLog;
}

private String defaultIfEmpty(String value) {
return (value == null || value.isEmpty()) ? "空" : value;
}
}

数据库

1
2
3
4
5
6
7
8
9
10
11
12
  CREATE TABLE `prj_operation_log` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`prj_number` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '项目编号',
`operation_type` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '操作类型',
`operator` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '操作人',
`operation_info` json DEFAULT NULL COMMENT '操作信息',
`create_time` datetime DEFAULT NULL COMMENT '创建日期',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新日期',
`log_type` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '日志类型(字典 project_log_type)',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=64 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='项目库详情操作日志表';