- 新增六对成对迁移 000232–000237:H5 弹窗类型、退款结算标识与申请人备注、优先轮询事实字段与两个新终态、通道阈值命中留痕、手机号最近解绑人、提现资格校验留痕 - 退款:原因必填与申请人备注、来源支付与渠道流水冻结、线下处理流水号补录审计、按订单查询可选退款方式、企微审批材料补齐且新增字段缺失映射即明确失败 - 优先轮询:人工关闭、有效期到期独立周期任务、失败与过期人工重触发、事实字段与异常重试查询、资产解析端点只读投影 - 通道阈值:命中事实同事务留痕与命中记录查询;员工账单:列表筛选与详情投影;商户池:列表投影与统计周期语义;H5:弹窗类型与类别排序 - 手机号:有效关联数量与最近解绑人、短信验证码失败次数限制;导出:佣金明细十五列与报表序号列 - 时间筛选:三处新增筛选纳入统一严格解析契约,员工账单产生时间参数改名 - 同步 12 份主 Spec 需求、两端点与异步任务证据链,门禁 context-health 与 OpenSpec 校验通过
9.9 KiB
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_configuration 加 popup_type(枚举 promotion/announcement,非空,默认 announcement)+ 取值 CHECK;up 以默认值回填既有行 |
| 2 | 000233_extend_refund_settlement_fields |
退款主表加 source_payment_no(varchar(64))、original_channel_trade_no(varchar(100),与来源列 tb_payment.third_party_trade_no 一致)、offline_settlement_no(varchar(128))、offline_settled_at、offline_settled_by;退款审批尝试表加 remark(申请人备注,逐次冻结) |
| 3 | 000234_extend_polling_priority_item |
优先轮询事实表加 asset_type、asset_id、device_no_snapshot、agent_shop_id_snapshot、attempt_limit(默认 3)、started_at、finished_at、next_run_at、dequeued_at、integration_log_id、priority_effective_from;重建状态 CHECK 容纳 closed 与 expired;活动项部分唯一索引谓词保持 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_to → start_time/end_time(旧参数名与旧格式一律拒绝,无兼容别名) |
前端发布后员工账单列表与详情的时间筛选可用 | 待维护者执行 |
两项上线前置未完成前:新字段的企微审批材料会在提交时明确失败并提示缺失控件(不静默丢弃);员工账单的旧时间参数调用会被参数非法拒绝。
2. 回滚步骤
2.1 代码回退
代码回退到上一版本后,数据库停留在 237 形态,各新增列可空或带默认值,旧二进制不写这些列也不会违反约束:
| 表 | 列 | 回滚期旧代码不写时的取值 |
|---|---|---|
tb_h5_popup_configuration |
popup_type |
默认 announcement |
tb_refund_request |
source_payment_no(varchar(64))/ original_channel_trade_no(varchar(100),与来源列 tb_payment.third_party_trade_no 一致)/ offline_settlement_no(varchar(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。 - 员工账单列表与统计响应新增「未核销金额」与「剩余可核销金额」两个并列字段;详情新增操作日志与来源订单快照投影。