merge master into codex/wechat-mini-program-virtual-payment

This commit is contained in:
kdletters
2026-05-30 16:46:11 +08:00
119 changed files with 3345 additions and 428 deletions

View File

@@ -0,0 +1,83 @@
# 作品可见性后台管理 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 在后台增加统一作品可见性列表与修改能力,让管理员可以把已发布作品从公开 read model 中隐藏或恢复显示。
**Architecture:** 可见性仍以各玩法源表 `visible` 字段为真相源;新增 SpacetimeDB admin procedure 统一列出和更新各玩法作品可见性,`api-server` 只做鉴权、DTO 校验和 BFF 转发,后台前端新增简洁管理页。统一公开 read model 继续只消费 `visible=true` 的 source view不向公开契约暴露后台字段。
**Tech Stack:** Rust server-rs + SpacetimeDB module/procedure + spacetime-client bindings/facade + shared-contracts DTO + React admin-web TypeScript。
---
### Task 1: 文档契约补齐
**Files:**
- Modify: `docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`
- Modify: `docs/technical/【后端架构】统一公开作品ReadModel设计-2026-05-26.md`
- [ ] 在 API 路由分组中补充 `/admin/api/works/visibility`
- [ ] 在统一公开作品 ReadModel 文档中写清后台只能修改源表 `visible`,隐藏后不进入 `public_work_gallery_entry` / `public_work_detail_entry`
### Task 2: DTO 与后端路由
**Files:**
- Modify: `server-rs/crates/shared-contracts/src/admin.rs`
- Modify: `server-rs/crates/api-server/src/admin.rs`
- Modify: `server-rs/crates/api-server/src/app.rs` 或现有 admin module router 文件
- [ ] 增加 `AdminWorkVisibilityEntryPayload``AdminWorkVisibilityListResponse``AdminUpdateWorkVisibilityRequest``AdminUpdateWorkVisibilityResponse`
- [ ] 新增 `GET /admin/api/works/visibility` handler必须走 `require_admin_auth`
- [ ] 新增 `POST /admin/api/works/visibility` handler校验 `sourceType``profileId` 非空并转发到 SpacetimeDB facade。
### Task 3: SpacetimeDB runtime/procedure 与 facade
**Files:**
- Modify: `server-rs/crates/module-runtime/src/domain.rs`
- Create: `server-rs/crates/spacetime-module/src/runtime/admin_work_visibility.rs`
- Modify: `server-rs/crates/spacetime-module/src/runtime.rs`
- Modify: `server-rs/crates/spacetime-module/src/lib.rs`
- Modify: `server-rs/crates/spacetime-client/src/runtime.rs`
- Modify: `server-rs/crates/spacetime-client/src/mapper/runtime.rs`
- [ ] 增加 module-runtime typed input/output 类型。
- [ ] SpacetimeDB procedure 统一读取各玩法已发布源表/view并返回可见性列表。
- [ ] SpacetimeDB procedure 根据 `sourceType + profileId` 修改对应源表 `visible``custom-world` 同步 `custom_world_gallery_entry.visible``big-fish` 使用 `session_id``bark-battle` 使用 `work_id`
- [ ] spacetime-client 增加 list/update facade 和 mapper。
### Task 4: 后台前端页面
**Files:**
- Modify: `apps/admin-web/src/api/adminApiTypes.ts`
- Modify: `apps/admin-web/src/api/adminApiClient.ts`
- Create: `apps/admin-web/src/pages/AdminWorkVisibilityPage.tsx`
- Modify: `apps/admin-web/src/app/adminRoutes.ts`
- Modify: `apps/admin-web/src/app/AdminShell.tsx`
- Modify: `apps/admin-web/src/app/AdminApp.tsx`
- [ ] 增加 API 类型和 client 方法。
- [ ] 新增简洁表格页,显示玩法、标题、作者、公开码、更新时间、可见状态。
- [ ] 修改可见性时使用 `useAdminWriteConfirm` 确认。
- [ ] 接入后台导航和 route switch。
### Task 5: 生成绑定与验证
**Files:**
- Generated: `server-rs/crates/spacetime-client/src/module_bindings*`
- Generated: front-end shared bindings if generator updates them
- [ ] Run: `npm run spacetime:generate`
- [ ] Run: `npm run check:spacetime-schema`
- [ ] Run: `cargo check -p spacetime-client --manifest-path server-rs/Cargo.toml`
- [ ] Run: `cargo check -p api-server --manifest-path server-rs/Cargo.toml`
- [ ] Run: `npm run admin-web:typecheck`
- [ ] Run: `npm run check:encoding`
### Task 6: 提交并推送
**Files:**
- All changed files
- [ ] Inspect `git diff` and `git status --short --branch`
- [ ] Commit with message `feat: add admin work visibility controls`
- [ ] Push current branch `codex/visible-work-field`

View File

@@ -39,8 +39,21 @@
- `sort_time_micros`
- `detail_payload_json`
作品源表新增 `visible` 可见性字段,默认 `true``visible` 属于源表 / source view 过滤条件,不作为统一公开契约默认返回字段;当 `visible=false` 时,对应作品不得进入 `public_work_gallery_entry``public_work_detail_entry`
其中 `detail_payload_json` 只承载平台详情页展示扩展,不承载正式 runtime 配置、玩法规则或草稿真相。
## 后台可见性管理
后台通过独立接口管理已发布作品的源表可见性:
- `GET /admin/api/works/visibility`
- `POST /admin/api/works/visibility`
后台操作 key 使用统一的 `sourceType + profileId` 组合。`profileId` 在大多数玩法中对应作品 profile特殊玩法维持既有源表身份`big-fish` 对应 `session_id``bark-battle` 对应 `work_id``custom-world` 更新源表时必须同步 `custom_world_gallery_entry.visible`,避免兼容 gallery 缓存与统一公开 read model 出现可见性漂移。
该后台能力只修改源表 / source view 过滤事实,不把 `visible` 暴露到公开列表或公开详情契约。隐藏作品后,统一 `public_work_gallery_entry``public_work_detail_entry` 不再返回该作品;恢复显示后重新进入公开 read model。
## 来源与兼容
统一 public view 由现有玩法 source view 组装:
@@ -63,6 +76,8 @@
- 旧 view 保留,不删除。
- 旧 view 退到底层 source / 兼容职责。
-`public_work_*` view 是 `api-server` 公开列表 / 详情的统一主读模型。
- 各玩法 source view 只暴露 `visible=true` 的已发布作品;旧数据迁移默认补 `visible=true`,避免历史作品被误隐藏。
- 临时运行约束SpacetimeDB 2.2 下抓大鹅 `match_3_d_gallery_view``publication_status` 索引过滤在源表更新触发统一 view 刷新时可能初始化 panic为避免后台隐藏作品打爆 module instance统一 `public_work_*` view 暂不级联抓大鹅 source view抓大鹅公开入口先保留玩法专用路径。后续应以 source projection 表替代索引 view 后再重新并入统一 read model。
-`/api/runtime/<play>/gallery` 响应 shape 保持兼容,由 BFF mapper 把统一 cache 再映射回当前 DTO。
- 旧详情 / runtime / 点赞 / 游玩 / Remix 仍走玩法专用路径。

View File

@@ -51,7 +51,7 @@ npm run check:server-rs-ddd
路由树由 `server-rs/crates/api-server/src/app.rs` 统一构造。当前主要分组:
- 健康检查:`GET /healthz`
- 后台管理:`/admin/api/*`包括登录、概览、HTTP debug、埋点、表查询、创作入口开关、兑换码、邀请码、任务配置和充值商品配置。
- 后台管理:`/admin/api/*`包括登录、概览、HTTP debug、埋点、表查询、创作入口开关、作品可见性、兑换码、邀请码、任务配置和充值商品配置。
- 认证与账号:`/api/auth/*``/api/profile/me`包括短信、密码、微信、refresh session、多端会话和登出。
- 个人中心:`/api/profile/*`,包括钱包流水、任务、领奖、充值、反馈、邀请、兑换、存档、历史浏览和游玩统计。
- LLM 与语音:`/api/llm/*``/api/speech/volcengine/*`
@@ -70,6 +70,12 @@ npm run check:server-rs-ddd
需要新增路由时,先确认玩法入口配置和 tracking 分类,不要绕过 `app.rs` 的统一中间件、鉴权和入口开关。
### 认证态用户与会话摘要下发口径
- `AuthUserPayload` / `AuthUser` 只保留前端当前会用到的身份与绑定展示字段:`id``publicUserCode``displayName``avatarUrl``phoneNumberMasked``loginMethod``bindingStatus``wechatBound`
- `AuthSessionSummaryPayload` / `AuthSessionSummary` 只保留设备卡片与撤销需要的摘要字段:`sessionId``sessionIds``sessionCount``clientLabel``ipMasked``isCurrent``createdAt``lastSeenAt``expiresAt`
- 设备诊断信息(例如原始 `clientType` / `clientRuntime` / `clientPlatform` / `userAgent` / `miniProgramAppId` / `miniProgramEnv` / `deviceDisplayName`)不再默认下发到前端;若未来确需展示,优先单独加窄 DTO而不是把账号 / 会话快照恢复为全量对象。
## api-server 模块化演进规则
`api-server` 的长期方向是从超大 `app.rs` 和超大 handler 文件收敛为按能力组织的 HTTP/BFF Module。后续改造必须保持 HTTP route、DTO、error envelope、SpacetimeDB schema、前端行为和计费语义默认不变任何 contract 或 schema 变化都要先在当前文档中写清影响范围和迁移计划。
@@ -167,7 +173,7 @@ npm run check:server-rs-ddd
- Hyper3D / Rodin只保留后端安全代理和旧数据兼容Rodin 提交、状态、下载和响应解析归属 `platform-hyper3d``api-server/src/hyper3d_generation.rs` 只做路由、配置和错误 envelope 映射;新 Match3D 草稿和批量新增不再生成 GLB。
- 音频视觉小说专用音频路由保留VectorEngine Suno/Vidu provider 协议、任务提交/查询、音频 URL 提取、下载、MIME/extension 归一和 OSS put 请求准备归属 `platform-audio``api-server/src/vector_engine_audio_generation.rs` 只做路由、配置、计费、asset object confirm、entity binding 和错误 envelope 映射;拼图、抓大鹅和敲木鱼提示词生成音效入口暂时关闭,通用 `/api/creation/audio/*` 对这些目标返回 `410 Gone`。敲木鱼创作只接收上传 / 录音音频资产;未提供时由 `api-server` 写回内置默认木鱼音 `/wooden-fish/default-hit-sound.mp3`
- OSS私有 generated legacy path 进入浏览器前必须通过 `/api/assets/read-url` 换签;不要裸请求 `/generated-*`
- 外部 API 失败审计:外部供应商调用未成功时,`api-server` 必须发送 OTLP 失败事件并写入 `tracking_event`。VectorEngine 图片 provider 在 `platform-image` 内输出结构化日志和 `PlatformImageFailureAudit`,覆盖 `request_send``response_body``upstream_status``response_parse``missing_image``image_download` 阶段;`api-server` 只把该 audit 映射成 `external_api_call_failure``scope_kind = module``scope_id = provider``module_key = external-api`。metadata 固定包含 provider、endpoint、operation、failureStage、statusCode、statusClass、timeout、retryable、errorMessage、latencyMs、promptChars、referenceImageCount、imageModelrawExcerpt。入库优先复用 tracking outboxoutbox 不可写或保护阈值拒绝时回退同步写 SpacetimeDB不得新增前端兜底或在 SpacetimeDB reducer 内做外部 I/O。
- 外部 API 失败审计:外部供应商调用未成功时,`api-server` 必须发送 OTLP 失败事件并写入 `tracking_event`。VectorEngine 图片 provider 在 `platform-image` 内输出结构化日志和 `PlatformImageFailureAudit`,覆盖 `request_send``response_body``upstream_status``response_parse``missing_image``image_download` 阶段;`api-server` 只把该 audit 映射成 `external_api_call_failure``scope_kind = module``scope_id = provider``module_key = external-api`。metadata 固定包含 provider、endpoint、operation、failureStage、statusCode、statusClass、timeout、retryable、errorMessage、latencyMs、promptChars、referenceImageCount、imageModelrawExcerpt,以及在调用方可获得上下文时补充的 `userId`(触发者)和 `profileId`(草稿 / 作品 / 场景作用域)。图片生成入口应优先把 owner user id 和 profile id 透传到失败审计,不要只保留 provider 级聚合,否则很难按“谁触发、哪个作品触发”定位问题。入库优先复用 tracking outboxoutbox 不可写或保护阈值拒绝时回退同步写 SpacetimeDB不得新增前端兜底或在 SpacetimeDB reducer 内做外部 I/O。
- 外部生成运行记录:所有外部生成编排的完成态统一写入 `tracking_event``event_key = external_generation_run``scope_kind = module``scope_id = provider``module_key = external-generation`。metadata 固定包含 `runId``provider``operation``requestLabel``requestPayload``status``success``failureReason``providerRequestId``resultPayload``startedAtMicros``completedAtMicros``durationMs`。这类记录只用于运行审计和排障,不再走 `ai_task` 旧表。
## SpacetimeDB 表目录
@@ -257,6 +263,7 @@ npm run check:server-rs-ddd
- Rust 结构体:`BarkBattlePublishedConfigRow`
- 源码:`server-rs/crates/spacetime-module/src/bark_battle/tables.rs`
- 字段变更:`visible` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。
### `bark_battle_runtime_run`
@@ -293,6 +300,7 @@ npm run check:server-rs-ddd
- Rust 结构体:`BigFishCreationSession`
- 源码:`server-rs/crates/spacetime-module/src/big_fish/tables.rs`
- 索引:`by_big_fish_session_owner_user_id``by_big_fish_session_stage`。公开广场 view 使用 `by_big_fish_session_stage` 读取已发布会话,避免扫整表。
- 字段变更:`visible` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。
### `big_fish_event`
@@ -356,11 +364,13 @@ npm run check:server-rs-ddd
- 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` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。
### `custom_world_profile`
- Rust 结构体:`CustomWorldProfile`
- 源码:`server-rs/crates/spacetime-module/src/custom_world.rs`
- 字段变更:`visible` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。
### `custom_world_session`
@@ -415,6 +425,7 @@ npm run check:server-rs-ddd
- 返回类型:`Vec<JumpHopGalleryViewRow>`
- 源码:`server-rs/crates/spacetime-module/src/jump_hop.rs`
- 说明:跳一跳公开详情兼容投影,包含作品、路径和素材字段;统一公开详情主路径通过 `public_work_detail_entry` 消费该 view只保留平台详情页展示摘要。
- 字段变更:`visible` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。
### `wooden_fish_agent_session`
@@ -450,6 +461,7 @@ npm run check:server-rs-ddd
- 返回类型:`Vec<WoodenFishGalleryViewRow>`
- 源码:`server-rs/crates/spacetime-module/src/wooden_fish.rs`
- 说明:敲木鱼公开详情兼容投影,包含敲击物图案、背景环境图、主题返回按钮图、敲击音效和飘字配置;统一公开详情主路径通过 `public_work_detail_entry` 消费该 view只保留平台详情页展示摘要。
- 字段变更:`visible` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。
### `match3d_agent_message`
@@ -466,10 +478,12 @@ npm run check:server-rs-ddd
- Rust 结构体:`Match3DRuntimeRunRow`
- 源码:`server-rs/crates/spacetime-module/src/match3d/tables.rs`
### `match3d_work_profile`
### `match_3_d_work_profile`
- Rust 结构体:`Match3DWorkProfileRow`
- Rust accessor`match_3_d_work_profile`
- 源码:`server-rs/crates/spacetime-module/src/match3d/tables.rs`
- 兼容说明dev 现有 SpacetimeDB 元数据中的真实表名 / 索引名为 `match_3_d_work_profile``match_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` 表名补默认字段。
### SpacetimeDB view`match_3_d_gallery_view`
@@ -477,6 +491,7 @@ npm run check:server-rs-ddd
- 返回类型:`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` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。
### `npc_state`
@@ -663,6 +678,7 @@ RPG 创作入口的配置 ID 是 `rpg`,当前 `visible=true`、`open=true`
结构化创作和 RPG 的 LLM JSON 链路默认不启用 Responses `web_search`;只有在明确需要联网增强时,才通过 `GENARRATIVE_RPG_LLM_WEB_SEARCH_ENABLED``GENARRATIVE_CREATION_AGENT_LLM_WEB_SEARCH_ENABLED` 显式打开。否则未开通工具的上游会先吐自然语言再返回 `ToolNotOpen`,这类失败要按上游工具不可用处理,不要误判成模型返回结果解析失败。
统一公开作品 BFF 路由是 `GET /api/public-works``GET /api/public-works/{publicWorkCode}`,响应契约由 `shared-contracts::public_work``packages/shared/src/contracts/publicWork.ts` 共同维护。前端首期仍走 BFF HTTP不直接订阅 SpacetimeDB后续若允许浏览器直连订阅也只能订阅 `public_work_gallery_entry` / `public_work_detail_entry` 这类稳定公开 read model不能订阅 `puzzle_work_profile``custom_world_profile` 等源表后自行拼装列表。设计细节见 `docs/technical/【后端架构】统一公开作品ReadModel设计-2026-05-26.md`
- 字段变更:`visible` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。
### `quest_log`
@@ -715,6 +731,7 @@ RPG 创作入口的配置 ID 是 `rpg`,当前 `visible=true`、`open=true`
- 返回类型:`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` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。
### `story_event`
@@ -790,3 +807,4 @@ RPG 创作入口的配置 ID 是 `rpg`,当前 `visible=true`、`open=true`
- 返回类型:`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` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。

View File

@@ -203,7 +203,7 @@ UI 相关修改要重点验证:
npm run database:backup:oss -- --data-dir /stdb --stop-service spacetimedb.service
```
脚本会将数据目录打包成 `tar.gz`,上传到 `oss://<bucket>/<prefix>/<database>/<database>-<UTC时间>.tar.gz`。生产建议做冷备份:传入 `--stop-service spacetimedb.service`,脚本会在打包前停止服务、打包后恢复服务,再上传 OSS。`Genarrative-Stdb-Module-Publish` 默认也会在 `spacetime publish` 前执行同一脚本;备份失败会阻断 publish只有显式勾选 `SKIP_DATABASE_BACKUP`脚本参数 `--skip-backup` 才跳过。若业务不能接受停机窗口,应先规划 SpacetimeDB 原生快照或主备策略,不要直接在写入中的数据目录上做热拷贝并当作强一致备份。
脚本会将数据目录打包成 `tar.gz`,上传到 `oss://<bucket>/<prefix>/<database>/<database>-<UTC时间>.tar.gz`。生产建议做冷备份:传入 `--stop-service spacetimedb.service`,脚本会在打包前停止服务、打包后恢复服务,再上传 OSS。由于 OSS 上传可能受服务器带宽限制,`Genarrative-Stdb-Module-Publish` 默认使用 `DATABASE_BACKUP_MODE=async`:先在 publish 前用 `--defer-upload` 生成本地冷备份和 `.manifest.json`,随后继续执行 publish发布脚本退出前会用后台 `node ... --upload-archive <tar.gz>` 上传同一份发布前备份,不等待上传完成。发布脚本在校验 wasm 后、执行 `spacetime publish` 前会等待显式 `SPACETIME_SERVER_URL``/v1/ping` 就绪,默认最多等待 `60` 秒;如生产机器冷备份恢复 `spacetimedb.service` 较慢,可临时设置 `GENARRATIVE_STDB_PUBLISH_READY_TIMEOUT_SECONDS` 调整等待时间。需要强一致发布闸门时改用 `DATABASE_BACKUP_MODE=sync`(等价脚本参数 `--backup-mode sync`),备份会在 publish 前同步打包并上传,失败会阻断 publish确认已有其他备份窗口时才使用 `DATABASE_BACKUP_MODE=skip`(兼容脚本参数 `--skip-backup`。若业务不能接受停机窗口,应先规划 SpacetimeDB 原生快照或主备策略,不要直接在写入中的数据目录上做热拷贝并当作强一致备份。
生产环境变量模板在 `deploy/env/api-server.env.example`
@@ -283,7 +283,7 @@ OpenTelemetry 现阶段默认开启 OTLP traces / metrics / logs但本地日
- debug exporter / Rider 转发都会同时接收 traces、metrics 和 logs。
- api-server 会随 metrics 发送进程级指标:`process.memory.usage``process.memory.virtual``process.cpu.time``genarrative.process.cpu.usage_percent``process.thread.count``genarrative.process.memory.private`Windows 额外发送 `process.windows.handle.count`Linux 额外发送 `process.unix.file_descriptor.count`。这些指标只描述当前进程,不携带请求、用户或作品 label。
- HTTP 运行态补充发送 `genarrative.http.server.response_bodies.in_flight``genarrative.http.server.request_permits.available`,后者带低基数 `pool=default|gallery|detail|admin` label用于区分业务 handler / 背压 permit 是否仍被占用;拼图广场热点缓存补充发送 `genarrative.puzzle_gallery.cache.*` 指标,记录 fresh hit、stale hit、未命中、后台刷新开始 / 失败、重建耗时和预序列化 data JSON 字节数。
- 外部 API 失败统一发送 OTLP 并落库。当前 VectorEngine `gpt-image-2` 图片生成 / 编辑失败由 `platform-image` provider 输出低基数字段结构化日志,字段包括 provider、endpoint、failure_stage、status、status_class、timeout、retryable、latency_ms、prompt_chars、reference_image_count、image_model 和 raw_excerpt`api-server` 再记录指标 `genarrative.external_api.failures{provider,failure_stage,status_class,retryable}`,并写入 `tracking_event``event_key = external_api_call_failure``module_key = external-api``scope_kind = module``scope_id = provider`。排障时先按 provider / failureStage 聚合,再结合 request 日志和上游响应 excerpt 判断是限流、超时、解析失败还是未返回图片。
- 外部 API 失败统一发送 OTLP 并落库。当前 VectorEngine `gpt-image-2` 图片生成 / 编辑失败由 `platform-image` provider 输出低基数字段结构化日志,字段包括 provider、endpoint、failure_stage、status、status_class、timeout、retryable、latency_ms、prompt_chars、reference_image_count、image_model 和 raw_excerpt`api-server` 再记录指标 `genarrative.external_api.failures{provider,failure_stage,status_class,retryable}`,并写入 `tracking_event``event_key = external_api_call_failure``module_key = external-api``scope_kind = module``scope_id = provider`调用方能拿到身份上下文时,失败事件还会在行级 `user_id` / `owner_user_id` / `profile_id``metadata_json.userId` / `metadata_json.profileId` 中记录触发者与草稿 / 作品作用域。排障时先按 provider / failureStage 聚合,再下钻 userId / profileId最后结合 request 日志和上游响应 excerpt 判断是限流、超时、解析失败还是未返回图片。
- SpacetimeDB 观测分为两类procedure / reducer 调用继续用 `genarrative.spacetime.procedure.*`,订阅本地 cache 读使用 `genarrative.spacetime.read.*``read=list_puzzle_gallery` 表示拼图广场当前从 `puzzle_gallery_card_view` 本地 cache 读取,不再每个 HTTP 请求调用 `list_puzzle_gallery` procedure。
- 本地 Windows 直连压测的内存高水位要结合 K6 VU / 连接数解释。250 RPS 下过高 `PREALLOCATED_VUS` 可能让 300 个本地 Established 连接把 `api-server` private memory 瞬时推到 GB 级,且 `/healthz` 小响应也能复现;若压测结束后回落、`response_bodies.in_flight` 和背压 permit 未显示业务积压,应优先按连接 / 发送链路高水位处理,而不是判断为 SpacetimeDB 或 JSON 缓存泄漏。
- Rider 的 Logs 面板只展示 log event 自身字段,不会自动展开父 span 的全部 attributes请求完成日志会直接带 `request_id``http.request.method``http.route``url.scheme``url.path``http.response.status_code``status_class``latency_ms``slow_request`,完整链路继续到 Traces 面板按 trace/span 查看。
@@ -342,7 +342,7 @@ cargo test -p platform-auth --manifest-path server-rs/Cargo.toml aliyun_send_sms
个人任务首版 scope 仅支持 `user`。后台、RPG、大鱼吃小鱼、Visual Novel、Story、Combat 等特定链路按 tracking 中间件排除规则处理;作品游玩统一使用 `work_play_start`
外部 API 失败审计复用 `tracking_event`,不新增表。失败事件优先写入本机 tracking outbox再由后台 worker 批量落库;如果 outbox 因权限、磁盘或保护阈值不可写,会回退同步直写 SpacetimeDB。`metadata_json` 包含 endpoint、operation、failureStage、statusCode、statusClass、timeout、retryable、errorMessage、latencyMs、promptChars、referenceImageCount、imageModelrawExcerpt。常用查询
外部 API 失败审计复用 `tracking_event`,不新增表。失败事件优先写入本机 tracking outbox再由后台 worker 批量落库;如果 outbox 因权限、磁盘或保护阈值不可写,会回退同步直写 SpacetimeDB。`metadata_json` 包含 endpoint、operation、failureStage、statusCode、statusClass、timeout、retryable、errorMessage、latencyMs、promptChars、referenceImageCount、imageModelrawExcerpt、userId 和 profileId其中 `userId` 是触发生成的用户,`profileId` 是调用方传入的草稿 / 作品 / 场景作用域,入口拿不到上下文时允许为空。常用查询:
```sql
SELECT event_id, scope_id AS provider, metadata_json, occurred_at
@@ -352,6 +352,25 @@ ORDER BY occurred_at DESC
LIMIT 50;
```
按失败阶段、触发者和作品作用域聚合时:
```sql
SELECT
json_extract(metadata_json, '$.failureStage') AS failure_stage,
user_id,
profile_id,
COUNT(*) AS failures,
MIN(occurred_at) AS first_seen,
MAX(occurred_at) AS last_seen
FROM tracking_event
WHERE event_key = 'external_api_call_failure'
GROUP BY failure_stage, user_id, profile_id
ORDER BY failures DESC, last_seen DESC
LIMIT 100;
```
VectorEngine `request_send``timeout = true` 的记录表示 `reqwest::Error::is_timeout()` 判定为超时,常见于连接、发送请求体、等待上游首包或上游长时间无响应;`errorSource = client error (SendRequest)` 是 Hyper 发送请求阶段的错误来源标签,不等于最终根因。若 `statusCode` 为空,应优先查同一时间窗口的 `api-server` request 日志、Nginx / 出口网络、VectorEngine 可用性和请求体大小;若已有 `502``429 moderation_blocked` 等状态码,则按上游网关或内容审核失败单独处理,不要和传输超时混为一类。
tracking outbox 默认配置:
```env

View File

@@ -276,7 +276,7 @@ RPG / 拼图等运行态存档仍以 `/api/profile/save-archives` 的后端列
- 创作 Tab 表单:填写作品标题、简介、主题 / 竞技背景描述、玩家形象描述、对手形象描述、拟声词和难度。拟声词支持换行、逗号、顿号、斜杠或竖线分隔;未手动编辑时随主题 / 形象描述自动重算,手动编辑后保持创作者自定义。
- 草稿编译:`POST /api/creation/bark-battle/drafts` 写入配置 JSON返回包含 `draftId`、稳定 `workId``configVersion``rulesetVersion` 的草稿结果。
- 生成页:`bark-battle-generating` 自动并行产出玩家形象、对手形象和竞技背景三图;前端生成页 UI 和其它玩法保持同一圆环主视觉,`media/create_bg_video.mp4` 作为固定全屏页面背景层循环静音播放,主进度圆环居中展示总进度,只保留当前步骤名称和当前步骤进度,不再渲染三行槽位列表。视频层需要显式触发播放。三图都走 Bark Battle 专用后端生图接口 `POST /api/creation/bark-battle/images/generate`,由后端按 `player-character``opponent-character``ui-background` 分别拼装正式提示词、写入 `generated-bark-battle-assets` 私有资产前缀并返回实际 prompt。玩家 / 对手形象提示词必须保持用户形象描述,不强行注入狗相关主体,并要求正面、单个完整形象和透明背景。部分失败也继续进入结果页。
- 结果页:围绕三图槽位展示错误态与已生成结果,只保留单槽重试、重新生成和上传,不再提供一次生成按钮、音频配置入口或排名配置。
- 结果页:围绕三图槽位展示错误态与已生成结果,只保留单槽重试、重新生成和上传,不再提供一次生成按钮、音频配置入口或排名配置;生成回写 `partial_failed` 时作品架不再显示整卡“生成中”遮罩,由结果页槽位错误承接失败
- 手动上传:结果页通过平台资产直传 `/api/assets/direct-upload-tickets``/api/assets/objects/confirm` 写入私有资产,再把返回的历史 generated 路径写回草稿配置。
- 发布:结果页确认后必须携带草稿返回的同一个 `workId` 和结果页最终 `publishedSnapshot` 调用 `POST /api/creation/bark-battle/works/publish`SpacetimeDB 发布态的 `config_json` 必须使用该最终快照works summary 若拿到 `publishedSnapshotJson` 也优先使用最终快照映射封面三图。发布成功后先进入统一作品详情页,再由详情页进入正式 runtime缺少 `workId` 的旧草稿状态需要重新生成草稿。
- 作品架Bark Battle 草稿 / 已发布列表优先读取后端 `/works`,但创建、生成完成、保存或发布后的本地摘要必须在后端 read model 尚未回读到同 `workId` 前继续保留;创作中心作品架同时接入 pending shelf 兜底,避免 ready 且三图齐全的草稿在刷新窗口期从“我的草稿 / 已发布”中消失。