diff --git a/docs/README.md b/docs/README.md index 6fdbb5b2e..a3d597434 100644 --- a/docs/README.md +++ b/docs/README.md @@ -24,7 +24,7 @@ - [后台游戏评价管理合同](./【玩法创作】平台入口与玩法链路-2026-05-15.md#后台游戏评价管理合同):查找、分页、隐藏/恢复/删除、必填原因、统计与个人状态联动;已实现并通过本地验证,待用户验收,未部署。 - [后台游戏评价管理里程碑](./project-memory/plans/【里程碑】后台游戏评价管理-2026-10-01.md)与[实施计划](./project-memory/plans/【实施计划】后台游戏评价管理-2026-10-01.md):单里程碑范围、接口/schema 边界及验收要求;本地证据已回写主规范。 - [游戏广场评分展示合同](./【玩法创作】平台入口与玩法链路-2026-05-15.md#游戏广场评分展示合同)、[里程碑](./project-memory/plans/【里程碑】游戏广场评分展示-2026-10-01.md)与[实施计划](./project-memory/plans/【实施计划】游戏广场评分展示-2026-10-01.md):已实现并通过本地定向验证,待用户验收,未部署;公开列表/详情携带真实摘要,卡片显示一位小数均分与人数,复用有效评价统计。 -- [游戏买断制泥点付费与播放鉴权合同](./【玩法创作】平台入口与玩法链路-2026-05-15.md#游戏买断制泥点付费与播放鉴权合同)与[里程碑](./project-memory/plans/【里程碑】游戏买断制泥点付费与播放鉴权-2026-10-05.md):已实现,本机真实栈 E2E 76 PASS,待用户验收;未部署;人工验收脚本 `check:game-distribution-purchase-e2e` 不在 CI 自动门禁内;作者可选买断制泥点付费,购买后永久可玩,后台审核可见价格且审核员不限次试用;网页与 AGC 两个发布入口一致支持定价并共用 `packages/shared` 组件,AGC 入口证据待补。 +- [游戏买断制泥点付费与播放鉴权合同](./【玩法创作】平台入口与玩法链路-2026-05-15.md#游戏买断制泥点付费与播放鉴权合同)与[里程碑](./project-memory/plans/【里程碑】游戏买断制泥点付费与播放鉴权-2026-10-05.md):已实现,本机真实栈 E2E 由人工验收脚本 `check:game-distribution-purchase-e2e` 覆盖(最近一次人工运行 76 PASS / 0 FAIL / 1 WARN),该脚本不在 CI 自动门禁内;待用户验收,未部署;作者可选买断制泥点付费,购买后永久可玩,后台审核可见价格且审核员不限次试用;网页与 AGC 两个发布入口一致支持定价并共用 `packages/shared` 组件 `PlatformGamePricingField`,AGC 定价的前端用例与 Rust 预填单测已覆盖,AGC 真实栈发布未覆盖。 - [游戏游玩次数计数](./adr/【ADR】游戏游玩次数计数-2026-10-03.md):点「开始游戏」前端上报一次游玩,api-server 纯内存聚合(5s flush、30min 去重、`IP+game` 限流、关停不强制 flush),批量 procedure 自增现有 `game_distribution_game.play_count`,不 bump `updated_at`。 - [外部 OpenAPI 与 API Key 接入方案](./【后端架构】外部OpenAPI与APIKey接入方案-2026-06-19.md) - [外部 MCP 语义工具说明与参数设计](./technical/【技术方案】外部MCP语义工具说明与参数设计-2026-09-23.md):15 个新增语义工具与全部原工具并存,复用现有 External API;包含工具说明、action、参数、幂等和兼容合同。 diff --git a/docs/project-memory/plans/【里程碑】游戏买断制泥点付费与播放鉴权-2026-10-05.md b/docs/project-memory/plans/【里程碑】游戏买断制泥点付费与播放鉴权-2026-10-05.md index 650f96cf2..946d62a3e 100644 --- a/docs/project-memory/plans/【里程碑】游戏买断制泥点付费与播放鉴权-2026-10-05.md +++ b/docs/project-memory/plans/【里程碑】游戏买断制泥点付费与播放鉴权-2026-10-05.md @@ -13,7 +13,7 @@ ## 范围 -- 作者端定价:网页 `/games/publish` 与 AGC 发布面板在新建与更新模式选择“免费 / 买断制 N 泥点”;两入口共用 `packages/shared` 定价组件,AGC 壳请求透传 `priceMudPoints`;给已上线作品发新版本时预填当前价格与模式,作者不改则价格不变。 +- 作者端定价:网页 `/games/publish` 与 AGC 发布面板在新建与更新模式选择“免费 / 买断制 N 泥点”;两入口共用 `packages/shared` 定价组件 `PlatformGamePricingField`,AGC 壳请求透传 `priceMudPoints`;给已上线作品发新版本时预填价格与模式,作者不改则价格不变。预填价口径:取该作品**最新版本的冻结价**(该版本可能是 `pending_review` / `rejected` 状态的版本,因此预填值可能尚未生效),仅当该冻结价缺失或为 0 时才回落作品行当前价,两条来源都取不到才按免费(0)。AGC 壳侧实现 `resolve_publication_prefill_price`(`apps/ai-game-creator-shell/src-tauri/src/game_distribution_publish.rs:811`);网页侧读版本详情 `detail.version.priceMudPoints ?? detail.game.priceMudPoints ?? 0`(`src/components/game-distribution/GamePublishPage.tsx:164`),后端 `version_detail_payload` 已把版本详情的 `game.priceMudPoints` 一并归一为同一冻结价口径(`server-rs/crates/api-server/src/modules/game_distribution.rs:3797`)。 - 玩家端:公开详情展示价格与购买态、泥点购买、购买后游玩。 - 付费游玩鉴权:未购买不得游玩,直连公开发行路径必须失败。 - 泥点钱包扣费与 `game_purchase` 流水。 @@ -35,7 +35,9 @@ ## 验收标准 - [x] 作者网页发布可选「免费」或「买断制 N 泥点」;负数/超上限/非整数被前后端同时拦截。证据:真实本地栈 E2E 价格 `1000001` → 400 且不落版本,`0` / `1000000` → 200;`cargo test -p module-game-distribution` 29 passed、`cargo test -p api-server game_distribution` 53 passed 覆盖定价校验,网页发布表单定价定向 Vitest 覆盖前端拦截。 -- [ ] AGC 发布面板与网页一致可选「免费 / 买断制 N 泥点」,非法值被前后端同时拦截;给已上线作品发新版本时预填当前价格与模式,作者不改则价格不变。证据:待补(由实现 worker 提供:AGC 发布面板定价定向 Vitest、AGC 壳 `cargo check` 编译、真实本地栈 E2E 发布付费版本);两入口共用 `packages/shared` 组件 `PlatformGamePricingField`。 +- [ ] AGC 发布面板与网页一致可选「免费 / 买断制 N 泥点」,非法值被前后端同时拦截;给已上线作品发新版本时按「最新版本冻结价优先、缺失或为 0 时才回落作品行当前价」预填价格与模式,作者不改则价格不变。 + - 已覆盖:AGC 发布面板定价定向 Vitest `apps/ai-game-creator-shell/tests/gameDistributionPublishPanel.test.tsx`(`:333` 默认免费提交 `priceMudPoints=0`;`:342` 买断制先本地校验:空值与 `1000001` 分别报「买断价必须是整数泥点」「买断价必须是 1 到 1000000 之间的整数泥点」且都不发发布请求,`120` 随版本提交 `priceMudPoints=120`;`:802` 历史响应没有回读价格时按免费预填;`:819` 更新模式预填线上买断价 240、作者不改仍按 240 提交);AGC 壳请求层校验定向 Vitest `apps/ai-game-creator-shell/tests/gameDistributionPublish.test.ts`(`:180` 透传 `priceMudPoints=240`;`:197` `1000001` / `1.5` 在 native command 之前失败关闭);预填口径 Rust 单测 `apps/ai-game-creator-shell/src-tauri/src/game_distribution_publish.rs:1715`(`publication_prefill_price_prefers_latest_frozen_price_and_falls_back`:最新版本冻结价 300 优先于作品行 240、历史版本无冻结价回落 240、免费作品与缺字段按 0);两入口共用 `packages/shared` 组件 `PlatformGamePricingField`。 + - **未覆盖,故本项保持未勾选**:AGC 真实栈「发布付费版本」未手点一次;真实本地栈 E2E 只覆盖网页发布入口的定价链路(`1000001` → 400 且不落版本、`0` / `1000000` → 200)。 - [x] 后台审核详情展示该版本价格;无价格的历史版本按免费展示。证据:`cargo test -p api-server game_distribution` 53 passed(`private_version_payload` / `version_detail_payload` 输出 `priceMudPoints`,历史无价格版本按 0);admin-web 审核价格展示定向 Vitest。 - [x] 审核员不限次数试玩待审付费版本,不校验购买、不扣泥点。证据:真实本地栈 E2E 审核员两次试玩待审付费版本均 200、两次包内字节一致,作者与审核员余额/流水均不变。 - [x] 未购买用户详情可见资料但无可播放入口;直连 `/games//` 返回 404。证据:真实本地栈 E2E 详情 `priceMudPoints=30` / `purchased=false` / `entryUrl=null`,发行网关与平台同源均 404,创建会话 403,未登录 401。 @@ -56,4 +58,12 @@ - 运行时:真实本地栈跑通「发布付费游戏 → 后台审核可见价格并试玩 → 通过 → 未购买访问失败 → 购买 → 游玩 → 重复游玩不扣费」。 - 边界:并发购买、余额不足、下架/封禁后播放会话失效、猜测 URL 直连、免费游戏回归。 -已执行(2026-10-05,人工验收):`npm run check:game-distribution-purchase-e2e` 在本机真实 SpacetimeDB / api-server / OSS 一次跑通 76 PASS / 0 FAIL / 1 WARN,exit 0,24.1s,teardown 无残留。唯一 WARN 为「管理员令牌购买」在当前产品形态不可达(管理员免购买经 play-session 管理员分支实现,购买路由的 403 `GAME_PURCHASE_ADMIN_NOT_ALLOWED` 分支作为纵深防御保留)。该脚本不在 CI 自动门禁内,CI 绿不代表该链路已验证;本里程碑结论最高到“本机真实栈验证通过,待用户验收,未部署”。 +## 验收证据摘要(真实栈 E2E) + +- 验收命令:`npm run check:game-distribution-purchase-e2e`(等价于 `node scripts/check-game-distribution-purchase-e2e.mjs`,脚本入口见 `package.json:92`)。 +- 断言总数:该脚本内共 76 处 `check(...)` 调用;`check(name, ok, detail)`(脚本 `:86`)每条打印一行 `PASS` / `FAIL`,因此 76 即本轮断言总数。 +- 最近一次人工运行结论(2026-10-05,本机真实 SpacetimeDB / api-server / OSS):**76 PASS / 0 FAIL / 1 WARN**(0 FAIL,进程退出码 0)。本节只记可复核的文本结论,不记耗时秒数。 +- 唯一 WARN:「管理员令牌购买」在现役登录链路下不可达 —— 后台管理员令牌在 `/api/*` 用户路由上先被 `require_bearer_auth` 判为无效登录态,走 401 前置,未进入 403 `GAME_PURCHASE_ADMIN_NOT_ALLOWED` 分支(该分支作为纵深防御保留);管理员免购买由 play-session 管理员分支覆盖(脚本 `:1122`-`:1173`)。 +- WARN 语义:脚本的 `warn()`(`:91`)表示环境条件不满足而**跳过**的断言(例如未发现主站 Vite、`spacetime sql` 直查不可用),不是失败。 +- 脚本定位:**人工验收脚本,不在 CI 自动门禁内**(需真实 SpacetimeDB + api-server 与 `E2E_ADMIN_USER` / `E2E_ADMIN_PASSWORD` 环境变量),CI 绿不代表该链路已验证。 +- 交付状态:本里程碑结论最高到「本机真实栈验证通过,待用户验收,未部署」。