Files
Genarrative/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md
T
lhk229 ee2cad377f 新增画布完美像素功能
在图片工具栏接入完美像素交互、占位流程与历史保护

新增严格像素网格检测和登录态同步处理接口

补齐素材类型校验、持久化边界、并发限制及技术文档

修复 Windows rlib 门禁长文件名解析
2026-07-30 11:42:45 +00:00

172 KiB
Raw Blame History

server-rs 与 SpacetimeDB 数据契约

2026-07-18 状态更新:旧创作入口、全部模板业务 API/worker/运行态及 SpacetimeDB 业务逻辑已退役。本文逐玩法路由、流程和 DTO 章节仅作为历史设计记录;相关持久化表仍按原结构作为最小 schema 数据壳编译,当前编译与运行边界以 server-rs/Cargo.tomlserver-rs/crates/api-server/src/app.rsdocs/technical/【架构下线】旧创作模板业务退役方案-2026-07-17.md 为准。

更新时间:2026-07-23

后端主线

当前后端固定为:

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.7.0;本地 spacetime CLI / standalone、生成的 spacetime-client bindings 和容器压测镜像也必须与 server-rs/Cargo.toml 锁定版本对齐,避免 BSATN / procedure result 反序列化错配。2.7.0 官方 CLI / standalone 发行包与容器镜像使用 v2.7.0-hotfix3 资产标签,二进制版本仍为 2.7.0;不得回退使用缺少后续 backing-view 迁移修复的裸 tag 构建。遇到版本不匹配时,不继续沿着业务超时排查,先把 CLI / standalone 直接升级到锁定版本并重启后再重试。2.6.1 还修复了 procedure context 中调用者 Identity / ConnectionId 丢失问题,因此依赖调用者身份的 procedure 不得继续运行在 2.6.0 standalone 上。

当前主要 crate

  • HTTP 与运维入口:api-serverpingora-gatewayserver-manager-panel
  • 现役领域模块:module-aimodule-assetsmodule-authmodule-editor-agentmodule-runtimemodule-runtime 继续承载账号、钱包、公共设置、追踪、功能门禁等现役平台领域能力;该 crate 名称不代表旧玩法 runtime 路由仍在运行。
  • 平台副作用:platform-agent-harnessplatform-editor-agentplatform-authplatform-audioplatform-hyper3dplatform-imageplatform-llmplatform-mattingplatform-ossplatform-speechplatform-wechat。已退役 Creative Agent 的旧 platform-agent 只保留历史源码,不属于现役 workspace 或依赖图。
  • 共享层:shared-contractsshared-kernelshared-logging
  • SpacetimeDBspacetime-clientspacetime-module
  • 测试支撑:tests-support

server-rs/Cargo.tomlexclude 中所列旧玩法纯业务 crate 仅保留源码,不是 workspace member、default member 或现役依赖。

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 对齐,不在页面内重新发明旧接口;packages/shared 还可复用无业务真相的 UI 组件和纯工具,领域规则、后端副作用与正式状态仍留在各自分层。
  7. 微信能力按两层收口:server-rs/crates/platform-wechat 承载现役微信支付 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 组织

spacetime-client 的 Cargo lib.path 指向 src/active.rs,现役 mapper 聚合入口是 src/active/mapper.rs;原 src/mapper.rs 及旧玩法 mapper 源码仅供历史追溯,不参与 crate 编译。

当前 mapper 按现役调用领域拆分为 admin_account.rsadmin_dashboard.rsai.rsassets.rsauth.rseditor_agent.rseditor_project.rsexternal_api_key.rsexternal_generation.rsruntime.rsruntime_profile.rsstory.rs 只承接必要的历史资产记录兼容映射,不恢复旧故事业务 facade。跨领域轻量 helper 和共享 record 放在 common.rs;不得重新引入旧玩法 mutation 或跨层 JSON 兼容结构。

API 路由分组

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

  • 健康检查:GET /healthzGET /readyz
  • 后台管理:/admin/api/*,现役路由包括登录与账号管理、Dashboard / 概览、HTTP debug、埋点、表查询、通用 feature gate、编辑器定价与素材 / 精选管理,以及账号侧兑换码、邀请码、任务、钱包、充值与退款管理;不再挂载旧创作入口配置、旧作品互动或旧玩法运营路由。环境变量管理员固定作为 owner,持久化 member 每次请求按当前 enabledtoken_version 和一级 Tab 权限实时校验;账号管理仅 owner 可访问,未登记权限映射的新后台路由对 member 默认拒绝。完整权限矩阵见 docs/technical/【后台管理】多账号与Tab访问权限方案-2026-07-14.mdDashboard 指标口径见 docs/technical/【后台管理】Dashboard运营看板方案-2026-06-23.md
  • 通用灰度控制面固定为后台 #gray-releaseGET/PUT /admin/api/feature-gates;页面固定目标只登记现役功能,不读取旧 /admin/api/creation-entry/config,也不恢复 creation-entry:* 动态目标。
  • 认证与账号:/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,负责直传、确认、绑定和读取。两个读取入口共用同一授权函数,并通过受 runtime service identity 限制的 procedure 在同一事务快照内按配置 bucket 与精确 key 权威查询 asset_object、计算现役编辑器精选素材派生授权;不得把任意连接的订阅 cache miss 或命中解释为当前授权真相。一旦存在 metadata,即使 key 命中 legacy 前缀,也必须按 PublicRead、当前登录 owner,或同 owner 且已通过、已展示、返还完成的 editor_showcase_asset 精确授权读取;旧创作模板作品不再形成资产读取授权。只有同 bucket / key 的权威查询确认未登记时,才允许显式 legacyPublicPath 命中 platform_oss::LEGACY_PUBLIC_PREFIXES curated 白名单后匿名兼容。已登记资产继续保持 private,公开精选只获得与正式展示快照生命周期一致的精确派生读授权,不得批量改为 PublicRead 或放开 generated-* 前缀。任意未登记 objectKey、跨 owner 和未获授权的匿名私有读取统一返回不存在,read-bytes 不得成为绕过 read-url 授权的同源代理;精选派生授权、PublicRead 和 legacy 兼容读取签发的 URL 统一限制为最长 600 秒,owner / admin 读取保持原有有效期口径。
  • 外部 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/editor/projects*/api/editor/assets*/api/editor/showcase/*/api/editor/*/generations/api/editor/images/*/api/editor/icon-spritesheets/*/api/editor/ui-designs/*,以及编辑器 Agent 会话路由。通用任务与素材支撑另保留 /api/ai/tasks*/api/assets/history/api/assets/character-visual/*/api/assets/character-animation/*/api/assets/character-workflow-cache*/api/assets/hyper3d/*
  • 现役 runtime 前缀公共能力:GET /api/runtime/frontend-config、鉴权的 GET/PUT /api/runtime/settings 以及鉴权的 /api/runtime/external-generation/*。这些分别承载非敏感前端开关、账号级公共设置和外部生成队列观测 / 确认,路径保留 runtime 前缀不代表旧玩法运行态恢复。/api/runtime/frontend-configapi-server 从运行时环境变量下发非敏感 UI 开关;画板右侧 Agent 入口由 GENARRATIVE_ENABLE_IMAGE_EDITOR_AGENT_SIDEBAR 控制,默认关闭。
  • 公共设置数据链:GET/PUT /api/runtime/settings 从 access token 取得 user_id,经 spacetime-client facade 调用 get_runtime_setting_or_default / upsert_runtime_setting_and_return,读写现役 runtime_setting 表的 music_volumeplatform_theme。该表不是历史数据壳,也不得为保留它而恢复任何旧存档、游玩历史或玩法 settings 路由。
  • 后台素材查询:GET /admin/api/editor-assets 通过 admin_list_editor_assets_and_return 后台只读 procedure 读取私有账号级 editor_assetsource_type = 'generated' 的素材,支持 ownerUserIdkeywordcreatedAftercreatedBeforecursorlimitownerUserId 接受内部用户 ID 或精确陶泥号,精确 SY-* keyword 也会在调用 procedure 前经认证服务解析成内部 user_id,未知陶泥号直接返回空列表。带 owner 条件时 procedure 直接走 by_editor_asset_owner_user_id,再按任务分组并以最终产物父项分页;响应中每个父项通过 children 携带可展开的中间产物,子项各占一行而不单独占分页名额。手动重拆图集保留真实 editor-atlas-split-* task_id;来源任务只能通过私有 provenance 由服务端生成账号素材的 source resource / asset object / Object Key 推导,再写入 group_task_id,不能信任素材库兼容推断值或普通资源创建接口可提交的 task_id/asset_kindprocedure 只有当前 editor generation runtime service identity 才能写入归组与完成事实。没有可信来源的现代拆分显式归到自身任务,不进入 legacy 回溯。每个切片同时写入 group_task_expected_asset_count,全部预期 asset ID 成功落库并逐行校验后写入不可逆 cohort 完成事实;read model 只让具有完成事实的一个拆分批次并入原图集父项,用户后来删除单片不会改变初始完成状态,部分失败批次与后续重复拆分按真实任务独立分页,避免残缺批次抢占根任务、单组无限增长或丢素材。历史行兼容沿项目资源链回溯,删除项目时只固化直接引用待删资源且尚未固化的历史行,持久化真实来源任务,不把有界展示 ID 反写覆盖来源。父项返回任务生成器和任务总成本,子项返回阶段生成器和阶段成本。provider 原图 / 角色动作预览承载模型生成成本,抠图、逐帧处理、透明图集和切片成本为 0;中间产物使用所属任务真实 asset_kind,不再新写 editor_green_screen_source,历史旧值只在 read model 中按 Object Key 映射为 charactericon-spritesheetcharacter-animation 并把旧任务成本只读归回 provider 原始产物。游标固定由父任务微秒时间和素材 ID 组成,API 必须同时识别整数微秒、seconds.microsZ 与 RFC3339 时间文本;内部时间无法编码时返回服务错误,不得静默返回 nextCursor = null。接口只用于查询,不提供分类筛选,也不执行精选审核、返还或展示状态修改,不通过后台 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。
  • 后台精选审核:GET /admin/api/editor-showcase/assets 的素材 payload 与素材查询共同返回稳定媒体引用,并透传底层精选快照已有的 thumbnailSrc,供视频封面或独立缩略图使用;两页前端共用同一缩略图、媒体类型、管理员换签和预览弹窗,不得在精选审核中复制一套立即全量换签或把绝对 OSS 私有地址直接交给浏览器的简化实现。 现役路由只以 app.rs::build_router 实际 .merge(...) 的 module 和支付回调为准。modules/ 中仍保留但未被 app.rs 声明 / 合并的 RPG、拼图、Match3D、敲木鱼、方洞、视觉小说、大鱼、跳一跳、汪汪声浪、儿童向、旧公开作品与 play_flow 源码都是历史追溯材料,不得由新的 .merge(...)、handler 转发或兼容 router 重新挂载。/api/creation-entry/config/admin/api/creation-entry/config*、旧 /api/creation/*、旧 /api/runtime/<play>/*、旧存档 / 游玩历史 / 公开作品路由均不是现役 API。新增公共路由仍必须经 app.rs 的统一中间件、鉴权、背压、埋点与可观测边界。

图片画布 Agent 对话

  • /api/editor/projects/{projectId}/agent-conversations 负责当前工程会话列表和新建;/api/editor/agent-conversations/{conversationId} 负责详情读取、终态工具消息懒回填和软删;POST /api/editor/agent-conversations/{conversationId}/messages 负责发送消息并返回普通 JSON EditorAgentMessageResponse,画布 Agent 不提供 /messages/stream SSE 路由。消息请求必须携带最长 128 字符的 clientMessageId;前端对该 POST 显式启用 1 次瞬时 transport 重试,并复用同一个序列化 body、clientMessageIdx-request-id。同一会话在锁内按该键幂等,重复键同内容返回已有回合或从已保存用户消息继续,异内容返回 409。数字 EditorAgentMessage.id 仍只作为工具确认 / 取消的后端消息定位符,不能复用为客户端幂等键。
  • module-editor-agent 只承载纯领域校验:标题派生、附件上限、消息输入规则和会话软删访问规则;不直接依赖 Axum、SpacetimeDB、OSS、LLM 或 Tokio。
  • platform-agent-harness 只承载与具体业务无关的 JSON function-calling 协议、工具 schema 注入、memory、hook、typed tool、轮次保护和待用户确认终止语义;platform-editor-agent 在其上叠加画布专属 LLM profile、system prompt、跨工具路由规则、图片上下文与八类生成工具。公共 harness 不依赖画布 DTO、计费、OSS、Axum 或 external job,也不得复用已退役的旧 platform-agent。harness 失败契约固定为 PromptRunError { error, partial_outputs }partial_outputs 显式携带失败前已产生的助手文本、成功工具和结构化失败工具输出。ToolFailurekindretryablefatal 及工具原始 output 必须对 harness 调用方可见,不得在公共层压成字符串或擅自丢弃。
  • prompt runner 对每个调用通过 AgentMemory::begin_staged 创建行为等价、写入隔离的 StagedAgentMemory 事务:限长、摘要、脱敏或持久化 memory 的 append 语义必须在本轮模型请求前生效,不得统一降级成 VecMemory。成功结束或已发生工具活动时必须显式调用 staged commit(),直接 drop 表示回滚。无工具活动的 completion、hook、解析或 max_turns 失败丢弃 staged transaction;已有成功或失败工具活动时在末尾追加 terminal error closure 后提交。外部 future drop / abort 若发生在工具完成后,必须提交工具结果与取消闭环;若工具仍在执行,则提交“已启动、结果未知”事实与取消闭环,供后续 reconcile,不能假装工具没有发生。
  • 画布 handler 的 18 分钟总 deadline 通过 runtime 提供的 deadline future 下沉到公共 runnercompletion await 可被 deadline 终止;effectful tool 在开始前检查 deadline,开始后不被中途 drop,返回后再携带结果收口为 PromptRunError。禁止用外层 timeout 直接 drop 整个 prompt future 并伪造空 partial_outputs
  • 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 上限、会话内串行锁、读改写、消息与工具结果持久化和 touch 元数据更新时间;该 JSON 不进入 editor_canvas.layers_json,也不作为画布布局真相。LLM 未配置、连接已经断开、请求明确失败、达到最终安全上限或规划不可解析时,必须写入 role=system、正文以 ERROR 开头的消息,并通过 deltaMessages 返回,errorMessage 保持为空;前端隐藏前缀并显示红色错误气泡,面向用户的错误正文使用中文语义,不暴露 completion error 等 framework 内部前缀或原始配置/定价错误;原始诊断只写后端结构化日志。后端仍把该 system 消息注入后续 LLM memory,使 Agent 能读取失败上下文。普通 JSON POST 尚未结束不形成持久化消息;工具失败同样必须形成可回读记录,不能只返回瞬时错误。
  • 画布 Agent 的 gpt-5.4-mini Chat Completions 规划使用 1024 max_tokens。前端在 POST pending 120 秒后显示不入库的耐心等待提示;provider request future 明确返回 connect/timeout/HTTP/transport 错误时立即进入正式失败,尚未返回则继续等待。专用 provider 单 attempt hard timeout 为 8 分钟;请求发起阶段的 timeout、连接失败、4084295xx 读取 GENARRATIVE_LLM_MAX_RETRIES,但画布 Agent 最多重试 1 次,显式配置 0 仍可关闭,专用重试退避最多 60 秒。消息规划生命周期从 handler 入口开始计入 18 分钟总 deadline,进入 agent.prompt(...) 时只使用剩余预算;该 deadline 覆盖会话锁/上下文准备与最多 3 轮规划,并为错误持久化/HTTP 返回预留约 2 分钟,不允许多轮规划绕过前端 20 分钟 timeout。已收到成功响应头后的响应体读取或解析失败直接按明确失败收口,并使用该成功响应所属的真实 attempt 记录错误。重试只包围 LLM 规划请求并发生在任何待确认工具执行之前,因此不会重复提交生成任务或扣费。
  • 对话附件只允许引用当前工程 editor_project_resource 或当前账号 editor_asset 的图片;前端可提交展示用 imageSrc / thumbnailSrc,后端必须按 resourceId / assetId 重新归一、校验 owner / project 和 objectKey,再给 LLM 或生成工具使用。
  • planning prompt 注入的 latestGeneratedImage.imageId 只能映射到 edit-image.object_image_idsource_image_id 不是现役 edit-image schema 字段,prompt、tool args、确认执行和测试中都不得生成或兼容该字段。
  • 画布 Agent 工具复用既有编辑器图片生成 / 修改 / 图标 spritesheet BFF,并继续使用后端模型定价和 execute_billable_asset_operation_with_cost;前端不提交 priceMudPoints
  • api-server 对 PromptRunError 的持久化顺序固定为:先按 partial_outputs 原顺序映射已成功工具,将其保存为 status=not_completed 且无 externalJobId 的待确认消息;再在同一会话增量末尾追加 ERROR terminal system 消息并整体写入 OSS。后续规划失败不得吞掉失败前已执行的成功工具结果;结构化 ToolFailed 可用于调用方诊断与流程决策,但画布确认面不得把它伪装成成功待确认卡。
  • /messages/{messageId}/confirm/messages/{messageId}/cancel 只返回成功确认;前端成功后立即重新读取整个会话,以会话详情中的权威消息状态和 externalJobId 驱动气泡展示与任务轮询。
  • 会话详情的终态懒回填必须在单次 GET 和同一 conversation lock 内完成有界重试:任务结果读取、completed payload 解析或工具 formatter 首次失败后最多重试 3 次,每次等待 100ms 并重新读取主任务。任务读取失败或 completed 任务暂缺 result_payload_json 时,本次重试耗尽后仍保留 OSS 工具消息的 not_completed + externalJobId,由下次会话读取继续 reconcile;JSON 损坏、结果结构不兼容或 formatter 失败等确定性致命错误在重试耗尽后原子写为 failed,保存“重试 3 次后仍失败”的最后错误,避免永久循环。
  • 画布 Agent 是“正式任务 payload 不进入通用用户 read model”规则的窄例外消费者:GET /api/editor/agent-conversations/{conversationId} 只按会话中已有的 externalJobId 定向读取主任务,完成后由对应工具 formatter 从 result_payload_json 提取并归一有界的图片 / 视频 / 音频引用,写入 OSS 工具消息后返回。前端仍不得通过通用任务列表 / 状态接口读取或解析 request_payload_json / result_payload_json;OSS 轻量媒体引用只是会话展示与后续 Agent 上下文,不替代 editor_project_resourceeditor_asset、结构化画布表或 external_generation_job 的业务真相。未激活结构化存储的 canvas 才继续以 editor_canvas.layers_json 作为 legacy 布局真相。

创作 / 游玩统一流程主干

历史设计记录:本节所述 play_flow 和逐玩法 Adapter 已退役,不代表当前 app.rs 路由树。

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”的语义描述,不把历史路径当新架构模板。

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

  • /api/auth/entry/api/auth/phone/*/api/auth/password/reset/api/auth/wechat/bind-phone 的普通手机号请求统一使用 purePhoneNumber 与可选 countryCode,不再接受旧 phone 字段;countryCode 未提供时默认中国大陆 86,显式值必须使用微信同口径的无加号国家码并且当前只允许 86module-auth 先校验国家码,再复用纯手机号规范化规则,最终统一以 +86 E.164 写入认证投影。微信小程序 getPhoneNumber 链路仍只接收客户端 wechatPhoneCode,后端必须要求微信 provider 成功响应中的 phoneNumberpurePhoneNumbercountryCode 均存在且非空,并只使用后两项执行国家码校验和 E.164 构造;腾讯未承诺 phoneNumber 为 E.164,不得依赖其前缀格式,也不得在微信链路默认 86
  • 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 协议不再放在玩法模块里实现。产品、计费、DTO、持久化和 VectorEngine 创建 / 编辑首选请求统一使用 gpt-image-2;只有符合回退条件时,provider 边界才切到兜底模型 gpt-image-2-c。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;公开作品资产读取授权投影是 public_work_asset_read_grant;各玩法既有 *_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. procedure 需要按调用者 identity 鉴权时,必须先从外层 ProcedureContext::sender() 捕获 caller,再把 caller 显式传入 try_with_tx 闭包内的事务函数。SpacetimeDB 2.6.0 的 TxContext 使用内部匿名事务身份,tx.sender() 固定为 Identity::ZERO2.6.1 已向事务透传 procedure caller,但项目仍保持显式 caller 参数,兼容尚未升级的运行环境并让鉴权边界不依赖 SDK 隐式语义。
  11. 修改后运行:
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 不处理充值过期。
  16. 普通微信支付 V3 退款事实由 profile_recharge_refund 保存,回调、退款 API 响应、主动查单和交易账单发现统一调用 record_profile_recharge_refund_observation_and_returnout_refund_no 是商户幂等键,provider_refund_id 唯一;退款申请响应和主动查单等生产者生成 observation ID 时必须同时纳入来源和事实指纹,同一来源同一事实稳定重放、不同来源不得复用 ID;重复 observation 必须校验订单、交易、金额、状态、来源和指纹,不允许仅按主键直接吞掉冲突。
  17. profile_recharge_order_refund_settlement 按原订单聚合累计成功退款金额和权益回收。部分退款不改充值订单 paid;累计金额等于订单金额时才改为 refunded。历史支付和首充资格以 paid_at 是否存在判断,退款不把用户重新变成首充。
  18. 泥点退款按累计成功退款比例计算目标回收量,全额退款强制精确回收原 points_delta。自动回收只扣普通永久泥点,每日免费和会员周期限时泥点保持不变;不足部分持久化为 shortfall 并冻结正式钱包消费,后续 worker 只重试本地回收。已成功退款的泥点订单若出现微信交易号、订单总额冲突或结算计划无效,必须将 settlement 标记为 wallet_frozen 并阻断普通消费。管理员只能对交易号或订单总额冲突执行“确认退款归属”:BFF 必须把微信退款事实的交易号、订单总额、当前冲突类型与本地订单值并排下发并展示,提交时携带管理员实际看到的 expectedErrorCodeSpacetimeDB procedure 在同一事务内核对当前错误码一致后,才在退款行尾部不可变记录管理员、原因、时间和本次获批的错误码并重新运行标准结算;已处理请求只有管理员、归一化原因和错误码全部一致时才视为幂等重放,状态变化或任一审计内容不一致必须 fail-closed。一次确认只豁免对应的交易号或订单总额冲突,渠道、支付状态及其他未获批冲突继续 fail-closed。能追回的永久泥点照常扣除,余额不足继续形成欠账;不能直接清空冻结或伪造已追回量。非法结算计划仍保持冻结等待数据/代码修复。流水来源为 recharge_refund_recovery。会员充值没有可逆 grant 快照,退款统一标记 manual_review,不猜测回滚档位、有效期或周期泥点,也不复用泥点退款冻结语义。
  19. 外部现金退款 SUCCESS 必须先持久化并 ACK,即使本地订单缺失、金额冲突、权益不足或会员需要人工处理,也不能回滚已经发生的现金事实。冲突 observation 记录 resolution code 并进入告警;只有验签、解密、契约解析或 SpacetimeDB 持久化失败才让微信重试。
  20. 主动查询与退款交易账单 worker 只由 HTTP 角色运行并由 WECHAT_PAY_REFUND_RECONCILIATION_ENABLED=true 显式开启。生产 env 示例和 API deploy 会为缺失配置补 true;当 WECHAT_PAY_ENABLED=true + WECHAT_PAY_PROVIDER=real 时显式关闭该开关必须阻断发布,启动日志也必须明确记录启用或异常关闭状态。非终态退款按 1 / 5 / 10 / 20 / 30 分钟衰减查单;北京时间次日 10 点后按 30 个稳定分片轮转补扫微信 API 可查询的近 90 天 bill_type=REFUND 交易账单,每 30 分钟覆盖完整窗口。单行失败不阻塞同日其他退款或其他日期,但该日不写完成 checkpoint 并继续重试;昨日返回 NO_STATEMENT_EXIST 时至少延迟到次日 10 点后再确认空账单。账单申请响应验签,GZIP 解压后按 SHA1 验真,CSV 用结构化 parser 和十进制定点金额解析;发现手工退款后必须再查单取得当前状态。
  21. 普通 V3 支付通知同时校验 AppID、商户号、本地订单渠道、金额和微信支付单号;success_time 缺失或非法时拒绝,不能用本机时间补齐。晚到通知遇到 refunded 订单只做交易号一致性幂等校验,不再次发放权益。
  22. 后台主动退款只支持 wechat_mpwechat_jsapiwechat_h5wechat_native 普通 V3 泥点订单。api-server 必须先按正式支付渠道和商品类型拦截不支持的订单,再做微信支付订单查单预检,然后调用 SpacetimeDB procedure 原子创建退款 hold;只有 hold 成功才允许调用微信退款。wechat_mp_virtual、历史非正式渠道值、会员、未支付、对账未完成、退款已满额、人工冻结、退款欠账或永久泥点不足必须在调用普通 V3 provider 前 fail-closed。
  23. profile_recharge_refund_hold 以稳定 out_refund_no 为主键,保存订单、用户、本次退款金额、占用永久泥点、管理员、原因和 active / settled / released 状态。重试只有在订单、out_refund_no、退款金额、管理员和归一化原因全部与原 hold 一致时才可复用,任一不一致都按幂等内容冲突 fail-closed,不能用新原因调用微信后保留旧审计。部分退款的 hold 在累计应追回增量之外额外保留 1 泥点并发舍入缓冲,全额退款不加缓冲;活动 hold 不改变钱包总额,但普通钱包消费必须预留全部活动 hold;成功退款 observation 扣款并结算匹配 hold,关闭退款释放 hold,外部退款追回不得消耗其他活动 hold。
  24. 退款欠账继续以 profile_recharge_order_refund_settlement.unrecovered_points 为唯一真相;不新增平行 debt 累计。profile_wallet_manual_restriction 只保存人工冻结,普通消费同时检查人工冻结与退款欠账。后续永久泥点到账后继续偿还欠账,每日免费与会员周期泥点不参与;解除人工冻结不得清除退款欠账限制。
  25. 管理员充值订单、用户详情、历史花费手动对账、退款预检/执行、应急退款号登记、退款人工复核和钱包冻结接口只留在 api-server 管理员鉴权路由。用户详情中的历史花费泥点数读取 profile_wallet_consumption_total 投影;已有投影时,每次 asset_operation_consume 负向流水落账在同一事务内按主键 O(1) 原子累加,退款不回减,充值退款追回、余额重置、赠送和 hold 均不计入。首次上线必须在停止业务写入的维护窗口内,由 owner 调用 POST /admin/api/profile/users/initialize-consumption-projections,一次扫描全部权威钱包流水,为每个已有钱包流水的用户初始化存量投影;接口成功后才能恢复流量。维护遗漏或新用户缺行时,首次消费按该用户索引一次性重建(当前消费流水已经在同一事务中,不能重复加本次金额);钱包详情首次读取也保留同一按用户兜底。POST /admin/api/profile/users/reconcile-consumption 是显式手动对账入口:owner 始终可用,member 必须单独持有 profile-wallet-consumption-reconcile 操作权限,任何一级 Tab 都不自动附带;用户详情只在后端返回 canReconcileConsumption=true 时展示按钮。操作经二次确认后扫描该用户全部权威钱包流水、比较并校准投影,同时记录管理员和对账时间。退款人工复核 BFF 必须从管理员会话写入操作人,要求非空原因,返回微信退款交易号、订单总额以及获批错误码等正式审计字段,并调用 runtime service identity 受限 procedure;后台确认面板必须展示这些后端事实,前端不得自行改 settlement、钱包冻结或消费累计。外部微信副作用由 platform-wechat 执行,退款/hold/钱包事务留在 spacetime-module,后台前端只展示 BFF 返回的正式状态。

创作入口泥点扣费契约

  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 设计图提取、角色动作和视频参考图,调用方必须提交 objectKey / resourceId / assetId 候选引用;BFF 只做内联媒体与 payload 门禁,登记状态和归属由 worker 统一解析。任务 request_payload_json / result_payload_json 任意层级都禁止 data: / blob:,并受统一字节上限保护。无效普通字符串可以入队,但必须在签名和 provider 调用前失败;本次不增加 API 侧数据库查询或同步 owner 校验。若以后要求无效引用同步返回 400,应作为独立改造。objectKey 最终必须归属于当前账号的 editor_project_resourceeditor_assetasset_object,由 worker 在解析后、签名读取 OSS 前完成归属校验。本地红框序号标注图必须先上传并确认对象,再把 objectKey 入队;不得把既有 objectKey 下载成 Data URL 后写入任务。图标素材和 UI 素材提取的额外参考图必须真正传入 provider,不得只写入 generationInputs 展示快照;图片快速编辑当前不开放额外参考图。UI 素材提取额外参考图上限为 5 张,普通图片生成上限 5 张,图标素材上限 8 张额外参考图。同步且不持久化的历史兼容入口即使仍能解析 Data URL,也不能把该值转存到工程、素材、元数据、审计或任务表。
  10. 已有静态图片的 POST /api/editor/images/pixel-art-snaps 是免费 inline 派生操作,不调用外部 provider、不创建 external_generation_job、不读写泥点 ledger,也不进入任务侧栏。免费不放宽 owner、稳定引用、输入上限、持久化或处理阶段零持久化门禁。

外部服务与资产

  • 已有图片完美像素化:登录态 POST /api/editor/images/pixel-art-snaps 使用 sourceImageSrc 承载 objectKey / resourceId / assetId 候选稳定引用,要求 projectId / canvasCompletioncanvasCompletion.dialogId 必须非空,并可携带 sourceResourceId / assetKind / generationInputs / assetFolderId / assetLabel;BFF 必须在下载前将候选解析为当前 owner 已登记的私有 OSS object key,并校验 project / resource / asset 归属,拒绝 data: / blob:、signed URL、普通外链和音频、视频、图片序列等非静态栅格输入。编码门禁只接受静态 PNG / JPEG / WebP,明确拒绝 GIF、带 acTL 的 APNG 及带动画标志 / ANIM / ANMF chunk 的 WebP。处理复用 platform-image 纯内存 snapper、进程级并发 2、30 秒排队加处理总预算、单边 10000 与总像素 8294400 上限,但该入口采用 strict 而非生成风格的 best-effort 语义:必须检测到双轴一致且达到置信门槛的既有逻辑像素网格,不能对普通照片或未识别网格使用生成风格中的 min(width,height)/64 兜底;读取、解码、校验、排队、规整、PNG 编码任一步失败 / 超时 / 不适用时,在最终持久化前返回错误,OSS PUT、asset object、project resource、账号素材和画布 layer 增量都必须为零。成功结果保留源图,只对最终 PNG 做一次 OSS PUT,并至多各创建一个 editor_project_resource 和一个 editor_asset;源图已有正式 project resource 时,结果资源以 source_resource_id 关联该资源,再按 canvasCompletion 尝试写入一个右侧派生 layer。completion 读取的权威 dialog 已删除时沿用现有语义跳过画布写入,不得用请求中的旧 placeholder 复活图层;已经成功落库的 resource / asset 可以保留。客户端回包时若本地 dialog 已删除,不应用完成快照;现有布局 CAS 没有 deletion tombstonecompletion 先提交、删除保存后冲突的极端竞态仍按权威快照收口。客户端不得为该 unsafe POST 配置 EDITOR_REQUEST_RETRY_OPTIONS,请求字节可能已发送后不因 transport 异常或 408 / 425 / 429 / 502 / 503 / 504 自动重放;Bearer 中间件在 handler 前拒绝请求后的既有认证恢复继续保留。结果未知时先 GET 权威项目 / 素材快照。
  • 完美像素持久化边界:所有可判定的稳定引用、owner、项目、来源资源、素材类型、静态编码、元数据、网格适用性、排队、CPU、解码、规整和编码校验都必须在首个最终 PNG PUT 前完成。进入持久化后沿用既有跨 OSS 与 SpacetimeDB 的非事务边界,依次确认 PNG / asset object、project resource、账号素材和 completion;后段失败可能保留此前已经确认的对象或记录,不做破坏性删除补偿,也不自动重放 unsafe POST。排障按响应或日志中的 task_id / object_key / resource_id 重新读取权威项目与素材快照;跨系统单事务 completion 留待独立 procedure 方案收口。
  • 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 图片 provider 归属 platform-image,密钥只在后端环境变量中;逻辑 SKU 与 provider 首选模型均固定为 gpt-image-2,只在明确模型不可用、408 / 非拒绝类 429 / 5xx、响应解析失败或非拒绝类缺图时切换兜底模型 gpt-image-2-c。401 / 403、普通参数或安全拒绝、本地配置 / 参考图错误、无法确认上游是否已受理的发送错误、request budget 耗尽和生成成功后的图片下载失败不得切模型。一次业务请求总发送上限仍为 5 次;切换兜底模型会消耗后续 attempt,不允许两个模型各重试 5 次。api-server 内的 openai_image_generation.rs 只是兼容调用面和外部失败审计桥接,不再承载 provider 协议实现。实际外部生成运行记录统一落 tracking_eventevent_key = external_generation_runmetadata 记录开始 / 结束时间、耗时、状态、成功标记、失败原因、provider task id、结果摘要和 recovered failure 数量;首选模型失败但兜底模型成功时,首选失败仍落 external_api_call_failure。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_msfallback_from_modelfallback_to_modelreference_image_bytes_totalrequest_params,不要把 SendRequest 当成上游业务错误。
  • 抠图输入以私有 OSS 作为内存生命周期边界:生成原图和角色动作抽取帧上传时消费图片字节所有权,上传完成后不保留原图缓冲;手动去背景直接解析并校验已有 OSS object key,不下载原图。BgFilter 必须为 object key 签发 600 秒 GET URL 并通过 multipart image_url 提交,不用 file 重传;flat 链路进入阿里云 fallback 时由 platform-matting URL 接口单独下载并上传 AuthorizeFileUpload 临时对象,在推理前释放下载缓冲,继续 fallback 到本地键色时再单独下载一次原图,本地产出后释放本次原图下载缓冲。签名 URL 不得写入日志、审计或持久化。
  • 角色动作抠图输入像素边界:仅图片画布角色动作链路在 FFmpeg 抽帧后、源帧上传 OSS 前,把帧解码为 RGB8,并按最终 frameWidth × frameHeight 的 contain 比例使用 Triangle 只缩放到内容尺寸;该阶段不得创建最终目标尺寸画布、不得引入 Alpha 通道,也不得插入任何 padding。BgFilter、阿里云通用抠图和本地键色降级共享这个无补边源帧 object key。抠图返回后才统一转为 RGBA8,按相同比例居中放入最终目标尺寸画布,并用 RGBA(0,0,0,0) 补齐透明 padding。以 560×752 → 323×480 为例,抠图输入固定为无 Alpha、无补边的 323×434 RGB8 PNG,最终输出为上下各 23px 透明补边的 323×480 RGBA8 PNG。旧 /api/assets/character-animation/* 动作发布链路继续保留原有帧 finalizer,不适用该输入规则。抽帧解码后若携带 Alpha 通道,必须先把像素按白底合成为不透明再转 RGB8,禁止直接丢弃 Alpha——全透明像素下未定义的 RGB 值会以杂色进入抠图输入,重新引入杂色边缘;共享 FFmpeg 抽帧命令保持不固定 -pix_fmt,白底合成只属于该链路的 BgFilter 输入准备阶段。
  • 阿里云通用抠图的非上海地域输入不得使用 viapiutils/GetOssStsToken、固定 viapi-customer-temp 或 OSS V1 PUT。platform-matting 必须按官方新版 SDK Advance 协议调用 AuthorizeFileUpload,使用动态返回的单对象 Policy 执行 multipart POST,再把临时上海 OSS URL 交给 SegmentCommonImage;输入归一化、结果下载与原尺寸 Alpha 回贴继续留在同一适配器内。该协议仍上传图片字节,不等同于阿里云服务端直接抓取任意公网 URL,也不改变上层 BgFilter → 阿里云 → 本地降级顺序。
  • 编辑器抠图服务:手动 POST /api/editor/images/background-removals 与角色形象生成、图标 spritesheet 生成、UI 设计图素材提取、角色动作抽帧后的透明化统一通过唯一 loopback bgfilter-worker 调用 BgFilter provider。provider 配置继续使用 GENARRATIVE_EDITOR_BGFILTER_BASE_URLGENARRATIVE_EDITOR_BGFILTER_TOKEN,默认 base URL 为 http://58.87.105.82/bgfilter;单次 provider attempt 上限不再独立配置,由公式 N × est × 2 运行时派生,其中 est = GENARRATIVE_EDITOR_BGFILTER_SINGLE_IMAGE_ESTIMATE_MS(默认 5000,依据为服务端高并发单图处理约 1-3s、网络约 3-5s),旧 GENARRATIVE_EDITOR_BGFILTER_REQUEST_TIMEOUT_MS 已删除;旧 GENARRATIVE_EDITOR_BACKGROUND_REMOVAL_TOKEN 只作为 provider token 的兼容回退别名,原手动去背景专用 base URL / timeout 配置已经删除。父流程先把候选 objectKeyresourceIdassetId 解析为当前 owner 已登记的私有 OSS object keyBFF 入队前统一拒绝 data: / blob:,底层 resolver 在解析引用前再次拒绝内联媒体并完成登记状态与 owner 校验。父流程只通过一次内部 HTTP RPC 传递 object key、排队预算 maxQueueWaitMs、调用预算 callBudgetMs 与模式参数,不传图片字节或签名 URL,并同步等待子 worker 返回的受限图片二进制 body。子 worker 在每次真实 provider attempt 前签发短期 OSS URL,承担 admission 保险丝 Q(默认 2048,仅防连接风暴)、provider 并发 N(生产 16);排队 deadline 从 Q admission 时刻起算,完成 JSON 校验并进入 provider permit 等待队列时再取得队长快照,按 min((队长+5)×est×2, maxQueueWaitMs) 约束排队等待。子 worker 还负责严格最多两次顺序 attempt、结果校验和按 flat / complex 隔离的进程级熔断;两种模式共享 GENARRATIVE_EDITOR_BGFILTER_CIRCUIT_FAILURE_THRESHOLD=3GENARRATIVE_EDITOR_BGFILTER_CIRCUIT_COOLDOWN_SECONDS=120 默认值,但失败和成功只更新当前模式,且只由子 worker 读写。手动去背景固定使用 background_mode=complexseg_model=birefnetcross_check=off,不传 filescreen_colorcomplex provider 失败累计自身熔断,任意失败或自身熔断都直接返回父流程失败,不接 flat fallback,也不影响 flat 熔断。标准纯色背景四条链路固定使用 background_mode=flatscreen_color=<screenColor>seg_model=<segModel>cross_check=<on|off>,其中角色形象生成、图标 spritesheet 生成和角色动作逐帧去背传 cross_check=onUI 设计图素材提取传 cross_check=off。前端用户路径不展示抠图模型、模式或 cross-check,固定提交默认 birefnet,后端仍识别内部保留的 anime-seg;这些参数只属于后端内部供应商策略,不进入前端或外部 OpenAPI。父侧不重试已建立连接的内部 RPC,仅对 TCP 连接从未建立的失败按父预算有界退避重试(跨过 worker 重启与开机排序窗口,收到任何 HTTP 响应即停止);flat 两次 provider attempt 失败、熔断、overload、内部 deadline 或断连后,只要父业务预算仍有效,父流程才继续“阿里云通用抠图 → 本地 editor_green_screen 键色扣除”,熔断期不得直接退化到本地兜底。角色动作视频生成的背景色已与生图链路统一:screenColor=auto 时由视觉 LLMgpt-5-miniResponses 协议、low 推理档)读源角色图自动决策,并经硬过滤器剔除与前景 / 皮肤撞色的候选,手动 hex 则尊重用户选择;透明源角色图在提交 Ark 图生视频前先合成到选定背景色实色,使视频背景等于抠图键色;抽帧后每帧先上传私有 OSS 并释放原帧缓冲,再以 object key 固定使用 seg_model=birefnetcross_check=on 进入上述三段式链路。阿里云通用抠图配置为 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。标准纯色背景链路中,子 worker 已发出的 BgFilter provider 失败(含被剩余预算截短后发生的 timeout 与 response 阶段超时,这类失败不计入熔断但仍是审计候选)由进程级 1024 个审计任务硬上限保护,获准任务写入共享 tracking outbox 根目录下独立的 bgfilter-worker/ 子目录并批量落库;满载、outbox 缺失、达到磁盘保护阈值或写盘失败时允许丢弃并记录指标,不回退逐条同步直写 SpacetimeDB。父侧阿里云抠图链路已开始后的失败(包括源 OSS GET 成功后的解码、尺寸校验和归一化失败)继续按通用外部 API 审计策略处理。真正开始外部调用前的本地预检不写该审计,并在 failureStage 中保留 source_decodesource_validate 等阶段。成功图片字节返回后,最终 Alpha / 尺寸恢复、OSS / asset object、画布写回、计费和父任务终态仍全部由父流程负责。
  • BgFilter 连接复用、超时与动作帧流水线:AppState 分别复用父侧内部 worker HTTP Client 和子 worker 专用 BgFilter provider HTTP Client;父侧对一次逻辑调用至多让 worker 接收一次内部 RPC,不重试已建立连接后的失败;仅 TCP 连接从未建立时(worker 重启 / 开机排序窗口)按每轮重算 maxQueueWaitMs 的有界退避序列重连——增加的只是连接尝试次数,不产生第二次被接收的 RPC。重连配额按本进程是否已连通过 worker 分档:首连前(冷启动)flat 22.5s / complex 约 62.5s,首连后 flat ≤1.5s / complex 22.5s;每次重连计 bgfilter_internal_connect_retry_total 指标。子 worker 在同一个 N permit 内严格最多执行两次顺序 provider attempt。唯一子 worker 使用 GENARRATIVE_BGFILTER_WORKER_CONCURRENCY=N(生产 16)限制真实 provider 在途数;GENARRATIVE_BGFILTER_WORKER_MAX_REQUESTS=Q 降级为可选 admission 保险丝(默认 2048,仅防连接风暴,显式配置时必须 >= N)。超时全部由 Nest 运行时派生:单 attempt 上限 N × est × 2、调用预算 callBudgetMs = 2 × attempt + 1s(自取得 N permit 起算)、排队等待受 min((provider 等待队列队长+5)×est×2, maxQueueWaitMs) 双重上界(动态项充当自适应过载探测,超时带 bound = estimate | parent 标记),排队不侵蚀调用预算;Nest 必须同放共享 API 基础环境;请求携带的 callBudgetMs 只是父侧配置指纹,worker 比对后不一致只告警并计 bgfilter_internal_call_budget_drift_total 指标、始终以本进程公式值执行——发布调优 N / est 的新旧进程共存窗口不得误伤在途任务,持久漂移由部署脚本共享 env 对齐校验在启动前拦截。角色动作不再增加 2000ms × 本次实际帧数32 / 40 / 48 帧使用相同公式。父侧按剩余绝对预算派生 maxQueueWaitMsflat 扣除 39s 父侧预留(37s fallback + 2s 传输窗),complex 只留 2s 传输窗;<= 0 时不发请求直接降级 / 失败),client timeout 取 maxQueueWaitMs + callBudgetMs + 2s;每次 attempt 前重新签发短期 OSS URL,剩余时间不足时不开始新的 attempt。父侧成功响应解码槽 P = 8。角色动作继续以 buffer_unordered(frame_count.max(1)) 将全部单帧逻辑调用加入无序在途集合;返回结果携带原始帧序并在最终 collect / drain 全部已提交 Future 后排序,任一帧最终失败时必须先排空全部已启动 Future,再让整个动作任务失败退款,不能发布缺帧动画。单帧按“绿幕源图 owned 上传 OSS 并释放原帧 → 以 object key 调内部 worker / 按 object key 由父侧降级 → 父侧处理透明帧并落 OSS”流水化。角色动画源帧 PUT、透明帧 PUT 和最终帧 HEAD 仍统一复用 AppState 内初始化一次的 OSS HTTP Client(连接池参数为 connect 30 秒、request 60 秒、idle 300 秒、每 host 8 个 idle 连接、TCP keepalive 60 秒),并受进程级 8 路 OSS semaphore 限制;BgFilter provider 的 N 不占该 OSS permit,阿里云和本地处理既不占 OSS permit,也不受 N / Q 限制。每个 OSS 网络 attempt 单独获取 permit,退避期间释放;PUT/HEAD 动画帧请求最多 3 次(250ms、500ms 退避),只重试无 HTTP 响应的传输错误、timeout、OSS PutObject 的 400 + RequestTimeout、PUT 400 错误体读取失败(未解析出 Code,按 timeout/transport 归类)、408、429 和 5xx。动作帧 PUT 只在 400 响应中有界读取最多 16 KiB OSS 错误 XML,并保留 Code 与响应头优先的 x-oss-request-id;错误体读取超时/断流时保留已读字节,已解析出的 Code 优先生效,未解析出 Code 则按 timeout/transport 归类重试;除 RequestTimeout 与该错误体读取失败情形外的其他 400、401/403/404、配置、URL/签名和空请求体错误不重试。最终帧 HEAD 失败只重试 HEAD,不重复 PUT。
  • 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。bgfilter-worker 是受限资源例外:它使用共享 tracking outbox 基础目录下独立的 bgfilter-worker/ 子目录,provider 失败审计在 spawn 前受进程级 1024 硬上限保护并由 shutdown tracker 跟踪;满载、outbox 缺失、保护阈值拒绝或写盘失败时直接丢弃并观测,不回退同步直写 SpacetimeDB。优雅退出先排空已获准任务的 enqueue,再封存并尽力 flush;进程被强杀时只有已 enqueue 记录可在下次启动重放。
  • 外部生成运行记录:所有外部生成编排的完成态统一写入 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 claim 只允许 source_module = editor-canvas;下述逐玩法生成和写回描述均为退役前历史。历史 pending / running 行继续保留原状态,不得领取、失败收口或改写 payload。

  • 用途:外部生成 worker 的内部持久任务队列;GENARRATIVE_EXTERNAL_GENERATION_MODE=queue 时,api-server HTTP 角色只入队,external-generation-worker 角色通过 claim lease 领取、续租、执行,并用 lease_token 栅栏回写阶段、完成 / 失败。队列行继续保存 worker 执行、计费与滚动发布兼容所需字段,末尾可选 phase 只取 generating / processingclaim 写 generating,真实进入抠图处理时由受 job_id + worker_id + lease_token 保护的 procedure 写 processing。phase procedure 以结构化结果区分 LeaseFencingRejectedOtherRejectedLeaseFencingRejected 立即终止,OtherRejected 以及 SDK 的 Procedure / Runtime 错误不重试,只有 Build / ConnectDropped / Timeout 在同一个 job attempt 内重试一次。该重试只重新上报 phase,不把任务写回 pending,也不重新调用 provider;编辑器 job 入队固定 max_attempts=1,第二次传输失败后任务进入 failed,不会回到 pending 或从 provider 生成起点重跑。用户可见任务列表、价格、状态、阶段、未确认终态数量和通知确认时间的正式读取事实源已经迁到 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 复用同一队列表。结构化 canvas 激活后,当前 worker completion 先以读取时 canvas revision 执行 CAS,并发冲突时拒绝覆盖并保留可诊断失败;目标是进一步收口为受 lease 栅栏保护的单事务幂等写入 editor_project_resource、结果 editor_canvas_layereditor_canvas_generation_dialog 终态和 canvas revision。未激活 canvas 在 2 MiB 上限内继续走 legacy editor_canvas.layers_json 兼容写回。前端只通过 BFF job 状态轮询和项目快照读取恢复完成态。GENARRATIVE_EXTERNAL_GENERATION_MODE=inline 时不创建该队列行,三个 external generation guard 字段必须同时为空才允许 api-server 受控同步写回,半空 guard 仍会拒绝。worker 成功写回业务事实后才能 complete job;业务失败态写回成功后才能 fail job,失败态未写回时保留租约等待后续重领。

  • 素材写回:worker 成功后仍经 api-server facade 写入 editor_project_resource / editor_asset;结构化 canvas 的 layer / dialog / revision 与未激活 canvas 的 legacy layers_json 分流按上一条执行,前端不直接发明正式完成态。

  • 载荷约束:本次先对 source_module = editor-canvasrequest_payload_json / result_payload_json 实施有限大小合法 JSON、任意层级禁止 data: / blob: 的双层门禁,只保存 worker 执行必需的普通参数和已登记媒体引用。画布 Agent 来源的任务可在 result_payload_json.editor-agent-tool-call-result 中保存有界的轻量结果和已登记媒体引用,供后端按已有 externalJobId + owner_user_id 定向懒回填;其它编辑器任务保持元数据结果,并可保存有界的 warning.code/reason。其它玩法在完成各自参考图资源化之前不由本次门禁静默改变既有请求契约。该主表只供 worker claim / 执行、受控维护以及画布 Agent 的定向结果回填读取;正式用户任务列表、单任务状态、队列概览与 acknowledge 不得返回或解析这两个 payload。画布 Agent 懒回填必须经对应工具 formatter 归一为有界轻量媒体引用后写入 OSS 会话,不能把原始 payload 直接透传前端。

  • 非阻断告警:角色形象、图标图集和 UI 素材提取已保存 provider 原图、但透明背景处理最终失败时,以原图唯一主图完成任务;透明图和切片不写入画布。这个 source-only 降级只包住透明背景处理的最终失败,phase 上报、provider 原图持久化、透明处理图持久化或画布写回失败仍按任务错误传播。图标 / UI 透明图集成功但自动拆分降级时仍保留透明图集;通用 warningsliceWarning 只在「透明背景最终失败」这一条上互斥,风格归一化或像素规整产生的通用 warning 可与 sliceWarning 并存。两类成功降级都以既有 completed 状态收口,不新增状态值:source-only 的 inline / external v1 响应使用结构化 warning.code/reason,仅拆分失败的 inline / external v1 响应继续使用既有 sliceWarning.code/reason,其 reason 保留原始诊断;queue worker 才把两者归一为有界的 result_payload_json.warning:只有一条时原样保留完整 reason,两条并存时按“通用在前、拆分在后”拼接且 code 收敛为 multiple-generation-warnings(两条 code 相同则沿用原 code),不允许任何一条被丢弃;sliceWarning.reason 无论是否并存都由 worker 添加“图集已生成,但自动拆分未完成:”前缀,拼接结果最后统一做长度上界收敛。除上述画布 Agent 定向回填的轻量结果外,队列结果不保存图片、切片列表或媒体 URL。

  • 2026-07-29 收口补充:上条 source-only 的“透明背景处理最终失败”同时包含 Alpha 比例漂移超过 5%、provider 原图修复性回读失败、Alpha 回贴失败和透明图完整解码失败;三条链路共用 helper,只写已保存 provider 原图画布层,图标 / UI 固定 iconImageSrcs=[]sliceWarning=null,不得写透明图、派生资源或切片。provider 原图本身解码失败时在首次持久化前失败,不允许 512×512 元数据兜底。图标自动拆分、手动拆分与 UI 提取共用受 2 路 semaphore、30 秒 / 请求 deadline 保护的 blocking helperplatform 对全部原始连通域设置 4096 硬上限、用空间网格查询邻近辅助候选,并在裁剪 / PNG 编码前执行 maxOutputSlices=64。自动超限只保留整张可信透明图并返回稳定 sliceWarning,不写切片;手动超限在首次持久化前返回 422

external_generation_job_summary

  • Rust 结构体:ExternalGenerationJobSummary
  • 源码:server-rs/crates/spacetime-module/src/external_generation.rs
  • 用途:外部生成正式任务列表的轻量投影,按 job_id 保存 owner、来源、状态、可选 phase、价格、有界错误摘要、通知确认时间、各阶段时间和入队时提取的 request_prompt,不包含 request/result payload、worker lease 或 dedupe 内部字段。错误摘要统一拒绝内联媒体并限制为 2048 字符;列表在单次 owner 扫描中同时计数并只保留请求 limit 的固定大小 top-N,不得先收集全量历史再截断。enqueue、claim、renew、phase update、complete、fail 事务同步投影;acknowledge 只更新该轻量表并写审计事件,后续主任务同步必须保留已有确认时间,禁止为了写确认时间加载 / 重写大 payload 行。BFF 的列表、状态和确认只调用 summary procedurerunning + processing 映射为“正在处理”,其它 running(含旧行 phase=None)映射为“正在生成”。历史终态任务由迁移操作员的游标分批 maintenance procedure 在压缩 payload 时同步回填摘要,正式列表不得为兼容旧数据回扫完整主表。
  • 非阻断告警:摘要字段 warning_message 是展示投影,由完成任务的轻量 result_payload_json.warning.reason 原样提取,不等同于公开 inline / external v1 的原始结构化诊断字段。complete 和历史 backfill 共用同一构建路径;历史任务按其结果载荷中已写入的 reason 快照投影,不为格式升级重写或补前缀。单 job 状态和任务列表 BFF 以 warning: string 返回该可直接展示的完整文案,不再返回结构化 code,Web 不得再次补前缀或按字符串推断告警类型。错误与告警摘要都不复制内联媒体并限制为 2048 字符。phasewarning_message 分别表示当前执行阶段和成功降级提示,不得混用;worker / BFF / Web 必须同版本协调发布,不保证滚动混部或旧 Web 缓存下的字符串语义兼容。
  • 正式读取 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。已登记对象默认并继续保持 private。API 通过 get_asset_object_by_location_and_return / get_asset_object_by_id_and_return 在服务端索引上权威查询 private table;资产读取 ACL 使用 get_asset_read_access_by_location_and_return 在同一事务快照内同时返回位置查询、现役 editor_showcase_asset 精选素材派生授权和当前已启用 editor_showcase_campaign_config 的活动卡专用目录 exact-key 派生授权,procedure 只允许 runtime service identity 调用。旧创作模板作品授权 view 和逐玩法资产采集器已经退出 module;spacetime-client 不订阅全量 asset_object,也不把连接级 cache 当成安全判断。位置查询发现重复 bucket / key 时失败关闭,不能任选一条继续授权。普通对象只有权威位置查询返回不存在且显式使用 curated legacyPublicPath 时才能进入白名单兼容;历史活动卡上传缺少 metadata 时,可由“global 配置已启用 + image_object_key 精确匹配 + key 位于 generated-character-drafts/editor/showcase-campaign/”这一窄授权继续换签,禁用或替换活动卡后旧 key 立即失效。新上传活动卡仍必须完成 asset_object confirm,不能把该历史兼容当作跳过正式登记的常规路径。

SpacetimeDB viewpublic_work_asset_read_grant

  • 状态:已退役,不再注册到 spacetime-module,也不再生成客户端绑定。
  • 历史源码:server-rs/crates/spacetime-module/src/public_asset_access.rs,仅供追溯,不参与现役 crate 根。
  • 现役替代:资产读取 procedure 只计算 editor_showcase_asset 精选素材的精确授权;历史作品表继续作为数据壳保留,但不再导出公开作品资产授权。

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 鉴权。

admin_account

  • Rust 结构体:AdminAccount
  • 源码:server-rs/crates/spacetime-module/src/admin_account_storage.rs
  • 说明:后台 member 私有账号表,保存规范化用户名、展示名、Argon2id 密码摘要、一级 Tab 权限 JSON、独立操作权限 JSON、启停状态、会话版本和创建 / 更新审计字段。action_permissions_json 是既有表末尾新增的可选字段,旧行默认空数组语义;当前唯一独立操作权限为 profile-wallet-consumption-reconcile。原环境变量管理员作为虚拟 owner,不写入该表。账号查询与写入 procedure 只接受 runtime service identityHTTP 列表和写响应不返回密码摘要。
  • 索引:account_id 为主键,username 为唯一登录名;Tab 权限、独立操作权限、密码或启停状态发生变化时在同一事务递增 token_version,使旧 JWT 下一次请求立即失效。

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 及其结构化子表,旧 project 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、owner、时间戳与当前 revision;当前编辑器读取 / 保存 project 的默认 canvas,后续支持一个工程多个 canvas。layers_json 继续保留,legacy canvas 在统一 2 MiB 上限内以它作为布局真相;editor_canvas_layout_migration 激活 structured 后,图层和生成对话框分别以 editor_canvas_layereditor_canvas_generation_dialog 为权威,layers_json 只作为存量迁移输入和受限回滚载体。所有结构化 mutation 必须以 expected_revision 做 CAS,成功事务只递增一次 revision。
  • 索引:by_editor_canvas_project_idby_editor_canvas_owner_user_id

editor_canvas_layer

  • Rust 结构体:EditorCanvasLayer
  • 源码:server-rs/crates/spacetime-module/src/editor_project_storage.rs
  • 说明:图片画布结构化图层表,一行保存一个 layer 的 canvas / project / owner 归属、几何、层级顺序、分组、hidden / locked / flip 状态与 resource_id。V1 的 item_json 只保留未结构化扩展字段,单行最大 512 KiB;快照以 typed 列重组,不得把完整图层 JSON 当作平行真相。媒体业务真相仍由 editor_project_resource / editor_asset 持有。历史 layout 的 sourceResourceId == resourceId 属于无意义自引用,迁移时按资源表真相剥离;其他资源字段冲突继续 fail-closed。唯一存量缺资源例外是已缺资源行、但帧与预览均为稳定站内对象路径的 local-* + generated + image-sequence 历史角色动作图层:迁移保留其有界媒体扩展并纳入 canonical hashactive 后只能续存同一行且扩展不可变,不能新增或篡改。其他缺资源图层继续 fail-closed。当前站内写入通过 revision CAS 把兼容布局事务性拆成行;后续再将新增、移动、缩放、删除、重排和分组收窄为有界 batch mutation。
  • 索引:按 canvas 读取完整结构化快照,按 project 做级联清理;owner 保留在行内用于归属校验。同一 canvas 的 layer id 必须稳定且唯一。

editor_canvas_generation_dialog

  • Rust 结构体:EditorCanvasGenerationDialog
  • 源码:server-rs/crates/spacetime-module/src/editor_project_storage.rs
  • 说明:图片画布生成对话框表,保存 canvas / project / owner、生成模式、状态、可选 source / generated layer、占位几何和有界扩展字段;typed 列为快照真相,dialog_json 只保留未结构化参数。当前 worker completion 以读取时 revision 调用 CAS 保存,冲突时拒绝覆盖;job_id + worker_id + lease_token 栅栏下的资源 / layer / dialog / job 单事务完成仍是后续收口。
  • 完美像素 completion:同步处理成功后按当前 revision 重新读取权威 dialog;目标 dialog 存在时只写入一个派生 layer 并关联结果,目标 dialog 的删除已先持久化时跳过 layer / dialog 写回,不得按请求快照重建占位。该分支不属于 external job completion,允许此前已成功创建的 resource / asset 保留。
  • 索引:按 canvas 和 project 读取结构化行;当前未建立 external job 二级索引。

editor_canvas_layout_migration

  • Rust 结构体:EditorCanvasLayoutMigration
  • 源码:server-rs/crates/spacetime-module/src/editor_project_storage.rs
  • 说明:每个 canvas 一行的结构化持久化控制与迁移审计表,保存 schema version、backfilled / active / rolled_back 状态、最后校验 revision、legacy / structured canonical hash、layer / dialog 数量、资源引用集合 hash 和激活 / 回滚时间。存量迁移固定执行幂等 backfill → hash / 数量 / 资源引用核对 → revision CAS activateactive 重入和 rollback 同样重新核对,不只按状态早返。回滚结果还必须不超过 2 MiB。三个 procedure 只允许 database migration operator 调用;npm run spacetime:editor-canvas-layout:migrate 默认 dry-run,显式 --apply 才写入。
  • 索引:canvas_id 唯一,一切模式切换仅允许受限迁移 procedure 执行;前端不得提交或推断存储模式。

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 快照返回前端;前端只保存稳定 resource_id 布局引用,不能把同一生成结果再次作为正式业务真相写入。项目封面快照也落在该表,使用 asset_kind = project-cover-snapshotsource_type = uploaded 和私有 OSS / asset object 引用,代表画布当前视口栅格化后的静态封面;项目列表和创作主页最近项目只读取最新封面快照资源,不在列表页根据 layout 临时拼画布。从账号级素材库把同一生成素材拖回同一项目画布时,后端优先复用同项目内同源同媒体资源,避免每个图层实例都插入新的资源行。账号级素材删除不级联删除该表,避免历史画布丢图。结构化 canvas 的几何、层级、分组和资源引用以 editor_canvas_layer 为权威,生成器对象以 editor_canvas_generation_dialog 为权威;legacy canvas 才在 2 MiB 上限内从 editor_canvas.layers_json 兼容读取。新写入不再把素材生成输入快照作为图层布局真相保存。历史普通图层缺资源只能由 migration operator 调用 repair_editor_canvas_resources_and_return 定向修复:procedure 每次只处理一个尚无迁移记录的 legacy canvas,校验 owner/project、revision、canvas/project 两份 raw layout SHA-256、精确 layer/resource/sourceResourceId、同工程替换资源与 private asset_object 谱系;图片只替换引用,音频只恢复经核验的 420x120 项目资源行。运维入口 npm run spacetime:editor-canvas-resources:repair 默认 dry-runapply 必须绑定 plan SHA-256 并在成功后自动复核 already-repaired,禁止手工 SQL 绕过事务 guard。
  • 完美像素资源:处理成功时只允许一个最终 PNG 对象对应一个新 project resource;源图已有正式 project resource 时,source_resource_id 指向该资源;结果尺寸与最终 PNG 一致,不写逻辑低分辨率图、诊断图或前后对比图。成功时同时创建一个同源 editor_asset,请求省略素材文件夹时落入默认素材文件夹;completion 因权威 dialog 的删除已先持久化而跳过画布写回时,这两类已确认资源无需回滚。
  • 索引: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_id、可选后台归组 group_task_id、拆分批次预期数量 group_task_expected_asset_countasset_kindgeneration_inputs_json、可选 source_resource_idgeneration_cost_mud_points。归组字段追加在表尾并默认 None,只用于稳定派生任务的后台分组,不替代 task_id;批次是否初始完整以独立完成事实为准,不按当前剩余素材数反推。素材在同一账号的所有项目中可见;图片 / 图标 / UI 提取等生成 BFF 在请求携带 asset_folder_id 时负责创建账号级生成素材并返回 asset 快照,若同次生成也创建了 editor_project_resource,则把该 resource_id 写入 source_resource_id。角色动作生成保留原始绿幕视频中间素材,同时把最终帧序列作为一条 asset_kind = character-animation 素材入库:首帧写入 image_src / thumbnail_src,完整帧列表、FPS、时长和预览视频写入 generation_inputs_json.characterAnimation,不把每帧拆成独立素材。生成视频会抽取首帧封面写入 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_asset_group_source_provenance

  • Rust 结构体:EditorAssetGroupSourceProvenance
  • 源码:server-rs/crates/spacetime-module/src/editor_project_storage.rs
  • 说明:手动图集拆分的私有可信来源索引。仅当账号素材由后端生成链路写入且具有正式 source_resource_id 时,按 owner + source resource、asset object 和 Object Key 记录真实来源 task_id;普通素材 / 资源创建请求不能直接写该表。跨项目复用素材后可通过稳定媒体引用找回原任务,历史行首次扫描命中后补写索引。该持久表纳入 migration_tables! 导入导出清单,数据库迁移与恢复不得遗漏来源归组事实。
  • 主键:lookup_key

editor_asset_group_cohort

  • Rust 结构体:EditorAssetGroupCohort
  • 源码:server-rs/crates/spacetime-module/src/editor_project_storage.rs
  • 说明:手动拆分批次的私有完成事实。api-server 只有在预期的全部 asset ID 已成功落库并由 runtime service identity 逐行校验 owner、真实 task、归组 task 和预期数量一致后才能插入;事实写入后不随用户删除单片素材而撤销。没有完成事实的现代批次不能并入来源根任务。该持久表纳入 migration_tables! 导入导出清单,数据库迁移与恢复必须保留不可逆批次完成事实。
  • 主键:cohort_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。后台可配置启用状态、标题、图片地址、提示词、作者和成本文案;上传按钮先通过后台受控上传票据把 private 图片写入 OSS,再调用 /admin/api/editor-showcase/campaign/image-upload-confirm 复用统一 OSS HEAD 校验并登记正式 asset_object,确认成功后才把 image_src、内部图片 OSS image_object_keyimage_widthimage_height 写回表单。公开精选接口只在启用时返回该配置,前端优先用 image_object_key 走签名读地址展示,并按记录的图片宽高决定活动卡比例。匿名读权限只对当前启用配置中、位于活动卡专用目录且精确匹配的 key 派生;禁用或换图后旧 key 自动失效,不能公开整个 generated 前缀。
  • 索引:主键 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
  • 生效时间:starts_at / expires_at 均可为空;两者同时存在时必须满足 starts_at < expires_at。开始时刻计入有效区间,截止时刻不计入有效区间。

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_refund

  • Rust 结构体:ProfileRechargeRefund
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs
  • 作用:普通微信支付 V3 退款单聚合。以 out_refund_no 为主键、provider_refund_id 唯一,保存原订单/微信支付单、四个分金额、微信状态、最近观察、权益目标/已回收/未回收量和人工处理错误码。管理员读取契约同步暴露微信交易号、订单总额和人工复核获批错误码。交易号或订单总额冲突经管理员确认归属后,追加保存处理管理员、非空原因、处理时间和本次获批的错误码;四字段只写一次,作为重新执行正式结算的审计事实。
  • 索引:by_profile_recharge_refund_order_idby_profile_recharge_refund_status_updated_at

profile_recharge_refund_observation

  • Rust 结构体:ProfileRechargeRefundObservation
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs
  • 作用:退款事实的追加观察记录。保存 callback / api_request / query / trade_bill 来源、按“来源 + 事实指纹”稳定生成的 observation ID、金额、状态、脱敏通知引用、事实指纹和 resolution code;同一事实从不同来源进入时使用不同 ID,不保存回调密文、签名、密钥、原始 CSV 或短时下载 URL。
  • 索引:by_profile_recharge_refund_observation_out_refund_noby_profile_recharge_refund_observation_order_id

profile_recharge_order_refund_settlement

  • Rust 结构体:ProfileRechargeOrderRefundSettlement
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs
  • 作用:原充值订单维度的退款与权益结算摘要,保存累计成功退款金额、目标/已回收/未回收泥点、权益状态和钱包冻结事实。部分退款不改订单终态;累计全额才把订单改为 refunded
  • 索引:主键 order_idby_profile_recharge_order_refund_settlement_user_id 用于钱包消费前检查退款欠款。

profile_recharge_refund_hold

  • Rust 结构体:ProfileRechargeRefundHold
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs
  • 作用:后台主动退款调用微信前的永久泥点占用。活动占用保护退款所需泥点但不改钱包总额;匹配退款成功后结算,关闭或确认未创建的退款释放。相同 out_refund_no 只允许内容完全一致的幂等重放。
  • 索引:主键 out_refund_noby_profile_recharge_refund_hold_order_idby_profile_recharge_refund_hold_user_idby_profile_recharge_refund_hold_status

profile_wallet_manual_restriction

  • Rust 结构体:ProfileWalletManualRestriction
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs
  • 作用:后台人工钱包冻结当前态,保存冻结原因、创建/更新管理员和时间。它不承载退款欠账;退款欠账仍来自订单退款 settlement,两个来源任一有效都阻断普通消费。
  • 索引:主键 user_id

profile_recharge_refund_bill_checkpoint

  • Rust 结构体:ProfileRechargeRefundBillCheckpoint
  • 源码:server-rs/crates/spacetime-module/src/runtime/profile.rs
  • 作用:按交易账单日期记录已完成的退款 reconciliation,保存账单 SHA1(确认稳定无账单时保存 NO_STATEMENT_EXIST)、处理退款行数和完成时间。任一退款行失败时不写完成 checkpoint,成功前缀依赖 observation 幂等重放;重复下载、多实例执行或进程重启不会重复回收权益。

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
  • 生效时间:复用邀请码时间窗口语义,starts_at / expires_at 均可为空;两者同时存在时必须满足 starts_at < expires_at。开始时刻计入有效区间,截止时刻不计入有效区间。用户兑换时以后端接收的 redeemed_at_micros 判定:未到开始时间拒绝为“兑换码未生效”,到达或超过截止时间拒绝为“兑换码已过期”。
  • 后台契约:AdminUpsertProfileRedeemCodeRequest 通过可空 startsAt / expiresAt 接收 RFC3339 时间,列表与保存响应同步返回这两个字段;后台页只负责输入、回填和显示,真正兑换判定留在后端事务路径。

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

profile_wallet_consumption_total

  • Rust 结构体:ProfileWalletConsumptionTotal
  • 源码:server-rs/crates/spacetime-module/src/runtime/active/profile.rs
  • 作用:用户历史消费泥点累计投影。已有投影时,只在 asset_operation_consume 负向流水成功落账时同事务按主键 O(1) 累加,退款不回减;用户详情按 user_id 主键读取。首次上线在停写维护窗口由 owner 管理接口调用 admin_initialize_profile_wallet_consumption_projections_and_return,全表扫描一次,为所有已有钱包流水的用户建立存量投影。维护遗漏或新用户缺行时,首次消费与管理员钱包详情读取都可按 profile_wallet_ledger.user_id 索引兜底重建一次;消费事务中的重建结果已包含当前流水,不再额外叠加当前金额。持有独立操作权限的管理员显式触发手动对账时,admin_reconcile_profile_wallet_consumption_and_return 扫描该用户权威流水并覆盖校准投影,记录 last_reconciled_by_admin_user_idlast_reconciled_at

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 不再把旧创作入口配置、公开作品聚合表或逐玩法 gallery view 作为连接池必需订阅。REQUIRED_CACHED_READ_MODEL_QUERIES 当前为空,user_account 仅作为认证兼容所需的可选缓存。新版 /creation 的公开内容通过 GET /api/editor/showcase/resources 读取编辑器精选 read model;旧作品、入口配置和玩法统计表只保留 schema 数据壳,不再形成订阅 cache、BFF 路由或前端契约。

退役前历史订阅清单

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

private asset_object 不进入长期订阅;安全判断通过受限 procedure 在服务端按主键或 (bucket, object_key) 索引读取事务内真相,避免全表复制、订阅失败和增量同步延迟把已登记私有对象误判为 legacy 未登记对象。

跨玩法公开作品列表 / 详情主读模型是 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 配置已退出 api-server 编译目标;不得为历史源码恢复 GENARRATIVE_RPG_LLM_WEB_SEARCH_ENABLEDGENARRATIVE_CREATION_AGENT_LLM_WEB_SEARCH_ENABLED

统一公开作品 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/active/settings.rs
  • 说明:这是现役账号级公共设置表,保存 music_volumeplatform_theme,不是旧玩法运行态数据壳。表结构与历史数据保持不变;鉴权后的 GET/PUT /api/runtime/settings 只能经 spacetime-client facade 调用 get_runtime_setting_or_defaultupsert_runtime_setting_and_return procedure 读写,不从连接订阅 cache 或前端本地状态伪造正式设置事实。

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