数据变更记录
一、背景与问题
对于中后台系统中的数据都是非常重要的,但是如有人不小心修改了数据,异或有意而为之等等,这样都会对系统造成很大的影响,甚至对于公司可能也会造成一些影响。所以对于一个个重要的数据但凡谁去改动,都应该有详细的记录变更,就好比大家熟悉的git一样,任何变动都有对应的记录。
那么具体需要记录哪些呢?
- 时间:什么时候修改的
- 用户:具体谁修改的
- 设备:在哪个设备、ip等修改的
- 修改前:修改之前的数据
- 修改后:修改之后的数据
二、架构与思想
具体前端的架构设计请看前端数据变动记录设计 ;
对于后端而言应该做到如下:
- 对于一些简单变动, 只需记录一些 简单的字符串就可以
- 对于复杂javabean对象,需要进行 javabean对象的比较操作,比较的结果作为字符串存起来
- 任何数据都有: 新增、修改、删除 ;这三样类型应该默认提供
三、具体使用
3.1、定义数据业务类型
在DataTracerTypeEnum.java
中定义自己的业务类型,如下:
@AllArgsConstructor
@Getter
public enum DataTracerTypeEnum implements BaseEnum {
GOODS(1, "商品"),
OA_NOTICE(2, "OA-通知公告"),
OA_ENTERPRISE(3, "OA-企业信息");
private final Integer value;
private final String desc;
}
3.2、JavaBean注解
提供字段的如下几种注解用于 两个javabean之间的对象比较,生成满足git diff
格式的对比数据:
@DataTracerFieldBigDecimal
用于 BigDecimal 类型字段@DataTracerFieldDict
用于 字典 类型字段@DataTracerFieldEnum
用于 枚举 类型字段@DataTracerFieldLabel
用于 字段名称@DataTracerFieldSql
用于 sql 查询注入字段
比如:EnterpriseEntity.java
的javabean:
@Data
@TableName("t_oa_enterprise")
public class EnterpriseEntity {
@TableId(type = IdType.AUTO)
private Long enterpriseId;
@DataTracerFieldLabel("企业名称")
private String enterpriseName;
@DataTracerFieldLabel("企业logo")
private String enterpriseLogo;
@DataTracerFieldLabel("统一社会信用代码")
private String unifiedSocialCreditCode;
@DataTracerFieldLabel("类型")
@DataTracerFieldEnum(enumClass = EnterpriseTypeEnum.class)
private Integer type;
......
}
通过DataTracerService.getChangeContent(enterpriseDetail)
方法,可以拿到具体的对象内容
3.3、 新增、删除、修改
任何数据都有: 新增、修改、删除 ;这三样类型应该默认提供如下方法: DataTracerService.java
中,如下方法:
// 新增
DataTracerService.insert(1,DataTracerTypeEnum.GOODS); // 新增商品 记录
// 更新
DataTracerService.update(1,DataTracerTypeEnum.GOODS, oldGoods, newGoods); // 更新商品,传入新、旧 对象
// 删除
DataTracerService.delete(1,DataTracerTypeEnum.GOODS); // 删除商品 记录
DataTracerService.batchDelete(1,DataTracerTypeEnum.GOODS); // 批量删除商品 记录
3.4、其他记录
任何数据除了: 新增、修改、删除 ,还有其他操作记录,这个时候需要用到:
DataTracerService.addTrace(...); // 添加数据痕迹 方法
比如,企业信息中的调用EnterpriseService.updateEnterprise
方法中:
@Transactional(rollbackFor = Exception.class)
public ResponseDTO<String> updateEnterprise(EnterpriseUpdateForm updateVO) {
Long enterpriseId = updateVO.getEnterpriseId();
// 校验企业是否存在
EnterpriseEntity oldEnterprise = enterpriseDao.selectById(enterpriseId);
if (Objects.isNull(oldEnterprise) || oldEnterprise.getDeletedFlag()) {
return ResponseDTO.userErrorParam("企业不存在");
}
// 数据编辑
EnterpriseEntity newEnterprise = SmartBeanUtil.copy(oldEnterprise, EnterpriseEntity.class);
SmartBeanUtil.copyProperties(updateVO, newEnterprise);
enterpriseDao.updateById(newEnterprise);
//变更记录
DataTracerForm dataTracerForm = DataTracerForm.builder()
.dataId(updateVO.getEnterpriseId())
.type(DataTracerTypeEnum.OA_ENTERPRISE)
.content("修改企业信息")
.diffOld(dataTracerService.getChangeContent(oldEnterprise))
.diffNew(dataTracerService.getChangeContent(newEnterprise))
.build();
dataTracerService.addTrace(dataTracerForm);
return ResponseDTO.ok();
}
四、实现原理
4.1、表结构
t_data_tracer
表
其中 git diff
主要面向diff_old
和diff_new
两个字段的比较
4.2、解析Javabean
知道比较javabean对象
利用反射就可以解析,但是无法知道一些特殊事项:
- 比如
enterpriseName
字段的中文名称是什么意思?给用户显示总不能显示enterpriseName
,应该显示企业名称
字样 - 比如 对于一些枚举值,用户希望显示具体的中文含义,而不是 数值
- 比如 字典 字段,用户希望显示 字典的中文含义,而不是字典值
- 比如 关联关系id,用户希望显示 关联的对象信息,而不是 关联id
以上几个问题,使用几个注解来对应解决的,如下:
@DataTracerFieldBigDecimal 用于 BigDecimal 类型字段
@DataTracerFieldDict 用于 字典 类型字段
@DataTracerFieldEnum 用于 枚举 类型字段
@DataTracerFieldLabel 用于 字段名称
@DataTracerFieldSql 用于 sql 查询注入字段
具体如何解析这些注解,请看DataTracerChangeContentService.java
,具体代码
4.3、其他
整个datatracer
模块在 sa-base
项目中的 support.datatracer
包;
具体使用可以查看sa-admin
项目中的net.lab1024.sa.admin.module.business.oa.enterprise
包
联系我们
- 教育领域(高职院校数字化、就业创业大数据平台、继续教育平台;在线教育系统、视频直播、题库等,包含:医学、应急管理、成考、专升本等)
- 供应链领域(网络货运平台、大宗贸易进销存ERP、物流管理TMS、B2B电商、仓储WMS、AI提效等)
- 中医领域(诊所数字化管理、互联网医院、AI辅助诊疗、中医适宜技术、在线云问诊、空中药房等)
- AI+软件领域(软件定制外包、开源技术、数据大屏、国产化改造、技术升级换代、人员外包、技术顾问、技术培训等)
加微信: 卓大 拉你入群,一起学习 | 公众号 :六边形工程师 分享:赚钱、代码、生活 | 请 “1024创新实验室” 烩面里加肉 咖啡配胡辣汤,提神又饱腹 | 抖音 : 六边形工程师 直播:赚钱、代码、中医 |