feat(framework): 全局 Long 字符串化,ID 一律以字符串下发
用户裁定方案 (a):雪花 id 精度问题全局修,统一契约。 - 新增 JacksonLongToStringConfig(fruitshorts-framework):Long/Long.TYPE → ToStringSerializer, 经 Jackson2ObjectMapperBuilderCustomizer 挂进 Spring Boot 的 ObjectMapper;admin-api 与 app-api 都是 scanBasePackages=com.fruitshorts,一处生效两端一致 - 例外:PageResult.total 是计数不是 id,用 mixin 显式保持数字(前端分页组件要数字); Integer/int(pageIndex/pageSize/Result.code 等)不受影响 - 反序列化方向不变:请求体 'id:123'、路径变量、查询参数都接受字符串 - 契约 api-phase1.md §0 新增「ID 字段一律以字符串下发」总口径,§2.11/§2.12 改为引用 §0; I1 里逐字段加的 @JsonSerialize 与 Result<String> 回退成自然写法,避免多套口径 - 测试:新增 LongToStringSerializationTest(普通/嵌套/List/Map/分页 + 反序列化 + 19 位路径变量), 契约测试的 standalone mapper 也挂上模块(否则测试假绿);C 端 4 条 id 断言改成 isString - mvn test:commerce 33 / admin-api 21 / app-api 49 / worker 12 = 115 条全过
This commit is contained in:
1 parent
c503629bf9
commit
f35db2cd3b
16 files changed
+355
-33
No files matched your search
+25
-6
@@ -16,6 +16,29 @@
|
||||
|
||||
`code = 0` 成功;非 0 时 `message` 可直接展示给用户。分页统一为 `{ total, list }`。
|
||||
|
||||
**ID 字段一律以字符串下发(2026-10-16 J1 起,全局生效)**
|
||||
|
||||
所有 ID 字段(主键与外键,例如 `id` / `dramaId` / `episodeId` / `userId` / `orderId` …)
|
||||
在 JSON 里都是**字符串**,不是数字:
|
||||
|
||||
```json
|
||||
{ "code": 0, "message": "ok", "data": { "id": "2111012753262120962", "dramaId": "5" } }
|
||||
```
|
||||
|
||||
原因:S0 之后主键是雪花 id,19 位十进制数已超过 JS 的 `Number.MAX_SAFE_INTEGER`
|
||||
(`9007199254740991`)。裸数字下发会被浏览器/IEEE754 客户端四舍五入
|
||||
(实测库里的 `2111012753262120962` 在浏览器里变成 `2111012753262121000`),
|
||||
页面再拿它回查就是 `200002 数据不存在`。
|
||||
|
||||
约定与边界:
|
||||
|
||||
- **id 回传时按字符串传**(路径变量 / 查询参数 / 请求体都接受字符串,服务端自己转 `Long`);
|
||||
- 实现方式:服务端**全局**把 `Long` / `long` 序列化成字符串(`fruitshorts-framework` 的
|
||||
`JacksonLongToStringConfig`),**各模块不再逐字段加注解**;
|
||||
- **例外:分页 `total` 仍是数字** —— 它是条数不是 id,前端分页组件要数字;
|
||||
`Integer` / `int` 字段(`pageIndex` / `pageSize` / 各类计数如 `totalEpisodes`、状态码 `code`)
|
||||
也**保持数字**。
|
||||
|
||||
**鉴权**:`Authorization: Bearer <token>`。
|
||||
管理端与客户端用**两套独立密钥**签发,管理端 token 打到客户端接口会直接被拒。
|
||||
客户端 `access token` 默认 2 小时,`refresh token` 30 天且每次刷新轮换。
|
||||
@@ -773,10 +796,7 @@ GET /app/notices?pageIndex=1&pageSize=20
|
||||
| `sort` / `status` | 排序 / 上下架 |
|
||||
| `effectiveAt` / `expireAt` | 生效与失效时间(可空) |
|
||||
|
||||
> **⚠️ `id` 以字符串下发**(2026-10-16 实测补充):S0 换雪花主键后 id 已超过 JS 的安全整数范围
|
||||
> (2^53-1 = 9007199254740991)。直接下发数字会被浏览器四舍五入(实测 `2111011206851596290`
|
||||
> 在 JS 里变成 `2111011206851596300`),页面拿这个 id 回查就是"商品不存在"。
|
||||
> 因此**本节所有响应里的 `id`(含新建返回的新 id)都是字符串**,前端原样拼进 URL 即可。
|
||||
> `id` 以字符串下发:见 **§0「ID 字段一律以字符串下发」**(本节不再单独规定)。
|
||||
|
||||
**校验与错误码**:
|
||||
|
||||
@@ -809,8 +829,7 @@ GET /app/notices?pageIndex=1&pageSize=20
|
||||
**字段**:`title` / `content` / `target`(`ALL` / `USER` / `CREATOR`)/ `type`(`SYSTEM` / `AUDIT` / `PAYMENT` / `ACTIVITY`)/
|
||||
`status` / `publishAt` / `expireAt`;响应另带 `statusText` / `targetText` / `typeText`。
|
||||
|
||||
> **⚠️ `id` 以字符串下发**:同 §2.11 —— 雪花 id 超过 JS 安全整数范围,数字下发会被浏览器四舍五入,
|
||||
> 导致"下架/编辑"回查不到记录。本节的 `id`(含新建返回的新 id)一律是字符串。
|
||||
> `id` 以字符串下发:见 **§0「ID 字段一律以字符串下发」**。
|
||||
|
||||
**与 C 端口径严格对齐(同一条查询实现)**:C 端 `GET /app/notices` 只返回
|
||||
`status=1` 且 `target ∈ {ALL, USER}`、`publish_at` 为空或 `<= now`、`expire_at` 为空或未过期的公告,
|
||||
|
||||
Reference in new issue
Block a user