前言
需求:对业务模块基本信息进行版本记录和替换,记录下对应修改编辑得字段
实现方案:利用自定义注解@compare,利用反射记录下修改前后得值
承接上文:
利用方法注解和字段属性注解完成变更日志记录:
自定义@compare注解
/**
* 字段标记注解
* 自定义接口属性,有需要那些字段进特殊处理得都可以定义注解进行处理翻译
* @author zyqok
* @since 2022/05/05
*/
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Compare {
/**
* 字段名称
*/
String value();
/**
* 字典类型(根据定义得字典来进行翻译,常用)
*/
String dictType() default "";
/**
* 业务名称
*/
String bizName() default "";
/**
* 表名
*/
String tableName() default "";
/**
* 以下为自定义属性,根据业务情况自行定义
*/
boolean isIndustryFocus() default false;
boolean isDate() default false;
boolean isProvince() default false;
boolean isCity() default false;
boolean isRegion() default false;
}
编写compareUtil方法
compareUtils完整代码如下:
/**
* 使用须知: <br>
* (1)该工具类主要用于两个同类对象的属性值比较; <br>
* (2)使用本工具类前,请将对应的类属性上打上 @Compare("xxx") 注解,其中xxx为字段的表达名称;<br>
* (3)为了比较灵活,只有打了该注解才会进行比较,不打的字段则不会进行比较 <br>
* (4)比较后,只会返回有变化的字段,无变化的字符则不返回 <br>
*
* @author fzy
* @since 2022/05/05
*/
public class CompareUtils<T> {
@Autowired
private SysAreaService areaService;
private static final String COMMA = ",";
// 数据版本表
private static List<WfBusVersionOperLog> returnEndRecords;
private static List<WfBusVersionOperLog> returnEndRecordsSon;
public CompareUtils() {
// 数据主表
returnEndRecords = new ArrayList<>();
// 数据子表
returnEndRecordsSon = new ArrayList<>();
}
/**
* 属性比较
*
* @param source 源数据对象
* @param target 目标数据对象
* @param operType 操作类型
* @return 对应属性值的比较变化
*/
public List<WfBusVersionOperLog> compare(T source, T target, String operType) {
return compare(source, target, null, operType);
}
/**
* 属性比较(子列表比较)
*
* @param source 源数据对象LIST
* @param target 目标数据对象LIST
* @return 对应属性值的比较变化
*/
public List<WfBusVersionOperLog> compareSon(List<T> source, List<T> target, String operType) {
return compareSon(source, target, null, operType);
}
/**
* 属性比较
*
* @param source 源数据对象
* @param target 目标数据对象
* @param ignoreCompareFields 忽略比较的字段
* @return 对应属性值的比较变化
*/
public List<WfBusVersionOperLog> compare(T source, T target, List<String> ignoreCompareFields, String operType) {
if (Objects.isNull(source) && Objects.isNull(target)) {
return returnEndRecords;
}
Map<String, CompareNode> sourceMap = this.getFiledValueMap(source);
Map<String, CompareNode> targetMap = this.getFiledValueMap(target);
if (sourceMap.isEmpty() && targetMap.isEmpty()) {
return returnEndRecords;
}
// 如果源数据为空,则只显示目标数据,不显示属性变化情况
if (sourceMap.isEmpty()) {
doEmpty(targetMap, ignoreCompareFields, operType);
}
// 如果源数据不为空,则显示属性变化情况
doCompare(sourceMap, targetMap, ignoreCompareFields, operType);
return returnEndRecords;
}
/**
* 属性比较
*
* @param source 源数据对象List
* @param target 目标数据对象List
* @param ignoreCompareFields 忽略比较的字段
* @return 对应属性值的比较变化
*/
public List<WfBusVersionOperLog> compareSon(List<T> source, List<T> target, List<String> ignoreCompareFields, String operType) {
if (CollectionUtils.isEmpty(source) && CollectionUtils.isEmpty(target)) {
return returnEndRecordsSon;
}
/**
* 先比较列表数据个数是否变化(是否是新增。新增则无需比较,
* 如果是修改(如果某一个是空,要么进行了删除,要么进行了新增///,个数一致,则进行比较属性变化,个数不一致,先将新增得插入日志,再比对旧数据属性是否变化))
*/
// if (StringUtils.isNotBlank(operType) && OperTypeEnum.INSERT.getTypeNum().equals(operType)) {
// // 新增
// for (T t : target) {
// Map<String, CompareNode> filedValueMap = this.getFiledValueMap(t);
// if (!filedValueMap.isEmpty()) {
// doEmptySon(filedValueMap, ignoreCompareFields, operType);
// }
// }
// }
// 修改
if (StringUtils.isNotBlank(operType) && OperTypeEnum.UPDATE.getTypeNum().equals(operType)) {
// 比较个数
if (CollectionUtils.isEmpty(source) || CollectionUtils.isEmpty(target)) {
// 新增了数据,或者删除了数据
if (CollectionUtils.isEmpty(source)) {
// 新增了数据
for (T t : target) {
Map<String, CompareNode> filedValueMap = this.getFiledValueMap(t);
if (!filedValueMap.isEmpty()) {
doEmptySon(filedValueMap, ignoreCompareFields, OperTypeEnum.INSERT.getTypeNum());
}
}
}
if (CollectionUtils.isEmpty(target)) {
// 删除了数据
for (T t : source) {
Map<String, CompareNode> filedValueMap = this.getFiledValueMap(t);
if (!filedValueMap.isEmpty()) {
doEmptySon(filedValueMap, ignoreCompareFields, OperTypeEnum.DELETE.getTypeNum());
}
}
}
}
if (CollectionUtils.isNotEmpty(source) &&CollectionUtils.isNotEmpty(target)) {
// 比较个数
// 如果个数不一致(先将原有得 找的到的来进行比对 如果找的到就比较找不到就是删除了 然后再找另一个id为null得 都是新增)
// 那么遍历size最大得来进行查找
for (T t : source) {
// 在旧数据里面找到对应id得数据
Field[] declaredFields = t.getClass().getSuperclass().getDeclaredFields();
Optional<Field> first = Arrays.stream(declaredFields).filter(field -> "id".equals(field.getName())).findFirst();
first.get().setAccessible(true);
Object o = null;
try {
o = first.get().get(t);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// 有id数据
String idValue = (String) o;
T n = getOtherObjByid(idValue, target);
if (Objects.isNull(n)) {
// 被删除了
Map<String, CompareNode> filedValueMapDe = this.getFiledValueMap(t);
if (!filedValueMapDe.isEmpty()) {
doEmptySon(filedValueMapDe, ignoreCompareFields, OperTypeEnum.DELETE.getTypeNum());
}
} else {
// 比对
getChangeParamsSon(t, n, ignoreCompareFields, operType);
}
}
for (T t : target) {
Field[] declaredFields = t.getClass().getDeclaredFields();
Optional<Field> first = Arrays.stream(declaredFields).filter(field -> "oriId".equals(field.getName())).findFirst();
first.get().setAccessible(true);
Object o = null;
try {
o = first.get().get(t);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (Objects.isNull(o)) {
// 新增
// 新增
Map<String, CompareNode> filedValueMap = this.getFiledValueMap(t);
if (!filedValueMap.isEmpty()) {
doEmptySon(filedValueMap, ignoreCompareFields, OperTypeEnum.INSERT.getTypeNum());
}
} else {
continue;
}
}
}
}
return returnEndRecordsSon;
}
/**
* 在每个子表里面加上字段,记录之前得子列表id
*
* @param idValue
* @param source
* @return
*/
private T getOtherObjByid(String idValue, List<T> source) {
T endObj = null;
for (T t : source) {
// 在旧数据里面找到对应id得数据
Field[] declaredFields = t.getClass().getDeclaredFields();
Optional<Field> first = Arrays.stream(declaredFields).filter(field -> "oriId".equals(field.getName())).findFirst();
first.get().setAccessible(true);
Object o = null;
try {
o = first.get().get(t);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
String idValueIn = (String) o;
if (!Objects.isNull(o) && idValue.equals(idValueIn)) {
endObj = t;
}
}
return endObj;
}
private List<WfBusVersionOperLog> getChangeParamsSon(T source, T target, List<String> ignoreCompareFields, String operType) {
Map<String, CompareNode> sourceMap = this.getFiledValueMap(source);
Map<String, CompareNode> targetMap = this.getFiledValueMap(target);
if (sourceMap.isEmpty() && targetMap.isEmpty()) {
return returnEndRecordsSon;
}
// 如果源数据为空,则只显示目标数据,不显示属性变化情况
if (sourceMap.isEmpty()) {
doEmptySon(targetMap, ignoreCompareFields, operType);
}
// 如果源数据为空,则显示属性变化情况
doCompareSon(sourceMap, targetMap, ignoreCompareFields, operType);
return returnEndRecordsSon;
}
private void doEmpty(Map<String, CompareNode> targetMap, List<String> ignoreCompareFields, String operType) {
StringBuilder sb = new StringBuilder();
Collection<CompareNode> values = targetMap.values();
int size = values.size();
int current = 0;
String tableName = "";
String bizName = "";
for (CompareNode node : values) {
current++;
Object o = Optional.ofNullable(node.getFieldValue()).orElse("");
tableName = node.getTableName();
bizName = node.getBizName();
if (Objects.nonNull(ignoreCompareFields) && ignoreCompareFields.contains(node.getFieldKey())) {
continue;
}
if (o.toString().length() > 0) {
if (operType.equals(OperTypeEnum.INSERT.getTypeNum()) && !sb.toString().contains("新增")) {
sb.append("【新增】->");
}
if (operType.equals(OperTypeEnum.DELETE.getTypeNum()) && !sb.toString().contains("删除")) {
sb.append("【删除】->");
}
sb.append("[" + node.getFieldName() + ":" + o + "]");
if (current < size) {
sb.append(COMMA);
}
}
}
if (StringUtils.isNotBlank(sb.toString())) {
WfBusVersionOperLog changeParams = new WfBusVersionOperLog();
changeParams.setOperType(operType);
changeParams.setOperContent(sb.toString());
changeParams.setSonRowName(tableName);
changeParams.setBizName(bizName);
returnEndRecords.add(changeParams);
}
}
private void doEmptySon(Map<String, CompareNode> targetMap, List<String> ignoreCompareFields, String operType) {
StringBuilder sb = new StringBuilder();
Collection<CompareNode> values = targetMap.values();
int size = values.size();
int current = 0;
String tableName = "";
String bizName = "";
for (CompareNode node : values) {
current++;
Object o = Optional.ofNullable(node.getFieldValue()).orElse("");
tableName = node.getTableName();
bizName = node.getBizName();
if (Objects.nonNull(ignoreCompareFields) && ignoreCompareFields.contains(node.getFieldKey())) {
continue;
}
if (o.toString().length() > 0) {
if (operType.equals(OperTypeEnum.INSERT.getTypeNum()) && !sb.toString().contains("新增")) {
sb.append("【新增】->");
}
if (operType.equals(OperTypeEnum.DELETE.getTypeNum()) && !sb.toString().contains("删除")) {
sb.append("【删除】->");
}
sb.append("[" + node.getFieldName() + ":" + o + "]");
if (current < size) {
sb.append(COMMA);
}
}
}
if (StringUtils.isNotBlank(sb.toString())) {
WfBusVersionOperLog changeParams = new WfBusVersionOperLog();
changeParams.setOperType(operType);
changeParams.setOperContent(sb.toString());
changeParams.setSonRowName(tableName);
changeParams.setBizName(bizName);
returnEndRecordsSon.add(changeParams);
}
}
private void doCompare(Map<String, CompareNode> sourceMap, Map<String, CompareNode> targetMap, List<String> ignoreCompareFields, String operType) {
Set<String> keys = sourceMap.keySet();
for (String key : keys) {
CompareNode sn = sourceMap.get(key);
CompareNode tn = targetMap.get(key);
if (Objects.nonNull(ignoreCompareFields) && ignoreCompareFields.contains(sn.getFieldKey())) {
continue;
}
Object o = Optional.ofNullable(sn.getFieldValue()).orElse("");
String sv = Optional.ofNullable(sn.getFieldValue()).orElse("").toString();
String tv = Optional.ofNullable(tn.getFieldValue()).orElse("").toString();
// 只有两者属性值不一致时, 才显示变化情况
WfBusVersionOperLog params = new WfBusVersionOperLog();
if (o instanceof BigDecimal) {
// 当为空串置为0处理
if (StringUtils.isBlank(sv)) {
sv = "0";
}
if (StringUtils.isBlank(tv)) {
tv = "0";
}
if (new BigDecimal(sv).compareTo(new BigDecimal(tv)) != 0) {
params.setFieldKey(sn.getFieldKey());
params.setFieldName(sn.getFieldName());
params.setFieldOldValue(String.valueOf(new BigDecimal(sv).setScale(2,BigDecimal.ROUND_HALF_UP)));
params.setFieldNewValue(String.valueOf(new BigDecimal(tv).setScale(2,BigDecimal.ROUND_HALF_UP)));
params.setOperType(operType);
params.setSonRowName(sn.getTableName());
params.setBizName(sn.getBizName());
returnEndRecords.add(params);
}
} else {
// 投资时间类得比较
if (!sv.equals(tv)) {
params.setFieldKey(sn.getFieldKey());
params.setFieldName(sn.getFieldName());
params.setFieldOldValue(sv);
params.setFieldNewValue(tv);
params.setOperType(operType);
params.setSonRowName(sn.getTableName());
params.setBizName(sn.getBizName());
returnEndRecords.add(params);
}
}
}
}
private void doCompareSon(Map<String, CompareNode> sourceMap, Map<String, CompareNode> targetMap, List<String> ignoreCompareFields, String operType) {
Set<String> keys = sourceMap.keySet();
for (String key : keys) {
CompareNode sn = sourceMap.get(key);
CompareNode tn = targetMap.get(key);
if (Objects.nonNull(ignoreCompareFields) && ignoreCompareFields.contains(sn.getFieldKey())) {
continue;
}
Object o = Optional.ofNullable(sn.getFieldValue()).orElse("");
String sv = Optional.ofNullable(sn.getFieldValue()).orElse("").toString();
String tv = Optional.ofNullable(tn.getFieldValue()).orElse("").toString();
// 只有两者属性值不一致时, 才显示变化情况
WfBusVersionOperLog params = new WfBusVersionOperLog();
if (o instanceof BigDecimal) {
// 当为空串置为0处理
if (StringUtils.isBlank(sv)) {
sv = "0";
}
if (StringUtils.isBlank(tv)) {
tv = "0";
}
if (new BigDecimal(sv).compareTo(new BigDecimal(tv)) != 0) {
params.setFieldKey(sn.getFieldKey());
params.setFieldName(sn.getFieldName());
params.setFieldOldValue(String.valueOf(new BigDecimal(sv).setScale(2,BigDecimal.ROUND_HALF_UP)));
params.setFieldNewValue(String.valueOf(new BigDecimal(tv).setScale(2,BigDecimal.ROUND_HALF_UP)));
params.setOperType(operType);
params.setSonRowName(sn.getTableName());
params.setBizName(sn.getBizName());
returnEndRecordsSon.add(params);
}
} else {
// 投资时间类得比较
if (!sv.equals(tv)) {
params.setFieldKey(sn.getFieldKey());
params.setFieldName(sn.getFieldName());
params.setFieldOldValue(sv);
params.setFieldNewValue(tv);
params.setOperType(operType);
params.setSonRowName(sn.getTableName());
params.setBizName(sn.getBizName());
returnEndRecordsSon.add(params);
}
}
}
}
private Map<String, CompareNode> getFiledValueMap(T t) {
if (Objects.isNull(t)) {
return Collections.emptyMap();
}
Field[] fields = t.getClass().getDeclaredFields();
List<Field> list = new ArrayList<>();
if (!Objects.isNull(t.getClass().getSuperclass())) {
Field[] declaredFields = t.getClass().getSuperclass().getDeclaredFields();
list = Arrays.stream(declaredFields).filter(field -> !Objects.isNull(field.getAnnotation(Compare.class))).collect(Collectors.toList());
}
List<Field> asList = Arrays.asList(fields);
List<Field> end = new ArrayList<>();
end.addAll(list);
end.addAll(asList);
if (Objects.isNull(fields) || fields.length == 0) {
return Collections.emptyMap();
}
System.out.println(end);
Map<String, CompareNode> map = new LinkedHashMap();
Compare annotation = t.getClass().getAnnotation(Compare.class);
String oneJobValue = "";
for (Field field : end) {
Compare compareAnnotation = field.getAnnotation(Compare.class);
if (Objects.isNull(compareAnnotation)) {
continue;
}
field.setAccessible(true);
try {
String fieldKey = field.getName();
CompareNode node = new CompareNode();
node.setFieldKey(fieldKey);
node.setFieldValue(field.get(t));
// 如果是id,那么进行跳过
if (fieldKey.equals("id") || fieldKey.equals("oriId")) {
continue;
}
// 字典翻译
if (StringUtils.isNotBlank(compareAnnotation.dictType())) {
String dictValue = "";
if (ObjectUtil.isNotNull(field.get(t))) {
dictValue = DictUtils.getDictLabel(field.get(t).toString(), compareAnnotation.dictType(), "");
}
node.setFieldValue(dictValue);
}
// 是否是投资伦茨
if (compareAnnotation.isRound()) {
String roundsName = "";
if (ObjectUtil.isNotNull(field.get(t))) {
if (field.get(t).toString().contains(",") && field.get(t).toString().contains("[") && field.get(t).toString().contains("]")) {
String s = field.get(t).toString().substring(2, field.get(t).toString().length() - 2);
// 分割逗号
String[] split = s.split(",");
roundsName = getRoundAll(split);
} else {
roundsName = getRoundOne(field.get(t).toString());
}
}
node.setFieldValue(roundsName);
}
// 是否是聚焦行业
if (compareAnnotation.isIndustryFocus()) {
node.setFieldValue(convertToCascadeShow(field.get(t).toString()));
}
// 是否是时间
if (compareAnnotation.isDate() && ObjectUtil.isNotNull(field.get(t))) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
String string = format.format((Date) field.get(t));
node.setFieldValue(string);
}
// 是否是省 是否是市 是否是区
if ((compareAnnotation.isProvince() || compareAnnotation.isCity() || compareAnnotation.isRegion()) && ObjectUtil.isNotNull(field.get(t))) {
node.setFieldValue(AreaUtils.getNameById(field.get(t).toString()));
}
// 是否是基金管理人
if (compareAnnotation.isManageIds() && ObjectUtil.isNotNull(field.get(t))) {
node.setFieldValue(FundManageUtils.getName(field.get(t).toString()));
}
// 是否是用户
if (compareAnnotation.isUser() && ObjectUtil.isNotNull(field.get(t))) {
node.setFieldValue(UserUtils.getUser(field.get(t).toString()).getNickName());
}
// 是否是多选用户
if (compareAnnotation.isUsers() && ObjectUtil.isNotNull(field.get(t))) {
String roundsName = "";
if (ObjectUtil.isNotNull(field.get(t))) {
if (field.get(t).toString().contains(",") && field.get(t).toString().contains("[") && field.get(t).toString().contains("]")) {
String s = field.get(t).toString().substring(2, field.get(t).toString().length() - 2);
// 分割逗号
String[] split = s.split(",");
roundsName = getUserAll(split);
} else {
roundsName = getUserOne(field.get(t).toString());
}
}
node.setFieldValue(roundsName);
}
// 是否是多选字典
if (compareAnnotation.isMultiple() && ObjectUtil.isNotNull(field.get(t))) {
String roundsName = "";
if (ObjectUtil.isNotNull(field.get(t))) {
if (field.get(t).toString().contains(",") && field.get(t).toString().contains("[") && field.get(t).toString().contains("]")) {
String s = field.get(t).toString().substring(2, field.get(t).toString().length() - 2);
// 分割逗号
String[] split = s.split(",");
roundsName = getDictAll(split, compareAnnotation.dictType());
} else {
roundsName = getDictOne(field.get(t).toString(), compareAnnotation.dictType());
}
}
node.setFieldValue(roundsName);
}
// 一级职业
if (compareAnnotation.isOneJob() && ObjectUtil.isNotNull(field.get(t))) {
String roundsName = "";
if (ObjectUtil.isNotNull(field.get(t))) {
oneJobValue = field.get(t).toString();
roundsName = getDictOne(field.get(t).toString(), compareAnnotation.dictType());
}
node.setFieldValue(roundsName);
}
// 二级职业根据一级职业来进行筛选
if (compareAnnotation.isTwoJob() && ObjectUtil.isNotNull(field.get(t))) {
String dictType = compareAnnotation.dictType();
String type = dictType + "_" + oneJobValue;
node.setFieldValue(DictUtils.getDictLabel(field.get(t).toString(), type, ""));
}
// 公司翻译
node.setFieldName(compareAnnotation.value());
// 表名
node.setTableName(annotation.tableName());
// 业务名称
node.setBizName(annotation.bizName());
map.put(field.getName(), node);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
return map;
}
/**
* 将输入框中得级联行业数据格式化成字符数组显示
*
* @param value
* @return
*/
private static String convertToCascadeShow(String value) {
if (value.contains(",")) {
// 分割逗号进行取最后一级显示
String[] split = value.split(",");
// 查询str对应得行业
return IndustryUtils.getNameById(split[0], split[split.length - 2], split[split.length - 1]);
}
return IndustryUtils.getNameById(value);
}
/**
* 格式化方法
*
* @param split
* @return
*/
private static String getRoundOne(String split) {
if (split.contains("[")) {
String substring = split.substring(2);
if (substring.contains("]")) {
String s = substring.substring(0, substring.length() - 2);
return FormatUtils.getDictLabel(s, "rounds_investment", split.toString());
}
}
return "null";
}
/**
* 格式化方法
*
* @param split
* @return
*/
private static String getUserOne(String split) {
if (split.contains("[")) {
String substring = split.substring(2);
if (substring.contains("]")) {
String s = substring.substring(0, substring.length() - 2);
return UserUtils.getUser(s).getNickName();
}
}
return "null";
}
private static String getDictOne(String split, String dict) {
if (split.contains("[")) {
String substring = split.substring(2);
if (substring.contains("]")) {
String s = substring.substring(0, substring.length() - 2);
return FormatUtils.getDictLabel(s, dict, split.toString());
}
}
return "null";
}
/**
* 格式化方法
*
* @param split
* @return
*/
private static String getRoundAll(String[] split) {
String str = "";
for (int i = 0; i < split.length; i++) {
String replace = split[i].replace("\"", "");
if (i != 0) {
str += "、" + FormatUtils.getDictLabel(replace, "rounds_investment", split.toString());
} else {
str = FormatUtils.getDictLabel(replace, "rounds_investment", split.toString());
}
}
return str;
}
/**
* 格式化方法
*
* @param split
* @return
*/
private static String getUserAll(String[] split) {
String str = "";
for (int i = 0; i < split.length; i++) {
String replace = split[i].replace("\"", "");
if (i != 0) {
str += "、" + UserUtils.getUser(replace).getNickName();
} else {
str = UserUtils.getUser(replace).getNickName();
}
}
return str;
}
private static String getDictAll(String[] split, String dict) {
String str = "";
for (int i = 0; i < split.length; i++) {
String replace = split[i].replace("\"", "");
if (i != 0) {
str += "、" + FormatUtils.getDictLabel(replace, dict, split.toString());
} else {
str = FormatUtils.getDictLabel(replace, dict, split.toString());
}
}
return str;
}
}
- 核心方法是doCompare()、getFiledValueMap()方法,主要就是再这两个方法进行逻辑业务扩展,比如注解中得isDate格式化,isDicttype这些处理。
- 这里面主要是有两套逻辑,一套是无son结尾得,这是主表数据得逻辑处理;另一个是带son关键字得方法,这一套是针对子列表进行得逻辑处理判断。
模块测试
简单得实体类测试:
-
主表逻辑测试
主表字段修改(新增值或者修改删除值,查询是否输出记录)
-
子表逻辑测试
子表之前无,现在新增
子表之前有,现在删除
子表之前有1条,现在有两条
子表之前和现在是同一个数据,只是改变了子表字段值
子表之前有,现在再之前的基础上新增了一条
SysUser user1 = new SysUser();
user1.setUserName("张三");
user1.setPhonenumber("112");
SysDept sysDept = new SysDept();
sysDept.setDeptName("wanwan1");
sysDept.setLeader("张三");
List<SysDept> depts=new ArrayList<>();
SysDept sysDept11 = new SysDept();
sysDept11.setId("333");
sysDept11.setDeptName("wanwan3");
sysDept11.setLeader("张三2");
depts.add(sysDept);
depts.add(sysDept11);
user1.setDepts(depts);
user1.setDept(sysDept);
SysUser user2 = new SysUser();
user2.setUserName("李四");
user2.setPhonenumber("110");
SysDept sysDept2 = new SysDept();
sysDept2.setDeptName("wanwan2");
sysDept2.setLeader("李四");
// SysDept sysDept3 = new SysDept();
// sysDept3.setId("333");
// sysDept3.setDeptName("wanwan4");
// sysDept3.setLeader("李四2");
// SysDept sysDept4 = new SysDept();
// sysDept4.setDeptName("wanwan5");
// sysDept4.setLeader("李四2");
List<SysDept> deptss = new ArrayList<>();
deptss.add(sysDept2);
// deptss.add(sysDept3);
// deptss.add(sysDept4);
user2.setDepts(deptss);
user2.setDept(sysDept2);
List<WfBusVersionOperLog> result = new CompareUtils<SysUser>().compare(user1, user2, OperTypeEnum.UPDATE.getTypeNum());
List<WfBusVersionOperLog> result2 = new CompareUtils<SysDept>().compare(user1.getDept(), user2.getDept(), OperTypeEnum.UPDATE.getTypeNum());
List<WfBusVersionOperLog> results = new CompareUtils<SysDept>().compareSon(user1.getDepts(), user2.getDepts(), OperTypeEnum.UPDATE.getTypeNum());
// for (ChangeParams changeParams : result) {
// System.out.println("字段:" + changeParams.getFieldKey() + " 字段名: " + changeParams.getFieldName() + " 旧值:" + changeParams.getFieldOldValue() + " 新值:" + changeParams.getFieldNewValue());
// }
for (WfBusVersionOperLog changeParams : result2) {
System.out.println("字段:" + changeParams.getFieldKey() + " 字段名: " + changeParams.getFieldName() + " 旧值:" + changeParams.getFieldOldValue() + " 新值:" + changeParams.getFieldNewValue());
}
for (WfBusVersionOperLog changeParams : results) {
System.out.println(changeParams.getOperContent());
}
业务模块接入使用
// 主表前后版本字段比较,返回修改记录
List<WfBusVersionOperLog> compare = new CompareUtils<>().compare(oldDealBases, dealBase, OperTypeEnum.UPDATE.getTypeNum());
// 主表扩展表字段比较
List<WfBusVersionOperLog> compare2 = new CompareUtils<>().compare(oldDealBases.getDealBaseEquity(), dealBase.getDealBaseEquity(), OperTypeEnum.UPDATE.getTypeNum());
// 附件信息(同子表处理)
List<WfBusVersionOperLog> compareFile = new CompareUtils<FileBase>().compareSon(oldDealBases.getFileList(), dealBase.getFileList(), OperTypeEnum.UPDATE.getTypeNum());
效果展示
- 版本表
- 版本结合日志展示(主表变更数据日志展示)
- 版本结合日志展示(子表变更数据日志展示=>>新增或者删除格式)
- 版本结合日志展示(子表变更数据日志展示=>>修改)
版本表和日志表sql
- 版本表建表sql
-- 版本表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for wf_bus_version_records
-- ----------------------------
DROP TABLE IF EXISTS `wf_bus_version_records`;
CREATE TABLE `wf_bus_version_records` (
`id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '主键id',
`bus_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '业务id',
`bus_type` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '业务类型',
`bus_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '业务名称',
`bus_ori_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '业务主版本id',
`version_num` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '版本号',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '更新者',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
`remark` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '备注',
`del_flag` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '删除标识',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '业务表数据操作日志版本表' ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
- 日志表建表sql
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for wf_bus_version_oper_log
-- ----------------------------
DROP TABLE IF EXISTS `wf_bus_version_oper_log`;
CREATE TABLE `wf_bus_version_oper_log` (
`id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '主键id',
`biz_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '业务名称',
`field_key` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '字段名',
`field_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '字段中文名',
`field_old_value` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '字段修改前得值',
`field_new_value` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '字段修改后得值',
`oper_type` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作类型',
`version_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '版本id',
`oper_content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '操作描述',
`son_row_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '子表名称',
`son_tab_flag` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '子表行标识',
`del_flag` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '删除标识',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '业务数据操作日志' ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
总结
本文主要是针对实体类中包含子列表list数据得日志记录,我们很多文章都讲述了主列表的变更日志记录处理,但是针对子列表的日志记录处理相对较少,这是我在项目做的版本日志记录,亲测可用~