16 KiB
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 共守)
- 单一写者:同一时刻只有一个 agent 在改代码/写 PROGRESS/跑门禁。
- 留痕:谁干活谁写
docs/plans/PROGRESS.md(命令 + 原始输出 + 解读,逐动作追加)。Trae 接手时用「【Trae 接手】」标记。- git 不冲突:提交前先
git status/git log;只git add自己的文件,禁止git add -A/.;不夹带对方改动。- 文件不覆盖:写 INBOX/PROGRESS 前先读最新,追加不覆盖。
- 求助通道:遇到阻塞/不确定,在本文件顶部写「【待 Trae 协助】+ 问题描述」。
- 禅道同步:任务「开始/完成」时同步更新禅道任务状态。
- 验收归用户:每批做完停在干净交接点,由用户亲自验证;回执必须含「用户可照做的验证步骤 + 每步预期结果」。
- 门禁(不达标不提交):后端 =
mvn test全过(核心接口须有契约测试);Web =pnpm build:pro通过 + eslint 零错误;Flutter =dart analyze lib test零告警 +flutter test全过。- 分支:在
develop提交并git push origin develop;绝不动main(dev→main 走 PR,由用户操作)。
[已处理·待用户验收] 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)
- 契约先行(依据
12-上线检查清单§一):先在docs/api-phase1.md写清两个模块的接口、字段、错误码、权限点(放在 §2.6 订单与权益之后,或新增一节),再动代码。 - 商品配置
/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
- 系统公告
/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 端能查到、过期/下架后查不到 - 权限点 + 菜单同上
- 菜单与权限点落库:写进
sql/data-phase1.sql(幂等),注意沿用既有保留段、不要与现有 id 冲突(现有菜单到 id 30)。建议:商品配置挂订单与权益(id 22) 下;系统公告可挂新的运营分组,具体由你按现有约定定。超管必须可见且可授权。 - Swagger 中文注解按 S1 规范补齐(
@Tag/@Operation)。 - 门禁:
mvn test存量 91 条不得跑红 + 新增契约测试,至少覆盖:store_product_id同平台唯一、code唯一、type校验、公告发布后 C 端可见 / 过期或下架后不可见、越权(无权限点被拒)。
三、后台页面(ReelShortWeb)
挂载机制(PM 已勘察):路由是静态注册在
src/router/index.ts(component: () => import('@/views/...')),而菜单树来自 DBsys_menu,其中component字段必须与视图路径一致(例:views/contentManagement/DramaManagementView)。两边都要加。
- 新增两个页面(放在与现有页面同级的目录,命名沿用
XxxManagementView.vue风格):- 商品配置:列表(分页/筛选)+ 新建/编辑弹窗 + 上下架 + 删除;表单字段按
product表;金额/金币用数字输入并做前端校验 - 系统公告:列表 + 新建/编辑(标题/正文/类型/目标/发布时间/失效时间)+ 发布/下架 + 删除
- 商品配置:列表(分页/筛选)+ 新建/编辑弹窗 + 上下架 + 删除;表单字段按
- 按现有约定写:API 封装放
src/api/admin/(新增如product.ts/notice.ts);权限码走现有的can(code);表格状态列用接口返回的status/statusText,不要在前端写死中文;大数值用formatCount。 src/router/index.ts静态注册路由(route_name与 DBsys_menu.route_name一致,注意不要与既有 route_name 重名 —— 之前踩过CreatorCenter重名导致白屏/404 的坑)。- 不要改动已对接的既有页面(避免与其他人正在做的改动冲突)。
- 门禁:
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 条)
- 先写计划文件
docs/plans/2026-10-16-I1.md(后端 + 前端两部分、步骤、风险、待确认)。 - 做,跑门禁。
- 停在干净交接点写回执:逐项列出改了什么;给你和用户可照做的验证步骤 + 每步预期结果(含「后台登录 → 菜单里能看到这两个新页面 → 建一个商品 → 用该商品在 C 端 verify 能对上」这样的端到端链路)。
- 不要连着抢做下一批。
回执(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;截图见下面步骤 |
交付内容
- 后端:
/admin/products6 个接口(page/{id}/POST/PUT/{id}/status/DELETE)、/admin/notices6 个接口;字段严格按product/ops_notice表现有结构,未改表。 - 校验:
code全局唯一、storeProductId同 platform 唯一、COIN_PACK需coinAmount>0、VIP需vipDays>0、新建默认下架。 - 公告可见性与 C 端共用一份实现:
OpsNoticeService.pageForApp(...),后台与/app/notices不再各写一套条件。 - 权限点
product:list/product:update/notice:list/notice:update+ 菜单 31(商品配置,挂 22「订单与权益」)/ 32(运营管理)/ 33(系统公告),超管已授权,幂等。 - 前端:
src/api/admin/product.ts、src/api/admin/notice.ts、ProductManagementView.vue、NoticeManagementView.vue;状态列只用接口statusText,不写死中文。
两个自己拍的口径(请复核)
- VIP 商品:允许建档、不允许启用 ——
status=1时回400 会员商品在订阅功能上线前不能启用(可先建档,上线后再上下架)。理由:上线日可以直接上下架,不必临时补商品数据;已写进契约 §2.11。 - ⚠️ 雪花 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 的地方会受影响)——这是平台级改动,请裁定,我没自行铺开。
- 菜单挂载机制与条目描述有出入(如实报出):实测菜单来自 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(可重复执行)。
- 菜单:
pnpm dev→ 打开http://127.0.0.1:3005/#/login→admin / admin123登录。 预期:左侧出现「订单与权益 → 商品配置」与「运营管理 → 系统公告」,其余菜单不变。 - 商品页:进
#/commerce/products。预期:3 行种子(coin_100/550/1200),列 = 商品编码/名称/类型/平台/商店商品 id/到账金币/价格/排序/状态/创建时间/操作;状态显示「已上架」。 - 新建商品(正常路径):点「新建商品」→ 编码
uitest_coin_500、名称随意、平台选 Google Play、商店商品 idUITEST_COIN_500、金币 500、赠送 50、价格 9900 → 保存。 预期:toast「商品已创建(默认下架,请确认后再上架)」,列表新增一行且状态「已下架」(id 是雪花 id,注意这是本轮修过的点)。 - 异常路径:再新建一个同编码商品 → 预期 toast「商品编码已存在:uitest_coin_500」;把类型改成「会员(第二阶段)」、天数 30 → 保存成功(允许建档),再点它的状态想上架 → 预期 toast「会员商品在订阅功能上线前不能启用(可先建档,上线后再上下架)」,状态不变。
- C 端对齐(命令):把第 3 步的商品点上架,然后
预期:查到 1 行、
psql -c "select id, code, platform, store_product_id, status from product where store_product_id='UITEST_COIN_500';"status=1—— 这正是/app/orders/verify找商品的同一条件。 (POST /app/orders/verify本机默认STORE_VERIFIER=disabled,会先回200003 商店校验尚未配置,这是 B1-1 的既定行为,不是商品没配上。) - 公告页:进
#/operation/notices→ 「新建公告」填标题/正文(默认「全部用户/系统」、不填时间=立即发布)→ 保存。 预期:toast「公告已发布」,行内状态「已发布」、失效时间显示「-」。curl -s "http://127.0.0.1:8081/app/notices?pageIndex=1&pageSize=5" # 预期 total 1,能看到刚发的标题 - 下架(异常路径的对照组):点该行状态「已发布」→ 预期 toast「公告已下架」、行内变「已下架」;再跑上面的 curl → 预期
total 0。 - 越权:用
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,...}。 - 清理:把第 3/4 步的商品、第 6 步的公告删掉;核对
select count(*) from product;= 3、select count(*) from ops_notice;= 0。 (本轮自测数据我已清干净:product3 行种子、ops_notice0 行。)
门禁/流程说明:本轮所有子步骤都在同一批内完成,未抢做下一批;mvn test 与 package 都是先停服务再打包、打包后重新起服务、对着活着的服务做的 curl 自测(不是对停掉的服务打)。
服务当前状态:8080/8081 我这边是已起的(自测完成时仍在监听);若你这边连不上就是被环境回收了,按上面「准备」两条命令起回来即可。