Files
junhong_cmp_fiber/openspec/changes/refactor-audit-log-persistence/tasks.md
邹进 8eb7d403c4 feat: 退款资产后处理逐次尝试记录与重试上限,停复机指令调用前结束写跳过记录
- 新增 tb_refund_post_process_attempt,每次尝试一条(步骤、触发方式、结果、错误码与信息、耗时)
- 退款单增加 asset_post_status/attempts/last_error/next_at;失败按 2^(n-1) 分钟退避(最长 1 小时),10 次后转人工
- 中间失败只写应用日志,成功或转人工写一条结论审计(带总尝试次数、首次失败时间、最后错误码)
- 补偿扫描跳过需人工处理与未到重试时间的退款单,Outbox 不再无限重投
- 新增 POST /refunds/:id/asset-post-processing/retry 人工重试
- 停复机指令执行前结束(已是目标状态、条件不再满足、需相反动作)写 skipped 停复机记录
- 迁移 000271 需维护者在测试库执行后再部署

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

7.7 KiB
Raw Blame History

1. 审计落库策略(第 1 次提交)

  • 1.1 internal/infrastructure/audit/registry.go:ActionDefinition 新增 Persist,取值为 always、on_success、outcome、log_only、none;按技术方案 4.1、4.2 为全部可由系统任务或定时任务发起的动作声明策略,integration.attempt_started 设为 none。
  • 1.2 writer.go:
    • 新增 persistDecision:人工操作者一律写表;系统任务和定时任务按 Persist 处理,未声明时按 log_only。
    • AppendInput 增加 Final;outcome 策略在 Final=false 时只写日志,Final=true 时按业务键 EventID 写表。
  • 1.3 log_only 和 on_success 的失败结果写 logger.GetAppLogger().Named("audit"),msg=audit_event。字段按 spec 写入,不含快照;成功记 Info,失败记 Warn。
  • 1.4 逐一检查 AppendAndGet 的 11 处调用,确保返回 nil 事件时不会出错。
  • 1.5 pkg/config 新增 audit.persist_overrides(动作编码 → 策略),运行时生效,只作用于系统任务和定时任务操作者。
  • 1.6 internal/infrastructure/integrationlog/repository.go:删除 Start 中补写 integration.attempt_started 的分支;确认 auditEventIDForSeries 的查询走索引。
  • 1.7 internal/governance/auditcoverage:可由系统任务发起但未声明 Persist 的动作,扫描判为失败;更新已移出审计入口的 AuditEvent / DomainLedger 分类。
  • 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 归档之后再归档。