Files
Genarrative/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md
T
kdletters 36a8719d0f 补齐微信虚拟支付发货确认
在虚拟支付商品入账后调用微信发货确认接口
允许已入账商品订单只重试发货而不重复发放权益
遇到失效 access token 时强制刷新并最多重放一次
要求使用微信 paid_time 并完善历史补单文件清理和回归测试
2026-07-13 13:39:59 +08:00

116 KiB
Raw Blame History

server-rs 与 SpacetimeDB 数据契约

更新时间:2026-07-03

后端主线

当前后端固定为:

server-rs + Axum + SpacetimeDB

server-node、Express、PostgreSQL、Go 服务端和 maincloud 口径全部为历史残留。新功能不得回到旧接口、旧兼容层或前端临时业务真相。

Rust workspace

server-rs/Cargo.toml 是 workspace 事实源。默认构建成员为 crates/api-server;第三方依赖版本和 workspace 内 crate path 统一放在 [workspace.dependencies]

SpacetimeDB 版本口径:当前 Rust crate spacetimedbspacetimedb-sdkspacetimedb-lib 统一锁定 2.6.0;本地 spacetime CLI / standalone、生成的 spacetime-client bindings 和容器压测镜像也必须与 server-rs/Cargo.toml 锁定版本对齐,避免 BSATN / procedure result 反序列化错配。遇到版本不匹配时,不继续沿着业务超时排查,先把 CLI / standalone 直接升级到锁定版本并重启后再重试。

当前主要 crate

  • HTTP 服务:api-server
  • 领域模块:module-aimodule-assetsmodule-authmodule-bark-battlemodule-big-fishmodule-combatmodule-creative-agentmodule-editor-agentmodule-custom-worldmodule-inventorymodule-match3dmodule-npcmodule-progressionmodule-puzzlemodule-questmodule-runtimemodule-runtime-itemmodule-runtime-storymodule-square-holemodule-storymodule-visual-novel
  • 平台副作用:platform-agentplatform-authplatform-imageplatform-llmplatform-mattingplatform-ossplatform-wechatplatform-speech
  • 共享层:shared-contractsshared-kernelshared-logging
  • SpacetimeDBspacetime-clientspacetime-module
  • 测试支撑:tests-support

DDD 边界

  1. module-* 不直接依赖 Axum、SpacetimeDB table/reducer/procedure、reqwest、OSS、LLM、spacetime-clienttokio 或文件系统。
  2. 业务规则、命令校验、领域错误和可纯测逻辑优先沉到 module-*
  3. SpacetimeDB 表结构、reducer、procedure 和持久化 row shape 留在 spacetime-module
  4. 后端访问 SpacetimeDB 必须经 spacetime-client facade。
  5. HTTP 鉴权、BFF 聚合、SSE、外部模型编排、OSS 上传和第三方回调在 api-server
  6. 前端共享 DTO 通过 shared-contractspackages/shared 对齐,不在页面内重新发明旧接口。
  7. 微信能力按两层收口:server-rs/crates/platform-wechat 承载微信协议 client、订阅消息 stable_token / subscribeMessage.send、微信支付 V3 / 虚拟支付消息推送的 HTTP header、签名、验签、解密、mock 响应和协议 payload 解析;server-rs/crates/api-server/src/wechat.rswechat/* 承载 Axum handler、AppConfig 到平台配置的映射、Genarrative 用户 / 订单 / 钱包 / SSE / 错误 envelope 编排。platform-auth 当前仍承载微信 OAuth / 小程序登录 provider 协议,api-server::wechat::provider 只作为组合根 adapter,不在业务 handler 内散落 provider 构造。

验证:

npm run check:server-rs-ddd

spacetime-client mapper 组织

server-rs/crates/spacetime-client/src/mapper.rs 只作为聚合入口,负责声明 src/mapper/ 下的领域子模块并 re-export 原有 record / mapper 能力;不要在该文件继续堆叠大段映射实现。

当前子模块按调用领域拆分:assets.rsauth.rsruntime.rsruntime_profile.rscustom_world.rspuzzle.rsmatch3d.rsjump_hop.rswooden_fish.rssquare_hole.rsvisual_novel.rsbig_fish.rsstory.rsai.rsbark_battle.rscombat.rsinventory.rsnpc.rs,跨领域轻量 helper 和共享 record 统一放在 common.rs。该拆分只改变 spacetime-client 文件组织,不改变 SpacetimeDB schema、生成绑定、procedure result 契约或外部 DTO;后续新增 mapper 时优先落到对应领域子模块,不得重新引入跨层 JSON 字符串兼容结构。

API 路由分组

路由树由 server-rs/crates/api-server/src/app.rs 统一构造。当前主要分组:

  • 健康检查:GET /healthz
  • 后台管理:/admin/api/*,包括登录、Dashboard 运营看板、概览、HTTP debug、埋点、表查询、精选审核、素材查询、创作入口开关、作品互动配置、作品可见性、兑换码、邀请码、任务配置和充值商品配置。Dashboard 指标口径见 docs/technical/【后台管理】Dashboard运营看板方案-2026-06-23.md
  • 认证与账号:/api/auth/*/api/profile/me,包括短信、密码、微信、refresh session、多端会话和登出。
  • 个人中心:/api/profile/*,包括钱包流水、任务、领奖、充值、反馈、邀请和兑换等账号侧能力。
  • 平台基础能力:/api/llm/*/api/speech/volcengine/*,只保留通用 LLM 和语音代理。
  • 资产基础能力:/api/assets/direct-upload-tickets/api/assets/sts-upload-credentials/api/assets/objects/*/api/assets/read-url/api/assets/read-bytes,负责直传、确认、绑定和读取。两个读取入口共用同一授权函数,并先按配置 bucket 与精确 key 查询 asset_object:一旦存在 metadata,即使 key 命中 legacy 前缀,也必须按 PublicRead 或当前登录 owner 授权;只有同 bucket / key 未登记 metadata 的历史对象,才允许显式 legacyPublicPath 命中 platform_oss::LEGACY_PUBLIC_PREFIXES curated 白名单后匿名兼容。任意未登记 objectKey、跨 owner 和匿名私有读取统一返回不存在,read-bytes 不得成为绕过 read-url 授权的同源代理。
  • 外部 OpenAPI/api/external/v1/openapi.json/api/external/v1/assets/direct-upload-tickets/api/external/v1/assets/objects/confirm/api/external/v1/assets/read-url/api/external/v1/editor/*,使用 Bearer API Key 鉴权;API Key 管理仍在登录态 /api/profile/api-keys,不进入外部 OpenAPI JSON。主站和 External 的 asset object confirm 都必须从已认证主体派生 owner,不能信任请求体 owner;同 bucket / key 已登记后不得改变 owner。
  • 创作 / 游玩支撑能力:/api/creation-entry/config/api/ai/tasks*/api/runtime/frontend-config/api/runtime/chat/*/api/runtime/settings/api/runtime/save/snapshot/api/profile/browse-history/api/profile/save-archives*/api/profile/play-stats/api/assets/history/api/assets/character-visual/*/api/assets/character-animation/*/api/assets/character-workflow-cache*/api/assets/hyper3d/*/api/runtime/custom-world/asset-studio/*/api/editor/projects*/api/editor/projects/{projectId}/agent-conversations/api/editor/agent-conversations/{conversationId}*/api/runtime/frontend-configapi-server 从运行时环境变量下发非敏感 UI 开关;画板右侧 Agent 入口由 GENARRATIVE_ENABLE_IMAGE_EDITOR_AGENT_SIDEBAR 控制,默认关闭,前端不再读取 VITE_* 构建期变量决定生产显示。/api/runtime/custom-world/asset-studio/* 解析默认角色形象 / 动作提示词时可以在 OSS 缓存不可用或未配置时按无缓存返回默认提示;保存 workflow 缓存和真实素材读写仍必须要求 OSS 正常可用。
  • 后台入口配置:/admin/api/creation-entry/config/admin/api/creation-entry/config/banners/admin/api/creation-entry/config/interactions
  • 后台素材查询:GET /admin/api/editor-assets 通过 admin_list_editor_assets_and_return 后台只读 procedure 读取私有账号级 editor_assetsource_type = 'generated' 的素材,支持 ownerUserIdkeywordcreatedAftercreatedBeforecursorlimit;返回缩略图 / Object Key、作者展示名、陶泥号、提示词、生成输入和生成成本,只用于查询,不提供分类筛选,也不执行精选审核、返还或展示状态修改,不通过后台 SQL 直查私有表。图片放大、视频和音频预览统一调用仅后台管理员会话可访问的 GET /admin/api/assets/read-url;该入口仅为预览允许后台跨 owner 签名,不改变主站 /api/assets/read-* 或 External API 的 owner 边界。成功换签后必须写入 event_key = admin_asset_read_urltracking_event,记录管理员 subject、Object Key / legacy path 和有效期,不记录 signed URL。
  • 自定义世界 / RPG/api/runtime/custom-world*/api/story/*/api/runtime/chat/*
  • 拼图:/api/runtime/puzzle/*
  • 抓大鹅 Match3D/api/creation/match3d/*/api/runtime/match3d/*
  • 敲木鱼:/api/creation/wooden-fish/*/api/runtime/wooden-fish/*
  • 方洞挑战:/api/creation/square-hole/*/api/runtime/square-hole/*
  • 视觉小说:/api/creation/visual-novel/*/api/runtime/visual-novel/*
  • 大鱼吃小鱼:/api/runtime/big-fish/*
  • 跳一跳:/api/creation/jump-hop/*/api/runtime/jump-hop/*
  • 汪汪声浪:/api/runtime/bark-battle/*
  • 儿童向创作:/api/creation/edutainment/*

需要新增路由时,先确认玩法入口配置和 tracking 分类,不要绕过 app.rs 的统一中间件、鉴权和入口开关。涉及创作、生成、作品、公开详情、试玩、正式运行态、运行态库存、运行态设置 / 存档、游玩历史、存档归档、游玩统计、AI task、角色资产工坊或玩法生成支撑资产的路由,不再直接在 app.rs 逐玩法 .merge(...),也不挂到 modules/platform.rs;必须先进入 server-rs/crates/api-server/src/modules/play_flow.rs 的统一玩法流程主干,再由主干注册表分发到各领域 HTTP Adapter 或支撑能力 handler。

图片画布 Agent 对话

  • /api/editor/projects/{projectId}/agent-conversations 负责当前工程会话列表和新建;/api/editor/agent-conversations/{conversationId} 负责详情读取和软删;/api/editor/agent-conversations/{conversationId}/messages/stream 负责发送消息并返回 SSE。
  • module-editor-agent 只承载纯领域校验:标题派生、附件上限、消息输入规则和会话软删访问规则;不直接依赖 Axum、SpacetimeDB、OSS、LLM 或 Tokio。
  • spacetime-moduleeditor_agent_conversation 只保存元数据;创建、列表、读取、更新时间和软删通过 create_editor_agent_conversation_and_returnlist_editor_agent_conversations_and_returnget_editor_agent_conversation_and_returntouch_editor_agent_conversation_and_returndelete_editor_agent_conversation_and_return procedure 完成,api-server 只能经 spacetime-client facade 访问。
  • 完整消息文档存 OSS editor-agent/{conversationId}.json,由 api-server 负责 2 MiB 上限、会话内串行锁、读改写、SSE 事件持久化和 touch 元数据更新时间;该 JSON 不进入 editor_canvas.layers_json,也不作为画布布局真相。
  • 对话附件只允许引用当前工程 editor_project_resource 或当前账号 editor_asset 的图片;前端可提交展示用 imageSrc / thumbnailSrc,后端必须按 resourceId / assetId 重新归一、校验 owner / project 和 objectKey,再给 LLM 或生成工具使用。
  • 画布 Agent 工具复用既有编辑器图片生成 / 修改 / 图标 spritesheet BFF,并继续使用后端模型定价和 execute_billable_asset_operation_with_cost;前端不提交 priceMudPoints

创作 / 游玩统一流程主干

modules/play_flow.rs 是后端创作与游玩流程的统一入口。现有外部 URL、DTO、错误 envelope、鉴权方式、入口开关语义和 SpacetimeDB schema 默认不变,但路由组织必须遵循:

  1. app.rs 只合并 modules::play_flow::router(state),不直接合并 RPG、拼图、抓大鹅、跳一跳、敲木鱼、拼消消、汪汪声浪、视觉小说或儿童向创作等逐玩法模块。
  2. play_flow 统一注册每个玩法的 playId、领域模块 key、创作路由前缀和运行态路由前缀;后续新增玩法或迁移旧玩法时,先补这个注册表,再挂具体领域模块路由。
  3. 新建创作、首次生成和 Remix 成草稿等会产生新创作的入口开关匹配规则同样归 play_flow 管理;creation_entry_config.rs 只复用该规则执行 open=false 熔断,不再维护第二份路径判断。
  4. play_flow 在进入领域 handler 前先解析并挂载 PlayFlowRequestContext,统一标记请求处于 CreationRuntimeCreationEntryConfigCreationSupportRuntimeSupportAiTaskPublicReadModelRuntimeInventory 阶段,并记录目标 playId / 领域模块 key;领域 handler 可以读取该上下文做后续收口,但不能绕过主干自建平行流程。
  5. play_flow 只做平台共性编排和领域 Adapter 组合,不下沉玩法规则;最后一步的草稿编译、资产生成、发布、运行态 start/action/finish、计分和排行榜仍交给对应 module-*spacetime-module procedure 和玩法 HTTP handler 处理。
  6. 公开作品聚合、作品详情、运行态库存、运行态设置 / 存档、游玩历史、存档归档、游玩统计、历史素材、AI task、runtime chat、文档解析、角色资产工坊、角色图像 / 动画生成和 Hyper3D 代理属于跨玩法或玩法支撑流程,也从 play_flow 主干挂入;modules/platform.rs 只保留通用 LLM / 语音代理,不再承接创作 / 游玩支撑路由。
  7. 如果某个旧玩法仍使用历史 /api/runtime/<play>/agent/* 作为创作命名空间,只保留外部兼容路径;新增实现和文档仍按“统一主干 -> 领域 Adapter”的语义描述,不把历史路径当新架构模板。

认证态用户与会话摘要下发口径

  • AuthUserPayload / AuthUser 只保留前端当前会用到的身份与绑定展示字段:idpublicUserCodedisplayNameavatarUrlphoneNumberphoneNumberMaskedloginMethodbindingStatuswechatBoundwechatDisplayNamewechatAccount。账号信息面板展示微信绑定时优先使用 wechatDisplayName;该字段只能来自微信平台 profile、历史已保存的微信身份资料,或小程序原生 input type="nickname" 提交的 displayName,不得用系统账号显示名或“微信旅人”这类假昵称兜底。小程序 /api/auth/wechat/miniprogram-login/api/auth/wechat/bind-phone 可接收 displayName/api/auth/wechat/miniprogram-login 额外返回 created,供小程序壳在快捷登录后判断是否需要补采集微信昵称。jscode2session 无法直接返回微信昵称或个人微信号,只能稳定拿到小程序维度 openid,后端以 wechatAccount 下发可区分的绑定账号标识,前端在缺少真实昵称时展示账号尾号。
  • AuthSessionSummaryPayload / AuthSessionSummary 只保留设备卡片与撤销需要的摘要字段:sessionIdsessionIdssessionCountclientLabelipMaskedisCurrentcreatedAtlastSeenAtexpiresAt
  • 设备诊断信息(例如原始 clientType / clientRuntime / clientPlatform / userAgent / miniProgramAppId / miniProgramEnv / deviceDisplayName)不再默认下发到前端;若未来确需展示,优先单独加窄 DTO,而不是把账号 / 会话快照恢复为全量对象。
  • 多端登录语义以 refresh_session 为粒度:同一账号可保留多个 active session,普通登录不会撤销旧设备;POST /api/auth/logout 只撤销当前 refresh session,不提升 token_versionPOST /api/auth/logout-all、改密、重置密码才吊销全端 session 并提升 token_version。鉴权中间件仍校验 Bearer sid 对应的 refresh session 是否 active,单独踢下线或当前设备退出可以让目标设备立即失效而不误伤其它设备。

api-server 模块化演进规则

api-server 的长期方向是从超大 app.rs 和超大 handler 文件收敛为按能力组织的 HTTP/BFF Module。后续改造必须保持 HTTP route、DTO、error envelope、SpacetimeDB schema、前端行为和计费语义默认不变;任何 contract 或 schema 变化都要先在当前文档中写清影响范围和迁移计划。

路由模块化规则:

  1. 每个能力 Module 只暴露 router(state) -> Router<AppState>;平台创作 / 游玩相关 Module 和支撑能力由 modules/play_flow.rs 统一 .merge(...) 或在支撑 router 内挂载,其它账号、资产基础、后台和平台基础能力再由 app.rs 直接合并。
  2. app.rs 只保留全局 middleware、TraceLayer、request context、tracking middleware、入口开关和少量顶层 glue;不得重新恢复逐玩法 creation/runtime merge 列表。
  3. 能力 Module 可在路由内部用 FromRef<AppState> 派生自己的 Feature State,例如 PuzzleApiState。全局 AppState 仍作为进程组合根、鉴权层和全局中间件状态,但业务 handler 优先只提取对应 Feature State,不直接暴露完整 AppState
  4. Feature State 只暴露该能力实际需要的 facade / adapter / 配置快照;若必须复用仍要求 AppState 的横切 helper(例如计费、外部失败审计或通用 tracking),应通过 Feature State 的窄方法或显式 root_state() 过渡,并在后续继续收窄。
  5. 路由迁移和业务重构分阶段处理;先移动路由装配,再拆 handler 内部实现,再收窄 handler 可见状态。
  6. 大 handler 拆分时优先按 router.rshandlers.rsapplication.rsassets.rsmapper.rserrors.rs 分层。handlers.rs 只做 Axum extract、鉴权和 request/response,业务规则继续下沉到 module-*
  7. 手写 Rust 模块入口统一使用同名 .rs 文件,例如 puzzle.rs + puzzle/*.rsmatch3d.rs + match3d/*.rs;不要再新增 mod.rs 入口。生成的 SpacetimeDB Rust bindings 也由生成脚本同步为 module_bindings.rs + module_bindings/*.rs 布局。

拼图 api-server 内部拆分:

  • server-rs/crates/api-server/src/modules/puzzle.rs 只负责路由装配、鉴权层和参考图 body limit;对外继续引用同一批 handler 名称。
  • server-rs/crates/api-server/src/state.rs 中的 PuzzleApiState 是拼图 HTTP/BFF 的 Feature State,集中暴露 SpacetimeClientPuzzleGalleryCache、OSS client、作者查询所需认证服务、拼图 LLM client 和少量 VectorEngine / Agent 配置快照。拼图 handler 只提取 State<PuzzleApiState>,不得重新改回 State<AppState>
  • server-rs/crates/api-server/src/puzzle.rs 只作为聚合入口,保留共享 import / 常量、内部模块声明和 handler re-export,不继续承载大段实现。
  • server-rs/crates/api-server/src/puzzle/handlers.rs 承接 Axum handler,负责 extract、鉴权上下文、调用 SpacetimeDB facade / 编排 helper,并返回 HTTP/SSE 响应。
  • server-rs/crates/api-server/src/puzzle/draft.rs 承接表单草稿保存、草稿编译、首关命名、UI 背景 prompt 和初始资产就绪校验。
  • server-rs/crates/api-server/src/puzzle/generation.rs 承接拼图图片与 UI 背景的生成编排、计费包裹和 reference image 路径选择。
  • server-rs/crates/api-server/src/puzzle/vector_engine.rs 承接 VectorEngine 请求体、HTTP 调用、下载 / base64 解码、OSS 写入、asset object / binding 持久化和上游错误归一。
  • server-rs/crates/api-server/src/puzzle/mappers.rs 承接 SpacetimeDB record 到 shared-contracts DTO 的映射。
  • server-rs/crates/api-server/src/puzzle/tags.rs 保留拼图标签生成、拼图通用错误映射和 SSE helper。

拼图发布 / 待发布门槛必须同时要求首图、关卡画面、UI spritesheet 与关卡背景资产包完整;module-puzzle::validate_publish_requirementsapi-server::puzzle::tags::is_puzzle_session_snapshot_publish_ready 使用同一资产语言,不得只凭 cover、标题、描述和标签把半成品标为 publishReadyready_to_publish

该拆分只改变 api-server 文件组织,不改变 /api/runtime/puzzle/* route、DTO、error envelope、SpacetimeDB schema、公开 gallery cache 语义或计费语义;后续继续细分时也必须先保持行为不变,再单独讨论领域规则下沉。

/api/runtime/puzzle/runs* 当前接受 RuntimePrincipal,可同时识别登录用户 Bearer 和 runtime guest token。推荐页嵌入运行态的正式开局、交换、拖拽、下一关、暂停、道具与排行榜请求,应由前端在登录态下继续携带账号 access token;匿名游客仅在确认为未登录时走 runtime guest token。不要再把拼图 runtime 当成只认普通 Bearer 的纯账号接口。

公开正式 runtime 的启动与局内同步动作统一接受 RuntimePrincipal,包括拼图、拼消消、跳一跳、敲木鱼、抓大鹅 Match3D、方洞挑战、视觉小说、大鱼吃小鱼和汪汪声浪。登录用户仍使用账号 Bearer;未登录推荐页或公开运行态使用 Runtime Guest Token,后端以 principal.subject() 作为本局 owner / player subject,并用 WorkPlayTrackingDraft::runtime_principal(...) 记录游玩。创作、个人作品、删除、发布、Remix、点赞等账号或所有权动作不得改成 runtime guest 鉴权。

抓大鹅 Match3D api-server 内部拆分:

  • server-rs/crates/api-server/src/modules/match3d.rs 继续负责路由装配和 body limit;对外 handler 名称保持不变。
  • server-rs/crates/api-server/src/match3d.rs 只作为聚合入口,保留共享 import / 常量 / 内部类型、模块声明和 handler re-export。
  • server-rs/crates/api-server/src/match3d/handlers.rs 承接 Axum handler,负责 extract、鉴权上下文、调用 SpacetimeDB facade / 编排 helper,并返回 HTTP 响应。
  • /api/runtime/match3d/works/{profile_id}/runs/api/runtime/match3d/runs/{run_id}/click/stop/restart/time-up 属于正式运行态局部请求,必须接受 RuntimePrincipal;登录用户使用账号 Bearer,推荐页匿名游客使用 runtime guest token,后端以 principal subject 作为本局 owner,不得退回只认普通 Bearer 的路由。
  • server-rs/crates/api-server/src/match3d/draft.rs 承接 Agent session、草稿编译、题材 / 难度 / 物品计划和草稿持久化编排。
  • server-rs/crates/api-server/src/match3d/works.rs 承接作品 CRUD、封面 / 背景 / 容器资产生成入口、发布 / Remix / 点赞 / 游玩记录和作品级 helper。
  • server-rs/crates/api-server/src/match3d/item_assets.rs 承接物品生成批次编排、append / replace / delete / sort / merge、计费外层和草稿素材映射;sheet prompt、绿幕 / 近白底透明化、切图和切片持久化复用 generated_asset_sheets 通用模块。
  • server-rs/crates/api-server/src/match3d/vector_engine_gemini.rs 仅保留历史 VectorEngine Gemini 物品 sheet helper;当前草稿物品 spritesheet 以关卡整图为参考走 gpt-image-2 编辑链路,提示词、绿幕透明化和 OSS 持久化由 item_assets.rs / works.rs 约束。
  • server-rs/crates/api-server/src/match3d/runtime.rs 保留运行态轻量归一 helpermappers.rs / tags.rs / tests.rs 分别承接 DTO 映射、标签 / 通用错误 helper 和原有单测。

该拆分只改变 api-server 文件组织,不改变 /api/creation/match3d/*/api/runtime/match3d/* route、DTO、error envelope、SpacetimeDB schema、公开 gallery cache 语义、VectorEngine / OSS 副作用边界或计费语义;后续继续细分时也必须先保持行为不变,再单独讨论领域规则下沉到 module-match3d

生成资产 Adapter 规则:

  1. 稳定单图链路可收敛到 api-server 内部生成资产 Adapterprovider 生成、下载/base64 解码、MIME/extension 归一、OSS private upload、HEAD、asset object confirm、entity binding。
  2. Adapter 输入应显式包含 provider、prompt、reference images、OSS prefix/path/file name、asset kind、entity kind/id、slot、owner/profile/source job、metadata 和可选透明背景后处理。
  3. Adapter 输出应保留 legacy public path、object key、asset object id、MIME、extension、task id 和实际 prompt。
  4. Adapter 不负责扣费、退款或钱包读取;计费仍由调用方显式包裹。
  5. 图片 provider 协议不再放在玩法模块里实现。VectorEngine gpt-image-2 创建 / 编辑协议、URL / base64 图片解析、远端图片下载、请求超时 / 上游状态 / 响应解析 / 缺图 / 下载失败的结构化日志统一在 server-rs/crates/platform-image/src/vector_engine/;其中 client.rs 只保留 provider 调用编排,transport.rs 负责 HTTP client 与 reqwest 错误归一,request.rs 负责请求体和路径,payload.rs 负责响应 JSON 字段提取,response.rs 负责响应状态分流和图片结果归一。api-server 只负责配置校验、玩法 prompt 编排、OSS / asset object / binding 持久化、计费和外部 API 失败审计落库。
  6. OSS 平台适配日志统一在 server-rs/crates/platform-oss 输出,覆盖 sign_post_objectsign_get_object_urlhead_objectput_object。日志字段固定使用 provideroperationbucketendpointobject_key / key_prefixaccesscontent_typecontent_lengthstatusstatus_classerror_kindelapsed_ms,只记录对象定位和排障信息;不得输出 AccessKey、policy、signature、Authorization header 或完整 signed URL。generated 私有对象上传时必须由 OSS 对象头承载浏览器 / CDN 缓存策略,默认写入 Cache-Control: public, max-age=31536000, immutable,不得改成 api-server 本地磁盘静态资源兜底。
  7. Puzzle、Match3D、音频、GLB、视频等复杂媒体可以复用 OSS + asset object + binding 的底层持久化能力,但玩法专属处理规则留在各自编排层,不塞进公共接口。
  8. 拼图入口页与结果页新增关卡的本地参考图不走浏览器直传 OSS,前端读取为 Data URL 后随创作 action 提交,并在读取前限制 6MB、显示“图片≤6MB”。api-server 必须对 Data URL 实际字节数再次校验;历史图片才提交 referenceImageAssetObjectId(s),后端校验 asset_object 的 bucket、kind、图片 MIME、大小和 owner 后签发只读 URL 给 VectorEngine 读取。
  9. 系列素材图集实现真相源在 server-rs/crates/platform-image/src/generated_asset_sheets/:调用方必须传入 grid_size 作为 n*nn,可选传入物品名称 prompt 模板和特殊设定 prompt;模块负责 sheet prompt 组装、按 n*n 切片、透明化、PNG 输出、OSS private upload 请求构造和 sheet / item / special prompt 元数据持久化。server-rs/crates/api-server/src/generated_asset_sheets.rs 只保留 AppState / AppError 适配和兼容导出。玩法只负责规划 slot、调用具体生图 provider、计费、失败回写,以及把通用切片结果映射回自己的 DTO / 草稿 / runtime 字段。

SpacetimeDB schema 变更规则

  1. 任何 table、view、reducer、procedure、row shape 或 bindings 变化,都必须同步本文件表 / view 目录和生成绑定;真实 table 变化还必须同步 server-rs/crates/spacetime-module/src/migration.rs,view 属于派生投影,不写入迁移导入导出表清单。
  2. 已有表新增字段必须放在 Rust 表结构体最后,并设置明确 #[default(...)]
  3. 已发布并持久化的 enum 新增 variant 只能追加到末尾,不能插入中间、删除、重命名或重排;否则会移动既有 variant 的判别序号,导致旧数据解释错误或生产发布 schema 迁移失败。
  4. 删除字段、改名、重排字段、改类型或修改字段属性前,必须先询问用户并确认迁移计划。
  5. Vec 字段不要直接写无法 const 求值的 default;需要默认空集合时优先使用 Option<Vec<T>>#[default(None::<Vec<T>>)],业务层归一为空数组。
  6. 运行态读表必须按已声明索引访问。只要 table 上存在覆盖查询前缀的 #[index(...)] 或主键 / unique accessor,列表、详情、快照组装和计数都先用对应 accessor .filter(...) / .find(...),再在内存中处理索引无法覆盖的残余条件;不得用 .iter().filter(...) 扫整表替代现成索引。
  7. 面向公开列表的只读投影优先做成 public view / public 读模型表,并由 api-serverspacetime-client 长期订阅后读本地 cache。跨玩法公开作品统一主读模型是 public_work_gallery_entrypublic_work_detail_entry;各玩法既有 *_gallery_card_view / *_gallery_view / custom_world_gallery_entry 保留为 source view 和兼容路径。短期不把作品列表整体交给浏览器前端直接订阅;不要让 HTTP 列表接口每次请求都调用 procedure 重新组装全量列表。需要请求时间窗口的轻量统计可订阅 public_work_play_daily_stat 后在 api-server 本地聚合,需要写入副作用的详情、点赞、游玩记录仍走玩法 procedure / reducer。前端不得直接订阅 puzzle_work_profilecustom_world_profile 等领域源表,也不得自己做 join、聚合或权限逻辑。首屏、排序、字段归一、权限降级和 HTTP fallback 由 api-server BFF 维持。
  8. 多列索引按 SpacetimeDB 绑定生成的元组参数直接传入,例如 .filter((source_type, profile_id, played_day));前缀查询只传前缀元组,例如 .filter((scope_kind, scope_id.as_str()))。不要为了绕过类型问题退回整表遍历。
  9. procedure result 必须返回 typed snapshot / typed value。spacetime-client mapper 不得再通过 row_json/session_json/work_json/items_json/run_json/event_json/feedback_json: Option<String> 做跨层 JSON 字符串传输,也不得在 mapper 里反序列化旧 *JsonRecord 兼容结构。业务内部持久化字段如 profile_payload_jsonlevels_json 等不属于 procedure result 载荷例外,仍按各自表契约处理。
  10. 修改后运行:
npm run spacetime:generate
npm run check:spacetime-runtime-access
npm run check:spacetime-schema
npm run check:server-rs-ddd

本地联调和人工排障不要使用 spacetime --root-dir。本地数据隔离使用项目脚本或 --data-dir;发布目标显式传 --server / --server-url

账户充值数据契约

  1. profile_recharge_product_config 是泥点和会员商品配置真相源,默认商品只在表为空时由 SpacetimeDB 播种。module-runtime 中的默认商品 helper 只作为空库种子和兼容入口,不再作为运行期业务真相。
  2. 默认泥点商品固定为四档:points_60 = 60 泥点 / 600 分points_180 = 180 + 90 泥点 / 1800 分points_300 = 300 + 150 泥点 / 3000 分points_680 = 680 + 340 泥点 / 6800 分points_60 的首充赠送为 0;后三档首次购买分别赠送基础泥点的 50%
  3. 后台通过 /admin/api/profile/recharge-products 读写充值商品配置;字段覆盖 productId、标题、商品类型、金额分、基础泥点、首充赠送泥点、会员天数、徽标、说明、会员层级、会员每周期限时泥点、周期天数、队列上限、折扣率、启用状态和排序。
  4. 充值中心、下单校验和支付确认入账都读取 profile_recharge_product_config。充值中心 BFF 还必须在 mudPointBalance 下发 totalPointspermanentPointslimitedPointslimitedExpiresAtdailyFreePointsdailyFreeResetPointsdailyFreeResetsAt;前端以该 read model 为真相源,不得自行用总额相减推算余额桶。当前版本公开 UI 只渲染不限时泥点和每日免费泥点,limitedPointslimitedExpiresAt 仅保留给存量兼容和后端结算。历史订单保留下单时写入的商品标题、金额、渠道、状态和 provider transaction id,不随配置改动回写。
  5. 泥点首充资格按 user_id + product_id 的历史 paid 订单独立判断。某个档位已支付后,只隐藏该档位的首充赠送;其它未购买档位仍展示和结算首充赠送。
  6. hasPointsRecharged 只保留为账号是否发生过任一泥点充值的兼容字段,不得驱动所有商品展示隐藏或结算金额计算。前端只渲染后端返回的商品快照。
  7. 当前版本公开充值 UI 只展示泥点商品,不渲染会员购买页签、会员商品、购买会员或升级会员入口。充值中心响应中的会员商品兼容字段、默认会员商品、profile_membership 和周期刷新逻辑继续保留;存量会员的 cycle_remaining_points 仍通过充值中心 read model 下发用于兼容和结算,但不作为限时泥点在当前版本前台展示。
  8. 默认会员商品为空库播种时使用 Starter / Basic / Pro / Ultimate 四档,默认有效期均为 30 天,每周期限时泥点分别为 200 / 800 / 2500 / 6000,队列上限分别为 2 / 2 / 5 / 10
  9. 会员有效期和周期重置时间是两条独立时间线。expires_at 只决定会员是否生效;cycle_resets_at 只决定当前周期限时泥点何时重置。会员升级只更新档位并补齐当前周期限时泥点差额,不延长 expires_at,不移动 cycle_resets_at 和周期天数。同级会员购买只从当前 expires_at 延长有效期,不发放额外当前周期泥点,也不移动重置时间。
  10. 会员周期刷新发生在个人中心、充值中心、任务中心、账单读取和钱包扣费入口:到达 cycle_resets_at 时先清除上周期剩余限时泥点,再发放当前会员档位周期额度;会员过期时清除剩余限时泥点并把状态降为普通。周期发放和重置流水分别使用 membership_period_grantmembership_period_reset
  11. paymentChannel 缺失、未知或冒用小程序支付设备时必须拒绝;真实微信渠道只允许 wechat_mpwechat_mp_virtualwechat_jsapiwechat_h5wechat_native,生产配置不得把真实支付静默降级为 mock
  12. access JWT 只携带最小设备快照 device.client_typedevice.client_runtimedevice.client_platform。充值下单按该快照拦截小程序渠道:小程序只允许 wechat_mp / wechat_mp_virtual;移动网页和微信内 H5 走 wechat_h5;桌面网页和桌面微信走 wechat_nativewechat_jsapi 仅保留后端能力,未接微信开放平台前不由前端自动选择。历史普通 Web 登录态若缺少设备快照也允许继续进入 JSAPI / H5 / Native 渠道的后续支付配置校验,但不放宽小程序虚拟支付。
  13. 所有微信真实渠道都以微信支付通知或服务端查单确认 SUCCESS 为到账事实;小程序、H5 跳转和 Native 二维码返回都不能直接发放泥点或会员。
  14. 微信 JSAPI / H5 / 小程序 / Native 下单统一显式传 5 分钟 time_expire,格式为 RFC3339 秒级时间;Native 额外通过 wechatNativePayment.expiresAt 下发给前端二维码弹窗展示。
  15. 真实微信渠道的新建 pending 充值订单会写入 SpacetimeDB 原生 scheduled 表 profile_recharge_order_expiration_timer。到期 reducer 只做数据库内状态转换:订单仍为 pending 时更新为 expired 并写 expired_at,同时删除 timer。HTTP api-server 只订阅这张活跃 timer 表的删除事件,收到 order_id 后通过 procedure 重新读取订单,只有状态确认为 expired 才执行微信查单补偿;支付或主动关闭同样会删除 timer,但会被状态判断忽略。监听断线期间遗漏的删除事件由未检查过期订单 catch-up 补齐,不订阅完整 profile_recharge_order 历史表。普通微信支付查单中 SUCCESS 可把 expired 补确认成 paid 入账;NOTPAY 会调用微信关单并把本地订单保持为 expiredCLOSED / REVOKED / PAYERROR / ORDER_NOT_EXIST 只记录检查结果。wechat_mp_virtual 使用小程序 access_token 和虚拟支付 AppKey 调用官方 /xpay/query_order,只在返回单号、支付类型 order_type=0/7、金额、合法 paid_time 与本地契约一致且状态为 2/3/4 时补入账;退款类型 1/8 不得触发充值,其余已知状态只记录检查结果。short_series_goodsstatus=2 恢复时先幂等入账,再调用 /xpay/notify_provide_goods,失败后允许基于本地 paid 状态只重试发货;short_series_coin 不调用现金单发货接口。external-generation-worker / controller 不处理充值过期。

创作入口泥点扣费契约

  1. creation_entry_type_config.unified_creation_spec_json 内的 mudPointCost 是玩法新建草稿初始生成的泥点成本真相源,同时供入口卡展示和前端余额前置校验使用;旧契约缺失时允许按代码默认成本兜底。
  2. api-server 执行拼图首图生成、抓大鹅完整草稿生成和汪汪声浪初始三图生成时,必须通过 GET /api/creation-entry/config 同源配置解析对应玩法成本后再调用钱包扣费 procedure,不得继续使用前端或后端硬编码常量作为实际扣费真相。
  3. 结果页单图重生成、发布、道具使用和其它独立资产操作仍按各自业务操作成本执行;不要把初始草稿成本误套到这些单次操作上。
  4. 资产操作的预扣费必须 fail-closed:钱包或 SpacetimeDB 预扣费不可达、超时或返回业务错误时,api-server 直接返回错误,不允许继续调用图片、音频、GLB 等外部生成 provider。
  5. 需要支持 HTTP retry 的计费 ledger id 必须包含当前请求的 request_id;前端 fetchWithApiAuth 同一次业务请求的静默刷新重试复用同一个 x-request-id,后端不得再使用 prompt 指纹或随机 asset id 作为扣费幂等键。
  6. 外部生成已预扣费但后续失败时必须先同步调用钱包退款;若 SpacetimeDB 暂不可用,退款请求写入 wallet-refund-outbox 本地文件并由后台 worker 重放。默认启用,配置项为 GENARRATIVE_WALLET_REFUND_OUTBOX_ENABLEDGENARRATIVE_WALLET_REFUND_OUTBOX_DIRGENARRATIVE_WALLET_REFUND_OUTBOX_BATCH_SIZEGENARRATIVE_WALLET_REFUND_OUTBOX_FLUSH_INTERVAL_MSGENARRATIVE_WALLET_REFUND_OUTBOX_MAX_BYTES。outbox 文件按 refund ledger id 幂等落盘;成功重放后删除,坏文件隔离为 corrupt-*。外部生成任务触发的扣费和退款必须在 profile_wallet_ledger.metadata_json 中写入 externalGenerationJobId,outbox 重放也必须保留同一任务 ID,便于从退款记录追溯到正式生成任务。
  7. 拼图首图后台生成的跨实例互斥锁必须落在 SpacetimeDB puzzle_background_compile_task 表,claim id 由 task_id + request_id 构成,释放时必须校验 claim id,避免旧后台任务释放新请求抢到的租约。

用户钱包与编辑器生成扣费契约

  1. 新用户账号完成注册并成功同步正式认证表后,注册赠送金额读取 profile_wallet_config.initial_mud_points;后台通过 /admin/api/profile/wallet-config 维护“账号初始泥点数”。未写入配置时默认仍为 100 泥点。流水原因仍使用 new_user_registration_reward,流水 ID 继续保持幂等,重复发放请求不得叠加余额。
  2. 用户钱包余额对外仍暴露为一个总余额,但后端扣费必须按“每日免费泥点 -> 会员周期限时泥点 -> 普通永久泥点”的顺序消耗,前端不得自行决定扣费桶。扣费流水 metadata_json 必须记录 dailyFreePointsDeltadailyFreeDayKeymembershipPeriodPointsDeltapermanentPointsDelta 和会员限时泥点所属 cycleResetsAtMicros;资产退款中的会员限时泥点只在原周期仍有效时恢复会员额度,其余会员部分进入普通永久泥点。原每日免费消费部分在同一业务日退款时恢复原当日额度;跨北京时间业务日退款时叠加到退款当日每日免费桶,不进入普通永久泥点,当日 granted_pointsremaining_points 均可因此超过 20。原永久泥点消费部分无论是否跨业务日,均按退款流水中的 permanentPointsDelta 退回普通永久泥点。
  3. 每日免费泥点是独立于每日任务和会员周期的正式余额额度,基础发放量固定为 20,不得由前端或后台任务配置改写。profile_daily_free_points 保存当前北京时间业务日、当日基础发放及跨日退款叠加后的总额度和剩余额度;北京时间每日 00:00 作为业务日边界,个人中心、充值中心、账单读取和钱包扣费入口在首次触达新业务日时原子清除昨日剩余及退款叠加量,并把今日 granted_pointsremaining_points 重置为 20。首次初始化使用 daily_free_grant 流水,跨日重置使用 daily_free_reset 流水。惰性落库不能改变“北京时间 00:00 后读取即为新日额度”的对外语义。
  4. 每日任务奖励继续使用 daily_task_reward 流水并进入普通永久泥点,但主站隐藏每日任务卡片和任务中心入口,不再把每日登录任务描述为“每日免费泥点”。任务配置、进度、领取记录和后台管理能力暂时保留,除非后续需求明确删除。
  5. 编辑器画板所有会调用外部生成 provider 的入口都不从前端请求接收 priceMudPoints;同步请求以 SpacetimeDB editor_generation_pricing_config 当前全局配置计算,外部生成队列则以 external_generation_job.price_mud_points 保存的入队价格为准,worker 的扣费、退款、响应和资产成本不得按执行时配置重算。前端按钮泥点只作为展示。
  6. 编辑器图片生成 / 图片修改 / 图标 spritesheet / UI 设计图提取素材 / 视频 / 角色动作 / 音效 / 背景音乐必须在后端计算模型价格后使用 execute_billable_asset_operation_with_cost 预扣泥点;预扣失败必须 fail-closed,不得继续提交 VectorEngine、Ark、Suno 或 Vidu 上游任务。
  7. 队列任务按 job_id + claim_attempt 使用独立 consume/refund ledger。新 attempt 结算旧 attempt 时必须先写 asset_operation_wallet_settlement:旧 consume 已存在则原子退款,尚不存在则写取消 intent;迟到 consume 在同一 SpacetimeDB 事务内看到 intent 后必须失败关闭。重复 consume/refund 只有用户、金额、来源和配对 ledger 全部一致时才可视为幂等成功。lease 过期时只有 attempt < max_attempts 才能递增并重领;最终 attempt 已耗尽时,claim transaction 必须直接把 job 收口为 failed、清理 lease、写失败事件并结算当前 attempt,不能再把任务返回 worker 或调用 provider。
  8. 音频生成的编辑器链路虽然任务提交和结果发布分离,仍必须把提交时后端计算出的模型价格写入 AudioAssetBindingTarget.billing_points_cost,最终发布落资产时按该价格扣费;创作音频目标未提供该字段时才使用旧的创作音频固定成本。
  9. 编辑器进入外部生成持久队列的图片生成、图片修改、去背景、图标 spritesheet、UI 设计图提取、角色动作和视频参考图,只允许提交已登记的 generated objectKeyresourceIdassetId;任务 request_payload_json / result_payload_json 任意层级都禁止 data: / blob:,并受统一字节上限保护。objectKey 必须归属于当前账号的 editor_project_resourceeditor_assetasset_object,后端通过归属校验后才签名读取 OSS。本地红框序号标注图必须先上传并确认对象,再把 objectKey 入队;不得把既有 objectKey 下载成 Data URL 后写入任务。图标素材和 UI 素材提取的额外参考图必须真正传入 provider,不得只写入 generationInputs 展示快照;图片快速编辑当前不开放额外参考图。UI 素材提取额外参考图上限为 5 张,普通图片生成上限 5 张,图标素材上限 8 张额外参考图。同步且不持久化的历史兼容入口即使仍能解析 Data URL,也不能把该值转存到工程、素材、元数据、审计或任务表。

外部服务与资产

  • LLM:通用 LLM 门面继续使用 GENARRATIVE_LLM_*;创意 Agent gpt-5.4-mini Chat Completions 文本链路已于 2026-06 从 APIMart 迁移到 VectorEngine,使用 VECTOR_ENGINE_BASE_URL / VECTOR_ENGINE_API_KEY 构造 OpenAI-compatible clientapi-server 会把未带 /v1 的 VectorEngine base URL 规范化到 /v1 后请求 /chat/completions。通用 /api/llm/chat/completions 代理使用 GENARRATIVE_LLM_PROVIDER=openai-compatibleGENARRATIVE_LLM_BASE_URL=https://api.vectorengine.cn/v1GENARRATIVE_LLM_MODEL=gpt-5.4-mini;未单独配置 GENARRATIVE_LLM_API_KEY 时可复用 VECTOR_ENGINE_API_KEYAPIMART_BASE_URL / APIMART_API_KEY 只作为历史残留,不再作为创意 Agent gpt-5.4-mini 客户端来源;后续排障时优先确认 VectorEngine /v1/models/v1/chat/completions/v1/responses 可用性。
  • 图片生成:VectorEngine gpt-image-2 图片 provider 归属 platform-image,密钥只在后端环境变量中;api-server 内的 openai_image_generation.rs 只是兼容调用面和外部失败审计桥接,不再承载 provider 协议实现。实际外部生成运行记录统一落 tracking_eventevent_key = external_generation_runmetadata 记录开始 / 结束时间、耗时、状态、成功标记、失败原因、provider task id 和结果摘要,不再写回过时的 ai_task。DashScope 只按仍在使用的历史能力单独处理,不作为 GPT-image-2 兜底。VectorEngine /v1/images/generations/v1/images/edits 上游 POST 使用 libcurl 发送;reqwest 只保留给参考图 URL 下载和响应中图片 URL 下载。/v1/images/edits 的 multipart 参考图必须作为 libcurl 文件上传 part 发送,字段名为 image,实现上使用 Form::buffer(file_name, bytes) 并设置 Content-Type;不能只用 contents(...).filename(...),否则上游会把请求转码为缺少图片并返回 image is requiredrequest_send 阶段的 curl timeout / connect error 按可重试传输错误处理,最多尝试 5 次,并使用指数退避加短抖动;排障时优先看 attemptmax_attemptsretry_delay_msreference_image_bytes_totalrequest_params,不要把 SendRequest 当成上游业务错误。
  • 编辑器抠图服务:手动 POST /api/editor/images/background-removals 继续代理独立 BiRefNet 服务,配置为 GENARRATIVE_EDITOR_BACKGROUND_REMOVAL_BASE_URLGENARRATIVE_EDITOR_BACKGROUND_REMOVAL_TOKENGENARRATIVE_EDITOR_BACKGROUND_REMOVAL_REQUEST_TIMEOUT_MS。角色形象生成、图标 spritesheet 生成和 UI 设计图素材提取的生成后纯色背景透明化改走独立 BgFilter 服务,配置为 GENARRATIVE_EDITOR_BGFILTER_BASE_URLGENARRATIVE_EDITOR_BGFILTER_TOKENGENARRATIVE_EDITOR_BGFILTER_REQUEST_TIMEOUT_MS,默认 base URL 为 http://58.87.105.82/bgfilter,默认请求超时为 180000msBgFilter 当前为 CPU 推理,单次抠图较慢,必须留足超时),token 未配置时复用 BiRefNet token。BgFilter 请求必须显式传 screen_color=<screenColor>seg_model=<segModel>;前端用户路径不展示抠图模型选择并固定提交默认 birefnet,后端仍识别内部保留的 anime-seg,其中 birefnet 只表示 BgFilter 管线内部后端,不等同于手动去背景的独立 BiRefNet 服务。BgFilter 调用失败,或连续失败达到 GENARRATIVE_EDITOR_BGFILTER_CIRCUIT_FAILURE_THRESHOLD(默认 3)并在 GENARRATIVE_EDITOR_BGFILTER_CIRCUIT_COOLDOWN_SECONDS(默认 300)内打开熔断时,均跳过或结束 BgFilter 调用后复用同一兜底链:先调用阿里云通用抠图,阿里云失败才使用本地 editor_green_screen 键色扣除;熔断期不得直接退化到本地兜底。角色动作视频生成的背景色已与生图链路统一:screenColor=auto 时由视觉 LLMgpt-5-miniResponses 协议、low 推理档)读源角色图自动决策,并经硬过滤器剔除与前景 / 皮肤撞色的候选,手动 hex 则尊重用户选择;透明源角色图在提交 Ark 图生视频前先合成到选定背景色实色,使视频背景等于抠图键色。抽帧后逐帧优先走阿里云通用抠图,失败时降级本地 editor_green_screen 键色兜底(按生成时选定的背景色,而非固定 #00FF00)。阿里云通用抠图配置为 GENARRATIVE_ALIYUN_MATTING_ENABLEDGENARRATIVE_ALIYUN_MATTING_ENDPOINTGENARRATIVE_ALIYUN_MATTING_ACCESS_KEY_IDGENARRATIVE_ALIYUN_MATTING_ACCESS_KEY_SECRETGENARRATIVE_ALIYUN_MATTING_REQUEST_TIMEOUT_MS;未配置专用 AK/SK 时可复用 ALIBABA_CLOUD_ACCESS_KEY_ID / ALIBABA_CLOUD_ACCESS_KEY_SECRET,默认 endpoint 为 imageseg.cn-shanghai.aliyuncs.com。BgFilter 与阿里云抠图失败都写入 external_api_call_failure 审计。
  • Match3D 物品 sheet:关卡整图完成后走 VectorEngine /v1/images/edits multipart image,模型为 gpt-image-22K 1:1 输出 10*10 spritesheet;物品 sheet prompt 固定要求单一纯绿色 #00FF00 / RGB(0,255,0) 绿幕背景,后端上传 OSS 前必须把绿幕扣成透明 PNG,并把透明整图写入 itemSpritesheetImageSrc/itemSpritesheetImageObjectKey。后端优先按透明 alpha 连通域从该 sheet 识别真实素材矩形并持久化 20 个物品、每个 5 个形态;识别数量不足时才回退 10*10 固定网格。通用系列素材图集的行列索引按每行 2 个物品计算,必须落在 1..=10,难度只决定运行态加载 3 / 9 / 15 / 20 种。
  • Match3D UI spritesheet 和背景派生图:关卡整图作为参考图并发生成 1K 1:1 UI spritesheet 与 1K 9:16 背景图,模型均为 gpt-image-2。UI spritesheet prompt 固定要求单一纯绿色 #00FF00 / RGB(0,255,0) 绿幕背景,后端上传 OSS 前必须把绿幕扣成透明 PNG;背景图必须合成为全画幅不透明 PNG。
  • Match3D 1:1 容器 UIVectorEngine /v1/images/edits multipart 参考图。该容器参考图是后端生图协议输入,必须通过 include_bytes!api-server 编译进二进制,避免 API 单独发布或运行目录缺少 public/ 时生成失败。
  • 敲木鱼敲击物和背景环境图:VectorEngine /v1/images/edits,模型固定 gpt-image-2。敲击物支持 multipart 多参考图,第一张固定为后端内嵌默认木鱼图,用户上传图只作为新主题参考;prompt 必须要求 1:1 单一纯绿色 #00FF00 / RGB(0,255,0) 绿幕背景主体图,并禁止黑底、白底、棋盘格和任何实底背景。当前敲击物和返回按钮上传 OSS 前只做服务端绿幕去背后处理,避免泛抠图误伤玉米等主体像素。背景环境图只使用第一步抠图完成后的透明敲击物图作为参考,prompt 必须要求中央主体预留区保持干净,中央 40% 区域禁止出现主题主体、主体局部特写、轮廓影子或重复元素,主题元素只能作为外围氛围,且必须显式声明不继承任何绿色底色、绿幕底色或纯绿色画布。
  • Hyper3D / Rodin:只保留后端安全代理和旧数据兼容;Rodin 提交、状态、下载和响应解析归属 platform-hyper3dapi-server/src/hyper3d_generation.rs 只做路由、配置和错误 envelope 映射;新 Match3D 草稿和批量新增不再生成 GLB。
  • 音频:视觉小说专用音频路由保留;VectorEngine Suno/Vidu provider 协议、任务提交/查询、音频 URL 提取、下载、MIME/extension 归一和 OSS put 请求准备归属 platform-audioapi-server/src/vector_engine_audio_generation.rs 只做路由、配置、计费、asset object confirm、entity binding 和错误 envelope 映射;拼图、抓大鹅和敲木鱼提示词生成音效入口暂时关闭,通用 /api/creation/audio/* 对这些目标返回 410 Gone。敲木鱼创作只接收上传 / 录音音频资产;前端选择或录音阶段只在浏览器本地处理待提交音频,统一限制裁切后最长 1 秒、裁掉前后声音过小片段,并用浏览器端近似响度算法平衡到 -15 LKFS 后做峰值保护。点击生成时才直传 OSS 并确认 asset_object,创作 JSON 只提交轻量 WoodenFishAudioAsset,不得继续上传 Data URL 音频;未提供时由 api-server 写回内置默认木鱼音 /wooden-fish/default-hit-sound.mp3
  • OSS:私有 generated path 进入浏览器前必须通过 /api/assets/read-url 换签;不要裸请求 /generated-*。请求参数的安全语义不能混用:legacyPublicPath 是历史公开作品兼容口,只允许 platform_oss::LEGACY_PUBLIC_PREFIXES 中的 curated 前缀匿名换签;objectKey 是正式对象引用,绝不能复用该前缀旁路,必须查询 asset_object 并校验配置 bucket、精确 key、PublicRead 或当前 owner。External OpenAPI 的 /api/external/v1/assets/read-url 还必须有 editor:asset scope,并始终以 API Key 绑定的 owner_user_id 执行同一 owner 校验;后台跨账号预览只能走管理员鉴权后的 /admin/api/assets/read-url/api/assets/read-bytes 与主站 read-url 共用完全相同的授权,默认仍应由浏览器使用 signed URL 直读,bytes 只作跨域字节读取 fallback。前端如果收到同一 OSS bucket 的完整 https://*.oss-*.aliyuncs.com/generated-* 地址,也必须先归一为 legacy path 后走同一换签链路,避免裸连私有 bucket 403 或绕过签名缓存。OSS 签名、读签名、HEAD 和 PUT 的结构化日志由 platform-oss 输出,排查资产写入 / 确认失败时优先按 operationobject_key / key_prefixstatus_classerror_kindelapsed_ms 下钻。新上传 generated 私有对象默认写入 Cache-Control: public, max-age=31536000, immutable;旧对象若缺该头,只能依赖 ETag / Last-Modified 协商缓存,应通过 OSS 元数据刷新或 CDN 配置补齐,不要恢复 api-server 静态代理。editor-agent/ 前缀只用于服务端内部读写画布 Agent 会话消息文档,不属于浏览器直传 legacy public prefix/api/assets/direct-upload-tickets 必须拒绝 legacyPrefix=editor-agent,内部读取只允许 editor-agent/{conversationId}.json 形态。
  • 外部 API 失败审计:外部供应商调用未成功时,api-server 必须发送 OTLP 失败事件并写入 tracking_event。VectorEngine 图片 provider 在 platform-image 内输出结构化日志和 PlatformImageFailureAudit,覆盖 request_sendresponse_bodyupstream_statusresponse_parsemissing_imageimage_download 阶段;编辑器 screenColor=auto 的 gpt-5-mini 背景色决策同样必须审计每次已发出的 LLM 调用失败,包括传输 / 超时、上游拒绝、响应体解析、空响应和返回候选外颜色;即使随后降级默认背景色并继续主流程也不得只记 warning。api-server 将这些失败映射成 external_api_call_failurescope_kind = modulescope_id = providermodule_key = external-api。metadata 固定包含 provider、endpoint、operation、failureStage、statusCode、statusClass、timeout、retryable、errorMessage、latencyMs、promptChars、referenceImageCount、imageModel、rawExcerpt,以及在调用方可获得上下文时补充的 userId(触发者)和 profileId(草稿 / 作品 / 场景作用域)。图片生成入口应优先把 owner user id 和 profile id 透传到失败审计,不要只保留 provider 级聚合,否则很难按“谁触发、哪个作品触发”定位问题。入库优先复用 tracking outboxoutbox 不可写或保护阈值拒绝时回退同步写 SpacetimeDB;不得新增前端兜底或在 SpacetimeDB reducer 内做外部 I/O。
  • 外部生成运行记录:所有外部生成编排的完成态统一写入 tracking_eventevent_key = external_generation_runscope_kind = modulescope_id = providermodule_key = external-generation。metadata 固定包含 runIdprovideroperationrequestLabelrequestPayloadstatussuccessfailureReasonproviderRequestIdresultPayloadstartedAtMicroscompletedAtMicrosdurationMs。这类记录只用于运行审计和排障,不再走 ai_task 旧表。

SpacetimeDB 表目录

下列 ### 标题是 schema guard 的机器可读表目录。新增、删除或改名表时必须同步这里。

ai_result_reference

  • Rust 结构体:AiResultReference
  • 源码:server-rs/crates/spacetime-module/src/ai/stages.rs

ai_task

  • Rust 结构体:AiTask
  • 源码:server-rs/crates/spacetime-module/src/ai/tasks.rs

ai_task_event

  • Rust 结构体:AiTaskEvent
  • 源码:server-rs/crates/spacetime-module/src/ai/events.rs

ai_task_stage

  • Rust 结构体:AiTaskStage
  • 源码:server-rs/crates/spacetime-module/src/ai/stages.rs

external_generation_job

  • Rust 结构体:ExternalGenerationJob
  • 源码:server-rs/crates/spacetime-module/src/external_generation.rs
  • 用途:外部生成 worker 的内部持久任务队列;GENARRATIVE_EXTERNAL_GENERATION_MODE=queue 时,api-server HTTP 角色只入队,external-generation-worker 角色通过 claim lease 领取、续租、执行,并用 lease_token 栅栏回写完成 / 失败。队列行继续保存 worker 执行、计费与滚动发布兼容所需字段,但用户可见任务列表、价格、状态、未确认终态数量和通知确认时间的正式读取事实源已经迁到 external_generation_job_summary;BFF 不得再为列表 / 详情 / acknowledge 读取该大表。拼图 compile_puzzle_draft 的前置 compile_puzzle_agent_draftgenerate_puzzle_imagesgenerate_puzzle_ui_background 的业务写回也在对应 SpacetimeDB transaction 内校验 job_id + worker_id + lease_token、job kind、owner 和 source entity,避免过期 worker 写 session / work profile;图片画布编辑器的 editor_image_generationeditor_image_editeditor_background_removaleditor_icon_spritesheet_generationeditor_ui_design_asset_extractioneditor_character_animation_generationeditor_video_generationeditor_sound_effect_generationeditor_background_music_generation 复用同一队列表,worker 成功后经 api-server facade 写入 editor_project_resource / editor_asset / editor_canvas.layers_json,前端只通过 BFF job 状态轮询和项目快照读取恢复完成态。GENARRATIVE_EXTERNAL_GENERATION_MODE=inline 时不创建该队列行,三个 external generation guard 字段必须同时为空才允许 api-server 受控同步写回,半空 guard 仍会拒绝。worker 成功写回业务事实后才能 complete job;业务失败态写回成功后才能 fail job,失败态未写回时保留租约等待后续重领。
  • 载荷约束:本次先对 source_module = editor-canvasrequest_payload_json / result_payload_json 实施有限大小合法 JSON、任意层级禁止 data: / blob: 的双层门禁,只保存 worker 执行必需的普通参数和已登记媒体引用;其它玩法在完成各自参考图资源化之前不由本次门禁静默改变既有请求契约。该主表只供 worker claim / 执行和受控维护读取;正式用户任务列表、单任务状态、队列概览与 acknowledge 不得再返回或解析这两个 payload。

external_generation_job_summary

  • Rust 结构体:ExternalGenerationJobSummary
  • 源码:server-rs/crates/spacetime-module/src/external_generation.rs
  • 用途:外部生成正式任务列表的轻量投影,按 job_id 保存 owner、来源、状态、价格、有界错误摘要、通知确认时间、各阶段时间和入队时提取的 request_prompt,不包含 request/result payload、worker lease 或 dedupe 内部字段。错误摘要统一拒绝内联媒体并限制为 2048 字符;列表在单次 owner 扫描中同时计数并只保留请求 limit 的固定大小 top-N,不得先收集全量历史再截断。enqueue、claim、renew、complete、fail 事务同步投影;acknowledge 只更新该轻量表并写审计事件,后续主任务同步必须保留已有确认时间,禁止为了写确认时间加载 / 重写大 payload 行。BFF 的列表、状态和确认只调用 summary procedure。历史终态任务由迁移操作员的游标分批 maintenance procedure 在压缩 payload 时同步回填摘要,正式列表不得为兼容旧数据回扫完整主表。
  • 正式读取 procedure 为 get_external_generation_job_summary_and_returnlist_external_generation_job_summaries_and_returnacknowledge_external_generation_job_summaries_and_return。历史维护 procedure 为 compact_external_generation_job_payloads_and_returnbackfill_external_generation_job_summaries_and_return,仅 migration operator 可调用;运维入口统一使用 npm run spacetime:external-generation:maintain -- ...,默认 dry-run、单批最多 25 条。B-tree cursor 选择阶段最多反序列化 limit + 1 行,apply 再按主键逐条读取选中行;怀疑存在单行异常巨型 JSON 时必须先使用 --limit 1。payload 压缩额外固定使用 source_module = editor-canvas 的复合 cursor 索引,不得静默改写其它玩法历史任务。

external_generation_job_event

  • Rust 结构体:ExternalGenerationJobEvent
  • 源码:server-rs/crates/spacetime-module/src/external_generation.rs
  • 用途:外部生成任务审计事件表,按 job_idowner_user_id 记录 enqueuedclaimedlease_renewedcompletedfailedacknowledged 等状态转换事实。状态转换只能由 SpacetimeDB procedure 写入,不由前端或 worker 直接改表;该表用于追溯任务生命周期和排障,不替代 external_generation_job 当前状态。

ai_text_chunk

  • Rust 结构体:AiTextChunk
  • 源码:server-rs/crates/spacetime-module/src/ai/stages.rs

analytics_date_dimension

  • Rust 结构体:AnalyticsDateDimension
  • 源码:server-rs/crates/spacetime-module/src/runtime/analytics_date_dimension.rs

asset_entity_binding

  • Rust 结构体:AssetEntityBinding
  • 源码:server-rs/crates/spacetime-module/src/asset_metadata/bindings.rs

asset_event

  • Rust 结构体:AssetEvent
  • 源码:server-rs/crates/spacetime-module/src/asset_metadata/objects.rs

asset_object

  • Rust 结构体:AssetObject
  • 源码:server-rs/crates/spacetime-module/src/asset_metadata/objects.rs
  • 说明:对象 metadata 以 bucket / key 标识正式对象及其 owner、访问策略。确认接口的 owner 必须来自登录会话或 External API Key 绑定的认证主体,不接受请求体指定 owner;同 bucket / key 首次登记后,重复 confirm 不得改变 owner。资产读取必须先查询同 bucket / key metadata,存在时严格执行 PublicRead / owner ACL;只有 metadata 不存在的历史对象才能进入 curated legacy 白名单兼容。

auth_identity

  • Rust 结构体:AuthIdentity
  • 源码:server-rs/crates/spacetime-module/src/auth/tables.rs
  • 职责:只表达登录入口身份键到 user_account.user_id 的绑定;provider_uid 保存手机号 E.164 或微信 openidprovider_union_id 保存微信 unionid。账户资料以 user_account.phone_number_e164user_account.display_nameuser_account.avatar_url 为准,auth_identity.phone_e164 / display_name / avatar_url 仅保留旧行兼容,不再作为写入或恢复真相。

auth_store_projection_meta

  • Rust 结构体:AuthStoreProjectionMeta
  • 源码:server-rs/crates/spacetime-module/src/auth/tables.rs

认证恢复策略:api-server 启动时只从 SpacetimeDB 正式认证表(user_account / auth_identity / refresh_session)导出 typed AuthStoreProjectionView,再恢复 module-auth 的进程内认证工作集;运行中 Bearer sid 或 refresh cookie 在本进程工作集内未命中时直接按失效处理,不再从 SpacetimeDB 导出整包认证状态刷新内存,避免旧投影把重复手机号或旧会话重新灌回进程。module-auth 只保留内存工作集和 projection 导入 / 导出能力,不再保留 JSON 快照导入 / 导出能力,也不写本地持久化文件;auth-store.json / GENARRATIVE_AUTH_STORE_PATH 不再是兼容恢复源。认证创建、登录会话、刷新、退出、改密、重置密码、绑定和资料变更等写操作必须在返回客户端前通过 sync_auth_store_projection 成功同步 SpacetimeDB 正式认证表;同步失败时接口返回错误,不允许把只存在于当前进程内存的账号或会话当成成功结果。新用户注册奖励、邀请码绑定和登录埋点必须排在认证同步成功之后,避免认证没落库时先写出钱包或邀请关系。若启动恢复阶段 SpacetimeDB 不可连接或超时,api-server 会按固定间隔持续重试认证工作集恢复,恢复成功后才开始监听 HTTP,避免一次短超时让进程永久停留在依赖不可用状态。

auth_store_snapshot 表和旧 import_auth_store_snapshot_json / export_auth_store_snapshot_from_tables procedure 已删除。认证投影同步只读写 user_accountauth_identityrefresh_sessionauth_store_projection_metaauth_identity 不再写 phone_e164display_nameavatar_url,这些账号资料只以 user_account 为准。

bark_battle_draft_config

  • Rust 结构体:BarkBattleDraftConfigRow
  • 源码:server-rs/crates/spacetime-module/src/bark_battle/tables.rs

bark_battle_leaderboard_entry

  • Rust 结构体:BarkBattleLeaderboardEntryRow
  • 源码:server-rs/crates/spacetime-module/src/bark_battle/tables.rs

bark_battle_personal_best_projection

  • Rust 结构体:BarkBattlePersonalBestProjectionRow
  • 源码:server-rs/crates/spacetime-module/src/bark_battle/tables.rs

bark_battle_published_config

  • Rust 结构体:BarkBattlePublishedConfigRow
  • 源码:server-rs/crates/spacetime-module/src/bark_battle/tables.rs
  • 字段变更:visible 控制是否进入公开列表 / 详情,新作品默认 false;旧迁移数据由 migration.rs 按历史公开默认补 true

bark_battle_runtime_run

  • Rust 结构体:BarkBattleRuntimeRunRow
  • 源码:server-rs/crates/spacetime-module/src/bark_battle/tables.rs

bark_battle_score_record

  • Rust 结构体:BarkBattleScoreRecordRow
  • 源码:server-rs/crates/spacetime-module/src/bark_battle/tables.rs

bark_battle_work_stats_projection

  • Rust 结构体:BarkBattleWorkStatsProjectionRow
  • 源码:server-rs/crates/spacetime-module/src/bark_battle/tables.rs

battle_state

  • Rust 结构体:BattleState
  • 源码:server-rs/crates/spacetime-module/src/gameplay.rs

big_fish_agent_message

  • Rust 结构体:BigFishAgentMessage
  • 源码:server-rs/crates/spacetime-module/src/big_fish/tables.rs

big_fish_asset_slot

  • Rust 结构体:BigFishAssetSlot
  • 源码:server-rs/crates/spacetime-module/src/big_fish/tables.rs

big_fish_creation_session

  • Rust 结构体:BigFishCreationSession
  • 源码:server-rs/crates/spacetime-module/src/big_fish/tables.rs
  • 索引:by_big_fish_session_owner_user_idby_big_fish_session_stage。公开广场 view 使用 by_big_fish_session_stage 读取已发布会话,避免扫整表。
  • 字段变更:visible 控制是否进入公开列表 / 详情,新作品默认 false;旧迁移数据由 migration.rs 按历史公开默认补 true

big_fish_event

  • Rust 结构体:BigFishEvent
  • 源码:server-rs/crates/spacetime-module/src/big_fish/events.rs

big_fish_runtime_run

  • Rust 结构体:BigFishRuntimeRun
  • 源码:server-rs/crates/spacetime-module/src/big_fish/tables.rs
  • Rust viewbig_fish_gallery_view
  • 返回类型:Vec<BigFishWorkSummarySnapshot>
  • 源码:server-rs/crates/spacetime-module/src/big_fish/session.rs
  • 说明:大鱼吃小鱼公开 source 投影,只从 Published creation session 组装公开卡片字段;统一公开列表 / 详情主路径通过 public_work_gallery_entry / public_work_detail_entry 消费该 view 并映射成跨玩法契约。玩法旧 gallery 路径保留兼容 shape;个人作品列表、详情、点赞、游玩记录和 Remix 仍按原有 procedure / reducer 路径处理。

chapter_progression

  • Rust 结构体:ChapterProgression
  • 源码:server-rs/crates/spacetime-module/src/gameplay.rs

creation_entry_config

  • Rust 结构体:CreationEntryConfig
  • 源码:server-rs/crates/spacetime-module/src/runtime/creation_entry_config.rs
  • 字段:config_idstart_titlestart_descriptionstart_idle_badgestart_busy_badgemodal_titlemodal_descriptionupdated_atevent_titleevent_descriptionevent_cover_image_srcevent_prize_pool_mud_pointsevent_starts_at_textevent_ends_at_textevent_banners_jsonpublic_work_interactions_json
  • 迁移兼容:旧迁移包缺少活动横幅字段时,由 migration.rs 写入 None / 58000 默认值;旧库缺少 event_banners_json 时写入 None,运行态读取层再按 module-runtime 默认公告数组归一,不覆盖后台已保存配置,也不把旧结构化 eventBanner 升格为前端优先数组。旧库缺少 public_work_interactions_json 时写入 None,读取层按 module-runtime 默认作品互动矩阵补齐 publicWorkInteractions,不覆盖后台已保存开关。HTTP 响应同时返回 eventBanners 数组、旧 eventBanner 单条兼容字段和 publicWorkInteractions 互动矩阵;前端优先消费数组与矩阵。后台新公告配置主格式为 HTML 公告字符串数组或 {title, htmlCode} 对象数组,旧结构化 banner 字段仅保留兼容。默认公告背景和旧结构化默认 coverImageSrc 必须引用 public/ 下真实存在的静态资源,当前为 /creation-type-references/puzzle.webp

creation_entry_type_config

  • Rust 结构体:CreationEntryTypeConfig
  • 源码:server-rs/crates/spacetime-module/src/runtime/creation_entry_config.rs
  • 字段:idtitlesubtitlebadgeimage_srcvisibleopensort_orderupdated_atcategory_idcategory_labelcategory_sort_orderunified_creation_spec_json
  • 迁移兼容:旧迁移包缺少入口分类字段或统一创作契约字段时,由 migration.rs 写入 None / 0 / None 默认值;入口分组展示由 module-runtime 和前端展示派生消费,统一创作契约由 module-runtime 解析为 creationTypes[].unifiedCreationSpec,为空时按 shared-contracts 中当前支持的统一创作默认 spec 回退。unifiedCreationSpec.title 是统一创作页表头契约内容,读取和保存时不按入口 title 自动覆盖。

feature_gate_config

  • Rust 结构体:FeatureGateConfig
  • 源码:server-rs/crates/spacetime-module/src/runtime/feature_gate_config.rs
  • 字段:gate_keyenabledrollout_percentallow_user_idsallow_user_tagsdeny_user_idsdescriptionupdated_at
  • 用途:通用功能灰度事实源。当前创作入口使用 creation-entry:<id> 约定关联入口 IDapi-server 按当前可选登录用户、用户标签和稳定百分比判定后,只把过滤后的入口配置返回普通前端,不下发灰度规则或用户标签。
  • 迁移兼容:新增表不改已有入口表字段;未配置 gate 或 enabled=false 时不限制功能,黑名单用户 ID 优先于白名单和百分比命中。

custom_world_agent_message

  • Rust 结构体:CustomWorldAgentMessage
  • 源码:server-rs/crates/spacetime-module/src/custom_world.rs

custom_world_agent_operation

  • Rust 结构体:CustomWorldAgentOperation
  • 源码:server-rs/crates/spacetime-module/src/custom_world.rs

custom_world_agent_session

  • Rust 结构体:CustomWorldAgentSession
  • 源码:server-rs/crates/spacetime-module/src/custom_world.rs
  • 发布约束:publish_world 的 action payload 不要求携带 settingTextspacetime-module 调用 module-custom-world::resolve_custom_world_publish_setting_text(...),优先从当前 draft_profile_json 草稿真相派生正式 setting_text,避免旧会话 seed_text 为空时在最终 compile / publish 阶段触发 custom_world.setting_text 不能为空

custom_world_draft_card

  • Rust 结构体:CustomWorldDraftCard
  • 源码:server-rs/crates/spacetime-module/src/custom_world.rs

custom_world_gallery_entry

  • Rust 结构体:CustomWorldGalleryEntry
  • 源码:server-rs/crates/spacetime-module/src/custom_world.rs
  • 作用:自定义世界公开 source 读模型。统一公开列表 / 详情主路径通过 public_work_gallery_entry / public_work_detail_entry 消费该投影并映射成跨玩法契约;/api/runtime/custom-world-gallery 保留旧 HTTP shape,并从统一 public cache 映射回旧 DTO。旧 procedure 只用于兼容旧库缺少 gallery 读模型行时的一次性同步兜底。
  • 字段变更:visible 控制是否进入公开列表 / 详情,新作品默认 false;旧迁移数据由 migration.rs 按历史公开默认补 true

custom_world_profile

  • Rust 结构体:CustomWorldProfile
  • 源码:server-rs/crates/spacetime-module/src/custom_world.rs
  • 字段变更:visible 控制是否进入公开列表 / 详情,新作品默认 false;旧迁移数据由 migration.rs 按历史公开默认补 true
  • 兼容约束:历史公开 RPG / 自定义世界 profile 可能存在 publication_status=Publishedpublished_at=None。公开详情、点赞、游玩、Remix 和 custom_world_gallery_entry 同步都以 Published + deleted_at=None + visible=true 判断作品可公开互动;展示和 gallery 同步时间在 published_at 缺失时回退 updated_at,不得仅因 published_at 为空返回“已发布作品不存在”。

custom_world_session

  • Rust 结构体:CustomWorldSession
  • 源码:server-rs/crates/spacetime-module/src/custom_world.rs

database_migration_import_chunk

  • Rust 结构体:DatabaseMigrationImportChunk
  • 源码:server-rs/crates/spacetime-module/src/migration.rs

database_migration_operator

  • Rust 结构体:DatabaseMigrationOperator
  • 源码:server-rs/crates/spacetime-module/src/migration.rs
  • 说明:migration operator 与在线 runtime writer 必须身份互斥。当前 runtime writer 不能被授权为 operator,任何已登记 operator 也不能成为 runtime writer;一旦已有 operatorbootstrap secret 不得新增或接管 operator,后续授权只能由既有 operator 完成。

external_api_key

  • Rust 结构体:ExternalApiKey
  • 源码:server-rs/crates/spacetime-module/src/external_api_key_storage.rs
  • 说明:外部 OpenAPI 调用使用的账号级 API Key 凭据表,只保存 key prefix、SHA-256 hash、作用域、撤销状态和使用时间;明文 Key 只在 /api/profile/api-keys 创建接口返回一次,不进入 SpacetimeDB,且 API Key 管理接口不写入外部 OpenAPI JSON。v1 默认作用域为 editor:projecteditor:canvaseditor:image-generateeditor:asset;其中 editor:project 覆盖项目列表、最近项目、创建、读取、重命名和删除,editor:canvas 覆盖默认画布布局保存,editor:image-generate 覆盖编辑器现有图片生成、重绘 / 调整、规范图、宣发素材、图标 spritesheet 生成 / 拆分、UI 设计图素材拆分、角色动画、视频、音效和背景音乐生成,editor:asset 覆盖素材直传凭证、素材对象确认、签名读取、账号级素材库和项目画布资源记录操作。
  • 索引:by_external_api_key_owner_user_id 用于登录态 API Key 列表;key_hash 唯一索引用于外部 API 鉴权。

editor_agent_conversation

  • Rust 结构体:EditorAgentConversation
  • 源码:server-rs/crates/spacetime-module/src/editor_agent_storage.rs
  • 说明:画布Agent对话会话元数据表,归属单个 editor_project;只保存会话 ID、project、owner、标题、消息 OSS 对象键(editor-agent/{conversationId}.json)、软删标记和时间戳。消息正文整体存 OSS,按会话粒度整体读写,不进 SpacetimeDB、不进画布工程快照 payload。删除为软删(deleted = true,OSS 对象保留)。领域校验(标题截取、附件上限、归属 / 软删规则)沉在 module-editor-agent
  • 过程:只通过 create_editor_agent_conversation_and_returnlist_editor_agent_conversations_and_returnget_editor_agent_conversation_and_returntouch_editor_agent_conversation_and_returndelete_editor_agent_conversation_and_return procedure 操作;api-server 不直接绕过 spacetime-client facade 读写表。消息文档当前由 api-server 做单会话串行锁和 2 MiB 上限保护,避免同一 SSE 回合并发重写同一个 OSS JSON 文档。
  • 索引:by_editor_agent_conversation_project_id 用于会话列表;by_editor_agent_conversation_owner_user_id 用于账号级归属校验。

editor_project

  • Rust 结构体:EditorProject
  • 源码:server-rs/crates/spacetime-module/src/editor_project_storage.rs
  • 说明:图片画布工程真相表,保存 owner、标题和工程时间戳;viewport 与图层布局已拆到 editor_canvas,旧 layout columns 暂作为兼容列保留,不再作为权威数据源。只通过 /api/editor/projects*/api/editor/projects/{projectId}/agent-conversations/api/editor/agent-conversations/{conversationId}* BFF 和 spacetime-client facade 读写;项目页列表、重命名和删除也使用该能力,删除工程时级联清理默认画布和资源元数据。
  • 索引:by_editor_project_owner_user_id 用于读取当前用户最近编辑工程和项目页工程列表。

editor_canvas

  • Rust 结构体:EditorCanvas
  • 源码:server-rs/crates/spacetime-module/src/editor_project_storage.rs
  • 说明:图片画布数据表,归属于 editor_project,保存默认画布的 viewport typed columns、图层布局 JSON、owner 和时间戳;当前编辑器读取 / 保存 project 的默认 canvas,后续支持一个工程多个 canvas。
  • 索引:by_editor_canvas_project_idby_editor_canvas_owner_user_id

editor_project_resource

  • Rust 结构体:EditorProjectResource
  • 源码:server-rs/crates/spacetime-module/src/editor_project_storage.rs
  • 说明:图片画布工程资源元数据表,保存已经放入某个 project 画布的上传 / 生成图片资源快照、OSS 引用、尺寸、来源类型、prompt、provider、task、源资源关系、asset_kindgeneration_inputs_json 和历史 public_showcase_enabledasset_kind 标记角色、图标、UI 设计图、视频、音频等素材类别;generation_inputs_json 保存用户可见生成输入快照,供图片信息页刷新后恢复。public_showcase_enabled 只保留旧接口兼容,不再作为 /creation陶泥儿精选 事实源;精选公开改由账号级生成素材提交 editor_showcase_asset 审核决定。图片 / 图标 / UI 提取等生成 BFF 在请求携带 project_id 时负责创建该表记录并把 resource 快照返回前端;前端只保存布局引用,不能把同一生成结果再次作为正式业务真相写入。项目封面快照也落在该表,使用 asset_kind = project-cover-snapshotsource_type = uploaded 和私有 OSS / asset object 引用,代表画布当前视口栅格化后的静态封面;项目列表和创作主页最近项目只读取最新封面快照资源,不在列表页根据 editor_canvas.layers_json 临时拼画布。从账号级素材库把同一生成素材拖回同一项目画布时,后端优先复用同项目内同源同媒体资源,避免每个图层实例都插入新的资源行。账号级素材删除不级联删除该表,避免历史画布丢图。editor_canvas.layers_json 只保存图层几何、层级、分组、资源引用和生成器对象;新写入不再把素材生成输入快照作为图层布局真相保存,旧布局字段只作为兼容兜底读取。
  • 索引:by_editor_project_resource_project_idby_editor_project_resource_owner_user_id

editor_asset_folder

  • Rust 结构体:EditorAssetFolder
  • 源码:server-rs/crates/spacetime-module/src/editor_project_storage.rs
  • 说明:图片画布账号级素材文件夹表,归属于用户账号而不是 project;首次读取素材库时自动创建系统默认“项目素材”文件夹。文件夹支持重命名、折叠和删除,系统默认文件夹不能删除。
  • 索引:by_editor_asset_folder_owner_user_id

editor_asset

  • Rust 结构体:EditorAsset
  • 源码:server-rs/crates/spacetime-module/src/editor_project_storage.rs
  • 说明:图片画布账号级素材表,保存用户上传 / 生成素材的名称、文件夹、图片读取地址、可选封面 thumbnail_src、OSS 引用、尺寸、来源类型、prompt、provider、task、asset_kindgeneration_inputs_json、可选 source_resource_idgeneration_cost_mud_points。素材在同一账号的所有项目中可见;图片 / 图标 / UI 提取等生成 BFF 在请求携带 asset_folder_id 时负责创建账号级生成素材并返回 asset 快照,若同次生成也创建了 editor_project_resource,则把该 resource_id 写入 source_resource_id。生成视频会抽取首帧封面写入 thumbnail_src,素材库和再次放入画布时用它作为 video poster。素材库快照通过 asset_id 回查对应 editor_showcase_asset,供左侧素材菜单展示 pending / approved / rejected 审核状态;公开事实不落在账号素材表,素材库只发起提交审核。素材放入画布时复制为 editor_project_resource 并由图层引用 resourceId,画布从 resource / asset 级元数据恢复素材类别和用户可见生成输入快照。
  • 索引:by_editor_asset_owner_user_idby_editor_asset_folder_id

editor_showcase_asset

  • Rust 结构体:EditorShowcaseAsset
  • 源码:server-rs/crates/spacetime-module/src/editor_project_storage.rs
  • 说明:陶泥儿精选 的独立审核与公开快照表。用户从账号级生成素材提交后,后端把素材媒体、提示词、生成输入、素材类型和 generation_cost_mud_points 快照到该表,初始 review_status = pendingdisplay_enabled = falseshowcase_category = null。后台审核通过后写入 approved,但仍保持不展示;运营可在后台按前台具体 Tab 手动设置 showcase_categorycharactersuimusicmarketing)并开启展示。未设置分类的素材不归入具体 Tab,但展示开启后仍进入前台“全部”。审核通过时生成确定性返还流水 editor-showcase-refund:{showcase_id},BFF 按 50% 生成成本返还泥点后回写 refund_completed_at;拒绝后写入 rejected。公开精选 GET /api/editor/showcase/resources 读取 review_status = approveddisplay_enabled = true 且媒体非空的记录,按通过时间 / showcase_id 倒序 cursor 分页。素材删除时,待审核记录标记 asset_deleted_while_pending,已拒绝记录删除,已通过记录保留快照继续展示。
  • 索引:by_editor_showcase_asset_owner_user_idby_editor_showcase_asset_review_status

editor_showcase_asset_like

  • Rust 结构体:EditorShowcaseAssetLike
  • 源码:server-rs/crates/spacetime-module/src/editor_project_storage.rs
  • 说明:陶泥儿精选 点赞去重表,主键由 showcase_id:user_id 组成。点赞 / 取消点赞通过登录接口更新该表,并同步回写 editor_showcase_asset.like_count;未通过审核或未展示的精选素材不能点赞。
  • 索引:by_editor_showcase_asset_like_showcase_idby_editor_showcase_asset_like_user_id

editor_showcase_campaign_config

  • Rust 结构体:EditorShowcaseCampaignConfig
  • 源码:server-rs/crates/spacetime-module/src/editor_project_storage.rs
  • 说明:陶泥儿精选 首位固定活动卡配置表,当前使用固定 config_id = global。后台可配置启用状态、标题、图片地址、提示词、作者和成本文案;上传按钮通过后台受控上传票据把图片写入 OSS,保存时同时落 image_src、内部图片 OSS image_object_keyimage_widthimage_height。公开精选接口只在启用时返回该配置,前端优先用 image_object_key 走签名读地址展示,并按记录的图片宽高决定活动卡比例。
  • 索引:主键 config_id

editor_generation_pricing_config

  • Rust 结构体:EditorGenerationPricingConfig
  • 源码:server-rs/crates/spacetime-module/src/editor_project_storage.rs
  • 说明:图片画布生成类模型定价全局配置表,当前使用固定 config_id = globalmodels: Vec<EditorGenerationModelPricing> 强类型保存模型、定价单位、单价或档位列表,procedure / spacetime-client 边界不传递不透明 JSON;模块事务会再次校验完整正式模型矩阵、必需档位、单位、正价格和重复键。writer_identity 只保留在 private 表内,记录首次初始化的真实 ctx.sender(),不进入 procedure 返回快照;后续 upsert_editor_generation_pricing_config_and_return 只允许同一 identity 或已授权迁移操作员修改价格,但始终保留原 writer。表为空时只有 HTTP 角色通过 initialize_editor_generation_pricing_config_if_missing_and_return 在单事务内仅缺失时种子入库;worker / controller 启动只调用受鉴权的 queue-stats procedure 做只读身份预检。后台保存请求携带 AppConfig 中的受保护 bootstrap secret,以便配置行意外缺失时原子恢复;表存在时 bootstrap secret 不能接管 writer。原始 bootstrap secret 固定为 64 位十六进制;WASM 只嵌入其 SHA-256procedure 对入参原文重新计算摘要并做常量时间比较。runtime queue / 钱包 procedure 只接受精确 writer,当前生产 API / worker / controller 因此继承同一 runtime token;迁移操作员不自动获得在线运行权限,且 operator / writer 身份必须互斥。SpacetimeDB 不可达时仅使用默认 JSON 或旧 override 缓存兜底。
  • 索引:主键 config_id

editor_generation_runtime_identity_rotation

  • Rust 结构体:EditorGenerationRuntimeIdentityRotation
  • 源码:server-rs/crates/spacetime-module/src/editor_project_storage.rs
  • 说明:模型生成运行时服务 identity 显式轮换审计表。只有已授权迁移操作员可调用 rotate_editor_generation_runtime_service_identity_and_return,且新 writer 不能等于当前 writer,也不能是任一已登记 migration operator。每次记录旧 writer、新 writer、迁移操作员 identity、操作人、原因和服务端时间;轮换只修改 writer,不覆盖已有模型价格。生产人工入口为 scripts/deploy/production-runtime-writer-identity-rotate.mjsCLI 会核对当前登录 operator identity 并要求双录新 identity。
  • 索引:自增主键 rotation_id

inventory_slot

  • Rust 结构体:InventorySlot
  • 源码:server-rs/crates/spacetime-module/src/gameplay.rs

jump_hop_agent_session

  • Rust 结构体:JumpHopAgentSessionRow
  • 源码:server-rs/crates/spacetime-module/src/jump_hop/tables.rs

jump_hop_event

  • Rust 结构体:JumpHopEventRow
  • 源码:server-rs/crates/spacetime-module/src/jump_hop/tables.rs

jump_hop_leaderboard_entry

  • Rust 结构体:JumpHopLeaderboardEntryRow
  • 源码:server-rs/crates/spacetime-module/src/jump_hop/tables.rs
  • 说明:跳一跳作品维度排行榜 read model,每个 profile_id + player_id 只保留 1 条最佳记录;排序口径为成功跳跃次数降序、游戏时长升序、更新时间升序,草稿试玩不作为公开排行榜语义。
  • 展示契约:player_id 只作为后端去重和 viewerBest 匹配身份键,不得直接进入 HTTP/UI 展示字段;/api/runtime/jump-hop/works/{profile_id}/leaderboard 必须补齐 displayName,已登录玩家读取账号显示名,匿名游客展示“游客玩家”,失效账号展示“失效玩家”。

jump_hop_runtime_run

  • Rust 结构体:JumpHopRuntimeRunRow
  • 源码:server-rs/crates/spacetime-module/src/jump_hop/tables.rs
  • 说明:运行记录持久化 runtime_mode,取值为 draft / published;草稿试玩只允许作品所有者启动,不累计公开游玩次数,也不写入公开排行榜。

jump_hop_work_profile

  • Rust 结构体:JumpHopWorkProfileRow
  • 源码:server-rs/crates/spacetime-module/src/jump_hop/tables.rs
  • 说明:作品投影持久化独立 theme_text,用于生成主题和公开卡片主题展示;历史行为空时按 work_title 兜底。back_button_asset_json 保存 image2 单独生成并去绿后的 1:1 左上角返回按钮资产快照;旧迁移数据按 None 兼容,运行态缺失该字段时使用同尺寸 CSS 主题按钮兜底。
  • Rust viewjump_hop_gallery_card_view
  • 返回类型:Vec<JumpHopGalleryCardViewRow>
  • 源码:server-rs/crates/spacetime-module/src/jump_hop.rs
  • 说明:跳一跳公开列表 source 投影,只暴露 publication_status = Published 的作品卡片字段;统一公开列表主路径通过 public_work_gallery_entry 消费该 view 并映射成跨玩法契约。个人作品列表、详情、发布和运行态仍按 procedure 路径处理。
  • Rust viewjump_hop_gallery_view
  • 返回类型:Vec<JumpHopGalleryViewRow>
  • 源码:server-rs/crates/spacetime-module/src/jump_hop.rs
  • 说明:跳一跳公开详情兼容投影,包含作品、路径和素材字段;统一公开详情主路径通过 public_work_detail_entry 消费该 view,只保留平台详情页展示摘要。
  • 字段变更:visible 控制是否进入公开列表 / 详情,新作品默认 false;旧迁移数据由 migration.rs 按历史公开默认补 true

wooden_fish_agent_session

  • Rust 结构体:WoodenFishAgentSessionRow
  • 源码:server-rs/crates/spacetime-module/src/wooden_fish/tables.rs

wooden_fish_event

  • Rust 结构体:WoodenFishEventRow
  • 源码:server-rs/crates/spacetime-module/src/wooden_fish/tables.rs

wooden_fish_runtime_run

  • Rust 结构体:WoodenFishRuntimeRunRow
  • 源码:server-rs/crates/spacetime-module/src/wooden_fish/tables.rs

wooden_fish_work_profile

  • Rust 结构体:WoodenFishWorkProfileRow
  • 源码:server-rs/crates/spacetime-module/src/wooden_fish/tables.rs
  • 说明:敲木鱼作品 profile 真相,包含敲击物图案、背景环境图、主题返回按钮图、敲击音效、飘字配置、发布状态和公开计数;background_asset_json 保存 image2 生成的 9:16 背景环境图资产快照,back_button_asset_json 保存 image2 生成并去绿后的 1:1 返回按钮图资产快照,旧迁移数据按 None 兼容。
  • Rust viewwooden_fish_gallery_card_view
  • 返回类型:Vec<WoodenFishGalleryCardViewRow>
  • 源码:server-rs/crates/spacetime-module/src/wooden_fish.rs
  • 说明:敲木鱼公开列表 source 投影,只暴露 publication_status = published 的作品卡片字段;统一公开列表主路径通过 public_work_gallery_entry 消费该 view 并映射成跨玩法契约。个人作品列表、详情、发布和运行态仍按 procedure 路径处理。
  • Rust viewwooden_fish_gallery_view
  • 返回类型:Vec<WoodenFishGalleryViewRow>
  • 源码:server-rs/crates/spacetime-module/src/wooden_fish.rs
  • 说明:敲木鱼公开详情兼容投影,包含敲击物图案、背景环境图、主题返回按钮图、敲击音效和飘字配置;统一公开详情主路径通过 public_work_detail_entry 消费该 view,只保留平台详情页展示摘要。
  • 字段变更:visible 控制是否进入公开列表 / 详情,新作品默认 false;旧迁移数据由 migration.rs 按历史公开默认补 true

match3d_agent_message

  • Rust 结构体:Match3DAgentMessageRow
  • 源码:server-rs/crates/spacetime-module/src/match3d/tables.rs

match3d_agent_session

  • Rust 结构体:Match3DAgentSessionRow
  • 源码:server-rs/crates/spacetime-module/src/match3d/tables.rs

match3d_runtime_run

  • Rust 结构体:Match3DRuntimeRunRow
  • 源码:server-rs/crates/spacetime-module/src/match3d/tables.rs

match_3_d_work_profile

  • Rust 结构体:Match3DWorkProfileRow
  • Rust accessormatch_3_d_work_profile
  • 源码:server-rs/crates/spacetime-module/src/match3d/tables.rs
  • 兼容说明:dev 现有 SpacetimeDB 元数据中的真实表名 / 索引名为 match_3_d_work_profilematch_3_d_work_profile_*_idx_btree。module 内部 accessor 必须与该 canonical name 对齐,避免 Rust SDK 在 index_id_from_name 初始化二级索引时查找 match3d_work_profile_* 并触发 No such index panic。migration.rs 仍兼容旧迁移包中的 match3d_work_profile 表名补默认字段。
  • Rust viewmatch3d_gallery_view
  • 返回类型:Vec<Match3DGalleryViewRow>
  • 源码:server-rs/crates/spacetime-module/src/match3d.rs
  • 说明:抓大鹅公开 source 投影,只暴露 publication_status = published 的作品卡片字段;统一公开列表 / 详情主路径通过 public_work_gallery_entry / public_work_detail_entry 消费该 view 并映射成跨玩法契约。个人作品列表、详情、发布、点赞、游玩记录和 Remix 仍按原有 procedure / reducer 路径处理。
  • 字段变更:visible 控制是否进入公开列表 / 详情,新作品默认 false;旧迁移数据由 migration.rs 按历史公开默认补 true

npc_state

  • Rust 结构体:NpcState
  • 源码:server-rs/crates/spacetime-module/src/gameplay.rs

player_progression

  • Rust 结构体:PlayerProgression
  • 源码:server-rs/crates/spacetime-module/src/gameplay.rs

profile_dashboard_state

  • Rust 结构体:ProfileDashboardState
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs

profile_daily_free_points

  • Rust 结构体:ProfileDailyFreePoints
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs
  • 作用:每日免费泥点事实源。day_key 使用北京时间业务日,基础发放量固定为 20remaining_points 保存当日剩余额度;跨业务日退款的每日免费消费部分会叠加到退款当日,使 granted_pointsremaining_points 可暂时超过 20,下一业务日首次触达时旧余额与叠加量一并失效并重置为 20

profile_feedback_submission

  • Rust 结构体:ProfileFeedbackSubmission
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs

profile_invite_code

  • Rust 结构体:ProfileInviteCode
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs

profile_code_operation

  • Rust 结构体:ProfileCodeOperation
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs
  • 作用:后台兑换码 / 邀请码的持久操作记录,记录新增、更新、停用的码值、操作人和操作时间。

profile_membership

  • Rust 结构体:ProfileMembership
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs
  • 作用:会员有效期和当前周期限时泥点事实源。started_at/expires_at 表示会员有效期,cycle_started_at/cycle_resets_at/cycle_period_days 表示当前周期,cycle_granted_points/cycle_remaining_points 表示当前周期已发放和剩余限时泥点。

profile_recharge_product_config

  • Rust 结构体:ProfileRechargeProductConfig
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs
  • 作用:泥点和会员充值商品配置真相源,供充值中心展示、下单校验、支付确认和后台“充值商品”页维护;当前公开充值中心只展示四档泥点商品,会员商品配置留存但不公开购买或升级入口。
  • 字段补充:会员商品追加 membership_period_pointsmembership_period_daysmembership_queue_limitmembership_discount_bps;泥点商品这些字段必须为 0

profile_played_world

  • Rust 结构体:ProfilePlayedWorld
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs

profile_recharge_order

  • Rust 结构体:ProfileRechargeOrder
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs
  • 作用:账户充值订单事实源。status 包含 pendingpaidfailedclosedrefundedexpired;过期补偿字段 expired_atexpiration_checked_atexpiration_provider_stateexpiration_last_error 用于记录本地过期和微信查单结果。

profile_recharge_order_expiration_schedule

  • Rust 结构体:ProfileRechargeOrderExpirationSchedule
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs
  • 作用:旧普通微信充值订单到期查单调度表,保留 schema 兼容但当前不再写入;当前过期路径改由原生 scheduled 表 profile_recharge_order_expiration_timer 触发。

profile_recharge_order_expiration_timer

  • Rust 结构体:ProfileRechargeOrderExpirationTimer
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs
  • 作用:充值订单原生 scheduled 表,scheduled_at 到点触发 expire_profile_recharge_order_timerorder_id 唯一,支付成功、本地关闭或 scheduled reducer 执行后删除对应行。

profile_redeem_code

  • Rust 结构体:ProfileRedeemCode
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs

profile_redeem_code_usage

  • Rust 结构体:ProfileRedeemCodeUsage
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs

profile_referral_relation

  • Rust 结构体:ProfileReferralRelation
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs

profile_save_archive

  • Rust 结构体:ProfileSaveArchive
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs

profile_task_config

  • Rust 结构体:ProfileTaskConfig
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs

profile_task_progress

  • Rust 结构体:ProfileTaskProgress
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs

profile_task_reward_claim

  • Rust 结构体:ProfileTaskRewardClaim
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs

profile_wallet_ledger

  • Rust 结构体:ProfileWalletLedger
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs
  • 说明:账号钱包流水表。created_at 表示钱包事务实际结算时间,列表先按当前余额反向校验 balance_after - amount_delta 的结算链,再以该时间倒序兜底,避免支付回调或退款重放延迟时出现余额顺序倒置;支付平台确认时间继续保存在充值订单 paid_atmetadata_json 为可选 JSON 对象字符串,旧行缺失时读取层按 {} 归一;外部生成扣费 / 退款写入 externalGenerationJobId,使退款记录可以追溯到对应 external_generation_job

asset_operation_wallet_settlement

  • Rust 结构体:AssetOperationWalletSettlement
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs
  • 说明:资产操作 consume/refund 配对结算事实表,主键为 consume ledger ID,并保存配对 refund ledger、用户、金额和结算时间。退款先到且 consume 尚不可见时,该表作为持久化取消 intent;迟到 consume 必须检测该行并拒绝扣费,避免 worker 崩溃重领期间双扣。
  • 索引:主键 consume_ledger_id

profile_wallet_config

  • Rust 结构体:ProfileWalletConfig
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs
  • 作用:账号钱包全局配置真相源,当前维护新账号注册初始泥点数;表为空时业务回退 100 泥点。

public_work_like

  • Rust 结构体:PublicWorkLike
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs

public_work_play_daily_stat

  • Rust 结构体:PublicWorkPlayDailyStat
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs

puzzle_agent_message

  • Rust 结构体:PuzzleAgentMessageRow
  • 源码:server-rs/crates/spacetime-module/src/puzzle.rs

puzzle_agent_session

  • Rust 结构体:PuzzleAgentSessionRow
  • 源码:server-rs/crates/spacetime-module/src/puzzle.rs

puzzle_background_compile_task

  • Rust 结构体:PuzzleBackgroundCompileTaskRow
  • 源码:server-rs/crates/spacetime-module/src/puzzle.rs
  • 说明:拼图首图后台生成的跨 api-server 实例互斥 claim 表,只保存活动任务租约,不表达最终生成结果;task_id 为主键,claim_id 用于释放时防止误删新租约,租约超时时间为 30 分钟。

puzzle_event

  • Rust 结构体:PuzzleEvent
  • 源码:server-rs/crates/spacetime-module/src/puzzle.rs

puzzle_leaderboard_entry

  • Rust 结构体:PuzzleLeaderboardEntryRow
  • 源码:server-rs/crates/spacetime-module/src/puzzle.rs

puzzle_runtime_run

  • Rust 结构体:PuzzleRuntimeRunRow
  • 源码:server-rs/crates/spacetime-module/src/puzzle.rs

puzzle_work_profile

  • Rust 结构体:PuzzleWorkProfileRow
  • 源码:server-rs/crates/spacetime-module/src/puzzle.rs
  • 说明:拼图作品 profile 表,保存草稿 / 已发布作品的标题、作者、关卡、封面、发布状态、可见性、基础游玩数、点赞数、改造数和积分激励领取状态。
  • 字段变更:visible 控制是否进入公开列表 / 详情、通关后的推荐下一作品候选、公开点赞 / Remix 和正式公开 runtime;新作品默认 false,旧迁移数据按历史公开默认补 true。后台隐藏后作品可保留 publication_status = Published,但公开消费路径必须按 Published + visible=true 判断。

puzzle_clear_agent_session

  • Rust 结构体:PuzzleClearAgentSessionRow
  • 源码:server-rs/crates/spacetime-module/src/puzzle_clear/tables.rs
  • 说明:拼消消创作会话表,保存轻表单草稿、生成状态、已发布 profile 关联和更新时间;只由拼消消 procedure 读写。

puzzle_clear_work_profile

  • Rust 结构体:PuzzleClearWorkProfileRow
  • 源码:server-rs/crates/spacetime-module/src/puzzle_clear/tables.rs
  • 说明:拼消消作品 profile 表,保存中央底图资产、4 张素材工作表切片后合成的最终 atlas、35 个复合图案组、95 个 1x1 卡牌切片、卡背占位图、发布状态、可见性和基础 play count;公开列表 / 详情只通过 read model 消费,不让前端直接订阅源表。
  • 字段变更:visible 控制是否进入公开列表 / 详情,新作品默认 false;旧迁移数据由 migration.rs 按历史公开默认补 true

puzzle_clear_runtime_run

  • Rust 结构体:PuzzleClearRuntimeRunRow
  • 源码:server-rs/crates/spacetime-module/src/puzzle_clear/tables.rs
  • 说明:拼消消正式 runtime run 表,保存当前关卡、已消除次数、棋盘 snapshot、开始 / 完成时间和 run 状态;正式胜负、重试、完成、超时和交换结果以后端 procedure 裁决为准。

puzzle_clear_event

  • Rust 结构体:PuzzleClearEventRow
  • 源码:server-rs/crates/spacetime-module/src/puzzle_clear/tables.rs
  • 说明:拼消消基础 runtime 事件表,记录 published run 的开局、关卡完成、全局完成、失败、超时和消除统计来源;首版不做排行榜。
  • Rust viewpuzzle_clear_gallery_view
  • 返回类型:Vec<PuzzleClearGalleryViewRow>
  • 源码:server-rs/crates/spacetime-module/src/puzzle_clear.rs
  • 说明:拼消消公开详情 source 投影,只暴露 publication_status = publishedvisible = true 的作品,包含 atlas、底图、图案组和卡牌切片等详情级字段;统一公开详情主路径通过 public_work_detail_entry 消费该 view,只保留平台详情页展示摘要。
  • Rust viewpuzzle_clear_gallery_card_view
  • 返回类型:Vec<PuzzleClearGalleryCardViewRow>
  • 源码:server-rs/crates/spacetime-module/src/puzzle_clear.rs
  • 说明:拼消消公开列表 source 投影,只暴露平台卡片需要的公开字段;统一公开列表主路径通过 public_work_gallery_entry 消费该 view/api/runtime/puzzle-clear/gallery 保留玩法专属 HTTP shape。
  • Rust viewpuzzle_gallery_view
  • 返回类型:Vec<PuzzleWorkProfile>
  • 源码:server-rs/crates/spacetime-module/src/puzzle.rs
  • 说明:拼图广场公开详情 source / 兼容投影,只暴露 publication_status = Publishedvisible = true 的作品,但返回完整 PuzzleWorkProfile,包含 levels / anchor_pack 等详情级字段;统一公开详情主路径通过 public_work_detail_entry 消费该 view,只保留平台详情页展示摘要。
  • Rust viewpuzzle_gallery_card_view
  • 返回类型:Vec<PuzzleGalleryCardViewRow>
  • 源码:server-rs/crates/spacetime-module/src/puzzle.rs
  • 说明:拼图公开列表 source 投影,只暴露 publication_status = Publishedvisible = true 的公开字段,不携带 levels / anchor_pack 等详情级载荷;统一公开列表主路径通过 public_work_gallery_entry 消费该 view/api/runtime/puzzle/gallery 保留旧 HTTP shape,并从统一 public cache 映射回 PuzzleGalleryResponse

拼图公开列表 HTTP 窗口缓存

  • 接口:GET /api/runtime/puzzle/gallery
  • 响应契约:保留 items 字段兼容旧前端;当前 items 只返回前 10 个完整卡片,新增 previewRefs 返回后 10 个 workId/profileId 引用,并返回 hasMorenextCursortotalCount
  • 缓存策略:api-serverPuzzleGalleryCache 中缓存最终 PuzzleGalleryResponse 的预序列化 data JSON。缓存 miss / 过期时单飞重建,避免并发请求重复排序、映射、DTO 深拷贝和 serde_json::Value 树构造;开启响应 envelope 时只按请求拼接轻量 meta,缓存短 TTL 刷新 recentPlayCount7d,后台 cleanup task 周期清理超过最大空闲窗口的旧响应。OTLP 通过 genarrative.puzzle_gallery.cache.*genarrative.spacetime.read.*genarrative.http.server.response_bodies.in_flightgenarrative.http.server.request_permits.available 区分缓存重建、SpacetimeDB 本地订阅读、响应 body 生命周期和 HTTP 背压状态。
  • 详情路径:公开详情、点赞、游玩记录和 Remix 仍按原有 procedure / reducer 路径处理;前端拿到 previewRefs 后如果需要展开更多内容,应优先使用后续列表窗口能力或详情 cache,不要把自动详情预取变成新的 procedure 热点。

api-server 长期订阅读模型

spacetime-client 建立每个池连接时会等待下列订阅初始同步:

  • SELECT * FROM public_work_gallery_entry
  • SELECT * FROM public_work_detail_entry
  • SELECT * FROM bark_battle_gallery_view
  • SELECT * FROM puzzle_gallery_card_view
  • SELECT * FROM puzzle_clear_gallery_card_view
  • SELECT * FROM jump_hop_gallery_card_view
  • SELECT * FROM wooden_fish_gallery_card_view
  • SELECT * FROM custom_world_gallery_entry
  • SELECT * FROM match_3_d_gallery_view
  • SELECT * FROM square_hole_gallery_view
  • SELECT * FROM visual_novel_gallery_view
  • SELECT * FROM big_fish_gallery_view
  • SELECT * FROM public_work_play_daily_stat WHERE source_type = 'puzzle'
  • SELECT * FROM public_work_play_daily_stat WHERE source_type = 'jump-hop'
  • SELECT * FROM public_work_play_daily_stat WHERE source_type = 'wooden-fish'
  • SELECT * FROM public_work_play_daily_stat WHERE source_type = 'custom-world'
  • SELECT * FROM public_work_play_daily_stat WHERE source_type = 'match3d'
  • SELECT * FROM public_work_play_daily_stat WHERE source_type = 'square-hole'
  • SELECT * FROM public_work_play_daily_stat WHERE source_type = 'visual-novel'
  • SELECT * FROM public_work_play_daily_stat WHERE source_type = 'big-fish'
  • SELECT * FROM public_work_play_daily_stat WHERE source_type = 'bark-battle'
  • SELECT * FROM public_work_play_daily_stat WHERE source_type = 'puzzle-clear'
  • SELECT * FROM creation_entry_config
  • SELECT * FROM creation_entry_type_config
  • SELECT * FROM asset_object

跨玩法公开作品列表 / 详情主读模型是 public_work_gallery_entrypublic_work_detail_entry。拼图、自定义世界等旧玩法公开列表 HTTP 路由保留原响应 shape,由 BFF mapper 从统一 public cache 映射回当前 DTO;旧 *_gallery_card_view / *_gallery_view / custom_world_gallery_entry 继续作为 source view 和兼容缓存。各玩法的个人作品列表、详情、发布、点赞、游玩记录、Remix 和其它需要鉴权或写入副作用的路径继续走 procedure / reducer;不要为了公开列表性能把这些 owner-specific 或 mutation 语义混进 public view。

GET /api/creation-entry/config、入口熔断和公开作品互动熔断优先从订阅 cache 读取创作入口配置;cache 缺失时使用最近一次成功读取的内存快照,再兜底调用 get_creation_entry_config procedure 完成空库种子或旧库兼容。 入口配置快照包含 start card、类型弹窗、公告位兼容字段、入口类型列表和 publicWorkInteractions 作品互动矩阵;入口类型列表新增 category_idcategory_labelcategory_sort_order 后,后台 upsert、shared-contractsmodule-runtimespacetime-client binding 必须同步,旧迁移 JSON 通过 migration.rs 默认值兼容。作品互动矩阵是全局公开作品详情能力配置,不属于单个 creation_entry_type_config;后台通过 /admin/api/creation-entry/config/interactions 保存,前端据此隐藏或拦截已接入的点赞 / Remix 入口,api-server 同时对已接入后端动作执行 public_work_interaction_disabled 熔断。

RPG 创作入口的配置 ID 是 rpg,当前 visible=trueopen=true;历史 custom-world 路由仍是 RPG 的工程域与运行态源类型。入口熔断把 /api/runtime/custom-world*/api/story/*/api/runtime/chat/* 统一映射到 rpg,不要新增平行 airp 路由或用 airp 接管当前文字冒险链路。

结构化创作和 RPG 的 LLM JSON 链路默认不启用 Responses web_search;只有在明确需要联网增强时,才通过 GENARRATIVE_RPG_LLM_WEB_SEARCH_ENABLEDGENARRATIVE_CREATION_AGENT_LLM_WEB_SEARCH_ENABLED 显式打开。否则未开通工具的上游会先吐自然语言再返回 ToolNotOpen,这类失败要按上游工具不可用处理,不要误判成模型返回结果解析失败。

统一公开作品 BFF 路由是 GET /api/public-worksGET /api/public-works/{publicWorkCode},响应契约由 shared-contracts::public_workpackages/shared/src/contracts/publicWork.ts 共同维护。前端首期仍走 BFF HTTP,不直接订阅 SpacetimeDB;后续若允许浏览器直连订阅,也只能订阅 public_work_gallery_entry / public_work_detail_entry 这类稳定公开 read model,不能订阅 puzzle_work_profilecustom_world_profile 等源表后自行拼装列表。设计细节见 docs/technical/【后端架构】统一公开作品ReadModel设计-2026-05-26.md

  • 字段变更:visible 控制是否进入公开列表 / 详情,新作品默认 false;旧迁移数据由 migration.rs 按历史公开默认补 true

quest_log

  • Rust 结构体:QuestLog
  • 源码:server-rs/crates/spacetime-module/src/gameplay.rs

quest_record

  • Rust 结构体:QuestRecord
  • 源码:server-rs/crates/spacetime-module/src/gameplay.rs

refresh_session

  • Rust 结构体:RefreshSession
  • 源码:server-rs/crates/spacetime-module/src/auth/tables.rs

runtime_setting

  • Rust 结构体:RuntimeSetting
  • 源码:server-rs/crates/spacetime-module/src/runtime/settings.rs

runtime_snapshot

  • Rust 结构体:RuntimeSnapshotRow
  • 源码:server-rs/crates/spacetime-module/src/runtime/snapshots.rs

square_hole_agent_message

  • Rust 结构体:SquareHoleAgentMessageRow
  • 源码:server-rs/crates/spacetime-module/src/square_hole/tables.rs

square_hole_agent_session

  • Rust 结构体:SquareHoleAgentSessionRow
  • 源码:server-rs/crates/spacetime-module/src/square_hole/tables.rs

square_hole_runtime_run

  • Rust 结构体:SquareHoleRuntimeRunRow
  • 源码:server-rs/crates/spacetime-module/src/square_hole/tables.rs

square_hole_work_profile

  • Rust 结构体:SquareHoleWorkProfileRow
  • 源码:server-rs/crates/spacetime-module/src/square_hole/tables.rs
  • Rust viewsquare_hole_gallery_view
  • 返回类型:Vec<SquareHoleGalleryViewRow>
  • 源码:server-rs/crates/spacetime-module/src/square_hole.rs
  • 说明:方洞挑战公开 source 投影,只暴露 publication_status = published 的作品卡片字段;统一公开列表 / 详情主路径通过 public_work_gallery_entry / public_work_detail_entry 消费该 view 并映射成跨玩法契约。个人作品列表、详情、发布、点赞、游玩记录和 Remix 仍按原有 procedure / reducer 路径处理。
  • 字段变更:visible 控制是否进入公开列表 / 详情,新作品默认 false;旧迁移数据由 migration.rs 按历史公开默认补 true

story_event

  • Rust 结构体:StoryEvent
  • 源码:server-rs/crates/spacetime-module/src/gameplay.rs

story_session

  • Rust 结构体:StorySession
  • 源码:server-rs/crates/spacetime-module/src/gameplay.rs

tracking_daily_stat

  • Rust 结构体:TrackingDailyStat
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs
  • 写入:由单条或批量 tracking procedure 在同一事务中随 tracking_event 更新,作为运营查询和个人任务进度的聚合投影。

tracking_event

  • Rust 结构体:TrackingEvent
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs
  • 写入:关键业务埋点同步调用单条 procedure;普通 HTTP route tracking 由 api-server 本机 outbox 批量调用 record_tracking_events_and_return。outbox 到达批量阈值时先封存 active 文件并切新 active,后台 worker 异步 flush sealed 文件,HTTP 请求线程不等待 SpacetimeDB。FLUSH_INTERVAL_MS 只负责兜底封存长时间未满批的 active 文件,MAX_BYTES 只做磁盘保护阈值。event_id 必须稳定且全局唯一,批量重试时用唯一索引做幂等跳过。
  • 外部 API 失败:event_key = external_api_call_failure 使用同一张表落库;它是供应商失败审计事实,不新增 SpacetimeDB 表,查询时按 module_key = 'external-api'scope_kind = module AND scope_id = '<provider>' 过滤。

treasure_record

  • Rust 结构体:TreasureRecord
  • 源码:server-rs/crates/spacetime-module/src/gameplay.rs

user_account

  • Rust 结构体:UserAccount
  • 源码:server-rs/crates/spacetime-module/src/auth/tables.rs
  • 职责:账号资料真相源,保存 phone_number_e164display_nameavatar_urlcreated_at、登录状态、密码 hash、token version 和账号标签。module-auth 进程内工作集必须通过 typed projection 与该表同步,不得再经 auth JSON 快照回灌。

user_browse_history

  • Rust 结构体:UserBrowseHistory
  • 源码:server-rs/crates/spacetime-module/src/runtime/browse_history.rs

visual_novel_agent_message

  • Rust 结构体:VisualNovelAgentMessageRow
  • 源码:server-rs/crates/spacetime-module/src/visual_novel.rs

visual_novel_agent_session

  • Rust 结构体:VisualNovelAgentSessionRow
  • 源码:server-rs/crates/spacetime-module/src/visual_novel.rs

visual_novel_runtime_event

  • Rust 结构体:VisualNovelRuntimeEvent
  • 源码:server-rs/crates/spacetime-module/src/visual_novel.rs

visual_novel_runtime_history_entry

  • Rust 结构体:VisualNovelRuntimeHistoryEntryRow
  • 源码:server-rs/crates/spacetime-module/src/visual_novel.rs

visual_novel_runtime_run

  • Rust 结构体:VisualNovelRuntimeRunRow
  • 源码:server-rs/crates/spacetime-module/src/visual_novel.rs

visual_novel_work_profile

  • Rust 结构体:VisualNovelWorkProfileRow
  • 源码:server-rs/crates/spacetime-module/src/visual_novel.rs
  • Rust viewvisual_novel_gallery_view
  • 返回类型:Vec<VisualNovelGalleryViewRow>
  • 源码:server-rs/crates/spacetime-module/src/visual_novel.rs
  • 说明:视觉小说公开 source 投影,只暴露 publication_status = published 的作品卡片字段,不把完整 draft 暴露给公开列表订阅;统一公开列表 / 详情主路径通过 public_work_gallery_entry / public_work_detail_entry 消费该 view 并映射成跨玩法契约。个人历史、详情、运行态和发布仍按原有 procedure / reducer 路径处理。
  • 字段变更:visible 控制是否进入公开列表 / 详情,新作品默认 false;旧迁移数据由 migration.rs 按历史公开默认补 true