Files
junhong_cmp_fiber/openspec/changes/fix-audit-retention-null-boundary/design.md
break c8052df8eb
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m30s
补充退款列表应当让本店铺的人看见
2026-08-18 11:44:30 +08:00

2.0 KiB

Context

internal/query/retention 以聚合查询计算审计与外部交互日志的在线留存边界。PostgreSQL 在没有匹配行时会为 MAXMIN 返回 NULL,而当前扫描目标不能接收空值,导致所有依赖该边界的审计调查接口失败。

Goals / Non-Goals

Goals:

  • 将空聚合结果识别为“尚无边界”,而非数据库错误。
  • 保持已有已清理边界、最早在线记录和当月兜底的语义。
  • 修复所有通过同一留存边界查询函数进入的审计接口。

Non-Goals:

  • 不执行审计物理清理,不改变归档或保留策略。
  • 不修改数据库结构、迁移记录或历史审计数据。
  • 不修改 API 路由、权限或响应字段。

Decisions

使用可空时间承接 SQL 聚合结果

留存边界查询使用标准库可空时间值承接 MAX(range_end)MIN(<time-column>)。仅在值有效时转换为目标时区并作为边界返回。

拒绝将聚合结果用当前时间或固定时间 SQL COALESCE:这会把“尚未清理”误判为已归档,改变响应中的留存语义。

同时覆盖已清理和最早在线两个聚合路径

MAX(range_end) 是本次线上错误入口;MIN 在在线表为空时具有相同的空值扫描风险。两个路径共用相同的可空聚合边界,应一次修复。

Risks / Trade-offs

  • [风险] 空边界被误判为已清理,错误拒绝历史查询 → 仅在聚合值有效时设置已清理标记与归档边界。
  • [风险] 修复遗漏其他审计接口 → 保持修改在所有审计查询共用的留存边界函数内。

Migration Plan

  1. 修改留存边界的空时间扫描逻辑。
  2. 格式化并构建 API。
  3. 仅替换 API 二进制并重启 API Unit。
  4. GET /api/admin/audit/events?page=1&page_size=20 验证无已清理记录时接口不再返回 500。

Rollback

无数据库迁移。若 API 启动或查询异常,覆盖回本次发布前 API 二进制并重启 API Unit。