From 49e18316f4f978e14420eecd2a19d8c6b5cfb891 Mon Sep 17 00:00:00 2001 From: Linghong Date: Thu, 1 Oct 2026 11:48:24 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BC=96=E5=86=99=E5=90=8E=E5=8F=B0=E6=B8=B8?= =?UTF-8?q?=E6=88=8F=E8=AF=84=E4=BB=B7=E7=AE=A1=E7=90=86=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 明确评价隐藏、恢复、删除及必填原因规则 补充后台筛选分页、权限与网站统计联动合同 定义隐藏字段、操作记录及存量迁移方案 新增管理里程碑和实施计划并同步文档索引与决策记录 --- docs/README.md | 2 + ...【实施计划】后台游戏评价管理-2026-10-01.md | 48 ++++++++++ .../【里程碑】后台游戏评价管理-2026-10-01.md | 53 ++++++++++ .../shared-memory/decision-log.md | 9 ++ ...„】server-rs与SpacetimeDB数据契约-2026-05-15.md | 12 +++ ...�玩法创作】平台入口与玩法链路-2026-05-15.md | 96 ++++++++++++++++++- 6 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 docs/project-memory/plans/【实施计划】后台游戏评价管理-2026-10-01.md create mode 100644 docs/project-memory/plans/【里程碑】后台游戏评价管理-2026-10-01.md diff --git a/docs/README.md b/docs/README.md index ad78d57c2..bd1606520 100644 --- a/docs/README.md +++ b/docs/README.md @@ -21,6 +21,8 @@ - [当前产品与工程约束](./【项目基线】当前产品与工程约束-2026-05-15.md):现役入口、账号钱包、UI 和后端分层。 - [平台入口与玩法链路](./【玩法创作】平台入口与玩法链路-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 边界及验收要求,未进入编码。 - [外部 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 契约唯一机器可读来源。 diff --git a/docs/project-memory/plans/【实施计划】后台游戏评价管理-2026-10-01.md b/docs/project-memory/plans/【实施计划】后台游戏评价管理-2026-10-01.md new file mode 100644 index 000000000..540e2000c --- /dev/null +++ b/docs/project-memory/plans/【实施计划】后台游戏评价管理-2026-10-01.md @@ -0,0 +1,48 @@ +# 【实施计划】后台游戏评价管理 + +| 字段 | 值 | +| --- | --- | +| Milestone | [后台游戏评价管理](./【里程碑】后台游戏评价管理-2026-10-01.md) | +| Status | draft(待技术方案评审,未开始编码) | +| 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 均未修改,所有功能测试及运行时证据待实现后提供。 diff --git a/docs/project-memory/plans/【里程碑】后台游戏评价管理-2026-10-01.md b/docs/project-memory/plans/【里程碑】后台游戏评价管理-2026-10-01.md new file mode 100644 index 000000000..16ce05610 --- /dev/null +++ b/docs/project-memory/plans/【里程碑】后台游戏评价管理-2026-10-01.md @@ -0,0 +1,53 @@ +# 【里程碑】后台游戏评价管理 + +| 字段 | 值 | +| --- | --- | +| Version | 1.0 | +| Status | proposed(产品规则已确认,技术方案待评审;未实现) | +| 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、管理原因泄漏、原子失败、状态并发、删除后主键复用、全部隐藏和页码越界。 +- 当前仅文档验证,不将既有评价功能证据复用为本里程碑通过记录。 diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 90295562b..9454a20a0 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -1,5 +1,14 @@ # 决策记录 +## 2026-10-01 后台游戏评价管理规则 + +- 用户确认新增管理员隐藏/删除评价,后台不能修改分数或正文;隐藏整条评价并从公共列表、平均分、人数和公共分页总数排除,恢复后重新参与。个人区域固定提示“已被管理员隐藏”,用户仍可编辑但不能自动恢复公开。 +- 隐藏和删除必须填写原因,恢复不要求;原因仅后台展示。删除物理移除,不能恢复,用户可重新评价,唯一规则继续成立。 +- 当前方案范围为游戏名称选择/ID定位、评价用户 ID、评论关键词、状态四类组合筛选,以及分页、详情、单条操作和持久操作记录。不增加批量、导出、举报、自动审核或评分/时间范围筛选。 +- 数据方案:评价表末尾追加默认 false 的 is_hidden;新增私有管理记录,事务保存操作人/原因/时间,以创建时间区分删除后重建记录,同 key 重试不得再次操作新评价。不增加统计缓存。 +- 状态:用户本次要求先写方案,未授权本轮编码/部署;产品规则已确认,接口/schema 技术方案待评审,既有评价测试不代表管理功能完成。 +- 权威入口:[后台游戏评价管理合同](../../【玩法创作】平台入口与玩法链路-2026-05-15.md#后台游戏评价管理合同);活动[里程碑](../plans/【里程碑】后台游戏评价管理-2026-10-01.md)与[实施计划](../plans/【实施计划】后台游戏评价管理-2026-10-01.md)。 + ## 2026-09-30 网站游戏评价范围与状态边界 - 已确认需求:网站游戏详情支持每账号每游戏唯一一条 1–10 分评分和可空的评论(最多 4000 字符),可修改自己的评价;个人区默认展示已有评价并提供编辑预填,公共列表分页且不排除自己。 diff --git a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md index 415bc5e99..e2954fe90 100644 --- a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md +++ b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md @@ -468,8 +468,20 @@ Responses 的终态载荷既是工具调用的恢复源,也是正文的恢复 - 事务内复用公开详情可见性,创建或更新唯一评价;重复相同内容不改时间。公共分页按创建时间和主键倒序,作者资料从 `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) + +状态:`proposed`,产品规则已确认,技术方案待评审;尚未实现。下述字段/表不属于当前 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`、`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 检查;在存量测试数据库兼容升级,验证 false 默认值及既有记录内容/唯一性/时间不变。部署后若已隐藏评价,不直接回退到未过滤隐藏记录的旧后端。 + ### `game_distribution_version` + - Rust 结构体:`GameDistributionVersion` - 源码:`server-rs/crates/spacetime-module/src/game_distribution.rs` - 用途:不可变发行版本与真实包确认事实。创建后冻结 `package_sha256`、字节数、文件数、根入口和版本号;后续只推进上传、校验、审核、公开、撤回状态,并记录私有对象键、文件清单、入口 URL、审核者和阶段时间。 diff --git a/docs/【玩法创作】平台入口与玩法链路-2026-05-15.md b/docs/【玩法创作】平台入口与玩法链路-2026-05-15.md index 562c8338c..6f167b9c5 100644 --- a/docs/【玩法创作】平台入口与玩法链路-2026-05-15.md +++ b/docs/【玩法创作】平台入口与玩法链路-2026-05-15.md @@ -1,8 +1,8 @@ # 平台入口与玩法链路 -> 更新时间:`2026-09-30` +> 更新时间:`2026-10-01` > -> 本文描述现役主站平台壳、图片画布编辑器和游戏分发合同;拟新增能力在各节单独标明状态。“网站游戏评分与评价合同”是待实现方案,不代表现役能力。当前实现以路由树、shared-contracts 和 SpacetimeDB module / bindings 为准。 +> 本文描述现役主站平台壳、图片画布编辑器和游戏分发合同;拟新增能力在各节单独标明状态。网站游戏评分与评价已实现并通过本地验证,后台游戏评价管理尚未实现。当前实现以路由树、shared-contracts 和 SpacetimeDB module / bindings 为准。 ## 现役平台入口 @@ -179,6 +179,8 @@ ## 网站游戏评分与评价合同 +本节记录已实现的基础评价合同;后台管理的增量规则见下方“后台游戏评价管理合同”,该增量尚未实现,不把基础功能的验收证据视为管理功能证据。 + | 字段 | 值 | | --- | --- | | Version | 1.0 | @@ -305,3 +307,93 @@ | 浏览器 | 本地真实 Chromium:1280×900 桌面与 375×812 移动视口通过游客浏览、登录、自己的评价在个人区/公共第二页各展示一次、编辑预填、取消恢复、方向键评分、提交收起、页码保留、重载回读、移动端空评论/1 分提交。4001 个 emoji 输入显示 `4001/4000` 并禁用提交,评分控件换行且输入/操作按钮不被底部导航遮挡;评论中的 HTML/Markdown 按纯文本显示。桌面与移动截图保存在本地忽略目录供复核 | | 验证限制 | 未使用物理手机或真实系统中文输入法;组合输入事件、超限和迟到响应/故障路径由定向交互测试覆盖。未重跑与评价无关的完整测试套件 | | 发布边界 | 未部署 dev 或 production;未执行原游戏发行生产验收 | + +## 后台游戏评价管理合同 + +| 字段 | 值 | +| --- | --- | +| Version | 1.0 | +| Status | proposed(产品规则已确认,技术方案待评审;未实现) | +| 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),不能把后续新评价当作旧操作结果。管理列表按后续读取更新。 + +### 迁移与验收 + +- 将隐藏字段追加在已有 Rust 表结构末尾并设置明确默认值 false,按 SpacetimeDB 2.8.3 兼容追加规则发布;新增管理记录表为空。不手工回填或清空旧评价。 +- 同步 `migration.rs`、后端表目录、生成绑定和 schema 检查。上线前用隔离数据库验证旧 schema 的评价升级后内容、唯一性、时间保留且全部公开;不把新数据库空表测试当成存量迁移证据。 +- 回滚应用时保留新增字段和管理记录;旧后端不认识隐藏状态,会重新暴露已隐藏评价,因此产生隐藏记录后禁止直接回退到未过滤隐藏记录的旧读取实现,采用兼容修复。 + +| 条款 | 必须获得的证据 | 当前状态 | +| --- | --- | --- | +| 查找与分页 | 超过 20 条记录,四类筛选组合、跨页关键词、游戏名称/ID选择、入口预填、重置、空态、越界页恢复 | 待实现验证 | +| 管理操作 | 隐藏/删除必填原因、恢复无原因;取消无写入,失败保留草稿,完整正文只读、分数不可编辑 | 待实现验证 | +| 统计与个人状态 | 隐藏后列表/人数/均分同步排除,全部隐藏为空态;自己显示精确提示,编辑仍隐藏,恢复重新计入 | 待实现验证 | +| 删除与重试 | 物理删除、可重新提交唯一评价;旧目标/旧 key 重试不能删新评价,同 key 同请求不重复日志、不同请求 409 | 待实现验证 | +| 权限与事务 | 401/403、伪造管理员/隐藏状态无效;私有记录不泄漏原因,失败不部分写入,下架游戏后台可管 | 待实现验证 | +| 存量与工程 | 旧数据库无损升级、schema/绑定/DTO、定向 Rust/Vitest/typecheck,真实数据库/API及桌面/移动浏览器 | 待实现验证 | + +本次只交付方案文档;既有网站评价的本地验证不代表本节已实现或部署。技术方案与里程碑评审后按单里程碑实施,不扩大首版配套功能。