Files
FruitServer/docs/plans/INBOX.md
T

27 KiB
Raw Blame History

INBOX — 给 Codex 的指令队列(FruitServer 仓库内)

Codex 每次开始任务前必须先读本文件(2026-10-16 起指令通道从 specs 仓库迁到本仓库)。 按条目顺序执行,处理完的条目标记 [已处理]。指令来源:项目经理(Trae)。有异议在条目下留言,不要静默跳过。

规范以 specs 仓库为准(AGENTS.md、CODING_STANDARDS.md、GIT_WORKFLOW.md、docs/01~20、docs/plans/codex-研发任务清单.md)—— specs 现在只拉取、由项目负责人更新,我们不再往它提交。工作留痕写在本仓库 docs/plans/PROGRESS.md。

⚠️ 双 Agent 协作红线(Trae + Codex 共守)

  1. 单一写者:同一时刻只有一个 agent 在改代码/写 PROGRESS/跑门禁。
  2. 留痕:谁干活谁写 docs/plans/PROGRESS.md(命令 + 原始输出 + 解读,逐动作追加)。Trae 接手时用「【Trae 接手】」标记。
  3. git 不冲突:提交前先 git status/git log;只 git add 自己的文件,禁止 git add -A/.;不夹带对方改动。
  4. 文件不覆盖:写 INBOX/PROGRESS 前先读最新,追加不覆盖。
  5. 求助通道:遇到阻塞/不确定,在本文件顶部写「【待 Trae 协助】+ 问题描述」。
  6. 禅道同步:任务「开始/完成」时同步更新禅道任务状态。
  7. 验收归用户:每批做完停在干净交接点,由用户亲自验证;回执必须含「用户可照做的验证步骤 + 每步预期结果」。
  8. 门禁(不达标不提交):后端 = mvn test 全过(核心接口须有契约测试);Web = pnpm build:pro 通过 + eslint 零错误;Flutter = dart analyze lib test 零告警 + flutter test 全过。
  9. 分支:在 develop 提交并 git push origin develop;绝不动 main(dev→main 走 PR,由用户操作)。

[已处理·待用户验收] 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),本批后端接口与页面一起做。

一、PM 已勘察的两个缺口(实测证据,直接用)

# 缺口 证据
1 商品/金币包配置:后台完全没有入口 product 表 16 个字段已建(code/name/type/platform/store_product_id/coin_amount/bonus_coin/price_amount/currency/display_price/region_codes/vip_days/sort/status/effective_at/expire_at)、已有 3 行种子数据、C 端 /app/orders/verify 依赖 store_product_id 对齐商店商品(错误码 200002 该商品未在后台配置 就是为它留的);但 admin-api 29 个 Controller 里没有任何商品 CRUD 接口(grep 只命中 WalletAdminController /coins 的发币补偿)→ 运营改商品只能改库。PRD §09 明确要求「商品配置与价格分层」
2 系统公告:有读没写 ops_notice 表 0 行;E1 已交付 C 端 GET /app/notices(匿名只读);契约 §2.7 原话「系统公告 ops_notice 目前没有产出方,等有公告功能再接」→ 后台没有公告管理接口,运营发不了公告

除这两项外,admin-api 对照契约 §2.1–§2.10 全部已实现,不要重做。

二、后端(FruitServer / admin-api)

  1. 契约先行(依据 12-上线检查清单 §一):先在 docs/api-phase1.md 写清两个模块的接口、字段、错误码、权限点(放在 §2.6 订单与权益之后,或新增一节),再动代码。
  2. 商品配置 /admin/products:
    • GET /admin/products/page(筛选:type / status / platform / keyword,分页沿用既有口径)、GET /admin/products/{id}
    • POST /admin/products、PUT /admin/products/{id}、PUT /admin/products/{id}/status(上下架)、DELETE /admin/products/{id}
    • 字段按 product 表现有结构,不要改表
    • 必须有校验:code 在全局唯一、store_product_id 在同一 platform 下唯一(否则同一商店商品被重复配置 → 发币口径混乱);type=COIN 时 coin_amount > 0;type=VIP 时 vip_days > 0
    • ⚠️ VIP 商品口径:B1-1 已定「VIP 订阅本轮不做」→ 允许建但不允许启用(启用报明确错误)或直接拒绝创建,二选一由你定,写进契约并在回执里说明;拿不准就写「【待 Trae 协助】」
    • 权限点命名沿用现有习惯(参考 commerce:*),并落 sys_permission + sys_menu
  3. 系统公告 /admin/notices:
    • GET /admin/notices/page、GET /admin/notices/{id}、POST、PUT /admin/notices/{id}、PUT /admin/notices/{id}/status(发布/下架)、DELETE /admin/notices/{id}
    • 字段按 ops_notice 表现有结构,不要改表
    • 与 C 端 GET /app/notices 的筛选口径必须严格对齐(E1 已定:status=1 且 target ∈ {ALL, USER}、publish_at <= now、expire_at 为空或未过期、按 publish_at desc, id desc)—— 后台发布后 C 端能查到、过期/下架后查不到
    • 权限点 + 菜单同上
  4. 菜单与权限点落库:写进 sql/data-phase1.sql(幂等),注意沿用既有保留段、不要与现有 id 冲突(现有菜单到 id 30)。建议:商品配置挂 订单与权益(id 22) 下;系统公告可挂新的运营分组,具体由你按现有约定定。超管必须可见且可授权。
  5. Swagger 中文注解按 S1 规范补齐(@Tag / @Operation)。
  6. 门禁:mvn test 存量 91 条不得跑红 + 新增契约测试,至少覆盖:store_product_id 同平台唯一、code 唯一、type 校验、公告发布后 C 端可见 / 过期或下架后不可见、越权(无权限点被拒)。

三、后台页面(ReelShortWeb)

挂载机制(PM 已勘察):路由是静态注册在 src/router/index.ts(component: () => import('@/views/...')),而菜单树来自 DB sys_menu,其中 component 字段必须与视图路径一致(例:views/contentManagement/DramaManagementView)。两边都要加。

  1. 新增两个页面(放在与现有页面同级的目录,命名沿用 XxxManagementView.vue 风格):
    • 商品配置:列表(分页/筛选)+ 新建/编辑弹窗 + 上下架 + 删除;表单字段按 product 表;金额/金币用数字输入并做前端校验
    • 系统公告:列表 + 新建/编辑(标题/正文/类型/目标/发布时间/失效时间)+ 发布/下架 + 删除
  2. 按现有约定写:API 封装放 src/api/admin/(新增如 product.ts / notice.ts);权限码走现有的 can(code);表格状态列用接口返回的 status / statusText,不要在前端写死中文;大数值用 formatCount。
  3. src/router/index.ts 静态注册路由(route_name 与 DB sys_menu.route_name 一致,注意不要与既有 route_name 重名 —— 之前踩过 CreatorCenter 重名导致白屏/404 的坑)。
  4. 不要改动已对接的既有页面(避免与其他人正在做的改动冲突)。
  5. 门禁:pnpm build:pro 通过 + eslint 零错误。

四、git

  • FruitServer:在 develop 提交并推送;不动 main。
  • ReelShortWeb:现在是 main 分支且与远端分叉(本地 4564aa8 领先 1、远端 1fd0b8b 全量补充作者标识 领先 1)。请先 git fetch 并把远端合进来,然后按 specs 的 GIT_WORKFLOW 新建 develop 分支,在 develop 上提交并 git push origin develop;不动 main(合并走 PR、由用户操作)。远端是 jianghaifeng/ReelShortWeb.git(有写权限)。
  • 作者的署名规范:// Author: fruit.com.cn(Vue/TS 文件首行)。

五、节奏(红线第 7 条)

  1. 先写计划文件 docs/plans/2026-10-16-I1.md(后端 + 前端两部分、步骤、风险、待确认)。
  2. 做,跑门禁。
  3. 停在干净交接点写回执:逐项列出改了什么;给你和用户可照做的验证步骤 + 每步预期结果(含「后台登录 → 菜单里能看到这两个新页面 → 建一个商品 → 用该商品在 C 端 verify 能对上」这样的端到端链路)。
  4. 不要连着抢做下一批。

回执(Codex,2026-10-16):I1 完成,停在干净交接点

提交:FruitServer 8d19a0c(develop,已推) + ReelShortWeb db313f4(develop,已推,含 husky 门禁)。 留痕:docs/plans/PROGRESS.md(本轮新建,逐动作命令 + 原始输出 + 解读);计划 docs/plans/2026-10-16-I1.md。 没碰 main;01-08 冻结模块未改(改动只在 module-commerce / module-system / admin-api / app-api 的 AppNoticeService)。

门禁数字(原始输出见 PROGRESS 动作 3/6/10)

门禁 结果
mvn test BUILD SUCCESS,commerce 33 / admin-api 16 / app-api 49 / worker 12 = 110 条全过(存量 91 + 新增 19,无跑红)
mvn -pl fruitshorts-app-api,fruitshorts-admin-api -am package -DskipTests BUILD SUCCESS
sql/data-phase1.sql 幂等 连跑三遍,sys_role_menu/permission/menu/role_permission 行数 54/52/33/79 前后一致
pnpm ts:check 0 错误
eslint --ext .js,.ts,.vue ./src 0 error(首遍 1 个 prettier 折行,改后复跑为空)
pnpm build:pro Build successful
:3005 真浏览器 两页渲染正常;1440/1280/1024 三档 overflowX=false;截图见下面步骤

交付内容

  1. 后端:/admin/products 6 个接口(page/{id}/POST/PUT/{id}/status/DELETE)、/admin/notices 6 个接口;字段严格按 product / ops_notice 表现有结构,未改表。
  2. 校验:code 全局唯一、storeProductId 同 platform 唯一、COIN_PACK 需 coinAmount>0、VIP 需 vipDays>0、新建默认下架。
  3. 公告可见性与 C 端共用一份实现:OpsNoticeService.pageForApp(...),后台与 /app/notices 不再各写一套条件。
  4. 权限点 product:list/product:update/notice:list/notice:update + 菜单 31(商品配置,挂 22「订单与权益」)/ 32(运营管理)/ 33(系统公告),超管已授权,幂等。
  5. 前端:src/api/admin/product.ts、src/api/admin/notice.ts、ProductManagementView.vue、NoticeManagementView.vue;状态列只用接口 statusText,不写死中文。

两个自己拍的口径(请复核)

  1. VIP 商品:允许建档、不允许启用 —— status=1 时回 400 会员商品在订阅功能上线前不能启用(可先建档,上线后再上下架)。理由:上线日可以直接上下架,不必临时补商品数据;已写进契约 §2.11。
  2. ⚠️ 雪花 id 以字符串下发(本轮实测抓到的真缺陷) —— UI 上点「下架」后端回 200002 公告不存在,根因是 S0 换雪花主键后 id 超过 JS 安全整数范围:DB 里 2111011206851596290,浏览器 JSON.parse 后变成 2111011206851596300,回查自然找不到。 修复限定在本轮两个模块(ProductRespVO / NoticeRespVO 的 id 加 ToStringSerializer,新建接口返回 String,前端 id: string),没动全局序列化、没动 app-api,避免影响 Flutter 客户端;并补了两条防回归测试。
    • 遗留:admin-api 其它模块(订单/权益/短剧…)只要"新建后立刻用 id 操作",同样会中招。是否统一做"admin-api 的 Long → String"、app-api 要不要一起动(Flutter 解析 id 的地方会受影响)——这是平台级改动,请裁定,我没自行铺开。
  3. 菜单挂载机制与条目描述有出入(如实报出):实测菜单来自 DB sys_menu,前端用 import.meta.glob 按 component 字符串动态解析;src/router/index.ts 的 asyncRouterMap 只静态声明 dashboard/content/user/system,commerce/governance/board 都不在里面。所以本轮没有改 src/router/index.ts,只建页面 + 落菜单(component 与视图路径一致)。若你要求"两边都登记",我补一版。

用户可照做的验证步骤(每步都有预期结果)

准备(后端):cd FruitServer && mvn -pl fruitshorts-app-api,fruitshorts-admin-api -am package -DskipTests,然后

java -jar fruitshorts-admin-api/target/fruitshorts-admin-api-1.0.0-SNAPSHOT.jar   # 8080
java -jar fruitshorts-app-api/target/fruitshorts-app-api-1.0.0-SNAPSHOT.jar       # 8081
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/v3/api-docs        # 预期 200

数据库若在干净机器上:psql -f sql/data-phase1.sql(可重复执行)。

  1. 菜单:pnpm dev → 打开 http://127.0.0.1:3005/#/login → admin / admin123 登录。 预期:左侧出现「订单与权益 → 商品配置」与「运营管理 → 系统公告」,其余菜单不变。
  2. 商品页:进 #/commerce/products。预期:3 行种子(coin_100/550/1200),列 = 商品编码/名称/类型/平台/商店商品 id/到账金币/价格/排序/状态/创建时间/操作;状态显示「已上架」。
  3. 新建商品(正常路径):点「新建商品」→ 编码 uitest_coin_500、名称随意、平台选 Google Play、商店商品 id UITEST_COIN_500、金币 500、赠送 50、价格 9900 → 保存。 预期:toast「商品已创建(默认下架,请确认后再上架)」,列表新增一行且状态「已下架」(id 是雪花 id,注意这是本轮修过的点)。
  4. 异常路径:再新建一个同编码商品 → 预期 toast「商品编码已存在:uitest_coin_500」;把类型改成「会员(第二阶段)」、天数 30 → 保存成功(允许建档),再点它的状态想上架 → 预期 toast「会员商品在订阅功能上线前不能启用(可先建档,上线后再上下架)」,状态不变。
  5. C 端对齐(命令):把第 3 步的商品点上架,然后
    psql -c "select id, code, platform, store_product_id, status from product where store_product_id='UITEST_COIN_500';"
    
    预期:查到 1 行、status=1 —— 这正是 /app/orders/verify 找商品的同一条件。 (POST /app/orders/verify 本机默认 STORE_VERIFIER=disabled,会先回 200003 商店校验尚未配置,这是 B1-1 的既定行为,不是商品没配上。)
  6. 公告页:进 #/operation/notices → 「新建公告」填标题/正文(默认「全部用户/系统」、不填时间=立即发布)→ 保存。 预期:toast「公告已发布」,行内状态「已发布」、失效时间显示「-」。
    curl -s "http://127.0.0.1:8081/app/notices?pageIndex=1&pageSize=5"    # 预期 total 1,能看到刚发的标题
    
  7. 下架(异常路径的对照组):点该行状态「已发布」→ 预期 toast「公告已下架」、行内变「已下架」;再跑上面的 curl → 预期 total 0。
  8. 越权:用 content.editor01 / admin123(内容运营,无 product:*/notice:*)登录后直接敲 #/commerce/products。 预期:页面 403/无权限(后端 {"code":403,"message":"无权限"});不带 token 请求 curl -s "http://127.0.0.1:8080/admin/products/page" → {"code":401,...}。
  9. 清理:把第 3/4 步的商品、第 6 步的公告删掉;核对 select count(*) from product; = 3、select count(*) from ops_notice; = 0。 (本轮自测数据我已清干净:product 3 行种子、ops_notice 0 行。)

门禁/流程说明:本轮所有子步骤都在同一批内完成,未抢做下一批;mvn test 与 package 都是先停服务再打包、打包后重新起服务、对着活着的服务做的 curl 自测(不是对停掉的服务打)。 服务当前状态:8080/8081 我这边是已起的(自测完成时仍在监听);若你这边连不上就是被环境回收了,按上面「准备」两条命令起回来即可。

禅道同步(红线第 6 条,如实说明):我把 水果短剧APP(项目 2)的 4 个执行下的任务都拉了一遍, 任务只有 S0/S1/S0-16、A1–A6、B1/B2、C1–C3 —— 没有 I1(后台管理端 商品配置 + 系统公告)这一条, 所以本轮没有可同步的任务号,我没有动任何既有任务的状态(避免误改 B1/C1/A* 这些别的批次)。 下一步请给 I1 建任务并告知任务号,我补一次状态同步(codex01 的 token 已可用)。


回执(Codex,2026-10-16):J1 完成,停在干净交接点

提交(三个仓库各自 develop,main 未动)

  • FruitServer f35db2c(develop,已推):全局 Long→String + 契约 §0 + 测试
  • ReelShortWeb 3ec9ea6(develop,已推):4 处 id 数字语义适配
  • ReelShort 94ff9f1(新建 develop,已推):客户端 Long 计数兼容解析 留痕:docs/plans/PROGRESS.md(本轮追加 J1 段,含原始输出);计划 docs/plans/2026-10-16-J1.md。

门禁数字

门禁 结果
mvn test BUILD SUCCESS — commerce 33 / admin-api 21 / app-api 49 / worker 12 = 115 条全过(存量 110 未跑红,新增 5)
mvn … package -DskipTests BUILD SUCCESS
pnpm ts:check / eslint / pnpm build:pro 0 错误 / 0 error / Build successful
dart analyze lib test / flutter test No issues found! / All tests passed!(+167)

每个仓库改了什么

FruitServer(后端)

  • 新增 fruitshorts-framework/.../web/JacksonLongToStringConfig.java(Long/long → 字符串; PageResult.total 用 mixin 保持数字;PlainLongSerializer 供 mixin 使用)
  • docs/api-phase1.md:§0 新增「ID 字段一律以字符串下发」总口径(含示例、边界、total 例外), §2.11/§2.12 改为引用 §0
  • I1 的特例回退:ProductRespVO / NoticeRespVO 去掉 @JsonSerialize, ProductAdminController / NoticeAdminController 的 create 回到 Result<Long>
  • 测试:新增 admin-api/.../web/LongToStringSerializationTest.java(5 条); ControllerContractSupport(admin + app 两处)挂上模块,避免契约测试假绿; C 端 4 条 id 断言改成 value("…") + isString()

ReelShortWeb(4 个文件)

  • src/utils/creatorImpersonation.ts — 代看创作者 id 全程字符串(原来 Number(raw),19 位会被四舍五入)
  • src/views/contentManagement/EpisodeManagementView.vue — Number(route.query.drama) → 字符串比较
  • src/views/creator-center/composables/useDramaEdit.js — Number(route.params.id) → 字符串比较
  • src/views/permission/composables/useMenuManagement.js — 顶级菜单哨兵 0 → '0'(与接口的 "0" 对齐)

ReelShort(Flutter)逐文件清单(只有这些,没夹带别的)

  • lib/core/data/models/json_numbers.dart(新增)— jsonToInt:接受数字或数字字符串
  • lib/core/data/models/auth_session.dart — expiresIn(后端 long)改用 jsonToInt
  • lib/core/data/models/drama.dart — viewCount(后端 Long)两处改用 jsonToInt
  • test/core/json_id_parsing_test.dart(新增)— 8 条回归用例

三个需要你确认的口径

  1. PageResult.total 保持数字(唯一例外) —— 它是条数不是 id,前端分页组件(el-pagination / Flutter 分页) 都要数字。实测 total = 2 int。若你要求"Long 一律字符串",删掉 JacksonLongToStringConfig 里那一行 mixin 即可。
  2. 反向(请求方向)未做限制:请求体 "id":"…"、路径变量、查询参数都接受字符串并转 Long(有测试)。 Integer/int(pageIndex/pageSize/Result.code/totalEpisodes/durationSec…)保持数字 —— 实测已确认。
  3. 客户端适配范围比"只改 id 解析"宽了一点点,原因是物理上必须:客户端 id 本来就是 (json['id'] ?? '').toString(),不会崩;真正会崩的是被全局规则扫到的 Long 计数, 所以改了 expiresIn 与 viewCount 共 3 处(json_numbers.dart 是新增工具)。 其余 8 处 as num? 对应的 Java 字段都是 Integer/int,按最小改动原则没动。 ⚠️ ReelShort 仓库原来只有 main(无 develop),我按条目要求新建了 develop 并在上面提交。 客户端由另一位同事推进,若他继续在 main 上走,需要把这笔 94ff9f1 带过去。

用户可照做的验证步骤(每步都有预期结果)

准备:cd FruitServer && mvn -pl fruitshorts-app-api,fruitshorts-admin-api -am package -DskipTests,起两个 jar; curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8080/v3/api-docs → 200。

  1. 复现 PM 那条(最直观):注册一个 C 端账号
    curl -s -X POST http://127.0.0.1:8081/app/auth/register -H 'Content-Type: application/json' \
      -d '{"targetType":"EMAIL","target":"你的邮箱@example.com","password":"Passw0rd!23","nickname":"手测","deviceId":"manual-1"}'
    
    预期:"expiresIn": "7200"(字符串)。
  2. 用响应里的 user id 调后台:curl -s "http://127.0.0.1:8080/admin/app-users/page?pageSize=50" -H "Authorization: Bearer <admin token>", 拿新用户的 id(预期是 "2111…" 这种带引号的字符串、total 是不带引号的数字),再 curl -s "http://127.0.0.1:8080/admin/app-users/<id>" -H "Authorization: Bearer <token>" → 预期 code=0、data.id 与列表一致 (修复前这里返回 200002 数据不存在)。
  3. JS 逐位一致:把第 2 步的响应存成 r.json,然后
    node -e 'const d=JSON.parse(require("fs").readFileSync("r.json","utf8"));console.log(String(d.data.id))'
    
    预期与 psql -c "select id::text from app_user where uid='<uid>'" 逐位相同。 对照:node -e 'console.log(String(JSON.parse("2111015748376858626")))' → 2111015748376858620 附近(会被改写),说明精度问题真实存在。
  4. 后台界面(:3005):admin/admin123 登录 → 商品配置新建一个商品 → 点它那一行的状态/编辑, 预期正常(toast 成功,不再"商品不存在");菜单管理编辑一个顶级菜单,预期父级下拉显示「顶级菜单」而不是 0。
  5. 短剧管理 → 剧集管理带参跳转:从短剧管理点某剧的「剧集」,URL 带 ?drama=<id>, 预期剧集页自动选中该剧(改成字符串比较前这里会选不中)。
  6. 客户端:cd ReelShort && flutter test(预期 All tests passed);有设备时 flutter run 打开首页看播放量/详情 不报解析异常(后端 viewCount 现在下发字符串)。
  7. 异常路径:请求体里 id 传数字(老客户端行为)也应被接受:PUT /admin/products/<id>/status?status=1 用数字 id 路径同样 200。

禅道:J1 在禅道里同样没有对应任务(项目 2 下仍是 S0/S1/S0-16、A1–A6、B1/B2、C1–C3),未动任何既有任务状态。 服务现状:8080/8081 是我刚重建起回的新 jar(自测时在跑);:3005 的 dev server 也在。若被环境回收,按上面「准备」起回来即可。