6e5cf195f8
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
真实库实证:51 条 published 时**最老的 3 条**(含最关键的 A0/A1/A11 母版与主干)在公开目录里不可见
(AGC 共创页显示「已检查最新 48 个作品(1 页)」;数据本身没丢)。根因:handler 写死
`limit: MAX_LIST_LIMIT(48)`(并忽略客户端 `limit`),事务过滤排序后 `truncate(limit)`,**既无 has_more
也无游标**。
- **页大小**:默认 **48**(保持既有 AGC 共创页首屏语义——48 只该是默认值而非硬上限)、上限 **100**
(单响应体量);`0` 取默认、超界**截断**(不报错);api-server 与事务共用同一归一函数。
- **游标**:`"{created_at_micros}:{game_id}"`(复用仓库既有 `"{micros}:{id}"` 惯例,与主题列表同构);
排序键 `created_at` **倒序** + `game_id` **升序**兜底(与改前比较器逐字同序 ⇒ 首页顺序不变;全序 ⇒
翻页不重不漏);**先过滤再切页**;「多取一条」判 has_more;`nextCursor` 为真值、**末页为 `null`**。
- **非法游标**:模块产出 `CATALOG_INVALID_CURSOR: 目录游标格式无效` → api-server 新增 `CATALOG_` 前缀分支
(**排在 owner 不匹配 / 不存在 / 状态 等子串分支之前**)→ **400 + 稳定码 `CATALOG_INVALID_CURSOR`**,
并纳入「码由模块真实文案可达」的枚举测试(防上次那种不可达码);未登记的 `CATALOG_` 码兜底
`CATALOG_ERROR`(400)。
- **响应形状不变**:仍是 `{ games, nextCursor }`(`nextCursor` 从恒 `null` 变成真值);抽命名构建器
`public_games_payload` 并登记 parity(构建器 18→**19**,`mustEmit: ['games','nextCursor']`)。
- **生成绑定**:`game_distribution_public_game_list_input_type.rs`(+`cursor`)、
`game_distribution_game_list_procedure_result_type.rs`(+`next_cursor`);client facade 改返回
`(games, next_cursor)`,mapper 同步。
- **文档**(同批):主规范 HTTP 表 `GET /games` 行改为真游标分页;创作者主页工程设计两处「最多 48 / 不分页」
改写(并注明前端尚未消费游标);`docs/README.md` 索引行同步。
- **测试 +15**:纯函数 9(**51 条能翻到最老那条**、恰好整页不产生空游标、同 `created_at` 用 `game_id` 兜底、
**先过滤再切页**、游标指向页内最后一条、limit 0/超界、非法游标 7 种);事务结构断言 1;api-server 5
(形状、`limit` 默认与截断、非法游标 400 + 稳定码、码可达性枚举、**51 条走真实 handler 链路翻页且
未公开/已软删/无公开版本三种不可见行不出现**)。
门禁:wasm build 0;`cargo check --all-targets` 0;`cargo test -p module-game-distribution` **143 passed**;
`-p spacetime-module` **307 passed / 1 ignored**;`-p spacetime-client` 44 passed;`-p api-server game_distribution`
**112 passed**;DTO parity 0(66 组 / **19** 构建器 / 15 手拼类型);`check:spacetime-schema` 0(98 表);
`check:encoding` 0(5548 files);`cargo fmt --all --manifest-path server-rs/Cargo.toml -- --check` 0;
`git diff --check` 0。
api-server 主工程 crate 占位说明
日期:2026-04-20
1. crate 职责
api-server 是新后端的 Axum 主工程 crate,后续负责:
main.rs启动入口Router装配with_state共享状态注入- 中间件挂载
/healthz、/api/*、SSE 与静态资源兼容层装配- 由
../../scripts/dev.ps1与../../scripts/dev.sh驱动的本地开发启动链路 - 由
../../scripts/test.ps1与../../scripts/test.sh驱动的本地测试链路 - 由
../../scripts/check.ps1与../../scripts/check.sh驱动的本地统一检查链路 - 由
../../scripts/smoke.ps1与../../scripts/smoke.sh驱动的本地启动与协议冒烟链路
2. 当前阶段说明
当前目录已经完成以下基础骨架:
- 目录占位
Cargo.tomlsrc/main.rssrc/app.rssrc/state.rssrc/config.rs- 基础
TraceLayer挂载 - 接入
shared-logging完成tracing subscriber初始化 - 接入
POST /api/auth/entry首版密码登录链路 - 接入
POST /api/auth/password/change登录后修改密码链路 - 接入
POST /api/auth/password/reset手机验证码重置密码链路 - 接入
POST /api/assets/direct-upload-tickets直传票据接口 - 接入
GET /api/auth/me当前用户查询链路 - 接入
POST /api/auth/refreshrefresh token 轮换链路 - 接入
POST /api/auth/logout当前设备退出链路 - 接入
POST /api/assets/objects/confirm上传完成确认链路 - 接入
GET /api/auth/login-options登录方式探测链路 - 接入
POST /api/auth/phone/send-code手机验证码发送链路 - 接入
POST /api/auth/phone/login手机验证码登录链路 - 接入
GET /api/auth/wechat/start微信授权起跳链路 - 接入
GET /api/auth/wechat/callback微信回调换取系统登录态链路 - 接入
POST /api/auth/wechat/bind-phone微信待绑定账号补绑手机号链路 - 接入
POST /api/assets/objects/bind已确认对象绑定业务实体槽位链路 - 接入
POST /api/assets/sts-upload-credentials禁用式 STS 写权限 contract - 接入
POST /api/assets/character-visual/generate - 接入
GET /api/assets/character-visual/jobs/{task_id} - 接入
POST /api/assets/character-visual/publish - 接入
GET /api/assets/character-animation/templates - 接入
POST /api/assets/character-animation/import-video - 接入
GET /api/assets/character-workflow-cache/{character_id} - 接入
POST /api/assets/character-workflow-cache - 接入
POST /api/assets/character-animation/generate - 接入
GET /api/assets/character-animation/jobs/{task_id} - 接入
POST /api/assets/character-animation/publish - 生成资产读取统一走
/api/assets/read-url或 asset object projection
后续与本 crate 直接相关的任务包括:
- 接入统一日志与 tracing
- 接入
request_id - 接入统一错误处理中间件
- 接入 response envelope
- 接入
/healthz - 接入
/api/auth/entry - 接入
/api/assets/direct-upload-tickets - 接入
/api/auth/me - 接入
/api/auth/refresh - 接入
/api/auth/logout - 接入
/api/assets/objects/confirm - 接入
/api/auth/login-options - 接入
/api/auth/phone/send-code - 接入
/api/auth/phone/login - 接入
/api/auth/wechat/start - 接入
/api/auth/wechat/callback - 接入
/api/auth/wechat/bind-phone - 接入
/api/assets/objects/bind - 接入
/api/assets/sts-upload-credentials - 接入
character-visual generate / jobs / publish第一批 OSS 主链 - 接入
character-animation templates / import-video第一批 OSS 草稿链路 - 接入
character-workflow-cache get / save第一批 OSS JSON 草稿链路 - 接入
character-animation generate / jobs / publish第一批 OSS 主链 - 生成资产读取统一走
/api/assets/read-url或 asset object projection
当前 tracing 约定:
- 进程启动时通过
shared-logging统一初始化tracing subscriber。 - 默认日志过滤器来自
GENARRATIVE_API_LOG,未提供时回落到info,tower_http=info。 - HTTP 访问日志统一通过 Axum 路由层的
TraceLayer输出,后续request_id、响应头与错误中间件继续在同一层扩展。 - 本地启动器
npm run dev:api-server和完整联调入口npm run dev会在保留终端实时输出的同时,把同一份cargo/api-server输出持久化到logs/api-server/。如需固定文件或目录,可设置GENARRATIVE_API_SERVER_LOG_FILE或GENARRATIVE_API_SERVER_LOG_DIR。
当前 request context 约定:
- 中间件优先读取来访
x-request-id,未提供时生成新的 UUID。 request_id会统一写入请求extensions与请求头,供 tracing、错误处理中间件和响应头层复用。- 最终响应会回写同一个
x-request-id,保证调用方、日志链路和后续 envelopemeta.requestId可对齐。
当前错误处理中间件约定:
- 对 Axum 默认产生的空
4xx / 5xx响应,统一归一化为 legacy 兼容 JSON 错误体:{ error: { code, message, details? } }。 - 已经带
content-type的业务错误响应不会被覆盖,避免抢走后续 response envelope 的职责。 - 统一错误日志会复用当前请求的
request_id,便于后续和响应头、envelope 元信息串联。
当前 response envelope 约定:
RequestContext已记录request_id、请求开始时间、默认operation与 envelope 协商结果。json_success_body(...)/json_error_body(...)会根据x-genarrative-response-envelope自动在“裸数据 / 标准 envelope / legacy error + meta”之间切换。meta.apiVersion、meta.requestId、meta.routeVersion、meta.operation、meta.latencyMs、meta.timestamp已按当前前端契约生成,响应头回写仍留给后续独立任务。
当前基础响应头约定:
- 所有响应都会回写
x-request-id。 - 所有响应都会回写固定的
x-api-version,值来自shared_contracts::api::API_VERSION,当前为2026-06-16,并与 bodymeta.apiVersion保持一致。 - 所有响应都会回写
x-route-version,当前阶段默认与x-api-version保持一致,后续再按路由粒度细分。 - 所有响应都会回写
x-response-time-ms,值来源于RequestContext内记录的请求开始时间。
当前 /healthz 约定:
- 路径固定为
/healthz。 - 裸响应继续返回
{ ok: true, service: "genarrative-node-server" },保持与当前 Node 工程兼容。 - 当请求携带
x-genarrative-response-envelope时,/healthz会返回标准 success envelope。 x-request-id、x-api-version、x-route-version、x-response-time-ms会在/healthz响应中一并回写。
当前本地检查链路约定:
../../scripts/check.ps1与../../scripts/check.sh统一串联cargo fmt --all --check、cargo clippy、cargo check、cargo test。- 默认检查整个
server-rsworkspace,确保后续多 crate 扩容时仍然保持统一口径。 - 当只需聚焦单个 crate 时,可通过
-Package或SERVER_RS_CHECK_PACKAGE收窄clippy / check / test目标。 cargo fmt --all --check仍固定覆盖整个 workspace,避免多 crate 下格式基线漂移。
当前本地 smoke 链路约定:
../../scripts/smoke.ps1与../../scripts/smoke.sh会先构建api-server,再拉起临时本地进程完成冒烟验证。- smoke 当前固定校验
/healthz的 raw 响应、envelope 响应以及x-request-id、x-api-version、x-route-version、x-response-time-ms头。 - smoke 通过后,可作为“Axum 服务可独立启动且基础 contract 可联通”的本地自动化证据。
3. 边界约束
api-server负责 HTTP、SSE、Cookie、Header、路由与协议装配。- 业务逻辑优先通过独立模块 crate 暴露能力,再由主工程组合。
- 外部副作用通过
platform-auth、platform-oss、platform-llm与各模块 crate 的应用层完成。 - 不把领域规则直接堆在 handler 中。
- 当前密码登录由
module-auth负责用例编排,api-server只负责请求解析、JWT 签发与 refresh cookie 写回。 - 当前
/api/auth/me复用现有 Bearer JWT 中间件与module-auth用户快照查询,不直接绕过模块边界读取内部状态。 - 当前
/api/auth/refresh复用module-auth的 refresh session 轮换能力,api-server负责 refresh cookie 读取、失败清理与 access token 重签。 - 当前
/api/auth/logout复用module-auth的当前会话吊销与用户版本递增能力,api-server负责 Bearer JWT、refresh cookie 读取与清理 cookie 回写。 - 当前
/api/assets/objects/confirm先由platform-oss完成私有HEAD Object校验,再通过spacetime-client调用spacetime-module的对象确认持久化入口。 - 当前
/api/assets/objects/bind只绑定已确认对象到业务实体槽位,不访问 OSS,不创建悬空asset_object_id。 - 当前手机号登录与微信登录都复用
module-auth的进程内认证仓储,api-server负责请求解析、场景判定、系统 JWT 签发与 refresh cookie 写回。 - 当前微信回调不会把第三方 token 直接透传给前端或 SpacetimeDB,而是统一换成系统签发的 JWT。
- 当前
/api/assets/sts-upload-credentials按“服务器上传、Web 只下载”口径固定返回403,不向浏览器下发 OSS 写权限。 - 当前
/api/assets/character-visual/*第一批只保证现役接口 contract、OSS 草稿/正式对象、asset_object与asset_entity_binding主链可用;真实图片模型、workflow cache 与本地角色覆盖写回仍在后续阶段。 - 当前
/api/assets/character-animation/import-video第一批只接受data:video/*;base64,...并写入 OSS 草稿区,不读取旧本地public/路径,也不创建正式asset_object。 - 当前
/api/assets/character-workflow-cache/*第一批只把工作流 JSON 草稿写入 OSS,不迁移历史本地缓存,也不创建正式asset_object。 - 当前
/api/assets/character-animation/generate第一批只用 Rust 占位产物打通AiTaskService + OSS草稿链;image-sequence写 SVG 帧,视频类策略优先复用参考视频或仓库内可播放占位视频,不代表真实上游视频模型已完成迁移。 - 当前
/api/assets/character-animation/publish会把前端提交帧、动作级 manifest 与总 manifest 写入 OSS,并只把总 manifest 确认为asset_object后绑定到character / animation_set。 /generated-*只作为legacyPublicPath/ OSS object key 标识,读取必须通过/api/assets/read-url或业务投影中的签名读 URL。