数据审计
录入人/时间、修改人/时间自动填充
概述
数据审计用于自动记录数据的创建和修改信息,包括录入人、录入时间、修改人、修改时间等。
实现方式
数据审计基于字段值生成机制实现,需要定义相应的 ValueProcessor。
基本用法
实体定义
@Entity
@Table(name = "user")
public class User {
@Id
private Long id;
private String name;
// 录入人
@Column(name = "input_user_id")
@GeneratedValue(InputUserValueProcessor.class)
private Long inputUserId;
// 录入时间
@Column(name = "input_time")
@GeneratedValue(InputTimeValueProcessor.class)
private LocalDateTime inputTime;
// 修改人
@Column(name = "update_user_id")
@GeneratedValue(UpdateUserValueProcessor.class)
private Long updateUserId;
// 修改时间
@Column(name = "update_time")
@GeneratedValue(UpdateTimeValueProcessor.class)
private LocalDateTime updateTime;
}
数据库结构
CREATE TABLE user (
id BIGINT PRIMARY KEY,
name VARCHAR(50),
input_user_id BIGINT, -- 录入人
input_time DATETIME, -- 录入时间
update_user_id BIGINT, -- 修改人
update_time DATETIME -- 修改时间
);
ValueProcessor 实现
录入人处理器
public class InputUserValueProcessor implements ValueProcessor {
@Override
public boolean supports(FieldMeta fieldMeta) {
return "inputUserId".equals(fieldMeta.getJavaColumnName());
}
@Override
public EnumSet<ValueProcessPhase> phases() {
// 只在新增时触发
return EnumSet.of(ValueProcessPhase.INSERT);
}
@Override
public Object process(ValueProcessContext context) {
// 获取当前登录用户 ID
return UserContext.getCurrentUserId();
}
}
录入时间处理器
public class InputTimeValueProcessor implements ValueProcessor {
@Override
public boolean supports(FieldMeta fieldMeta) {
return "inputTime".equals(fieldMeta.getJavaColumnName());
}
@Override
public EnumSet<ValueProcessPhase> phases() {
// 只在新增时触发
return EnumSet.of(ValueProcessPhase.INSERT);
}
@Override
public Object process(ValueProcessContext context) {
return LocalDateTime.now();
}
}
修改人处理器
public class UpdateUserValueProcessor implements ValueProcessor {
@Override
public boolean supports(FieldMeta fieldMeta) {
return "updateUserId".equals(fieldMeta.getJavaColumnName());
}
@Override
public EnumSet<ValueProcessPhase> phases() {
// 在新增和更新时都触发
return EnumSet.of(ValueProcessPhase.INSERT, ValueProcessPhase.UPDATE);
}
@Override
public Object process(ValueProcessContext context) {
return UserContext.getCurrentUserId();
}
}
修改时间处理器
public class UpdateTimeValueProcessor implements ValueProcessor {
@Override
public boolean supports(FieldMeta fieldMeta) {
return "updateTime".equals(fieldMeta.getJavaColumnName());
}
@Override
public EnumSet<ValueProcessPhase> phases() {
// 在新增和更新时都触发
return EnumSet.of(ValueProcessPhase.INSERT, ValueProcessPhase.UPDATE);
}
@Override
public Object process(ValueProcessContext context) {
return LocalDateTime.now();
}
}
用户上下文
上下文接口
public class UserContext {
private static final ThreadLocal<Long> CURRENT_USER = new ThreadLocal<>();
public static void setCurrentUserId(Long userId) {
CURRENT_USER.set(userId);
}
public static Long getCurrentUserId() {
return CURRENT_USER.get();
}
public static void clear() {
CURRENT_USER.remove();
}
}
拦截器配置
@Component
public class UserContextInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
// 从请求中获取用户信息
Long userId = getUserIdFromToken(request);
UserContext.setCurrentUserId(userId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
UserContext.clear();
}
}
完整示例
// 用户实体
@Entity
@Table(name = "user")
public class User {
@Id
private Long id;
private String name;
@Column(name = "input_user_id")
@GeneratedValue(InputUserValueProcessor.class)
private Long inputUserId;
@Column(name = "input_time")
@GeneratedValue(InputTimeValueProcessor.class)
private LocalDateTime inputTime;
@Column(name = "update_user_id")
@GeneratedValue(UpdateUserValueProcessor.class)
private Long updateUserId;
@Column(name = "update_time")
@GeneratedValue(UpdateTimeValueProcessor.class)
private LocalDateTime updateTime;
}
// DAO 接口
@Repository
public interface UserDao extends SimpleDao<User, UserQuery, Long> {
}
// Service 使用
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void createUser(String name) {
User user = new User();
user.setName(name);
// inputUserId, inputTime, updateUserId, updateTime 自动填充
userDao.insert(user);
}
public void updateUserName(Long id, String newName) {
User user = userDao.findById(id);
user.setName(newName);
// updateUserId, updateTime 自动填充
userDao.updateById(user);
}
}
扩展场景
审计字段基类
@MappedSuperclass
public abstract class AuditEntity {
@Column(name = "input_user_id")
@GeneratedValue(InputUserValueProcessor.class)
private Long inputUserId;
@Column(name = "input_time")
@GeneratedValue(InputTimeValueProcessor.class)
private LocalDateTime inputTime;
@Column(name = "update_user_id")
@GeneratedValue(UpdateUserValueProcessor.class)
private Long updateUserId;
@Column(name = "update_time")
@GeneratedValue(UpdateTimeValueProcessor.class)
private LocalDateTime updateTime;
}
// 继承基类
@Entity
@Table(name = "user")
public class User extends AuditEntity {
@Id
private Long id;
private String name;
}
租户审计
@Column(name = "tenant_id")
@GeneratedValue(TenantValueProcessor.class)
private Long tenantId;
public class TenantValueProcessor implements ValueProcessor {
@Override
public boolean supports(FieldMeta fieldMeta) {
return "tenantId".equals(fieldMeta.getJavaColumnName());
}
@Override
public EnumSet<ValueProcessPhase> phases() {
return EnumSet.of(ValueProcessPhase.INSERT);
}
@Override
public Object process(ValueProcessContext context) {
return TenantContext.getCurrentTenantId();
}
}
注意事项
-
用户上下文必需:需要确保 UserContext 在操作前已设置用户信息
-
线程安全:使用 ThreadLocal 保证线程安全
-
上下文清理:请求结束后清理 ThreadLocal,避免内存泄漏
-
批量操作:批量操作时所有记录使用同一用户信息