合并 master 并保留外部生成 worker 模式
合入 master 的拼消消、微信能力、OpenSSL 3.2 和 SpacetimeDB 2.4.1 更新 保留外部内容生成 queue/inline、worker lease 与动态扩缩容口径 补齐拼图后台图片生成队列轮询和运行态返回恢复 同步容器、生产运维和 Hermes 共享记忆中的 worker 文档
This commit is contained in:
@@ -111,6 +111,9 @@ WECHAT_MOCK_DISPLAY_NAME="微信旅人"
|
||||
WECHAT_MOCK_AVATAR_URL=""
|
||||
WECHAT_MINIPROGRAM_MESSAGE_TOKEN=""
|
||||
WECHAT_MINIPROGRAM_MESSAGE_ENCODING_AES_KEY=""
|
||||
WECHAT_MINIPROGRAM_SUBSCRIBE_MESSAGE_ENABLED="true"
|
||||
WECHAT_MINIPROGRAM_GENERATION_RESULT_TEMPLATE_ID="m5z7BkkBhJGbcH0cdDeHaeRU2tViDEguP38XdrRRCdU"
|
||||
WECHAT_MINIPROGRAM_SUBSCRIBE_MESSAGE_STATE="formal"
|
||||
|
||||
# Model name for chat completions.
|
||||
VITE_LLM_MODEL="doubao-1-5-pro-32k-character-250715"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
# Genarrative 项目共享概览
|
||||
|
||||
更新时间:`2026-05-29`
|
||||
更新时间:`2026-06-03`
|
||||
|
||||
## 一句话定位
|
||||
|
||||
@@ -10,6 +10,7 @@ Genarrative / 陶泥儿是一个 AI 原生互动内容与小游戏平台,把 A
|
||||
|
||||
- RPG / 自定义世界创作与运行时。
|
||||
- 拼图玩法创作、草稿、发布、运行态和排行榜。
|
||||
- 拼消消玩法创作、素材图集生成、结果页、发布、统一作品详情、正式运行态和基础统计。
|
||||
- 敲木鱼玩法创作、草稿、发布、运行态、公开详情和分享码。
|
||||
- 抓大鹅 Match3D 创作、2D 多视角素材生成、发布和运行态。
|
||||
- 大鱼吃小鱼、方洞挑战、视觉小说、汪汪声浪和儿童向寓教于乐玩法。
|
||||
@@ -33,7 +34,7 @@ Genarrative / 陶泥儿是一个 AI 原生互动内容与小游戏平台,把 A
|
||||
server-rs + Axum + SpacetimeDB
|
||||
```
|
||||
|
||||
当前 SpacetimeDB crate、SDK、CLI / standalone、生成 bindings 和容器压测镜像统一按 `2.3.0` 对齐。
|
||||
当前 SpacetimeDB crate、SDK、CLI / standalone、生成 bindings 和容器压测镜像统一按 `2.4.1` 对齐。
|
||||
|
||||
职责边界:
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
3. 新增或沉淀 Markdown 文档时,确认文件名已使用 `【标签名】` 前缀。
|
||||
4. 若产生长期有效知识,更新 `.hermes/shared-memory/`。
|
||||
5. 若形成可复用流程,考虑沉淀到 `.hermes/skills/`。
|
||||
6. 在提交信息中区分代码变更与文档/记忆变更。
|
||||
6. 提交代码时,提交标题使用中文;标题后逐行写明本次提交修改了什么,每条变更单独一行。
|
||||
|
||||
## 文档阅读顺序
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ Single-context layout: read root `CONTEXT.md` when present. Current architecture
|
||||
- UI面板中不要默认写一些规则描述文案,清爽一些,按照游戏UI设计规范设计即可。
|
||||
- UI设计需要兼顾网页端、移动端双端的使用体验,确保在不同设备上都能正常显示和操作,移动端优先考虑。
|
||||
- 不要在gitignore中添加.env.local文件。
|
||||
- 提交代码时,提交标题必须使用中文;标题后必须逐行写明本次提交修改了什么,每条变更单独一行。
|
||||
- 严格遵循简洁的代码风格
|
||||
- 请默认保持系统的简洁性,能复用、修改、扩展现有系统、页面就不新建新系统新页面。
|
||||
- 禁止将功能说明描述类的文本默认写入UI界面中。
|
||||
|
||||
+26
@@ -18,6 +18,32 @@ _Avoid_: 为每个玩法单独发明素材流水线、把系列素材建模成
|
||||
|
||||
## Language
|
||||
|
||||
### Puzzle Clear
|
||||
|
||||
**拼消消**:
|
||||
基于拼图交换 / 拖拽手感的新玩法模板,玩家移动 1x1 卡牌碎片,把同一复合图案组拼成完整矩形后消除,并由顶部对应纵列补牌继续游玩。
|
||||
_Avoid_: 拼图整图过关、三消槽位玩法、前端本地裁决
|
||||
|
||||
**复合图案组**:
|
||||
拼消消中可被消除的一幅小图,由 `1x2`、`1x3`、`2x2` 或 `2x3` 的 1x1 卡牌碎片组成;只有组内碎片按正确相对位置拼成完整矩形后才消除。
|
||||
_Avoid_: 单张卡牌、整关大图、任意相邻同色块
|
||||
|
||||
**1x1 卡牌碎片**:
|
||||
复合图案组被服务端切成的最小可移动单位,带有所属组、形状、组内坐标和图片资产。
|
||||
_Avoid_: 前端临时裁图、无所属图案的普通方块
|
||||
|
||||
**半锁定拼接组**:
|
||||
非 2 格复合图案组中已经局部完成的拼接状态,可作为整体拖动;玩家用外部单格撞入组内某格时只交换该格,其余部分保留并退回半完成状态。
|
||||
_Avoid_: 永久锁死、补牌打散、完整消除
|
||||
|
||||
**顶部卡牌准备区**:
|
||||
拼消消棋盘上方按纵列排列的背面卡牌队列;某列产生空位时,准备区对应列的卡牌从顶部下落补齐。
|
||||
_Avoid_: 全局随机发牌槽、底部三消槽
|
||||
|
||||
**防死局发牌**:
|
||||
拼消消开局和每次补牌后由后端保证至少存在一步可拼接;补牌时至少有一张新掉落卡能与场上剩余某张卡对应。
|
||||
_Avoid_: 前端提示代替可解性、完全随机补牌
|
||||
|
||||
### Wooden Fish
|
||||
|
||||
**敲木鱼**:
|
||||
|
||||
@@ -205,6 +205,7 @@ export interface AdminUpsertCreationEntryEventBannersRequest {
|
||||
export interface UnifiedCreationSpecPayload {
|
||||
playId: string;
|
||||
title: string;
|
||||
mudPointCost: number;
|
||||
workspaceStage: string;
|
||||
generationStage: string;
|
||||
resultStage: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* @vitest-environment jsdom */
|
||||
|
||||
import {fireEvent, render, screen, waitFor} from '@testing-library/react';
|
||||
import {fireEvent, render, screen, waitFor, within} from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import {beforeEach, expect, test, vi} from 'vitest';
|
||||
|
||||
@@ -27,7 +27,8 @@ vi.mock('../api/adminApiClient', () => ({
|
||||
|
||||
const puzzleSpec: UnifiedCreationSpecPayload = {
|
||||
playId: 'puzzle',
|
||||
title: '想做个什么玩法?',
|
||||
title: '拼图',
|
||||
mudPointCost: 10,
|
||||
workspaceStage: 'puzzle-agent-workspace',
|
||||
generationStage: 'puzzle-generating',
|
||||
resultStage: 'puzzle-result',
|
||||
@@ -88,8 +89,27 @@ test('创作入口后台展示并保存统一创作契约', async () => {
|
||||
|
||||
await screen.findByText('pictureDescription');
|
||||
expect(container.querySelector('.admin-subsection .admin-info-list')).not.toBeNull();
|
||||
expect(
|
||||
container.querySelector('.admin-subsection .admin-info-list')?.textContent,
|
||||
).toContain('拼图');
|
||||
expect(container.querySelector('.admin-panel .admin-panel')).toBeNull();
|
||||
expect(container.querySelector('.admin-muted')).toBeNull();
|
||||
expect(screen.queryByLabelText('契约 JSON')).toBeNull();
|
||||
expect(screen.queryByText('puzzle-generating')).toBeNull();
|
||||
|
||||
await user.click(screen.getByRole('button', {name: '修改契约'}));
|
||||
const dialog = screen.getByRole('dialog', {name: '统一创作契约'});
|
||||
expect(within(dialog).queryByLabelText('玩法 ID')).toBeNull();
|
||||
expect(within(dialog).queryByLabelText('工作台阶段')).toBeNull();
|
||||
expect(within(dialog).queryByLabelText('生成阶段')).toBeNull();
|
||||
expect(within(dialog).queryByLabelText('结果阶段')).toBeNull();
|
||||
fireEvent.change(within(dialog).getByLabelText('泥点消耗'), {
|
||||
target: {value: '12'},
|
||||
});
|
||||
await user.click(within(dialog).getByRole('button', {name: '应用修改'}));
|
||||
|
||||
expect(screen.queryByRole('dialog', {name: '统一创作契约'})).toBeNull();
|
||||
expect(screen.getByText('12泥点数')).toBeTruthy();
|
||||
|
||||
await user.click(screen.getByRole('button', {name: '保存入库'}));
|
||||
await user.click(screen.getByRole('button', {name: '确认'}));
|
||||
@@ -99,7 +119,10 @@ test('创作入口后台展示并保存统一创作契约', async () => {
|
||||
'admin-token',
|
||||
expect.objectContaining({
|
||||
id: 'puzzle',
|
||||
unifiedCreationSpec: puzzleSpec,
|
||||
unifiedCreationSpec: {
|
||||
...puzzleSpec,
|
||||
mudPointCost: 12,
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
@@ -107,6 +130,18 @@ test('创作入口后台展示并保存统一创作契约', async () => {
|
||||
|
||||
test('创作入口后台拒绝 playId 不一致的统一创作契约', async () => {
|
||||
const user = userEvent.setup();
|
||||
vi.mocked(getAdminCreationEntryConfig).mockResolvedValueOnce({
|
||||
...configResponse,
|
||||
entries: [
|
||||
{
|
||||
...configResponse.entries[0]!,
|
||||
unifiedCreationSpec: {
|
||||
...puzzleSpec,
|
||||
playId: 'match3d',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
render(
|
||||
<AdminCreationEntrySwitchPage
|
||||
token="admin-token"
|
||||
@@ -114,15 +149,7 @@ test('创作入口后台拒绝 playId 不一致的统一创作契约', async ()
|
||||
/>,
|
||||
);
|
||||
|
||||
const textarea = await screen.findByLabelText('契约 JSON');
|
||||
fireEvent.change(textarea, {
|
||||
target: {
|
||||
value: JSON.stringify({
|
||||
...puzzleSpec,
|
||||
playId: 'match3d',
|
||||
}),
|
||||
},
|
||||
});
|
||||
await screen.findByText('pictureDescription');
|
||||
await user.click(screen.getByRole('button', {name: '保存入库'}));
|
||||
|
||||
expect(await screen.findByText('统一创作契约 playId 必须与入口 ID 一致')).toBeTruthy();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@ interface AdminWorkVisibilityPageProps {
|
||||
|
||||
const sourceLabels: Record<string, string> = {
|
||||
puzzle: '拼图',
|
||||
'puzzle-clear': '拼消消',
|
||||
'custom-world': '自定义世界',
|
||||
'jump-hop': '跳一跳',
|
||||
'wooden-fish': '敲木鱼',
|
||||
|
||||
@@ -791,6 +791,67 @@ button:disabled {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.admin-contract-card {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
border: 1px solid #eaded2;
|
||||
border-radius: 8px;
|
||||
background: #fffdf9;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.admin-contract-field-list,
|
||||
.admin-contract-field-editor-list {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.admin-contract-field-list {
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
}
|
||||
|
||||
.admin-contract-field-card,
|
||||
.admin-contract-field-editor {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
border: 1px solid #eaded2;
|
||||
border-radius: 8px;
|
||||
background: #fff8f1;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.admin-contract-field-card strong,
|
||||
.admin-contract-field-card span {
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.admin-contract-field-card strong {
|
||||
color: #3d1f10;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.admin-contract-field-card span {
|
||||
color: #8f7868;
|
||||
font-size: 12px;
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.admin-contract-dialog {
|
||||
width: min(100%, 860px);
|
||||
}
|
||||
|
||||
.admin-contract-field-editor-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.1fr) minmax(120px, 0.6fr) minmax(0, 1fr) auto;
|
||||
gap: 10px;
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.admin-contract-required-toggle {
|
||||
min-height: 42px;
|
||||
}
|
||||
|
||||
.admin-status {
|
||||
display: inline-flex;
|
||||
max-width: 460px;
|
||||
@@ -948,7 +1009,8 @@ button:disabled {
|
||||
.admin-two-column-wide,
|
||||
.admin-form-row,
|
||||
.admin-filter-grid,
|
||||
.admin-table-query-grid {
|
||||
.admin-table-query-grid,
|
||||
.admin-contract-field-editor-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ Linux Docker Engine 若要从宿主机 CLI 连到容器内服务,直接用 `ht
|
||||
|
||||
## 构建工具链
|
||||
|
||||
`api-server` 容器镜像只构建 Linux release API 二进制,不构建 `spacetime-module`。当前 `api-server -> spacetime-client -> spacetimedb-sdk 2.3.0` 依赖链要求 Rust 1.93,因此 `deploy/container/api-server.Dockerfile` 的 Rust builder 固定为 `rust:1.93-bookworm`。镜像构建阶段会同时复制 `public/`,用于满足 API 二进制里 `include_bytes!` 引用的内置素材;不要把 `public/generated-*` 放入镜像上下文。如果本机 Docker Hub 拉取失败,可以先在本机准备同名本地 builder 镜像,但不要把临时 bootstrap 容器或私有 registry 凭据写入仓库。
|
||||
`api-server` 容器镜像只构建 Linux release API 二进制,不构建 `spacetime-module`。当前 `api-server -> spacetime-client -> spacetimedb-sdk 2.4.1` 依赖链要求 Rust 1.93,因此 `deploy/container/api-server.Dockerfile` 的 Rust builder 固定为 `rust:1.93-bookworm`。镜像构建阶段会同时复制 `public/`,用于满足 API 二进制里 `include_bytes!` 引用的内置素材;不要把 `public/generated-*` 放入镜像上下文。如果本机 Docker Hub 拉取失败,可以先在本机准备同名本地 builder 镜像,但不要把临时 bootstrap 容器或私有 registry 凭据写入仓库。
|
||||
|
||||
## 启动与验证
|
||||
|
||||
@@ -71,12 +71,6 @@ npm run container:ps
|
||||
curl -sS http://127.0.0.1:18080/api/runtime/puzzle/gallery
|
||||
```
|
||||
|
||||
如果宿主机 SpacetimeDB CLI 已登录过其他 server,临时容器库可能因为旧 token 返回 `401 InvalidSignature`。本地容器验证可改用匿名身份发布:
|
||||
|
||||
```bash
|
||||
spacetime publish genarrative-loadtest --server http://127.0.0.1:13101 --module-path server-rs/crates/spacetime-module --yes --build-options="--debug" --anonymous
|
||||
```
|
||||
|
||||
查看日志:
|
||||
|
||||
```bash
|
||||
@@ -146,8 +140,6 @@ npm run container:up -- spacetimedb
|
||||
spacetime publish genarrative-loadtest --server http://127.0.0.1:13101 --module-path server-rs/crates/spacetime-module --yes --build-options="--debug"
|
||||
```
|
||||
|
||||
如果遇到临时容器库 token 签名不匹配,按上文给发布命令追加 `--anonymous`。
|
||||
|
||||
发布完成后再执行 `npm run container:up` 和 `npm run container:k6`。如果 `deploy/container/api-server.env` 里的 `GENARRATIVE_SPACETIME_DATABASE` 改成了别的库名,发布命令里的库名也要同步修改。
|
||||
|
||||
如果要压 1000 HTTP req/s,把 `PEAK_RPS` 调到 `500`;如果要压 5000 HTTP req/s,把 `PEAK_RPS` 调到 `2500`,并同时提高 `PREALLOCATED_VUS` / `MAX_VUS`,观察是否先被带宽、Nginx `limit_conn` / `limit_req` 或 api-server 分组背压限制。当前容器 Nginx 对公开 gallery list 使用 `genarrative_gallery_rps`,公开详情和普通 API 使用 `genarrative_api_rps`,后台 API 使用 `genarrative_admin_rps`;api-server 侧对应 `GENARRATIVE_API_GALLERY_MAX_CONCURRENT_REQUESTS`、`GENARRATIVE_API_DETAIL_MAX_CONCURRENT_REQUESTS` 和 `GENARRATIVE_API_ADMIN_MAX_CONCURRENT_REQUESTS`。
|
||||
|
||||
@@ -2,7 +2,7 @@ name: genarrative-container-loadtest
|
||||
|
||||
services:
|
||||
spacetimedb:
|
||||
image: clockworklabs/spacetime:v2.3.0
|
||||
image: clockworklabs/spacetime:v2.4.1
|
||||
user: root
|
||||
command:
|
||||
[
|
||||
|
||||
@@ -10,6 +10,7 @@ User=genarrative
|
||||
Group=genarrative
|
||||
WorkingDirectory=/opt/genarrative/current
|
||||
EnvironmentFile=/etc/genarrative/api-server.env
|
||||
Environment="LD_LIBRARY_PATH=/opt/genarrative/openssl-3.2.0/lib64:/opt/genarrative/openssl-3.2.0/lib"
|
||||
ExecStart=/opt/genarrative/current/api-server
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
@@ -9,10 +9,9 @@ User=root
|
||||
Group=root
|
||||
WorkingDirectory=/opt/genarrative/current
|
||||
EnvironmentFile=/etc/genarrative/api-server.env
|
||||
ExecStart=/usr/bin/node /opt/genarrative/current/scripts/database-backup-to-oss.mjs --env-file /etc/genarrative/api-server.env --stop-service spacetimedb.service
|
||||
ExecStart=/usr/bin/node /opt/genarrative/current/scripts/database-backup-to-oss.mjs --env-file /etc/genarrative/api-server.env --stop-service spacetimedb.service --restart-service-after genarrative-api.service
|
||||
|
||||
# 备份需要停止 / 启动 spacetimedb.service,并读取 /stdb、写入 /var/lib/genarrative/database-backups。
|
||||
PrivateTmp=true
|
||||
ProtectSystem=full
|
||||
ReadWritePaths=/stdb /var/lib/genarrative
|
||||
|
||||
|
||||
@@ -37,6 +37,74 @@ SpacetimeDB 表结构变更、自动迁移边界和保留旧数据的分阶段
|
||||
|
||||
AI 文字游戏模板接入以 [AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_2026-05-05.md](./prd/AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_2026-05-05.md) 为最新口径:只吸收 MOKU / 幕间类 AI 文游的剧本游乐场、自由行动、AI GM、记忆和模拟器强反馈经验,禁止迁入外部社区、支付、榜单、私有存档或回放。
|
||||
|
||||
前端 Server-Sent Events 客户端传输层收口到 `src/services/sseStream.ts`,事件边界、UTF-8 flush、JSON 解析跳过和提前取消约定见 [【前端架构】SSE客户端传输层收口约定-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91SSE%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BC%A0%E8%BE%93%E5%B1%82%E6%94%B6%E5%8F%A3%E7%BA%A6%E5%AE%9A-2026-06-03.md)。
|
||||
|
||||
平台入口公开作品身份、跨玩法去重、公开作品流聚合、推荐运行态 kind 判定、推荐 runtime 启动意图、ready 判定和最新排序收口到 `src/components/platform-entry/platformPublicGalleryFlow.ts`,规则见 [【前端架构】平台入口PublicGalleryFlowModule收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91%E5%B9%B3%E5%8F%B0%E5%85%A5%E5%8F%A3PublicGalleryFlowModule%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
||||
|
||||
统一作品详情页的玩法 kind、详情打开策略、自有作品动作模式、编辑 / 点赞 / 改造 / 启动意图和公开详情映射收口到 `src/components/platform-entry/platformPublicWorkDetailFlow.ts`;抓大鹅公开详情映射与启动 / 编辑 Adapter 的素材归一仍归 `platformMatch3DRuntimeProfile.ts`,规则见 [【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md](./technical/【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md)。
|
||||
|
||||
创作中心作品架打开动作由 `CreationWorkShelfItem.actions.open` 统一承载,生产 Hub 只接收 `CreationWorkShelfItem[]` 与 UI 状态,不再接收各玩法 raw items 和回调列阵,规则见 [【前端架构】WorkShelfModule收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91WorkShelfModule%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
||||
|
||||
作品架删除确认的标题、删除说明、草稿 notice key 和拼图派生稳定 ID 收口到 `src/components/platform-entry/platformCreationWorkDeleteFlow.ts`,平台壳只保留删除 API、刷新、错误和页面跳转副作用,规则见 [【前端架构】CreationWorkDeleteFlow收口计划-2026-06-04.md](./technical/【前端架构】CreationWorkDeleteFlow收口计划-2026-06-04.md)。
|
||||
|
||||
创作入口点击的占位、隐藏模板拦截、未知入口 no-op 与工作台启动目标收口到 `src/components/platform-entry/platformCreationLaunchModel.ts`,壳层只执行启动前准备、错误提示和受保护动作,规则见 [【前端架构】PlatformCreationLaunchModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformCreationLaunchModel收口计划-2026-06-04.md)。
|
||||
|
||||
平台入口公开码搜索的用户 ID、陶泥号、RPG 作品号、各玩法作品号前缀、per-play 公开码匹配、详情卡 DTO 映射和失败回退顺序收口到 `src/components/platform-entry/platformPublicCodeSearchModel.ts`,壳层只按计划执行网络读取、详情打开和错误归航副作用,规则见 [【前端架构】PlatformPublicCodeSearchModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformPublicCodeSearchModel收口计划-2026-06-04.md)。
|
||||
|
||||
个人“玩过作品”面板的玩法别名、`worldKey` 前缀兜底、RPG 公开详情 payload 和大鱼吃小鱼 gallery miss fallback 收口到 `src/components/platform-entry/platformPlayedWorkOpenModel.ts`,壳层只执行面板关闭、gallery 读取、详情打开和错误提示副作用,规则见 [【前端架构】PlatformPlayedWorkOpenModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformPlayedWorkOpenModel收口计划-2026-06-04.md)。
|
||||
|
||||
平台入口生成页进度 tick 的 stage 到生成状态映射、终态判定和视觉小说轻量生成特例收口到 `src/components/platform-entry/platformGenerationProgressTickModel.ts`,壳层只保留 `Date.now()`、`setInterval` 和 cleanup 副作用,规则见 [【前端架构】PlatformGenerationProgressTickModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformGenerationProgressTickModel收口计划-2026-06-04.md)。
|
||||
|
||||
平台壳的拼图 runtime 恢复 work、方洞 session draft 转 profile、视觉小说 work detail 转 Agent session、跳一跳 pending session、敲木鱼 detail 恢复 session、敲木鱼生成中作品摘要和敲木鱼 pending session DTO 映射收口到 `src/components/platform-entry/platformMiniGameSessionMappingModel.ts`,壳层只保留网络、状态、URL 与 stage 副作用,规则见 [【前端架构】PlatformMiniGameSessionMappingModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformMiniGameSessionMappingModel收口计划-2026-06-04.md)。
|
||||
|
||||
平台小游戏生成状态的恢复、失败 / 完成收尾、展示 rebase、拼图后端进度合并、抓大鹅生成资产旁路进度合并和 ready / generating 判定收口到 `src/components/platform-entry/platformMiniGameDraftGenerationStateModel.ts`,壳层只保留 API、后台任务、React state、URL 与 stage 副作用,规则见 [【前端架构】PlatformMiniGameDraftGenerationStateModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformMiniGameDraftGenerationStateModel收口计划-2026-06-04.md)。
|
||||
|
||||
平台小游戏草稿恢复和提交所需的拼图 / 抓大鹅表单 payload、拼图作品更新 payload、拼图编译 action、跳一跳 / 敲木鱼生成 action、pending metadata 与拼图 form-only 草稿判定收口到 `src/components/platform-entry/platformMiniGameDraftPayloadModel.ts`,壳层只保留 API、Action 执行、background task 与状态副作用,规则见 [【前端架构】PlatformMiniGameDraftPayloadModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformMiniGameDraftPayloadModel收口计划-2026-06-04.md)。
|
||||
|
||||
平台拼图生成完成后刷新恢复的草稿归一化与可恢复完成态判定收口到 `src/components/platform-entry/platformPuzzleDraftRecoveryModel.ts`,恢复链路只有在首图、关卡画面、UI spritesheet 与关卡背景资产包完整时才抬为 ready,规则见 [【前端架构】PlatformPuzzleDraftRecoveryModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformPuzzleDraftRecoveryModel收口计划-2026-06-04.md)。
|
||||
|
||||
拼图排行榜提交回包后的服务端 run 快照合并收口到 `src/components/platform-entry/platformPuzzleRuntimeStateModel.ts`,只合并排行榜、run 身份、通关数上限和下一关 handoff,保留前端即时裁决的关卡状态与棋盘,规则见 [【前端架构】PlatformPuzzleRuntimeStateModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformPuzzleRuntimeStateModel收口计划-2026-06-04.md)。
|
||||
|
||||
后端拼图发布 / 待发布门槛收紧到首图、关卡画面、UI spritesheet 与关卡背景资产包完整,`module-puzzle` 的 preview blockers 与 `api-server` 的 session stage 判定保持同一规则,方案见 [【后端架构】PuzzlePublishAssetGate收紧计划-2026-06-04.md](./technical/【后端架构】PuzzlePublishAssetGate收紧计划-2026-06-04.md)。
|
||||
|
||||
平台入口个人钱包本地 delta、dashboard 乐观更新与服务端快照对账规则收口到 `src/components/platform-entry/platformProfileWalletDeltaModel.ts`,平台壳只保留 API、ref 与 state 副作用,规则见 [【前端架构】PlatformProfileWalletDeltaModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformProfileWalletDeltaModel收口计划-2026-06-04.md)。
|
||||
|
||||
Bark Battle 草稿三图完整性、生成状态归一、作品架摘要恢复草稿配置、发布快照 / 发布回包资产兜底和草稿 / 已发布作品进入 runtime 前的 `BarkBattlePublishedConfig` 映射收口到 `src/components/platform-entry/barkBattleWorkCache.ts`,规则见 [【前端架构】BarkBattleWorkCache草稿状态收口计划-2026-06-04.md](./technical/【前端架构】BarkBattleWorkCache草稿状态收口计划-2026-06-04.md)。
|
||||
|
||||
平台首页推荐 runtime 的匿名 Runtime Guest Token、已登录 background auth、非 embedded no-op 和拼图 isolated/default auth mode 计划收口到 `src/components/platform-entry/platformRecommendRuntimeAuthModel.ts`,规则见 [【前端架构】PlatformRecommendRuntimeAuthModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformRecommendRuntimeAuthModel收口计划-2026-06-04.md)。
|
||||
|
||||
平台首页推荐 runtime 自动启动的桌面 / Tab / stage / loading gate、active entry 查找、ready 判定和 clear/start/noop 决策收口到 `src/components/platform-entry/platformPublicGalleryFlow.ts`,规则见 [【前端架构】PlatformRecommendRuntimeAutoStart收口计划-2026-06-04.md](./technical/【前端架构】PlatformRecommendRuntimeAutoStart收口计划-2026-06-04.md)。
|
||||
|
||||
RPG Agent 结果页发布门禁展示和预览来源 label 收口到 `src/components/platform-entry/platformRpgAgentResultPreviewModel.ts`,壳层只保留 session/profile 编排和结果页 props 传递,规则见 [【前端架构】PlatformRpgAgentResultPreviewModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformRpgAgentResultPreviewModel收口计划-2026-06-04.md)。
|
||||
|
||||
平台入口创作生成通知、pending 作品架占位、作品详情更新回填、失败覆盖、跨玩法草稿打开优先级、拼图稳定 ID 和草稿 Tab 未读点收口到 `src/components/platform-entry/platformDraftGenerationShelfModel.ts`,规则见 [【前端架构】DraftGenerationShelfModel收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91DraftGenerationShelfModel%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
||||
|
||||
平台入口创作恢复 URL 私有 query、初始恢复判定、创作直达恢复目标解析、恢复目标身份匹配、跳一跳 / 敲木鱼恢复阶段落点、拼图 runtime query 与拼图稳定身份互推收口到 `src/components/platform-entry/platformCreationUrlStateModel.ts` 和 `src/components/platform-entry/platformPuzzleIdentityModel.ts`,规则见 [【前端架构】CreationUrlStateModel收口计划-2026-06-03.md](./technical/【前端架构】CreationUrlStateModel收口计划-2026-06-03.md)。
|
||||
|
||||
平台入口错误 / 完成弹窗的文案归一、来源格式、候选择一、dismiss key 与任务完成文案收口到 `src/components/platform-entry/platformDialogStateModel.ts`,规则见 [【前端架构】PlatformDialogStateModel收口计划-2026-06-03.md](./technical/【前端架构】PlatformDialogStateModel收口计划-2026-06-03.md)。
|
||||
|
||||
平台入口受保护数据失效后的 stage 去留判定,以及缺失草稿 / 作品 / run 时的阶段回退,收口到 `src/components/platform-entry/platformSelectionStageModel.ts`,壳层只执行缓存清空、布尔事实汇总和必要跳转,规则见 [【前端架构】PlatformSelectionStageModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformSelectionStageModel收口计划-2026-06-04.md)。
|
||||
|
||||
小游戏 runtime client 的路径编码、JSON 请求、runtime guest auth 与 retry 选项收口到 `src/services/runtimeRequest.ts`,Match3D、SquareHole、Big Fish、Bark Battle、Puzzle 公开 / 推荐运行态请求、Jump Hop / Wooden Fish 正式 run 请求和 Visual Novel 局部 JSON runtime 请求已先迁移,规则见 [【前端架构】RuntimeClientFamily收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91RuntimeClientFamily%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
||||
|
||||
抓大鹅 runtime profile 的公开详情转 work、session draft 转 profile、生成背景资产提升和 run/profile/public detail 素材优先级收口到 `src/components/platform-entry/platformMatch3DRuntimeProfile.ts`,规则见 [【前端架构】Match3DRuntimeProfile收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91Match3DRuntimeProfile%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
||||
|
||||
公开作品分类选项、搜索、跨来源去重、今日筛选、排行排序和时间戳解析收口到 `src/components/rpg-entry/rpgEntryPublicGalleryViewModel.ts`,规则见 [【前端架构】PublicGalleryViewModel收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91PublicGalleryViewModel%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
||||
|
||||
公开作品的玩法类型 label、公开作者 lookup 与游玩 / 改造 / 点赞等紧凑计数格式收口到 `src/components/rpg-entry/rpgEntryWorldPresentation.ts`,规则见 [【前端架构】PublicWorkPresentation收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91PublicWorkPresentation%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
||||
|
||||
推荐 feed 的公开作品去重、普通内容过滤、active 窗口与上一条 / 下一条回环选择也收口到 `src/components/rpg-entry/rpgEntryPublicGalleryViewModel.ts`,规则见 [【前端架构】RecommendFeedViewModel收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91RecommendFeedViewModel%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
||||
|
||||
移动端推荐首页 swipe deck 的拖拽阈值、offset clamp、commit 方向、rail class 和分享文案收口到 `src/components/rpg-entry/rpgEntryRecommendSwipeDeckModel.ts`,规则见 [【前端架构】RecommendSwipeDeckModel收口计划-2026-06-03.md](./technical/【前端架构】RecommendSwipeDeckModel收口计划-2026-06-03.md)。
|
||||
|
||||
排行频道的默认 tab、tab 文案、空态文案、排序字段与指标 label/value 收口到 `src/components/rpg-entry/rpgEntryPublicGalleryViewModel.ts`,规则见 [【前端架构】RankingViewModel收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91RankingViewModel%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
||||
|
||||
每日任务卡片与任务中心弹窗的任务选择、进度、状态标签和按钮文案收口到 `src/components/rpg-entry/rpgEntryProfileTaskViewModel.ts`,规则见 [【前端架构】ProfileTaskViewModel收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91ProfileTaskViewModel%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
||||
|
||||
个人数据卡、钱包 chip 与“玩过”弹窗的计数、时长、作品类型和作品号展示收口到 `src/components/rpg-entry/rpgEntryProfileDashboardPresentation.ts`,规则见 [【前端架构】ProfileDashboardPresentation收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91ProfileDashboardPresentation%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
||||
|
||||
个人资金展示的账单来源、金额正负号、余额兜底、充值价格、商品主值和会员摘要收口到 `src/components/rpg-entry/rpgEntryProfileFundsViewModel.ts`,规则见 [【前端架构】ProfileFundsViewModel收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91ProfileFundsViewModel%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。
|
||||
|
||||
## 推荐阅读顺序
|
||||
|
||||
1. 先看 [经验沉淀](./experience/README.md),快速建立这个项目的开发共识。
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# 拼消消玩法模板 PRD
|
||||
|
||||
日期:`2026-05-30`
|
||||
|
||||
## 目标
|
||||
|
||||
新增玩法模板 **拼消消**,工程域与 `playId` 均为 `puzzle-clear`,公开作品码前缀为 `PC-`。拼消消以拼图的交换 / 拖拽手感为原型,但运行态规则独立:玩家移动 1x1 卡牌碎片,把同一复合图案组拼成完整矩形后消除;消除产生空位后,由顶部对应纵列的卡牌准备区下落补位。
|
||||
|
||||
首版必须完成公开闭环:
|
||||
|
||||
```text
|
||||
创作入口 -> 轻表单工作台 -> 独立生成页 -> 结果页 -> 试玩 -> 发布 -> 统一作品详情 -> 正式 runtime -> 基础统计 / 作品架 / 广场
|
||||
```
|
||||
|
||||
## 创作工具平台接入声明
|
||||
|
||||
- 工作台模式:表单 / 图片输入创作工作台。
|
||||
- 创作链路:入口 -> 工作台 -> 生成页 -> 结果页 -> 试玩 -> 发布 -> 运行态。
|
||||
- 单图资产槽位:
|
||||
- `board-background` / `ui-background` / `中央场地底图` / `boardBackgroundPrompt` 优先、空值时回退 `themePrompt`,并支持用户上传图 / 写回 `draft.boardBackgroundAsset`、`draft.boardBackgroundPrompt`、`work.boardBackgroundAsset` 与 `work.boardBackgroundPrompt` / 允许历史图 / 允许 AI 重绘。
|
||||
- 中央场地底图的字段名沿用平台表面口径,实际作用是玩家逐步消除清空中央棋盘后慢慢看到的主题目标图;AI 生成尺寸必须与中央棋盘一致,使用 1:1 正方形画面。prompt 必须强绑定主题、画面精致、强表现力并一眼体现主题,带来探索、揭开全貌和追求目标完成的感受;不得继续要求“画面干净”或“适合作为卡牌棋盘底图”。
|
||||
- 系列素材槽位:
|
||||
- `batchId=puzzle-clear-pattern-atlas-v1`。
|
||||
- `sheetSpec`:4 张素材工作表,每张 `1024x1536` 竖版,后台按 `4 列 x 6 行` 裁切,每个 1x1 单元为 `256x256`;服务端再把切片合成一张 `10x10 / 2560x2560` 最终 atlas。复合图案组总数为 `35`,形状配比 `1x2=23`、`1x3=5`、`2x2=4`、`2x3=3`,总计 `95` 个 1x1 卡牌切片。
|
||||
- `slotSpecs`:每个复合图案组一个 `patternGroup`,服务端预排 `groupId`、`shape`、atlas 坐标和 1x1 切片坐标。
|
||||
- 切图规则:生图 prompt 只要求复合图案组能按 4x6 素材工作表均等切成 1x1 方形小份,不允许模型在图上绘制切分线、边框、网格线或裁切参考线;服务端按 sheet 布局直接裁出 1x1 卡牌碎片,校验每个编号占格数与领域图案组面积一致,再合成最终 atlas,写入 `patternGroups[]` 与 `cardAssets[]`。
|
||||
- 透明化规则:首版保留完整方形卡面,不强制透明化;若 provider 输出带边框、切分线、网格、裁切参考线或文字,生成任务失败并回写审计。
|
||||
- 失败回写:生成页写回 `generationStatus=failed` 与失败阶段;结果页保留重试入口。
|
||||
- 局部重生成:v1 允许整批 4 张素材工作表重试,不做单组局部重生。
|
||||
- API 命名空间:`/api/creation/puzzle-clear/...` 与 `/api/runtime/puzzle-clear/...`。
|
||||
- 业务真相:草稿、发布、runtime snapshot、胜负、补牌、防死局、统计均由后端裁决;前端只做动画和交互表现。
|
||||
- 创作工具模式例外:无。
|
||||
- 验证命令:`npm run check:encoding`、`npm run typecheck`、`npm run test -- src/services/puzzle-clear/puzzleClearLocalRuntime.test.ts`、`npm run test -- src/components/puzzle-clear-result/PuzzleClearResultView.test.tsx src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx`、`cargo test -p module-puzzle-clear --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server puzzle_clear --manifest-path server-rs/Cargo.toml -- --nocapture`;涉及 SpacetimeDB schema 后运行 `npm run spacetime:generate`、`npm run check:spacetime-runtime-access`、`npm run check:spacetime-schema`、`npm run check:server-rs-ddd`。
|
||||
|
||||
## 工作台字段
|
||||
|
||||
| 字段 | 契约字段 | 默认值 | 校验 | 落库 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 作品标题 | `workTitle` | 空 | 必填,1-30 字 | session draft / work profile |
|
||||
| 简介 | `workDescription` | 空 | 0-120 字 | session draft / work profile |
|
||||
| 主题词 | `themePrompt` | 空 | 必填,1-80 字 | 生成 prompt 与草稿 |
|
||||
| 场地底图主题词 | `boardBackgroundPrompt` | 空 | 0-80 字;为空时底图生成回退 `themePrompt` | session draft / work profile / 主题目标图生成 prompt |
|
||||
| 中央场地底图 | `boardBackgroundAsset` | 空 | 上传或 AI 生成至少一种 | 单图资产槽位 |
|
||||
| AI 生成底图 | `generateBoardBackground` | `true` | boolean | 生成编排参数 |
|
||||
|
||||
规则参数不开放创作者编辑:棋盘尺寸、倒计时、消除次数、形状解锁、防死局发牌和半锁定规则固定。
|
||||
|
||||
## 运行规则
|
||||
|
||||
| 关卡 | 棋盘 | 目标消除 | 倒计时 | 解锁形状 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1 | 6x6 | 35 | 10 分钟 | 1x2、1x3、2x2、2x3 |
|
||||
|
||||
- 开局每个小格子从背面翻向正面。
|
||||
- 可消除图由横向或纵向复合图案组组成,最小消除单位为两张图拼接。
|
||||
- 完成一个复合图案组后,该组所有 1x1 卡牌碎片消除。
|
||||
- 消除后空位按列由顶部卡牌准备区下落补齐。
|
||||
- 每次补牌至少保证掉落卡中有一张可以与场上剩余某张卡拼接,防止死局。
|
||||
- 非 2 格消除时,若场上已有局部完成的半锁定拼接组,补牌不得破坏它。
|
||||
- 半锁定拼接组可整体拖动;玩家用外部单格撞入组内某格时,只交换该格,组其余部分保留,组状态退回半完成。
|
||||
- 超时只判当前关失败,可重试当前关;完成 35 次目标并清空当前棋盘后整局完成。
|
||||
|
||||
## 结果页
|
||||
|
||||
结果页展示:素材 atlas、中央场地底图、发布状态、试玩入口和失败重试。结果页不写功能说明类文案,不开放规则编辑器,不新增排行榜配置。
|
||||
|
||||
## 统计
|
||||
|
||||
首版只记录正式 `published` run:
|
||||
|
||||
- 开局。
|
||||
- 全局完成。
|
||||
- 当前关失败。
|
||||
- 耗时。
|
||||
- 消除统计。
|
||||
|
||||
草稿试玩不写正式统计,不进入排行榜;v1 不做排行榜。
|
||||
@@ -205,10 +205,11 @@ WF-*
|
||||
|
||||
1. 若 payload 已包含上传/录音音频资产,`compile-draft` 跳过音效生成,直接持久化该资产;
|
||||
2. 若 payload 已上传或录制音频,则直接写回 `hitSoundAsset`;
|
||||
3. 若两者都没有,后端写回默认木鱼音 `/wooden-fish/default-hit-sound.mp3`;
|
||||
4. 音效资产必须包含可播放地址、对象键、asset object id、来源和可选时长;
|
||||
5. 通用创作音频接口当前对 `wooden_fish` 的 `hit_sound` 目标返回 `410 Gone`,不得在创作流程中按提示词生成音效;
|
||||
6. `spacetime-client` 不得自行合成 `/generated-wooden-fish-assets/...` 音效占位路径;缺少真实 `hitSoundAsset` 时应使用默认木鱼音兜底展示与播放。
|
||||
3. 麦克风录制音频在保存前由前端自动裁掉开头连续静音段;上传音频不做裁剪,裁剪失败时保留原始录音继续保存;
|
||||
4. 若两者都没有,后端写回默认木鱼音 `/wooden-fish/default-hit-sound.mp3`;
|
||||
5. 音效资产必须包含可播放地址、对象键、asset object id、来源和可选时长;
|
||||
6. 通用创作音频接口当前对 `wooden_fish` 的 `hit_sound` 目标返回 `410 Gone`,不得在创作流程中按提示词生成音效;
|
||||
7. `spacetime-client` 不得自行合成 `/generated-wooden-fish-assets/...` 音效占位路径;缺少真实 `hitSoundAsset` 时应使用默认木鱼音兜底展示与播放。
|
||||
|
||||
### 6.3 封面
|
||||
|
||||
@@ -371,7 +372,7 @@ finish
|
||||
|
||||
音频播放:
|
||||
|
||||
1. 前端使用小复音池;
|
||||
1. 前端使用 10 路小复音池;
|
||||
2. 设置最小播放间隔,避免极端连点导致浏览器抖动;
|
||||
3. 点击计数不能因为音频节流而丢失;
|
||||
4. 签名 URL 未就绪时先静音表现,不请求裸 generated 私有路径。
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,46 @@
|
||||
# 【前端架构】Bark Battle Work Cache 草稿状态收口计划
|
||||
|
||||
## 背景
|
||||
|
||||
`PlatformEntryFlowShellImpl.tsx` 仍内联维护 Bark Battle 草稿三图完整性、生成状态归一、作品架摘要恢复草稿配置,以及草稿 / 已发布作品进入 runtime 前的 `BarkBattlePublishedConfig` 映射。壳层因此需要同时理解三图资产字段、`partial_failed` 与 `pending_assets` 的差异、`publishedAt` 兜底、作品摘要字段和草稿试玩配置默认值。
|
||||
|
||||
这些规则属于 Bark Battle 作品摘要与草稿缓存的纯模型。若留在平台壳层,后续发布、作品架刷新、公开详情启动或草稿试玩都容易重复一份字段清单。
|
||||
|
||||
## 决策
|
||||
|
||||
扩展 `src/components/platform-entry/barkBattleWorkCache.ts`,作为 Bark Battle Work Cache **Module** 继续承接作品摘要缓存和草稿 runtime 配置规则。新增公开 **Interface**:
|
||||
|
||||
- `hasBarkBattleDraftRequiredImages(draft)`:判断草稿是否已具备玩家形象、对手形象和竞技背景三图。
|
||||
- `resolveBarkBattleDraftGenerationStatus(draft, partialFailed)`:三图齐备返回 `ready`,否则按是否部分失败返回 `partial_failed` 或 `pending_assets`。
|
||||
- `buildBarkBattleDraftConfigFromWorkSummary(work)`:把作品架摘要恢复成可编辑 / 可试玩的 `BarkBattleDraftConfig`。
|
||||
- `buildBarkBattlePublishedConfigFromDraft(draft)`:把草稿结果页试玩所需配置映射为 `BarkBattlePublishedConfig`。
|
||||
- `buildBarkBattlePublishedConfigFromWork(work)`:把作品架 / 公开详情启动正式 runtime 所需配置映射为 `BarkBattlePublishedConfig`。
|
||||
- `buildBarkBattlePublishSnapshot(draft)`:拼装发布接口所需的最终草稿快照。
|
||||
- `mergeBarkBattlePublishedConfigAssets(published, draft)`:发布回包缺少三图字段时沿用结果页草稿图。
|
||||
|
||||
`PlatformEntryFlowShellImpl.tsx` 继续作为 **Adapter**:它只负责 API 请求、React state、URL、运行态 stage 切换和错误提示,不再持有 Bark Battle 三图完整性与 runtime config 字段清单。
|
||||
|
||||
## Interface 约束
|
||||
|
||||
- 草稿三图必须同时具备 `playerCharacterImageSrc`、`opponentCharacterImageSrc` 和 `uiBackgroundImageSrc` 的非空值,才视为 `ready`。
|
||||
- 未齐三图且 `partialFailed=true` 时返回 `partial_failed`,否则返回 `pending_assets`。
|
||||
- 作品摘要恢复草稿时,`draftId` 缺失回退 `workId`,`description` 来自 summary,三图 null 归一为 `undefined`,`configVersion=1` 且 `rulesetVersion='bark-battle-ruleset-v1'`。
|
||||
- 草稿试玩配置的 `workId` 优先使用草稿稳定 `workId`,缺失时回退 `draftId`。
|
||||
- 草稿试玩配置的 `configVersion` 与 `rulesetVersion` 使用草稿值,缺失时回退 `1` 与 `bark-battle-ruleset-v1`。
|
||||
- 已发布作品配置的 `publishedAt` 缺失时回退 `updatedAt`,保持旧 runtime 启动语义。
|
||||
- 发布快照只携带草稿已有的三图字段,不凭空补空字符串。
|
||||
- 发布接口回包缺少三图字段时,结果页草稿图继续作为 runtime 和作品摘要的兜底。
|
||||
|
||||
## Depth / Leverage / Locality
|
||||
|
||||
- **Depth**:壳层传入草稿或作品摘要,即可得到生成状态、草稿配置或 runtime 配置;字段归一、默认值和三图完整性藏入 Module Implementation。
|
||||
- **Leverage**:作品架草稿恢复、结果页试玩、作品架启动、公开详情启动和缓存刷新可复用同一组 Bark Battle 规则。
|
||||
- **Locality**:Bark Battle 资产完整性与配置映射集中到纯测试面,后续变更三图字段或规则集默认值时无需搜索巨型平台壳。
|
||||
|
||||
## 验收
|
||||
|
||||
- `npm run test -- src/components/platform-entry/barkBattleWorkCache.test.ts`
|
||||
- `npx eslint --max-warnings 0 src/components/platform-entry/barkBattleWorkCache.ts src/components/platform-entry/barkBattleWorkCache.test.ts`
|
||||
- `npx eslint src/components/platform-entry/PlatformEntryFlowShellImpl.tsx --quiet`
|
||||
- `npm run typecheck`
|
||||
- `npm run check:encoding`
|
||||
@@ -0,0 +1,41 @@
|
||||
# CreationUrlStateModel 收口计划
|
||||
|
||||
## 背景
|
||||
|
||||
`PlatformEntryFlowShellImpl.tsx` 曾直接承载多玩法创作恢复 URL 的拼装规则:`sessionId`、`profileId`、`draftId`、`workId` 的优先级、拼图草稿 runtime query、以及空值归一化散在壳层 Implementation 内。平台壳因此需要理解各玩法快照结构,新增玩法或修复刷新恢复时缺少稳定测试面。
|
||||
|
||||
## 决策
|
||||
|
||||
- 新增 `src/components/platform-entry/platformCreationUrlStateModel.ts` 作为 Creation URL State Module。
|
||||
- 该 Module 的 Interface 收口为各玩法 `build*CreationUrlState`、拼图 `buildPuzzle*RuntimeUrlState`、`normalizeCreationUrlValue`、`hasCreationUrlStateValue`、`hasPuzzleRuntimeUrlStateValue`、`buildPuzzleRuntimeUrlStateKey`、初始创作 URL 恢复判定 `resolveInitialCreationUrlRestoreDecision`、创作直达恢复目标解析 `resolveCreationUrlRestoreTarget`、恢复目标身份匹配谓词,以及跳一跳 / 敲木鱼恢复后的阶段落点判定。
|
||||
- 新增 `src/components/platform-entry/platformPuzzleIdentityModel.ts` 作为拼图稳定身份 Module,统一 `puzzle-session-*`、`puzzle-profile-*`、`puzzle-work-*` 的互推规则。
|
||||
- `PlatformEntryFlowShellImpl.tsx` 保留 React state、路由、登录门禁、网络请求和 URL 写入副作用 Adapter;不再在壳层内定义各玩法 URL 状态构造函数,也不直接内联初始恢复的已处理 / 等待 / 可恢复判定。
|
||||
|
||||
## Interface 约束
|
||||
|
||||
- 创作恢复私有 query 只使用 `sessionId`、`profileId`、`draftId`、`workId`;不得新增说明性 query 字段。
|
||||
- 空字符串、全空白字符串统一视为 `null`,避免刷新恢复时写入无效私有参数。
|
||||
- work-backed 玩法优先使用后端 work summary 的公开 `workId` / `profileId`;仅缺失时才回退 session draft。
|
||||
- 拼图 runtime query 独立使用 `mode`、`runtimeSessionId`、`runtimeProfileId`、`runtimeLevelId`、`publicWorkCode`,不与创作恢复 query 混写。
|
||||
- 拼图 draft runtime 若没有 `sourceSessionId`,只允许从 `puzzle-profile-*` 反推出 `puzzle-session-*`。
|
||||
- 初始创作 URL 恢复只在未处理、当前路径属于创作恢复路径、私有 query 有值、平台配置加载完成且受保护数据可读时执行;非创作路径或无私有 query 时标记已处理,加载中或暂不可读时等待。
|
||||
- 创作直达恢复目标由 `resolveCreationUrlRestoreTarget(pathname, state)` 统一识别;它只返回玩法 kind、归一化后的四个私有 query、生成路径标记和大鱼吃小鱼 session 兜底,不执行网络请求、草稿打开、stage 切换或 URL 写回。
|
||||
- 作品 / 草稿身份匹配只允许非空目标值命中,避免 query 缺失时用 `null` / 空值误匹配到无效草稿。匹配谓词仍只判断身份,不触发列表读取或打开动作。
|
||||
- 跳一跳和敲木鱼的恢复阶段落点由 `resolveJumpHopCreationUrlRestoreStage` 与 `resolveWoodenFishCreationUrlRestoreStage` 决定;生成路径优先进入生成页,否则按是否恢复到 draft / work 落到结果页或工作台。
|
||||
- `/creation/rpg` 当前仍不归入具体恢复目标;若后续要恢复 RPG 直达,需要先补明确恢复规则和测试,不得让壳层重新内联路径判定。
|
||||
|
||||
## Depth / Leverage / Locality
|
||||
|
||||
- **Depth**:调用方只传玩法快照或作品摘要,即可得到规范化 URL state;各玩法字段优先级藏在 Module Implementation 内。
|
||||
- **Leverage**:新增或调整玩法恢复规则、恢复目标或恢复等待条件时,优先补 Module Interface 测试,再接壳层 Adapter。
|
||||
- **Locality**:恢复 query、拼图 runtime query 和拼图稳定身份规则集中在两个小 Module,避免散落在页面壳、作品架和 runtime 打开逻辑中。
|
||||
|
||||
## 验收
|
||||
|
||||
- `npm run test -- src/components/platform-entry/platformCreationUrlStateModel.test.ts src/components/platform-entry/platformPuzzleIdentityModel.test.ts`
|
||||
- `npm run test -- src/services/creationUrlState.test.ts`
|
||||
- `npm run test -- src/components/platform-entry/platformDraftGenerationShelfModel.test.ts`
|
||||
- `npx eslint src/components/platform-entry/platformCreationUrlStateModel.ts src/components/platform-entry/platformCreationUrlStateModel.test.ts src/components/platform-entry/platformPuzzleIdentityModel.ts src/components/platform-entry/platformPuzzleIdentityModel.test.ts --max-warnings 0`
|
||||
- `npx eslint src/components/platform-entry/PlatformEntryFlowShellImpl.tsx src/components/platform-entry/platformDraftGenerationShelfModel.ts --quiet`
|
||||
- `npm run typecheck`
|
||||
- `npm run check:encoding`
|
||||
@@ -0,0 +1,33 @@
|
||||
# 【前端架构】Creation Work Delete Flow 收口计划
|
||||
|
||||
## 背景
|
||||
|
||||
平台入口作品架的删除入口覆盖 RPG、拼图、抓大鹅、方洞挑战、大鱼吃小鱼、视觉小说和宝贝识物。此前 `PlatformEntryFlowShellImpl.tsx` 在每个删除 handler 内重复计算确认框标题、删除说明、草稿 notice key 和拼图派生稳定 ID。壳层既要理解每种玩法的作品身份,又要承接异步删除、刷新列表、错误状态和页面跳转,导致删除确认规则缺少稳定测试面。
|
||||
|
||||
该 **Interface** 过浅:页面只想展示“删除哪个作品、会从哪里移除、删除成功后清哪些生成 notice”,却必须知道 `workId` / `profileId` / `sourceSessionId` / `draftId`、`status` / `publicationStatus` / `publishStatus` 和宝贝识物特殊公开去向。
|
||||
|
||||
## 决策
|
||||
|
||||
新增 `src/components/platform-entry/platformCreationWorkDeleteFlow.ts` 作为 Creation Work Delete Flow **Module**。其唯一公开 **Interface** 是 `resolvePlatformCreationWorkDeleteConfirmationModel(input)`,输入为带 `kind` 的 union,输出:
|
||||
|
||||
- `id`:确认框和删除 busy 使用的稳定作品 ID。
|
||||
- `title`:确认框标题,含拼图、视觉小说和宝贝识物标题兜底。
|
||||
- `detail`:草稿 / 已发布删除说明,宝贝识物已发布使用“寓教于乐板块”文案。
|
||||
- `noticeKeys`:删除成功后应标记已读的草稿生成 notice keys,拼图包含 `buildPuzzleResultWorkId` / `buildPuzzleResultProfileId` 派生 key。
|
||||
|
||||
`PlatformEntryFlowShellImpl.tsx` 仍作为副作用 **Adapter**:负责鉴权保护、确认框 state、调用各玩法删除 API、清错误、刷新作品架 / 公开广场、`markDraftNoticeSeen` 和必要的页面跳转。`run` 不进入纯 **Module**,避免把网络副作用和 React state 写入藏入模型层。
|
||||
|
||||
## 约定
|
||||
|
||||
- 新玩法接入作品架删除时,先补齐后端删除链路、作品架 action 和本 **Module** 的确认模型,再开放删除按钮。
|
||||
- Jump Hop、Wooden Fish 和 Bark Battle 当前仅有作品架 action 预留,平台壳不传删除 handler;不得因本 Module 存在而默认开放删除。
|
||||
- 删除确认文案不得散回平台壳;若公开去向不是公开广场,应在本 **Module** 明确分支。
|
||||
- 草稿 notice key 的身份扩展必须复用 `collectDraftNoticeKeys`,保持 trim、去空和去重语义一致。
|
||||
|
||||
## 验证
|
||||
|
||||
- `npm run test -- src/components/platform-entry/platformCreationWorkDeleteFlow.test.ts`
|
||||
- `npm run test -- src/components/platform-entry/platformDraftGenerationShelfModel.test.ts`
|
||||
- `npx eslint src/components/platform-entry/platformCreationWorkDeleteFlow.ts src/components/platform-entry/platformCreationWorkDeleteFlow.test.ts src/components/platform-entry/PlatformEntryFlowShellImpl.tsx --quiet`
|
||||
- `npm run typecheck`
|
||||
- `npm run check:encoding`
|
||||
@@ -0,0 +1,40 @@
|
||||
# 【前端架构】Draft Generation Shelf Model 收口计划
|
||||
|
||||
## 背景
|
||||
|
||||
`PlatformEntryFlowShellImpl.tsx` 同时承载创作生成状态、草稿 Tab 未读点、pending 作品架占位、失败文案覆盖、作品详情更新回填和跨玩法 notice key。拼图、抓大鹅、方洞、跳一跳、敲木鱼、视觉小说、汪汪声浪、大鱼吃小鱼和宝贝识物各有不同的 `workId` / `profileId` / `sourceSessionId` / `draftId`,这些规则散在平台壳 **Implementation** 内,导致调用方必须理解每种玩法的草稿身份形状。
|
||||
|
||||
该 **Interface** 过浅:页面看似只关心“生成中 / 已完成未读 / 失败”,却要知道多 ID 去重、pending 草稿去重、失败摘要、拼图空标题兜底和持久化 generating 覆盖规则。
|
||||
|
||||
## 决策
|
||||
|
||||
新增 `src/components/platform-entry/platformDraftGenerationShelfModel.ts` 作为 Draft Generation Shelf **Module**。其 **Interface** 收口为:
|
||||
|
||||
- `collectDraftNoticeKeys(kind, ids)` / `getGenerationNoticeShelfKeys(item)`:统一把玩法草稿身份映射为 notice key。
|
||||
- `createPendingDraftShelfState(...)` 与 `buildPending*Works(...)`:统一把本地 pending 生成状态映射成作品架占位,并避免与后端已有草稿重复。
|
||||
- `buildCreationWorkShelfRuntimeState({ item, notices, pendingShelfItems })`:统一输出 `CreationWorkShelfRuntimeState`,处理失败覆盖、拼图空标题 `拼图草稿` 兜底、summary 占位覆盖、生成中遮罩和 ready 未读点。
|
||||
- `collectVisibleDraftNoticeKeys(...)` / `hasUnreadDraftGenerationUpdates(...)`:统一草稿 Tab 顶部未读点规则。
|
||||
- `mergePuzzleWorkSummary(current, updated)` 与 `mergeBigFishWorkSummary(current, updated)`:统一作品详情更新后回填作品架和当前详情的身份匹配规则。
|
||||
- `resolvePuzzleDraftOpenIntent(...)`、`resolveMatch3DDraftOpenIntent(...)`、`resolveSquareHoleDraftOpenIntent(...)`、`resolveBigFishDraftOpenIntent(...)`、`resolveVisualNovelDraftOpenIntent(...)`、`resolveJumpHopDraftOpenIntent(...)` 与 `resolveWoodenFishDraftOpenIntent(...)`:统一拼图、抓大鹅、方洞挑战、大鱼吃小鱼、视觉小说、跳一跳和敲木鱼草稿打开时的已发布详情、缺 session、ready 未读试玩、失败 / active / background 生成页、当前结果页、持久化 generating 恢复、失败 fallback stage 和普通草稿恢复优先级。
|
||||
- `buildPuzzleResultWorkId(...)` / `buildPuzzleResultProfileId(...)`、`isPersistedDraftGenerating(...)` / `isPersistedDraftFailed(...)`:把拼图稳定 ID 与持久化状态判断收在同一 **Seam**。
|
||||
|
||||
`PlatformEntryFlowShellImpl.tsx` 仍作为 React state 与副作用 **Adapter**:负责写入 `draftGenerationNotices` / `pendingDraftShelfItems`、读取生成 session、启动 ready 草稿试玩、刷新后端列表、打开结果页和弹窗;它不再内联 pending shelf row shape、notice key 汇总、作品架 runtime state 和上述玩法草稿打开优先级。
|
||||
|
||||
## 约定
|
||||
|
||||
- 新玩法若需进入草稿生成通知,必须在此 **Module** 补 notice key、pending 占位和 visible key 映射,避免在平台壳里新增散落 switch。
|
||||
- pending 作品只用于本地生成任务尚未被后端作品架返回时的临时展示;一旦后端已有同一 `sourceSessionId` / `profileId` / `workId`,pending 占位必须让位。
|
||||
- 拼图作品详情更新只以 `profileId` 匹配回填;大鱼吃小鱼作品详情更新只以 `sourceSessionId` 匹配回填。
|
||||
- 失败 notice 优先级高于持久化 generating,且可通过 pending metadata 提供更具体 summary;否则回退玩法默认失败摘要。
|
||||
- 已有封面的拼图草稿即使局部关卡仍在后台生成,也不得被整卡遮罩为不可打开的生成中状态。
|
||||
- 草稿打开 intent 只返回纯计划、notice keys 与必要稳定 ID,不创建失败生成态、不请求详情、不写 stage;这些仍由壳层 Adapter 执行。
|
||||
- 本 **Module** 不做网络请求、路由切换、弹窗副作用或 React state 写入,只保留纯 **Implementation**,以提高 **Depth**、**Leverage** 与 **Locality**。
|
||||
|
||||
## 验证
|
||||
|
||||
- `npm run test -- src/components/platform-entry/platformDraftGenerationShelfModel.test.ts`
|
||||
- `npm run test -- src/components/custom-world-home/creationWorkShelf.test.ts -t "generation state|failure notice|failed puzzle"`
|
||||
- `npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "persisted generating puzzle draft|persisted generating match3d draft|completed baby object match draft"`
|
||||
- 针对新 **Module** 与测试执行 ESLint;`PlatformEntryFlowShellImpl.tsx` 保留既有 hook dependency warnings,不在本切片扩大处理。
|
||||
- `npm run typecheck`
|
||||
- `npm run check:encoding`
|
||||
@@ -0,0 +1,34 @@
|
||||
# 【前端架构】Match3D Runtime Profile 收口计划
|
||||
|
||||
## 背景
|
||||
|
||||
`PlatformEntryFlowShellImpl.tsx` 同时编排抓大鹅创作、作品详情、推荐 runtime 和正式 runtime。运行态启动前的 profile 规范化、公开详情转 work、生成背景资产提升、run / profile / public detail 优先级和 runtime 素材选择原本都在平台壳 **Implementation** 内,导致平台壳必须理解抓大鹅生成素材的内部结构。
|
||||
|
||||
## 决策
|
||||
|
||||
新增 `src/components/platform-entry/platformMatch3DRuntimeProfile.ts`,作为抓大鹅 runtime profile **Module**。该 **Module** 的 **Interface** 收口为:
|
||||
|
||||
- `mapPublicWorkDetailToMatch3DWork(entry)`:把公开作品详情映射为可启动 runtime 的 Match3D work,并补齐生成背景资产。
|
||||
- `buildMatch3DProfileFromSession(session)`:从创作 session draft 生成 runtime profile。
|
||||
- `normalizeMatch3DWorkForRuntimeUi(profile)` / `mapMatch3DWorksForRuntimeUi(profiles)`:统一作品列表进入 UI / runtime 前的素材规范化。
|
||||
- `promoteMatch3DGeneratedBackgroundAsset(profile)`:从 `generatedBackgroundAsset` 或 `generatedItemAssets[].backgroundAsset` 提升背景图、对象 key 与 prompt。
|
||||
- `hasMatch3DRuntimeAsset(profile.generatedItemAssets)` / `hasMatch3DRuntimeBackgroundAsset(profile)`:统一判断 runtime 是否具备物品与背景素材。
|
||||
- `resolveActiveMatch3DRuntimeProfile(run, runtimeProfile, profile)`:按 run 的 `profileId` 选择当前 profile,避免切屏时误用旧草稿。
|
||||
- `resolveMatch3DRuntimeGeneratedItemAssets(...)`、`resolveMatch3DRuntimeGeneratedBackgroundAsset(...)`、`resolveMatch3DRuntimeBackgroundImageSrc(...)`:统一 run / profile / public detail 的素材优先级。
|
||||
|
||||
`PlatformEntryFlowShellImpl.tsx` 只保留启动 run、预加载、路由、错误和 state 编排;抓大鹅素材规则集中到该 **Module**,提升 **Locality** 与测试 **Leverage**。
|
||||
|
||||
## 约定
|
||||
|
||||
- 公开详情补 runtime 素材时,只有 `profileId` 与 run 匹配才优先使用公开详情;错配时不得污染当前 run。
|
||||
- 当前启动时拿到的 `runtimeProfile` 优先于旧草稿 profile;若 run 指向旧草稿 profile,才使用草稿 profile。
|
||||
- 背景资产提升不得覆盖已有显式 `backgroundImageSrc` / `backgroundImageObjectKey` / `generatedBackgroundAsset`,只补缺。
|
||||
- 本 **Module** 只放纯 profile / asset 规则,不引入启动 run、预加载、URL、状态机或 UI 副作用。
|
||||
|
||||
## 验证
|
||||
|
||||
- `npm run test -- src/components/platform-entry/platformMatch3DRuntimeProfile.test.ts`
|
||||
- `npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "match3d|抓大鹅"`
|
||||
- `npm run typecheck`
|
||||
- `npm run check:encoding`
|
||||
- 针对新 Module 与测试执行 ESLint;`PlatformEntryFlowShellImpl.tsx` 保留既有 hook dependency warnings,不在本切片扩大处理。
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user