Skip to content
bitzorcas
中EN

Reference

Auditing 存储、分表与查询 API

对比 SqlSugar、Mongo 与 Dapper 的表/集合映射、稳定标识、全局分页、过滤、导出、字段安全和当前 Provider 差异。

Last updated

审计写侧使用完整的存储中性 AuditLogEntry,读侧只返回 16 字段 AuditEnvelope。查询 API 不暴露请求正文、响应正文、实体 Diff 或异常详情;需要展示的资源字段还会经过模块贡献的 IAuditEnvelopeProjector,例如 Identity 用户字段继续受 Field Security 约束。

1. SqlSugar 六组分表

记录类型表模板实际 SplitType逻辑类别
SysAuditLogRecordSysAuditLog_{year}{month}{day}MonthHTTP Activity
SysActivityLogRecordSysActivityLog_{year}{month}{day}Year业务 Activity
SysEntityPropertyChangesLogRecordSysEntityPropertyChangesLogs_{year}{month}{day}YearEntityChange
SysExternalRequestLogRecordSysExternalRequestLogRecord_{year}{month}{day}MonthExternalRequest
SysCommunicationLogRecordSysCommunicationLog_{year}{month}{day}MonthCapConsumer / BackgroundJob
SysSpecialLogRecordSysSpecialLog_{year}{month}{day}YearSecurity / Exception

SqlSugarAuditTableInitializer 在 --init-schema 中创建当前时间桶;任何初始化异常都会向上传播,使命令失败。未来时间桶由 SqlSugar 分表写入路径按事件时间创建。

两个配置边界仍需留意:AutoCreateTables=false 当前没有被 SchemaInitializationCommand 检查;AuditShardingSchedule 会做合法性校验,但 GetShardingType 没有参与实体映射,物理粒度仍由上表的 [SplitTable] 决定。

2. SqlSugar 写入与类别区分

Store 先按类别分组,再在一个数据库事务中写六组表:

  • Activity 以 HttpMethod 且 Module=Http 区分请求与业务活动;
  • CapConsumer/BackgroundJob 共表,RunPars 写入稳定类别标记;
  • Security/Exception 共表,Level 写入 Security 或 Exception;
  • Exception 的可公开错误摘要写入 Message,不会把任意堆栈直接投影给列表 API;
  • 每组插入前按目标分表和稳定 ID 查重,同 ID 证据不同则失败。

这意味着“六张物理表族”不等于“六个逻辑类别”。查询与保留必须继续使用类别标记,不能只按表名判断 CAP、Job、Security 或 Exception。

3. Mongo 七集合与索引

Mongo 将七个 Category 分别存入:

audit_activity、audit_entity_change、audit_security、audit_exception、audit_external_request、audit_cap_consumer、audit_background_job。

读、写、保留首次访问前共享 MongoAuditIndexInitializer:

  • 删除旧版 ttl_occurred_at 全局 TTL 索引;
  • 为每个集合创建统一查询索引;
  • 初始化取消或失败会把状态恢复为未完成,下次访问可重试;
  • 旧 AuditTtlEnabled/AuditTtlDays 只产生迁移 Warning,不再创建类别无关 TTL;
  • 连接或数据库配置不完整时失败关闭。
选择 Mongo 审计后端
Audit:
# 审计读、写、保留和耐久实体变更 Store 一并切换。
StoreProvider: Mongo
Mongo:
# 生产环境应从 Secret Provider 注入,不要把凭据提交进配置文件。
ConnectionString: "secret-ref:mongodb-audit"
Database: "bitzorcas"

Mongo 以 _id=AuditId 做 $setOnInsert,随后回读并比较完整文档;已有同 ID、不同内容不会被覆盖。

4. Dapper 读侧

Dapper 只替换 IAuditQueryPort,不承担通用写入或保留。它按共享表定义发现 SQL Server 分表,并有以下边界:

  • 每个表前缀最多发现 512 张物理表;
  • Tenant、时间、User、Module、TraceId、类别和游标条件尽可能下推到 SQL;
  • 每表只读取当前 AuditPageWindow.FetchCount 所需候选,不再把所有匹配行装入内存;
  • 每表结果和每类别结果都用同一稳定归并器;
  • 表名来自受控前缀与系统目录,不接受客户端提供物理表名。

它仍使用 SQL Server 的系统目录与方言。选择 MySQL/SQLite 时不要把 Dapper 审计读侧当作跨数据库替代品。

5. AuditEnvelope 与投影

公开摘要字段为:AuditId、OccurredAt、Category、TenantId、CallerType、UserId、ClientId、CorrelationId、TraceId、Module、ResourceType、ResourceId、Action、Result、DurationMs 与 SensitiveFieldsMasked。

Store Mapper 会限制可能含正文的旧列,查询 Handler 随后串行应用所有匹配的 IAuditEnvelopeProjector。任一投影失败,整个查询返回失败,避免在字段安全无法判定时放行原值。

6. 查询与导出入口

方法与路由用途关键约束
GET /api/audit租户审计列表auditing.audit.view、userPolicy、HeavyRead
GET /api/operations/audit运维兼容路由同一 QueryAuditCommand
POST /api/audit/exportCSV/JSON 异步导出同一查看权限、租户所有权、幂等键

列表支持 Category、UserId、Module、TraceId、From、To、PageIndex、PageSize。Handler 将页码下限设为 1;PageSize 非法或大于 200 时回退为 20。公共列表不接受 TenantId,租户只能来自可信 CurrentUser,也不提供 Host 跨租户绕过。

导出复用同一查询端口与投影链,每批使用 OccurredAt + AuditId 游标读取;只允许 csv/json,要求非平台租户、认证用户和最长 128 字符的幂等键。下载授权、文件保留和 DLP 由通用 Export 子系统继续控制。

7. 全局分页合同

三个 Provider 都调用 AuditPageWindow 和 AuditCandidateWindow:

  1. 每个物理来源读取从全局开头算起的 skip + pageSize 个候选;
  2. 按 OccurredAt desc, AuditId desc 线性归并,并持续裁剪到候选上限;
  3. 所有来源合并后才做一次全局 Skip/Take;
  4. 总数独立累加。

这修复了旧实现“每个类别先 skip 再合并”导致的第二页错序。Offset 模式最多物化 10,000 个候选;超出窗口时返回空 Items 但仍给出 TotalCount。连续导出或深翻页应使用内部游标合同:

OccurredAt < cursorTime OR (OccurredAt = cursorTime AND AuditId < cursorId)。

游标模式每来源只取 PageSize,内存保持有界,也不会因翻页期间插入新记录而重复跨过同一边界。

8. Provider 过滤差异

条件SqlSugarMongoDapper
Tenant所有表强制所有集合强制所有表强制
Category七类均可达七个独立集合七类均可达
User按各表对应列user_id按表列映射
Module按各表语义列module按表列映射
Time全部全部全部
TraceId当前未应用trace_id 或 correlation_id按表 Trace/Correlation 列
Security/Exception按 Level 分离独立集合按 Level 分离
CAP/Job按 RunPars 分离独立集合按 RunPars 分离
Cursor支持支持支持

GET /api/audit?traceId=... 在 SqlSugar 默认组合下目前不会缩小结果,这是已确认的源码缺口。修复前,调用方不能把该参数当作可靠调查过滤器;可先用时间、类别和模块缩小范围,再核对 CorrelationId。

9. 租户与模拟边界

普通列表、导出和保留策略读取认证主体的所有者 TenantId。即使用户正在 operate-as 另一个租户,这些管理面也不会自动切到 Effective Tenant。这一选择可以防止模拟会话借普通查看权限读取目标租户全部审计历史。

真正的 Host 跨租户调查应有独立命令、Purpose、审批、结果上限和自身审计,不能通过传 TenantId、清除 ORM Filter 或使用平台租户 0 偷渡实现。

10. 查询验收合同

调查请求应先用权威支持的时间、类别和模块过滤缩小候选,再通过响应中的 Cursor 继续翻页:

Terminal window
# traceId 在默认 SqlSugar 组合修复前不作为唯一过滤条件。
curl -fsS --get -H "Authorization: Bearer <TOKEN>" \
--data-urlencode "category=Security" --data-urlencode "module=Authorization" \
--data-urlencode "from=2026-08-10T00:00:00Z" --data-urlencode "pageSize=100" \
https://<HOST>/api/audit
  • 三 Provider 对相同 Fixture 返回相同类别、结果和稳定顺序;
  • Activity 双表、七类别、第 1/2/末页以及同时间戳 AuditId 排序;
  • offset 10,000 边界、超界空页与游标连续读取;
  • Security/Exception、CAP/Job 严格分离;
  • Tenant、User、Module、From/To、TraceId 的逐 Provider 合同测试;
  • From>To、非法租户/标识/时间/游标、取消与 Timeout;
  • Identity Field Security 投影失败时 fail-closed;
  • 导出重试、幂等、DLP、文件过期和下载授权;
  • Mongo 索引初始化失败后可重试,Dapper 物理表上限生效。

上一篇:队列与交付语义 · 下一篇:保留与合规操作

100%

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