补齐游戏分发发布恢复链路并修掉双击提交建两份游戏
Project CI / AI game creator shell Rust crates (push) Successful in 1m37s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m7s
Project CI / Backend tests (push) Successful in 4m12s
Project CI / Frontend tests (push) Successful in 2m9s
Project CI / Native shell tests (push) Successful in 6m20s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 8m28s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 9m31s
Project CI / AI game creator shell web tests (push) Successful in 1m40s
Project CI / Repository checks (push) Successful in 2m25s

- 新增 scripts/check-game-distribution-web-publish-recovery-e2e.mjs:上传中断留草稿、关窗/登录失效后重登续传沿用原 versionId、双击只落一份、换账号不显示他人草稿,共 10 项 PASS
- GamePublishPage 加同步 submitInFlightRef 在途守卫与按次稳定的 Idempotency-Key(失败重试复用、成功后清空),双击不再创建第二份游戏
- 游戏分发里程碑阶段 C 第 5 条改为已勾选
- pitfalls 记录「点一次会建资源的入口需要同步守卫或稳定幂等键」
This commit is contained in:
kdletters
2026-09-28 20:37:48 +08:00
parent bbbd91db88
commit bbe416ca60
5 changed files with 401 additions and 7 deletions
@@ -99,7 +99,7 @@
- [ ] AGC 发布面板隐藏发行包技术摘要;打开时基于有界、脱敏的项目上下文免费生成一句话简介与白名单分类,失败保留本地兜底且不阻断发布;作者始终可以直接编辑生成结果。
- [ ] AGC 发布封面支持基于项目上下文生成,复用现役图片生成与泥点扣费链路;生成结果登记为当前账号平台素材后自动作为 `coverAssetId`,不二次上传。
- [x] 网页可选 ZIP、提交封面和必需资料,进入相同上传/校验/审核流程;任一客户端可以查看同账号游戏状态,更新沿用相同 `gameId`。
- [ ] 上传中断、双击、登录失效、窗口关闭后恢复原操作;换账号不能恢复前账号私有状态;待审不能显示为已发布。
- [x] 上传中断、双击、登录失效、窗口关闭后恢复原操作;换账号不能恢复前账号私有状态;待审不能显示为已发布。
- [x] 目录支持真实数据、关键词/分类/设备筛选、空/错/加载态;详情提供明确主动作,搜索与返回恢复上下文。
- [x] 游客从目录/分享链接进入详情再启动真实已发布游戏;开始操作后才加载 iframe,加载失败可重试,退出不会自动重启游戏。
- [ ] 桌面与移动导航、详情、上传/发布面板、旋转提示、全屏、安全区和焦点可达符合主规范;不适配移动端的游戏有明确门槛。
@@ -208,5 +208,6 @@
- 已勾选(第 4 条:网页可选 ZIP、提交封面与必需资料,进入同一上传/校验/审核流程;同账号状态可见;更新沿用同一 `gameId`)——本轮补齐
- 新增 `scripts/check-game-distribution-web-publish-e2e.mjs`(`npm run check:game-distribution-web-publish-e2e`,真实 dev 网页栈 + Chromium):网页里用密码登录/注册 → `/games/publish` 填资料、选封面、选发行包 ZIP → 提交后页面给出「已提交审核」;服务端回读同一账号 `my-games` 是 `latestVersion.status=pending_review` + 游戏 `unpublished`,广场里看不到该游戏;网页「我的游戏」显示「审核中」。管理员审核通过后广场出现该游戏且 `entryUrl=/games/<gameId>/`,网页「我的游戏」同步显示「已发布」;再用 `/games/publish?game=<gameId>` 提交新版本,`versions.length=2`、`latestVersion=pending_review`、游戏仍是 `published`(旧版在线)——更新沿用同一 `gameId`。**12 项 PASS**。
- **本轮顺带修掉一处真实缺陷**:`loadFrontendRuntimeConfig()` 之前用 `skipAuth: true` 调 `/api/runtime/frontend-config`,而发布灰度是**按作者**判定的(匿名恒为 false)。结果是:只要运营显式配置过 `game-distribution:publish`,登录作者在网页端也永远看不到发布入口(页面一直显示「发布功能正在灰度中」)。改为带登录态请求后,登录后壳层重读配置即可拿到发布入口,无需刷新页面;`frontendRuntimeConfigService.test.ts` 同步更新。
- 仍未勾选(第 5 条其余部分)
- 「待审不能显示为已发布」已随本轮取证(更新待审时游戏仍显示已发布、新版单独处于审核中);**仍缺**:上传中断、双击提交、登录失效、窗口关闭后恢复原操作,以及换账号不能恢复前账号私有状态的用例。
- 已勾选(第 5 条:上传中断、双击、登录失效、窗口关闭后恢复原操作;换账号不能恢复前账号私有状态;待审不能显示为已发布)——本轮补齐
- 新增 `scripts/check-game-distribution-web-publish-recovery-e2e.mjs`(`npm run check:game-distribution-web-publish-recovery-e2e`,真实网页栈 + Chromium,**10 项 PASS**):拦截一次发行包上传让页面失败 → 本地留下恢复草稿且服务端只有一份游戏/一个版本;清掉 access token 与 refresh cookie 后发布页要求重新登录,重登同一账号后恢复提示仍在;关窗后用新页面打开 `/games/publish` 能看到「上次发布未完成(版本 v1)/ 继续上传」,续传沿用**原 versionId** 进入待审并清掉草稿;把前一账号的草稿留在同一浏览器里换成另一账号打开时不再显示该草稿。待审不冒充已发布由发布链路脚本的更新场景覆盖(游戏仍 `published`,新版本单独 `pending_review`)。
- **本轮顺带修掉一处真实缺陷**:连点两次「提交审核」会创建两份游戏(`isSubmitting` 是 React 状态,双击落在同一次渲染窗口里还没生效,再叠上 `prepareGamePackage` 的压缩耗时)。修复:加同步的 `submitInFlightRef` 在途守卫,并让同一次发布(含失败后重试)复用同一组 Idempotency-Key(`publishKeyRef`),服务端按幂等重放;E2E 里「双击只落一份游戏与一个版本(games=1 versions=1)」就是这条的回归证据,`GamePublishPage.test.tsx` 15 passed。 - 「待审不能显示为已发布」已随本轮取证(更新待审时游戏仍显示已发布、新版单独处于审核中);**仍缺**:上传中断、双击提交、登录失效、窗口关闭后恢复原操作,以及换账号不能恢复前账号私有状态的用例。
@@ -6073,4 +6073,9 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
- **现象**:运营把 `game-distribution:publish` 显式配置成 `enabled=true, rolloutPercent=100` 后,登录作者在网页 `/games/publish` 仍看到「发布功能正在灰度中 / 当前账号还没有发布入口」,但用同一账号的 access token 直接请求 `/api/runtime/frontend-config` 得到 `gameDistributionPublishEnabled=true`。
- **根因**:`src/services/frontendRuntimeConfigService.ts` 的 `loadFrontendRuntimeConfig()` 传了 `skipAuth: true`,请求不带登录态;而发布灰度是**按作者**判定的(匿名恒为 false),所以壳层拿到的永远是匿名结果。
- **处理**:去掉 `skipAuth`(保留 `skipRefresh` 等选项),登录后壳层按 `gamePublishGateUserId` 重读配置即可拿到发布入口;`frontendRuntimeConfigService.test.ts` 与 `check:game-distribution-web-publish-e2e` 都覆盖回归。教训:任何「按用户/作者灰度」的配置接口都不能走 `skipAuth`,匿名结果与登录结果语义不同。
- **处理**:去掉 `skipAuth`(保留 `skipRefresh` 等选项),登录后壳层按 `gamePublishGateUserId` 重读配置即可拿到发布入口;`frontendRuntimeConfigService.test.ts` 与 `check:game-distribution-web-publish-e2e` 都覆盖回归。教训:任何「按用户/作者灰度」的配置接口都不能走 `skipAuth`,匿名结果与登录结果语义不同。
## 2026-09-28 发布按钮连点两次会建两份游戏:React isSubmitting 挡不住同一次渲染窗口里的双击
- **现象**:网页发布页飞快双击「提交审核」,服务端出现两份游戏(各带一个版本)。`isSubmitting` 是 React 状态,双击发生在同一次渲染窗口里时还没生效,再叠上 `prepareGamePackage` 的 ZIP 压缩耗时,两次点击各走一遍创建工作。
- **处理**:`GamePublishPage` 加同步 `submitInFlightRef` 在途守卫(进入提交前同步置位、`finally` 复位),并让同一次发布(含失败后重试)复用同一组 Idempotency-Key(`publishKeyRef`,成功后清空),服务端按幂等重放;`check:game-distribution-web-publish-recovery-e2e` 断言「双击只落一份游戏与一个版本」,`GamePublishPage.test.tsx` 15 passed。
- **教训**:任何「点一次会建资源」的入口都需要同步守卫或稳定幂等键,只靠 React 状态禁用按钮不够。