Merge remote-tracking branch 'origin/master'

# Conflicts:
#	docs/technical/README.md
#	server-rs/crates/spacetime-client/src/module_bindings/mod.rs
This commit is contained in:
2026-05-01 01:14:04 +08:00
601 changed files with 19836 additions and 4468 deletions

View File

@@ -72,7 +72,7 @@
1. 不复用 RPG 的世界、角色、章节、剧情推进结构。
2. 不复用拼图的网格切图、交换、合并块和下一关推荐算法。
3. 不复用大鱼吃小鱼的实时吞噬、实体等级和摇杆移动规则。
4. 不把 Match3D 运行规则写成前端本地真相源。
4. 不把 Match3D 运行规则写成前端本地真相源,但局内即时反馈效果由前端负责呈现
5. 不使用 `server-node` 或 PostgreSQL 作为新增玩法后端。
## 3.3 独立玩法域要求
@@ -110,7 +110,7 @@ Match3D 必须形成独立玩法域,后续技术方案至少需要覆盖:
13. 清空圆形空间中全部物品即胜利。
14. 倒计时结束或备选栏满即失败。
15. 胜利 / 失败后展示结算界面。
16. 点击判定、入槽、消除、失败、胜利必须由后端裁决
16. 点击、入槽、消除、失败、胜利的即时反馈效果由前端先行呈现,后端负责权威确认、状态落库和成绩可信性
---
@@ -128,7 +128,7 @@ Match3D 必须形成独立玩法域,后续技术方案至少需要覆盖:
8. 不做真实物理碰撞结算。
9. 不做必须试玩通关才能发布的门槛。
10. 不把玩法规则说明长文默认写入 UI 面板。
11.前端即时完成规则裁决
11.前端即时反馈当作最终规则真相
12. 不使用 `server-node` 或 PostgreSQL 新增实现。
---
@@ -292,31 +292,36 @@ totalItemCount = clearCount * 3
圆形空间里的物品可以重叠、遮挡、堆叠。
首版使用 2D 逻辑实现遮挡和点击判定
首版使用 2D 逻辑实现遮挡和点击反馈
1. 被完全遮挡的物品不允许点击。
2. 如果物品有局部露出,且露出区域可被点击选中,则允许点击。
3. 具体露出区域判定使用 2D 逻辑的最优方案,不做真实 3D 遮挡
3. 前端基于后端下发的物品层级、位置、半径和可点击快照,执行即时命中检测与选中反馈
4. 后端收到点击意图后做权威确认;如果确认失败,前端必须回滚本次即时反馈。
5. 具体露出区域判定使用 2D 逻辑的最优方案,不做真实 3D 遮挡。
## 8.8 点击入槽
玩家点击通过后,后端裁决该物品可选中
前端播放飞行动画,把物品放入下方备选栏。
玩家点击可见物品后,前端立即播放按压、选中和飞行动画,把物品表现为飞入下方备选栏
飞行动画过程中,物品不再与其他物品产生碰撞。
前端播放即时反馈的同时,必须向后端提交点击意图。后端确认后固化入槽结果;后端拒绝时,前端恢复物品位置和备选栏表现。
## 8.9 备选栏
下方备选栏固定为 `7` 个格子。
1. 每次成功点击后,物品进入备选栏。
2. 备选栏中每出现 `3` 个相同物品 id自动消除并腾出格子。
3. 如果备选栏满且无法消除,则判定关卡失败
1. 每次点击进入即时反馈流程后,前端先把物品表现为进入备选栏。
2. 备选栏中每出现 `3` 个相同物品 id前端立即播放自动消除效果并腾出格子。
3. 后端确认后固化真实备选栏和消除结果;若后端返回状态不一致,前端必须以最新后端快照校正
4. 如果备选栏满且无法消除,前端可以立即展示失败过渡,最终失败状态以后端确认为准。
## 8.10 胜利
圆形空间内全部物品被消除后,播放胜利动画并展示胜利界面
圆形空间内全部物品被消除后,前端立即播放胜利动画。
正式胜利界面、使用时间和成绩记录以后端确认的运行态为准。
胜利结算页至少展示:
@@ -332,6 +337,8 @@ totalItemCount = clearCount * 3
1. 倒计时结束。
2. 备选栏满。
倒计时归零或备选栏满时,前端立即展示失败过渡;正式失败原因和完成进度以后端确认的运行态为准。
失败结算页至少展示:
1. 失败原因。
@@ -378,33 +385,38 @@ totalItemCount = clearCount * 3
1. 创建玩法草稿。
2. 编译运行时初始局面。
3. 生成物品序列与布局。
4. 判断物品是否可点击
5. 处理点击入槽
6. 判断 `3` 个相同物品 id 消除
7. 判断备选栏满失败
8. 判断倒计时结束失败。
9. 判断清空空间胜利
10. 记录成绩所需的基础数据
4. 下发前端即时反馈所需的物品位置、层级、半径、可点击快照和版本号
5. 权威确认玩家点击意图是否合法
6. 权威确认入槽结果
7. 权威确认 `3` 个相同物品 id 消除
8. 权威确认备选栏满失败。
9. 权威确认倒计时结束失败
10. 权威确认清空空间胜利
11. 记录成绩所需的基础数据。
## 10.2 前端职责
前端负责:
前端负责所有游戏过程中需要即时呈现的反馈效果
1. 展示 Agent 创作界面。
2. 展示结果页和基础编辑表单。
3. 上传参考图片。
4. 展示运行态场景、物品、倒计时和备选栏。
5. 发送玩家点击意图
6. 播放点击、飞入、消除、胜利和失败动画
7. 展示结算界面
5. 基于最新后端快照执行 2D 命中检测、悬停、按压和选中反馈
6. 发送玩家点击意图
7. 在等待后端确认期间,先行播放飞入、入槽、三消、腾格、胜利和失败过渡效果
8. 收到后端确认后,把本地表现校正到权威快照。
9. 展示结算界面。
前端不得自行完成规则裁决
前端可以做即时表现预判,但不得把预判结果作为最终规则真相或成绩来源
## 10.3 防作弊要求
首版即按正式版本搭建规则裁决链路。
首版即按正式版本搭建“前端即时反馈 + 后端权威确认”的链路。
前端不可信任本地点击、消除、胜利或成绩结果;所有关键状态必须后端裁决后下发
前端不可信任本地点击、消除、胜利或成绩结果;所有关键状态必须后端确认后的快照为准
为了保证手感,前端可以先行展示操作反馈;为了防作弊,发布成绩、结算状态、消除计数和运行态持久化必须以后端确认为准。
---
@@ -484,6 +496,8 @@ interface Match3DItemSnapshot {
}
```
`Flying` 可以作为前端表现态使用,不要求后端把飞行动画过程逐帧落库。后端只需要确认物品是否从 `InBoard` 进入 `InTray``Cleared`
## 11.5 备选栏快照
```ts
@@ -672,7 +686,7 @@ Agent 每轮优先追问最影响 demo 生成的一个问题。
13. 倒计时结束或备选栏满后失败。
14. 胜利结算展示使用时间。
15. 失败结算展示完成进度和重新开始按钮。
16. 关键规则由后端裁决,前端不本地判定胜负
16. 局内即时反馈由前端先行呈现,关键状态以后端确认快照校正
17. 相关中文文档通过编码检查。
---
@@ -696,14 +710,15 @@ Agent 每轮优先追问最影响 demo 生成的一个问题。
1. 新增 `module-match3d` 规则。
2. 新增 SpacetimeDB 运行态表和 procedure。
3. 实现开始、点击、消除、失败、胜利。
3. 实现开始、点击确认、消除确认、失败确认、胜利确认
## 阶段 D前端运行态
1. 展示圆形空间和 2D 物品。
2. 展示 `7` 格备选栏。
3. 接入点击接口和后端快照。
4.飞入、消除、胜负动画
4.点击命中、飞入、入槽、消除、腾格、胜负过渡等即时反馈
5. 补后端确认失败时的前端回滚和快照校正。
## 阶段 E分发与成绩预留
@@ -715,4 +730,4 @@ Agent 每轮优先追问最影响 demo 生成的一个问题。
## 17. 一句话结论
Match3D 首版不是临时前端 demo而是以“抓大鹅”模板为外壳、以后端规则裁决为真相源、以独立玩法域为工程边界的单局经典消除玩法链路;首轮先跑通题材创作、结果页、试玩、发布和单局清空胜负闭环。
Match3D 首版不是临时前端 demo而是以“抓大鹅”模板为外壳、以前端即时反馈保证手感、以后端权威确认保证规则可信、以独立玩法域为工程边界的单局经典消除玩法链路;首轮先跑通题材创作、结果页、试玩、发布和单局清空胜负闭环。

View File

@@ -0,0 +1,835 @@
# 抓大鹅 Match3D 创作与运行态最小落地技术方案 2026-04-30
## 1. 文档目的
本文件承接 PRD《AI 原生抓大鹅 Match3D 玩法创作工具与玩法系统 PRD》冻结首版 demo 的最小可开发方案。
本轮目标不是先做一个纯前端临时小游戏,而是在当前平台内新增独立 `match3d` 玩法域,跑通下面这条最小主链:
1. 平台创作入口选择“抓大鹅”。
2. Agent 对话确认题材、需要消除次数和难度。
3. 编译 Match3D 草稿。
4. 进入结果页编辑游戏名称、标签和封面图。
5. 发布前试玩,可随时停止并返回修改。
6. 发布作品。
7. 玩家进入单局运行态。
8. 前端即时呈现点击、飞入、入槽、三消、腾格、胜负过渡。
9. 后端权威确认点击、入槽、消除、失败、胜利和成绩。
本文是后续并行开发的工程合同。若实现过程中发现字段、路由、表结构或前后端职责需要变化,必须先更新本文,再进入对应编码分支。
---
## 2. 本轮明确不做
1. 不做多关卡链。
2. 不做排行榜展示。
3. 不做道具逻辑,只预留字段和扩展点。
4. 不做真实 3D 模型和真实 3D 物理遮挡。
5. 不做洗牌、重置、旋转、放大等局内操作。
6. 不做必须试玩通关才能发布。
7. 不做前端本地最终规则真相。
8. 不接入 `server-node` 或 PostgreSQL。
9. 不把 Match3D 挂到 RPG、拼图或大鱼吃小鱼旧命名下。
---
## 3. 分层边界
## 3.1 前端
前端继续使用当前 `React + TypeScript + Vite` 平台壳层,负责所有即时呈现的局内反馈:
1. 创作入口、Agent 工作区、结果页、试玩和运行态 UI。
2. 参考图片上传入口。
3. 运行态圆形空间、2D 物品、倒计时和 `7` 格备选栏展示。
4. 基于后端快照做 2D 命中检测、悬停、按压、选中反馈。
5. 在等待后端确认期间,先行播放飞入、入槽、三消、腾格、胜利和失败过渡。
6. 收到后端确认后,以权威快照校正本地表现。
7. 后端拒绝或版本冲突时,回滚本次即时反馈。
前端禁止:
1. 把本地即时反馈作为最终规则真相。
2. 本地生成可提交成绩。
3. 本地伪造胜利、失败、消除计数或运行态持久化结果。
4. 在 UI 中默认展示长篇玩法规则说明。
## 3.2 api-server
`server-rs/crates/api-server` 负责 Match3D 对外 HTTP facade
1. 鉴权、请求上下文、错误 envelope。
2. 创作 Agent 的 LLM turn 编排。
3. 参考图片上传复用现有资产/OSS 能力。
4. 调用 `spacetime-client` 读写 Match3D 会话、作品和运行态。
5. 对前端返回稳定 HTTP DTO不泄露 SpacetimeDB 内部表结构。
## 3.3 SpacetimeDB
`server-rs/crates/spacetime-module` 负责 Match3D 真相态:
1. 存储 Agent session / message。
2. 存储作品 profile。
3. 存储运行态 run snapshot。
4. 通过 procedure 同步返回会话、作品和运行态快照。
5. 在 reducer/procedure 内保持确定性,不做网络、文件系统或外部模型调用。
## 3.4 纯领域 crate
新增:
```text
server-rs/crates/module-match3d
```
职责:
1. 创作配置校验。
2. 物品类型规划。
3. 初始布局生成输入/输出模型。
4. 2D 遮挡与可点击快照计算。
5. 点击确认。
6. 入槽与三消确认。
7. 胜负确认。
8. 成绩基础数据计算。
`module-match3d` 不直接依赖 Axum、不访问 OSS、不调用 LLM、不读写 SpacetimeDB 表。
---
## 4. 共享契约
## 4.1 TypeScript shared contracts
新增:
```text
packages/shared/src/contracts/match3dAgent.ts
packages/shared/src/contracts/match3dWorks.ts
packages/shared/src/contracts/match3dRuntime.ts
```
### `match3dAgent.ts`
承载:
1. `Match3DAgentSession`
2. `Match3DAgentMessage`
3. `Match3DCreatorConfig`
4. `Match3DCompileDraftRequest`
5. `Match3DCompileDraftResult`
### `match3dWorks.ts`
承载:
1. `Match3DWorkProfile`
2. `Match3DWorkSummary`
3. `Match3DWorkUpdateRequest`
4. `Match3DPublishRequest`
5. `Match3DPublishResult`
### `match3dRuntime.ts`
承载:
1. `Match3DRunSnapshot`
2. `Match3DItemSnapshot`
3. `Match3DTraySlot`
4. `Match3DStartRunRequest`
5. `Match3DClickItemRequest`
6. `Match3DClickItemResult`
7. `Match3DStopRunRequest`
8. `Match3DRestartRunRequest`
## 4.2 Rust shared contracts
新增:
```text
server-rs/crates/shared-contracts/src/match3d_agent.rs
server-rs/crates/shared-contracts/src/match3d_works.rs
server-rs/crates/shared-contracts/src/match3d_runtime.rs
```
并在 `server-rs/crates/shared-contracts/src/lib.rs` 导出。
Rust DTO 只承载 HTTP contract 和跨 crate 稳定模型,不直接暴露 `module-match3d` 内部结构。
## 4.3 命名约束
1. 对外展示:抓大鹅。
2. 工程域:`match3d`
3. TypeScript 类型前缀:`Match3D`
4. Rust 类型前缀:`Match3D`
5. HTTP path`/api/creation/match3d/*``/api/runtime/match3d/*`
6. SpacetimeDB 表与 procedure 前缀:`match3d_`
---
## 5. SpacetimeDB 表
首版保持最小闭环,复杂结构统一使用结构化字段 + `snapshot_json` / `draft_json`,避免过早拆出多张高耦合子表。
新增表属于安全 schema 演进;后续如果改字段,必须遵守 `SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md`,不能直接删除、重排或改名已有列。表结构变更后必须同步对齐 `migration.rs`
## 5.1 `match3d_agent_session`
作用:保存 Match3D 创作 Agent 会话、配置草稿和发布指针。
字段:
1. `session_id: String`,主键。
2. `owner_user_id: String`,索引。
3. `seed_text: String`,用户初始输入或自动配置摘要。
4. `current_turn: u32`
5. `progress_percent: u32`
6. `stage: String`,建议值:`Collecting``ReadyToCompile``DraftCompiled``Published`
7. `config_json: String`,序列化 `Match3DCreatorConfig`
8. `draft_json: String`,序列化草稿结果。
9. `last_assistant_reply: String`
10. `published_profile_id: String`,未发布为空字符串。
11. `created_at: Timestamp`
12. `updated_at: Timestamp`
## 5.2 `match3d_agent_message`
作用:保存 Match3D 创作 Agent 消息流水。
字段:
1. `message_id: String`,主键。
2. `session_id: String`,索引。
3. `role: String`,建议值:`user``assistant``system`
4. `kind: String`,建议值:`text``action``error`
5. `text: String`
6. `created_at: Timestamp`
## 5.3 `match3d_work_profile`
作用:保存 Match3D 作品主表和发布状态。
字段:
1. `profile_id: String`,主键。
2. `owner_user_id: String`,索引。
3. `source_session_id: String`
4. `author_display_name: String`
5. `game_name: String`
6. `theme_text: String`
7. `summary_text: String`
8. `tags_json: String`
9. `cover_image_src: String`
10. `cover_asset_id: String`
11. `clear_count: u32`
12. `difficulty: u32`
13. `config_json: String`
14. `publication_status: String`,建议值:`Draft``Published`
15. `play_count: u32`
16. `updated_at: Timestamp`
17. `published_at: Option<Timestamp>`,未发布为 `None`
## 5.4 `match3d_runtime_run`
作用:保存 Match3D 单局运行态快照和成绩基础数据。
字段:
1. `run_id: String`,主键。
2. `owner_user_id: String`,索引。
3. `profile_id: String`,索引。
4. `status: String`,建议值:`Running``Won``Failed``Stopped`
5. `snapshot_version: u32`
6. `started_at_ms: i64`
7. `duration_limit_ms: i64`,首版固定 `600000`
8. `finished_at_ms: i64`,未结束为 `0`
9. `elapsed_ms: i64`
10. `clear_count: u32`
11. `total_item_count: u32`
12. `cleared_item_count: u32`
13. `failure_reason: String`,建议值为空、`TimeUp``TrayFull`
14. `snapshot_json: String`,序列化 `Match3DRunSnapshot`
15. `created_at: Timestamp`
16. `updated_at: Timestamp`
## 5.5 `match3d_play_record`
首版可选,若本轮不做排行榜,可先不建表,只在 `match3d_runtime_run` 保留成绩字段。
若实现成绩历史,字段建议:
1. `record_id: String`,主键。
2. `profile_id: String`,索引。
3. `owner_user_id: String`,索引。
4. `run_id: String`
5. `status: String`
6. `elapsed_ms: i64`
7. `cleared_item_count: u32`
8. `total_item_count: u32`
9. `created_at: i64`
---
## 6. SpacetimeDB procedure
本轮全部使用 procedure 同步返回快照,避免 `api-server` 在写入后再读 private table。
## 6.1 创作链
1. `create_match3d_agent_session(input)`
创建会话,写入初始配置或空配置,返回 session snapshot。
2. `get_match3d_agent_session(input)`
获取会话、消息和当前 draft。
3. `submit_match3d_agent_message(input)`
只写 user message不调用 LLM不生成 assistant 回复。
4. `finalize_match3d_agent_message_turn(input)`
`api-server` LLM turn 完成后写入 assistant message、配置状态、进度和 `last_assistant_reply`
5. `compile_match3d_draft(input)`
校验题材、需要消除次数、难度,生成草稿和作品 draft profile。
## 6.2 作品链
1. `update_match3d_work(input)`
更新游戏名称、标签、封面、题材、需要消除次数和难度。
2. `publish_match3d_work(input)`
校验基础信息完整后发布作品,不要求试玩通关。
3. `list_match3d_works(input)`
查询当前用户作品。
4. `get_match3d_work_detail(input)`
查询作品详情,支持结果页恢复和作品详情页。
5. `delete_match3d_work(input)`
可后置;若接入创作中心删除,需要与其他玩法卡片删除语义一致。
## 6.3 运行态链
1. `start_match3d_run(input)`
基于作品配置生成单局快照,返回 `Match3DRunSnapshot`
2. `get_match3d_run(input)`
返回当前权威运行态快照。
3. `click_match3d_item(input)`
根据 `run_id / item_instance_id / client_snapshot_version` 权威确认点击、入槽、三消、失败或胜利,返回新快照和确认结果。
4. `stop_match3d_run(input)`
把运行态标记为 `Stopped`,供试玩中止和返回结果页使用。
5. `restart_match3d_run(input)`
复用同一作品配置创建新 run返回新快照。
6. `finish_match3d_time_up(input)`
可选。若倒计时由前端触发,前端在倒计时归零时调用该 procedure后端确认 `TimeUp`。也可以由 `click_match3d_item``get_match3d_run` 懒确认超时。
## 6.4 procedure 输入输出约束
1. 所有 mutation 输入必须带 `owner_user_id` 或由 `api-server` 注入用户上下文SpacetimeDB 内部仍需以可信身份或 owner 字段校验归属。
2. 运行态 mutation 必须携带 `client_snapshot_version`
3. 若版本不匹配,返回 `VersionConflict`,并携带最新快照。
4. procedure 返回字符串化 JSON 时,`spacetime-client` 必须负责反序列化和错误归一化。
---
## 7. 运行态确认协议
Match3D 首版采用“前端即时反馈 + 后端权威确认”。
## 7.1 点击流程
```text
玩家点击物品
-> 前端基于最新快照做 2D 命中检测
-> 前端立即播放按压/选中/飞入表现
-> 前端调用 click_match3d_item
-> 后端确认点击是否合法
-> 后端返回新快照与确认结果
-> 前端按确认结果固化或回滚表现
```
## 7.2 点击请求
```ts
interface Match3DClickItemRequest {
runId: string;
itemInstanceId: string;
clientSnapshotVersion: number;
clientEventId: string;
clickedAtMs: number;
}
```
字段说明:
1. `clientSnapshotVersion` 用于发现前端基于旧快照操作。
2. `clientEventId` 用于前端去重和日志定位。
3. `clickedAtMs` 只用于观测,不作为成绩可信时间源。
## 7.3 点击结果
```ts
type Match3DClickConfirmStatus =
| 'Accepted'
| 'RejectedNotClickable'
| 'RejectedAlreadyMoved'
| 'RejectedTrayFull'
| 'VersionConflict'
| 'RunFinished';
interface Match3DClickItemResult {
status: Match3DClickConfirmStatus;
run: Match3DRunSnapshot;
acceptedItemInstanceId?: string;
clearedItemInstanceIds: string[];
failureReason?: 'TimeUp' | 'TrayFull';
}
```
## 7.4 前端回滚规则
1. `Accepted`:固化飞入、入槽、消除或胜负表现。
2. `RejectedNotClickable`:被点物品回到原位,备选栏恢复。
3. `RejectedAlreadyMoved`:直接应用后端最新快照。
4. `RejectedTrayFull`:应用后端失败快照。
5. `VersionConflict`:取消当前局部动画,应用最新快照,允许用户继续操作。
6. `RunFinished`:应用后端胜负快照,进入结算。
## 7.5 快照版本
1. 每次后端接受会改变运行态的操作,`snapshot_version` 必须递增。
2. 前端所有即时反馈都基于某个明确版本。
3. 前端同时只能有一个未确认的点击操作;首版不做多点击并发队列。
4. 如果动画期间用户再次点击,前端应忽略或排队到当前确认完成后再处理;首版建议忽略。
---
## 8. 运行态快照
## 8.1 `Match3DRunSnapshot`
```ts
interface Match3DRunSnapshot {
runId: string;
profileId: string;
status: 'Running' | 'Won' | 'Failed' | 'Stopped';
snapshotVersion: number;
startedAtMs: number;
durationLimitMs: number;
serverNowMs: number;
remainingMs: number;
clearCount: number;
totalItemCount: number;
clearedItemCount: number;
traySlots: Match3DTraySlot[];
items: Match3DItemSnapshot[];
failureReason?: 'TimeUp' | 'TrayFull';
}
```
说明:
1. `serverNowMs` 用于前端校准倒计时。
2. `remainingMs` 由后端按 `durationLimitMs` 和服务端时间计算。
3. 前端可以本地递减倒计时,但归零失败必须调用后端确认或等待下一次后端确认。
## 8.2 `Match3DItemSnapshot`
```ts
interface Match3DItemSnapshot {
itemInstanceId: string;
itemTypeId: string;
visualKey: string;
x: number;
y: number;
radius: number;
layer: number;
state: 'InBoard' | 'Flying' | 'InTray' | 'Cleared';
clickable: boolean;
}
```
说明:
1. `Flying` 可以作为前端表现态使用,不要求后端逐帧落库。
2. 后端主要确认 `InBoard -> InTray -> Cleared` 的权威状态变化。
3. `clickable` 是后端计算给前端的可点击快照,前端命中检测必须尊重它。
## 8.3 `Match3DTraySlot`
```ts
interface Match3DTraySlot {
slotIndex: number;
itemInstanceId?: string;
itemTypeId?: string;
visualKey?: string;
}
```
## 8.4 2D 遮挡口径
首版不做真实物理遮挡。
建议后端按以下输入计算 `clickable`
1. 物品圆形或近似圆形碰撞范围。
2. `layer` 越大越靠上。
3. 被更高层物品覆盖到低于可点击阈值时,标记为不可点击。
4. 阈值首版作为领域常量,后续体验后再参数化。
前端基于 `clickable` 和自身命中检测呈现即时反馈;后端仍在点击确认时再次校验。
---
## 9. 领域规则冻结
## 9.1 创作配置
```ts
interface Match3DCreatorConfig {
themeText: string;
referenceImageSrc?: string;
clearCount: number;
difficulty: number;
}
```
规则:
1. `themeText` 必填。
2. `clearCount` 必须为正整数。
3. `difficulty` 范围 `1~10`
4. `referenceImageSrc` 首版只支持图片,不支持视频。
## 9.2 物品数量
```text
totalItemCount = clearCount * 3
```
每种 `itemTypeId` 的数量必须是 `3` 的倍数。
## 9.3 demo 视觉素材
首版使用 `10` 种颜色形状组合素材。
1. `visualKey` 固定为内置素材 key。
2. 题材主题先进入作品配置和 Agent 文案,不强制生成题材素材。
3. 后续接入真实题材素材前,必须另补资产生成方案。
## 9.4 难度
首版 `difficulty` 只作为布局和生成算法参数。
后端需要保留参数入口,但难度公式先保持简洁:
1. 难度越高,物品尺寸可整体略小。
2. 难度越高,堆叠层级可略深。
3. 难度越高,首屏可直接三消的可见组合可略少。
具体数值不在 A0 冻死,由 B1 领域 crate 分支给出首版常量并通过测试覆盖。
---
## 10. api-server HTTP facade
## 10.1 创作链
```text
POST /api/creation/match3d/sessions
GET /api/creation/match3d/sessions/:sessionId
POST /api/creation/match3d/sessions/:sessionId/messages
POST /api/creation/match3d/sessions/:sessionId/messages/stream
POST /api/creation/match3d/sessions/:sessionId/compile
```
说明:
1. 同步消息接口用于普通提交。
2. 流式接口复用现有 Agent SSE 基建。
3. `messages` 只写 user messageLLM 推理由 `api-server` 完成后 finalize 到 SpacetimeDB。
4. `compile` 不生成额外素材,只生成 Match3D 草稿和作品 draft。
## 10.2 作品链
```text
PATCH /api/creation/match3d/works/:profileId
POST /api/creation/match3d/works/:profileId/publish
GET /api/creation/match3d/works
GET /api/creation/match3d/works/:profileId
```
首版发布不要求试玩通关。
## 10.3 运行态链
```text
POST /api/runtime/match3d/works/:profileId/runs
GET /api/runtime/match3d/runs/:runId
POST /api/runtime/match3d/runs/:runId/click
POST /api/runtime/match3d/runs/:runId/stop
POST /api/runtime/match3d/runs/:runId/restart
POST /api/runtime/match3d/runs/:runId/time-up
```
`time-up` 可后置;若不单独实现,`get` 或下一次 `click` 必须能懒确认超时失败。
## 10.4 错误语义
HTTP 层使用现有 API envelope。
建议错误码:
1. `MATCH3D_SESSION_NOT_FOUND`
2. `MATCH3D_WORK_NOT_FOUND`
3. `MATCH3D_RUN_NOT_FOUND`
4. `MATCH3D_INVALID_CONFIG`
5. `MATCH3D_PUBLISH_BLOCKED`
6. `MATCH3D_RUN_VERSION_CONFLICT`
7. `MATCH3D_RUN_ALREADY_FINISHED`
---
## 11. 前端落点
## 11.1 contracts 与 service
新增:
```text
src/services/match3d-creation/
src/services/match3d-works/
src/services/match3d-runtime/
```
分别负责 Agent/草稿、作品/发布、运行态请求。
## 11.2 组件
新增:
```text
src/components/match3d-creation/
src/components/match3d-result/
src/components/match3d-runtime/
```
## 11.3 平台入口
需要接入:
1. `src/components/platform-entry/platformEntryCreationTypes.ts`
2. `src/components/platform-entry/PlatformEntryCreationTypeModal.tsx`
3. `src/components/platform-entry/usePlatformCreationAgentFlowController.ts`
入口展示:
1. 名称:`抓大鹅`
2. 子标题:`经典消除玩法`
## 11.4 运行态 UI
首版运行态必须移动端优先:
1. 圆形空间占据主要区域。
2. 备选栏固定 `7` 格。
3. 倒计时清晰但不遮挡物品。
4. 物品点击区域稳定,不因动画造成布局跳动。
5. 胜利/失败结算使用独立面板,不在当前面板下方展开。
## 11.5 本地 mock 口径
F3 运行态即时反馈分支可以先用本地 mock snapshot 开发,但必须满足:
1. mock 类型来自 `packages/shared/src/contracts/match3dRuntime.ts`
2. mock 字段不得脱离 A0 文档。
3. 接入真实 API 时删除或降级为测试 fixture。
---
## 12. 并行开发包
## 12.1 第二波并行
### B1 + B2领域 crate 与 shared contracts
写入范围:
1. `server-rs/crates/module-match3d/`
2. `server-rs/Cargo.toml`
3. `server-rs/crates/shared-contracts/src/match3d_*.rs`
4. `packages/shared/src/contracts/match3d*.ts`
交付:
1. 领域规则单测。
2. DTO 编译通过。
3. 不接 SpacetimeDB。
### B3SpacetimeDB 表与 procedure
写入范围:
1. `server-rs/crates/spacetime-module/src/match3d/`
2. `server-rs/crates/spacetime-module/src/lib.rs`
3. `server-rs/crates/spacetime-module/src/migration.rs`
4. 生成后的 bindings 由后续 B4 处理。
交付:
1. 表和 procedure 定义。
2.`module-match3d` 规则接线。
3. `spacetime build` 或仓库现有等价脚本通过。
B3 当前落地状态:
1. `server-rs/crates/spacetime-module/src/match3d/` 已承载 Match3D 的表、procedure 输入输出类型和 procedure 实现,并由 `server-rs/crates/spacetime-module/src/lib.rs` 挂载导出。
2. `migration.rs` 已纳入 `match3d_agent_session``match3d_agent_message``match3d_work_profile``match3d_runtime_run` 四张表,后续字段变更继续按 `SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md` 追加兼容字段。
3. 运行态 `start_match3d_run``click_match3d_item``stop_match3d_run``finish_match3d_time_up` 通过适配层调用 `module-match3d` 的领域规则SpacetimeDB 层只负责归属校验、事务写入、权威快照持久化和 procedure JSON 返回。
4. B3 对外仍返回当前首版快照字段 `snapshotVersion / clientSnapshotVersion` 对应语义;`module-match3d` 内部的 `board_version` 只在适配层中转换,避免影响并行中的 B4/F3 接入。
5. SpacetimeDB module 的有效验收命令是 `spacetime build --module-path crates/spacetime-module`;不要用普通 native `cargo test -p spacetime-module` 作为验收口径,因为该 crate 会链接 SpacetimeDB 宿主符号。
### F1创作入口与 Agent UI
写入范围:
1. `src/components/platform-entry/`
2. `src/components/match3d-creation/`
3. `src/services/match3d-creation/`
交付:
1. 平台入口可见。
2. Agent 工作区能收集题材、需要消除次数和难度。
3. 可用 mock client等待 B5 接口。
### F3运行态即时反馈 UI
写入范围:
1. `src/components/match3d-runtime/`
2. `src/services/match3d-runtime/`
交付:
1. 圆形空间、2D 物品、`7` 格备选栏。
2. 点击命中、飞入、入槽、三消、腾格、胜负过渡。
3. 后端确认失败时的回滚和快照校正逻辑。
4. 先用 mock snapshot。
## 12.2 第三波并行
### B4 + B5spacetime-client 与 api-server facade
写入范围:
1. `server-rs/crates/spacetime-client/src/match3d.rs`
2. `server-rs/crates/spacetime-client/src/lib.rs`
3. `server-rs/crates/api-server/src/match3d.rs`
4. `server-rs/crates/api-server/src/app.rs`
5. `server-rs/crates/api-server/src/main.rs` 如需注册模块
交付:
1. HTTP facade 可调用 SpacetimeDB procedure。
2. 创作、作品、运行态接口返回 shared-contract DTO。
3. 后端定向测试通过。
### F2结果页与发布
写入范围:
1. `src/components/match3d-result/`
2. `src/services/match3d-works/`
3. 创作中心作品恢复相关最小接线。
交付:
1. 编辑游戏名称、标签、封面图。
2. 试玩入口。
3. 发布入口。
### F4平台分发最小接入
写入范围:
1. 创作中心作品货架。
2. 首页/分类/广场卡片映射。
3. 作品详情启动运行态入口。
交付:
1. 已发布 Match3D 作品可进入平台列表。
2. 卡片可进入详情或运行态。
## 12.3 最后集成
### Q1集成验收
交付:
1. 创作到发布到试玩主链通过。
2. 运行态点击、入槽、三消、失败、胜利通过。
3. 移动端视口检查通过。
4. `npm run api-server:maincloud` 通过。
5. 对应测试与 `npm run check:encoding` 通过。
---
## 13. 合并顺序
建议合并顺序:
1. A0本文档。
2. B1 + B2领域 crate 与 shared contracts。
3. B3SpacetimeDB 表和 procedure。
4. B4 + B5spacetime-client 与 api-server facade。
5. F1 / F2 / F3前端创作、结果页、运行态。
6. F4平台分发。
7. Q1集成收口。
如果 F1/F3 先完成,应只以 mock client 保持可编译,不直接修改后端合同。
---
## 14. 验收命令
后续编码分支按改动范围执行。
文档分支:
```powershell
npm run check:encoding -- docs/technical/MATCH3D_CREATION_AND_RUNTIME_MINIMAL_IMPLEMENTATION_2026-04-30.md docs/technical/README.md
```
后端分支:
```powershell
cargo test -p module-match3d
cargo test -p shared-contracts
npm run api-server:maincloud
npm run check:encoding
```
SpacetimeDB 分支按仓库现有发布脚本执行,并在需要生成绑定时使用 `spacetime generate` 或仓库封装脚本。不得手写生成文件。
前端分支:
```powershell
npm run check:encoding
npm run typecheck
```
若新增定向测试,应补跑对应 `vitest`
---
## 15. 一句话结论
Match3D 首版按独立玩法域落地:前端负责所有局内即时反馈以保证手感,后端通过 SpacetimeDB procedure 权威确认规则和成绩api-server 只暴露稳定 HTTP facade后续并行分支必须围绕本文冻结的 DTO、表、procedure 和路由推进。

View File

@@ -0,0 +1,111 @@
# 抓大鹅 Match3D 领域规则与共享契约 Stage1 方案
日期:`2026-04-30`
## 1. 文档目的
本文件承接 [MATCH3D_CREATION_AND_RUNTIME_MINIMAL_IMPLEMENTATION_2026-04-30.md](./MATCH3D_CREATION_AND_RUNTIME_MINIMAL_IMPLEMENTATION_2026-04-30.md),只冻结 B1 + B2 开发范围:
1. 新增 `module-match3d` 纯领域 crate。
2. 新增 Rust shared contracts。
3. 新增 TypeScript shared contracts。
本阶段不实现 SpacetimeDB 表、procedure、`spacetime-client` 调用封装、`api-server` facade 和前端页面。
## 2. Stage1 边界
## 2.1 本阶段做
1. 领域层定义创作配置、作品草稿、作品 profile、运行态快照、物品、托盘、点击确认结果。
2. 领域层提供纯函数:
- 校验创作配置
- 编译默认草稿
- 校验发布字段
- 按确定性 seed 生成初始运行态
- 刷新 2D 可点击快照
- 确认点击、入槽、三消、胜利、托盘满失败
- 确认倒计时失败
3. Rust / TypeScript shared contracts 提供前后端对齐的请求与响应 DTO。
4. 运行态采用“前端即时反馈 + 后端权威确认”契约:
- 前端可先播放点击、飞入、入槽、三消、腾格和胜负过渡。
- 后端确认后返回权威快照。
- 后端拒绝或快照版本不一致时,前端按权威快照回滚或校正。
## 2.2 本阶段不做
1. 不新增 SpacetimeDB 表。
2. 不新增 SpacetimeDB procedure。
3. 不生成新的 SpacetimeDB bindings。
4. 不新增 `api-server` 路由。
5. 不接入平台入口、结果页或运行态 UI。
6. 不接入真实图片生成。
7. 不做排行榜与后续关卡推荐。
## 3. 领域 crate 设计
新增:
```text
server-rs/crates/module-match3d
```
该 crate 是纯领域层,不读写数据库,不访问网络,不依赖浏览器或文件系统。
本阶段虽然不落 SpacetimeDB 表和 procedure但领域模型已经为后续 SpacetimeDB 接入预留 `spacetime-types` feature。后续在 `spacetime-module` 内使用这些类型时,仍必须遵守 reducer 确定性、`ctx.sender()` 鉴权和表结构迁移约束。
核心类型:
1. `Match3DCreatorConfig`
2. `Match3DResultDraft`
3. `Match3DWorkProfile`
4. `Match3DRunSnapshot`
5. `Match3DItemSnapshot`
6. `Match3DTraySlot`
7. `Match3DClickConfirmation`
核心函数:
1. `build_creator_config`
2. `compile_result_draft`
3. `validate_publish_requirements`
4. `create_work_profile`
5. `publish_work_profile`
6. `start_run_with_seed_at`
7. `confirm_click_at`
8. `resolve_run_timer_at`
## 4. 即时反馈与权威确认
本阶段将点击处理明确拆成两层:
1. 前端即时反馈层
- 读取后端快照中的 `boardVersion`、物品位置、层级、半径和 `clickable`
- 本地做命中检测和动画。
- 立即表现飞入、入槽、三消和胜负过渡。
2. 后端权威确认层
- 校验 `runId``itemInstanceId`、运行态状态和物品是否仍可点击。
- 重新计算入槽、三消、托盘满失败和胜利。
- 返回最新 `Match3DRunSnapshot`
-`boardVersion` 帮前端识别是否需要校正。
`Flying` 只作为前端表现态,不要求后端逐帧落库。后端只确认物品是否已从 `InBoard` 进入 `InTray``Cleared`
运行态领域内部使用 `board_version` 表示权威快照版本HTTP 与 TypeScript shared contracts 对外使用 `snapshotVersion` / `clientSnapshotVersion`,由后续 `api-server` facade 做字段映射。
## 5. 生成规则 Stage1 口径
1. `clearCount` 必须是正整数。
2. `totalItemCount = clearCount * 3`
3. 难度范围为 `1~10`
4. 首版内置 `10` 种 demo 视觉 key。
5.`clearCount > 10` 时,复用视觉 key并保证每种物品数量仍为 `3` 的倍数。
6. 初始布局使用确定性 seed 生成圆形空间内的 2D 坐标。
7. 可点击判定只做 2D 近似:若物品被更高层物品完全覆盖,则不可点击;否则可点击。
## 6. 验收
1. `cargo test -p module-match3d` 通过。
2. `cargo test -p shared-contracts match3d` 通过。
3. `npm run check:encoding` 覆盖新增中文文档和新增源码。
4. 本阶段不要求运行 `npm run api-server:maincloud`因为未修改后端运行服务入口、SpacetimeDB 表或 `api-server` facade。

View File

@@ -0,0 +1,91 @@
# 抓大鹅 Match3D F1 创作入口与 Agent UI 落地记录 2026-04-30
## 1. 阶段边界
本文件承接《MATCH3D_CREATION_AND_RUNTIME_MINIMAL_IMPLEMENTATION_2026-04-30.md》的 F1 包。
F1 只处理前端创作入口、Agent 工作区和等待后端 B5 facade 前的 mock client。它不实现运行态规则不修改 SpacetimeDB 表,不接 `api-server` 路由。
## 2. 本阶段写入范围
1. `src/components/platform-entry/`
2. `src/components/match3d-creation/`
3. `src/services/match3d-creation/`
4. `packages/shared/src/contracts/match3dAgent.ts`
其中 `packages/shared/src/contracts/match3dAgent.ts` 作为 F1 与后续 B5 的 DTO 对齐点F1 mock client 不自建脱离共享契约的临时类型。
## 3. 入口接入
平台入口新增可见创作类型:
```text
id: match3d
title: 抓大鹅
subtitle: 经典消除玩法
badge: 可创建
```
入口来源统一走 `getVisiblePlatformCreationTypes()`,因此创作首页首屏卡带与“选择创作类型”弹层会同时出现抓大鹅。
## 4. Agent 工作区
新增 `Match3DAgentWorkspace`,复用通用 `CreationAgentWorkspace`
Agent 只收集三类锚点:
1. 题材主题。
2. 需要消除次数。
3. 难度。
工作区支持参考图片上传入口。图片在 F1 中先以 Data URL 形式随消息 payload 带给 mock clientB5 接入后由后端 facade 替换为正式资产上传与引用。
UI 中不默认展示玩法规则长文,只展示进度、锚点、聊天内容和必要按钮。
## 5. mock client
新增 `src/services/match3d-creation/match3dCreationClient.ts`
mock client 提供:
1. `createMatch3DCreationSession`
2. `getMatch3DCreationSession`
3. `streamMatch3DCreationMessage`
4. `executeMatch3DCreationAction`
mock 行为:
1. 创建本地会话。
2. 从中文输入中提取题材、消除次数和难度。
3. 支持“自动配置”。
4. 当三项配置完整时允许执行 `match3d_compile_draft`
5. 编译后返回 `draft_ready` 会话和草稿。
## 6. 结果承接
F1 新增 `Match3DDraftReadyView` 作为草稿生成后的临时承接页,只展示草稿基础信息并允许返回 Agent 修改。
正式结果页的基础信息编辑、封面图、试玩、发布由 F2 接入F1 不在这里模拟发布。
## 7. 后续替换点
B5 完成后,只需要把 `match3dCreationClient` 的本地 Map mock 替换为 HTTP/SSE facade
```text
POST /api/creation/match3d/sessions
GET /api/creation/match3d/sessions/:sessionId
POST /api/creation/match3d/sessions/:sessionId/messages/stream
POST /api/creation/match3d/sessions/:sessionId/compile
```
`PlatformEntryFlowShellImpl``Match3DAgentWorkspace` 不应再改一轮业务字段。
## 8. 验收口径
1. 创作首页能看到“抓大鹅 / 经典消除玩法”。
2. 弹层选择“抓大鹅”能进入 Agent 工作区。
3. 输入题材、消除次数、难度后进度到 `100%`
4. 点击“生成结果页”进入草稿承接页。
5. 可从草稿承接页返回 Agent 修改。
6. `npm run check:encoding` 通过。
7. `npm run typecheck` 通过。

View File

@@ -0,0 +1,394 @@
# 抓大鹅 Match3D F2 结果页与发布技术方案
日期:`2026-04-30`
## 1. 文档目的
本文件承接 [MATCH3D_CREATION_AND_RUNTIME_MINIMAL_IMPLEMENTATION_2026-04-30.md](./MATCH3D_CREATION_AND_RUNTIME_MINIMAL_IMPLEMENTATION_2026-04-30.md),只冻结 F2 开发范围:
1. Match3D 待发布结果页。
2. 作品基础信息编辑。
3. 发布前试玩入口。
4. 发布入口。
5. 已发布作品二次编辑恢复口径。
本阶段不实现运行态即时反馈 UI不实现 SpacetimeDB 表与 procedure不实现 `api-server` facade。F2 可以先基于 shared contracts 与 mock client 开发,等待 B4+B5 接入真实 HTTP。
---
## 2. 前置依赖
F2 依赖以下已冻结文档:
1. PRD[AI_NATIVE_MATCH3D_CREATOR_AND_GAMEPLAY_SYSTEM_PRD_2026-04-30.md](../prd/AI_NATIVE_MATCH3D_CREATOR_AND_GAMEPLAY_SYSTEM_PRD_2026-04-30.md)
2. A0[MATCH3D_CREATION_AND_RUNTIME_MINIMAL_IMPLEMENTATION_2026-04-30.md](./MATCH3D_CREATION_AND_RUNTIME_MINIMAL_IMPLEMENTATION_2026-04-30.md)
3. B1+B2[MATCH3D_DOMAIN_AND_CONTRACTS_STAGE1_2026-04-30.md](./MATCH3D_DOMAIN_AND_CONTRACTS_STAGE1_2026-04-30.md)
F2 可在 B4+B5 之前并行开发,但必须遵守 B2 的 TypeScript contract不得在前端私自扩字段。
---
## 3. 本阶段做
1. 新增 Match3D 结果页组件目录。
2. 新增 Match3D works service 目录。
3. 展示草稿配置摘要:
- 题材主题
- 需要消除次数
- 难度
- 参考图片预览
4. 支持编辑发布基础信息:
- 游戏名称
- 标签
- 封面图
5. 支持发布前试玩入口。
6. 支持试玩中止后回到结果页继续编辑。
7. 支持发布入口。
8. 支持已发布作品二次编辑的前端恢复路径。
---
## 4. 本阶段不做
1. 不生成题材物品素材。
2. 不生成额外封面图;封面图只接收已有图片、上传图片或后端已有占位结果。
3. 不要求试玩通关后才能发布。
4. 不实现运行态点击、飞入、三消等即时反馈。
5. 不实现首页、分类页和广场投影。
6. 不实现排行榜。
7. 不在 UI 中默认展示玩法规则说明长文。
8. 不把发布校验只写在前端;前端只做即时提示,后端 publish gate 是最终门槛。
---
## 5. 文件落点
## 5.1 前端组件
新增:
```text
src/components/match3d-result/
```
建议文件:
```text
src/components/match3d-result/Match3DResultView.tsx
src/components/match3d-result/Match3DResultView.test.tsx
src/components/match3d-result/index.ts
```
如组件变大,可后续拆分:
```text
Match3DResultHeader.tsx
Match3DResultBasicsForm.tsx
Match3DResultConfigPreview.tsx
Match3DResultPublishPanel.tsx
```
首版不要过早拆太多文件,优先保持可读和低冲突。
## 5.2 前端 service
新增:
```text
src/services/match3d-works/
```
建议文件:
```text
src/services/match3d-works/match3dWorksClient.ts
src/services/match3d-works/index.ts
```
F2 只负责 works 维度:
1. 读取作品详情。
2. 更新作品基础信息。
3. 发布作品。
4. 删除作品可后置,若 F4 需要再补。
运行态启动接口归 `src/services/match3d-runtime/`F2 只调用上层传入的 `onStartTestRun`
---
## 6. shared contracts 使用
F2 只消费 B2 已冻结的 TypeScript contract
```text
packages/shared/src/contracts/match3dWorks.ts
packages/shared/src/contracts/match3dAgent.ts
packages/shared/src/contracts/match3dRuntime.ts
```
必要类型:
1. `Match3DWorkProfile`
2. `Match3DWorkSummary`
3. `Match3DWorkUpdateRequest`
4. `Match3DPublishRequest`
5. `Match3DPublishResult`
6. `Match3DCompileDraftResult`
7. `Match3DCreatorConfig`
F2 不新增独立的前端私有数据结构来表达作品真相;只允许使用局部表单状态承载未保存输入。
---
## 7. 结果页 props contract
建议 `Match3DResultView` props
```ts
type Match3DResultViewProps = {
profile: Match3DWorkProfile;
draft?: Match3DCompileDraftResult | null;
isBusy?: boolean;
error?: string | null;
onBack: () => void;
onStartTestRun: (profile: Match3DWorkProfile) => void;
onPublish: (payload: Match3DPublishRequest) => void;
onSaved?: (profile: Match3DWorkProfile) => void;
};
```
说明:
1. `profile` 是结果页当前作品真相源。
2. `draft` 只用于展示草稿生成附加信息;不能覆盖 `profile` 的发布字段。
3. `onStartTestRun` 进入 F3/B5 运行态链路。
4. `onPublish` 可以先由 mock client 实现B5 完成后替换为真实 HTTP。
5. `onSaved` 用于把自动保存后的 profile 回写给上层流程控制器。
---
## 8. 页面内容顺序
结果页保持单列表,不做多 Tab。
固定顺序:
1. 顶部返回与保存状态。
2. 封面图。
3. 游戏名称。
4. 标签。
5. 题材主题。
6. 需要消除次数。
7. 难度。
8. 参考图片预览。
9. 试玩按钮。
10. 发布按钮。
UI 只呈现必要信息,不在页面中展示玩法规则说明长文。
---
## 9. 字段编辑规则
## 9.1 游戏名称
1. 必填。
2. 首版建议前端限制 `1~30` 个中文字符等价长度。
3. 默认值来自 Agent 确认题材或系统生成草稿。
## 9.2 标签
1. 必填。
2. 首版建议 `3~6` 个标签,与拼图发布门槛保持一致。
3. 输入支持中文逗号、英文逗号、顿号、换行拆分。
4. 前端需要去重和去空格。
## 9.3 封面图
1. 必填。
2. F2 可先复用参考图片、占位封面或用户上传图。
3. 图片真实存储由现有资产链或后续 B5 facade 处理。
4. 前端不得把本地临时 blob URL 当作已发布封面真相。
## 9.4 题材主题、需要消除次数、难度
首版结果页允许展示并可编辑这些配置。
修改后必须同步保存到作品 profile
1. `themeText`
2. `clearCount`
3. `difficulty`
注意:
1. `clearCount` 必须为正整数。
2. `difficulty` 必须在 `1~10`
3. 修改配置后,下一次试玩必须基于最新保存配置启动。
---
## 10. 自动保存
F2 建议实现自动保存,口径参考拼图结果页:
1. 输入变更后 `600ms` debounce。
2. 只保存结果页可编辑字段。
3. 保存中展示轻量状态。
4. 保存失败展示轻量错误,不弹长说明。
5. 发布前必须等待最后一次保存完成,或发布 payload 直接携带当前表单字段。
建议状态:
```ts
type Match3DAutoSaveState = 'idle' | 'saving' | 'saved' | 'error';
```
---
## 11. 发布门槛
前端即时 blocker
1. 游戏名称为空。
2. 标签数量不在 `3~6`
3. 封面图为空。
4. `clearCount` 不是正整数。
5. `difficulty` 不在 `1~10`
后端 publish gate 是最终门槛,前端不得绕过。
发布不要求试玩通关。
---
## 12. 试玩入口
结果页提供“试玩”入口。
行为:
1. 点击试玩前先保存当前表单。
2. 保存成功后调用 `onStartTestRun(profile)`
3. 上层进入 Match3D 运行态。
4. 运行态停止或返回后,回到同一个结果页继续编辑。
F2 不实现运行态本身;只冻结结果页如何发起试玩。
---
## 13. 发布接口
F2 service 建议接口:
```ts
const MATCH3D_WORKS_API_BASE = '/api/creation/match3d/works';
export async function getMatch3DWorkDetail(profileId: string): Promise<Match3DWorkDetailResponse>;
export async function updateMatch3DWork(
profileId: string,
payload: Match3DWorkUpdateRequest,
): Promise<Match3DWorkMutationResponse>;
export async function publishMatch3DWork(
profileId: string,
payload: Match3DPublishRequest,
): Promise<Match3DPublishResult>;
```
后续 B5 必须提供同名 HTTP facade 或在 service 层做最小适配。
---
## 14. Mock client 口径
F2 可以在真实 B5 接口完成前使用 mock client。
要求:
1. mock 数据必须来自 shared contracts。
2. mock profile 字段必须覆盖发布必填项。
3. mock publish 只能返回“可发布成功”的本地结果,不得伪造平台广场投影。
4. B5 接入后mock 只能保留为测试 fixture。
---
## 15. 已发布作品二次编辑
进入自己已发布 Match3D 作品时,结果页应支持二次编辑。
规则:
1. 优先通过 `sourceSessionId` 恢复原创作 session。
2. 如果没有 session则通过 `profileId` 读取作品详情进入结果页。
3. 二次发布不得创建新作品,必须覆盖同一 `profileId`
4. 不清零 `playCount`
5. 不改变作品归属。
---
## 16. 与其它分支的接口边界
## 16.1 依赖 F1
F1 负责创建会话和 Agent UI。F2 接收 F1 编译出的 `profile / draft`,不重复实现 Agent 对话。
## 16.2 依赖 F3
F3 负责运行态 UI。F2 只提供 `onStartTestRun` 入口。
## 16.3 依赖 B5
B5 负责真实 HTTP facade。F2 的 service path 和 DTO 必须按本文冻结,避免后续替换 mock 时改组件结构。
## 16.4 依赖 F4
F4 负责首页、分类页和广场分发。F2 发布成功后只需要把返回 profile 交给上层;不直接刷新广场列表。
---
## 17. 测试要求
建议新增:
```text
src/components/match3d-result/Match3DResultView.test.tsx
```
覆盖:
1. 展示游戏名称、标签、封面图、题材、需要消除次数和难度。
2. 游戏名称为空时发布按钮阻断。
3. 标签数量不足时发布按钮阻断。
4. `clearCount` 非正整数时发布按钮阻断。
5. `difficulty` 超出 `1~10` 时发布按钮阻断。
6. 点击试玩前触发保存。
7. 发布不要求试玩通关。
service 测试可在 B5 接入后补齐。
---
## 18. 验收命令
F2 文档分支:
```powershell
npm run check:encoding -- docs/technical/MATCH3D_F2_RESULT_AND_PUBLISH_2026-04-30.md docs/technical/README.md
```
F2 前端实现分支:
```powershell
npm run check:encoding
npm run typecheck
```
如新增组件测试,补跑对应 `vitest`
---
## 19. 一句话结论
F2 只负责把 Match3D 草稿变成可编辑、可试玩、可发布的作品工作台;它必须复用平台结果页和发布体验,发布不要求试玩通关,并为 B5 真实后端接口与 F3 运行态试玩入口保留清晰边界。

View File

@@ -0,0 +1,126 @@
# 抓大鹅 Match3D B4+B5 spacetime-client 与 api-server facade 落地记录
日期:`2026-04-30`
## 1. 本阶段目标
本文件记录 B4+B5 的技术落地范围:把 B3 已生成的 Match3D SpacetimeDB procedure 接到 `spacetime-client`,再通过 `api-server` 暴露给前端使用的 HTTP facade。
本阶段不改 SpacetimeDB 表结构,不新增 migration不接入真实题材素材生成也不改前端即时反馈实现。
## 2. 已落地范围
### 2.1 SpacetimeDB bindings
使用仓库封装脚本重新生成 bindings
```powershell
npm run spacetime:generate
```
Windows 下 SpacetimeDB CLI 可能在 Rust bindings 已生成后输出 `Could not format generated files: 文件名或扩展名太长。 (os error 206)`。脚本已调整为:当 CLI 退出码为 `0` 且只是格式化警告时继续同步生成文件。
生成文件仍视为机器产物,禁止手写修改。
### 2.2 spacetime-client facade
新增:
```text
server-rs/crates/spacetime-client/src/match3d.rs
```
并在 `spacetime-client/src/lib.rs` 导出 `match3d` 模块与 Match3D Record 类型。
已覆盖:
1. 创作会话create / get / submit message / finalize / compile draft。
2. 作品list / get detail / update / publish / delete / public gallery list。
3. 运行态start / get / click / stop / restart / time-up。
`mapper.rs` 负责把 procedure 返回的 JSON 字符串解析为稳定 Record不把 generated bindings 泄露到 `api-server`
### 2.3 api-server HTTP facade
新增:
```text
server-rs/crates/api-server/src/match3d.rs
```
并在 `main.rs` 注册模块,在 `app.rs` 挂载路由。
已挂载路由:
```text
POST /api/creation/match3d/sessions
GET /api/creation/match3d/sessions/{session_id}
POST /api/creation/match3d/sessions/{session_id}/messages
POST /api/creation/match3d/sessions/{session_id}/messages/stream
POST /api/creation/match3d/sessions/{session_id}/actions
POST /api/creation/match3d/sessions/{session_id}/compile
GET /api/creation/match3d/works
GET /api/creation/match3d/works/{profile_id}
PATCH /api/creation/match3d/works/{profile_id}
PUT /api/creation/match3d/works/{profile_id}
DELETE /api/creation/match3d/works/{profile_id}
POST /api/creation/match3d/works/{profile_id}/publish
GET /api/runtime/match3d/gallery
POST /api/runtime/match3d/works/{profile_id}/runs
GET /api/runtime/match3d/runs/{run_id}
POST /api/runtime/match3d/runs/{run_id}/click
POST /api/runtime/match3d/runs/{run_id}/stop
POST /api/runtime/match3d/runs/{run_id}/restart
POST /api/runtime/match3d/runs/{run_id}/time-up
```
`api-server` 返回 `shared-contracts` 中的 Match3D DTO前端不需要感知 SpacetimeDB 内部 JSON 快照结构。
## 3. 创作 Agent 当前口径
B5 首版先采用确定性配置抽取,不在本阶段新增真实 LLM prompt
1. 创建会话时可从 `themeText / seedText / clearCount / difficulty / referenceImageSrc` 形成配置。
2. 发送消息时根据用户文本或 `quickFillRequested` 更新题材、需要消除次数和难度。
3. `match3d_compile_draft` 动作调用 SpacetimeDB `compile_match3d_draft`,生成 draft work profile。
后续若要接真实 LLM turn应复用现有创作 Agent 公共编排,并保持 submit/finalize 两阶段职责不变。
## 4. 运行态确认协议
B5 保持 PRD 调整后的边界:
1. 前端负责点击、飞入、入槽、三消、腾格、胜负等即时表现。
2. 后端通过 `click_match3d_item``finish_match3d_time_up` 等 procedure 做权威确认。
3. HTTP response 会把 SpacetimeDB `Accepted / VersionConflict / RunFinished` 等状态归一到前端 shared contract 可消费的 `accepted / rejectReason / run` 结构。
4. `snapshotVersion` 继续作为前端即时反馈和后端确认之间的版本校验字段。
## 5. shared contract 对齐
本阶段补齐 Rust shared contract 与 TypeScript contract 的已知差异:
1. `Match3DAgentSessionSnapshotResponse` 增加 `anchorPack`
2. `Match3DResultDraftResponse` 增加 `profileId / summaryText / totalItemCount`,同时保留 `summary` 兼容结果页读取。
3. `PutMatch3DWorkRequest` 增加可选 `themeText`,结果页可编辑题材;旧请求缺省时 API 会沿用已有作品题材。
## 6. 验收命令
本阶段至少执行:
```powershell
cargo check -p spacetime-client --manifest-path server-rs\Cargo.toml
cargo check -p api-server --manifest-path server-rs\Cargo.toml
cargo test -p shared-contracts match3d --manifest-path server-rs\Cargo.toml
npm run check:encoding
npm run api-server:maincloud
```
`api-server:maincloud` 是修改后端后的必跑项;如果本地缺少 Maincloud 环境或 SpacetimeDB 发布态不一致,需要在最终结果里明确说明。
## 7. 后续接入点
1. F1/F2/F3 可把 mock client 替换到上述 HTTP facade。
2. F4 平台分发可先读取 `/api/runtime/match3d/gallery` 的已发布作品列表。
3. 若后续要记录排行榜或作品播放统计,需要补 Match3D 成绩表或 play record procedure并同步更新 migration。

View File

@@ -6,6 +6,10 @@
- [SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md](./SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md):冻结 SpacetimeDB 表结构变更约束、自动迁移可接受范围、冲突后的系统行为,以及保留旧数据的增量迁移流程;凡涉及 `spacetime publish`、表字段调整或 `migration.rs` 对齐时优先参考。
- [PROFILE_INVITE_CODE_REGISTRATION_AND_ADMIN_2026-04-30.md](./PROFILE_INVITE_CODE_REGISTRATION_AND_ADMIN_2026-04-30.md):冻结邀请码从“我的 Tab 填写”迁到注册环节的前后端边界、`profile_invite_code.metadata_json` 表结构扩展、管理员邀请码虚拟主体和奖励规则。
- [MATCH3D_CREATION_AND_RUNTIME_MINIMAL_IMPLEMENTATION_2026-04-30.md](./MATCH3D_CREATION_AND_RUNTIME_MINIMAL_IMPLEMENTATION_2026-04-30.md):冻结抓大鹅 Match3D 首版 demo 的独立玩法域、表与 procedure、HTTP facade、前端即时反馈/后端权威确认协议,以及可并行开发包。
- [MATCH3D_DOMAIN_AND_CONTRACTS_STAGE1_2026-04-30.md](./MATCH3D_DOMAIN_AND_CONTRACTS_STAGE1_2026-04-30.md):冻结抓大鹅 Match3D B1+B2 的纯领域规则 crate、Rust/TypeScript shared contracts以及 Stage1 不触碰 SpacetimeDB 表和 api-server 的边界。
- [MATCH3D_F1_CREATION_ENTRY_AND_AGENT_UI_2026-04-30.md](./MATCH3D_F1_CREATION_ENTRY_AND_AGENT_UI_2026-04-30.md):记录抓大鹅 F1 创作入口、Agent 工作区、参考图入口、本地 mock client 与后续 B5 HTTP facade 替换点。
- [MATCH3D_F2_RESULT_AND_PUBLISH_2026-04-30.md](./MATCH3D_F2_RESULT_AND_PUBLISH_2026-04-30.md):冻结抓大鹅 F2 结果页、基础信息编辑、发布前试玩入口、发布门槛、自动保存和已发布作品二次编辑恢复口径。
- [PLATFORM_MOBILE_BOTTOM_DOCK_VIEWPORT_FIX_2026-04-30.md](./PLATFORM_MOBILE_BOTTOM_DOCK_VIEWPORT_FIX_2026-04-30.md):记录平台首页底部 dock 在手机浏览器地址栏展开时脱离可见区域的根因,以及 `100dvh`、固定底部锚点和安全区占位的修复口径。
- [SPACETIMEDB_JSON_STRING_MIGRATION_PROCEDURE_2026-04-27.md](./SPACETIMEDB_JSON_STRING_MIGRATION_PROCEDURE_2026-04-27.md):记录 SpacetimeDB private 表迁移 JSON 导出/导入 procedure、迁移操作员授权、HTTP 413 分片导入、Jenkins 自动迁移回灌和导入脚本参数。
- [JENKINS_SPACETIMEDB_DATABASE_MIGRATION_PIPELINES_2026-04-29.md](./JENKINS_SPACETIMEDB_DATABASE_MIGRATION_PIPELINES_2026-04-29.md):记录 `Genarrative-Database-Export` / `Genarrative-Database-Import` 两条 SCM-backed 数据库迁移流水线参数、默认 dry-run、token 边界和 `CHUNK_SIZE` 413 规避参数。

View File

@@ -27,6 +27,7 @@ spacetime sql <db> "SELECT * FROM custom_world_gallery_entry"
| RPG 运行时 | `story_session`, `story_event`, `npc_state`, `inventory_slot`, `battle_state`, `treasure_record`, `quest_record`, `quest_log`, `player_progression`, `chapter_progression` |
| 世界创作 | `custom_world_profile`, `custom_world_session`, `custom_world_agent_session`, `custom_world_agent_message`, `custom_world_agent_operation`, `custom_world_draft_card`, `custom_world_gallery_entry` |
| 拼图 | `puzzle_agent_session`, `puzzle_agent_message`, `puzzle_work_profile`, `puzzle_runtime_run` |
| 抓大鹅 Match3D | `match3d_agent_session`, `match3d_agent_message`, `match3d_work_profile`, `match3d_runtime_run` |
| 大鱼吃小鱼 | `big_fish_creation_session`, `big_fish_agent_message`, `big_fish_asset_slot`, `big_fish_runtime_run` |
| 资产 | `asset_object`, `asset_entity_binding` |
| AI 任务 | `ai_task`, `ai_task_stage`, `ai_text_chunk`, `ai_result_reference` |
@@ -446,6 +447,53 @@ SELECT * FROM puzzle_runtime_run WHERE run_id = '<run_id>';
SELECT * FROM puzzle_runtime_run WHERE owner_user_id = '<user_id>' ORDER BY updated_at DESC;
```
## 抓大鹅 Match3D 表
### `match3d_agent_session`
- 作用:抓大鹅 Match3D 创作 Agent 会话表,保存种子、配置 JSON、草稿 JSON 和发布 profile 指针。
- 结构:`session_id PK: String`, `owner_user_id: String`, `seed_text: String`, `current_turn: u32`, `progress_percent: u32`, `stage: String`, `config_json: String`, `draft_json: String`, `last_assistant_reply: String`, `published_profile_id: String`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`owner_user_id`
```sql
SELECT * FROM match3d_agent_session WHERE session_id = '<session_id>';
SELECT * FROM match3d_agent_session WHERE owner_user_id = '<user_id>' ORDER BY updated_at DESC;
```
### `match3d_agent_message`
- 作用:抓大鹅 Match3D 创作 Agent 消息流水。
- 结构:`message_id PK: String`, `session_id: String`, `role: String`, `kind: String`, `text: String`, `created_at: Timestamp`
- 索引:`session_id`
```sql
SELECT * FROM match3d_agent_message WHERE session_id = '<session_id>' ORDER BY created_at ASC;
```
### `match3d_work_profile`
- 作用:抓大鹅 Match3D 作品主表,保存作品基础信息、配置、发布状态和游玩次数。
- 结构:`profile_id PK: String`, `owner_user_id: String`, `source_session_id: String`, `author_display_name: String`, `game_name: String`, `theme_text: String`, `summary_text: String`, `tags_json: String`, `cover_image_src: String`, `cover_asset_id: String`, `clear_count: u32`, `difficulty: u32`, `config_json: String`, `publication_status: String`, `play_count: u32`, `updated_at: Timestamp`, `published_at: Option<Timestamp>`
- 索引:`owner_user_id`, `publication_status`
```sql
SELECT * FROM match3d_work_profile WHERE profile_id = '<profile_id>';
SELECT * FROM match3d_work_profile WHERE owner_user_id = '<user_id>' ORDER BY updated_at DESC;
SELECT * FROM match3d_work_profile WHERE publication_status = 'Published';
```
### `match3d_runtime_run`
- 作用:抓大鹅 Match3D 单局运行态表,保存权威快照、快照版本、胜负状态和成绩基础字段。
- 结构:`run_id PK: String`, `owner_user_id: String`, `profile_id: String`, `status: String`, `snapshot_version: u32`, `started_at_ms: i64`, `duration_limit_ms: i64`, `finished_at_ms: i64`, `elapsed_ms: i64`, `clear_count: u32`, `total_item_count: u32`, `cleared_item_count: u32`, `failure_reason: String`, `snapshot_json: String`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`owner_user_id`, `profile_id`
```sql
SELECT * FROM match3d_runtime_run WHERE run_id = '<run_id>';
SELECT * FROM match3d_runtime_run WHERE owner_user_id = '<user_id>' ORDER BY updated_at DESC;
SELECT * FROM match3d_runtime_run WHERE profile_id = '<profile_id>';
```
## 大鱼吃小鱼表
### `big_fish_creation_session`