This commit is contained in:
2026-04-25 22:19:04 +08:00
parent 2ebfd1cf55
commit 8404081d7b
149 changed files with 10508 additions and 2732 deletions

View File

@@ -0,0 +1,19 @@
# 平台首页 Banner 图尺寸修复记录
更新时间:`2026-04-25`
## 问题
首页 Hero / banner 使用作品封面作为背景图。全局 `.platform-surface > *` 会把所有直接子节点重新设为 `position: relative`,导致带有 Tailwind `absolute` 类的背景图和遮罩被覆盖,图片重新进入普通文档流后可能撑高首页,影响首屏布局。
## 落地
- 修改 `src/index.css`,将 `.platform-surface > *` 收敛为 `.platform-surface > :not(.absolute)`
- 保留普通内容层的 `z-index: 1`,让文字、按钮仍稳定压在背景之上。
- 让 banner 背景图继续使用绝对定位和 `object-cover`,只在固定容器内裁切显示,不参与首页高度计算。
## 经验
- 首页、结果页、详情页的背景图都必须由固定尺寸容器承载,图片本身不要参与布局流。
- 给通用容器写层级规则时,不能无差别覆盖 `.absolute` 节点,否则会破坏所有“背景图 + 遮罩 + 内容”的结构。
- 后续若新增平台 Hero优先沿用 `platform-surface--hero`,并确认背景图节点仍是 `absolute inset-0 h-full w-full object-cover`

View File

@@ -250,13 +250,27 @@ node scripts/vite-cli.mjs --port=3000 --host=0.0.0.0
这类项目里,后者几乎一定导致返工。
## 13. 一句话总结
## 13. 后端修改后的重启与测试
后端代码更新后统一执行:
```bash
npm run api-server:maincloud
```
执行要求:
- 该命令是后端更新后的默认重启入口,不再使用此前的后端重启命令。
- 重启后必须继续执行与本次后端改动对应的自动测试;涉及 Rust workspace 时优先跑 `server-rs` 下的检查或测试脚本。
- 若本次改动涉及 SpacetimeDB 发布、绑定生成或 Maincloud 联调,按 `spacetimedb-cli` 经验执行,并在验证记录中写清楚实际命令与结果。
## 14. 一句话总结
这个项目最重要的经验不是“做了多少页面和功能”,而是:
**必须把 AI 文本生成、本地规则、动画演出、场景状态、编辑器工具这几套系统严格分层,再通过 function 和统一状态流把它们重新接起来。**
## 14. 相关文档
## 15. 相关文档
如需继续细看已有沉淀,可结合以下文档一起阅读:

View File

@@ -27,3 +27,5 @@
## 近期专项记录
- [RPG_DRAFT_IMAGE_PARALLEL_GENERATION_2026-04-24.md](./RPG_DRAFT_IMAGE_PARALLEL_GENERATION_2026-04-24.md):记录 RPG 底稿阶段角色主形象与场景背景图并行生成约束。
- [PLATFORM_HOME_BANNER_IMAGE_SIZE_FIX_2026-04-25.md](./PLATFORM_HOME_BANNER_IMAGE_SIZE_FIX_2026-04-25.md):记录首页 banner 背景图不能进入普通布局流的修复经验。
- [RPG_PUBLISH_GALLERY_REFRESH_FIX_2026-04-25.md](./RPG_PUBLISH_GALLERY_REFRESH_FIX_2026-04-25.md):记录 RPG 发布后首页 / 分类页公开作品列表刷新链路。

View File

@@ -0,0 +1,36 @@
# RPG 发布作品广场刷新修复 2026-04-25
## 背景
已发布 RPG 作品会进入 `custom_world_profile`,并由 SpacetimeDB 模块同步到公开读模型 `custom_world_gallery_entry`。平台首页和分类页不直接读取“我的作品”,而是共同消费 `/api/runtime/custom-world-gallery` 返回的公开作品列表。
本次问题表现为:结果页显示发布完成后,作品没有立即出现在平台首页和分类页。
## 修复原则
1. `publish_world` 必须写入真实作者公开信息。
- Axum 层在执行 `publish_world` 前补充 `authorPublicUserCode``authorDisplayName`
- SpacetimeDB 模块发布时优先使用 payload 中的作者公开信息,再退回历史兜底。
2. 发布完成后必须刷新公开广场列表。
- 前端 `executePublishWorld` 等待发布 operation 完成后,同时刷新 `refreshPublishedGallery()``refreshCustomWorldWorks()`
- 首页和分类页都复用 `publishedGalleryEntries`,因此刷新 gallery 后两个页面会同步看到新发布作品。
## 多玩法公开列表补充
- 平台首页 / 分类页不是只展示 RPG 作品,也需要展示已经发布到公开接口的 Puzzle 作品。
- Puzzle 已有 `/api/runtime/puzzle/gallery` 公开接口;平台入口额外读取该接口,并把 `PuzzleWorkSummary` 映射为首页卡片模型。
- 首页 / 分类页按 `rpg:{ownerUserId}:{profileId}``puzzle:{ownerUserId}:{profileId}` 去重后按发布时间排序。
- 点击 RPG 卡片仍进入 RPG 公开详情;点击 Puzzle 卡片进入现有 Puzzle 广场详情,不复用 RPG 详情链路。
3. 历史已发布作品必须能自动补齐 gallery 投影。
- 公开列表读取 `list_custom_world_gallery_entries` 前,会扫描 `custom_world_profile` 中已发布且未删除的 profile。
- 若已发布 profile 缺少 `custom_world_gallery_entry`,或缺少公开作品码 / 作者叙世号,会先补齐公开字段并同步 gallery 投影。
- 这样旧版本发布成功但未落入广场读模型的作品,在下一次首页 / 分类页读取公开列表时会自动出现。
## 经验
- 作品发布链不能只看“我的创作”列表,必须同时检查公开读模型。
- 分类页的数据不是独立接口,修首页公开列表通常也会影响分类页。
- Agent 发布链和普通 library profile 发布链要共享公开作者字段语义,避免同一个 gallery card 在不同发布入口下字段不一致。
- 已发布 profile 与 gallery 投影不是同一张表,线上修复时要考虑历史数据补投影,不能只修新增发布路径。

View File

@@ -0,0 +1,46 @@
# 后端创作 Agent LLM Turn 公共化 2026-04-25
## 背景
RPG、大鱼吃小鱼、拼图三条创作 Agent 后端 turn 已经统一使用 `platform-llm`,但在 `api-server` 内仍重复维护以下流程:
1. 检查 `LlmClient` 是否可用。
2. 构造 `system + user` 两段消息。
3. 调用 `stream_text` 并从增量 JSON 中抽取 `replyText` 给 SSE 前端。
4. 从模型最终文本中截取并解析 JSON。
5. 把模型调用失败、JSON 解析失败映射成中文业务错误。
这些逻辑属于 turn 级基础设施,不应散落在不同玩法文件里;但各玩法的 prompt、anchor pack 解析、stage 推进、SpacetimeDB finalize 写回仍是领域逻辑,不在本轮合并。
## 目标
1. 新增 `api-server` 内部公共模块 `creation_agent_llm_turn`
2. 公共化流式 JSON turn 调用、非流式 JSON turn 调用、`replyText` 增量解析、最终 JSON 截取解析。
3. 大鱼、拼图、RPG Agent turn 复用公共调用骨架。
4. 保留各玩法原有结果结构、中文错误文案和持久化写回契约。
## 非目标
1. 不统一 RPG、大鱼、拼图的 prompt。
2. 不统一三类 anchor pack / draft profile schema。
3. 不改变 SpacetimeDB reducer/procedure 或 session 表结构。
4. 不改变前端 SSE contract。
## 落地边界
1. `server-rs/crates/api-server/src/creation_agent_llm_turn.rs`
- 提供 `stream_creation_agent_json_turn(...)`
- 提供 `request_creation_agent_json_turn(...)`
- 提供 `parse_json_response_text(...)``extract_reply_text_from_partial_json(...)`
2. `custom_world_agent_turn.rs`
- 保留 RPG 动态状态判断、八锚点解析、结果写回。
- 将正式单轮生成和状态识别的 LLM 请求改走公共模块。
3. `big_fish_agent_turn.rs` / `puzzle_agent_turn.rs`
- 将 LLM 流式请求和 JSON 解析改走公共模块。
- 继续保留玩法自己的 anchor pack 解析和 quick fill 规则。
## 验收
1. `cargo test -p api-server` 中相关 turn 单测通过。
2. `cargo check -p api-server` 不引入新的编译错误。
3. 编码检查通过。

View File

@@ -0,0 +1,55 @@
# 角色主形象 IP 审核失败兜底修复2026-04-25
## 1. 问题
自动生成草稿素材时,角色「艾瑞克」主形象连续 3 次失败,供应商返回:
```json
{
"code": "IPInfringementSuspect",
"message": "Input data is suspected of being involved in IP infringement."
}
```
这类失败不是网络抖动。原样重试会把同一份包含角色姓名、长设定和可能触发审核的专名继续提交给 DashScope容易稳定失败。
## 2. 参考日志与文档
- 用户提供的失败日志:`request_id=a18fb05d-d3be-9b9c-8d37-e0427397789e``task_id=cb768c95-13b7-4790-9f18-35a8a8761b31``task_status=FAILED``code=IPInfringementSuspect`。该日志确认失败源于供应商 IP/内容审核,而不是 OSS、SpacetimeDB 或下载链路。
- `docs/audits/CHARACTER_ASSET_PROMPT_CHAIN_AUDIT_2026-04-20.md`:确认角色主形象正式模型 prompt 层由后端 prompt builder 编译,不能只改前端默认描述文本。
- `docs/technical/CUSTOM_WORLD_ASSET_PROMPT_DEFAULTS_2026-04-24.md`:确认当前主链已迁到 `server-rs/crates/api-server/src/custom_world_asset_prompts.rs``server-rs/crates/api-server/src/custom_world_ai.rs`,不再修改旧 `server-node`
- `docs/technical/ASSET_EXTERNAL_GENERATION_MANUAL_VERIFICATION_RUNBOOK_2026-04-23.md`:确认角色主形象真实生成入口走 Rust `api-server`、DashScope、OSS 与资产绑定链路。
- `server-rs/crates/api-server/src/character_animation_assets.rs` 现有日志/代码经验:动作生成已经有审核失败时的安全兜底 prompt 策略,本次沿用到角色主形象。
## 3. 修复方案
1. 在角色主图 prompt 层新增安全兜底 prompt
- 不携带角色姓名、作品名、长设定原文。
- 保留职业原型,如原创近战剑士、原创法术职业冒险者。
- 明确要求不参考现有动漫、游戏、影视、小说角色,不使用可识别 IP 元素。
2. 在 DashScope 角色主形象请求层识别审核类错误:
- `IPInfringementSuspect`
- `inappropriate`
- `sensitive`
- `risk`
- 中文内容审核、疑似侵权、知识产权等关键词。
3. 首次提交遇到上述错误时,后端自动用安全兜底 prompt 再提交一次。
4. 非审核类错误仍按原错误返回不隐藏模型、网络、OSS、超时等真实问题。
5. 错误映射保留 `raw` 字段,便于后续从日志直接看到供应商原始 `code/message/task_id`
## 4. 落地文件
- `server-rs/crates/api-server/src/prompt/character_visual.rs`
- 新增 `build_fallback_moderation_safe_character_visual_prompt`
- `server-rs/crates/api-server/src/custom_world_asset_prompts.rs`
- 导出角色主图审核兜底 prompt builder。
- `server-rs/crates/api-server/src/character_visual_assets.rs`
- 角色主形象 DashScope 请求增加审核兜底提交。
- `RequestModel` 阶段结构化日志增加 `moderationFallbackApplied`
- DashScope 上游错误保留 `raw`
## 5. 验收口径
- 用户不需要手动改「艾瑞克」这个名字;后端遇到 `IPInfringementSuspect` 会自动切换到原创安全 prompt 再试一次。
- 若兜底 prompt 生成成功,后续 OSS 草稿、发布和资产绑定链路保持原样。
- 若兜底 prompt 仍失败,错误中仍能看到 DashScope 原始失败内容,方便继续定位具体触发项。

View File

@@ -0,0 +1,41 @@
# 创作 Agent Client 与平台流程 Controller 复用方案 2026-04-25
## 背景
RPG、自定义大鱼吃小鱼、拼图三类作品创作都已经采用 Agent-first 主链,但前端仍存在两类重复:
1. 三类 Agent client 都手写 `createSession / getSession / sendMessage / streamMessage / executeAction`
2. 平台入口壳层内直接维护大鱼、拼图的 session、流式消息、忙碌态、错误态与草稿恢复逻辑。
聊天 UI、SSE 解析、快捷补齐已经有共用模块,本轮只补齐 client 与平台流程编排层的复用,不改玩法领域数据结构。
## 目标
1. 新增通用 `createCreationAgentClient` 工厂统一请求、SSE POST、retry、超时与中文错误文案配置。
2. 大鱼、拼图 client 保留原导出函数名但内部先改走通用工厂RPG client 暂保留既有成熟实现,后续只有在不影响自动保存/结果预览语义时再接入。
3. 新增平台创作流程 controller hook收口轻量玩法的创建会话、恢复草稿、发送流式消息与执行 action。
4. 平台壳层只保留玩法差异动作:运行态启动、作品删除、拼图公开详情跳转等。
## 非目标
1. 不统一 RPG、大鱼、拼图的 session schema。
2. 不把大鱼或拼图强行接入 RPG 的发布门禁、自动保存、运行时协议。
3. 不改后端 SpacetimeDB 表、procedure 或现有路由。
## 落地边界
1. `src/services/creation-agent/creationAgentClientFactory.ts`
- 负责统一 HTTP/SSE client 骨架。
- 允许每个玩法配置 basePath、runtime 前缀、错误文案、返回值提取方式。
2. `src/components/platform-entry/usePlatformCreationAgentFlowController.ts`
- 负责通用前端流程态session、busy、error、streaming、open、restore、submit、execute。
- 通过 adapter 接收玩法差异client、stage、compile action、session 是否已有 draft。
3. `PlatformEntryFlowShellImpl.tsx`
- 大鱼与拼图切到 controller。
- 保留 RPG 现有成熟 controller不在本轮合并避免把 RPG 复杂自动保存链拉进轻量玩法抽象。
## 验收
1. 已接入工厂的 Agent client 公开 API 不变RPG client 公开 API 与既有实现都不变。
2. 大鱼、拼图仍能从平台入口新建、恢复草稿、发送消息、生成结果页。
3. 现有定向测试通过,编码检查通过。

View File

@@ -0,0 +1,76 @@
# Agent 创作页文档输入上传方案
更新时间:`2026-04-25`
## 1. 目标
Agent 创作页需要支持用户上传文档,并把文档内容解析成当前输入框里的文本,让用户可以继续编辑后再发送给 Agent。
本次只解决“文档作为输入内容”的轻量闭环,不把文件作为资产入库,也不改变 Agent 会话、消息、草稿生成的后端主链。
## 2. 职责边界
1. 前端 `CreationAgentWorkspace` 负责展示上传入口、先做文件格式与大小预检、读取浏览器选择的文件为 base64、调用解析接口、把返回文本追加到输入框。
2. Rust `api-server` 负责文件类型、大小、编码与文本抽取规则,前端不直接承载文档解析逻辑。
3. 解析完成后仍走现有 `onSubmitText` 消息提交,不新增 Agent 消息结构。
4. 该能力覆盖所有复用 `CreationAgentWorkspace` 的 Agent 创作页RPG / 自定义世界、拼图、大鱼吃小鱼。
## 3. 首版支持范围
支持扩展名:
1. `.txt`
2. `.md`
3. `.markdown`
4. `.csv`
5. `.json`
大小限制:单文件最大 `256 KiB`
编码限制:首版按 UTF-8 文本处理。若文件不是 UTF-8服务端返回 `400`,由前端展示错误。
暂不支持 `.pdf` / `.doc` / `.docx` 的二进制结构解析;后续扩展时只需要在 Rust 解析接口内部补 extractor不改变前端输入框接入方式。
## 4. 接口设计
路径:`POST /api/runtime/creation-agent/document-inputs/parse`
鉴权Bearer 登录态。
请求:
```json
{
"fileName": "世界设定.md",
"contentType": "text/markdown",
"contentBase64": "..."
}
```
响应:
```json
{
"document": {
"fileName": "世界设定.md",
"contentType": "text/markdown",
"sizeBytes": 128,
"text": "..."
}
}
```
## 5. UI 规则
1. 输入框左侧新增文件图标按钮,使用图标与 hover title 表达,不在页面铺功能说明文本。
2. 上传前先在浏览器侧拒绝不支持格式、空文件和超限文件,避免无意义读取大文件。
3. 上传处理中禁用按钮和发送按钮,避免同一输入框状态并发写入。
4. 文件解析成功后追加到当前草稿文本后方,若当前草稿非空则用两个换行分隔。
5. 错误展示复用输入区附近的短错误条,不弹独立面板。
## 6. 验收标准
1. 上传 `.txt``.md` 后,输入框出现文档内容。
2. 输入框已有内容时,解析文本追加在末尾,并用空行分隔。
3. 上传不支持格式或超限文件时,页面展示中文错误,不发送 Agent 消息。
4. 定向测试覆盖解析客户端、Rust 接口和 `CreationAgentWorkspace` 上传交互。

View File

@@ -0,0 +1,24 @@
# 创作 Agent 发送后即时等待点动画修复
日期:`2026-04-25`
## 1. 背景
统一创作 Agent 工作区已经用 `CreationAgentWorkspace` 承载 RPG / Custom World、大鱼吃小鱼、拼图三条聊天链路。旧展示条件只有在 `streamingReplyText` 已经收到文本时才追加临时 assistant 气泡,因此用户发送消息后到首个 SSE token 到达前,聊天区会短暂没有任何等待反馈。
## 2. 设计
本轮只改前端表现层不改变后端会话、SSE、消息落库或推荐回复语义
1. `CreationAgentWorkspace``isStreamingReply` 作为临时 assistant 气泡的展示条件。
2.`streamingReplyText` 为空时,临时气泡内部展示三个脉冲点。
3. 当首个流式文本到达后,同一个临时气泡切换为文本内容与光标。
4. 最终 session 回写后,临时气泡消失,由正式 assistant 消息接管原位置。
5. 大鱼吃小鱼与拼图适配层显式透传 `isStreamingReply`,不再用 `Boolean(streamingReplyText)` 推断等待状态。
## 3. 验收
1. 用户发送消息后,聊天列表底部立即出现三点等待动画。
2. 首个 SSE 文本到达前等待动画持续存在。
3. 流式文本到达后等待动画切换为正常流式回复。
4. `CreationAgentWorkspace` 定向测试覆盖空文本流式等待态。

View File

@@ -0,0 +1,46 @@
# 作品货架统一 2026-04-25
## 背景
创作中心目前已经把 RPG、大鱼吃小鱼、拼图三类作品展示在同一个网格里但前端组件仍直接消费三类原始 works
1. RPG 使用 `status``title``subtitle``canEnterWorld`
2. 大鱼使用 `status``title`、资源完成度字段。
3. 拼图使用 `publicationStatus``levelName``authorDisplayName``themeTags`
这导致筛选、计数、按钮文案、卡片标题、副标题、标签、删除忙碌态都在 UI 组件里做三套判断。后续再接新作品类型时,货架组件会继续膨胀。
## 目标
1. 新增前端统一作品货架视图模型 `CreationWorkShelfItem`
2. 由归一化函数把 RPG / Big Fish / Puzzle works 映射成统一字段。
3. `CustomWorldCreationHub` 只负责筛选、空态和动作分发。
4. `CustomWorldWorkCard` 只负责渲染统一字段,不再理解三类原始 schema。
## 非目标
1. 本轮不新增后端统一 works 聚合接口。
2. 不改变三类现有 works API contract。
3. 不改变平台首页公开广场的 gallery 合并逻辑。
4. 不改变删除、体验、恢复草稿的业务规则。
## 统一字段
`CreationWorkShelfItem` 至少包含:
1. `id`:稳定货架 id。
2. `kind``rpg | big-fish | puzzle`
3. `status``draft | published`
4. `title / subtitle / summary / updatedAt`
5. `coverImageSrc / coverRenderMode / coverCharacterImageSrcs`
6. `badges`:状态、类型、阶段、标签等展示徽标。
7. `metrics`:角色数、地点数、素材完成度、游玩数等底部指标。
8. `openActionLabel`:卡片无障碍文案与主动作语义。
9. `source`:保留原始 work用于平台壳层执行动作。
## 验收
1. 创作中心三类作品仍在同一个网格展示。
2. 草稿 / 已发布筛选计数统一从 `CreationWorkShelfItem.status` 读取。
3. 卡片渲染不再直接判断 `publicationStatus` 或不同 works schema 的标题字段。
4. 现有创作中心交互测试通过。

View File

@@ -0,0 +1,48 @@
# 前端页面独立路由路径说明
## 背景
平台入口、RPG 创作链、拼图创作链和大鱼吃小鱼创作链已经在 `PlatformEntryFlowShellImpl` 中通过 `selectionStage` 分阶段渲染。此前多数页面共享同一个浏览器路径,导致刷新、复制地址和浏览器前进后退时缺少清晰页面语义。
本轮目标是在不引入 React Router、不拆现有页面组件的前提下为现有主要页面分配稳定路径并让内部阶段切换同步浏览器地址。
## 路由原则
- `src/routing/appRoutes.tsx` 继续只负责应用级入口:正式主应用、拼图调试直达页、大鱼吃小鱼调试直达页。
- 正式主应用内部页面路径由 `src/routing/appPageRoutes.ts` 统一维护,不在组件里散落硬编码字符串。
- `/puzzle``/big-fish` 保持为玩法调试直达入口;正式链路中的拼图和大鱼运行页使用 `/runtime/puzzle``/runtime/big-fish`,避免语义冲突。
- 独立路径先解决页面阶段语义和浏览器前进后退;依赖运行中内存对象的详情页、结果页和运行页直接刷新后仍允许回退到平台首页或展示现有恢复态,不在本轮扩展资源 ID 深链加载。
## 页面路径表
| 页面阶段 | 路径 | 说明 |
| --- | --- | --- |
| `platform` | `/` | 平台首页、广场、我的、创作中心等主入口 |
| `detail` | `/worlds/detail` | RPG 世界详情页,依赖当前已选作品 |
| `agent-workspace` | `/creation/rpg/agent` | RPG Agent 共创工作区 |
| `custom-world-generating` | `/creation/rpg/generating` | RPG 世界草稿生成进度页 |
| `custom-world-result` | `/creation/rpg/result` | RPG 世界结果页与编辑页 |
| `big-fish-agent-workspace` | `/creation/big-fish/agent` | 大鱼吃小鱼 Agent 共创工作区 |
| `big-fish-result` | `/creation/big-fish/result` | 大鱼吃小鱼草稿结果页 |
| `big-fish-runtime` | `/runtime/big-fish` | 正式链路中的大鱼吃小鱼运行页 |
| `puzzle-agent-workspace` | `/creation/puzzle/agent` | 拼图 Agent 共创工作区 |
| `puzzle-result` | `/creation/puzzle/result` | 拼图草稿结果页 |
| `puzzle-gallery-detail` | `/gallery/puzzle/detail` | 拼图作品详情页,依赖当前已选作品 |
| `puzzle-runtime` | `/runtime/puzzle` | 正式链路中的拼图运行页 |
| RPG 选角页 | `/runtime/rpg/characters` | 进入世界后、确认角色前的选角阶段 |
| RPG 冒险页 | `/runtime/rpg/adventure` | 已确认角色后的 RPG 主运行态 |
## 落地边界
- `useRpgRuntimeOverlayState` 初始化时从当前路径推导 `selectionStage`
- `setSelectionStage(...)` 被统一包一层,阶段变化时同步 `history.pushState`
- RPG 选角和冒险运行态由 `RpgRuntimeShell` 按当前 `gameState` 同步路径。
- 浏览器 `popstate` 时只回写 `selectionStage`,不重建详情页依赖的业务对象。
- 已有 `/puzzle``/big-fish` 调试入口继续由应用级路由分流,不进入 `selectionStage`
## 验收口径
1. 访问 `/creation/rpg/agent``/creation/puzzle/agent``/creation/big-fish/agent` 能进入主应用并初始化到对应页面阶段。
2. 从页面内切换到结果页、运行页或返回首页时,浏览器路径同步更新。
3. 浏览器后退/前进能驱动 `selectionStage` 回到对应页面。
4. `/puzzle``/big-fish` 仍进入原有玩法调试直达页。

View File

@@ -0,0 +1,87 @@
# “我的”账户充值弹窗落地设计
日期:`2026-04-25`
## 1. 范围
本轮在“我的”页面的“会员充值”入口落地账户充值弹窗,包含两个页签:
1. `积分充值`
2. `会员卡充值`
前端只负责展示与发起购买,套餐、价格、赠送规则、会员权益、生效时间、钱包余额与交易流水统一由 `server-rs` 后端返回。当前没有真实支付网关,本轮采用服务端模拟支付成功:创建订单后立即写入余额或会员状态,并返回最新账户中心快照。后续接入真实支付时,只替换订单支付状态推进,不改前端套餐与账户快照 contract。
## 2. 产品规则
### 2.1 积分充值套餐
| productId | 积分 | 金额分 | 徽标 | 说明 |
| --- | ---: | ---: | --- | --- |
| `points_10` | 10 | 100 | 首充送积分 | 首充送19积分 |
| `points_60` | 60 | 600 | 无首充赠礼 | 无首充赠送 |
| `points_240` | 240 | 2400 | 首充双倍 | 首充送240积分 |
| `points_450` | 450 | 4500 | 首充双倍 | 首充送450积分 |
| `points_950` | 950 | 9500 | 首充双倍 | 首充送950积分 |
| `points_1980` | 1980 | 19800 | 首充双倍 | 首充送1980积分 |
首充赠送只按用户维度判断一次:用户历史上没有 `points_recharge` 流水时,购买支持首充赠送的套餐才发放赠送积分。实际到账积分写入交易流水,余额以 SpacetimeDB projection 为准。
### 2.2 会员卡套餐
| productId | 类型 | 天数 | 金额分 | 权益 |
| --- | --- | ---: | ---: | --- |
| `member_month` | 月卡 | 30 | 2800 | 免积分回合数100每日签到加成0% |
| `member_season` | 季卡 | 90 | 7800 | 免积分回合数100每日签到加成100% |
| `member_year` | 年卡 | 365 | 24800 | 免积分回合数100每日签到加成210% |
购买会员时,如果当前会员仍有效,则从当前到期时间顺延;如果已过期或从未购买,则从当前服务端时间开始计算。状态只区分 `普通` 与已生效会员,前端不自行推断。
## 3. 后端接口
### 3.1 `GET /api/profile/recharge-center`
需要 Bearer JWT。返回
1. 当前积分余额、会员状态、到期时间
2. 积分套餐与会员套餐
3. 会员权益表
4. 最近订单摘要
兼容路径:`GET /api/runtime/profile/recharge-center`
### 3.2 `POST /api/profile/recharge/orders`
需要 Bearer JWT。请求
```json
{
"productId": "points_240",
"paymentChannel": "mock"
}
```
行为:
1. 校验 `productId`
2. 后端创建已支付订单
3. 积分套餐写入钱包余额与流水
4. 会员套餐写入会员状态
5. 返回最新账户中心快照与订单摘要
兼容路径:`POST /api/runtime/profile/recharge/orders`
## 4. 前端交互
1. “我的”页会员充值按钮打开独立弹窗,不在当前面板下方展开。
2. 弹窗顶部标题为 `账户充值`,右上角关闭。
3. 默认打开 `积分充值`,可切换到 `会员卡充值`
4. 点击套餐后调用下单接口,按钮进入处理中状态,成功后刷新 `profileDashboard`
5. 弹窗内不写大段说明文案,只保留必要金额、积分、会员权益和状态反馈。
## 5. 验收
1. 普通用户打开弹窗能看到积分与会员套餐。
2. 积分购买后余额增加,流水来源为 `points_recharge`
3. 首充赠送只在首次积分充值时生效。
4. 会员购买后会员状态与到期时间立即更新。
5. 移动端弹窗单列可滚动,桌面端接近参考图卡片网格。

View File

@@ -0,0 +1,73 @@
# 我的 Tab 邀请与玩家社区首期落地方案
更新时间:`2026-04-25`
## 目标
在现有“我的”Tab 常用功能区落地三个轻量入口:
1. `邀请好友`:弹出面板展示当前账号绑定的邀请码。
2. `填邀请码`:弹出面板填写邀请码,成功后邀请者与被邀请者各获得 `30` 积分。
3. `玩家社区`:弹出面板展示微信群与 QQ 群二维码占位图,后续替换为正式图片。
## 后端边界
- 邀请码、邀请关系与奖励发放全部存入 `server-rs/crates/spacetime-module`
- Axum 只做鉴权、参数转发与响应映射,不在 API 层自行计算奖励。
- 前端只读取后端状态与调用提交接口,不做本地加积分。
- 钱包余额继续复用 `profile_dashboard_state.wallet_balance`
- 奖励流水继续复用 `profile_wallet_ledger`,新增来源类型:
- `invite_inviter_reward`
- `invite_invitee_reward`
## SpacetimeDB 表设计
### `profile_invite_code`
- `user_id`:主键,账号 ID。
- `invite_code`:唯一邀请码。
- `created_at` / `updated_at`:创建与更新时间。
### `profile_referral_relation`
- `invitee_user_id`:主键,被邀请账号 ID。保证每个用户最多填写一次邀请码。
- `inviter_user_id`:邀请者账号 ID。
- `invite_code`:绑定时使用的邀请码快照。
- `inviter_reward_granted`:邀请者本次是否获得奖励。
- `invitee_reward_granted`:被邀请者是否获得奖励。
- `bound_at`:绑定时间。
## 业务规则
- 每个用户拥有一个稳定邀请码,首次进入邀请中心时自动生成。
- 用户不能填写自己的邀请码。
- 用户最多填写一个邀请码,成功后不可修改。
- 被邀请者绑定成功后获得 `30` 积分。
- 邀请者每天最多获得 `10` 次邀请奖励,超过后关系仍可绑定,被邀请者仍获得奖励,邀请者当次不再加分。
- 每次奖励都写入钱包流水,钱包余额以后端返回为准。
## API
### `GET /api/runtime/profile/referrals/invite-center`
返回当前用户的邀请码、邀请链接、今日奖励次数、剩余奖励次数、已绑定状态与奖励参数。
### `POST /api/runtime/profile/referrals/redeem-code`
请求体:
```json
{
"inviteCode": "ABCD1234"
}
```
返回绑定后的邀请中心状态与本次奖励发放结果。
## 前端交互
- 三个入口继续放在“我的”Tab 常用功能区,不新增页面。
- `邀请好友` 弹窗展示邀请码、复制按钮、邀请链接。
- `填邀请码` 弹窗在未绑定时展示输入框;已绑定时展示短状态。
- `玩家社区` 弹窗展示两个紧凑二维码占位区。
- 弹窗文案只保留必要标签和短提示,不放长规则说明。

View File

@@ -0,0 +1,25 @@
# 平台首页仅展示作品入口调整记录
更新时间:`2026-04-25`
## 目标
- 首页首屏与 banner 不再展示创作入口,也不再在无存档时点击进入创作工作台。
- 首页主体只承担公开作品浏览、作品详情打开和最近存档继续游玩的入口。
- 创作 Tab 作为独立主导航入口保留,不把入口放进首页 banner 或作品推荐区域。
- 创作工作台保留在既有创作流程内,避免误删已有草稿、发布和多玩法创作能力。
## 落地
- `src/components/rpg-entry/RpgEntryHomeView.tsx`
- 移动端首页 Hero 固定指向作品:有精选/最新作品时打开首个作品详情,无作品时跳转分类页。
- 桌面端首页 banner 固定指向作品,不再显示 `CREATE WORLD``创作入口``进入创作工作台` 等入口文案。
- 桌面端右侧快捷区域改为“最近作品 / 最近浏览 / 作品广场”结构,不再提供创作启动 CTA。
- 首页底部导航与桌面侧栏保留“创作”Tab创作入口只在独立导航层露出。
## 验收标准
- 首页作品区域与 banner 不出现创作入口按钮或创作入口文案。
- 底部导航与桌面侧栏正常显示“创作”Tab。
- 首页仍能打开精选、最新、趋势和最近浏览作品。
- 无公开作品时显示作品空状态,不引导创建草稿。

View File

@@ -4,13 +4,24 @@
## 文档列表
- [SPACETIMEDB_TABLE_CATALOG.md](./SPACETIMEDB_TABLE_CATALOG.md):持续维护当前 SpacetimeDB 表目录,按领域说明每张表的作用、字段结构、索引和常用 `spacetime sql` 查询模板。
- [RPG_WORK_DELETE_SPACETIMEDB_PROCEDURE_EXPORT_FIX_2026-04-25.md](./RPG_WORK_DELETE_SPACETIMEDB_PROCEDURE_EXPORT_FIX_2026-04-25.md):记录 RPG 作品删除时报 `No such procedure` 的根因,补齐 `delete_custom_world_agent_session` 在有效 SpacetimeDB 模块入口中的导出,并要求发布后核验 Maincloud schema。
- [CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.md](./CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.md):冻结当前后端唯一落地口径,明确新功能以 `server-rs + Axum + SpacetimeDB` 为准,旧 `server-node` / Express / PostgreSQL 与 Go 方向只允许作为迁移参考。
- [RPG_DRAFT_GENERATION_CONTINUE_AND_ETA_FIX_2026-04-25.md](./RPG_DRAFT_GENERATION_CONTINUE_AND_ETA_FIX_2026-04-25.md):记录世界草稿生成失败/中断后进度不再误到 `100%`、主按钮改为“继续生成草稿”并复用已保存底稿续跑,以及按阶段耗时模型估算预计等待时间的修复口径。
- [RUNTIME_NPC_CHAT_LLM_MIGRATION_2026-04-25.md](./RUNTIME_NPC_CHAT_LLM_MIGRATION_2026-04-25.md):冻结运行时 NPC 聊天从 Rust 确定性兜底迁到 `platform-llm` 的边界,要求旧 Node 聊天提示词原样迁移,覆盖回复、建议、好感变化与限轮收束。
- [API_SERVER_DEV_STACK_COLD_BUILD_TIMEOUT_FIX_2026-04-25.md](./API_SERVER_DEV_STACK_COLD_BUILD_TIMEOUT_FIX_2026-04-25.md):记录 `npm run dev:rust` 在 Windows 冷编译/链接阶段误把 `api-server` `/healthz` 等待判定为超时并杀掉 `cargo run` 的根因,以及将 SpacetimeDB 与 api-server 等待窗口拆分的脚本口径。
- [API_ERROR_DETAILS_MESSAGE_DISPLAY_FIX_2026-04-25.md](./API_ERROR_DETAILS_MESSAGE_DISPLAY_FIX_2026-04-25.md):记录统一 API envelope 错误展示优先读取 `error.details.message` 的修复口径,避免 Big Fish 发布校验等业务错误只显示通用“请求参数不合法”。
- [CHARACTER_VISUAL_IP_MODERATION_FALLBACK_FIX_2026-04-25.md](./CHARACTER_VISUAL_IP_MODERATION_FALLBACK_FIX_2026-04-25.md):记录角色主形象遇到 DashScope `IPInfringementSuspect` 时自动改用原创安全 prompt 兜底重试的修复口径,并保留供应商原始错误便于排查。
- [CREATION_AGENT_IMMEDIATE_WAITING_DOTS_FIX_2026-04-25.md](./CREATION_AGENT_IMMEDIATE_WAITING_DOTS_FIX_2026-04-25.md):记录创作 Agent 用户发送消息后立刻展示三点等待动画的前端展示条件,避免首个 SSE token 到达前聊天区无反馈。
- [CREATION_AGENT_DOCUMENT_INPUT_UPLOAD_2026-04-25.md](./CREATION_AGENT_DOCUMENT_INPUT_UPLOAD_2026-04-25.md):冻结 Agent 创作页上传文本类文档并解析为输入框内容的前后端边界、接口、支持范围和验收标准。
- [CREATION_AGENT_CLIENT_AND_FLOW_CONTROLLER_REUSE_2026-04-25.md](./CREATION_AGENT_CLIENT_AND_FLOW_CONTROLLER_REUSE_2026-04-25.md):冻结三类作品创作 Agent client 通用工厂与平台轻量流程 controller 的复用边界,明确本轮只收口 HTTP/SSE 骨架和大鱼/拼图会话流程,不合并 RPG 自动保存主链。
- [BACKEND_CREATION_AGENT_LLM_TURN_COMMONIZATION_2026-04-25.md](./BACKEND_CREATION_AGENT_LLM_TURN_COMMONIZATION_2026-04-25.md):冻结后端创作 Agent LLM turn 公共化边界,收口模型可用性检查、流式 JSON 回复抽取、最终 JSON 解析与中文错误映射,玩法 schema 和写回逻辑继续留在各自模块。
- [CREATION_WORK_SHELF_UNIFICATION_2026-04-25.md](./CREATION_WORK_SHELF_UNIFICATION_2026-04-25.md):冻结创作中心作品货架统一视图模型,先在前端归一 RPG、大鱼、拼图 works 的展示字段、筛选状态和卡片动作语义,不新增后端聚合接口。
- [BIG_FISH_DIRECTION_TOUCH_CONTROL_2026-04-24.md](./BIG_FISH_DIRECTION_TOUCH_CONTROL_2026-04-24.md):记录大鱼吃小鱼从固定摇杆改为屏幕首触点方向控制,并要求本地直达局在未操作时保持对象运动。
- [RUST_WORKSPACE_DEFAULT_BUILD_SCOPE_FIX_2026-04-25.md](./RUST_WORKSPACE_DEFAULT_BUILD_SCOPE_FIX_2026-04-25.md):记录 `server-rs` 无参数 `cargo build` 链接 `spacetime-module` 失败的根因,并冻结默认只构建原生 `api-server`、模块产物继续走 `spacetime build` 的命令边界。
- [BIG_FISH_DIRECT_ROUTE_PLAYGROUND_2026-04-24.md](./BIG_FISH_DIRECT_ROUTE_PLAYGROUND_2026-04-24.md):记录 `/big-fish` 大鱼吃小鱼玩法直达入口,明确复用现有 `BigFishRuntimeShell` 和本地占位运行态的调试边界。
- [PUZZLE_DIRECT_ROUTE_PLAYGROUND_2026-04-24.md](./PUZZLE_DIRECT_ROUTE_PLAYGROUND_2026-04-24.md):记录 `/puzzle` 拼图玩法直达入口,明确复用现有 `PuzzleRuntimeShell` 和本地占位图运行态的调试边界。
- [FRONTEND_INDEPENDENT_PAGE_ROUTES_2026-04-25.md](./FRONTEND_INDEPENDENT_PAGE_ROUTES_2026-04-25.md)记录平台入口、RPG 创作、拼图创作和大鱼吃小鱼创作各页面的独立前端路径,以及与 `/puzzle``/big-fish` 调试直达入口的边界。
- [CREATION_AGENT_PUBLISH_GATE_NORMALIZE_WRITEBACK_FIX_2026-04-24.md](./CREATION_AGENT_PUBLISH_GATE_NORMALIZE_WRITEBACK_FIX_2026-04-24.md):记录结果页 profile 归一化回写丢失顶层 `worldHook / playerPremise` 导致 publish gate 继续误报结构 blocker 的根因,并冻结前端归一化保留发布字段的修复口径。
- [CUSTOM_WORLD_RESULT_ENTITY_GENERATION_FIX_2026-04-24.md](./CUSTOM_WORLD_RESULT_ENTITY_GENERATION_FIX_2026-04-24.md):记录世界结果页在 Agent 草稿模式下新增场景、新增 NPC 生成成功但结果页字段不可用的根因,并冻结 `api-server` 生成归一化层补齐 profile 字段的修复口径。
@@ -148,11 +159,13 @@
- [CREATION_FLOW_CHAIN_REFACTOR_WORK_PACKAGE_G_PROGRESS_2026-04-21.md](./CREATION_FLOW_CHAIN_REFACTOR_WORK_PACKAGE_G_PROGRESS_2026-04-21.md):记录工作包 G 已完成的 runtime profile 目录化、服务端 preview compiler 收口,以及 foundation draft 主生成链与 preview 编译边界的直接拆开。
- [CREATION_FLOW_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md#93-工作包-c前端结果页与编辑器拆分](./CREATION_FLOW_CHAIN_REFACTOR_EXECUTION_PLAN_2026-04-21.md#93-%E5%B7%A5%E4%BD%9C%E5%8C%85-c%E5%89%8D%E7%AB%AF%E7%BB%93%E6%9E%9C%E9%A1%B5%E4%B8%8E%E7%BC%96%E8%BE%91%E5%99%A8%E6%8B%86%E5%88%86):记录工作包 C 已完成的结果页壳层拆分、编辑器目标分发与 mapper 收口、角色资产工坊 section/workflow 拆分,以及仍保留的阶段性 shared 实现边界。
- [CREATION_PAGE_MOBILE_UI_FIX_2026-04-21.md](./CREATION_PAGE_MOBILE_UI_FIX_2026-04-21.md):创作页移动端底部 Tab、亮色主题 token 与滚动权责修复记录。
- [RPG_FOUNDATION_DRAFT_EIGHT_ANCHOR_SEED_FIX_2026-04-25.md](./RPG_FOUNDATION_DRAFT_EIGHT_ANCHOR_SEED_FIX_2026-04-25.md):记录 RPG 创作 Agent session 八锚点进入 foundation draft seed 时被旧字段压缩的根因、修复和后续约束。
- [TXT_MODE_VISUAL_NOVEL_MIGRATION_EXECUTION_PLAN_2026-04-20.md](./TXT_MODE_VISUAL_NOVEL_MIGRATION_EXECUTION_PLAN_2026-04-20.md):把外部仓库 TXT 模式完整迁入当前项目的冻结边界、模块映射、分阶段计划与验收清单。
- [AI_CHARACTER_ANIMATION_TECHNICAL_SOLUTION_2026-04-04.md](./AI_CHARACTER_ANIMATION_TECHNICAL_SOLUTION_2026-04-04.md)AI 生成角色形象与角色动画的技术路线。
- [ALIYUN_NPC_IMAGE_ANIMATION_EXPERIMENT_2026-04-07.md](./ALIYUN_NPC_IMAGE_ANIMATION_EXPERIMENT_2026-04-07.md):面向编辑器的阿里云 NPC 形象与动作实验方案,按 4 条生成链路对比。
- [PIXELMOTION_TECHNICAL_BREAKDOWN_2026-04-04.md](./PIXELMOTION_TECHNICAL_BREAKDOWN_2026-04-04.md)PixelMotion 产品形态与能力拆解。
- [SERVER_DEPLOYMENT_AND_CORS_TECHNICAL_SOLUTION_2026-04-05.md](./SERVER_DEPLOYMENT_AND_CORS_TECHNICAL_SOLUTION_2026-04-05.md):服务端部署、代理层与 CORS 方案。
- [WORLD_DRAFT_FOUNDATION_EDITOR_TARGET_FIX_2026-04-25.md](./WORLD_DRAFT_FOUNDATION_EDITOR_TARGET_FIX_2026-04-25.md):记录世界草稿“基本设定”独立编辑目标、分号标签化展示与编辑回写边界。
## 使用建议

View File

@@ -0,0 +1,29 @@
# RPG 草稿生成失败续跑与预计等待修正 2026-04-25
## 背景
世界草稿生成过程页在失败或中断后存在两个体验问题:
1. 后端部分失败路径把 `draft_foundation``operation_progress` 写成 `100`,前端总进度会误显示完成。
2. 生成页按钮文案是“重新生成草稿”,实际用户需要从当前草稿继续补齐,而不是理解成完全重开。
3. 预计等待时间按 `已耗时 / 当前进度` 线性估算,遇到角色主形象、幕背景图等长阶段时偏差明显。
## 落地规则
1. 失败态进度不能直接显示 `100%`
- 服务端失败路径按失败阶段写入最后可信进度。
- 前端兼容旧记录:若 `failed + progress >= 100`,按 `phaseLabel` 所在阶段回落到 `< 100` 的阶段起点。
2. 生成过程页非运行态主按钮统一显示“继续生成草稿”。
3. “继续生成草稿”继续调用 `draft_foundation`
- 若 session 已经保存 `draftProfile`,后端跳过世界底稿 LLM 分批生成,继续执行素材补齐、草稿卡编译和结果页写回。
- 若没有可复用 `draftProfile`,仍从世界底稿阶段重新生成。
4. 预计等待时间不再用线性百分比估算:
- 前端按阶段定义维护预期耗时,结合 operation 的 `updatedAt` 估算当前阶段剩余时间。
- 服务端每次写入 `draft_foundation` 阶段进度时输出结构化日志,后续可从日志统计真实阶段耗时并调整阶段预期。
## 验收点
- 失败或中断停在生成过程页时,总进度不会显示为 `100%`
- 失败后的主按钮显示“继续生成草稿”,点击后继续执行 `draft_foundation`
- 已保存部分底稿的失败会话再次执行时,不重复生成世界底稿,直接继续素材与结果页阶段。
- `customWorldAgentGenerationProgress` 针对失败进度纠偏和阶段 ETA 的单元测试通过。

View File

@@ -0,0 +1,31 @@
# RPG 草稿角色形象描述前置校验修正 2026-04-25
## 背景
RPG 作品生成过程中出现“角色缺少 visualDescription不能在角色形象设定文本生成前直接生图”。用户观察到前面步骤加起来不到 1 秒就跳到“生成角色主形象”,说明并没有完整执行角色形象设定文本生成链路。
## 根因
1. “继续生成草稿”会复用 session 中已经保存的 `draftProfile`,并直接进入素材补齐阶段。
2. 旧失败记录里可能已经保存了不完整角色对象,例如只有 `name/title/role/description`,缺少 `visualDescription/actionDescription/sceneVisualDescription`
3. 新底稿生成链路只保证 LLM 返回 JSON 可解析,没有在角色名单阶段强校验每个角色资产文本字段是否齐全。
4. 后续叙事档案与养成档案阶段只补 `backstory/personality/skills/items`,不会再补角色形象文本,因此缺失会一直传递到生图前才暴露。
## 落地规则
1. 生图前仍然必须强依赖 `visualDescription`,不能回退到 `description` 或通用兜底词。
2. 复用已保存 `draftProfile` 前必须检查所有 `playableNpcs/storyNpcs`
- `name`
- `visualDescription`
- `actionDescription`
- `sceneVisualDescription`
3. 已保存底稿缺少上述字段时,不再跳过世界底稿 LLM 生成,而是重新执行完整底稿生成链路。
4. 新底稿角色框架名单批次返回后必须先校验资产文本字段;如果缺失,发起一次 LLM JSON 修复请求补齐字段,再进入后续档案补全。
5. 修复请求只允许补齐同名角色的资产文本与框架字段,不允许新增角色、改名或删除角色。
## 验收点
- 旧失败会话继续生成时,不会在 1 秒内直接进入角色主形象生图。
- 角色名单阶段输出缺少 `visualDescription` 时,会先请求 LLM 修复,而不是进入素材阶段失败。
- 修复后 `playableNpcs/storyNpcs` 的每个角色都带有 `visualDescription/actionDescription/sceneVisualDescription`
- 角色主形象生图仍只读取 `visualDescription`,缺字段继续视为底稿质量问题。

View File

@@ -0,0 +1,30 @@
# RPG 草稿恢复与结果页同步修正 2026-04-25
## 背景
用户进入一份还没有生成草稿的 RPG Agent 作品时,前端错误进入了结果页,并触发后端错误:
`sync_result_profile is only available during object_refining or visual_refining`
问题根因有两处:
1. 创作页打开草稿时,旧逻辑会用作品摘要里的 `playableNpcCount / landmarkCount` 推断是否已有可编辑结果页。摘要字段可能来自旧快照或残留投影,不能代表当前会话已经生成 `draftProfile`
2. Agent 结果页自动保存和返回创作时仍会调用 `sync_result_profile`,但这个 action 只允许在对象精修或视觉精修阶段使用;还在锚点采集、澄清或底稿生成前后时调用会被后端拒绝。
## 本次约束
- 进入 RPG Agent 草稿时,以最新 `session.draftProfile``session.stage` 作为唯一结果页门槛。
- `collecting_intent / clarifying / foundation_review / error` 或缺少 `draftProfile` 时,必须恢复 Agent 工作区。
- 只有 `object_refining / visual_refining / long_tail_review / ready_to_publish / published` 且存在 `draftProfile` 时,才能打开结果页。
- Agent 结果页不再自动把前端 profile 回写到 session`session.draftProfile` 是草稿真相源。
- `sync_result_profile` 只保留给显式对象精修链路,不允许作为打开作品、返回创作或自动保存的隐式动作。
## 落地
- `useRpgEntryLibraryDetail` 打开草稿后先拉取最新 session再按 `draftProfile + stage` 决定进入 Agent 工作区或结果页。
- `useRpgCreationResultAutosave``syncAgentDraftResultProfile` 改为刷新 session 快照并保存最新 `draftProfile`,不再调用 `sync_result_profile`
- Agent 结果页返回创作时直接回创作中心,不再发起同步 reducer。
- 回归测试覆盖:
-`draftProfile` 但摘要对象数量非 0 时仍回 Agent 工作区。
- 结果页返回创作不触发 `sync_result_profile`
- 结果页自动保存不触发 `sync_result_profile`,仍携带 `sourceAgentSessionId` 保存作品库草稿。

View File

@@ -0,0 +1,44 @@
# RPG foundation draft 八锚点 seed 修复 2026-04-25
## 背景
RPG 创作 Agent session 当前以 `anchorContent` 保存八锚点:
1. `worldPromise`
2. `playerFantasy`
3. `themeBoundary`
4. `playerEntryPoint`
5. `coreConflict`
6. `keyRelationships`
7. `hiddenLines`
8. `iconicElements`
底稿生成入口 `build_foundation_generation_seed_text()` 应直接消费这份八锚点状态,保证 Agent 多轮共创沉淀下来的内容完整进入 foundation draft 生成。
## 问题
`build_eight_anchor_foundation_text()` 仍读取旧字段:
- `coreLoop`
- `mainConflict`
- `keyCharacters`
- `keyPlaces`
- `toneAndStyle`
- `firstScene`
因此当前 session 里只有 `worldPromise``playerEntryPoint` 两项能命中。若 `anchorContent` 更残缺,还会回退到 `creatorIntent` 的五段摘要:世界核心、玩家身份、开局处境、核心冲突、标志元素。
这会造成链路语义错位Agent 前段持续迭代八锚点,底稿生成 seed 却只吃到残缺锚点或五段摘要,后续再通过 draft profile 与前端展示恢复成八锚点相关结构时,内容已经发生压缩和漂移。
## 修复
`server-rs/crates/api-server/src/custom_world_foundation_draft.rs` 已调整:
- `build_eight_anchor_foundation_text()` 改为读取当前八锚点字段。
- 输出使用中文锚点标签,便于 foundation prompt 直接理解结构语义。
- 新增 `has_meaningful_anchor_value()`,避免空对象、空数组、空字符串被误当作有效锚点。
- 新增回归测试,确保完整八锚点 seed 不会回退到 `anchorPack.creatorIntentSummary` 或旧字段名。
## 后续约束
后续任何 foundation draft 生成、预览编译、发布门禁都应以 Agent session 的当前八锚点字段为准。旧字段名只能作为历史文档或迁移参考,不再进入 `server-rs` 主生成链。

View File

@@ -0,0 +1,37 @@
# RPG 作品删除 SpacetimeDB procedure 导出修复 2026-04-25
## 问题
创作页或作品详情删除 RPG 作品时报 `No such procedure`
本次核对 Maincloud `xushi-p4wfr` schema 后确认:
1. 已发布 / 作品库 profile 删除依赖 `delete_custom_world_profile_and_return`
2. 草稿作品删除依赖 `delete_custom_world_agent_session`
3. 本地 Rust client 绑定里存在 `delete_custom_world_agent_session`,但 Maincloud schema 中没有该 procedure。
## 根因
`server-rs/crates/spacetime-module/src/custom_world/mod.rs` 中有一份草稿删除实现,但当前有效发布入口仍是 `server-rs/crates/spacetime-module/src/lib.rs` 中的 custom world 实现。`lib.rs` 未导出 `delete_custom_world_agent_session`,导致发布到 Maincloud 的模块 schema 缺少该 procedure。
## 落地口径
本次只补最小闭环:
1. 在有效入口 `server-rs/crates/spacetime-module/src/lib.rs` 增加 `delete_custom_world_agent_session` procedure。
2. 删除纯 Agent 草稿时同步清理:
- `custom_world_agent_session`
- `custom_world_agent_message`
- `custom_world_agent_operation`
- `custom_world_draft_card`
3. 若前端误把已发布作品按 `sessionId` 走入草稿删除接口,则回落到关联 `custom_world_profile` 软删除,并返回最新 works 列表。
4. 不新增前端逻辑,不回退到 server-node。
## 验证
1. `cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml`
2. `npm run check:encoding`
3. `npm run spacetime:publish:maincloud`
4. 发布后用 `spacetime describe xushi-p4wfr --server maincloud --json` 确认 schema 包含:
- `delete_custom_world_profile_and_return`
- `delete_custom_world_agent_session`

View File

@@ -30,3 +30,16 @@
2. `cargo check -p api-server` 通过。
3. 四条链路仍能从原调用点拿到相同语义的提示词。
4. 文档明确后续 prompt 修改主源在 `src/prompt/`
## 5. 聊天类 Prompt 追加迁移
2026-04-25 追加迁移两类聊天提示词:
1. Agent 聊天创作:`server-rs/crates/api-server/src/prompt/agent_chat.rs`,承接原 `custom_world_rpg_draft_prompts.rs` 中的共创主系统提示词、状态识别提示词、输出契约、动态状态上下文、聊天历史上下文等脚本。
2. 游戏运行时与对方角色聊天:`server-rs/crates/api-server/src/prompt/runtime_chat.rs`承接运行时剧情导演、NPC 对话导演、战斗结算叙事的 system prompt 与 user prompt 组装。
迁移后约束:
1. `custom_world_rpg_draft_prompts.rs` 只作为兼容 re-export后续不要在该文件新增提示词正文。
2. `runtime_story/compat/ai.rs` 只负责读取状态、调用 LLM 和组装返回,不再内联 NPC 对话或剧情导演提示词。
3. 后续所有 Agent 共创聊天、运行时角色聊天的提示词调整统一进入 `src/prompt/`

View File

@@ -0,0 +1,535 @@
# SpacetimeDB 表说明与查询目录
> 维护状态:持续维护。凡是新增、删除或修改 `server-rs/crates/spacetime-module` 中的 `#[spacetimedb::table]`,必须同步更新本文。
## 维护规则
- 表结构以 `server-rs/crates/spacetime-module/src/lib.rs` 及其已挂载子模块为主;`server-rs/crates/spacetime-client/src/module_bindings/*_table.rs``*_type.rs` 用于校对当前生成绑定。
- `public` 表可以被客户端订阅;未标 `public` 的表是服务端真相表,通常通过 reducer / procedure / Axum facade 间接访问。
- SQL 示例使用 `<db>``<user_id>``<session_id>` 等占位符,实际执行时替换为真实值:
```powershell
spacetime sql <db> "SELECT * FROM custom_world_gallery_entry"
```
- 修改表后维护顺序:
1. 更新 Rust 表定义和对应领域注释。
2. 重新发布 / 生成绑定,确认 `module_bindings` 与源码一致。
3. 更新本文的作用、结构、索引和查询 SQL。
4. 运行 `npm run check:encoding`,避免中文文档或源码被写坏。
## 总览
| 领域 | 表 |
| --- | --- |
| 认证 | `auth_store_snapshot`, `user_account`, `auth_identity`, `refresh_session` |
| 运行时档案 | `runtime_setting`, `runtime_snapshot`, `user_browse_history`, `profile_dashboard_state`, `profile_wallet_ledger`, `profile_played_world`, `profile_save_archive` |
| 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` |
| 大鱼吃小鱼 | `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` |
## 认证表
### `auth_store_snapshot`
- 作用:保存旧内存认证仓储的整份 JSON 快照,用于迁移和恢复;后续正式表拆分后仍可作为导入/导出桥。
- 结构:`snapshot_id PK: String`, `snapshot_json: String`, `updated_at: Timestamp`
- 索引:主键 `snapshot_id`
```sql
SELECT * FROM auth_store_snapshot;
SELECT * FROM auth_store_snapshot WHERE snapshot_id = 'default';
```
### `user_account`
- 作用:用户账号主表,保存用户名、公开叙世号、手机号掩码、登录方式、密码登录开关和 token 版本。
- 结构:`user_id PK: String`, `public_user_code: String`, `username: String`, `display_name: String`, `phone_number_masked: Option<String>`, `phone_number_e164: Option<String>`, `login_method: String`, `binding_status: String`, `wechat_bound: bool`, `password_hash: String`, `password_login_enabled: bool`, `token_version: u64`
- 索引:`username`, `public_user_code`
```sql
SELECT * FROM user_account WHERE user_id = '<user_id>';
SELECT * FROM user_account WHERE username = '<username>';
SELECT * FROM user_account WHERE public_user_code = '<public_user_code>';
```
### `auth_identity`
- 作用:第三方/手机号身份绑定表,把 provider 身份映射到内部 `user_id`
- 结构:`identity_id PK: String`, `user_id: String`, `provider: String`, `provider_uid: String`, `provider_union_id: Option<String>`, `phone_e164: Option<String>`, `display_name: Option<String>`, `avatar_url: Option<String>`
- 索引:`user_id`, `(provider, provider_uid)`
```sql
SELECT * FROM auth_identity WHERE user_id = '<user_id>';
SELECT * FROM auth_identity WHERE provider = 'wechat' AND provider_uid = '<provider_uid>';
```
### `refresh_session`
- 作用:刷新令牌会话表,支持多端登录、吊销、过期和最近活跃时间查询。
- 结构:`session_id PK: String`, `user_id: String`, `refresh_token_hash: String`, `issued_by_provider: String`, `client_info_json: String`, `expires_at: String`, `revoked_at: Option<String>`, `created_at: String`, `updated_at: String`, `last_seen_at: String`
- 索引:`user_id`, `refresh_token_hash`
```sql
SELECT * FROM refresh_session WHERE session_id = '<session_id>';
SELECT * FROM refresh_session WHERE user_id = '<user_id>';
SELECT * FROM refresh_session WHERE refresh_token_hash = '<hash>';
```
## 运行时档案表
### `runtime_setting`
- 作用:保存用户运行时设置,目前承接音乐音量和平台主题。
- 结构:`user_id PK: String`, `music_volume: f32`, `platform_theme: RuntimePlatformTheme`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:主键 `user_id`
```sql
SELECT * FROM runtime_setting WHERE user_id = '<user_id>';
```
### `runtime_snapshot`
- 作用:用户当前运行时快照,保存底部 Tab、游戏状态 JSON 和当前剧情 JSON。
- 结构:`user_id PK: String`, `version: u32`, `saved_at: Timestamp`, `bottom_tab: String`, `game_state_json: String`, `current_story_json: Option<String>`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:主键 `user_id`
```sql
SELECT * FROM runtime_snapshot WHERE user_id = '<user_id>';
```
### `user_browse_history`
- 作用:用户浏览/进入世界历史,用于最近访问、继续浏览和去重展示。
- 结构:`browse_history_id PK: String`, `user_id: String`, `owner_user_id: String`, `profile_id: String`, `world_name: String`, `subtitle: String`, `summary_text: String`, `cover_image_src: Option<String>`, `theme_mode: RuntimeBrowseHistoryThemeMode`, `author_display_name: String`, `visited_at: Timestamp`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`user_id`, `(user_id, owner_user_id, profile_id)`
```sql
SELECT * FROM user_browse_history WHERE user_id = '<user_id>';
SELECT * FROM user_browse_history WHERE user_id = '<user_id>' AND owner_user_id = '<owner_user_id>' AND profile_id = '<profile_id>';
```
### `profile_dashboard_state`
- 作用:个人主页聚合状态,保存钱包余额和总游玩时长。
- 结构:`user_id PK: String`, `wallet_balance: u64`, `total_play_time_ms: u64`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:主键 `user_id`
```sql
SELECT * FROM profile_dashboard_state WHERE user_id = '<user_id>';
```
### `profile_wallet_ledger`
- 作用:钱包流水账,记录金币/货币变更来源与变更后的余额。
- 结构:`wallet_ledger_id PK: String`, `user_id: String`, `amount_delta: i64`, `balance_after: u64`, `source_type: RuntimeProfileWalletLedgerSourceType`, `created_at: Timestamp`
- 索引:`user_id`, `(user_id, created_at)`
```sql
SELECT * FROM profile_wallet_ledger WHERE user_id = '<user_id>';
SELECT * FROM profile_wallet_ledger WHERE user_id = '<user_id>' ORDER BY created_at DESC;
```
### `profile_played_world`
- 作用:记录用户玩过的世界及最后游玩时间,用于个人页历史和继续游戏入口。
- 结构:`played_world_id PK: String`, `user_id: String`, `world_key: String`, `owner_user_id: Option<String>`, `profile_id: Option<String>`, `world_type: Option<String>`, `world_title: String`, `world_subtitle: String`, `first_played_at: Timestamp`, `last_played_at: Timestamp`, `last_observed_play_time_ms: u64`
- 索引:`user_id`, `(user_id, world_key)`, `(user_id, last_played_at)`
```sql
SELECT * FROM profile_played_world WHERE user_id = '<user_id>';
SELECT * FROM profile_played_world WHERE user_id = '<user_id>' AND world_key = '<world_key>';
SELECT * FROM profile_played_world WHERE user_id = '<user_id>' ORDER BY last_played_at DESC;
```
### `profile_save_archive`
- 作用:用户存档列表,保存世界信息、封面、当前状态 JSON 和剧情 JSON。
- 结构:`archive_id PK: String`, `user_id: String`, `world_key: String`, `owner_user_id: Option<String>`, `profile_id: Option<String>`, `world_type: Option<String>`, `world_name: String`, `subtitle: String`, `summary_text: String`, `cover_image_src: Option<String>`, `saved_at: Timestamp`, `bottom_tab: String`, `game_state_json: String`, `current_story_json: Option<String>`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`user_id`, `(user_id, world_key)`, `(user_id, saved_at)`
```sql
SELECT * FROM profile_save_archive WHERE archive_id = '<archive_id>';
SELECT * FROM profile_save_archive WHERE user_id = '<user_id>' ORDER BY saved_at DESC;
SELECT * FROM profile_save_archive WHERE user_id = '<user_id>' AND world_key = '<world_key>';
```
## RPG 运行时表
### `story_session`
- 作用RPG 剧情会话主表,保存运行时会话、玩家、世界、最近叙事文本和可选选择函数。
- 结构:`story_session_id PK: String`, `runtime_session_id: String`, `actor_user_id: String`, `world_profile_id: String`, `initial_prompt: String`, `opening_summary: Option<String>`, `latest_narrative_text: String`, `latest_choice_function_id: Option<String>`, `status: StorySessionStatus`, `version: u32`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`runtime_session_id`, `actor_user_id`
```sql
SELECT * FROM story_session WHERE story_session_id = '<story_session_id>';
SELECT * FROM story_session WHERE runtime_session_id = '<runtime_session_id>';
SELECT * FROM story_session WHERE actor_user_id = '<user_id>' ORDER BY updated_at DESC;
```
### `story_event`
- 作用:剧情事件流水,记录每次开场/推进生成的叙事文本和选择函数。
- 结构:`event_id PK: String`, `story_session_id: String`, `event_kind: StoryEventKind`, `narrative_text: String`, `choice_function_id: Option<String>`, `created_at: Timestamp`
- 索引:`story_session_id`
```sql
SELECT * FROM story_event WHERE story_session_id = '<story_session_id>' ORDER BY created_at ASC;
SELECT * FROM story_event WHERE event_id = '<event_id>';
```
### `npc_state`
- 作用NPC 在某个运行时会话中的关系、好感、招募、传闻和已见剧情状态。
- 结构:`npc_state_id PK: String`, `runtime_session_id: String`, `npc_id: String`, `npc_name: String`, `affinity: i32`, `relation_state: NpcRelationState`, `help_used: bool`, `chatted_count: u32`, `gifts_given: u32`, `recruited: bool`, `trade_stock_signature: Option<String>`, `revealed_facts: Vec<String>`, `known_attribute_rumors: Vec<String>`, `first_meaningful_contact_resolved: bool`, `seen_backstory_chapter_ids: Vec<String>`, `stance_profile: NpcStanceProfile`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`runtime_session_id`, `npc_id`, `(runtime_session_id, npc_id)`
```sql
SELECT * FROM npc_state WHERE npc_state_id = '<npc_state_id>';
SELECT * FROM npc_state WHERE runtime_session_id = '<runtime_session_id>';
SELECT * FROM npc_state WHERE runtime_session_id = '<runtime_session_id>' AND npc_id = '<npc_id>';
```
### `inventory_slot`
- 作用:背包/装备槽真相表,避免继续把物品状态塞在运行时 JSON 中。
- 结构:`slot_id PK: String`, `runtime_session_id: String`, `story_session_id: Option<String>`, `actor_user_id: String`, `container_kind: InventoryContainerKind`, `slot_key: String`, `item_id: String`, `category: String`, `name: String`, `description: Option<String>`, `quantity: u32`, `rarity: InventoryItemRarity`, `tags: Vec<String>`, `stackable: bool`, `stack_key: String`, `equipment_slot_id: Option<InventoryEquipmentSlot>`, `source_kind: InventoryItemSourceKind`, `source_reference_id: Option<String>`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`runtime_session_id`, `actor_user_id`, `(container_kind, slot_key)`, `item_id`
```sql
SELECT * FROM inventory_slot WHERE runtime_session_id = '<runtime_session_id>';
SELECT * FROM inventory_slot WHERE actor_user_id = '<user_id>';
SELECT * FROM inventory_slot WHERE container_kind = 'Backpack' AND slot_key = '<slot_key>';
SELECT * FROM inventory_slot WHERE item_id = '<item_id>';
```
### `battle_state`
- 作用:战斗状态真相表,保存玩家/目标血蓝、回合、奖励、上次动作和结算结果。
- 结构:`battle_state_id PK: String`, `story_session_id: String`, `runtime_session_id: String`, `actor_user_id: String`, `chapter_id: Option<String>`, `target_npc_id: String`, `target_name: String`, `battle_mode: BattleMode`, `status: BattleStatus`, `player_hp: i32`, `player_max_hp: i32`, `player_mana: i32`, `player_max_mana: i32`, `target_hp: i32`, `target_max_hp: i32`, `experience_reward: u32`, `reward_items: Vec<RuntimeItemRewardItemSnapshot>`, `turn_index: u32`, `last_action_function_id: Option<String>`, `last_action_text: Option<String>`, `last_result_text: Option<String>`, `last_damage_dealt: i32`, `last_damage_taken: i32`, `last_outcome: CombatOutcome`, `version: u32`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`story_session_id`, `runtime_session_id`, `actor_user_id`
```sql
SELECT * FROM battle_state WHERE battle_state_id = '<battle_state_id>';
SELECT * FROM battle_state WHERE story_session_id = '<story_session_id>';
SELECT * FROM battle_state WHERE runtime_session_id = '<runtime_session_id>';
SELECT * FROM battle_state WHERE actor_user_id = '<user_id>' ORDER BY updated_at DESC;
```
### `treasure_record`
- 作用:宝箱/奇遇结算记录,保存奖励物品、生命/法力/货币奖励和剧情提示。
- 结构:`treasure_record_id PK: String`, `runtime_session_id: String`, `story_session_id: String`, `actor_user_id: String`, `encounter_id: String`, `encounter_name: String`, `scene_id: Option<String>`, `scene_name: Option<String>`, `action: TreasureInteractionAction`, `reward_items: Vec<RuntimeItemRewardItemSnapshot>`, `reward_hp: u32`, `reward_mana: u32`, `reward_currency: u32`, `story_hint: Option<String>`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`story_session_id`, `runtime_session_id`, `actor_user_id`, `encounter_id`
```sql
SELECT * FROM treasure_record WHERE treasure_record_id = '<treasure_record_id>';
SELECT * FROM treasure_record WHERE runtime_session_id = '<runtime_session_id>';
SELECT * FROM treasure_record WHERE encounter_id = '<encounter_id>';
```
### `quest_record`
- 作用:任务主表,保存任务来源、目标、进度、奖励、叙事绑定、步骤和完成/交付时间。
- 结构:`quest_id PK: String`, `runtime_session_id: String`, `story_session_id: Option<String>`, `actor_user_id: String`, `issuer_npc_id: String`, `issuer_npc_name: String`, `scene_id: Option<String>`, `chapter_id: Option<String>`, `act_id: Option<String>`, `thread_id: Option<String>`, `contract_id: Option<String>`, `title: String`, `description: String`, `summary: String`, `objective: QuestObjectiveSnapshot`, `progress: u32`, `status: QuestStatus`, `completion_notified: bool`, `reward: QuestRewardSnapshot`, `reward_text: String`, `narrative_binding: QuestNarrativeBindingSnapshot`, `steps: Vec<QuestStepSnapshot>`, `active_step_id: Option<String>`, `visible_stage: u32`, `hidden_flags: Vec<String>`, `discovered_fact_ids: Vec<String>`, `related_carrier_ids: Vec<String>`, `consequence_ids: Vec<String>`, `created_at: Timestamp`, `updated_at: Timestamp`, `completed_at: Option<Timestamp>`, `turned_in_at: Option<Timestamp>`
- 索引:`runtime_session_id`, `actor_user_id`, `issuer_npc_id`
```sql
SELECT * FROM quest_record WHERE quest_id = '<quest_id>';
SELECT * FROM quest_record WHERE runtime_session_id = '<runtime_session_id>';
SELECT * FROM quest_record WHERE actor_user_id = '<user_id>' ORDER BY updated_at DESC;
SELECT * FROM quest_record WHERE issuer_npc_id = '<npc_id>';
```
### `quest_log`
- 作用:任务事件流水,记录领取、信号推进、完成通知、交付等状态变化。
- 结构:`log_id PK: String`, `quest_id: String`, `runtime_session_id: String`, `actor_user_id: String`, `event_kind: QuestLogEventKind`, `status_after: QuestStatus`, `signal_kind: Option<QuestSignalKind>`, `signal: Option<QuestProgressSignal>`, `step_id: Option<String>`, `step_progress: Option<u32>`, `created_at: Timestamp`
- 索引:`quest_id`, `runtime_session_id`, `actor_user_id`
```sql
SELECT * FROM quest_log WHERE quest_id = '<quest_id>' ORDER BY created_at ASC;
SELECT * FROM quest_log WHERE runtime_session_id = '<runtime_session_id>';
SELECT * FROM quest_log WHERE actor_user_id = '<user_id>' ORDER BY created_at DESC;
```
### `player_progression`
- 作用:玩家成长主表,按用户保存等级、当前等级经验、总经验和待处理升级数。
- 结构:`user_id PK: String`, `level: u32`, `current_level_xp: u32`, `total_xp: u32`, `xp_to_next_level: u32`, `pending_level_ups: u32`, `last_granted_source: Option<PlayerProgressionGrantSource>`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:主键 `user_id`
```sql
SELECT * FROM player_progression WHERE user_id = '<user_id>';
```
### `chapter_progression`
- 作用:章节成长预算和实际记账表,承接计划经验、实际任务/敌对经验、击败数和节奏档位。
- 结构:`chapter_progression_id PK: String`, `user_id: String`, `chapter_id: String`, `chapter_index: u32`, `total_chapters: u32`, `entry_pseudo_level_millis: u32`, `exit_pseudo_level_millis: u32`, `entry_level: u32`, `exit_level: u32`, `planned_total_xp: u32`, `planned_quest_xp: u32`, `planned_hostile_xp: u32`, `actual_quest_xp: u32`, `actual_hostile_xp: u32`, `expected_hostile_defeat_count: u32`, `actual_hostile_defeat_count: u32`, `level_at_entry: u32`, `level_at_exit: Option<u32>`, `pace_band: ChapterPaceBand`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`user_id`, `chapter_id`, `(user_id, chapter_id)`
```sql
SELECT * FROM chapter_progression WHERE chapter_progression_id = '<chapter_progression_id>';
SELECT * FROM chapter_progression WHERE user_id = '<user_id>';
SELECT * FROM chapter_progression WHERE user_id = '<user_id>' AND chapter_id = '<chapter_id>';
```
## 世界创作表
### `custom_world_profile`
- 作用:自定义世界正式工件真相表,承接作品库、发布、进入世界和软删除审计。
- 结构:`profile_id PK: String`, `owner_user_id: String`, `public_work_code: Option<String>`, `author_public_user_code: Option<String>`, `source_agent_session_id: Option<String>`, `publication_status: CustomWorldPublicationStatus`, `world_name: String`, `subtitle: String`, `summary_text: String`, `theme_mode: CustomWorldThemeMode`, `cover_image_src: Option<String>`, `profile_payload_json: String`, `playable_npc_count: u32`, `landmark_count: u32`, `author_display_name: String`, `published_at: Option<Timestamp>`, `deleted_at: Option<Timestamp>`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`owner_user_id`, `publication_status`
```sql
SELECT * FROM custom_world_profile WHERE profile_id = '<profile_id>';
SELECT * FROM custom_world_profile WHERE owner_user_id = '<user_id>' ORDER BY updated_at DESC;
SELECT * FROM custom_world_profile WHERE publication_status = 'Published';
```
### `custom_world_session`
- 作用:旧 custom-world/sessions 传统问答流会话表,不与 Agent 会话混存。
- 结构:`session_id PK: String`, `owner_user_id: String`, `generation_mode: CustomWorldGenerationMode`, `status: CustomWorldSessionStatus`, `setting_text: String`, `creator_intent_json: Option<String>`, `question_snapshot_json: String`, `result_payload_json: Option<String>`, `last_error_message: Option<String>`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`owner_user_id`
```sql
SELECT * FROM custom_world_session WHERE session_id = '<session_id>';
SELECT * FROM custom_world_session WHERE owner_user_id = '<user_id>' ORDER BY updated_at DESC;
```
### `custom_world_agent_session`
- 作用RPG 创作 Agent 会话聚合表,保存八锚点、草稿、发布门禁、进度、建议和 checkpoint。
- 结构:`session_id PK: String`, `owner_user_id: String`, `seed_text: String`, `current_turn: u32`, `progress_percent: u32`, `stage: RpgAgentStage`, `focus_card_id: Option<String>`, `anchor_content_json: String`, `creator_intent_json: Option<String>`, `creator_intent_readiness_json: String`, `anchor_pack_json: Option<String>`, `lock_state_json: Option<String>`, `draft_profile_json: Option<String>`, `last_assistant_reply: Option<String>`, `publish_gate_json: Option<String>`, `result_preview_json: Option<String>`, `pending_clarifications_json: String`, `quality_findings_json: String`, `suggested_actions_json: String`, `recommended_replies_json: String`, `asset_coverage_json: String`, `checkpoints_json: String`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`owner_user_id`, `stage`
```sql
SELECT * FROM custom_world_agent_session WHERE session_id = '<session_id>';
SELECT * FROM custom_world_agent_session WHERE owner_user_id = '<user_id>' ORDER BY updated_at DESC;
SELECT * FROM custom_world_agent_session WHERE stage = '<stage>';
```
### `custom_world_agent_message`
- 作用RPG 创作 Agent 消息流水表,避免把聊天记录继续塞回 session 大 JSON。
- 结构:`message_id PK: String`, `session_id: String`, `role: RpgAgentMessageRole`, `kind: RpgAgentMessageKind`, `text: String`, `related_operation_id: Option<String>`, `created_at: Timestamp`
- 索引:`session_id`
```sql
SELECT * FROM custom_world_agent_message WHERE session_id = '<session_id>' ORDER BY created_at ASC;
```
### `custom_world_agent_operation`
- 作用RPG 创作 Agent 异步操作真相表,承接操作阶段、进度和错误。
- 结构:`operation_id PK: String`, `session_id: String`, `operation_type: RpgAgentOperationType`, `status: RpgAgentOperationStatus`, `phase_label: String`, `phase_detail: String`, `progress: u32`, `error_message: Option<String>`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`session_id`
```sql
SELECT * FROM custom_world_agent_operation WHERE operation_id = '<operation_id>';
SELECT * FROM custom_world_agent_operation WHERE session_id = '<session_id>' ORDER BY updated_at DESC;
```
### `custom_world_draft_card`
- 作用RPG 创作草稿卡片表,拆出角色/地点/章节等卡片实体,支持详情、更新和资产状态查询。
- 结构:`card_id PK: String`, `session_id: String`, `kind: RpgAgentDraftCardKind`, `status: RpgAgentDraftCardStatus`, `title: String`, `subtitle: String`, `summary: String`, `linked_ids_json: String`, `warning_count: u32`, `asset_status: Option<CustomWorldRoleAssetStatus>`, `asset_status_label: Option<String>`, `detail_payload_json: Option<String>`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`session_id`, `kind`
```sql
SELECT * FROM custom_world_draft_card WHERE card_id = '<card_id>';
SELECT * FROM custom_world_draft_card WHERE session_id = '<session_id>';
SELECT * FROM custom_world_draft_card WHERE kind = '<kind>';
```
### `custom_world_gallery_entry`
- 作用:公开画廊读模型,专供客户端订阅和广场展示,避免运行时从 profile 即席拼装。
- 可见性:`public`
- 结构:`profile_id PK: String`, `owner_user_id: String`, `public_work_code: String`, `author_public_user_code: String`, `author_display_name: String`, `world_name: String`, `subtitle: String`, `summary_text: String`, `cover_image_src: Option<String>`, `theme_mode: CustomWorldThemeMode`, `playable_npc_count: u32`, `landmark_count: u32`, `published_at: Timestamp`, `updated_at: Timestamp`
- 索引:`owner_user_id`, `theme_mode`, `public_work_code`
```sql
SELECT * FROM custom_world_gallery_entry;
SELECT * FROM custom_world_gallery_entry WHERE owner_user_id = '<user_id>';
SELECT * FROM custom_world_gallery_entry WHERE theme_mode = '<theme_mode>';
SELECT * FROM custom_world_gallery_entry WHERE public_work_code = '<public_work_code>';
```
## 拼图表
### `puzzle_agent_session`
- 作用:拼图创作 Agent 会话表,保存种子、阶段、锚点包、草稿和已发布 profile。
- 结构:`session_id PK: String`, `owner_user_id: String`, `seed_text: String`, `current_turn: u32`, `progress_percent: u32`, `stage: PuzzleAgentStage`, `anchor_pack_json: String`, `draft_json: Option<String>`, `last_assistant_reply: Option<String>`, `published_profile_id: Option<String>`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`owner_user_id`
```sql
SELECT * FROM puzzle_agent_session WHERE session_id = '<session_id>';
SELECT * FROM puzzle_agent_session WHERE owner_user_id = '<user_id>' ORDER BY updated_at DESC;
```
### `puzzle_agent_message`
- 作用:拼图创作 Agent 聊天消息流水。
- 结构:`message_id PK: String`, `session_id: String`, `role: PuzzleAgentMessageRole`, `kind: PuzzleAgentMessageKind`, `text: String`, `created_at: Timestamp`
- 索引:`session_id`
```sql
SELECT * FROM puzzle_agent_message WHERE session_id = '<session_id>' ORDER BY created_at ASC;
```
### `puzzle_work_profile`
- 作用:拼图作品主表,保存作品信息、封面、发布状态、游玩次数和锚点包。
- 结构:`profile_id PK: String`, `work_id: String`, `owner_user_id: String`, `source_session_id: Option<String>`, `author_display_name: String`, `level_name: String`, `summary: String`, `theme_tags_json: String`, `cover_image_src: Option<String>`, `cover_asset_id: Option<String>`, `publication_status: PuzzlePublicationStatus`, `play_count: u32`, `anchor_pack_json: String`, `publish_ready: bool`, `created_at: Timestamp`, `updated_at: Timestamp`, `published_at: Option<Timestamp>`
- 索引:`owner_user_id`, `publication_status`
```sql
SELECT * FROM puzzle_work_profile WHERE profile_id = '<profile_id>';
SELECT * FROM puzzle_work_profile WHERE owner_user_id = '<user_id>' ORDER BY updated_at DESC;
SELECT * FROM puzzle_work_profile WHERE publication_status = 'Published';
```
### `puzzle_runtime_run`
- 作用:拼图游玩运行态,保存当前关卡、网格、已玩 profile 列表、标签和运行快照。
- 结构:`run_id PK: String`, `owner_user_id: String`, `entry_profile_id: String`, `current_profile_id: String`, `cleared_level_count: u32`, `current_level_index: u32`, `current_grid_size: u32`, `played_profile_ids_json: String`, `previous_level_tags_json: String`, `snapshot_json: String`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`owner_user_id`
```sql
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;
```
## 大鱼吃小鱼表
### `big_fish_creation_session`
- 作用:大鱼吃小鱼创作会话表,保存种子、阶段、锚点包、草稿、资产覆盖和发布就绪状态。
- 结构:`session_id PK: String`, `owner_user_id: String`, `seed_text: String`, `current_turn: u32`, `progress_percent: u32`, `stage: BigFishCreationStage`, `anchor_pack_json: String`, `draft_json: Option<String>`, `asset_coverage_json: String`, `last_assistant_reply: Option<String>`, `publish_ready: bool`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`owner_user_id`
```sql
SELECT * FROM big_fish_creation_session WHERE session_id = '<session_id>';
SELECT * FROM big_fish_creation_session WHERE owner_user_id = '<user_id>' ORDER BY updated_at DESC;
```
### `big_fish_agent_message`
- 作用:大鱼吃小鱼创作 Agent 消息流水。
- 结构:`message_id PK: String`, `session_id: String`, `role: BigFishAgentMessageRole`, `kind: BigFishAgentMessageKind`, `text: String`, `created_at: Timestamp`
- 索引:`session_id`
```sql
SELECT * FROM big_fish_agent_message WHERE session_id = '<session_id>' ORDER BY created_at ASC;
```
### `big_fish_asset_slot`
- 作用大鱼吃小鱼资产槽位表记录背景、鱼、动作等资产生成状态、URL 与 prompt 快照。
- 结构:`slot_id PK: String`, `session_id: String`, `asset_kind: BigFishAssetKind`, `level: Option<u32>`, `motion_key: Option<String>`, `status: BigFishAssetStatus`, `asset_url: Option<String>`, `prompt_snapshot: String`, `updated_at: Timestamp`
- 索引:`session_id`
```sql
SELECT * FROM big_fish_asset_slot WHERE slot_id = '<slot_id>';
SELECT * FROM big_fish_asset_slot WHERE session_id = '<session_id>';
```
### `big_fish_runtime_run`
- 作用:大鱼吃小鱼运行态表,保存当前 run 的快照、最后输入方向和 tick。
- 结构:`run_id PK: String`, `session_id: String`, `owner_user_id: String`, `status: BigFishRunStatus`, `snapshot_json: String`, `last_input_x: f32`, `last_input_y: f32`, `tick: u64`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`owner_user_id`, `session_id`
```sql
SELECT * FROM big_fish_runtime_run WHERE run_id = '<run_id>';
SELECT * FROM big_fish_runtime_run WHERE owner_user_id = '<user_id>' ORDER BY updated_at DESC;
SELECT * FROM big_fish_runtime_run WHERE session_id = '<session_id>';
```
## 资产表
### `asset_object`
- 作用:正式资产对象元数据表,保存 OSS bucket/key、访问策略、大小、hash、版本和业务归属。
- 结构:`asset_object_id PK: String`, `bucket: String`, `object_key: String`, `access_policy: AssetObjectAccessPolicy`, `content_type: Option<String>`, `content_length: u64`, `content_hash: Option<String>`, `version: u32`, `source_job_id: Option<String>`, `owner_user_id: Option<String>`, `profile_id: Option<String>`, `entity_id: Option<String>`, `asset_kind: String`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`asset_kind`, `(bucket, object_key)`
```sql
SELECT * FROM asset_object WHERE asset_object_id = '<asset_object_id>';
SELECT * FROM asset_object WHERE bucket = '<bucket>' AND object_key = '<object_key>';
SELECT * FROM asset_object WHERE asset_kind = '<asset_kind>';
```
### `asset_entity_binding`
- 作用:资产到业务实体槽位的绑定表,例如把角色主图、场景背景、动作视频绑定到 profile/entity/slot。
- 结构:`binding_id PK: String`, `asset_object_id: String`, `entity_kind: String`, `entity_id: String`, `slot: String`, `asset_kind: String`, `owner_user_id: Option<String>`, `profile_id: Option<String>`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:`(entity_kind, entity_id, slot)`, `asset_object_id`
```sql
SELECT * FROM asset_entity_binding WHERE binding_id = '<binding_id>';
SELECT * FROM asset_entity_binding WHERE entity_kind = '<entity_kind>' AND entity_id = '<entity_id>' AND slot = '<slot>';
SELECT * FROM asset_entity_binding WHERE asset_object_id = '<asset_object_id>';
```
## AI 任务表
### `ai_task`
- 作用AI 任务主表,保存任务类型、所属用户、来源模块、请求载荷、状态、最新输出和生命周期时间。
- 结构:`task_id PK: String`, `task_kind: AiTaskKind`, `owner_user_id: String`, `request_label: String`, `source_module: String`, `source_entity_id: Option<String>`, `request_payload_json: Option<String>`, `status: AiTaskStatus`, `failure_message: Option<String>`, `latest_text_output: Option<String>`, `latest_structured_payload_json: Option<String>`, `version: u32`, `created_at: Timestamp`, `started_at: Option<Timestamp>`, `completed_at: Option<Timestamp>`, `updated_at: Timestamp`
- 索引:`owner_user_id`, `status`, `task_kind`
```sql
SELECT * FROM ai_task WHERE task_id = '<task_id>';
SELECT * FROM ai_task WHERE owner_user_id = '<user_id>' ORDER BY updated_at DESC;
SELECT * FROM ai_task WHERE status = '<status>';
SELECT * FROM ai_task WHERE task_kind = '<task_kind>';
```
### `ai_task_stage`
- 作用AI 任务阶段表,保存每个阶段的标签、详情、顺序、状态、输出和警告。
- 结构:`task_stage_id PK: String`, `task_id: String`, `stage_kind: AiTaskStageKind`, `label: String`, `detail: String`, `stage_order: u32`, `status: AiTaskStageStatus`, `text_output: Option<String>`, `structured_payload_json: Option<String>`, `warning_messages: Vec<String>`, `started_at: Option<Timestamp>`, `completed_at: Option<Timestamp>`
- 索引:`task_id`, `(task_id, stage_order)`
```sql
SELECT * FROM ai_task_stage WHERE task_id = '<task_id>' ORDER BY stage_order ASC;
SELECT * FROM ai_task_stage WHERE task_stage_id = '<task_stage_id>';
```
### `ai_text_chunk`
- 作用AI 流式文本增量表,按任务、阶段和 sequence 保存文本 delta。
- 结构:`text_chunk_row_id PK: String`, `chunk_id: String`, `task_id: String`, `stage_kind: AiTaskStageKind`, `sequence: u32`, `delta_text: String`, `created_at: Timestamp`
- 索引:`task_id`, `(task_id, stage_kind, sequence)`
```sql
SELECT * FROM ai_text_chunk WHERE task_id = '<task_id>' ORDER BY sequence ASC;
SELECT * FROM ai_text_chunk WHERE task_id = '<task_id>' AND stage_kind = '<stage_kind>' ORDER BY sequence ASC;
```
### `ai_result_reference`
- 作用AI 任务产物引用表把任务结果关联到资产、profile、业务实体等外部结果。
- 结构:`result_reference_row_id PK: String`, `result_ref_id: String`, `task_id: String`, `reference_kind: AiResultReferenceKind`, `reference_id: String`, `label: Option<String>`, `created_at: Timestamp`
- 索引:`task_id`
```sql
SELECT * FROM ai_result_reference WHERE result_reference_row_id = '<row_id>';
SELECT * FROM ai_result_reference WHERE task_id = '<task_id>' ORDER BY created_at ASC;
```
## 当前维护风险
- `story_session``story_event``npc_state``inventory_slot``battle_state``treasure_record``quest_record``quest_log``player_progression``chapter_progression``src/lib.rs``src/gameplay/mod.rs` 都能看到表定义。当前编译入口以 `src/lib.rs` 为准;后续完成拆分时,需要删除重复定义或正式挂载子模块,并同步更新本文。
- `custom_world/*` 子模块中也保留了一份表骨架;当前生成绑定与 `src/lib.rs` 对齐,包含 `public_work_code``author_public_user_code``custom_world_gallery_entry.public_work_code` 索引。维护时不要只看子模块文件。

View File

@@ -0,0 +1,20 @@
# 世界草稿基本设定编辑入口修复 2026-04-25
## 问题
世界草稿页中“世界概述”和“基本设定”两个区块原先都使用 `{ kind: 'world' }` 打开编辑器,导致点击“基本设定”仍进入世界概述编辑页面。
RPG 草稿的基本设定字段由八锚点内容拼接而来,很多内容天然是以分号分隔的碎片化标签,不适合在目录页继续按长段落展示。
## 落地
1. 新增编辑目标 `{ kind: 'foundation' }`,专门打开“编辑基本设定”面板。
2. “世界概述”继续使用 `{ kind: 'world' }`,只编辑世界名称、副标题、概述、基调、目标等概述字段。
3. “基本设定”使用 `{ kind: 'foundation' }`,编辑八锚点结构化字段,并保存回 `anchorContent`、关键顶层字段与 `creatorIntent`
4. 基本设定目录页与编辑页都通过分号解析标签,支持中文分号与英文分号。
## 约束
- 前端只做字段展示、拆分和编辑回写,不改变后端草稿生成语义。
- 分号解析只影响 UI 展示与编辑草稿,不在读取时改写原始中文内容。
- 后续新增基本设定字段时,应优先扩展 `customWorldFoundationEntries`,避免在目录页和编辑页各自拼接字段。