yeg
2026-09-17 1805a7b9648ff9cfbd19129255a5a4fa62f0261e
chore(db): 迁移编号让位到 V151–V154,解与远端 V148–V150 的撞号

共同祖先只到 V147:远端 shinnosuke 的 d2d27be/38dd07d 先推送了 V148__policy_article_top_time、
V149__clear_service_hero_placeholders、V150__merchant_hero_image,而本地(未推送)同期占了
V148__merchant_capability_sync_until、V149__payment_attempt_convergence、
V150__merchant_numeric_code_attempt、V151__order_cancel_request。git 对此不报冲突,Flyway 会直接报
「同一版本两个迁移」,所以按「已推送的优先」让号:

V148 → V151(merchant_capability_sync_until)
V149 → V152(payment_attempt_convergence)
V150 → V153(merchant_numeric_code_attempt)
V151 → V154(order_cancel_request)

- 两个 scripts/dev-schema-*.sql 同步改名;四个迁移文件头与两个脚本头都留了让位记录(写明原编号与
撞了谁),下一个人查编号时能对上。
- 引用同步更新:docs/agents/testing.md(存储过程写法那条、首次拿到新列的举例)、apply-sql-file.ps1
的用法示例、docs/adr/0003 的修订段、.scratch 的 plan.md;feature README 补「迁移编号让位」一节。
- 测试库按 docs/agents/testing.md 用 reset-test-db.ps1 重建(旧编号已在 Flyway history 里,不再解析得到)。

验证:全量 376 项,仅 testing.md 记录的 3 项基线失败(含对方新加的 PortalServiceTests);后台
talent-card-admin 的 vue-tsc -b 通过。两个小程序工程本机无法编译,未跑。
6 文件已重命名
5个文件已修改
47 ■■■■■ 已修改文件
.scratch/pending-order-payment-flow/README.md 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.scratch/pending-order-payment-flow/plan.md 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/adr/0003-payment-converges-per-attempt.md 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/agents/testing.md 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/scripts/apply-sql-file.ps1 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/scripts/dev-schema-v152-payment-attempt.sql 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/scripts/dev-schema-v154-order-cancel-request.sql 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/resources/db/migration/V151__merchant_capability_sync_until.sql 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/resources/db/migration/V152__payment_attempt_convergence.sql 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/resources/db/migration/V153__merchant_numeric_code_attempt.sql 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/resources/db/migration/V154__order_cancel_request.sql 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.scratch/pending-order-payment-flow/README.md
@@ -190,3 +190,20 @@
- `issues/01-pending-order-deadlock.md`:用户报的死锁(诊断,不是票)。
- `issues/02-store-consume-order-notification.md`:D1(到店消费通知)的移交文档。
- `issues/03-stock-and-create-order-idempotency.md`:审计 §13(库存/销量/下单幂等)的移交文档。
## 迁移编号让位(2026-09-17)
本轮新增的四个迁移原本取 V148–V151,但远端 `shinnosuke` 的两个提交(`d2d27be`、`38dd07d`)先推送了
`V148__policy_article_top_time`、`V149__clear_service_hero_placeholders`、`V150__merchant_hero_image`:
共同祖先只到 `V147`,双方各自独立占了同一段编号(git 不会报冲突,Flyway 会直接报「同一版本两个迁移」)。
合并时**本地让号**(远端那三个已推送,可能已在别人的环境里跑过),按原有先后顺延:
| 原编号 | 现编号 |
| --- | --- |
| `V148__merchant_capability_sync_until` | `V151` |
| `V149__payment_attempt_convergence` | `V152` |
| `V150__merchant_numeric_code_attempt` | `V153` |
| `V151__order_cancel_request` | `V154` |
两个 `scripts/dev-schema-*.sql` 同步改名,每个迁移文件头也留了让位记录。票面与 issue 里出现的旧编号
按当时的事实保留(历史记录),以后新增迁移请照 `AGENTS.md` 先看当前最大版本号再取。
.scratch/pending-order-payment-flow/plan.md
@@ -5,7 +5,7 @@
**一处修正**:我此前说过「第一刀大概率不需要迁移」,那是只考虑了新增 `status` 取值。C4(对账任务
无轮转)与 Q8(关单重试上限)都需要按行记状态,所以**第一刀需要一次迁移**。当前迁移最大版本是
`V148`,本次取 `V149`,开工前再确认没被他人占用(`AGENTS.md`:迁移编号全仓共享)。
`V148`,本次取 `V149`;实际开工时远端另有提交把 V148–V150 占走,本地让号后落在 `V152`(见 README「编号让位」)。
---
@@ -13,7 +13,7 @@
**目标**:任何入口进的待支付订单都不再卡死——能再付、能取消、能到期。
### 迁移 `V149`
### 迁移 `V149` → `V152`(编号让位后)
`payment_transaction` 加两列(都在 `payment_transaction` 上,不动别人的区域):
docs/adr/0003-payment-converges-per-attempt.md
@@ -45,6 +45,6 @@
真实的收款+退款流水(手续费大概率不退,对账也要处理),换来的是取消立刻生效。这是产品明确选的取舍:
取消是高频操作,「等一分钟」是每次都要付的代价;「收了再退」是极少数情况。
同时把取消意图降级为审计事实:`benefit_order.cancel_requested_at`(V151)只记录用户何时点的取消,
同时把取消意图降级为审计事实:`benefit_order.cancel_requested_at`(V154)只记录用户何时点的取消,
**不再驱动任何行为**——它驱动的那个「等关单结论再落地」的对账扫描已经删掉,留它只是为了在
「取消后收到钱」那类偶发路径上把时间对得上。
docs/agents/testing.md
@@ -12,7 +12,7 @@
三条由此推出的硬约束:
- **迁移必须同时兼容 5.7 与 8.0**:测试只在 8.0 上跑,跑绿不代表开发库能执行(`V148` 的存储过程写法
- **迁移必须同时兼容 5.7 与 8.0**:测试只在 8.0 上跑,跑绿不代表开发库能执行(`V151` 的存储过程写法
  已在 5.7 上实跑验证过,但每次新增迁移都值得照做一遍)。
- **开发库的结构不由 Flyway 维护**:缺表缺列只能靠启动时的初始化器 best-effort 补,补不上就手工补(见下)。
- 本机 `8134` 实例上另有 `talent_card`、`talent_card_dryrun`、`talent_card_verify1` 三个库:它们是
@@ -79,8 +79,8 @@
  逐条执行 SQL(默认连开发库,可 `-Database/-DatabaseHost/-Port` 覆盖;不带 `-Yes` 只打印不执行)。
  它认得 `DELIMITER`,所以 `db/migration/V*.sql` 也能原样重放。默认指向**开发库**,别拿它去改测试库
  (测试库由 Flyway 从 `db/migration` 重建,手工 DDL 会让迁移与 history 表对不上)。
  开发者首次拿到新列:2026-09-17 的 `V149`(支付流水两列)与 `V150`(手输核销码计数表)就是这样落地的,
  对应脚本 `scripts/dev-schema-v149-payment-attempt.sql`(写法可重复执行的那份在迁移里,两者等价)。
  开发者首次拿到新列:2026-09-17 的 `V152`(支付流水两列)与 `V153`(手输核销码计数表)就是这样落地的,
  对应脚本 `scripts/dev-schema-v152-payment-attempt.sql`(写法可重复执行的那份在迁移里,两者等价)。
- 动结构之前先备份。本机**没有** mysql 客户端、也没有 Docker(README 里指向 `D:\MySQL\...` 和
  `docker exec talent-card-mysql-1` 的两条老做法都已失效),要备份就在 DB 主机上做,或照
  `scripts/reset-test-db.ps1` 的办法用项目已有的 JDBC 驱动写个单文件程序。
talent-card-server/scripts/apply-sql-file.ps1
@@ -6,8 +6,8 @@
# request that touches it fails with "Unknown column ...". See docs/agents/testing.md.
#
# Usage:
#   .\scripts\apply-sql-file.ps1 -File scripts\dev-schema-v149-payment-attempt.sql -Yes
#   .\scripts\apply-sql-file.ps1 -File src\main\resources\db\migration\V149__payment_attempt_convergence.sql -Yes
#   .\scripts\apply-sql-file.ps1 -File scripts\dev-schema-v152-payment-attempt.sql -Yes
#   .\scripts\apply-sql-file.ps1 -File src\main\resources\db\migration\V152__payment_attempt_convergence.sql -Yes
#   .\scripts\apply-sql-file.ps1 -File <file>                 # prints the statements without executing
#
# The target defaults to the shared dev database. It is a shared database, so the script executes
talent-card-server/scripts/dev-schema-v152-payment-attempt.sql
File was renamed from talent-card-server/scripts/dev-schema-v149-payment-attempt.sql
@@ -1,10 +1,13 @@
-- 开发库(局域网共享的 talent_card,MySQL 5.7,Flyway 关闭)手工补齐 V149 的两列。
-- 开发库(局域网共享的 talent_card,MySQL 5.7,Flyway 关闭)手工补齐支付尝试收敛的两列。
--
-- 编号让位(2026-09-17):这两列对应的迁移原为 V149,因与远端已推送的 V149__clear_service_hero_placeholders
-- 撞号,本地让号改为 V152__payment_attempt_convergence.sql;本脚本同步改名。
--
-- 为什么需要这份文件:开发库的结构不由迁移维护,而现有启动初始化器只补商户进件相关的表,
-- 不覆盖 payment_transaction。只写迁移会让开发库缺列,对账任务与支付接口直接以
-- Unknown column 500(见 docs/agents/testing.md 记录的同类事故)。
--
-- 与 V149__payment_attempt_convergence.sql 逐列等价,只是不含存储过程(JDBC 无法直接执行
-- 与 V152__payment_attempt_convergence.sql 逐列等价,只是不含存储过程(JDBC 无法直接执行
-- DELIMITER 批处理),所以只能执行一次:重复执行会在「列已存在」上报错,这是安全的失败。
--
-- 执行方式(本机没有 mysql 客户端,用项目已有的 JDBC 驱动):
talent-card-server/scripts/dev-schema-v154-order-cancel-request.sql
File was renamed from talent-card-server/scripts/dev-schema-v151-order-cancel-request.sql
@@ -1,6 +1,7 @@
-- 编号让位(2026-09-17):这一列对应的迁移原为 V151,同批让号后为 V154__order_cancel_request.sql;本脚本同步改名。
-- 开发库(局域网共享的 talent_card,MySQL 5.7,Flyway 关闭)手工补列。
--
-- 与 V151__order_cancel_request.sql 等价,只是不含存储过程(JDBC 无法直接执行 DELIMITER 批处理),
-- 与 V154__order_cancel_request.sql 等价,只是不含存储过程(JDBC 无法直接执行 DELIMITER 批处理),
-- 所以只能执行一次:重复执行会在「列已存在」上报错,这是安全的失败。
--
-- 执行方式:
talent-card-server/src/main/resources/db/migration/V151__merchant_capability_sync_until.sql
File was renamed from talent-card-server/src/main/resources/db/migration/V148__merchant_capability_sync_until.sql
@@ -1,3 +1,4 @@
-- 编号让位(2026-09-17):本文件原为 V148,与远端已推送的 V148__policy_article_top_time 撞号,本地让号改为 V151。
-- 商户端「立即核实」的冷却时间戳:手动核实一次通道查询后,在此时间之前不再重复查询。
-- 不放在 capability_verified_at:查询失败时那一列不推进,会被连点;也不放在 updated_at:
-- 那是 MySQL 的 on update current_timestamp,任何无关 UPDATE 都会顺带把它重置。
talent-card-server/src/main/resources/db/migration/V152__payment_attempt_convergence.sql
File was renamed from talent-card-server/src/main/resources/db/migration/V149__payment_attempt_convergence.sql
@@ -1,3 +1,4 @@
-- 编号让位(2026-09-17):本文件原为 V149,与远端已推送的 V149__clear_service_hero_placeholders 撞号,本地让号改为 V152。
-- 支付尝试按「这一次尝试」收敛所需要的两列(判定见 docs/adr/0003)。
--
-- last_reconciled_at  对账任务的公平轮转与退避依据。旧实现按「最早 100 条」取未决流水,一旦积压
talent-card-server/src/main/resources/db/migration/V153__merchant_numeric_code_attempt.sql
File was renamed from talent-card-server/src/main/resources/db/migration/V150__merchant_numeric_code_attempt.sql
@@ -1,3 +1,4 @@
-- 编号让位(2026-09-17):本文件原为 V150,与远端已推送的 V150__merchant_hero_image 撞号,本地让号改为 V153。
-- 商户端手输 6 位数字核销码的失败计数与锁定。
--
-- 核销码本来就下发一个 6 位数字码给顾客(order_dynamic_verification_code.display_code),但系统里
talent-card-server/src/main/resources/db/migration/V154__order_cancel_request.sql
File was renamed from talent-card-server/src/main/resources/db/migration/V151__order_cancel_request.sql
@@ -1,3 +1,4 @@
-- 编号让位(2026-09-17):本文件原为 V151,同批让号后顺延为 V154(远端那两个提交占了 V148–V150)。
-- 「取消意图」要能跨请求活到关单结论回来的那一刻。
--
-- 用户在通道说「关单处理中」时点了取消,服务端当次只能回一句「取消处理中」——真正该取消的时机是