package jnpf.audit.sdk;
|
|
import jnpf.audit.AuditEventApi;
|
import org.springframework.beans.factory.ObjectProvider;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.annotation.Bean;
|
import org.springframework.core.env.Environment;
|
|
/**
|
* audit-sdk 自动装配(Task 7 Step 5)。
|
*
|
* <p>注册机制:{@code META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports}
|
* ——平台默认构建走 JDK21/boot3 profile(Spring Boot 3.5.7),Spring Boot 3 已移除 spring.factories
|
* 的自动装配入口,必须用 {@code .imports};且该机制自 Spring Boot 2.7 起即受支持,对 boot2 profile 亦向后兼容。
|
*
|
* <p>{@code audit.enabled}(默认 true,缺省视为启用)为总开关;置 false 时整套 Bean 不装配。
|
* 各 Bean 均加 {@link ConditionalOnMissingBean} 供宿主按需覆盖。
|
*
|
* <p><b>层次现状(2026-08-11 起)</b>:SDK 侧只剩层 2({@code @AuditLog} 切面)与层 3
|
* ({@link AuditClient} 显式埋点)。层 1(MyBatis Executor 插件)已整套移除——它自交付起
|
* 一直默认关,{@code audit_events} 表内 {@code source_layer=1} 零行,属未启用即负债。
|
* 需要恢复见 {@code docs/handoff-2026-08-11-audit-into-biz-common.md} 记的 git revert 坐标。
|
*/
|
@AutoConfiguration
|
@ConditionalOnProperty(name = "audit.enabled", matchIfMissing = true)
|
public class AuditSdkAutoConfiguration {
|
|
@Bean
|
@ConditionalOnMissingBean
|
public AuditContextCollector auditContextCollector(Environment environment) {
|
return new AuditContextCollector(environment.getProperty("spring.application.name"));
|
}
|
|
/**
|
* spool 双层锁(Codex 批次二复验 #1/#2 终局修复):writer 与 replayer <b>共享同一实例</b>,
|
* 把彼此的 spool 文件操作串行化(进程内 monitor + 跨进程 {@code spool.lock} FileLock)。
|
*/
|
@Bean
|
@ConditionalOnMissingBean
|
public AuditSpoolLock auditSpoolLock(
|
@Value("${audit.spool-dir:./audit-spool}") String spoolDir) {
|
return new AuditSpoolLock(spoolDir);
|
}
|
|
@Bean
|
@ConditionalOnMissingBean
|
public AuditSpoolWriter auditSpoolWriter(
|
@Value("${audit.spool-dir:./audit-spool}") String spoolDir,
|
AuditSpoolLock spoolLock) {
|
return new AuditSpoolWriter(spoolDir, spoolLock);
|
}
|
|
@Bean
|
@ConditionalOnMissingBean
|
public AuditEventPublisher auditEventPublisher(ObjectProvider<AuditEventApi> feignApi,
|
AuditSpoolWriter spoolWriter) {
|
return new AuditEventPublisher(feignApi, spoolWriter);
|
}
|
|
@Bean
|
@ConditionalOnMissingBean
|
public AuditSpoolReplayer auditSpoolReplayer(AuditEventPublisher publisher,
|
@Value("${audit.spool-dir:./audit-spool}") String spoolDir,
|
@Value("${audit.spool-replay-enabled:true}") boolean replayEnabled,
|
// 调度周期(毫秒):默认 300000(5 分钟,spec);测试环境可调短加速降级链重放验证。
|
// 仅改轮询频率,不改孤儿年龄安全阈值(见 AuditSpoolReplayer.ORPHAN_REPLAYING_AGE_MS)。
|
@Value("${audit.spool-replay-interval-ms:300000}") long replayIntervalMs,
|
AuditSpoolLock spoolLock) {
|
return new AuditSpoolReplayer(publisher, spoolDir, replayEnabled, spoolLock, replayIntervalMs);
|
}
|
|
@Bean
|
@ConditionalOnMissingBean
|
public AuditRequestScopeFilter auditRequestScopeFilter(
|
@Value("${audit.request-scope.frontend-only:false}") boolean frontendOnly,
|
@Value("${audit.request-scope.allowed-app-codes:}") String allowedAppCodes) {
|
return new AuditRequestScopeFilter(frontendOnly, allowedAppCodes);
|
}
|
|
@Bean
|
@ConditionalOnMissingBean
|
public AuditClient auditClient(AuditContextCollector collector,
|
AuditEventPublisher publisher,
|
AuditRequestScopeFilter requestScopeFilter,
|
@Value("${audit.enabled:true}") boolean enabled) {
|
return new AuditClient(collector, publisher, enabled, requestScopeFilter);
|
}
|
|
/**
|
* 层 2 切面(默认开启,可用 audit.layer2-enabled=false 单独关)。
|
* @ConditionalOnClass 防止宿主没引 AOP starter 时装配失败。
|
*/
|
@Bean
|
@ConditionalOnMissingBean
|
@ConditionalOnClass(org.aspectj.lang.ProceedingJoinPoint.class)
|
@ConditionalOnProperty(name = "audit.layer2-enabled", matchIfMissing = true)
|
public jnpf.audit.sdk.aspect.AuditLogAspect auditLogAspect(AuditClient auditClient,
|
AuditEventPublisher publisher,
|
ApplicationContext applicationContext) {
|
return new jnpf.audit.sdk.aspect.AuditLogAspect(auditClient, publisher, applicationContext);
|
}
|
}
|