Merge remote-tracking branch 'origin/master' into fix/test-concurrent-problem
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 1m44s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Failing after 2m57s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m48s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m15s
Project CI / Backend tests (pull_request) Failing after 18s
Project CI / Repository checks (pull_request) Failing after 22s
Project CI / Frontend tests (pull_request) Successful in 2m31s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m24s
Project CI / Native shell tests (pull_request) Successful in 6m53s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 1m44s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Failing after 2m57s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m48s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m15s
Project CI / Backend tests (pull_request) Failing after 18s
Project CI / Repository checks (pull_request) Failing after 22s
Project CI / Frontend tests (pull_request) Successful in 2m31s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m24s
Project CI / Native shell tests (pull_request) Successful in 6m53s
# Conflicts: # docs/project-memory/shared-memory/pitfalls.md
This commit is contained in:
+5
-1
@@ -19,7 +19,11 @@
|
||||
## 当前产品与平台
|
||||
|
||||
- [当前产品与工程约束](./【项目基线】当前产品与工程约束-2026-05-15.md):现役入口、账号钱包、UI 和后端分层。
|
||||
- [平台入口与玩法链路](./【玩法创作】平台入口与玩法链路-2026-05-15.md):只描述现役平台壳与图片画布编辑器链路。
|
||||
- [平台入口与玩法链路](./【玩法创作】平台入口与玩法链路-2026-05-15.md):平台壳、图片画布、游戏分发与在线游玩合同;网站游戏评分与评价已实现并通过本地验证,待用户验收,未部署。
|
||||
- [网站游戏评分与评价里程碑](./project-memory/plans/【里程碑】网站游戏评分与评价-2026-09-30.md):唯一评价、编辑预填、4000 字符、公共分页与平均分/人数的验收边界与本地证据。
|
||||
- [后台游戏评价管理合同](./【玩法创作】平台入口与玩法链路-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):已实现并通过本地定向验证,待用户验收,未部署;公开列表/详情携带真实摘要,卡片显示一位小数均分与人数,复用有效评价统计。
|
||||
- [外部 OpenAPI 与 API Key 接入方案](./【后端架构】外部OpenAPI与APIKey接入方案-2026-06-19.md)
|
||||
- [外部 MCP 语义工具说明与参数设计](./technical/【技术方案】外部MCP语义工具说明与参数设计-2026-09-23.md):15 个新增语义工具与全部原工具并存,复用现有 External API;包含工具说明、action、参数、幂等和兼容合同。
|
||||
- [External v1 OpenAPI](./openapi/genarrative-external-v1.openapi.json):公开 HTTP 契约唯一机器可读来源。
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
# 【实施计划】后台游戏评价管理
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Milestone | [后台游戏评价管理](./【里程碑】后台游戏评价管理-2026-10-01.md) |
|
||||
| Status | implemented(本地验证通过,待用户验收;未部署) |
|
||||
| Owner | Codex |
|
||||
|
||||
## 修改边界
|
||||
|
||||
- 主规范:[后台游戏评价管理合同](../../【玩法创作】平台入口与玩法链路-2026-05-15.md#后台游戏评价管理合同);不扩大其四类筛选与单条管理范围。
|
||||
- 领域:`server-rs/crates/module-game-distribution/src/reviews.rs` 的管理动作/原因校验与有效评价统计规则。
|
||||
- 数据:`spacetime-module/src/game_distribution.rs` 追加隐藏字段、用户索引、新私有管理记录及读写事务;同步 migration、表目录与生成绑定。
|
||||
- 后端:`spacetime-client` facade/mapper、`shared-contracts`、`api-server/src/modules/game_distribution.rs` 路由与 handler,`admin.rs` 页面权限映射;不得使用新直接数据库访问路径。
|
||||
- 前端:admin-web 的路由/导航/账号权限/DTO/client/评价管理页及游戏管理跳转;网站评价 DTO、个人提示、摘要/列表读取。复用共享列表、表单、弹窗和后台写确认。
|
||||
- 文档:主规范、后端数据契约、决策记录和索引;不改 AGC、游戏发行审核或外部 OpenAPI。
|
||||
|
||||
## 实现顺序
|
||||
|
||||
1. 技术评审后将方案转为 ready;冻结原因校验、四类筛选、DTO及错误码。
|
||||
2. 追加默认 false 字段与索引、新管理记录表;事务实现隐藏/恢复/删除、创建时间目标核对及操作 key 重放,公共所有统计统一过滤,个人写保留隐藏状态。
|
||||
3. 同步迁移/绑定和 facade;新增后台列表、游戏选择、详情与管理 POST,接入独立 game-reviews 权限;普通用户投影不包含原因。
|
||||
4. 接入后台页、游戏入口、分页筛选、原因确认和详情内记录;网站个人状态加精确提示。请求重试复用同 key,不用旧响应覆盖新目标。
|
||||
5. 定向测试及真实运行时验证,包括旧 schema 带评价升级、删除后主键复用和后台/网站联动;回写证据和限制,交付验收。
|
||||
|
||||
## 验证命令与操作
|
||||
|
||||
- `cargo test --manifest-path server-rs/Cargo.toml --locked -p module-game-distribution`
|
||||
- `cargo test --manifest-path server-rs/Cargo.toml --locked -p api-server game_distribution`,另运行 admin 权限映射相关定向测试。
|
||||
- `cargo check --manifest-path server-rs/Cargo.toml --locked -p spacetime-module`
|
||||
- `npm run spacetime:generate`、`npm run check:spacetime-schema`、`npm run check:game-distribution-dto-parity`
|
||||
- Vitest 定向运行后台新评价页、路由/权限/client与网站 `GameReviews` 测试;以仓库 include 配置核对实际执行名单。
|
||||
- `npm run admin-web:typecheck`、`npm run typecheck`,受影响文件 ESLint/Prettier。
|
||||
- `npm run check:doc-index`、`npm run check:encoding`、`git diff --check`
|
||||
- 通过项目 dev 脚本启动隔离数据库及 `npm run dev:api-server` 并检查 `/healthz`;端口按运行状态核实,不使用人工 `spacetime --root-dir`。
|
||||
- 在隔离数据库先发布旧 schema 并写入评价,再兼容升级;核对旧评价全部公开且内容/时间不变。真实 HTTP 验证跨页过滤、隐藏/统计/恢复/删除、重新评价、旧 key 重试和权限。
|
||||
- 桌面与移动视口真实浏览器验证游戏管理跳转、长评论弹窗、原因必填/取消/失败,以及网站回读。缺失证据如实标记未验证。
|
||||
|
||||
## 风险与回滚点
|
||||
|
||||
- 删除是不可恢复操作;复用确认交互,事务校验原创建时间和幂等操作 key,禁止旧请求作用于新评价。
|
||||
- schema 只兼容追加和新增私有表,不删字段/表或清空数据。发布冲突时停止,不通过 delete-data 绕过迁移。
|
||||
- 隐藏后的旧后端会重新暴露评价;产生隐藏记录后只能回滚到仍过滤隐藏状态的兼容实现,保留全部数据和操作记录。
|
||||
- 不引入前端内容版本锁、搜索服务或统计缓存;当前规模采用完整筛选后分页,不在有限首批数据上筛选造成漏查。
|
||||
|
||||
## 当前状态
|
||||
|
||||
已按上述边界完成实现;定向自动化、旧 schema 带 21 条评价无损升级、真实 HTTP 管理链路和桌面/375×812 移动视口验证通过。完整证据及未验证项见主规范“迁移与验收”。保留计划供用户验收;未部署。
|
||||
@@ -0,0 +1,66 @@
|
||||
# 【实施计划】游戏广场评分展示
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Milestone | [游戏广场评分展示](./【里程碑】游戏广场评分展示-2026-10-01.md) |
|
||||
| Status | implemented(本地验证通过,待用户验收;未部署) |
|
||||
| Date | 2026-10-01 |
|
||||
| Owner | Codex |
|
||||
|
||||
## 修改边界
|
||||
|
||||
- 主规范:[游戏广场评分展示合同](../../【玩法创作】平台入口与玩法链路-2026-05-15.md#游戏广场评分展示合同);仅实施本里程碑。
|
||||
- 数据读取:`server-rs/crates/spacetime-module/src/game_distribution.rs` 的公开快照、列表/详情读取及 procedure 返回类型;复用已有评分 helper。现有持久化表、索引、评价写入与 migration 登记不变。
|
||||
- 后端:`spacetime-client/src/active/mapper/game_distribution.rs` 公开 record 和 mapper、`shared-contracts/src/game_distribution.rs`、`api-server/src/modules/game_distribution.rs` 公开响应与 no-store;通过既有 facade,不新增数据库访问通道。
|
||||
- 前端:`packages/shared/src/contracts/gameDistribution.ts`、共享评分文本组件及导出,网站 `GameGalleryPage.tsx`、`GameReviews.tsx` 和对应 CSS/定向测试;API client 只按必要调整类型,不新增逐卡读取。
|
||||
- 契约检查:`scripts/check-game-distribution-dto-parity.mjs` 与生成绑定;运行时复用现有隔离数据,不新增 E2E 脚本或扩大评分/后台管理回归矩阵。
|
||||
- 文档:主规范、本文、里程碑、文档索引及稳定决策。后台 UI、AGC、外部 OpenAPI、发行包链路不在修改范围。
|
||||
|
||||
## 实现顺序
|
||||
|
||||
1. 扩展 `GameDistributionPublicGameSnapshot` 返回类型,追加 `average_score:Option<f64>` 与 `rating_count:u64`;复用 `game_distribution_user_rating_summary` 及领域 `visible_review_summary` 的隐藏过滤、一位小数和空态。列表先核对公开可见性与有效版本、按原规则排序并限量,再只为返回项构建含摘要的快照;不能让无效版本占用限量名额。详情共用该公开快照。
|
||||
2. 同步生成绑定与公开 record/mapper,将摘要映射为既有 `GameDistributionRatingSummaryRecord`。浮点数进入公开快照/record 后,移除其及引用结果类型中不适用的 `Eq`,保留 `PartialEq`;record 嵌入摘要时按现有序列化用途补齐必要 derives。
|
||||
3. Rust/TS 共用游戏 DTO 追加可选 `ratingSummary`,Rust 允许旧字段缺失;公开 `public_game_payload` 必须输出摘要对象,复用既有摘要转换。同步列表/详情 no-store、DTO parity 的公开构建器 `mustEmit` 和必要嵌套约束;作者 `game_payload` 可省略摘要,不造零值。
|
||||
4. 将纯评分文本展示抽到 `packages/shared`,只接收摘要数据,复用一位小数及人数表现。现有 `GameRatingSummary` 保留详情评价的加载/错误/重试职责,已加载文本使用共享组件;广场卡片使用列表摘要,无额外评价请求,缺字段显示“评分暂不可用”。卡片简介下增摘要行,采用现有视觉变量并允许移动换行。
|
||||
5. 在现有广场页面测试中补有评分、零评价、缺摘要三种展示断言;复用现有公开响应/DTO 测试核对新增字段。有效评价统计、管理动作、筛选、滚动恢复和迟到响应沿用已有测试,不另加重复用例;纯文本共享组件由页面测试覆盖即可。
|
||||
6. 执行定向工程检查,以现有隔离数据做一次公开 API smoke 和桌面/移动页面检查,按主规范回写证据和限制,交付用户验收;验收通过后融合持久结论并清理临时计划。
|
||||
|
||||
## 必要测试范围
|
||||
|
||||
- 前端:在已有页面测试中覆盖正常评分、一位小数/人数、无人评分与缺字段提示;运行受影响的广场及详情评价测试。
|
||||
- 后端:复用已有响应/契约测试验证摘要字段,沿用有效评价统计测试确认隐藏过滤。相同映射不在 mapper、handler、DTO 每层重复添加一套测试。
|
||||
- 联调:复用现有两款游戏及少量评价,一次核对列表/详情/评价接口摘要一致、隐藏记录不计入且游戏间统计不串联;浏览器检查布局与详情返回后的更新。
|
||||
- 不新增测试框架、专用 E2E 脚本、大批量账号/分页 fixture、故障注入或完整管理动作矩阵。仅在实现确实改动已有行为且现有测试未覆盖时补相应回归。
|
||||
|
||||
## 验证命令与操作
|
||||
|
||||
- 运行 `module-game-distribution` 已有有效评价统计定向测试;领域逻辑未改时不新增同类测试。
|
||||
- `cargo test --manifest-path server-rs/Cargo.toml --locked -p shared-contracts game_distribution`
|
||||
- `cargo test --manifest-path server-rs/Cargo.toml --locked -p api-server game_distribution`
|
||||
- `cargo check --manifest-path server-rs/Cargo.toml --locked -p spacetime-module -p spacetime-client`
|
||||
- `npm run spacetime:generate`、`npm run check:generated-bindings`、`npm run check:spacetime-schema`、`npm run check:game-distribution-dto-parity`
|
||||
- `npm run test -- src/components/game-distribution/GameDistributionPages.test.tsx src/components/game-distribution/GameReviews.test.tsx`;只有 API client 实现发生变化时追加其已有测试,不单独新增纯文本共享组件测试。
|
||||
- `npm run typecheck`,对受影响代码运行仓库现有格式/静态检查。
|
||||
- `npm run check:doc-index`、`npm run check:encoding`、`git diff --check`。
|
||||
- 按现有 dev 脚本启动显式隔离数据库和 `npm run dev:api-server`,读取 `.app/dev-stack.json` 核对目标/端口,检查 `/healthz`;复用现有隔离数据做一次列表/详情/评价摘要对照,不为本增量重建整套 ratings/moderation E2E 流程。
|
||||
- 真实浏览器桌面与 375px 移动视口检查评分行、换行及详情改分后返回;确认摘要随列表返回。筛选/滚动恢复、错误/重试及迟到响应沿用已有定向测试,不重复建立浏览器矩阵。
|
||||
|
||||
## 风险与回滚点
|
||||
|
||||
- 公开 procedure 返回类型属于配套运行时合同;module、生成绑定和后端先在隔离环境一起验证,再按现有运维流程发布,网站随后更新。无需持久化迁移或数据回填,不能用删除数据处理类型不匹配。
|
||||
- 评分聚合仅作用于返回项,并使用既有按游戏索引;保留有效版本过滤与排序,避免额外扫描全部匹配游戏的评价。当前规模不增加缓存、统计表或预计算任务。
|
||||
- 缺字段只表示不可用,不能默认成 `null/0`;详情评价状态继续由现有读取/保存驱动,避免公开投影摘要覆盖提交后的最新值。
|
||||
- 回滚网站展示可保留新增公开字段;回滚 module/后端必须恢复互相匹配的绑定,并继续过滤隐藏评价。不得退回不识别管理隐藏状态的基础评价旧实现。
|
||||
|
||||
## 执行结果
|
||||
|
||||
- 已完成上述代码与绑定接入;公开列表先过滤有效版本、排序并限量,只为返回游戏聚合评分。持久化表与评价写入规则未变。
|
||||
- 前端两文件 36 项、API game_distribution 34 项、shared-contracts 4 项、领域 summary 2 项通过;module/client 编译、typecheck、受影响代码静态/格式检查通过。
|
||||
- `spacetime:generate` 使用固定 2.8.3 完成;保留新增快照字段,清除无关生成格式 diff。生成绑定、schema(87 表)、DTO parity(31 类型/4 构建器)、编码、文档索引和 diff 检查通过。
|
||||
- `dev:api-server` 启动后 `/healthz` 通过;本机显式隔离库没有既有评分 fixture,复用现有 helper 创建两个账号、两款游戏、两条评价。游客/登录公开列表摘要一致,列表/详情/评价摘要 `8/2`,隐藏一条后 `7/1`,另一游戏始终 `null/0`;列表/详情 no-store 通过。临时联调代码与数据仅留在 ignored 本地产物,未新增专用仓库 E2E 脚本。
|
||||
- 1280px/375px 浏览器评分行无横向溢出,卡片可进入详情;详情期间通过 API 将可见评分从 7 改为 6,返回广场显示 `6.0/10 · 1 人评分`。
|
||||
- 前端实现与后端只读审查由两个 `gpt-6.1-sol` subagent 协助,后端审查未发现可执行问题。
|
||||
|
||||
## 未验证与待验收
|
||||
|
||||
用户最终验收与生产部署待完成,保留本计划。未跑完整仓库测试、完整评价/后台管理 E2E、真实手机、长标题/极大人数浏览器矩阵、浏览器登录改分和实际发行包游玩。返回刷新通过 API 改分验证,登录公开读取通过 HTTP smoke 验证;未增加复杂测试矩阵。
|
||||
@@ -0,0 +1,43 @@
|
||||
# 【实施计划】网站游戏评分与评价
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Milestone | [网站游戏评分与评价](./【里程碑】网站游戏评分与评价-2026-09-30.md) |
|
||||
| Status | implemented(本地验证通过,待用户验收) |
|
||||
| Owner | Codex |
|
||||
|
||||
## 修改边界
|
||||
|
||||
- 按用户确认的最新主规范实施网站详情评价、评分统计、公共分页与个人编辑。
|
||||
- 后端限定现有 game-distribution 领域、私有评价表、spacetime-client、共享 DTO 与 API 路由;新增表不破坏现有表。
|
||||
- 前端限定网站详情、评价组件与 API client,复用现有登录、按钮和状态组件。
|
||||
- 不扩展 AGC、广场、外部 API,不新增删除、审核、回复、点赞、排行榜或统计缓存。
|
||||
|
||||
## 实现顺序
|
||||
|
||||
1. 补齐评分、码点长度、归一化、排序统计领域规则与 Rust/TS DTO。
|
||||
2. 新增 game_distribution_review 私有表,以游戏/用户组合为唯一主键,按游戏建立索引;同步 migration、表目录、生成绑定。在事务内检查公开可玩状态并保存,读取分页与统计使用一致快照。
|
||||
3. facade 和 HTTP 接入公共分页、个人读取、唯一 PUT,身份由认证主体取得,响应 no-store。
|
||||
4. 网站实现个人表单/记录/编辑、公共分页与平均分人数,输入冻结及读取请求序号隔离;补定向交互测试。
|
||||
5. 定向 Rust/Vitest、typecheck、DTO/schema/编码/索引检查及真实数据库/API/桌面移动浏览器 smoke;记录逐条证据和限制。
|
||||
|
||||
## 验证命令
|
||||
|
||||
- `cargo test --manifest-path server-rs/Cargo.toml -p module-game-distribution`
|
||||
- `cargo test --manifest-path server-rs/Cargo.toml -p api-server game_distribution`
|
||||
- `cargo test --manifest-path server-rs/Cargo.toml -p spacetime-module game_distribution`
|
||||
- `npm run spacetime:generate`、`npm run check:spacetime-schema`、`npm run check:game-distribution-dto-parity`
|
||||
- `npx vitest run src/components/game-distribution src/services/gameDistributionClient.test.ts`
|
||||
- `npm run typecheck`、`npm run check:encoding`、`npm run check:doc-index`、`git diff --check`
|
||||
- `npm run dev:api-server` 检查 `/healthz`,真实本地 SpacetimeDB/API 验证两账号、唯一并发与至少 21 条评价;浏览器验证桌面、移动、编辑取消与键盘评分。
|
||||
|
||||
## 风险与回滚点
|
||||
|
||||
- 当前是新增私有表,无旧数据回填。回滚页面/API 时保留评价数据,不删除数据库记录。
|
||||
- 中文/emoji 统一按换行归一化后的 Unicode 码点计数,不依赖 textarea 原生 UTF-16 maxLength。
|
||||
- 保存成功与账号/游戏切换使旧读取失效,旧成功、失败、finally 均不得覆盖当前状态。
|
||||
- 若环境不支持真实 smoke,明确记录已通过的自动化证据和未验证项,不标记已上线或已验收。
|
||||
|
||||
## 验证结果
|
||||
|
||||
本计划步骤已实施;各条行为和验证证据已回写[主规范](../../【玩法创作】平台入口与玩法链路-2026-05-15.md#网站游戏评分与评价合同)。真实数据库/API 49 项及桌面/移动视口浏览器 smoke 通过,未部署。用户验收后与里程碑一起清理。
|
||||
@@ -0,0 +1,53 @@
|
||||
# 【里程碑】后台游戏评价管理
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Version | 1.0 |
|
||||
| Status | implemented(本地验证通过,待用户验收;未部署) |
|
||||
| Date | 2026-10-01 |
|
||||
| Parent Spec | [后台游戏评价管理合同](../../【玩法创作】平台入口与玩法链路-2026-05-15.md#后台游戏评价管理合同) Version 1.0 |
|
||||
|
||||
## 目标
|
||||
|
||||
交付后台查找评价、隐藏/恢复/删除、原因与操作记录,并同步网站公共分页/统计和个人隐藏状态,保持每账号每游戏唯一评价。
|
||||
|
||||
## 范围
|
||||
|
||||
- 独立“游戏评价”后台页及页面权限;从游戏管理带游戏 ID 进入,完整正文和评分只读。
|
||||
- 游戏名称搜索选择/ID定位、评价用户 ID、正文关键词和公开/隐藏状态组合筛选;完整数据过滤后分页。
|
||||
- 隐藏/删除必填原因,恢复无须原因;管理操作原子保存,失败与取消不产生部分写入。
|
||||
- 隐藏排除整条评价及评分,个人固定提示“已被管理员隐藏”;用户编辑保持隐藏,恢复重新参与统计。
|
||||
- 删除不可恢复,允许用户重新评价;旧管理目标和重复请求不能误处理重新创建的记录。
|
||||
- 存量无损升级、管理员权限、私有原因隔离、真实数据库/API和桌面/移动后台及网站联动。
|
||||
|
||||
## 不在范围内
|
||||
|
||||
- 管理员修改正文/分数、删除恢复、再次评价封禁、批量、导出、举报、自动审核、申诉、通知/轮询/推送。
|
||||
- 评分/时间范围等额外筛选、独立操作日志页、AGC 评价、外部 API、统计缓存或清理任务。
|
||||
|
||||
## 依赖与前置条件
|
||||
|
||||
- 用户已确认主规范与本里程碑按方案实施;实施计划进入执行,范围仍限本里程碑。
|
||||
- 基础网站评价已实现且本地验证通过,其用户验收/生产发布状态独立;本次不推定该功能已验收。
|
||||
- 现有后台认证/账号授权、游戏/账号读取、评价唯一写入、受信服务身份及 SpacetimeDB 2.8.3 可用。
|
||||
- 有隔离数据库可验证带真实存量评价的旧 schema 升级,以及超过 20 条评价的筛选/分页。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- [ ] 游戏管理入口按权限显示,ID 参数首次及切换生效;四条件组合查询、重置、刷新、跨页匹配、空态正确。
|
||||
- [ ] 列表与详情显示游戏/用户/评分/完整纯文本;仅评分记录可管理,管理员没有编辑内容/分数入口。
|
||||
- [ ] 原因 1–4000 码点,隐藏/删除空原因被拒,恢复不要求;取消无写入,失败保留草稿,删除明确不可恢复。
|
||||
- [ ] 隐藏后公共列表/人数/均分排除,全部隐藏为空态;个人提示精确、仍可编辑且不解除隐藏。
|
||||
- [ ] 恢复后参与公共统计,创建和用户修改时间不变;游戏恢复公开不自动解除隐藏。
|
||||
- [ ] 删除后个人读取为空并可重新评价,记录仍唯一;删除后总数减少、越界页正确回退。
|
||||
- [ ] 原因和管理员身份仅后台可见;匿名 401、无权限 403、伪造身份/隐藏状态无效,下架游戏后台可管理。
|
||||
- [ ] 管理变更与记录原子保存;同 key 重试不重复动作或日志,不同请求 409;旧页面/旧 key 不删除重新提交的新评价。
|
||||
- [ ] 旧 schema 有数据升级后内容/时间/唯一性不变,默认公开;schema/绑定/DTO 和定向检查通过。
|
||||
- [ ] 真实数据库/API及桌面/移动浏览器覆盖后台操作、前台回读、分页、长评论和失败;证据与未验证项逐条记录。
|
||||
|
||||
## 证据要求
|
||||
|
||||
- 自动化:领域原因校验/统计过滤,API鉴权/错误/DTO,后台筛选及操作交互、网站隐藏个人状态。
|
||||
- 运行时:存量 schema 升级,21 条以上评价的筛选分页、隐藏/恢复/删除、重新提交与重试;浏览器后台和网站联动。
|
||||
- 边界:401/403、管理原因泄漏、原子失败、状态并发、删除后主键复用、全部隐藏和页码越界。
|
||||
- 本里程碑的自动化、存量升级、真实 HTTP 及桌面/移动视口证据已回写[主规范](../../【玩法创作】平台入口与玩法链路-2026-05-15.md#迁移与验收);上方复选框保留供用户验收,不以既有评价功能证据替代。
|
||||
@@ -36,7 +36,7 @@
|
||||
- 正式状态来自后端,素材仍复用平台上传与归属能力;前端和 AGC 不另建公开游戏状态、owner 事实或审核结果。
|
||||
- 真实发行包具有不可变版本和 SHA-256,AGC dist 归一化为发行根 `index.html`;网页 ZIP 与 AGC 共用一条服务管道。
|
||||
- 不恢复退役玩法 API、公开作品表或专属 runtime;不把私有项目源码镜像公开。
|
||||
- 首版不包含原生/Wasm 游戏、任意外网依赖、服务端进程、多人联机、云存档、评论/评分/关注、排行榜、推荐算法和收益结算。
|
||||
- 本发行里程碑不包含原生/Wasm 游戏、任意外网依赖、服务端进程、多人联机、云存档、评论/评分/关注、排行榜、推荐算法和收益结算。网站评分与评价作为[独立里程碑](./【里程碑】网站游戏评分与评价-2026-09-30.md)推进,不借用本文件的发行验收结论。
|
||||
- 本文件只协调本业务;不顺带改造图片编辑器、Agent Runtime 执行模型或无关项目数据。
|
||||
|
||||
## 阶段 A:真实包、身份与可恢复发行
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# 【里程碑】游戏广场评分展示
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Version | 1.0 |
|
||||
| Status | implemented(本地验证通过,待用户验收;未部署) |
|
||||
| Date | 2026-10-01 |
|
||||
| Parent Spec | [游戏广场评分展示合同](../../【玩法创作】平台入口与玩法链路-2026-05-15.md#游戏广场评分展示合同) Version 1.0 |
|
||||
|
||||
## 目标
|
||||
|
||||
在网站游戏广场交付真实评分摘要展示,保持与详情评价及后台隐藏/恢复/删除后的统计一致,并保留原有目录交互。
|
||||
|
||||
## 范围
|
||||
|
||||
- 游客与登录用户均可在卡片查看 10 分制一位小数均分和评分人数,无有效评价显示“暂无评分”。
|
||||
- 公开列表和详情返回同形状摘要,统计来自后端全部有效评价,与公开游戏资料采用一致读取快照。
|
||||
- 空评论计人数,编辑不增加人数;隐藏/删除不计入,恢复重新参与,新版本保留评分。
|
||||
- 列表一次取得摘要,详情返回广场重新读取,并保留既有筛选与滚动位置。
|
||||
- 缺少摘要与无人评分明确区分,列表故障沿用重试;桌面和移动卡片均可正常展示和操作。
|
||||
- 共享契约、生成绑定和配套运行时验证,不改变持久化表或既有数据。
|
||||
|
||||
## 不在范围内
|
||||
|
||||
- 卡片输入评分、评论预览、评分排序、排行、推荐、AGC 或作者管理页评分展示。
|
||||
- 统计持久化/缓存/重算任务、推送/轮询、评价写入和管理规则变更、外部 API。
|
||||
- 原网站评价与后台管理的用户验收、生产部署或游戏发行包验收。
|
||||
|
||||
## 依赖与前置条件
|
||||
|
||||
- 用户已确认方案、提交文档,并后续授权工程实现;本里程碑代码与本地定向验证已完成。
|
||||
- 当前基础评价、后台管理与有效评价统计已实现并通过本地验证,其用户验收和生产发布状态独立。
|
||||
- 现有公开游戏目录、详情、平台认证、数据库服务身份与后端访问链路可用。
|
||||
- 可复用现有隔离测试游戏和少量评价,完成统计对照及桌面/移动视口检查;不要求新建大批量账号或分页数据。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- [ ] 游客和登录用户均显示真实 `8.2/10 · 26 人评分` 形状,均分固定一位小数。
|
||||
- [ ] 无评价及全部隐藏/删除为 `null/0` 和“暂无评分”,缺摘要为“评分暂不可用”,不显示虚构分数。
|
||||
- [ ] 公开列表、公开详情、评价接口在稳定数据集上的统计一致;多游戏不串数据,统计覆盖全部有效评价而非当前评价页。
|
||||
- [ ] 空评论计人数,改分只改均分,改文字不改均分;隐藏/恢复/删除后下一次读取正确变化。
|
||||
- [ ] 下架/封禁或没有有效公开版本的游戏不进入目录;摘要不泄漏评论、管理原因、管理员或个人状态。
|
||||
- [ ] 广场不逐卡请求评价;列表失败显示原错误/重试,旧响应不覆盖新筛选结果。
|
||||
- [ ] 详情修改评分后返回广场读取最新值;筛选、封面、卡片点击和滚动恢复行为保留。
|
||||
- [ ] 桌面和 375px 移动视口下长标题、大人数及评分换行不溢出、不阻挡操作。
|
||||
- [ ] 新公开响应保证带摘要,作者/旧响应可省略;DTO/绑定/schema 与定向检查通过,持久化数据保持。
|
||||
- [ ] 以现有数据完成一次隔离 API 摘要对照和桌面/移动页面检查,缺失验证如实标明;无需重跑完整评价/后台管理 E2E。
|
||||
|
||||
## 证据要求
|
||||
|
||||
- 新增自动化仅覆盖:已有广场页面测试中的正常/零评价/缺摘要展示,以及已有公开响应/契约测试中的新增摘要字段。
|
||||
- 复用已有测试:有效评价统计、隐藏/恢复/删除、改分、可见性及目录/详情交互,不为每层映射或本增量再复制完整矩阵。
|
||||
- 运行时:现有两款游戏和少量评价的一次列表/详情/评价摘要对照,确认隐藏过滤与统计不串联;桌面/移动检查评分布局及详情返回后的更新。
|
||||
- 不新增测试框架、专用 E2E 脚本或大批量分页 fixture;纯文本共享组件由页面测试覆盖。只有新增行为或实际改动的旧行为缺少覆盖时补测试。
|
||||
|
||||
## 实现与验证证据
|
||||
|
||||
- 公开列表/详情快照同事务携带摘要,限量后聚合;共享 DTO、mapper、2.8.3 生成绑定与公开 no-store 已接入。网站卡片与详情复用纯文本组件,缺字段不会造零值。
|
||||
- 前端受影响两文件 36 项、API game_distribution 34 项、shared-contracts 4 项、领域 summary 2 项通过。仅补评分文案参数化断言并扩展已有响应测试,未新增测试基础设施。
|
||||
- module/client 编译、TypeScript typecheck、受影响文件静态/格式检查、DTO parity、生成绑定/schema、编码、文档索引与 diff 检查通过。
|
||||
- 本机隔离库没有既有评分数据,复用既有 helper 建两个账号、两款游戏和两条空评论评价;三接口摘要 `8/2` 一致,隐藏一条后为 `7/1`,另一游戏 `null/0`。游客/登录列表一致,公开列表/详情 no-store、健康检查通过。
|
||||
- 1280px 桌面及 375px 移动浏览器显示正常且评分行无横向溢出;详情期间经真实 API 改分,返回广场更新为 `6.0/10 · 1 人评分`。
|
||||
|
||||
## 未验证与待验收
|
||||
|
||||
验收勾选保留给用户最终确认,临时计划尚未清理。未执行完整仓库测试、完整评价/后台管理 E2E、真实手机、长标题/极大人数浏览器矩阵、浏览器登录改分、实际发行包游玩或生产部署;本次没有重建管理动作/分页矩阵。
|
||||
@@ -0,0 +1,64 @@
|
||||
# 【里程碑】网站游戏评分与评价
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Version | 1.0 |
|
||||
| Status | implemented(本地验证通过,待用户验收;未部署) |
|
||||
| Date | 2026-09-30 |
|
||||
| Parent Spec | [平台入口与玩法链路](../../【玩法创作】平台入口与玩法链路-2026-05-15.md#网站游戏评分与评价合同)中的“网站游戏评分与评价合同”Version 1.0 |
|
||||
|
||||
## 目标
|
||||
|
||||
在网站公开游戏详情页交付“一用户一游戏一条评分与可选评论 → 查看个人评价 → 编辑与取消/提交 → 公共分页浏览 → 平均分与评分人数更新”的完整行为闭环。
|
||||
|
||||
## 范围
|
||||
|
||||
- 复用现有平台登录态,游客可浏览公开评价与统计,登录后可保存和修改自己的唯一评价。
|
||||
- 评分为 1–10 整数且必选,评论可空、最多 4000 Unicode 码点;实时显示字数与上限。
|
||||
- 已评价默认显示个人记录和编辑按钮,编辑时预填;取消不保存修改,成功提交收起输入模块。
|
||||
- 公共列表默认每页 20 条,按首次提交时间倒序;包含当前用户和空评论评价,不因编辑改变位置。
|
||||
- 详情显示平均分与评分人数,无人评分为“暂无评分”;人数不因修改增加,均分覆盖全部评价。
|
||||
- 评价随游戏身份保留,新版本发布不清空;下架与封禁关闭评价读写,重新公开恢复读取。
|
||||
- 覆盖并发唯一性、账号切换、失败与重试、移动端和键盘可操作性。
|
||||
|
||||
## 不在范围内
|
||||
|
||||
- AGC 客户端评价、独立评价页面、游戏广场评分卡片、评论回复/点赞/附件/Markdown。
|
||||
- 评价删除、评价审核、评分排行/推荐、必须游玩或消费才能评价的门槛。
|
||||
- 通用评论框架、统计缓存、评分任务或外部 API;不补做游戏发行原里程碑的生产上线验收。
|
||||
|
||||
## 依赖与前置条件
|
||||
|
||||
- 主规范与本里程碑评审通过,整数评分、分页与取消行为等默认规则已确认;均分/人数已由用户确认纳入范围。
|
||||
- 当前游戏详情、公开可见性、平台认证、账号公开资料读取与受信服务身份链路在验收环境可用。
|
||||
- 能建立独立测试账号及公开游戏,运行真实 SpacetimeDB/API 和网站浏览器验收;不得把本地通过当成生产上线。
|
||||
- 新增评价 schema、共享 DTO 与既有绑定变更在实现前写入本里程碑的单独实施计划,不对现有表进行破坏性修改。
|
||||
|
||||
## 验收标准
|
||||
|
||||
下列条款已完成本地验证,证据与限制见主规范;保留勾选供用户验收确认。
|
||||
|
||||
- [ ] 游客可读公开游戏评价与摘要;未登录不能保存或读取个人评价。
|
||||
- [ ] 1/10 分和空评论成功,未选/越界/非整数分数与超长评论均被拒。
|
||||
- [ ] 中文、英文、emoji、空格及换行计数一致,4000/4001 边界、粘贴和中文输入法符合主规范。
|
||||
- [ ] 已评价先显示自己的记录,编辑预填;取消恢复旧记录,提交中禁用评分选择、评论输入及取消/提交,成功隐藏表单,失败恢复编辑并保留草稿。
|
||||
- [ ] 首次取消不落库;同一账号/游戏的重复与并发保存只有一条;不能指定他人身份写入。
|
||||
- [ ] 至少 21 条真实记录验证分页、顺序和总数,自己的评价允许在个人区与公共区各出现一次。
|
||||
- [ ] 空评论也展示分数并计人数;编辑不增加人数、不改变创建时间或排序位置。
|
||||
- [ ] 无评价显示“暂无评分”;改分正确更新全部评价均分,改评论不改变均分,分页不影响统计。
|
||||
- [ ] 切换账号或游戏不残留个人草稿、不接受旧响应覆盖;读取失败不伪装为未评价。
|
||||
- [ ] 保存成功后此前的个人评价与公共列表读取失效,不覆盖新记录、摘要或列表;快速翻页/重复刷新只接受各区域最新请求的结果,旧请求不改写错误及加载状态。
|
||||
- [ ] 游戏下架/封禁后读写不可见,恢复公开后仍能读取已有评价,新版本不清空评价。
|
||||
- [ ] 评价读取或保存故障不阻断既有游戏资料与游玩;桌面/移动/键盘操作均可用。
|
||||
- [ ] 定向测试、网站类型、DTO、schema/生成绑定、真实 API/SpacetimeDB/browser smoke 及编码/文档索引/diff 检查有可复核结果。
|
||||
|
||||
## 证据要求
|
||||
|
||||
- 自动化:评分/字数/唯一性与修改规则、统计、分页、页面交互及账号切换定向测试。
|
||||
- 运行时:真实 SpacetimeDB/API 上的两账号写入、并发首次保存与分页统计;真实浏览器编辑/取消/提交、移动端字数提示与键盘评分。
|
||||
- 边界:未登录、伪造身份、不可见游戏、4000/4001 字符、0/11/非整数分数、响应丢失与迟到响应。
|
||||
- 证据按主规范条款逐项记录环境、操作、结果与未验证原因,原游戏发行证据不自动代替本次验收。
|
||||
|
||||
## 实施与验证状态
|
||||
|
||||
实现与本地验收已完成,详细证据已回写主规范:前端 77 项、领域 18 项、API 31 项、DTO 4 项、真实数据库/API 49 项(21 条评价)与桌面/移动视口浏览器操作通过。网站类型、定向严格类型/ESLint、DTO/schema/绑定、DDD/runtime access、编码/索引/diff 检查通过。未部署,未验证物理手机/真实系统输入法或发行资源游玩;用户验收后清理本里程碑和实施计划。
|
||||
@@ -1,5 +1,38 @@
|
||||
# 决策记录
|
||||
|
||||
## 2026-10-01 游戏广场评分展示边界
|
||||
|
||||
- 用户确认广场卡片增加一位小数的 10 分制平均分与评分人数,无有效评价显示“暂无评分”;保留现有排序、筛选、卡片打开详情及返回上下文。
|
||||
- 公开游戏列表和详情共用投影,随现有请求返回 ratingSummary;复用后端有效评价统计并排除隐藏记录,不逐卡请求评价,不增加统计表、缓存或重算任务。
|
||||
- 共用 DTO 可选字段兼容作者及旧响应,当前公开列表/详情保证返回;缺字段显示“评分暂不可用”,不能伪装为无人评分。读取与后台管理后的下一次刷新一致,不增加推送/轮询。
|
||||
- 本增量不改变持久化表或评价写入,仅同步读取投影、DTO 与生成绑定;不扩展评分排序、推荐、AGC 或外部 API。
|
||||
- 用户要求测试保持简单:仅补卡片正常/零评价/缺摘要及公开摘要契约断言,复用已有统计/管理/目录测试和少量隔离数据 smoke;不新增专用 E2E、分页 fixture 或逐层重复测试。
|
||||
- 权威入口:[游戏广场评分展示合同](../../【玩法创作】平台入口与玩法链路-2026-05-15.md#游戏广场评分展示合同)。已完成工程实现及本地定向验证,待用户验收,未部署;持久化表未变,公开 procedure 返回类型与后端绑定需配套发布。
|
||||
|
||||
## 2026-10-01 后台游戏评价管理规则
|
||||
|
||||
- 用户确认新增管理员隐藏/删除评价,后台不能修改分数或正文;隐藏整条评价并从公共列表、平均分、人数和公共分页总数排除,恢复后重新参与。个人区域固定提示“已被管理员隐藏”,用户仍可编辑但不能自动恢复公开。
|
||||
- 隐藏和删除必须填写原因,恢复不要求;原因仅后台展示。删除物理移除,不能恢复,用户可重新评价,唯一规则继续成立。
|
||||
- 当前方案范围为游戏名称选择/ID定位、评价用户 ID、评论关键词、状态四类组合筛选,以及分页、详情、单条操作和持久操作记录。不增加批量、导出、举报、自动审核或评分/时间范围筛选。
|
||||
- 实现边界:评价表末尾追加默认 false 的 is_hidden;新增私有管理记录,事务保存操作人/原因/时间,以创建时间区分删除后重建记录,同 key 重试不得再次操作新评价。不增加统计缓存;用户编辑保持隐藏状态,管理操作不改变用户内容时间。
|
||||
- 状态:用户确认按方案实施;后台与网站联动已实现并通过本地存量升级、真实 HTTP 和浏览器验证,证据见主规范。待用户验收,未部署。产生隐藏记录后不得直接回退到未过滤隐藏状态的旧后端。
|
||||
- 权威入口:[后台游戏评价管理合同](../../【玩法创作】平台入口与玩法链路-2026-05-15.md#后台游戏评价管理合同);活动[里程碑](../plans/【里程碑】后台游戏评价管理-2026-10-01.md)与[实施计划](../plans/【实施计划】后台游戏评价管理-2026-10-01.md)。
|
||||
|
||||
## 2026-09-30 网站游戏评价范围与状态边界
|
||||
|
||||
- 已确认需求:网站游戏详情支持每账号每游戏唯一一条 1–10 分评分和可空的评论(最多 4000 字符),可修改自己的评价;个人区默认展示已有评价并提供编辑预填,公共列表分页且不排除自己。
|
||||
- 已确认扩展:详情显示真实平均分与评分人数,空评论计人数,修改不增加人数;当前先由后端评价记录计算,不增加统计缓存。
|
||||
- 状态:用户已确认按最新方案实现;整数评分、默认每页 20 条、创建时间排序、取消行为与游戏维度保留作为现行合同。网站与后端实现已落地,本地验收证据见主规范;用户验收与生产发布单独确认。原发行 Version 0.2 的排除项不再被解释为永久禁止新增用户评价。
|
||||
- 实现边界:新增私有 `game_distribution_review` 表和三条评价 API,游戏/账号组合主键保证唯一;可见性与写入同事务,分页与均分来自一致快照,作者资料读时关联账号。网站用游戏/账号上下文隔离草稿,以读取序号隔离旧成功、错误和加载结束,保存期间冻结输入。
|
||||
- 权威入口:[平台入口与玩法链路](../../【玩法创作】平台入口与玩法链路-2026-05-15.md#网站游戏评分与评价合同);执行验收使用[独立里程碑](../plans/【里程碑】网站游戏评分与评价-2026-09-30.md),不得把既有发行或本次文档检查当作评价功能上线证据。
|
||||
|
||||
## 2026-10-01 生产 Nginx 以模板为唯一来源:host-only 块独立成 snippet、退役路由不做显式 404
|
||||
|
||||
- 背景:线上主站 `genarrative.conf` 长期手工维护,`profile` 未进 SPA allowlist 导致 `/profile` 刷新 404,`client_max_body_size` 也停在 `64m`;同时线上存在 4 处仓库模板没有的 host-only 块(画廊读取限流、`/finance-forecast/`、`/medical-science/`、`/home/` 官网首页入口),直接用 `Genarrative-Server-Provision` 覆盖会静默删除它们。
|
||||
- 决策:生产 vhost 以 `deploy/nginx/genarrative.conf` 为唯一来源;生产机专属路径收进 `deploy/nginx/snippets/genarrative-host-extras.conf`,由主模板 include、由 Server-Provision 安装,新增平台路由仍必须在主模板内声明并同步 `deploy/pingora/nginx-route-parity.matrix.json` 与 Pingora 网关。snippet 内的 location 有意不在矩阵覆盖范围,Pingora 接公网 443 前必须单独确认这些路径的处理方式。
|
||||
- 边界:退役路由(`/match3d`、`/puzzle`、`/runtime/*`、`/gallery/*/detail`、`/works/detail`、`/worlds/detail`、`/bark-battle` 等)不再进 SPA allowlist,也不配置显式 404,统一落 `location /` 的 `error_page 404 /404.html`。`/home/` 是 2026-08-11 官网拆分前的历史入口,是否退役需与官网侧一起决定。
|
||||
- 验证方式:`node scripts/check-nginx-spa-routes.mjs`、`node scripts/check-pingora-route-parity.mjs`、`npm run check:production-ops`、`npm run check:encoding`、`git diff --check`、`bash -n scripts/jenkins-server-provision.sh`;线上改后按生效配置烟测 `/profile`、模板 12 条 SPA 路由、`/admin/`、`/home/`、`/finance-forecast/`、`/medical-science/`、画廊 API 与 `/games/game_<id>/…` 发行网关。
|
||||
|
||||
## 2026-10-01 DirectProject 审批拒绝原因留痕
|
||||
|
||||
- 决策:宿主拒绝 app-server 的审批 / 交互请求时,原因必须落 AppData `RUST` 日志。稳定键 `agent.direct_codex.approval.denied`,字段为 `thread_id` / `method` / `reason` / `distinct_reasons`;未绑定宿主执行器时另记 `agent.direct_codex.interaction.no_adapter`(`method` / `outcome`,`outcome` 区分 `decline` / `empty-permissions` / `unsupported-method`);回包未送达另记 `agent.direct_codex.approval.response_delivery_failed`,`cause` 区分 `response-write-failed` 与 `turn-bind-mismatch`。
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
|
||||
这里只记录对当前开发仍有用的症状、根因、排查方法和风险边界。同一事实保留一个当前口径;退役对象的专属过程与单轮测试结果由 Git 历史追溯。遇到旧路径或版本时,以现行代码和专题文档为准。
|
||||
|
||||
## 2026-10-01 线上 Nginx 手工维护漂移:`/profile` 刷新 404、发行包请求体仍限 64m
|
||||
|
||||
- **现象**:登录后刷新 `https://www.genarrative.world/profile` 直接 404,返回 Nginx 默认 404 页(162 字节);应用内点击进入正常。`curl` 复现:`/`、`/project`、`/creation`、`/components`、`/design-system`、`/games*`、`/editor/canvas` 全部 200 且正文与 `/` 同一份 SPA 外壳,只有 `/profile`(含 `/PROFILE`、`/profile/`)404。
|
||||
- **原因**:刷新是真实 HTTP 请求,命中线上 `/etc/nginx/conf.d/genarrative.conf` 的 SPA allowlist;线上白名单是手工维护的(留档 `genarrative.conf.bak-sparoutes-20260923T153751Z`),`profile` 从未加入,该深链落回 `location /` 的 `try_files $uri $uri/ =404`。仓库三份模板当时都写了 `profile`,而 `npm run check:nginx-spa-routes` 只校验仓库模板,查不出线上漂移。同一份线上文件还把 `client_max_body_size` 停在 `64m`(模板 210m,会让 200 MiB 发行包上传 413),并保有 4 处模板没有的 host-only 块(画廊读取限流 location、`/finance-forecast/`、`/medical-science/`、`/home/` 官网首页入口),所以直接拿模板整文件覆盖会删掉这些线上能力。
|
||||
- **处理**:线上只收敛 SPA allowlist(12 条,含 `/profile`)并补 `error_page 404 /404.html`;host-only 块收进 `deploy/nginx/snippets/genarrative-host-extras.conf`,由主模板 include、由 `Genarrative-Server-Provision` 安装。退役路由不进白名单,也不需要显式 404 配置,落 `location /` 即得品牌 404(非 HTML 客户端保持纯 404)。
|
||||
- **判据/取证**:改前 `grep -c profile /etc/nginx/conf.d/genarrative.conf` 为 0;改后 `curl -s -o /dev/null -w '%{http_code}' https://www.genarrative.world/profile` 为 200(正文 sha 与 `/` 一致),`sudo nginx -T | grep -c 'editor/canvas|profile|project'` 为 1,`match3d` 计数为 0。
|
||||
- **长期口径**:任何 SPA/路由白名单改动都要同时核对**生效配置**(`sudo nginx -T`)与仓库模板;线上主站 vhost 只由模板 + `genarrative-host-extras.conf` 生成,不再手工追加路由。snippet 内的 location 不在 Pingora 路由矩阵覆盖范围,Pingora 接公网 443 前要单独确认这些 host-only 路径。
|
||||
## 2026-09-30 构建期 staging 撞上不装 npm 依赖的 Linux 门禁:AGC 壳 Rust lane 全红
|
||||
|
||||
- **现象**:`Project CI` 的 AGC 壳 Rust 三条 lane(`npm run check:native-shells:agc-rust-shard-*`)在 `fb130d184` 之后全部失败,日志只有 `error: failed to run custom build command for genarrative-ai-game-creator-shell` 与 `thread 'main' panicked at build.rs:65:28: Claude Agent SDK 缺失;请先执行 npm ci`(run 3083 / job 17521 实测,1 分钟即失败)。
|
||||
@@ -6200,3 +6207,16 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
|
||||
- **处理(现行口径)**:`src/tests/mod.rs` 新增 `MOCK_PROVIDER_REQUEST_TIMEOUT`(30 秒)作为「等一条 mock provider 请求到达」的唯一预算,`wait_for_captured_mock_request` 与 `sessions.rs` 里 7 处 `recv_timeout` 统一引用它;负向断言(确认「不会有下一个请求」的 200ms 窗口)保持不动。请求到达即返回,成功路径零成本,真断链仍会失败,只是失败得更晚。
|
||||
- **验证**:`cargo test --bin genarrative-ai-game-creator-shell tests::sessions:: -- --test-threads=1` 44 passed;`node apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.mjs --shards=4 --shard-index=4` 787 tests ok in 88.7s(正是原先失败的那一片)。
|
||||
- **关联**:`apps/ai-game-creator-shell/src-tauri/src/tests/sessions.rs`、`apps/ai-game-creator-shell/src-tauri/src/tests/mod.rs`。
|
||||
|
||||
## 2026-10-01 AGC 官方 Claude 模型缺少账号令牌会被误报为通道断开
|
||||
|
||||
- **现象**:模型目录把回合路由到 `cc`,本地 `game-creator.config.json` 的 `llm.apiKey` 为空时,Claude Agent SDK 返回失败终态;界面只显示“执行通道未能建立或已断开”。
|
||||
- **根因**:Claude sidecar 只从 `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN` 或本地 `llm.apiKey` 读取认证,没有复用已登录的 AGC 平台会话;同时失败终态解析丢弃了上游错误摘要。
|
||||
- **处理**:官方模型且未启用自定义目录时,将当前平台会话令牌仅注入 sidecar 子进程环境;保留最多 512 字符的 Claude 终态错误摘要,继续由统一诊断层脱敏,避免凭据落盘。
|
||||
## 2026-10-01 AGC 首页把 Web 预检错误与 Tauri IPC 错误合并,造成无法诊断的生成阻拦
|
||||
|
||||
- **现象**:用户在首页点击「做游戏」后看到「Web 游戏环境预检未通过,请检查 Node/npm 或浏览器」,但同一安装包的 `--environment-check` 可能已经返回 `status=ready`;首页仍会阻止自动命名、建项和首次生成。
|
||||
- **根因**:`homeWebPreflight.ts` 原先对 `preflight_web_game_creation` 的所有异常统一抛出 `HOME_WEB_PREFLIGHT_FAILURE`,调用方 `useHomeProjectCreation.ts` 的两层 `catch` 又再次覆盖原始错误。于是宿主真实预检码、Tauri IPC 断连、命令未注册和 WebView `Failed to fetch` 都显示成 Node/npm/浏览器故障。
|
||||
- **处理**:保留预检失败关闭语义,但提取 Rust 返回的安全错误码;对无错误码的 Tauri 调用故障显示独立 IPC 提示,并仅对这类瞬态调用失败重试一次。首页调用方必须透传该安全提示,不能重新覆盖为通用文案。
|
||||
- **验证**:`apps/ai-game-creator-shell/tests/homeWebPreflight.test.tsx` 覆盖 ready、具体宿主错误码、IPC 失败、无错误码 blocked 和 planning 绕过;同一 Windows 安装包 CLI 实测 Node `v22.22.2`、npm `10.9.7`、Edge desktop/mobile PNG 均通过。
|
||||
- **关联**:`apps/ai-game-creator-shell/src/features/app-shell/homeWebPreflight.ts`、`apps/ai-game-creator-shell/src/features/app-shell/useHomeProjectCreation.ts`、`apps/ai-game-creator-shell/tests/homeWebPreflight.test.tsx`、`apps/ai-game-creator-shell/src-tauri/src/environment_check/web_creation.rs`。
|
||||
|
||||
@@ -116,6 +116,7 @@ UI 编辑器的“分析参考图”步骤、Rust 命令 `suggest_ui_design_sema
|
||||
- 对客户端刚创建且内容仍匹配可信模板的 Web 脚手架,在正式生成前由宿主执行受控依赖准备和真实 Vite 构建。依赖安装禁用生命周期脚本;不自动安装或覆盖导入/用户修改过的工程。
|
||||
- 准备凭证的 `ready` 只证明初次环境准备成功,不代表当前游戏已验收,后续正常修改不得因此重新安装。`preparing` 中断恢复必须证明原拥有者已结束且其执行子树已回收;身份或归属未知时保留阻断,不重复执行。
|
||||
- 输出明确区分“Node/npm 构建能力通过”与“本项目 Vite 构建通过”;任何一步失败保留可行动错误,不降级为预检成功。
|
||||
- 首页必须区分宿主预检阻塞与 Tauri IPC 调用失败:Rust 返回的安全错误码可透传到状态栏;无错误码的瞬态 IPC 失败只允许一次有界重试后显示独立的客户端连接故障,不得统一伪装成 Node/npm 或浏览器故障。预检仍保持失败关闭,不得因为展示错误变得可绕过。
|
||||
- 安装载荷提供相同的安全预检 CLI,验证无系统 Node 的独立运行、缺包/篡改失败关闭。NSIS 解包载荷 smoke 与真实安装器注册流程分开报告,不覆盖当前用户的既有安装。
|
||||
|
||||
### 跑酷固定基线
|
||||
|
||||
@@ -461,8 +461,34 @@ Responses 的终态载荷既是工具调用的恢复源,也是正文的恢复
|
||||
- 复用规则:末尾可空列 `local_project_id` 保存发布方本地项目标识(AGC 的 `manifest.projectId`)。同一 `owner_user_id` 再次以相同 `local_project_id` 创建游戏时复用既有 `game_id` 并只新增版本,避免“更新”被实现成新建游戏;该字段只是复用提示,不构成所有权或路径凭证,也不能用于跨账号匹配。
|
||||
- 索引:`by_game_distribution_game_owner_user_id` 用于作者私有游戏列表;`game_id` 为主键。公开目录只返回 `visibility = published` 且存在有效 `active_version_id` 的投影。
|
||||
|
||||
### `game_distribution_review`
|
||||
|
||||
- Rust 结构体:`GameDistributionReview`,源码:`server-rs/crates/spacetime-module/src/game_distribution.rs`;私有表,仅通过受信 API 服务身份的 procedure 与 BFF 提供评价投影。
|
||||
- 主键 `review_id` 使用游戏 ID 字节长度前缀与 `game_id`、`user_id` 无歧义组合,确保每账号每游戏只有一条;`by_game_distribution_review_game_id` 用于游戏评价读取。保存 `score`(1–10 整数)、`comment`(换行统一 LF 后最多 4000 Unicode 码点)、首次创建与最近实际修改时间,不绑定发行版本。
|
||||
- 事务内复用公开详情可见性,创建或更新唯一评价;重复相同内容不改时间。公共分页按创建时间和主键倒序,作者资料从 `user_account` 读取;均分和人数与分页在同一事务快照内计算,不落额外统计或缓存。
|
||||
- 网站 HTTP:`GET /api/game-distribution/games/{gameId}/reviews` 允许游客页码读取;`GET/PUT /api/game-distribution/games/{gameId}/my-review` 使用认证账号,均返回 `Cache-Control: no-store`。未公开、下架、暂停或无有效公开版本时统一 404;不受发布灰度影响。完整行为见玩法链路的“网站游戏评分与评价合同”。
|
||||
|
||||
### 后台游戏评价管理数据增量(2026-10-01)
|
||||
|
||||
状态:`implemented`,本地验证通过,待用户验收,未部署。下述字段/表已进入当前 schema,完整合同与验证证据见[后台游戏评价管理](./【玩法创作】平台入口与玩法链路-2026-05-15.md#后台游戏评价管理合同)。
|
||||
|
||||
- `game_distribution_review` 末尾追加 `is_hidden: bool` 并设置 `#[default(false)]`,已有评价全部公开;新增 `user_id` 索引,保留现有主键和游戏索引。用户编辑只改变 score/comment/updated_at,保留隐藏状态;管理动作不改变用户内容时间。
|
||||
- 新增私有 `game_distribution_review_moderation_log`:`operation_id: String` 主键、`review_id/game_id/user_id: String`、`review_created_at: Timestamp`、`action: String`(hide/restore/delete 白名单)、`admin_user_id: String`、`reason: Option<String>`、`created_at: Timestamp`;按 `review_id` 建索引。隐藏/删除原因必填、恢复为 None,只返回后台;不复制正文,不级联删除记录。
|
||||
- `operation_id` 为认证管理员与幂等 key 的无歧义组合。事务核对记录中的目标、原评价创建时间、动作和原因识别重放;同 key 不同请求冲突,同请求不重做。请求携带 `expectedCreatedAt` 区分同一组合主键删除前后重建的评价,旧目标请求不得作用于新行。
|
||||
- 受信服务 procedure 原子完成查询过滤/分页,或管理状态变更/物理删除与操作记录插入;后端访问继续经 typed facade。普通公开读取及所有评分摘要先过滤 `is_hidden=false`;个人读取仍返回隐藏状态,后台读取不受游戏公开状态限制。不增加统计缓存。
|
||||
- 新后台 `/admin/api/game-distribution/user-review-games`、`/user-reviews`、`/user-reviews/{reviewId}`、`/user-reviews/{reviewId}/moderation` 与原发行 `/reviews` 分开;新增 Tab 权限 `game-reviews`,HTTP 认证与页面权限均校验,管理员 ID 来自会话。个人/公共评价 DTO 追加 isHidden,不暴露原因。
|
||||
- 已同步 `migration.rs`、表目录、生成绑定并通过 schema 检查;隔离数据库的 21 条旧评价兼容升级后默认公开,既有记录内容/唯一性/时间不变。部署后若已隐藏评价,不直接回退到未过滤隐藏记录的旧后端。
|
||||
|
||||
### `game_distribution_review_moderation_log`
|
||||
|
||||
- Rust 结构体:`GameDistributionReviewModerationLog`,源码:`server-rs/crates/spacetime-module/src/game_distribution.rs`;私有管理操作表,仅通过受信服务 procedure 提供后台投影。
|
||||
- 保存 `operation_id` 主键、评价/游戏/用户 ID、目标创建时间、动作、管理员 ID、可空原因和操作时间;按 `review_id` 建索引,读取时同时匹配目标创建时间,记录不随评价删除。
|
||||
- 管理员与幂等 key 的无歧义组合唯一定位请求;同请求重试不重做,同 key 不同请求冲突。变更与日志同事务,原因只供后台,完整合同见上方后台评价管理数据增量。
|
||||
|
||||
### `game_distribution_version`
|
||||
|
||||
|
||||
|
||||
- Rust 结构体:`GameDistributionVersion`
|
||||
- 源码:`server-rs/crates/spacetime-module/src/game_distribution.rs`
|
||||
- 用途:不可变发行版本与真实包确认事实。创建后冻结 `package_sha256`、字节数、文件数、根入口和版本号;后续只推进上传、校验、审核、公开、撤回状态,并记录私有对象键、文件清单、入口 URL、审核者和阶段时间。
|
||||
|
||||
@@ -23,6 +23,30 @@ grep -i '^cache-control: no-store' ~/data/tmp/tsingnova-client-download.headers
|
||||
|
||||
验收必须同时满足:接口返回 `200 application/json`,响应包含 `downloads` 与 `unavailablePlatforms`,并带 `Cache-Control: no-store`;`https://tsingnovagames.com/` 仍返回官网首页。若 API 直连 `http://127.0.0.1:8082/api/client-downloads` 是 `200`、官网域名仍是 `404`,说明官网 vhost 没有 include 下载 snippet,不能靠 reload 旧配置解决。
|
||||
|
||||
### 主站 SPA 白名单与 host-only 覆盖
|
||||
|
||||
主站 SPA 深链(`/profile`、`/project`、`/games/*` 等)只在 `deploy/nginx/genarrative.conf` 的 `# BEGIN GENARRATIVE MAIN SPA ROUTES` allowlist 内回退 `/index.html`;不在白名单的路径落回 `location /` 的 `try_files $uri $uri/ =404`,再经 `error_page 404 /404.html` 返回品牌 404 页。非 HTML 客户端(`Accept` 不含 `text/html`)保持纯 404,由维护 snippet 的 `location = /404.html` 决定,属预期行为。
|
||||
|
||||
2026-10-01 线上 `/profile` 刷新 404 就是白名单漂移:线上 `genarrative.conf` 是手工维护的,`profile` 从未加进 allowlist,而仓库三份模板都写了它;`npm run check:nginx-spa-routes` 只校验仓库模板,查不出线上漂移。同一份线上文件还把 `client_max_body_size` 停在 `64m`,并保有 4 处模板当时没有的 host-only 块,因此**不能**直接拿模板整文件覆盖线上。
|
||||
|
||||
- 生产 vhost 以 `deploy/nginx/genarrative.conf` 为唯一来源;生产机专属路径收在 `deploy/nginx/snippets/genarrative-host-extras.conf`(`/finance-forecast/`、`/medical-science/`、公开画廊读取限流 location、`/home/` 官网首页入口),由主模板 include,并由 `Genarrative-Server-Provision` 安装到 `/etc/nginx/snippets/`。
|
||||
- snippet 内的 location 不在 `deploy/pingora/nginx-route-parity.matrix.json` 覆盖范围;新增**平台**路由必须在主模板内声明并同步矩阵与 Pingora 网关,否则 `npm run check:pingora-route-parity` 的「模板 location 必须被矩阵覆盖」会失败。
|
||||
- 退役路由(`/match3d`、`/puzzle`、`/runtime/*`、`/gallery/*/detail`、`/works/detail`、`/worlds/detail` 等)不需要任何显式 404 配置,也不进白名单,落 `location /` 即得品牌 404。
|
||||
|
||||
改动或排障后按生效配置核对,不要只看仓库模板:
|
||||
|
||||
```bash
|
||||
sudo nginx -T | grep -n -A6 'GENARRATIVE MAIN SPA ROUTES' # allowlist 生效内容
|
||||
sudo nginx -T | grep -n 'client_max_body_size' # 期望 210m
|
||||
sudo nginx -T | grep -n 'genarrative-host-extras' # host-only snippet 已 include
|
||||
for p in / /profile /project /creation /components /design-system /games /games/detail \
|
||||
/games/mine /games/play /games/publish /editor/canvas; do
|
||||
printf '%s %s\n' "$p" "$(curl -s -o /dev/null -w '%{http_code}' https://www.genarrative.world$p)"
|
||||
done # 期望全部 200,且正文与 / 的 index.html 一致
|
||||
curl -s -o /dev/null -w '%{http_code}\n' https://www.genarrative.world/match3d # 期望 404
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
## 构建回归的隔离与发布文件权限
|
||||
|
||||
Git hook 的临时仓库测试必须清除子进程继承的仓库定位环境(例如 `GIT_DIR`、`GIT_WORK_TREE`、`GIT_INDEX_FILE`);仅设置 `cwd` 不能隔离 Git。回归应从带这些变量的外层仓库运行,验证外层引用、索引与配置不变。临时测试文件必须留在独立目录并清理,不得通过测试生成主仓库提交或覆盖 ESLint、Prettier 配置。修复 lint 配置时保留原有规则、忽略范围与零警告门禁,不以关闭规则代替排障。
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# 平台入口与玩法链路
|
||||
|
||||
> 更新时间:`2026-09-23`
|
||||
> 更新时间:`2026-10-01`
|
||||
>
|
||||
> 本文只描述现役主站平台壳、图片画布编辑器和对应后端契约。未出现在现役路由树、shared-contracts 或 SpacetimeDB module / bindings 中的入口与数据对象不属于本文范围。
|
||||
> 本文描述现役主站平台壳、图片画布编辑器和游戏分发合同;拟新增能力在各节单独标明状态。网站游戏评分与评价、后台游戏评价管理已实现并通过本地验证,待用户验收,未部署;游戏广场评分展示方案已确认,待实现。当前实现以路由树、shared-contracts 和 SpacetimeDB module / bindings 为准。
|
||||
|
||||
## 现役平台入口
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
|
||||
交付“AGC 一键提交游戏 / 网页上传游戏 ZIP → 后端收取真实发行包 → 校验与人工审核 → 主站发现、详情、游客在线游玩 → 更新与下架”的完整闭环。验收必须使用真实上传、真实存储、真实审核状态和隔离发行域名;静态演示卡片、metadata-only 请求或前端本地发布状态不能作为完成证据。
|
||||
|
||||
首版支持可以离线运行的静态 Web 游戏:HTML、JavaScript、CSS、JSON 与图片、字体、音视频。AGC 首先支持现有 npm/Vite Web 工程;网页入口允许上传符合相同包合同的 ZIP,不以游戏引擎名称限制普通静态产物。Godot/Cocos 工程源码、原生可执行文件、Wasm、服务端进程、外部 API、多人联机、云存档和跨版本存档迁移不在首版内。暂不做评分、评论、关注、榜单、推荐算法、交易和创作者收入。
|
||||
首版支持可以离线运行的静态 Web 游戏:HTML、JavaScript、CSS、JSON 与图片、字体、音视频。AGC 首先支持现有 npm/Vite Web 工程;网页入口允许上传符合相同包合同的 ZIP,不以游戏引擎名称限制普通静态产物。Godot/Cocos 工程源码、原生可执行文件、Wasm、服务端进程、外部 API、多人联机、云存档和跨版本存档迁移不在首版内。本发行合同 Version 0.2 不包含用户评分与评论,其扩展范围见下文“网站游戏评分与评价合同”;关注、榜单、推荐算法、交易和创作者收入仍不在范围内。
|
||||
|
||||
### 入口与产品体验
|
||||
|
||||
@@ -176,3 +176,287 @@
|
||||
3. 是否接受首版离线静态包、无 Wasm/外网/持久存档的范围,以及建议包额度、7 天失败包保留和 60 秒缓存撤销窗口;公开/撤销版本及审核记录保留周期待定。
|
||||
4. 是否接受平台同源路径发行(`/games/<gameId>/` + `sandbox="allow-scripts"` 的不透明来源隔离)替代独立发行域名:选择该方案后独立域名、通配 DNS/TLS 与 CDN 不再是上线门禁,存储区域与运营责任仍需确定。
|
||||
5. 本节已提供页面行为、发行状态、真实上传、幂等/CAS 和 API 草案,足以评审完整业务;尚不足以直接实现持久化和部署,必须在对应里程碑评审前冻结表/索引/服务身份、额度/清理、最终响应 DTO 与发行基础设施配置。未经评审不建立 `ready` 实施计划,不把 proposed 标记为 accepted。
|
||||
|
||||
## 网站游戏评分与评价合同
|
||||
|
||||
本节记录已实现的基础评价合同;已实现的后台管理增量规则及其独立验证证据见下方“后台游戏评价管理合同”。
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Version | 1.0 |
|
||||
| Status | implemented(本地验证通过,待用户验收;未部署) |
|
||||
| Date | 2026-09-30 |
|
||||
| 入口 | 网站 `/games/detail?id=<gameId>` |
|
||||
| 交付目标 | 登录用户对同一游戏保存并修改唯一一条评分与可选评论;所有访客分页浏览评价,并查看真实平均分与评分人数 |
|
||||
| 里程碑 | [网站游戏评分与评价](./project-memory/plans/【里程碑】网站游戏评分与评价-2026-09-30.md) |
|
||||
|
||||
### 范围与默认规则
|
||||
|
||||
- 本次只扩展网站游戏详情页、现有游戏分发后端与共享契约;复用平台账号,评分与评论合并为一条“评价”。
|
||||
- 用户已明确要求:评分范围 1–10、评论可空且最多 4000 字符、可修改自己的评价、每用户每游戏最多一条、个人评价展示与编辑预填、公共列表分页且包含自己的评价。
|
||||
- 用户已确认加入详情页平均分和评分人数:无人评分显示“暂无评分”,空评论计入评分人数,修改不增加人数,当前规模先由后端读取评价记录计算。
|
||||
- 本方案采用以下评审默认值:评分只接受整数;每页默认 20 条;按首次提交时间倒序;取消首次填写清空草稿,取消编辑恢复原评价;评价跟随游戏身份,不随发行版本更新清空。
|
||||
- 登录用户均可评价,包括游戏作者;不增加必须先游玩、消费或具有发布权限的条件。评价权限不复用 `game-distribution:publish` 发布灰度。
|
||||
- 基础评价功能不包含 AGC 客户端评价界面、独立评价页面、游戏目录评分展示、评论回复/点赞/图片/Markdown、评价删除、评价审核或推荐排行;后台管理与广场评分展示分别由下方独立合同扩展,不新建通用评论框架、评分缓存或独立统计任务。
|
||||
|
||||
### 评分、评论与统计口径
|
||||
|
||||
1. 评分必选,取值为整数 1–10;首次填写时不预选分数,不能仅提交文字而不评分。
|
||||
2. 评论为空字符串或未提供时均允许提交。纯空白评论保存为空字符串;普通评论保留首尾空格与换行。
|
||||
3. 输入与提交前统一把 `CRLF` / `CR` 换行为 `LF`,再按 Unicode 码点计数;空格、换行同样计入上限。前端与后端计数一致,不按 UTF-8 字节或 UTF-16 单元计数;组合 emoji 可能含多个码点。
|
||||
4. 长度校验先于纯空白归一化,4000 字符允许,4001 字符拒绝;不静默截断后保存。输入框旁始终显示 `当前字数/4000`,超限显示可读错误并阻止提交;中文输入法与粘贴同样按该口径处理。
|
||||
5. 每个评价对应一个已认证账号和一个稳定 `gameId`,不绑定 `versionId`。分数与评论一起原子保存;修改已有记录,`createdAt` 不变,有内容变化时更新 `updatedAt`。
|
||||
6. 平均分等于所有有效评价分数之和除以评价人数,包含作者、当前用户和只评分的记录;每账号最多计一次。后端返回保留一位小数的平均分,页面显示如“8.2/10 · 26 人评分”。
|
||||
7. 无评价时 `ratingCount=0`、`averageScore=null`,显示“暂无评分”;不能显示“0 分”或虚构默认分。修改评分重新计算平均分,人数保持不变;修改文字不改变均分。
|
||||
8. 平均分与人数来自后端对该游戏评价的权威读取,不能由当前分页或前端累计计算。本次不持久化总分、人数或均分,不增加缓存及重算任务。
|
||||
|
||||
### 页面布局与交互
|
||||
|
||||
游戏详情保留“立即玩”和现有资料,在游戏介绍后增加“我的评价”“全部评价”两个区块;平均分与人数显示在详情资料区,并在评价区域共用同一份后端摘要。
|
||||
|
||||
| 状态 | 我的评价区域 | 操作结果 |
|
||||
| --- | --- | --- |
|
||||
| 未登录 | 显示“登录后评价”入口,不展示可提交表单 | 使用现有平台登录入口;公共列表和评分摘要仍可浏览 |
|
||||
| 读取自己的评价中 | 显示加载状态 | 确认不存在已有评价前,不提前展示首次填写表单 |
|
||||
| 已登录且没有评价 | 展示 1–10 分选择、可选评论输入框、字数统计、取消/提交按钮 | 首次输入的评分和文字仅为当前页面草稿 |
|
||||
| 已登录且已有评价 | 展示自己的分数、评论、修改时间及“编辑评价”按钮,隐藏输入模块 | 空评论仍展示分数,正文不填造提示内容 |
|
||||
| 编辑已有评价 | 展示输入模块,完整预填分数与评论 | 个人展示区域暂由编辑表单替代,公共列表仍保留该评价 |
|
||||
| 提交中 | 保留表单及内容,显示提交状态并禁用评分选择、评论输入及取消/提交 | 冻结本次提交内容,防止重复点击和后续输入被成功响应收起表单时丢失;失败后恢复编辑,不把在途请求解释为已保存 |
|
||||
| 提交成功 | 用后端权威记录显示自己的评价,隐藏输入模块 | 刷新平均分、人数与公共列表 |
|
||||
| 读取或提交失败 | 给出错误与重试入口 | 读取失败不伪装成“没有评价”;提交失败保留草稿,不覆盖原评价 |
|
||||
|
||||
- 首次填写点击取消:清空评分与文字,保留首次填写表单,不产生后端写入;编辑点击取消:丢弃草稿,恢复上次成功读取/保存的个人评价。
|
||||
- 首次提交成功后公共列表回到第一页;修改成功后刷新当前页,保留页码。提交响应中的个人记录与统计摘要先更新,分页读取失败单独提示重试,不回滚已成功保存的评价。
|
||||
- 保存成功后,使此前发出的个人评价与公共列表读取失效;迟到响应不得覆盖已保存的个人记录、评分摘要或刷新后的列表。同一游戏内快速翻页或重复刷新时,各读取区域只接受其最新请求的结果,旧请求的成功、错误和加载结束均不得覆盖当前状态。
|
||||
- 刷新页面重新读取后端保存结果,不自动保存或恢复未提交草稿;退出登录、切换账号或切换游戏时清空个人评价、草稿与旧错误,旧请求的迟到响应不得写入新上下文。
|
||||
- 评论纯文本展示并保留换行,不执行 HTML、不解析 Markdown;正文与上方作者/评分行之间保留 8px 间距。移动端评分控件可换行,评论与按钮不被底部导航遮挡;控件支持键盘选择并提供可访问名称。
|
||||
- 评分摘要、公共列表和个人评价分别承接加载/失败;评价区域故障不阻断已有游戏资料和游玩动作。摘要加载失败显示重试状态,不能误显示“暂无评分”。
|
||||
- 优先复用共享动作按钮、状态提示、空态、输入与分页表现;存在跨页面重复时再抽取共享表现组件,领域规则仍留在后端。
|
||||
|
||||
### 公共评价列表与分页
|
||||
|
||||
- 游客和登录用户都能读取公开游戏的评价;列表展示作者公开昵称/头像、分数、评论、首次提交时间,编辑后标明修改时间。
|
||||
- 只评分的评价仍进入列表与总数,不制造“无评论”正文;**不排除当前用户**。自己的评价可以在个人区域与公共列表各出现一次,公共列表内部只能出现一条。
|
||||
- 使用服务端页码分页,`page` 从 1 开始,`pageSize` 默认 20、允许 1–50;页面提供上一页、下一页、当前页及总页数。前端不能一次取全后自行分页。
|
||||
- 默认按 `createdAt` 倒序,同时间按稳定评价标识倒序;编辑不改变排序位置,不新增排序/筛选选项。
|
||||
- 无记录时第一页返回空列表、`total=0`、`totalPages=0`,页面显示真实空态并禁用翻页;超出末页返回空列表,不把不存在的页作为服务端错误。
|
||||
- 分页与统计在单次读取的一致快照中返回;不同翻页请求之间允许反映新提交,不承诺跨请求冻结整份评价集合。
|
||||
|
||||
### 数据、身份与持久化边界
|
||||
|
||||
游戏评价私有表 `game_distribution_review` 保存以下必要字段:
|
||||
|
||||
| 逻辑字段 | 约束 |
|
||||
| --- | --- |
|
||||
| `reviewId` | 稳定唯一标识;通过 `gameId + userId` 的无歧义组合标识保证每对游戏/账号只占一个主键 |
|
||||
| `gameId` | 已存在的稳定游戏身份;建立按游戏查询索引 |
|
||||
| `userId` | 仅来自平台认证主体,不接受请求指定或转移归属 |
|
||||
| `score` | 整数 1–10 |
|
||||
| `comment` | 归一化后的纯文本,最多 4000 Unicode 码点,可为空 |
|
||||
| `createdAt` | 首次保存时间,修改时不变 |
|
||||
| `updatedAt` | 最近一次实际内容修改时间 |
|
||||
|
||||
- 游戏必须与当前公开详情采用相同可见性判定,存在已公开可玩版本;未公开、作者下架或管理员封禁时,评价公开读取、个人读取和提交均返回 404。已有评价保留,游戏重新公开后可继续读取和编辑。
|
||||
- `api-server` 先认证并取得用户 ID;SpacetimeDB 入口继续校验现有受信运行服务身份。数据库上下文中的调用者是 API 服务身份,不能把它误当成网页用户。
|
||||
- 在同一数据库事务里检查游戏可见性并按唯一主键创建或更新;不得用 HTTP 层“先查是否存在,再生成随机 ID 新建”保证唯一性。并发首次提交、双击与重试最终都只有一条记录。
|
||||
- 对同一条评价的并发修改采用后提交成功的事务覆盖,避免为该低风险编辑引入新版本锁或操作账本;重复提交相同内容返回已有记录,不变更人数和创建时间,也不制造修改时间。
|
||||
- 公共作者资料由现有账号公开投影取得,不能由评论请求伪造昵称或头像;私有表不直接对浏览器订阅公开。日志不记录评论正文、Token 或私有账号字段。
|
||||
- 新增表,不删除、改名、重排或修改现有游戏/版本字段;同步迁移登记、表目录、生成绑定与 schema 检查。现有评价数据不存在,无需旧数据回填;已有游戏无评价时自然返回空列表与“暂无评分”。
|
||||
- 基础评价功能由新增评价接口提供评分摘要,不要求旧客户端或原游戏目录、详情 DTO 增加必填字段;广场评分展示对公开游戏投影的增量约束见下方独立合同。本次不扩展 `/api/external/v1`,不改外部 OpenAPI。
|
||||
- 规则与校验进入 `module-game-distribution`,表与事务进入 `spacetime-module`,读取经 `spacetime-client` facade,HTTP/鉴权进入现有 `api-server` 分发路由,DTO 同步 `shared-contracts` / `packages/shared`;网站只持有展示和编辑草稿。
|
||||
|
||||
### HTTP 与 DTO
|
||||
|
||||
以下接口已实现,路径以 `/api/game-distribution` 为前缀,返回沿用平台 `ok/data/error/meta` envelope。用户评价与既有后台游戏发行“审核”是不同对象,`/admin/api/game-distribution/reviews` 继续只表示发行审核队列。
|
||||
|
||||
| 方法与路径 | 身份 | 请求与成功响应 |
|
||||
| --- | --- | --- |
|
||||
| `GET /games/{gameId}/reviews?page=1&pageSize=20` | 游客可读 | `data={reviews,page,pageSize,total,totalPages,ratingSummary}`;公共作者资料随每条评价返回 |
|
||||
| `GET /games/{gameId}/my-review` | 登录用户 | `data={review}`;未评价为 `review:null`,不返回 404 |
|
||||
| `PUT /games/{gameId}/my-review` | 登录用户 | `{score,comment}`;`comment` 可省略,按空字符串保存;首次保存与修改均返回 `200`、`data={review,ratingSummary}` |
|
||||
|
||||
- `review` 字段:`id`、`gameId`、`author:{id,name,avatarUrl}`、`score`、`comment`、`createdAt`、`updatedAt`;`avatarUrl` 可为空。个人与公共评价使用同一展示形状,时间为统一 UTC 格式。
|
||||
- `ratingSummary={averageScore:number|null,ratingCount:number}`;公共列表 `total` 等于 `ratingCount`。`totalPages` 由总数与实际 `pageSize` 得出;均分与人数覆盖该游戏全部评价,不只覆盖当前页。
|
||||
- PUT 是“保存我的唯一评价”,不能指定目标用户或改他人记录;首次创建和更新不拆成不同接口,不接收任意 `reviewId` 定位写入。
|
||||
- `400` 表示 JSON/字段类型、非整数分数或分页参数错误;`401` 表示未登录/认证失效;`404` 表示游戏不可公开访问;`422` 表示整数评分越界或评论超长;复用现有限流的 `429` 及可读 `5xx` 失败。
|
||||
- 网络中断或响应丢失时保存结果可能未知,前端不能宣布“已提交”或删除原评价;保留草稿并可通过个人读取核对结果后重试。PUT 重试不会追加第二条记录。
|
||||
- 公共与个人响应均不缓存过期的评价/评分摘要,使用 `Cache-Control: no-store`;不混入发布灰度或扩大现有发行沙箱权限。
|
||||
|
||||
### 验收标准与证据
|
||||
|
||||
| 条款 | 必须获得的证据 |
|
||||
| --- | --- |
|
||||
| 首次评分与可选评论 | 1 分、10 分和空评论成功;未选择评分、0/11/非整数被拒,后端没有无效写入 |
|
||||
| 字数与输入 | 中文、英文、emoji、空格与换行按统一口径计数;4000 成功、4001 拒绝,字数提示实时可见,粘贴/中文输入法不绕过上限 |
|
||||
| 唯一与并发 | 同一用户同游戏的双击、重试与并发首次保存后仍只有一条;不同用户、不同游戏各自独立 |
|
||||
| 修改与取消 | 已评价先显示个人卡片;编辑完整预填,取消保留旧值,提交更新同一记录;提交中评分、评论和操作按钮均禁用,失败恢复编辑且不丢草稿;首次取消不落库 |
|
||||
| 公共分页与重复展示 | 至少 21 条真实评价验证翻页/总数/顺序/空态;自己的评价在个人区和所属公共页各出现一次;空评论评价可见,编辑不换位 |
|
||||
| 均分与人数 | 无记录为 `null/0`;仅评分计人数;改分只改变均分、不加人数;改评论不改变均分;统计基于所有页 |
|
||||
| 身份与可见性 | 匿名读成功、写/读个人为 401;伪造用户不能修改他人;下架与封禁后读写 404、恢复公开后记录仍在 |
|
||||
| 生命周期与故障 | 切换账号/游戏后旧响应不覆盖新状态;保存成功后旧读取不覆盖新记录与摘要,快速翻页/刷新只接受各区域最新读取结果;个人读取失败不误展首次表单,摘要失败不误显示无评分;评价失败不阻断游玩 |
|
||||
| 页面与工程 | 桌面/移动真实浏览器可操作,评分可键盘选择;相关前后端定向测试、网站类型检查、真实 SpacetimeDB/API smoke、DTO/schema/绑定、编码/文档索引/diff 检查 |
|
||||
|
||||
实现已覆盖本节合同,按独立里程碑进行本地验收;具体证据如下。生产发布与用户验收仍单独确认,不能把本地验证当成上线证据。
|
||||
|
||||
| 证据 | 当前结果 |
|
||||
| --- | --- |
|
||||
| 前端交互与 API client | 游戏详情/发布/作者页等相关 Vitest 共 77 项通过,其中新增评价交互 10 项;覆盖编辑取消、空评论、码点上限、提交冻结、失败保留、保存后旧读取、快速分页与账号/游戏切换 |
|
||||
| 领域与 HTTP/DTO | `module-game-distribution` 18 项、`api-server game_distribution` 31 项、共享 DTO 4 项通过;SpacetimeDB module 编译与 wasm 构建通过。module 的 `game_distribution` 测试筛选为 0 项,事务行为由真实数据库验收补充 |
|
||||
| 工程契约 | 网站 typecheck 与游戏组件测试定向严格类型检查、定向 ESLint、DTO parity(20 组)、2.8.3 绑定生成/schema guard(86 表)、DDD/runtime access、共享生成契约、编码/文档索引/diff 检查通过 |
|
||||
| 真实数据库/API | 使用本机隔离 `genarrative-reviews-e2e` 数据库,`E2E_REVIEWS_DATABASE=genarrative-reviews-e2e node scripts/check-game-distribution-ratings-e2e.mjs` 49 项通过;含 21 个账号/评价、六个并发首次保存、唯一性/同内容重试、1/10/空评论、码点与空白 4000/4001、换行归一化、身份伪造、分页/全量均分、no-store、下架/封禁/恢复公开与新版本保留。使用 `npm run dev:api-server` 拉起 API,健康检查通过;fixture 经现有受信 procedure 建立公开游戏,不上传 OSS,不替代发行资源/游玩验收 |
|
||||
| 浏览器 | 本地真实 Chromium:1280×900 桌面与 375×812 移动视口通过游客浏览、登录、自己的评价在个人区/公共第二页各展示一次、编辑预填、取消恢复、方向键评分、提交收起、页码保留、重载回读、移动端空评论/1 分提交。4001 个 emoji 输入显示 `4001/4000` 并禁用提交,评分控件换行且输入/操作按钮不被底部导航遮挡;评论中的 HTML/Markdown 按纯文本显示。桌面与移动截图保存在本地忽略目录供复核 |
|
||||
| 验证限制 | 未使用物理手机或真实系统中文输入法;组合输入事件、超限和迟到响应/故障路径由定向交互测试覆盖。未重跑与评价无关的完整测试套件 |
|
||||
| 发布边界 | 未部署 dev 或 production;未执行原游戏发行生产验收 |
|
||||
|
||||
## 后台游戏评价管理合同
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Version | 1.0 |
|
||||
| Status | implemented(本地验证通过,待用户验收;未部署) |
|
||||
| Date | 2026-10-01 |
|
||||
| 交付目标 | 管理员查找游戏评价并隐藏、恢复或删除;公共列表和评分统计同步遵循管理状态,用户仍可编辑自己的评价 |
|
||||
| 入口 | 后台“游戏评价”页,以及“游戏管理”的“查看评价”入口 |
|
||||
| 里程碑 | [后台游戏评价管理](./project-memory/plans/【里程碑】后台游戏评价管理-2026-10-01.md) |
|
||||
|
||||
### 范围与已确认规则
|
||||
|
||||
- 评价是评分与可选评论组成的整条记录,包含仅评分记录;管理员只读分数与正文,不能修改两者。
|
||||
- 隐藏整条评价,公共列表不展示,平均分、评分人数和公共分页总数均不计算该记录;恢复后重新参与。
|
||||
- 用户自己的评价保留,固定标记“已被管理员隐藏”,仍可编辑;编辑不自动解除隐藏,只有管理员可恢复。
|
||||
- 删除物理移除评价,不支持恢复;用户再次读取自己的评价时得到 `review:null`,可重新提交,仍是一账号一游戏最多一条。
|
||||
- 隐藏和删除必须填写原因,恢复不要求填写;原因只在后台展示,不返回普通用户。用户提示不拼接管理原因。
|
||||
- 保留管理员、时间、原因和操作目标的持久操作记录,删除后仍可追查;不复制完整评论正文,不提供删除内容恢复。
|
||||
- 首版只增加下述四类筛选、分页、详情、单条操作和详情内操作记录。不增加评分范围/时间范围筛选、批量处理、导出、举报、自动审核、申诉、通知、回复或点赞;不扩展 AGC、目录评分卡片或外部 API。
|
||||
- 基础评价合同中“评价删除/审核不在范围内”仅指原基础功能。本节新增后台管理范围,不引入评价发布前审批。
|
||||
|
||||
### 后台入口、查找与展示
|
||||
|
||||
- 新增 `#game-reviews`,独立 Tab 权限为 `game-reviews`。Owner 沿用全部权限;member 由现有账号管理授权。所有读取和写入接口都校验该权限。
|
||||
- “游戏管理”仅对有评价管理权限的管理员提供“查看评价”,跳转 `#game-reviews?gameId=<编码后的游戏ID>` 并自动查询;首次进入与 hash 参数变化都要生效。
|
||||
- 游戏筛选:输入名称搜索并选择游戏,也可粘贴游戏 ID 精确选择。搜索覆盖已公开、未公开、作者下架和管理员暂停的游戏,游戏选择后按 ID 查询评价。
|
||||
- 用户筛选:输入评价用户 ID 精确匹配,不按昵称模糊匹配;评论关键词:trim 后做纯文本包含匹配,区分大小写,不执行正则,空条件不限制,空评论不命中非空关键词。
|
||||
- 状态筛选:全部/公开/已隐藏,默认全部;公开指评价未被隐藏,不代表所属游戏可公开访问。各条件以 AND 组合。
|
||||
- 提供查询、重置、刷新;查询和重置回第一页,重置清空筛选并恢复“全部”。刷新保留当前条件与页码。
|
||||
- 服务端先对完整数据筛选,再按首次提交时间倒序、同时间按评价 ID 倒序排序分页;编辑、隐藏和恢复不改变原顺序。默认每页 20 条,`page>=1`、`pageSize=1–50`;返回总条数和总页数,空结果为 0 页并禁用翻页。
|
||||
- 列表列为游戏名称/ID、评价用户昵称/ID、评分、评论摘要、状态、提交时间、修改时间和操作。空评论在后台显示“仅评分”;摘要最多 120 Unicode 码点,截断时加省略号。
|
||||
- “查看详情”使用现有弹窗,展示完整纯文本评论、分数、游戏/用户/评价 ID、创建和修改时间,并展示该次评价的操作记录。名称与头像关联现有游戏/账号信息,不复制到评价表。
|
||||
- 评价表格的评论与操作原因保留换行,清除单元格内段落的默认上下外边距,与其他列顶对齐。
|
||||
- 行内提供“隐藏”或“恢复”和“删除”。隐藏、删除通过原因输入及确认弹窗提交;原因 trim 后必须为 1–4000 Unicode 码点,不静默截断;恢复沿用现有后台确认交互。删除明确提示“删除后不可恢复,用户可以重新评价”。
|
||||
- 请求期间禁用对应操作,失败保留原因草稿并显示错误,不先移除记录;成功刷新当前筛选。删除或状态过滤导致当前页越界时回到最后一页,零记录回第 1 页并显示空态。详情打开期间切换目标,不接受旧目标的迟到响应覆盖。
|
||||
- 管理列表/详情不受游戏公开可见性限制。移动端表格横向滚动、弹窗内正文可滚动,操作按钮可达。
|
||||
|
||||
### 网站读取与统计变更
|
||||
|
||||
- 公共评价读取及保存后的摘要只计算 `is_hidden=false` 的记录。统计与公共分页在同一事务快照内计算,公共 `total=ratingCount`,不建立统计缓存或统计表。
|
||||
- 所有记录被隐藏/删除时,网站显示“暂无评分”“暂无评价”;个人隐藏评价仍展示分数、正文、编辑按钮和“已被管理员隐藏”。
|
||||
- 个人 GET/PUT 评价投影追加 `isHidden:boolean`,现有字段不删不改;公共列表只返回未隐藏记录,也可带同一字段(恒为 false)。管理原因和管理员身份不进入普通用户 DTO。
|
||||
- 编辑隐藏评价保留隐藏状态,保存后仍不计入公共摘要;仅隐藏/恢复不修改评价 `createdAt` 或 `updatedAt`,后者继续只表示用户实际修改内容的时间。
|
||||
- 管理动作完成后的下一次读取或刷新反映新状态;不新增推送、轮询或自动通知。用户在删除后保存仍按现有唯一 PUT 语义创建一条新评价,不增加“禁止再次评价”的记录。
|
||||
- 游戏下架/封禁时,网站个人/公共读写仍按现有规则 404;后台仍可管理。游戏恢复公开不会自动解除评价隐藏。
|
||||
|
||||
### 数据、事务、重试与权限
|
||||
|
||||
- 现有私有评价表末尾追加 `is_hidden:bool`,明确默认值 false;旧评价自动视为未隐藏,不改主键、已有字段顺序或类型。保留游戏索引,新增用户 ID 索引。
|
||||
- 新增私有 `game_distribution_review_moderation_log`:`operation_id` 主键、`review_id`、`game_id`、`user_id`、`review_created_at`、`action`(hide/restore/delete)、`admin_user_id`、`reason`(可空)、`created_at`。按 `review_id` 建索引,查看历史时同时匹配 `review_created_at`,避免混合删除前后两次评价。
|
||||
- `operation_id` 使用管理员身份与 `Idempotency-Key` 的无歧义组合,重复 key 必须核对目标、动作、目标创建时间和规范化原因;不同请求复用 key 返回 409。同 key 同请求返回已完成操作,不能重新执行,尤其不能删除后来重新提交的评价。
|
||||
- 每次管理请求带目标 `expectedCreatedAt`,事务核对评价当前创建时间;旧页面指向已删除后重建记录时返回 409,要求刷新。目标不存在且没有同请求成功记录时返回 404。
|
||||
- 事务内原子完成身份校验、重试核对、目标校验、状态变更/物理删除及操作记录插入。已隐藏再隐藏或已公开再恢复不改变评价,但新的操作 key 仍记录本次管理请求,确保该次请求重试不会在状态后来改变后重新执行;同 key 重试不重复记录。
|
||||
- API 从管理员会话获取身份,不能接受请求指定管理员;数据库 procedure 只允许现有受信 API 服务身份。用户 PUT 只修改内容,不能设置或重置隐藏状态。用户编辑与管理并发由数据库事务顺序处理,不引入内容版本锁。
|
||||
- 操作记录独立于评价行存续,删除评价不级联删除记录;本次不新增自动清理任务或单独日志管理页面。历史仍可通过已有受权限保护的表查询查看,详情只展示当前评价实例的记录。
|
||||
- 分层沿用 `module-game-distribution` 规则、`spacetime-module` 表/事务、`spacetime-client` facade、`api-server` HTTP/权限、共享 DTO、admin-web/网站表现;不新增直接访问私有表的前端路径。
|
||||
|
||||
### HTTP 与 DTO
|
||||
|
||||
新增路径以 `/admin/api/game-distribution` 为前缀,沿用平台 envelope 和 `Cache-Control: no-store`。原 `/reviews` 保持发行审核队列含义;评价 ID 在路径中按组件编码。
|
||||
|
||||
| 方法与路径 | 请求 | 成功 `data` |
|
||||
| --- | --- | --- |
|
||||
| `GET /user-review-games` | `query` 可空,非空先精确匹配 ID,同时对游戏名称做包含查询;名称匹配区分大小写;`page=1&pageSize=20` | `{games:[{gameId,title,status}],page,pageSize,total,totalPages}`,按创建时间/ID 倒序,分页范围同评价列表 |
|
||||
| `GET /user-reviews` | 可选 `gameId,userId,keyword,status=all\|visible\|hidden`;`page=1&pageSize=20` | `{reviews,page,pageSize,total,totalPages}` |
|
||||
| `GET /user-reviews/{reviewId}` | 无 body | `{review,operations}`,操作记录按时间/操作 ID 倒序,只包含当前创建时间对应的记录 |
|
||||
| `POST /user-reviews/{reviewId}/moderation` | `Idempotency-Key`(沿用已有 1–128 字符限制);`{action:"hide"\|"restore"\|"delete",expectedCreatedAt,reason?}`;恢复省略原因 | `{review,operation,replayed}`;删除 `review=null` |
|
||||
|
||||
- 后台 `review` 为 `{id,gameId,game:{title,status},author:{id,name,avatarUrl},score,comment,isHidden,createdAt,updatedAt}`。时间使用统一 UTC 格式;游戏/作者资料来自关联读取,关联缺失时保留 ID、名称显示占位,不影响定位和处理。
|
||||
- `operation` 为 `{id,reviewId,gameId,userId,reviewCreatedAt,action,adminUserId,reason,createdAt}`;恢复 `reason=null`。`replayed` 表示同一次成功操作重放,新 key 首次成功为 false。
|
||||
- 400 为字段类型、枚举、分页、时间格式或幂等头错误;401 未认证;403 无页面权限;404 目标评价不存在;409 目标重建或同 key 请求不一致;422 原因为空/超长。错误不产生部分写入。网络失败保留草稿,以原 key 重试。
|
||||
- 操作响应的 `review` 是该次操作事务的结果;重放返回当前评价实例的状态(原实例已删除则为 null),不能把后续新评价当作旧操作结果。管理列表按后续读取更新。
|
||||
|
||||
### 迁移与验收
|
||||
|
||||
- 日常评价回归与旧库迁移验收独立运行。先用 `check-game-distribution-ratings-e2e.mjs` 在显式指定的本机 `reviews-e2e` 隔离库生成新 fixture,再运行 `check-game-distribution-review-moderation-e2e.mjs check`;后台凭据通过该脚本的 `prepare` 模式提前准备,按现有后台 owner 配置启动 API。`check` 以本次读取的评价为基线,不依赖旧快照或迁移证据;`snapshot` / `migration` 仅用于下面的旧库升级验收。非法输入的完整矩阵保留在领域/API 测试,真实 HTTP 回归保留代表性拒绝、无写入及权限、统计、幂等与删除重建验证。
|
||||
- 将隐藏字段追加在已有 Rust 表结构末尾并设置明确默认值 false,按 SpacetimeDB 2.8.3 兼容追加规则发布;新增管理记录表为空。不手工回填或清空旧评价。
|
||||
- 同步 `migration.rs`、后端表目录、生成绑定和 schema 检查。上线前用隔离数据库验证旧 schema 的评价升级后内容、唯一性、时间保留且全部公开;不把新数据库空表测试当成存量迁移证据。
|
||||
- 回滚应用时保留新增字段和管理记录;旧后端不认识隐藏状态,会重新暴露已隐藏评价,因此产生隐藏记录后禁止直接回退到未过滤隐藏记录的旧读取实现,采用兼容修复。
|
||||
|
||||
| 条款 | 必须获得的证据 | 当前状态 |
|
||||
| --- | --- | --- |
|
||||
| 查找与分页 | 超过 20 条记录,四类筛选组合、跨页关键词、游戏名称/ID选择、入口预填、重置、空态、越界页恢复 | 定向交互测试及真实 HTTP 通过;浏览器确认四条件 AND、名称选择、入口预填、21 条跨页和刷新保留页码 |
|
||||
| 管理操作 | 隐藏/删除必填原因、恢复无原因;取消无写入,失败保留草稿,完整正文只读、分数不可编辑 | 自动化及真实 HTTP 通过;浏览器确认隐藏空原因报错、取消、提交、恢复、删除警示和取消,长评论详情只读 |
|
||||
| 统计与个人状态 | 隐藏后列表/人数/均分同步排除,全部隐藏为空态;自己显示精确提示,编辑仍隐藏,恢复重新计入 | 真实 HTTP 通过;浏览器确认隐藏后 21→20 人、均分更新,恢复后 20→21 人,个人提示、编辑预填及保存保持隐藏 |
|
||||
| 删除与重试 | 物理删除、可重新提交唯一评价;旧目标/旧 key 重试不能删新评价,同 key 同请求不重复日志、不同请求 409 | 真实数据库/HTTP 通过,覆盖删除后重建、旧目标 409、旧 key 重放及同状态操作重试;浏览器未执行永久删除 |
|
||||
| 权限与事务 | 401/403、伪造管理员/隐藏状态无效;私有记录不泄漏原因,失败不部分写入,下架游戏后台可管 | API/领域测试及真实 HTTP 通过;额外伪造管理员请求确认操作人仍来自认证会话 |
|
||||
| 存量与工程 | 旧数据库无损升级、schema/绑定/DTO、定向 Rust/Vitest/typecheck,真实数据库/API及桌面/移动浏览器 | 21 条旧评价升级无损且默认公开;87 表 schema、生成绑定、31 组 DTO 校验通过;桌面及 375×812 视口确认筛选、表格、操作弹窗和长正文滚动 |
|
||||
|
||||
2026-10-01 本地验证:领域 21 项、API game_distribution 34 项、独立权限映射 1 项、基础共享 DTO 4 项及后台 DTO 1 项测试通过;最终前端回归为 7 个文件、74 项通过,含后台新评价页 8 项(提交途中切换游戏不覆盖新上下文)、网站评价 11 项,以及相关路由/client/游戏管理和发行审核。Spacetime module/client cargo check、Rust 格式、admin-web/网站类型检查、定向 ESLint、admin-web 构建、DDD/runtime 边界、文档索引、编码和 diff 检查通过。
|
||||
|
||||
真实运行时通过项目 dev 脚本启动隔离数据库与 API(`/healthz` 正常);旧 wasm 带 21 条评价兼容升级,逐项核对 ID、正文、评分、创建/修改时间均保持且隐藏默认 false。`scripts/check-game-distribution-review-moderation-e2e.mjs` 完成 57 项 HTTP 检查,另验证伪造管理员身份不能覆盖认证操作人。浏览器验证仅操作本地合成账号与游戏,没有生产数据写入或 OSS 上传。
|
||||
|
||||
回归脚本精简后,在现有本机隔离服务上以新 fixture 复跑:网站 40 项、后台 51 项 HTTP 检查通过,后台 `prepare` / `check` 不再依赖旧库验收产物。网站交互 11 项、评价领域 5 项、Spacetime module 编译、网站类型检查及定向格式/静态检查通过。此轮未重新发布 module 或重跑旧库迁移、浏览器验收;HTTP 结果用于验证脚本回归流程,新的内部读取简化由编译和源码条件对照验证。
|
||||
|
||||
未验证:生产部署、真实移动设备/系统输入法、无关全量测试和真实游戏包游玩。永久删除由真实 HTTP 验证,浏览器仅验证提示与取消;网络失败草稿由交互自动化验证,未在浏览器断网复现。用户验收尚未完成,计划和里程碑保留;不据此宣称已上线。
|
||||
|
||||
## 游戏广场评分展示合同
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Version | 1.0 |
|
||||
| Status | implemented(本地验证通过,待用户验收;未部署) |
|
||||
| Date | 2026-10-01 |
|
||||
| 入口 | 网站 `/games` 游戏广场卡片 |
|
||||
| 交付目标 | 游客与登录用户在游戏卡片查看真实平均分和评分人数,统计与详情评价及后台管理口径一致 |
|
||||
| 里程碑 | [游戏广场评分展示](./project-memory/plans/【里程碑】游戏广场评分展示-2026-10-01.md) |
|
||||
|
||||
### 范围与展示规则
|
||||
|
||||
- 在广场卡片简介下方、分类和作者信息上方增加评分摘要,保留标题、游玩次数、封面、卡片点击、现有排序及筛选。移动端摘要允许换行,长标题和较大人数不能撑宽卡片。
|
||||
- 有评分时显示如 `8.2/10 · 26 人评分`,均分固定一位小数;不把 10 分制转换成 5 星制。无人评分时显示“暂无评分”。
|
||||
- 摘要来自列表响应,不为每张卡片请求公共评价页或个人评价;详情仍由现有评价读取/保存结果驱动,保存后可即时更新详情摘要。
|
||||
- 广场返回时重新读取列表,保留既有筛选与滚动位置;用户改分或管理员隐藏、恢复、删除后的下一次成功读取反映新统计。不增加推送、轮询或后台管理通知。
|
||||
- 本增量只覆盖网站公开评分摘要,不增加评分排序、排行、推荐、评论预览、卡片评分输入、AGC 评价或作者管理页评分展示。
|
||||
|
||||
### 统计、可见性与失败行为
|
||||
|
||||
- 统计沿用已有有效评价口径:只计算未隐藏的评价,包含空评论、当前用户和作者的评价;每账号每游戏最多计一次,编辑不增加人数,新发行版本不清空评价。
|
||||
- 游戏列表仍只返回满足现有公开可见性与有效公开版本规则的游戏;评分不放宽下架、封禁或版本可见性。游戏资料和摘要在同一次数据库读取事务内形成一致快照。
|
||||
- 无有效评价时严格返回 `averageScore=null`、`ratingCount=0`;全部评价被隐藏或删除后同样返回该空态。均分由后端对该游戏全部有效评价计算,不由前端分页或累计推导。
|
||||
- 列表读取失败沿用整页错误与重新加载入口,不能用虚构零分或“暂无评分”代替失败。兼容期响应缺少评分摘要时,卡片显示“评分暂不可用”,保留游戏资料和打开详情能力;只有明确的 `null/0` 摘要表示无人评分。
|
||||
- 公共摘要只包含均分与人数,不返回评论正文、管理原因、管理员身份或个人评价状态;游客读取不要求登录或发布权限。
|
||||
|
||||
### API、兼容与数据边界
|
||||
|
||||
| 方法与路径 | 增量响应合同 |
|
||||
| --- | --- |
|
||||
| `GET /api/game-distribution/games` | `data.games[]` 每项保证增加 `ratingSummary:{averageScore:number|null,ratingCount:number}`,原字段和 envelope 保留 |
|
||||
| `GET /api/game-distribution/games/{gameId}` | 与列表共用公开投影,保证返回同形状 `ratingSummary`;详情评价接口仍提供提交/刷新时的权威摘要 |
|
||||
|
||||
- 公开列表和详情使用 `Cache-Control: no-store`,避免浏览器或代理复用过期摘要;既有评价接口的 no-store 行为保留。
|
||||
- 共用游戏 DTO 中 `ratingSummary` 为可选字段,以允许作者侧响应和旧响应省略;当前公开列表/详情必须返回对象。字段缺失与 `averageScore=null,ratingCount=0` 具有不同含义,旧客户端可忽略新增字段。
|
||||
- 同步 Rust/TypeScript 共享 DTO、数据库 procedure 返回类型、后端 facade/mapper、生成绑定和响应字段契约检查。摘要在读取时计算,不增加持久化统计表、字段、缓存、回填或重算任务。
|
||||
- 公开投影返回类型变化需要数据库 module 与后端绑定配套更新;先验证配套后端/module,再更新网站。现有持久化表及评价数据不变,无破坏性迁移。
|
||||
- 不扩展 `/api/external/v1`,不改变外部 OpenAPI、后台管理接口或评价写入语义。
|
||||
|
||||
### 验收与当前证据
|
||||
|
||||
| 条款 | 验收方式 | 当前证据 |
|
||||
| --- | --- | --- |
|
||||
| 展示与字段 | 现有页面测试补正常评分/零评价/缺摘要三种情况,现有公开响应/DTO 测试核对新增字段 | 前端两文件 36 项、API 定向 34 项通过;仅新增评分文案参数化断言并扩展已有响应测试 |
|
||||
| 统计口径 | 复用有效评价统计及管理测试;用两款游戏和少量评价做一次列表/详情/评价摘要对照,确认隐藏过滤与统计不串联 | 领域 summary 2 项通过;隔离库两款游戏、两条空评论评价,`8/2` 隐藏一条后为 `7/1`,另一游戏始终 `null/0`,三接口摘要一致 |
|
||||
| 页面与原行为 | 桌面及 375px 移动检查评分行和详情返回后的更新;可见性、列表失败、筛选与滚动恢复沿用现有测试 | 1280px/375px 浏览器卡片无横向溢出;详情期间经 API 改分,返回广场由 `7.0/1` 更新为 `6.0/1`;既有目录/详情测试通过 |
|
||||
| 工程与兼容 | 受影响代码定向测试/typecheck、DTO/绑定/schema、编码/文档索引/diff 检查及上述一次隔离 API smoke | Rust shared-contracts 4 项、module/client 编译、TypeScript typecheck、受影响文件静态/格式检查及 DTO/绑定/schema/编码/文档索引/diff 检查通过;真实列表/详情 no-store 通过 |
|
||||
|
||||
测试按本增量风险控制:只补新展示与摘要契约的必要断言,复用已有评价、管理和目录测试;不新增框架、专用 E2E 脚本、大批量分页数据、故障注入或重复的多层映射测试。既有证据可支撑未改动的规则,但仍须验证本次公开投影和页面接入。
|
||||
|
||||
2026-10-01 按用户后续实现授权完成公开快照、共享契约、后端响应和共享评分文本接入。绑定以仓库固定 SpacetimeDB 2.8.3 生成,持久化表未变;只为列表最终返回游戏聚合评价。隔离库没有既有评分 fixture,本次复用既有 helper 临时创建两个账号、两款游戏和两条评价,未新增仓库 E2E 脚本或完整管理矩阵。
|
||||
|
||||
本地验证通过,尚待用户验收和生产部署。未跑完整仓库测试、完整评价/后台管理 E2E、真实手机、长标题/极大人数浏览器矩阵、浏览器登录改分和实际发行包游玩;返回刷新使用真实 API 改分验证,登录用户公开列表已在 API smoke 对照。
|
||||
|
||||
Reference in New Issue
Block a user