feat(api): A5 审计采集、检索与导出

AuditInterceptor 成功 / 失败双写 audit_logs(响应前 await、脱敏、sink 缺失退化 Pino);登录 / 权限 / 客户 / 字典 / 配置等既有动作补审计;审计列表 / 详情 / 动作目录 / 异步 CSV 导出 / HMAC 签名下载;分区维护与保留归档占位;OpenAPI 快照更新。
This commit is contained in:
Stev_Wang committed 2026-11-04 16:26:42 +08:00
1 parent 6215f204ce
commit 6bdda6ec62
44 files changed
+3845 -127

No files matched your search

@@ -0,0 +1,55 @@
/**
* 审计保留与归档(A5-BE-04 占位)
*
* 口径(PRD Q-08 / QT-10,最终保留期仍待产品确认):
* - `audit.retention_months` 默认 12、下限 12(不得低于 12 个月);
* - `audit_logs` 是 append-only:**本服务不执行 UPDATE / DELETE**,只提供归档骨架;
* - 真正的归档(冷存储搬迁)依赖对象存储与任务中心,A5 只登记占位;
* - 归档动作本身也应写审计(后续批次接 `audit.retention.archive`)。
*
* 当前实现只读取配置并返回计划,不会修改任何审计数据。
*/
import { Inject, Injectable, Logger } from '@nestjs/common';
import { SystemConfigService } from '../system-config/system-config.service';
export interface AuditArchivePlan {
/** 计划保留月数(来自 system_configs,下限 12) */
retentionMonths: number;
/** 该时间之前的审计可归档(只读搬迁,不删除) */
before: Date;
/** 当前仅占位:实际搬迁未启用 */
supported: false;
message: string;
}
@Injectable()
export class AuditRetentionService {
private readonly logger = new Logger('AuditRetention');
constructor(@Inject(SystemConfigService) private readonly configs: SystemConfigService) {}
/** 计算归档窗口;`before` 由调用方传入用于日志 / 任务参数 */
async archiveBefore(before: Date): Promise<AuditArchivePlan> {
const retentionMonths = await this.configs.getNumber('audit.retention_months', 12);
this.logger.warn(
`审计归档为 A5 占位:before=${before.toISOString()} retentionMonths=${String(retentionMonths)}(未执行任何删除 / 更新)`,
);
return {
retentionMonths: Math.max(12, retentionMonths),
before,
supported: false,
message:
'审计日志 append-only,归档仅做只读冷存储搬迁、不删除数据;待对象存储与任务中心就绪后接入(QT-10 保留期最终值待确认)',
};
}
/** 计划归档窗口:now - retentionMonths;供后台任务 / 运维查询 */
async plan(): Promise<AuditArchivePlan> {
const retentionMonths = await this.configs.getNumber('audit.retention_months', 12);
const months = Math.max(12, retentionMonths);
const before = new Date();
before.setMonth(before.getMonth() - months);
return this.archiveBefore(before);
}
}