From aedca161ecc77dc1b8b9fe05259e5c4bba9aa89d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=94?= Date: Sat, 18 Apr 2026 13:04:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(audit):=20=E5=A2=9E=E5=BC=BA=E5=AE=A1?= =?UTF-8?q?=E8=AE=A1=E6=97=A5=E5=BF=97=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加数据库迁移脚本,为 audit_log 表补充缺失字段(create_by, update_by, updated_at, deleted_at) - 改进审计日志切面,排除对审计日志实体本身的审计,避免循环依赖 - 优化 ObjectMapper 配置,禁用时间戳序列化和自引用失败 --- .../gym/manage/app/config/JacksonConfig.java | 8 +++++++ .../V15__Add_missing_audit_log_columns.sql | 12 ++++++++++ .../gym/manage/sys/audit/AuditLogAspect.java | 23 +++++++++++++++---- 3 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 gym-manage-api/manage-db/src/main/resources/db/migration/V15__Add_missing_audit_log_columns.sql diff --git a/gym-manage-api/manage-app/src/main/java/cn/novalon/gym/manage/app/config/JacksonConfig.java b/gym-manage-api/manage-app/src/main/java/cn/novalon/gym/manage/app/config/JacksonConfig.java index a654065..1261426 100644 --- a/gym-manage-api/manage-app/src/main/java/cn/novalon/gym/manage/app/config/JacksonConfig.java +++ b/gym-manage-api/manage-app/src/main/java/cn/novalon/gym/manage/app/config/JacksonConfig.java @@ -3,6 +3,8 @@ package cn.novalon.gym.manage.app.config; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; @@ -39,6 +41,12 @@ public class JacksonConfig { javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter)); objectMapper.registerModule(javaTimeModule); + + SimpleModule longModule = new SimpleModule(); + longModule.addSerializer(Long.class, ToStringSerializer.instance); + longModule.addSerializer(Long.TYPE, ToStringSerializer.instance); + objectMapper.registerModule(longModule); + objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); diff --git a/gym-manage-api/manage-db/src/main/resources/db/migration/V15__Add_missing_audit_log_columns.sql b/gym-manage-api/manage-db/src/main/resources/db/migration/V15__Add_missing_audit_log_columns.sql new file mode 100644 index 0000000..d4a278f --- /dev/null +++ b/gym-manage-api/manage-db/src/main/resources/db/migration/V15__Add_missing_audit_log_columns.sql @@ -0,0 +1,12 @@ +-- Novalon管理系统审计日志表补充字段 +-- 版本: V15 +-- 描述: 为审计日志表添加缺失的基础字段,与BaseDomain保持一致 +ALTER TABLE audit_log ADD COLUMN IF NOT EXISTS create_by VARCHAR(50); +ALTER TABLE audit_log ADD COLUMN IF NOT EXISTS update_by VARCHAR(50); +ALTER TABLE audit_log ADD COLUMN IF NOT EXISTS updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP; +ALTER TABLE audit_log ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMP; + +COMMENT ON COLUMN audit_log.create_by IS '创建人'; +COMMENT ON COLUMN audit_log.update_by IS '更新人'; +COMMENT ON COLUMN audit_log.updated_at IS '更新时间'; +COMMENT ON COLUMN audit_log.deleted_at IS '删除时间'; diff --git a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/audit/AuditLogAspect.java b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/audit/AuditLogAspect.java index d3e1946..8a60aa8 100644 --- a/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/audit/AuditLogAspect.java +++ b/gym-manage-api/manage-sys/src/main/java/cn/novalon/gym/manage/sys/audit/AuditLogAspect.java @@ -2,8 +2,12 @@ package cn.novalon.gym.manage.sys.audit; import cn.novalon.gym.manage.sys.audit.domain.AuditLog; import cn.novalon.gym.manage.sys.audit.service.IAuditLogService; +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @@ -39,12 +43,17 @@ public class AuditLogAspect { public AuditLogAspect(IAuditLogService auditLogService, ObjectMapper objectMapper) { this.auditLogService = auditLogService; - this.objectMapper = objectMapper; + this.objectMapper = new ObjectMapper() + .registerModule(new JavaTimeModule()) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .disable(SerializationFeature.FAIL_ON_SELF_REFERENCES); } - @Around("execution(* cn.novalon.gym.manage.db.repository.*Repository.save(..)) || " + + @Around("(execution(* cn.novalon.gym.manage.db.repository.*Repository.save(..)) || " + "execution(* cn.novalon.gym.manage.db.repository.*Repository.delete(..)) || " + - "execution(* cn.novalon.gym.manage.db.repository.*Repository.deleteById(..))") + "execution(* cn.novalon.gym.manage.db.repository.*Repository.deleteById(..))) && " + + "!execution(* cn.novalon.gym.manage.db.repository.AuditLogRepository.*(..)) && " + + "!execution(* cn.novalon.gym.manage.db.dao.AuditLogDao.*(..))") public Object logAuditEvent(ProceedingJoinPoint joinPoint) throws Throwable { String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getSimpleName(); @@ -72,6 +81,12 @@ public class AuditLogAspect { private Object handleSaveOperation(ProceedingJoinPoint joinPoint, Object entity, String entityType, String operationType) throws Throwable { + String entityClassName = entity.getClass().getSimpleName(); + if (entityClassName.contains("AuditLog") || entityClassName.contains("AuditLogEntity")) { + logger.debug("跳过审计日志实体的审计记录: {}", entityClassName); + return joinPoint.proceed(); + } + try { final String[] beforeDataHolder = {null}; final Long[] entityIdHolder = {null}; @@ -197,7 +212,7 @@ public class AuditLogAspect { .then(); }) .onErrorResume(error -> { - logger.error("创建审计日志失败,但不影响主流程: {}", error.getMessage()); + logger.error("创建审计日志失败,但不影响主流程: {}", error.getMessage(), error); return Mono.empty(); }); }