Files
junhong_cmp_fiber/openspec/changes/archive/2026-09-18-close-august-iteration-gaps/rollout-and-rollback.md
break 5ed6b39deb
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
feat(收口): 补齐 8 月迭代缺口并同步 Spec 与证据链
- 新增六对成对迁移 000232–000237:H5 弹窗类型、退款结算标识与申请人备注、优先轮询事实字段与两个新终态、通道阈值命中留痕、手机号最近解绑人、提现资格校验留痕
- 退款:原因必填与申请人备注、来源支付与渠道流水冻结、线下处理流水号补录审计、按订单查询可选退款方式、企微审批材料补齐且新增字段缺失映射即明确失败
- 优先轮询:人工关闭、有效期到期独立周期任务、失败与过期人工重触发、事实字段与异常重试查询、资产解析端点只读投影
- 通道阈值:命中事实同事务留痕与命中记录查询;员工账单:列表筛选与详情投影;商户池:列表投影与统计周期语义;H5:弹窗类型与类别排序
- 手机号:有效关联数量与最近解绑人、短信验证码失败次数限制;导出:佣金明细十五列与报表序号列
- 时间筛选:三处新增筛选纳入统一严格解析契约,员工账单产生时间参数改名
- 同步 12 份主 Spec 需求、两端点与异步任务证据链,门禁 context-health 与 OpenSpec 校验通过
2026-09-18 15:34:29 +08:00

9.9 KiB
Raw Blame History

close-august-iteration-gaps 上线与回滚说明

对应 tasks.md §14.1 / §14.2 / §14.4。上线顺序与回滚口径依据 design.md「Migration Plan」与 proposal.md「Impact」。

1. 上线顺序(必须先迁移,再发二进制,再配置企微,最后开放新字段使用)

第一步:执行六对迁移(按编号顺序,不修改既有迁移)

顺序 迁移编号 内容
1 000232_add_h5_popup_type tb_h5_popup_configurationpopup_type(枚举 promotion/announcement,非空,默认 announcement+ 取值 CHECKup 以默认值回填既有行
2 000233_extend_refund_settlement_fields 退款主表加 source_payment_novarchar(64))、original_channel_trade_novarchar(100),与来源列 tb_payment.third_party_trade_no 一致)、offline_settlement_novarchar(128))、offline_settled_atoffline_settled_by;退款审批尝试表加 remark(申请人备注,逐次冻结)
3 000234_extend_polling_priority_item 优先轮询事实表加 asset_typeasset_iddevice_no_snapshotagent_shop_id_snapshotattempt_limit(默认 3started_atfinished_atnext_run_atdequeued_atintegration_log_idpriority_effective_from;重建状态 CHECK 容纳 closedexpired;活动项部分唯一索引谓词保持 status IN ('pending','processing') 不变;为 next_run_at 建部分索引;既有活动项 priority_effective_from 回填为迁移时刻,资产列按既有设备—卡绑定回填
4 000235_extend_carrier_threshold_hit 阈值锁表加命中累计流量、阈值数值、阈值单位、判定时间、触发来源、解锁时间、复机结果;判定时间回填既有行创建时间(并置默认 now()),命中数值与阈值留空;新增按通道、卡与判定时间的查询索引
5 000236_add_phone_association_invalidator_name 手机号—资产关联加 invalidator_name_snapshot(非空默认空串)
6 000237_extend_withdrawal_attempt_qualification 提现审批尝试表加资料资格版本标识、校验时间、是否通过、未通过稳定原因

执行方式:维护者按既有迁移脚本(scripts/migrate.sh)执行,六对全部落盘后再统一 up(本次测试环境即如此,避免先落盘的号被后落盘的低号跳过)。执行后用只读查询核对列/默认值/CHECK/索引(见 verification.md 第 2 节)。

第二步:发布 API / Worker 二进制

手工发布,步骤见 docs/deployment/production-runbook.md。发布前确认工作树包含本变更全部生产代码改动(go build ./cmd/api ./cmd/worker 通过)。

第三步:维护者配置企业微信控件映射(仅本变更新增字段强制映射)

  • 退款审批场景:套餐已用量与总量、资产类型、设备类型与型号、原支付渠道交易流水号。
  • 代理注册审批场景:业务员。
  • GET /api/admin/wecom/scenes 返回的启用记录与 control_mapping 作为验收证据。既有未映射的可选控件保持既有静默跳过行为。
  • 未配置前:退款/注册审批提交时对缺失的新增控件明确失败并提示缺失控件(不静默丢弃)。

第四步:开放新字段使用

新端点与新增筛选按第 3 节启用;前端按第 4 节的参数改名同步。

上线前置清单(两项,待维护者执行)

内容 验收证据 状态
1 在生产配置企业微信控件映射:退款审批的套餐用量(已用量与总量)、资产类型、设备类型与型号、原渠道流水号;代理注册审批的业务员 GET /api/admin/wecom/scenes 返回的启用记录与 control_mapping 待维护者执行
2 员工账单时间参数改名的前端同步:created_from/created_tostart_time/end_time(旧参数名与旧格式一律拒绝,无兼容别名) 前端发布后员工账单列表与详情的时间筛选可用 待维护者执行

两项上线前置未完成前:新字段的企微审批材料会在提交时明确失败并提示缺失控件(不静默丢弃);员工账单的旧时间参数调用会被参数非法拒绝

2. 回滚步骤

2.1 代码回退

代码回退到上一版本后,数据库停留在 237 形态,各新增列可空或带默认值,旧二进制不写这些列也不会违反约束:

回滚期旧代码不写时的取值
tb_h5_popup_configuration popup_type 默认 announcement
tb_refund_request source_payment_novarchar(64)/ original_channel_trade_novarchar(100),与来源列 tb_payment.third_party_trade_no 一致)/ offline_settlement_novarchar(128) 默认空串
tb_refund_request offline_settled_at NULL可空
tb_refund_request offline_settled_by 默认 0
tb_refund_request_attempt remark 默认空串
tb_polling_priority_item asset_type / device_no_snapshot / integration_log_id 默认空串
tb_polling_priority_item asset_id / agent_shop_id_snapshot / started_at / finished_at / next_run_at / dequeued_at / priority_effective_from NULL可空
tb_polling_priority_item attempt_limit 默认 3
tb_carrier_traffic_threshold_lock hit_traffic_mb / hit_threshold_value NULL可空
tb_carrier_traffic_threshold_lock hit_threshold_unit / trigger_source / resume_result 默认空串
tb_carrier_traffic_threshold_lock judged_at 默认 now()
tb_carrier_traffic_threshold_lock unlocked_at NULL可空
tb_phone_asset_association invalidator_name_snapshot 默认空串
tb_commission_withdrawal_request_attempt qualification_version_id / qualification_passed 默认 0
tb_commission_withdrawal_request_attempt qualification_checked_at NULL可空
tb_commission_withdrawal_request_attempt qualification_failure_reason 默认空串

judged_at 单独说明:该列需要非空(判定时间是命中记录的查询与排序依据),但若不给默认值,回退到上一版本二进制时旧代码写入停机锁不带该列就会违反 NOT NULL使达量停机事实无法落库。因此保留 DEFAULT now(),让旧代码插入的命中事实以插入时刻作为判定时间,与「既有行以创建时间回填」的语义自洽。

2.2 读侧兼容

  • 读侧对上述空值与默认值一律按「无」处理,不因新列缺失或为空阻断查询(命中读数/阈值历史行为空按「无」展示,判定时间取既有创建时间)。
  • 既有优先轮询行的有效期起算时间(priority_effective_from)为迁移时刻,不会在上线后被首轮到期扫描批量判为已过期;既有行的 next_run_at 为空时,读侧 MUST NOT 据此判定已过期。
  • 历史审批尝试不受本变更的必填与留痕影响:退款原因必填只对上线后新提交与重提生效,历史申请与补发历史审批不追溯;提现历史尝试的资格校验留痕为空按「历史尝试无资格校验留痕」处理。

2.3 企微控件配置回滚

代码回滚时需同步移除本变更新增的控件映射(退款审批的套餐用量/资产类型/设备类型与型号/原支付渠道交易流水号,代理注册审批的业务员),避免模板残留未使用控件。

2.4 时间参数替换回滚

员工账单列表「产生时间」参数改名属有意的破坏性收敛,旧参数名不再被接受;回滚需同步前端(见第 4 节)。

2.5 迁移 down

如需实际回滚 Schema按 6→1 逆序执行六对迁移的 down注意各 down 均声明不可逆事实的丢弃H5 类型、退款结算标识与备注、优先轮询留痕、命中数字证据、解绑人名称快照、资格校验留痕),其中 000234 的 down 在存在 closed/expired 终态行时会拒绝执行。

3. 新增端点与失效字段默认值

3.1 新增端点(方法 / 路径 / 权限主体)

方法 路径 权限主体
GET /api/admin/refunds/order-options 退款组门禁(超级管理员/平台/代理,企业账号拒绝)+ 订单数据范围
POST /api/admin/refunds/:id/offline-settlement 同退款组门禁 + 订单数据范围
POST /api/admin/polling-priority-items/:id/close 超级管理员与平台账号
POST /api/admin/polling-priority-items/:id/retrigger 与人工入队一致(含数据范围内的代理账号)
GET /api/admin/carrier-traffic-threshold-hits 超级管理员与平台账号 + 卡数据范围

同时新增周期任务类型优先轮询有效期到期扫描与出队Worker 调度注册,@every 周期)。

3.2 失效字段默认值(新增列的上线默认值)

见 2.1 表;一句话概括:新增列一律可空或带默认值(空串 / 0 / 3 / announcement / now()),既有行零变更。

4. 调用方同步要求

  • 员工账单列表:既有「产生时间」参数由 created_from/created_to 改名为 start_time/end_time,并改用统一严格解析器。旧参数名(created_from/created_to与旧格式date-only、无时区串、空格分隔、小数秒、±hhmm一律以参数非法错误拒绝,不保留别名或兼容;前端 MUST 同步改用 start_time/end_time
  • 员工账单「核销通过时间」使用 decided_start_time/decided_end_time,与主字段同一解析器、同一闭区间(带时区 RFC3339 秒级、含两端)。
  • 优先轮询项查询、通道阈值命中查询的筛选时间字段亦使用 start_time/end_time
  • 员工账单列表与统计响应新增「未核销金额」与「剩余可核销金额」两个并列字段;详情新增操作日志与来源订单快照投影。