refactor(审计日志): 重构审计日志模块,修复SQL插入错误

问题分析:AuditLog领域对象直接继承R2dbcRepository导致SQL插入时缺少entity_id字段

解决方案:参考OperationLog实现模式,新增Entity/Dao/Converter/Repository分层

测试验证:后端启动成功,调试测试通过
This commit is contained in:
张翔
2026-04-08 16:57:08 +08:00
parent 99c78954a3
commit 7e534f3049
6 changed files with 416 additions and 44 deletions
@@ -0,0 +1,87 @@
package cn.novalon.manage.db.converter;
import cn.novalon.manage.sys.audit.domain.AuditLog;
import cn.novalon.manage.db.entity.AuditLogEntity;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
/**
* 审计日志实体转换器
*
* @author 张翔
* @date 2026-04-08
*/
@Component
public class AuditLogConverter {
public AuditLog toDomain(AuditLogEntity entity) {
if (entity == null) {
return null;
}
AuditLog domain = new AuditLog();
domain.setId(entity.getId());
domain.setEntityType(entity.getEntityType());
domain.setEntityId(entity.getEntityId());
domain.setOperationType(entity.getOperationType());
domain.setOperator(entity.getOperator());
domain.setOperationTime(entity.getOperationTime());
domain.setBeforeData(entity.getBeforeData());
domain.setAfterData(entity.getAfterData());
domain.setChangedFields(entity.getChangedFields());
domain.setIpAddress(entity.getIpAddress());
domain.setUserAgent(entity.getUserAgent());
domain.setDescription(entity.getDescription());
domain.setCreateBy(entity.getCreateBy());
domain.setUpdateBy(entity.getUpdateBy());
domain.setCreatedAt(entity.getCreatedAt());
domain.setUpdatedAt(entity.getUpdatedAt());
domain.setDeletedAt(entity.getDeletedAt());
return domain;
}
public AuditLogEntity toEntity(AuditLog domain) {
if (domain == null) {
return null;
}
AuditLogEntity entity = new AuditLogEntity();
entity.setId(domain.getId());
entity.setEntityType(domain.getEntityType());
entity.setEntityId(domain.getEntityId());
entity.setOperationType(domain.getOperationType());
entity.setOperator(domain.getOperator());
entity.setOperationTime(domain.getOperationTime());
entity.setBeforeData(domain.getBeforeData());
entity.setAfterData(domain.getAfterData());
entity.setChangedFields(domain.getChangedFields());
entity.setIpAddress(domain.getIpAddress());
entity.setUserAgent(domain.getUserAgent());
entity.setDescription(domain.getDescription());
entity.setCreateBy(domain.getCreateBy());
entity.setUpdateBy(domain.getUpdateBy());
entity.setCreatedAt(domain.getCreatedAt());
entity.setUpdatedAt(domain.getUpdatedAt());
entity.setDeletedAt(domain.getDeletedAt());
return entity;
}
public List<AuditLog> toDomainList(List<AuditLogEntity> entities) {
if (entities == null) {
return null;
}
return entities.stream()
.map(this::toDomain)
.collect(Collectors.toList());
}
public List<AuditLogEntity> toEntityList(List<AuditLog> domains) {
if (domains == null) {
return null;
}
return domains.stream()
.map(this::toEntity)
.collect(Collectors.toList());
}
}
@@ -0,0 +1,53 @@
package cn.novalon.manage.db.dao;
import cn.novalon.manage.db.entity.AuditLogEntity;
import org.springframework.data.r2dbc.repository.R2dbcRepository;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.LocalDateTime;
/**
* 审计日志数据访问接口
*
* @author 张翔
* @date 2026-04-08
*/
@Repository
public interface AuditLogDao extends R2dbcRepository<AuditLogEntity, Long> {
Flux<AuditLogEntity> findByEntityTypeAndDeletedAtIsNull(String entityType);
Flux<AuditLogEntity> findByEntityIdAndDeletedAtIsNull(Long entityId);
Flux<AuditLogEntity> findByEntityTypeAndEntityIdAndDeletedAtIsNull(String entityType, Long entityId);
Flux<AuditLogEntity> findByOperatorAndDeletedAtIsNull(String operator);
Flux<AuditLogEntity> findByOperationTypeAndDeletedAtIsNull(String operationType);
Flux<AuditLogEntity> findByOperationTimeBetweenAndDeletedAtIsNull(LocalDateTime startTime, LocalDateTime endTime);
Flux<AuditLogEntity> findByEntityTypeAndOperationTimeBetweenAndDeletedAtIsNull(
String entityType,
LocalDateTime startTime,
LocalDateTime endTime
);
Flux<AuditLogEntity> findByOperatorAndOperationTimeBetweenAndDeletedAtIsNull(
String operator,
LocalDateTime startTime,
LocalDateTime endTime
);
Mono<Long> countByEntityTypeAndDeletedAtIsNull(String entityType);
Mono<Long> countByOperationTypeAndDeletedAtIsNull(String operationType);
Mono<Long> countByOperatorAndDeletedAtIsNull(String operator);
Mono<Long> countByOperationTimeBetweenAndDeletedAtIsNull(LocalDateTime startTime, LocalDateTime endTime);
Flux<AuditLogEntity> findByDeletedAtIsNull();
}
@@ -0,0 +1,135 @@
package cn.novalon.manage.db.entity;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;
/**
* 审计日志数据库实体类
*
* @author 张翔
* @date 2026-04-08
*/
@Table("audit_log")
public class AuditLogEntity extends BaseEntity {
@Column("entity_type")
private String entityType;
@Column("entity_id")
private Long entityId;
@Column("operation_type")
private String operationType;
@Column("operator")
private String operator;
@Column("operation_time")
private java.time.LocalDateTime operationTime;
@Column("before_data")
private String beforeData;
@Column("after_data")
private String afterData;
@Column("changed_fields")
private String[] changedFields;
@Column("ip_address")
private String ipAddress;
@Column("user_agent")
private String userAgent;
@Column("description")
private String description;
public String getEntityType() {
return entityType;
}
public void setEntityType(String entityType) {
this.entityType = entityType;
}
public Long getEntityId() {
return entityId;
}
public void setEntityId(Long entityId) {
this.entityId = entityId;
}
public String getOperationType() {
return operationType;
}
public void setOperationType(String operationType) {
this.operationType = operationType;
}
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
public java.time.LocalDateTime getOperationTime() {
return operationTime;
}
public void setOperationTime(java.time.LocalDateTime operationTime) {
this.operationTime = operationTime;
}
public String getBeforeData() {
return beforeData;
}
public void setBeforeData(String beforeData) {
this.beforeData = beforeData;
}
public String getAfterData() {
return afterData;
}
public void setAfterData(String afterData) {
this.afterData = afterData;
}
public String[] getChangedFields() {
return changedFields;
}
public void setChangedFields(String[] changedFields) {
this.changedFields = changedFields;
}
public String getIpAddress() {
return ipAddress;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public String getUserAgent() {
return userAgent;
}
public void setUserAgent(String userAgent) {
this.userAgent = userAgent;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
@@ -0,0 +1,130 @@
package cn.novalon.manage.db.repository;
import cn.novalon.manage.sys.audit.domain.AuditLog;
import cn.novalon.manage.sys.audit.repository.IAuditLogRepository;
import cn.novalon.manage.db.converter.AuditLogConverter;
import cn.novalon.manage.db.dao.AuditLogDao;
import cn.novalon.manage.db.entity.AuditLogEntity;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.LocalDateTime;
/**
* 审计日志仓储实现类
*
* @author 张翔
* @date 2026-04-08
*/
@Repository
public class AuditLogRepository implements IAuditLogRepository {
private final AuditLogDao auditLogDao;
private final AuditLogConverter auditLogConverter;
public AuditLogRepository(AuditLogDao auditLogDao, AuditLogConverter auditLogConverter) {
this.auditLogDao = auditLogDao;
this.auditLogConverter = auditLogConverter;
}
@Override
public Mono<AuditLog> findById(Long id) {
return auditLogDao.findById(id)
.map(auditLogConverter::toDomain);
}
@Override
public Mono<AuditLog> save(AuditLog auditLog) {
AuditLogEntity entity = auditLogConverter.toEntity(auditLog);
return auditLogDao.save(entity)
.map(auditLogConverter::toDomain);
}
@Override
public Mono<Void> deleteById(Long id) {
return auditLogDao.deleteById(id);
}
@Override
public Flux<AuditLog> findAll() {
return auditLogDao.findByDeletedAtIsNull()
.map(auditLogConverter::toDomain);
}
@Override
public Flux<AuditLog> findByEntityType(String entityType) {
return auditLogDao.findByEntityTypeAndDeletedAtIsNull(entityType)
.map(auditLogConverter::toDomain);
}
@Override
public Flux<AuditLog> findByEntityId(Long entityId) {
return auditLogDao.findByEntityIdAndDeletedAtIsNull(entityId)
.map(auditLogConverter::toDomain);
}
@Override
public Flux<AuditLog> findByEntityTypeAndEntityId(String entityType, Long entityId) {
return auditLogDao.findByEntityTypeAndEntityIdAndDeletedAtIsNull(entityType, entityId)
.map(auditLogConverter::toDomain);
}
@Override
public Flux<AuditLog> findByOperator(String operator) {
return auditLogDao.findByOperatorAndDeletedAtIsNull(operator)
.map(auditLogConverter::toDomain);
}
@Override
public Flux<AuditLog> findByOperationType(String operationType) {
return auditLogDao.findByOperationTypeAndDeletedAtIsNull(operationType)
.map(auditLogConverter::toDomain);
}
@Override
public Flux<AuditLog> findByOperationTimeBetween(LocalDateTime startTime, LocalDateTime endTime) {
return auditLogDao.findByOperationTimeBetweenAndDeletedAtIsNull(startTime, endTime)
.map(auditLogConverter::toDomain);
}
@Override
public Flux<AuditLog> findByEntityTypeAndOperationTimeBetween(
String entityType,
LocalDateTime startTime,
LocalDateTime endTime
) {
return auditLogDao.findByEntityTypeAndOperationTimeBetweenAndDeletedAtIsNull(entityType, startTime, endTime)
.map(auditLogConverter::toDomain);
}
@Override
public Flux<AuditLog> findByOperatorAndOperationTimeBetween(
String operator,
LocalDateTime startTime,
LocalDateTime endTime
) {
return auditLogDao.findByOperatorAndOperationTimeBetweenAndDeletedAtIsNull(operator, startTime, endTime)
.map(auditLogConverter::toDomain);
}
@Override
public Mono<Long> countByEntityType(String entityType) {
return auditLogDao.countByEntityTypeAndDeletedAtIsNull(entityType);
}
@Override
public Mono<Long> countByOperationType(String operationType) {
return auditLogDao.countByOperationTypeAndDeletedAtIsNull(operationType);
}
@Override
public Mono<Long> countByOperator(String operator) {
return auditLogDao.countByOperatorAndDeletedAtIsNull(operator);
}
@Override
public Mono<Long> countByOperationTimeBetween(LocalDateTime startTime, LocalDateTime endTime) {
return auditLogDao.countByOperationTimeBetweenAndDeletedAtIsNull(startTime, endTime);
}
}
@@ -1,9 +1,7 @@
package cn.novalon.manage.sys.audit.domain;
import cn.novalon.manage.sys.core.domain.BaseDomain;
import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;
import java.time.LocalDateTime;
@@ -13,75 +11,47 @@ import java.time.LocalDateTime;
* @author 张翔
* @date 2026-04-01
*/
@Table("audit_log")
@Schema(description = "审计日志实体")
public class AuditLog {
public class AuditLog extends BaseDomain {
@Id
@Schema(description = "主键ID")
private Long id;
@Column("entity_type")
@Schema(description = "实体类型(如User, Role等)", example = "User")
private String entityType;
@Column("entity_id")
@Schema(description = "实体ID", example = "1")
private Long entityId;
@Column("operation_type")
@Schema(description = "操作类型(CREATE, UPDATE, DELETE", example = "UPDATE")
private String operationType;
@Column("operator")
@Schema(description = "操作人", example = "admin")
private String operator;
@Column("operation_time")
@Schema(description = "操作时间")
private LocalDateTime operationTime;
@Column("before_data")
@Schema(description = "变更前数据(JSON格式)")
private String beforeData;
@Column("after_data")
@Schema(description = "变更后数据(JSON格式)")
private String afterData;
@Column("changed_fields")
@Schema(description = "变更字段列表")
private String[] changedFields;
@Column("ip_address")
@Schema(description = "IP地址", example = "192.168.1.100")
private String ipAddress;
@Column("user_agent")
@Schema(description = "用户代理")
private String userAgent;
@Column("description")
@Schema(description = "操作描述", example = "更新用户信息")
private String description;
@Column("created_at")
@Schema(description = "记录创建时间")
private LocalDateTime createdAt;
public AuditLog() {
this.operationTime = LocalDateTime.now();
this.createdAt = LocalDateTime.now();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEntityType() {
return entityType;
}
@@ -169,12 +139,4 @@ public class AuditLog {
public void setDescription(String description) {
this.description = description;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
}
@@ -1,8 +1,6 @@
package cn.novalon.manage.sys.audit.repository;
import cn.novalon.manage.sys.audit.domain.AuditLog;
import org.springframework.data.r2dbc.repository.R2dbcRepository;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -14,8 +12,15 @@ import java.time.LocalDateTime;
* @author 张翔
* @date 2026-04-01
*/
@Repository
public interface IAuditLogRepository extends R2dbcRepository<AuditLog, Long> {
public interface IAuditLogRepository {
Mono<AuditLog> findById(Long id);
Mono<AuditLog> save(AuditLog auditLog);
Mono<Void> deleteById(Long id);
Flux<AuditLog> findAll();
Flux<AuditLog> findByEntityType(String entityType);