作为一个JAVA程序员, 都应该了解操作日志的需求吧? 这里介绍一个快速实现类似操作日志, 审核日志等审查功能的开源工具oplog4j.
实例
先来看一个OpLog4j生成的操作日志实例. 为了便于查看, 这里对内容做了格式化, 还添加了一些注释说明. 开发者可以根据这个实例的内容判断OpLog4j是否能满足产品的需求.
//以下内容是基于一个单元测试中, com.github.djbing85.model.DefaultOpLog的真实输出
summary: update user info //操作项名称, 这里是更新用户信息
operator: system admin //这里记录了操作人, 如果你的操作人是一个ID, 也可以在想要的地方进行转换
//变更前的UserBO, JSON格式
pre: {"auditStatus":0,"balance":815146771322186439.1516528490624142744280788974720053374767303466796875,"classifyMsg":"top secret: 06dd9897-8600-49bf-a61a-ef99dae25136","createTime":1599493177359,"ignoreField":"invisible.de272fdd-f483-4a5c-b841-3b6ab4932941","phone":"911","pswd":"The quick brown fox jumps over the lazy dog","status":0,"type":"e","userId":1,"userName":"jasper.d.0e6721ab-f88a-4144-908a-e5667792fe7a"}
//变更后的UserBO, JSON格式
post: {"auditStatus":1,"balance":5825067578147956587.33458620514737635875945898078498430550098419189453125,"classifyMsg":"top secret: 677b5c9d-b7f5-4ef3-9f67-d8b37391cbad","createTime":1599493177359,"ignoreField":"invisible.9a1c12c3-6ea9-4249-9176-9f13ddd464e9","phone":"911","pswd":"The quick brown fox jumps over the lazy dog","status":1,"type":"p","userId":1,"userName":"jasper.d.1454e20f-3f34-46d5-8c4e-cb20442348f8"}
//变更详情, 变更详情的内容是在com.github.djbing85.aop.DefaultOpLogAOPInterceptor.getModelDiff(Class<BO>, Object, Object)中生成的, 可以通过继承类的方式定制自己想要的变更详情样式.
diff:
//<变更项目名称> <变更前的值> --> <变更后的值>
User Name: jasper.d.6b25870e-bf31-448d-a3e2-8c19a87b915e --> jasper.d.7ea08e0e-0409-4da1-b283-e04de87a93d8
//状态的原值是0/1, 这里转换成了Enable/Disable, 详细的实现请参考后续的文档
Status: Enable --> Disable
//注意这里的0, 是没有正确进行fieldMapping的情况下, 取的原值展示
Audit Status: 0 --> PENDING
//对比原值-7500572336794443600.2980875307817212327421430018148384988307952880859375, 可见数字被格式化了
Balance: -7,500,572,336,794,443,600.3 --> 8,830,565,855,212,825,657.58
//操作的时间
opTime: Tue Sep 08 11:05:01 CST 2020
//类型
opType: UPDATE
//BO类
modelClass: class com.github.djbing85.test.xml.model.UserBO
上面这样详细的操作变更详情, 应该就可以满足大部分对于操作日志的需求了.
这里我们总结一下操作日志的几个要素:
操作日志的要素
要素 | 说明 |
---|---|
summary | 方法简要描述 |
operator | 操作人 |
pre | 变更前BO值 |
post | 变更后BO值 |
diff | 差异 |
opTime | 操作时间 |
opType | 操作类型, 新增/编辑/删除 |
modelClass | 操作日志bean的类型 |
实现流程
引入依赖
Maven Dependency
参考 Maven Center Repo, 如下引入MAVEN依赖
<dependency>
<groupId>com.github.djbing85</groupId>
<artifactId>oplog4j</artifactId>
<version>0.0.1-RC01</version>
</dependency>
Gradle Groovy
implementation 'com.github.djbing85:oplog4j:0.0.1-RC01'
Gradle Kotlin
implementation("com.github.djbing85:oplog4j:0.0.1-RC01")
配置总览
之后开发者所需要做的, 仅仅是通过一些简单配置, 就可以快速实现操作日志的功能
配置项目 | 说明 | 类 |
---|---|---|
Bean注释 | 标记BO Model bean的属性名称, 转换规则等等 | OpLogModel, OpLogField |
Service/Dao的切入方法 | 配置产生日志的方法, 该方法通常变更了对应BO的数据 | OpLogJoinPoint, OpLogParam, OpLogID |
Spring配置 | 支持xml方式和springboot方式配置; 如果是xml, 仅需要最少8行配置代码 | 参考Spring配置配置 |
Handler类 | 方法执行完毕后, 日志如果需要写入DB, 则需要在这里实现 | 实现对应BO Model bean的IOpLogHandler接口 |
下面我们一步步地介绍如何实现
Bean注释
对于需要生成操作日志的BO类, OpLog4j要求必需有且仅有一个ID做为主键, 目前暂时不支持复合主键的类;
特别地, 对于特定的组合bean, 也可以通过巧妙的配置达到生成操作日志的功能, 但这类组合bean也需要满足这样的要求: 能够由一个ID为主键从存储介质中加载.
Bean 注释的类是OpLogModel与OpLogField, 分别作用于类与属性上.
OpLogModel
这个annotation 的作用域是ElementType.TYPE
属性 | 说明 |
---|---|
daoBeanId | Spring中定义的DAO bean ID |
method | 如com.xxx.dao.UserBODao.getById(Long id), 则method应该取值: "getById" |
通过上面两个配置, 我们就可以从Spring中获取得到一个BO(Business Object)对应DAO的getById方法. 这个方法在生成操作日志时获取"变更前对象"和"变更后对象"起到了重要的作用.
OpLogField
这个annotation 的作用域是ElementType.FIELD
我们直接给出一个例子:
import java.math.BigDecimal;
import java.util.Date;
import com.github.djbing85.annotation.OpLogField;
import com.github.djbing85.annotation.OpLogModel;
//BO类注释, 注意代码中一定要有对应的com.xxx.dao.UserBODao.getById(Long id)方法
@OpLogModel(daoBeanId = "userBODao", method = "getById")
public class UserBO {
//如果有自定义的构造方法, 那么请一定补充一个默认构造方法, 否则会报异常导致无法生成操作日志
public UserBO() {}
//id = 0表示这是主键的第0个参数, 目前只支持一个属性做主键, 复合主键功能尚不支持;
//fieldName是属性的名称, 如果有国际化的需求, 需要注意这里
@OpLogField(id = 0, fieldName = "User ID")
private Long userId;
//fieldName为""或者null时, 会直接使用fieldName = "userName"
@OpLogField(fieldName = "User Name")
private String userName;
//DB中type的取值是p/e, 分别表示Personal/Enterprise, fieldMapping则是一个JSON, 在生成对比的变化内容时, 会把p/e转换成可读性更好的Personal/Enterprise.
@OpLogField(fieldName = "User Type", fieldMapping = "{\&