Files
junhong_cmp_fiber/openspec/changes/refactor-audit-log-persistence/tasks.md
邹进 b7a7430cc3
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 12m8s
feat: 审计动作按落库策略分级写入,系统过程动作改写应用日志
- 审计注册表为可由系统任务、定时任务发起的动作声明落库策略(always/on_success/outcome/log_only/none),未声明按 log_only
- 人工、开放接口、个人客户、外部系统发起的动作始终写审计表
- log_only 写所在服务 app.log(logger=audit),不含快照
- 对接日志不再补写 integration.attempt_started,无上游审计时 audit_event_id 为空
- 新增 audit.persist_overrides 按动作覆盖策略
- 审计覆盖扫描要求机器动作必须声明策略
- 新增 OpenSpec 提案 refactor-audit-log-persistence 与技术方案文档

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 18:27:24 +09:00

72 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 1. 审计落库策略(第 1 次提交)
- [x] 1.1 `internal/infrastructure/audit/registry.go`:`ActionDefinition` 新增 `Persist`,取值为 `always`、`on_success`、`outcome`、`log_only`、`none`;按技术方案 4.1、4.2 为全部可由系统任务或定时任务发起的动作声明策略,`integration.attempt_started` 设为 `none`。
- [x] 1.2 `writer.go`:
- 新增 `persistDecision`:人工操作者一律写表;系统任务和定时任务按 `Persist` 处理,未声明时按 `log_only`。
- `AppendInput` 增加 `Final`;`outcome` 策略在 `Final=false` 时只写日志,`Final=true` 时按业务键 `EventID` 写表。
- [x] 1.3 `log_only` 和 `on_success` 的失败结果写 `logger.GetAppLogger().Named("audit")`,`msg=audit_event`。字段按 spec 写入,不含快照;成功记 Info,失败记 Warn。
- [x] 1.4 逐一检查 `AppendAndGet` 的 11 处调用,确保返回 nil 事件时不会出错。
- [x] 1.5 `pkg/config` 新增 `audit.persist_overrides`(动作编码 → 策略),运行时生效,只作用于系统任务和定时任务操作者。
- [x] 1.6 `internal/infrastructure/integrationlog/repository.go`:删除 `Start` 中补写 `integration.attempt_started` 的分支;确认 `auditEventIDForSeries` 的查询走索引。
- [x] 1.7 `internal/governance/auditcoverage`:可由系统任务发起但未声明 `Persist` 的动作,扫描判为失败;更新已移出审计入口的 `AuditEvent` / `DomainLedger` 分类。
- [x] 1.8 `gofmt`、`go build ./cmd/api ./cmd/worker`、`openspec validate refactor-audit-log-persistence`;提交并推送 8-11。
## 2. 退款后处理与停复机跳过记录(第 2 次提交)
- [ ] 2.1 迁移:
- 新建 `tb_refund_post_process_attempt`,按月分区,并建立索引;
- 退款单新增 `asset_post_status`、`asset_post_attempts`、`asset_post_last_error`、`asset_post_next_at`;
- 补齐 down 脚本。
- [ ] 2.2 `internal/service/refund`:
- `processRefundAsset` 每次执行都写尝试记录(步骤、触发方式、结果、错误码与错误信息、耗时、关联停复机记录);
- 失败时按指数退避(最长 1 小时)更新退款单;
- 达到 10 次后置为需人工处理,并写一条 `outcome` 失败结论审计;
- 成功时写 `outcome` 成功结论审计,`asset_reset` 同步置为完成。
- [ ] 2.3 Outbox 处理器与 `RecoverRefundPostProcessing` 在执行前检查状态和 `next_at`:未到期、已完成、需人工处理的都跳过;补偿扫描负责拉起已到期的待重试单。
- [ ] 2.4 新增 `POST /refunds/:id/asset-post-processing/retry` 人工重试接口,只允许在需人工处理状态下调用,记为人工重试;在路由 `RouteSpec` 中登记,并生成接口文档。
- [ ] 2.5 停复机指令执行前的重新判断处,按三种结束原因写 `skipped` 停复机记录(已是目标状态、条件不再满足、不允许停复机);限流推迟不写。
- [ ] 2.6 构建、校验、提交并推送 8-11。
## 3. 流量采集分表与原始数据(第 3 次提交)
- [ ] 3.1 `internal/gateway/client.go`:新增 `WithRawCapture`,`executeWithRetry` 按尝试写入最后一次的明文请求、HTTP 状态、响应体、`code`、`msg`、`trace_id` 和尝试次数;不记录加密报文、签名、`app_id`、密钥和请求头。
- [ ] 3.2 迁移:
- 新建 `tb_card_traffic_collection_failure`,按月分区,并建立索引;
- 采集成功表增加 `carrier_id` 和原始数据列(`raw_request`、`http_status`、`gateway_code`、`gateway_msg`、`gateway_trace_id`、`raw_response`、`raw_response_text`、`raw_truncated`、`gateway_attempts`),新增 D9 中列出的索引。
- [ ] 3.3 `polling_carddata_handler`:
- 调用 `QueryFlow` 前挂上收集器;
- 网关失败时填入错误类别、上游码与信息、耗时;
- 成功和失败都把原始数据带进 `TrafficObservation`。
- [ ] 3.4 `internal/application/cardobservation`:`writeCollectionRecord` 按结果分流。成功写成功表(保持"有套餐才写"),失败直接写失败表(不查套餐);原始响应超过 4 KB 时截断并标记。
- [ ] 3.5 `MaintainPartitions` 改为成功表、失败表共用的四步归档(摘出、导出 `csv.gz.tmp`、校验并写 manifest 后改名、删除),并处理上一轮遗留的已摘出表、清理本地压缩包(默认保留 3 个月);常量改为 `TrafficCollectionRetentionMonths=2`。
## 4. 记录查询接口(第 3 次提交)
- [ ] 4.1 在 `internal/store/postgres` 中为各记录表实现统一的 `List(ctx, Filter)`;资产标识复用现有资产解析,解析不到时返回空页。
- [ ] 4.2 采集成功列表 `GET /traffic-collections`、采集失败列表 `GET /traffic-collection-failures`;单卡采集接口增加筛选条件和"包含失败"参数,成功表查询按 `result='ok'` 过滤。
- [ ] 4.3 停复机记录列表:`iccid` 扩展为资产标识,增加场景、原因、指令号筛选;单卡历史增加动作、结果、来源筛选;新建或重建 D9 中列出的索引。
- [ ] 4.4 新增卡日流量列表;新增套餐日流量详单列表,原单套餐接口改为分页;新增冲正流水列表,原单套餐接口增加时间范围筛选。
- [ ] 4.5 新增退款后处理尝试列表;退款列表增加 `asset_post_status` 筛选,列表和详情返回后处理次数、最后错误、下次重试时间。
- [ ] 4.6 统一校验规则:时间范围必填、跨度不超过 31 天、分页上限;所有接口在 `RouteSpec` 中登记输入输出 DTO,并生成接口文档。
## 5. 时间线与测试环境部署配置(第 3 次提交)
- [ ] 5.1 先与前端或运营确认是否有页面依赖机器动作的审计,再在 `internal/query/audit` 中让卡和设备时间线合并停复机记录。
- [ ] 5.2 `docker-compose.prod.yml`:api 挂载 `./api/logs`,worker 挂载 `./worker/logs` 和 `./worker/archive`;两者增加 `MAX_BACKUPS=40`、`MAX_AGE=14`。`.gitea/workflows/deploy.yaml` 改为创建这三个目录。
- [ ] 5.3 构建、`openspec validate --all`、`./scripts/context-health.sh`;提交并推送 8-11。
## 6. 测试环境验证(AGENTS.md 规定自动化测试为 N/A;按 ENG-TEST-001 在测试环境验证)
- [ ] 6.1 审计:部署后 24 小时,审计日增约 300 条(按小时只读统计);人工操作照常写审计;`app.log` 中有 `logger=audit` 的行且不含快照;新对接日志的 `audit_event_id` 可为空。
- [ ] 6.2 运行时覆盖:把一个机器动作覆盖为 `log_only`,再恢复,确认写入行为随之切换。
- [ ] 6.3 退款后处理:在测试环境造一张后处理会失败的退款单,确认每次尝试都有记录、退避生效、第 10 次后转人工并写结论审计,人工重试成功后状态变为已完成。
- [ ] 6.4 停复机:造一条执行前已是目标状态的停机指令,确认写入 `skipped` 记录。
- [ ] 6.5 采集:成功记录带原始数据,失败记录带错误详情和原始数据,无套餐卡的失败也写入;记录中没有密钥、签名或加密报文。
- [ ] 6.6 归档:在测试库用一个过期的空分区或小分区执行一次维护,确认生成 `csv.gz` 和 manifest、行数一致后分区被删除;模拟目录不可写时,已摘出的分区仍保留。
- [ ] 6.7 查询接口:每个接口分别验证查单个、查全部、分页总数和各筛选条件;用 `EXPLAIN` 确认走索引并做了分区裁剪。
- [ ] 6.8 部署:确认 `/opt/junhong_cmp/api/logs`、`/opt/junhong_cmp/worker/logs`、`/opt/junhong_cmp/worker/archive` 生成且轮换正常,同步服务器能拉到文件。
## 7. 归档顺序
- [ ] 7.1 本 Change 必须在 `add-card-traffic-queue-and-observed-deduction` 和 `add-carrier-gateway-rate-limit` 归档之后再归档。