Skip to content
bitzorcas
中EN

Guide

Auditing 保留、清理与合规操作

说明租户保留策略、法定下限、逐租户逐分类调度、有界物理删除、Production 审批、影响预估和仍需治理的边界。

Last updated

保留策略要同时回答四件事:删哪类证据、属于哪个租户、早于什么时间,以及谁批准了这次不可逆操作。当前实现已经把租户和逻辑范围带到存储端,不再出现“一类策略触发全表删除”的旧偏差。

1. 默认期限与法定下限

0 表示永久保留;正数表示只保留最近 N 天。租户策略保存时,正数不能低于对应下限。

数据全局/租户默认允许清理的最低天数
HTTP Request180180
业务 Activity0,永久90
EntityChange0,永久90
Security0,永久365
Exception365365
ExternalRequest180180
CapConsumer9090
BackgroundJob9090

租户策略实体还限制最大值为 365,000 天,并以 ExpectedVersion 做乐观并发。未保存显式策略的租户使用默认值。

2. 租户策略 API

方法与路由行为
GET /api/audit/retention-policy返回显式策略或默认值、法定下限、版本和 IsConfigured
PUT /api/audit/retention-policy校验所有期限与 ExpectedVersion 后创建或更新
POST /api/audit/retention-policy/preview估算拟定期限下各类别可清理行数

三个入口只接受认证主体的租户所有者,不允许平台租户 0,也不因 operate-as 自动切换到目标租户。

当前授权映射有一项需要单独治理:保存命令与读取/预估一样使用 auditing.audit.view。这意味着具备查看权限的主体也可能修改保留策略。生产 RBAC 应暂时只把该权限授予审计管理员,并计划把 mutation 拆到 Update/Manage 动作。

3. 影响预估的准确边界

Preview 使用同一个 IAuditQueryPort 统计截止时间之前的记录,适合在保存前评估数量,但它不是删除审批,也不是执行结果保证。

当前源码遍历七个 AuditCategory,而 HTTP Request 与业务 Activity 共用 Activity 类别:

  • RequestRetentionDays 没有形成独立预估行;
  • Activity 的 TotalCount 可能同时包含 HTTP 与业务活动;
  • Preview 没有先执行完整策略合法性校验,非法输入也可能得到一个估算;
  • 查询与清理之间继续写入或并发清理时,实际删除数会变化。

因此 UI 应把预估标成“当前快照”,并在保存前单独展示 Request 期限;不能把七行估算误称为八类精确删除计划。

4. 自动执行路径

Activity/Exception auditIAuditRetentionPortTenant directory and policy storeAuditRetentionJobExecutorQuartz or API fallbackActivity/Exception auditIAuditRetentionPortTenant directory and policy storeAuditRetentionJobExecutorQuartz or API fallbackloop[each tenant with days > 0]loop[each logical scope]execute audit-retentionenumerate tenants and load policyintent with RootCrossTenantOperationPruneTenantAsync(tenant, scope, cutoff)outcome and deleted count

默认 Cron 为 0 3 * * *。JobHost 由 Quartz 调度;显式启用 API fallback 时,统一 Cron 计算器调用同一个 Executor。Audit:Retention:Enabled=false 会完全跳过自动清理。

Executor 当前遍历七个物理清理范围:HttpRequest、Activity、EntityChange、Security、Exception、ExternalRequest、Communication。它为每个租户加载显式策略或全局值,使用受信 RootCrossTenantOperation 进入存储,并记录意图与结果。单个清理失败会继续后续租户/范围,最终使 Job 失败并保留首个异常。

策略 Store 查询返回 Failure 时,当前实现会跳过该租户而不计入最终失败数。这是运维可见性缺口:应监控策略存储错误,不能仅以 Job 最终 Success 证明所有租户都被处理。

5. 分类映射

Retention ScopeSqlSugarMongo
HttpRequest仅 SysAuditLogaudit_activity 中 module=Http 且有方法
Activity仅 SysActivityLogaudit_activity 中排除 HTTP 条件
EntityChangeSysEntityPropertyChangesLogsaudit_entity_change
SecuritySysSpecialLog 的 Security/旧 Authorization Infoaudit_security
ExceptionSysSpecialLog 的 Exception/Error/Fatalaudit_exception
ExternalRequestSysExternalRequestLogRecordaudit_external_request
CommunicationSysCommunicationLogaudit_cap_consumer + audit_background_job

Communication 的两类数据共用一个 Scope。租户策略为 CAP 和 BackgroundJob 提供两个期限时,Executor 取较长者,避免先删掉应保留更久的一类;代价是期限较短的一类也会多保留一段时间。

6. 有界物理删除

SqlSugar 和 Mongo 都按 1,000 条主键批次执行物理删除,每张表/每个集合最多 10,000 批,即单次最多处理 1,000 万条。达到上限会抛错,下一次执行从剩余记录继续,而不是发出一个无界 DeleteMany。

SqlSugar 先按分表日期裁剪旧分片,再在记录上同时应用 TenantId、Scope 和 CreateTime < cutoff。Mongo 使用 tenant_id、类别/HTTP 条件与 occurred_at < cutoff,先取稳定 _id 批次再删除。

删除不是软删除,也没有自动恢复。执行前应完成数据库备份策略、Legal Hold 核对和影响预估;大租户还应观察锁等待、事务日志、复制延迟与 Job 超时。

7. 手工清理入口

手工清理要求 operations.audit.delete,资源敏感级别为 admin:

  • POST /api/operations/audit/prune:JSON Body 包含 beforeDate 与 approvalTicket;
  • DELETE /api/audit/retention/{beforeDate}?ApprovalTicket=...:兼容路由,使用同一个 Command。

Handler 会拒绝早于 SQL Server 1753 边界的日期、未来日期、非规范或超过 64 字符的工单号,以及无法形成稳定 Actor 的调用者。Production 下 ISensitiveOperationApprovalPort 必须确认工单有效;其他环境是否要求审批由组合根决定。

删除前先写入 PruneAudit:<cutoff>:started 活动记录。若这条意图无法进入审计边界,物理删除不会开始。成功后记录总删除数;失败时记录异常类型并保留原异常。

8. 合规删除流程

建议把一次生产清理当成有编号的变更,而不是普通 API 调用:

  1. 冻结范围:Tenant、Scope、Cutoff、Purpose 与发起人;
  2. 预览:行数、最老/最新时间、分表和存储容量影响;
  3. 核对:法定下限、合同保留期、Legal Hold、诉讼保全与备份可用性;
  4. 审批:Incident、双人复核、有效期与执行窗口;
  5. 执行:先意图审计,再有界删除;
  6. 验证:删除计数、剩余最老记录、失败批次和复制状态;
  7. 证明:把工单、命令、结果和核验人写入保留期更长、不可被同一命令删除的合规账本。

通用 Activity 仍在待删审计存储内,不能作为唯一删除证明。监管要求严格时,应把清理证明投递到独立 Ledger/WORM。

9. 保留策略验收

预估与保存接受同一份输入。下面的示例保留 HTTP/外呼 180 天、异常 365 天,并让业务活动、实体变更和安全审计永久保留:

retention-policy.json
{
"requestRetentionDays": 180,
"activityRetentionDays": 0,
"entityChangeRetentionDays": 0,
"securityRetentionDays": 0,
"exceptionRetentionDays": 365,
"externalRequestRetentionDays": 180,
"capConsumerRetentionDays": 90,
"backgroundJobRetentionDays": 90,
"expectedVersion": 0
}

先保留预估响应,再把 GET 返回的最新版本写回文件后执行 PUT;不要在并发冲突后盲目把版本改大:

Terminal window
# 同一输入先预估、经审批后再保存。
curl -fsS -H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" \
--data @retention-policy.json https://<HOST>/api/audit/retention-policy/preview
curl -fsS -X PUT -H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" \
--data @retention-policy.json https://<HOST>/api/audit/retention-policy
  • 每个 Scope 只删除对应物理源,Security/Exception 与 HTTP/Activity 严格分离;
  • 永久类别在其他 Scope/Cutoff 下保持不变;
  • 两租户、平台租户、operate-as 和租户目录枚举;
  • 显式策略、缺省策略、乐观并发、法定下限和 365,000 上限;
  • Request 独立执行与 Preview 的已知合并边界;
  • Communication 取 CAP/Job 较长期限;
  • 月/年分表边界、时区、闰日、相等时间和 1753 下限;
  • 1,000 条批次、1,000 万条上限、取消、部分失败与下次续跑;
  • Production POST/DELETE 的工单校验、过期工单和审批服务不可用;
  • 意图审计失败时零删除,删除失败与结果审计失败的组合故障;
  • 策略 Store Failure 被监控发现,而不是静默遗漏租户。

上一篇:存储与查询 · 下一篇:测试与生产运维

100%

滚轮或按钮缩放 · 放大后拖动画面 · 双击切换 100% / 200%