docs(plans): 新增 J1 条目(全局 ID 字符串化 + 契约统一,用户裁定方案 a)
雪花 id 19 位超出 JS 安全整数,I1 期间 Codex 实测与 PM 独立复核均复现 (点下架回 200002、/admin/app-users/page 下发裸数字)。用户裁定全局修改并统一契约。 本条目给出范围:framework 全局 Long→String、契约 §0 总口径、两个前端适配、 验证要求(含 node 逐位比对证据)与门禁。
This commit is contained in:
1 parent
10264c4ef6
commit
c503629bf9
1 file changed
+52
@@ -19,6 +19,58 @@
|
||||
|
||||
---
|
||||
|
||||
## [待处理] 2026-10-16 18:30 | J1 全局 ID 字符串化(Long → String)+ 契约统一
|
||||
|
||||
**用户裁定(2026-10-16 18:10)**:雪花 id 精度问题 **「按方案 (a) 全局修改,统一契约」**。
|
||||
|
||||
**背景(两方独立复现同一根因,故现在铺全局)**:
|
||||
- 你在 I1 里实测到:UI 点「下架」后端回 `200002 公告不存在` —— 库里 `2111011206851596290`,浏览器 `JSON.parse` 后变 `2111011206851596300`,回查自然找不到。你把修复**限定在本轮两个模块**并留了待裁定。
|
||||
- PM 独立复核同样确认:新注册用户 id `2111012753262120962` 由 `/admin/app-users/page` **以裸 JSON 数字**下发;用真 JS 引擎验证 `JSON.parse('{"id":2111012753262120962}').id` → `2111012753262121000`(**不相等**)。根因是 JS `Number.MAX_SAFE_INTEGER = 9007199254740991`,19 位雪花 id 必然超出;而服务端**没有全局 `Long→String` 序列化配置**。
|
||||
- 种子数据都是小 id(4/5/25)所以老页面一直正常 —— 这是**只要"新建后立刻拿 id 操作"就必然踩**的坑。
|
||||
|
||||
### 一、后端:全局序列化(放 `fruitshorts-framework`,两个入口都生效)
|
||||
|
||||
1. `Long.class` 与 `Long.TYPE` 一律用 `ToStringSerializer`。
|
||||
2. ⚠️ **不要动 `Integer` / `int`**(分页 `pageIndex`/`pageSize` 等保持数字)。
|
||||
3. 必须覆盖:普通字段、**嵌套对象**、`List`/`Map` 的值、分页 `list`。
|
||||
4. **反序列化方向也要验**:请求体 / 路径变量 / 查询参数里 id 传**字符串**要能正确绑定成 `Long`(前端回传的就是字符串)。
|
||||
|
||||
### 二、契约统一(`docs/api-phase1.md` §0 通用约定)
|
||||
|
||||
- 写明「**所有 ID 字段一律以字符串下发**」(含主键与外键 id),并给示例。
|
||||
- 把 I1 里逐模块的描述(商品/公告 VO 各自加 `ToStringSerializer`)**收拢到这条总口径**,不要各写一套。
|
||||
- 顺手把 §2.11/§2.12 里与总口径重复的表述改成引用 §0。
|
||||
|
||||
### 三、两个前端适配
|
||||
|
||||
1. **ReelShortWeb**:审计现有页面有没有把 id 当**数字**用 —— `parseInt(id)` / `Number(id)` / `id + 1` / 拿 id 当数组下标 / `===` 与数字比较 / `sort((a,b)=>a.id-b.id)`,逐处改成字符串语义。本轮新加的两个页面已按 `string` 写,**别回退**。
|
||||
2. **ReelShort(Flutter)**:`fromJson` 里 `json['id'] as int` 这类写法在字符串下发时会**直接抛异常**,必须审计并修。策略二选一(推荐前者,改动小且向前兼容):
|
||||
- **兼容解析**:统一按"接受字符串或数字"的方式解析(如 `int.tryParse(v.toString())`),或把模型里的 id 保留为 `String`;
|
||||
- 或全量改成 `String`(改动更大,要与客户端同事对齐)。
|
||||
- ⚠️ 客户端现由另一同事在推进:**只做"ID 解析"这一类最小改动**,不要顺手改别的;有分歧或改不动就写「【待 Trae 协助】」,并在回执里给出**逐文件清单**。
|
||||
|
||||
### 四、验证(必须有证据,不接受"应该没问题")
|
||||
|
||||
1. 后端契约测试:`Long` 字段确实以**字符串**输出(含嵌套 / 分页 list / Map)。
|
||||
2. **端到端复现 I1 那个 bug 并证明已消失**:admin 新建一条记录 → 用响应里的 id 调详情 / 编辑 / 上下架 → **成功**(全局范围内,不再只限商品/公告)。
|
||||
3. **JS 侧一致性证据**:拿真实响应喂 `node -e 'const d=JSON.parse(...); console.log(String(d.id))'`,证明与库里的值**逐位相等**。
|
||||
4. C 端:注册 / 播放票据 / 订单等返回的 id 也是字符串,且**客户端解析不崩**(Flutter 侧跑测试)。
|
||||
|
||||
### 五、门禁与 git
|
||||
|
||||
- `mvn test` 存量 **110 条不得跑红** + 新增测试;Web `pnpm ts:check` + eslint 零错误 + `pnpm build:pro` 通过;若改了客户端则 `dart analyze lib test` 零告警 + `flutter test` 全过。
|
||||
- FruitServer / ReelShortWeb / ReelShort **各自在 `develop` 提交并推送**;**绝不动 `main`**。
|
||||
- 新写文件首行 `// Author: fruit.com.cn`(Vue/TS 同;Dart 用 `// Author: fruit.com.cn`)。
|
||||
|
||||
### 六、节奏
|
||||
|
||||
先写 `FruitServer/docs/plans/2026-10-16-J1.md`(方案 + 影响面清单 + 风险)→ 做 → 跑门禁 → **停在干净交接点写回执**,回执必须含:
|
||||
- 每个仓库改了哪些文件(**客户端逐文件清单**要单独列)
|
||||
- 「用户可照做的验证步骤 + 每步预期结果」
|
||||
- 若客户端有未改完/有分歧的部分,**如实列出**(不许含糊)
|
||||
|
||||
---
|
||||
|
||||
## [已处理·待用户验收] 2026-10-16 17:30 | I1 后台管理端:商品(金币包)配置 + 系统公告管理(后端接口 + Web 页面)
|
||||
|
||||
**用户指令(2026-10-16 17:10)**:「现在做后台管理端,规范还是按照 specs」+「ReelShortWeb 就是这个项目」→ **后台管理端 = `ReelShortWeb`(Vue3 + Element Plus),本批后端接口与页面一起做**。
|
||||
|
||||
Reference in new issue
Block a user