暂存提交
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
package com.baiyue.audit;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 审计微服务启动入口,初始化 Spring Boot 应用并扫描审计日志 Mapper。
|
||||
*/
|
||||
@MapperScan("com.baiyue.audit.mapper")
|
||||
@SpringBootApplication
|
||||
public class BaiyueAuditApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(BaiyueAuditApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.baiyue.audit.controller;
|
||||
|
||||
import com.baiyue.audit.dto.AuditIngestRequest;
|
||||
import com.baiyue.audit.service.AuditIngestService;
|
||||
import com.baiyue.common.core.api.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 内部审计事件接收接口,校验服务间提交的审计数据并交由审计服务持久化。
|
||||
*/
|
||||
@Tag(
|
||||
name = "Internal audit ingestion / 内部审计写入",
|
||||
description = "Internal service-to-service API for immutable business audit events. / 服务间写入不可变业务审计事件的内部接口。"
|
||||
)
|
||||
@RestController
|
||||
@RequestMapping("/internal/audit/events")
|
||||
public class AuditIngestController {
|
||||
@Resource
|
||||
private AuditIngestService service;
|
||||
@Operation(
|
||||
summary = "Ingest business audit event / 写入业务审计事件",
|
||||
description = "Validates and stores one sanitized event in baiyue_audit.sys_audit_log. / 校验并将一条脱敏事件写入 baiyue_audit.sys_audit_log。"
|
||||
)
|
||||
@ApiResponses({
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "202", description = "Event accepted and stored / 事件已接收并存储"),
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "Invalid event payload / 事件载荷无效"),
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "Audit storage failed / 审计存储失败")
|
||||
})
|
||||
@PostMapping
|
||||
@ResponseStatus(HttpStatus.ACCEPTED)
|
||||
public ApiResponse<Void> ingest(@Valid @RequestBody AuditIngestRequest event) {
|
||||
service.ingest(event);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.baiyue.audit.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.PositiveOrZero;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 内部审计写入请求契约,承载操作对象、租户与用户、请求链路、执行结果及脱敏快照。
|
||||
*/
|
||||
@Schema(name = "AuditIngestRequest", description = "Internal business audit event / 内部业务审计事件")
|
||||
public record AuditIngestRequest(
|
||||
@Schema(description = "Event code / 事件编码", example = "WAREHOUSE_UPDATE", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank @Size(max = 64) String eventType,
|
||||
@Schema(description = "Resource type / 资源类型", example = "WAREHOUSE", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank @Size(max = 64) String resourceType,
|
||||
@Schema(description = "Resource identifier / 资源标识", example = "190000000000000001")
|
||||
@Size(max = 128) String resourceId,
|
||||
@Schema(description = "Bilingual event description / 中英文事件描述", example = "Update warehouse / 更新仓库")
|
||||
@Size(max = 255) String description,
|
||||
@Schema(description = "Request ID / 请求 ID", example = "req-20260728-001")
|
||||
@Size(max = 128) String requestId,
|
||||
@Schema(description = "Distributed trace ID / 分布式链路 ID", example = "trace-20260728-001")
|
||||
@Size(max = 128) String traceId,
|
||||
@Schema(description = "Business data tenant ID / 业务数据租户 ID", example = "10001")
|
||||
Long tenantId,
|
||||
@Schema(description = "Operator tenant ID / 操作人所属租户 ID", example = "0")
|
||||
Long operatorTenantId,
|
||||
@Schema(description = "Operator user ID / 操作人用户 ID", example = "90001")
|
||||
Long userId,
|
||||
@Schema(description = "Operator username / 操作人用户名", example = "platform.admin")
|
||||
@Size(max = 128) String username,
|
||||
@Schema(description = "Application scope / 应用权限域", example = "PLATFORM")
|
||||
@Size(max = 64) String appCode,
|
||||
@Schema(description = "Client type / 客户端类型", example = "WEB")
|
||||
@Size(max = 32) String clientType,
|
||||
@Schema(description = "HTTP method / HTTP 方法", example = "PUT")
|
||||
@Size(max = 16) String httpMethod,
|
||||
@Schema(description = "Request URI / 请求 URI", example = "/api/platform/v1/warehouse-admin/190000000000000001")
|
||||
@Size(max = 512) String requestUri,
|
||||
@Schema(description = "Client IP / 客户端 IP", example = "10.0.0.10")
|
||||
@Size(max = 64) String clientIp,
|
||||
@Schema(description = "Request duration in milliseconds / 请求耗时(毫秒)", example = "35")
|
||||
@PositiveOrZero long durationMs,
|
||||
@Schema(description = "Whether the business operation succeeded / 业务操作是否成功", example = "true")
|
||||
boolean success,
|
||||
@Schema(description = "Error code when failed / 失败时错误码", example = "WAREHOUSE_NOT_FOUND")
|
||||
@Size(max = 64) String errorCode,
|
||||
@Schema(description = "Sanitized error message / 脱敏后的错误信息", example = "Warehouse not found / 仓库不存在")
|
||||
@Size(max = 512) String errorMessage,
|
||||
@Schema(description = "Sanitized JSON snapshot before change / 变更前脱敏 JSON 快照", example = "{\"status\":\"INACTIVE\"}")
|
||||
String beforeData,
|
||||
@Schema(description = "Sanitized JSON snapshot after change / 变更后脱敏 JSON 快照", example = "{\"status\":\"ACTIVE\"}")
|
||||
String afterData,
|
||||
@Schema(description = "Event occurrence time in UTC / 事件发生时间(UTC)", example = "2026-07-28T07:30:00Z", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull Instant occurredAt,
|
||||
@Schema(description = "Source service name / 来源服务名称", example = "warehouse-service")
|
||||
@Size(max = 64) String serviceName
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.baiyue.audit.entity;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 业务审计日志持久化实体,对应 {@code sys_audit_log} 表。
|
||||
* 用于保存跨租户敏感操作的不可变审计证据,不用于领域事件投递。
|
||||
*/
|
||||
@Schema(name = "AuditLog", description = "Stored business audit log / 已存储业务审计日志")
|
||||
@Getter
|
||||
@Setter
|
||||
@TableName("sys_audit_log")
|
||||
public class AuditLogEntity {
|
||||
@Schema(description = "Audit log ID / 审计日志 ID", example = "190000000000000201")
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private String eventType;
|
||||
private String resourceType;
|
||||
private String resourceId;
|
||||
private String description;
|
||||
private String requestId;
|
||||
private String traceId;
|
||||
private Long tenantId;
|
||||
private Long operatorTenantId;
|
||||
private Long userId;
|
||||
private String username;
|
||||
private String appCode;
|
||||
private String clientType;
|
||||
private String httpMethod;
|
||||
private String requestUri;
|
||||
private String clientIp;
|
||||
private Long durationMs;
|
||||
private Boolean success;
|
||||
private String errorCode;
|
||||
private String errorMessage;
|
||||
private String beforeData;
|
||||
private String afterData;
|
||||
private String serviceName;
|
||||
private Instant occurredAt;
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.baiyue.audit.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baiyue.audit.entity.AuditLogEntity;
|
||||
|
||||
/**
|
||||
* 审计日志 MyBatis-Plus Mapper,提供 {@code sys_audit_log} 表的单表写入和查询能力。
|
||||
*/
|
||||
public interface AuditLogMapper extends BaseMapper<AuditLogEntity> {
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.baiyue.audit.repository;
|
||||
|
||||
import com.baiyue.audit.dto.AuditIngestRequest;
|
||||
import com.baiyue.audit.entity.AuditLogEntity;
|
||||
import com.baiyue.audit.mapper.AuditLogMapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* 审计事件持久化仓储,负责将内部审计请求映射并追加写入审计日志表。
|
||||
*/
|
||||
@Repository
|
||||
public class AuditEventRepository {
|
||||
private final AuditLogMapper mapper;
|
||||
|
||||
public AuditEventRepository(AuditLogMapper mapper) {
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
public void save(AuditIngestRequest event) {
|
||||
AuditLogEntity entity = new AuditLogEntity();
|
||||
entity.setEventType(event.eventType());
|
||||
entity.setResourceType(event.resourceType());
|
||||
entity.setResourceId(event.resourceId());
|
||||
entity.setDescription(event.description());
|
||||
entity.setRequestId(event.requestId());
|
||||
entity.setTraceId(event.traceId());
|
||||
entity.setTenantId(event.tenantId());
|
||||
entity.setOperatorTenantId(event.operatorTenantId());
|
||||
entity.setUserId(event.userId());
|
||||
entity.setUsername(event.username());
|
||||
entity.setAppCode(event.appCode());
|
||||
entity.setClientType(event.clientType());
|
||||
entity.setHttpMethod(event.httpMethod());
|
||||
entity.setRequestUri(event.requestUri());
|
||||
entity.setClientIp(event.clientIp());
|
||||
entity.setDurationMs(event.durationMs());
|
||||
entity.setSuccess(event.success());
|
||||
entity.setErrorCode(event.errorCode());
|
||||
entity.setErrorMessage(event.errorMessage());
|
||||
entity.setBeforeData(jsonOrNull(event.beforeData()));
|
||||
entity.setAfterData(jsonOrNull(event.afterData()));
|
||||
entity.setOccurredAt(event.occurredAt());
|
||||
entity.setServiceName(event.serviceName());
|
||||
mapper.insert(entity);
|
||||
}
|
||||
|
||||
private static String jsonOrNull(String value) {
|
||||
return value == null ? "null" : value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.baiyue.audit.service;
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import com.baiyue.audit.dto.AuditIngestRequest;
|
||||
import com.baiyue.audit.repository.AuditEventRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 审计事件写入应用服务,在事务内接收并持久化不可变业务审计记录。
|
||||
*/
|
||||
@Service
|
||||
public class AuditIngestService {
|
||||
@Resource
|
||||
private AuditEventRepository repository;
|
||||
|
||||
|
||||
@Transactional
|
||||
public void ingest(AuditIngestRequest event) {
|
||||
repository.save(event);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user