scaffold-audit 审计模块
模块概述
scaffold-audit 提供业务数据变更审计和登录审计两大能力。通过 @AuditLog 注解标记 Controller 方法,结合 AOP 切面自动记录实体字段级别的变更详情(旧值 -> 新值)。登录审计模块记录用户的登录/登出事件,支持近 N 天登录统计。审计模块完全异步执行,不影响业务接口性能。
功能列表
- 数据变更审计:自动记录实体的字段级变更(旧值、新值、字段名、字段标签)
- 登录审计:记录登录/登出日志,支持按时间段统计
- 实体快照:通过 ThreadLocal 在更新前保存旧实体 JSON 快照
- 字段标签解析:自动读取实体字段的
@Schema(description) 注解作为中文标签
- 审计日志清理:按保留天数清理历史审计日志
- 变更摘要生成:自动生成人类可读的字段变更摘要文本
核心组件
| 层 |
类名 |
职责 |
| Annotation |
@AuditLog |
标记在 Controller 方法上,声明审计元数据 |
| Aspect |
AOP 切面 |
拦截 @AuditLog 注解方法,自动采集审计数据 |
| Controller |
AuditLogController |
审计日志查询接口,路径 /v1/audit/log |
| Controller |
LoginAuditController |
登录审计接口,路径 /v1/audit/login |
| Core |
AuditDataCollector |
对比新旧实体数据,生成字段变更列表 |
| Core |
EntityStateSnapshot |
ThreadLocal 实体快照管理,在 update 前保存旧值 JSON |
| Core |
FieldLabelResolver |
解析实体字段的中文名称(读取 @Schema 注解) |
| Service |
AuditLogService |
审计日志的存储与查询 |
| Service |
LoginAuditService |
登录审计的存储、查询与统计 |
| Entity |
AuditLog |
审计日志实体 |
| Entity |
AuditLoginLog |
登录审计日志实体 |
| Entity |
AuditFieldChange |
字段变更记录实体 |
| Config |
AuditAutoConfiguration |
自动配置类,启用异步和组件扫描 |
| Config |
AuditProperties |
配置属性类 |
配置参数
配置前缀:scaffold.audit
| 参数 |
类型 |
默认值 |
说明 |
enabled |
boolean |
true |
模块开关 |
retainDays |
Integer |
90 |
审计日志保留天数 |
maxFieldValueLength |
Integer |
2000 |
字段值最大记录长度 |
loginAuditEnabled |
boolean |
true |
是否启用登录审计 |
API 接口列表
审计日志 /v1/audit/log
| 方法 |
路径 |
说明 |
| GET |
/page |
分页查询审计日志 |
| GET |
/get/{id} |
审计日志详情(含字段变更列表) |
| GET |
/getByTraceId/{traceId} |
按链路追踪ID查询审计日志 |
| POST |
/clean?retainDays=N |
清理历史审计日志(保留最近 N 天) |
登录审计 /v1/audit/login
| 方法 |
路径 |
说明 |
| GET |
/page |
分页查询登录日志 |
| GET |
/stats/recent?days=7 |
近 N 天登录统计 |
@AuditLog 注解说明
@AuditLog 标注在 Controller 方法上,声明该操作的审计元数据。支持 SpEL 表达式从方法参数中动态提取业务 ID 和名称。
| 属性 |
类型 |
默认值 |
说明 |
module |
String |
- |
业务模块名称(必填) |
operation |
String |
- |
操作描述(必填) |
businessType |
String |
"" |
业务实体类名,支持 SpEL:#dto.class.simpleName |
businessId |
String |
"" |
业务ID,支持 SpEL:#id 或 #dto.id |
businessName |
String |
"" |
业务名称,支持 SpEL |
operType |
int |
0 |
操作类型:0-其他,1-新增,2-修改,3-删除 |
trackFields |
String[] |
{} |
要追踪的字段名列表,空数组=追踪所有变更字段 |
recordParams |
boolean |
true |
是否记录请求参数 |
注意:审计日志的 status 字段遵循布尔语义规范,1 = 正常,0 = 异常。
使用示例
在 Controller 上使用审计注解
@PostMapping("/update")
@AuditLog(
module = "商品管理",
operation = "更新商品",
businessType = "#dto.class.simpleName",
businessId = "#dto.id",
businessName = "#dto.productName",
operType = 2,
trackFields = {"name", "price", "status"}
)
public Result<Boolean> updateProduct(@RequestBody ProductUpdateDTO dto) {
return Result.success(productService.updateProduct(dto));
}
在 Service 中保存实体快照(用于修改操作的变更对比)
public Boolean updateProduct(ProductUpdateDTO dto) {
// 更新前:保存旧实体快照到 ThreadLocal
Product oldProduct = productRepository.getById(dto.getId());
EntityStateSnapshot.set(String.valueOf(dto.getId()), JSONUtil.toJsonStr(oldProduct));
// 执行更新
Product newProduct = BeanCopyUtils.copy(dto, Product.class);
productRepository.updateById(newProduct);
return true;
}
查询审计日志
// 分页查询审计日志
const { data } = await get('/v1/audit/log/page', {
params: { pageNum: 1, pageSize: 20, module: '商品管理' }
})
// 查看审计详情(含字段变更列表)
const { data: detail } = await get(`/v1/audit/log/get/${id}`)
// detail.fieldChanges = [
// { fieldName: 'price', fieldLabel: '价格', oldValue: '99.00', newValue: '129.00' }
// ]
注意事项
@AuditLog 的 businessId 和 businessName 支持 SpEL 表达式,可以引用方法参数。
- 修改操作需要在 Service 层手动调用
EntityStateSnapshot.set() 保存旧实体快照,否则无法生成字段变更记录。
FieldLabelResolver 通过读取实体字段的 @Schema(description) 注解解析中文标签,建议在实体类上标注该注解。
- 审计日志默认保留 90 天,可通过
scaffold.audit.retain-days 调整。
- 字段变更记录的最大值长度为 2000 字符,超出部分会被截断。
- 系统字段(id、createTime、updateTime、createBy、updateBy、deleted、tenantId)自动跳过,不记录变更。
- 模块启用条件:
scaffold.audit.enabled=true(默认启用),通过 AuditAutoConfiguration 自动配置。
- 状态枚举:审计日志的
status 字段遵循布尔语义规范,1 = 正常,0 = 异常。