yeg
2026-09-17 f9ee56e16ea18789c3b9906175c2500b540a4a7c
docs(agents): 按开发库改用共享库更正环境事实

开发库已不是本机库,仓库里多处仍在说「开发库就是 127.0.0.1:8134/talent_card」,
会让人查错库、甚至把共享库当成本机副本去动。

- docs/agents/testing.md:新增「环境一览」——开发库是局域网 192.168.110.13:3306/talent_card
(MySQL 5.7.44、flyway 关闭、后端与后端管理页和两个小程序都连它),测试库是本机
127.0.0.1:8134/talent_card_test(8.0.36、flyway 开启);由此写明两条硬约束(迁移要同时兼容
5.7 与 8.0;开发库结构只靠初始化器 best-effort 补)。「不要重建开发库」一节换成正确的库、
把失效的 docker exec 备份命令换成可行的做法,并补上初始化器 5 秒等锁上限导致「重启过也不一定
有新列」这条(就是 09-17 那次 500 的成因)。另加一节「环境相关失败」记录
PaymentFundConfirmationTests 因测试库遗留行超过 limit 50 而必然失败,附交接文档链接。
- AGENTS.md:悬空指针「各业务域的代码位置见 CONTEXT.md」改为直接写明两个域的范围,并注明
CONTEXT.md 只是术语表。
- application.yml 注释:默认连共享开发库,本机副本只作 MYSQL_URL 覆盖示例。
- talent-card-server/README.md:本地启动不再称「默认适配本机 MySQL 5.7」,环境变量表里
MYSQL_URL 的默认值改为实际值,失效的 D:\MySQL 路径与旧库修复脚本标注为历史。
- .scratch/db-merge/README.md:标一句合并已完成、本机副本不再作为开发库。
5个文件已修改
79 ■■■■■ 已修改文件
.scratch/db-merge/README.md 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
AGENTS.md 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/agents/testing.md 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/README.md 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/resources/application.yml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.scratch/db-merge/README.md
@@ -3,6 +3,11 @@
本目录记录 2026-09-16 把**本机开发库**与**服务器库**并到一起的过程,以及可复现的工具与判定规则。
目录里的探针都是一次性只读/迁移工具,不是业务代码。
**2026-09-17 现状**:合并已完成,开发库从此就是那个共享库(`192.168.110.13:3306/talent_card`,MySQL 5.7.44);
本机 `127.0.0.1:8134/talent_card` 只是合并前的副本,**不再作为开发库**使用(当前环境一览见
`docs/agents/testing.md`)。本目录的工具只在需要重新比对或补 DDL 时才用,跑之前把 URL 换成当前开发库——
`.scratch/` 下更早留下的一次性探针同理,它们默认写的就是那份旧副本。
## 当时的事实
| | 服务器库 `192.168.110.13:3306/talent_card` | 本机开发库 `127.0.0.1:8134/talent_card` |
AGENTS.md
@@ -20,7 +20,9 @@
- **只改用户本次指派范围内的代码、迁移和前端。** 范围以用户的说明为准;用户没说清、或任务看起来横跨
  多个业务域时,先问,不要自己扩大范围。
- **范围外的业务只做只读排查**:可以读代码、查库、跑 SQL、给出结论和建议,但不要直接改它的代码、
  迁移和前端。各业务域的代码位置见 `CONTEXT.md`。
  迁移和前端。域划分大致是:**支付链路**=汇付/斗拱进件与开通、支付、核销后的资金确认、自动结算与
  结算记录、退款;**其余业务**=家属卡、洗车、停车、票券包、权益规则、资讯、各类申请审核等。
  (根目录 `CONTEXT.md` 是**术语表**,里面没有代码位置。)
- 需要修范围外的缺陷时,把「现象 + 根因 + 证据 + 建议改法」整理成 Markdown(放
  `.scratch/<feature-slug>/issues/`)交给用户转给该业务的负责人,不要直接动手。
- **迁移编号是全仓共享的**:新增迁移前先看 `talent-card-server/src/main/resources/db/migration/` 里当前
@@ -34,8 +36,9 @@
后端测试跑在共享的 MySQL 测试库上。跑测试用 `talent-card-server/scripts/run-server-tests.ps1`,
重置库用 `talent-card-server/scripts/reset-test-db.ps1`,并遵守:已执行的迁移只追加不修改、同一
checkout 一次只跑一个 Maven 构建、不要 `drop database`。详见 `docs/agents/testing.md`(含 3 项已知
基线失败,不要为证明它们"是既有的"而重复造 worktree)。
checkout 一次只跑一个 Maven 构建、不要 `drop database`。详见 `docs/agents/testing.md`(**开发库与
测试库是哪两个库、什么版本**、3 项已知基线失败,以及一类会随测试库累积而出现的环境相关失败;不要为
证明它们是"既有的"而重复造 worktree)。
### Git 提交
docs/agents/testing.md
@@ -3,6 +3,23 @@
本文件是 `talent-card-server` 的测试约定。目标是让**新会话**不需要重新摸索环境:一条命令跑测试,
一条命令重置库,并且不重蹈已知的坑。
## 环境一览(2026-09-17 实测)
| | 库 | 版本 | Flyway | 谁在连 |
| --- | --- | --- | --- | --- |
| **开发库** | `192.168.110.13:3306/talent_card`(局域网共享) | MySQL **5.7.44** | **关**(`flyway.enabled=false`) | 后端、后台管理页、两个小程序 |
| **测试库** | `127.0.0.1:8134/talent_card_test`(本机实例) | MySQL **8.0.36** | **开** | 本文件的测试脚本 |
三条由此推出的硬约束:
- **迁移必须同时兼容 5.7 与 8.0**:测试只在 8.0 上跑,跑绿不代表开发库能执行(`V148` 的存储过程写法
  已在 5.7 上实跑验证过,但每次新增迁移都值得照做一遍)。
- **开发库的结构不由 Flyway 维护**:缺表缺列只能靠启动时的初始化器 best-effort 补,补不上就手工补(见下)。
- 本机 `8134` 实例上另有 `talent_card`、`talent_card_dryrun`、`talent_card_verify1` 三个库:它们是
  2026-09-16 把本机库并入服务器库**之前**的本机副本,**不再是开发库**,不要拿它们判断开发库状态。
  早先会话留在 `.scratch/` 下的一次性探针(`*Probe*.java`、`Inspect*.java`、`Apply*.java` 等)大多把 URL
  写死成 `127.0.0.1:8134/talent_card`,指的就是这份旧副本;复用时要显式传开发库 URL,别照默认值跑。
## 一条命令跑测试
```powershell
@@ -38,19 +55,24 @@
## 不要重建开发库
`127.0.0.1:8134/talent_card` 是**开发库**(后台管理页和两个小程序都连它),不是测试库。它里面有
开发库是局域网共享的那一个(`192.168.110.13:3306/talent_card`,见上表),不是测试库。它里面有
一批**迁移从未创建**的表——当年是直接在库里手建的,例如 `dependent_card_apply`、`merchant_benefit_rule`。
所以「把测试库结构克隆到开发库」「`reset-test-db.ps1 -Database talent_card`」这类操作会把开发库里这些
表连同数据一起删掉:2026-09-13 就这样丢过一批,最后靠 `127.0.0.1:6134/talent_card` 上的同源快照才回灌回来。
表连同数据一起删掉:2026-09-13 就这样丢过一批,最后靠一份同源快照才回灌回来(`reset-test-db.ps1`
对名为 `talent_card` 的库一律拒绝,正是为此)。
注意开发库的 `spring.flyway.enabled=false`:它**不会**被迁移自动修正,缺表只能手工补。
注意开发库的 `spring.flyway.enabled=false`:它**不会**被迁移自动修正,缺表缺列只能手工补。
需要让开发库结构跟上迁移时:
- 只补缺失的 DDL(`create table if not exists`),**不要**删表重建;
- 动结构之前先 dump:`docker exec talent-card-mysql-1 mysqldump -uroot -p123456 talent_card > backup.sql`;
- 从旧快照回灌数据用 `.scratch/automatic-withdrawal/tools/RestoreOldData.java`(按列交集 `INSERT IGNORE`,
  只新增不删改)。该 MySQL 开了 binlog 且保留 30 天,误删后还能查证与补救。
- 只补缺失的 DDL(`create table if not exists`、`add column`),**不要**删表重建。手工补 DDL 正是
  2026-09-17「点开收款能力就 500 / `Unknown column 'capability_sync_until'`」的解药。
- 动结构之前先备份。本机**没有** mysql 客户端、也没有 Docker(README 里指向 `D:\MySQL\...` 和
  `docker exec talent-card-mysql-1` 的两条老做法都已失效),要备份就在 DB 主机上做,或照
  `scripts/reset-test-db.ps1` 的办法用项目已有的 JDBC 驱动写个单文件程序。
- 初始化器是 **best-effort** 的:它的 `alter table` 等 DDL 元数据锁的上限只有 5 秒,等不到就记一条
  error 放行(宁可少一列,也不能让启动被 DDL 挂住——2026-09-15 为此瘫过一次)。所以「重启过就一定有
  新列」不成立:启动日志里搜 `Merchant onboarding schema: could not add`,那里会给出可直接执行的手工补救语句。
## 迁移是只追加的
@@ -70,6 +92,18 @@
新会话不要为了证明它们"是既有的"而重建 worktree:直接看这张表。修好其中任何一项时,请删掉对应行。
## 环境相关失败(不是基线,别当既有失败放过)
`PaymentFundConfirmationTests.aPendingConfirmationOfAVerifiedOrderIsRetriedWithoutAnotherVerification`
在测试库累计的「已核销 + 待确认」订单超过 50 条时会**必然**失败:补偿任务扫全库、`order by o.id asc
limit 50`(`PaymentFundConfirmationRetry.retry`),而用例新建的那单 id 最大、排在窗口之外,于是状态停在
`pending`。这是测试库长期不清理累积出来的,不是代码回归——2026-09-17 实测符合条件的行为 60 条,最早一条
09-14 就留下了,而且被处理过的行多数停在 `confirming`(仍在扫描条件内),所以不会自己排空。
现象、证据与建议改法见
[.scratch/payment-fund-confirmation-retry/issues/01](../../.scratch/payment-fund-confirmation-retry/issues/01-retry-limit-blocked-by-accumulated-leftovers.md)
(属支付链路范围,本轮只排查未修)。要么让用例自己把窗口腾出来,要么清理测试库遗留(后者只能缓解)。
## 测试库的状态是共享的
Spring 测试共用一个 schema,测试之间不做清理(没有 `@Transactional` 回滚)。因此:
talent-card-server/README.md
@@ -15,12 +15,15 @@
## 本地启动
当前默认适配本机 MySQL 5.7:`localhost:3306`,数据库名 `talent_card`,账号 `root`,密码 `123456`。
默认连**共享开发库**:`192.168.110.13:3306/talent_card`(MySQL 5.7,`root`/`123456`,
`flyway.enabled=false`)。本机不需要再起 MySQL。
先在本机 MySQL 执行初始化脚本:
下面的初始化脚本与 `D:\MySQL\MySQL Server 5.7\...` 那套做法是**并入共享库之前的历史**,只在自己搭一套
全新的库时才需要(本机那份 mysql 客户端已不存在)。库的归属与版本以
[`docs/agents/testing.md`](../docs/agents/testing.md) 的「环境一览」为准。
```powershell
& "D:\MySQL\MySQL Server 5.7\bin\mysql.exe" -uroot -p < scripts\init-local-mysql.sql
& "<本机 mysql 客户端>\bin\mysql.exe" -uroot -p < scripts\init-local-mysql.sql
```
```bash
@@ -53,6 +56,7 @@
```
该脚本仅用于已中断 V9 的旧本机数据库;它保留历史电影票记录,并让 Flyway 重新执行 V9 及后续迁移。新建数据库不需要执行。
(命令里的本机客户端路径已失效,换成任意可用的 mysql 客户端即可;共享开发库不涉及这套修复。)
如果临时想用 Docker MySQL,compose 已经避开本机 3306,映射为 `localhost:3307`:
@@ -87,7 +91,7 @@
| 变量 | 默认值 |
| --- | --- |
| `SERVER_PORT` | `8080` |
| `MYSQL_URL` | `jdbc:mysql://localhost:3306/talent_card...` |
| `MYSQL_URL` | `jdbc:mysql://192.168.110.13:3306/talent_card...`(共享开发库;可用环境变量覆盖) |
| `MYSQL_USERNAME` | `root` |
| `MYSQL_PASSWORD` | `123456` |
| `REDIS_HOST` | `localhost` |
talent-card-server/src/main/resources/application.yml
@@ -9,8 +9,9 @@
      max-file-size: 10MB
      max-request-size: 10MB
  datasource:
    ## 本地开发:用 MYSQL_URL 覆盖成本机库(例如 jdbc:mysql://127.0.0.1:8134/talent_card)。
    ## url: ${MYSQL_URL:jdbc:mysql://127.0.0.1:8134/talent_card?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false}
    ## 默认连共享开发库(局域网 192.168.110.13,MySQL 5.7,flyway 关闭)。
    ## 要连别的库时用 MYSQL_URL 覆盖,例如本机 8134 上的那份副本:
    ##   jdbc:mysql://127.0.0.1:8134/talent_card?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false
    url: ${MYSQL_URL:jdbc:mysql://192.168.110.13:3306/talent_card?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false}
    username: ${MYSQL_USERNAME:root}
    password: ${MYSQL_PASSWORD:123456}