This commit is contained in:
2026-05-16 22:59:18 +08:00
69 changed files with 11457 additions and 2899 deletions
@@ -1 +0,0 @@
C:/proj/Genarrative/.hermes/skills/behavior-driven-development
+1
View File
@@ -0,0 +1 @@
../../.hermes/skills/behavior-driven-development/
File diff suppressed because it is too large Load Diff
+158 -56
View File
@@ -1,20 +1,40 @@
# 开发工作流
更新时间:`2026-05-15`
> 用途:给本地 Hermes 和开发人员提供统一的开发、测试、提交流程。具体命令以 `package.json`、`server-rs/Cargo.toml`、`AGENTS.md` 和相关 `docs/` 最新文档为准。
## 标准流程
## 标准任务流程
```text
同步代码 -> 读取 AGENTS.md -> 读取 .hermes/shared-memory -> 查当前 docs -> 小步实现 -> 本地验证 -> 更新 docs / .hermes -> 提交
同步代码 读取 AGENTS.md 读取 .hermes/shared-memory → 查找/完善 docs → 制定计划 → 小步实现 本地验证 更新文档/记忆 → 提交
```
当前 `docs/` 已压缩为少量融合文档。复杂任务优先读:
## 建议启动方式
1. `docs/README.md`
2. `docs/【项目基线】当前产品与工程约束-2026-05-15.md`
3. `docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`
4. `docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`
5. `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`
在项目根目录启动 Hermes
```bash
cd /path/to/Genarrative
hermes
```
在本机当前常见路径为:
```bash
/home/dsk/workspace/Genarrative
```
其他开发者以自己本地实际路径为准,不要把个人绝对路径写入共享文档作为通用规则。
## 开发前检查清单
- [ ] 当前分支是否正确
- [ ] 是否已拉取最新代码
- [ ] 是否阅读 `AGENTS.md`
- [ ] 是否阅读 `.hermes/shared-memory/` 相关文件
- [ ] 是否阅读 `README.md` 中的运行和检查命令
- [ ] 是否阅读 `docs/README.md` 及任务相关分类 README
- [ ] 是否存在足够具体的 PRD / 设计 / 技术文档
- [ ] 是否明确测试、验收和文档更新方式
## 本地运行命令
@@ -24,108 +44,190 @@
npm install
```
完整联调:
完整联调开发环境
```bash
npm run dev
```
该命令会启动:
- SpacetimeDB standalone
- Rust `api-server`
- 主站 Vite
- 后台 Vite
开启自动刷新:
```bash
npm run dev -- --watch
```
watch 模式只由外层调度器自动处理后端侧刷新:`spacetime-module` 改动后重新发布模块但不重启 standalone 宿主,`api-server` 改动后重启 Rust 进程。主站 Vite 与后台 Vite 的源码变化交给 Vite 自身 HMR,避免外层 watcher 监听到依赖缓存或临时文件后循环重启。
非 watch 模式下,`npm run dev` 终端支持输入 `rs spacetime``rs api-server``rs web``rs admin-web``rs all`。其中 `rs spacetime` 只会重新发布 `spacetime-module`,不会重启 standalone 宿主;其他模块仍按进程重启。
单独启动 SpacetimeDB
```bash
npm run dev:spacetime
```
单独启动 Rust API server
```bash
npm run dev:api-server
```
单独启动前端:
```bash
npm run dev:web
```
单独启动 Rust API server
单独启动后台管理前端
```bash
npm run api-server
npm run dev:admin-web
```
后台前端:
`npm run dev:api-server` 会保留终端实时输出,并把同一份输出持久化到 `logs/api-server/api-server-<timestamp>.log`。完整联调入口 `npm run dev` 启动的 Rust `api-server` 使用同一套日志规则。如需改写路径,可设置 `GENARRATIVE_API_SERVER_LOG_FILE`;如只改目录,可设置 `GENARRATIVE_API_SERVER_LOG_DIR`
查看本地 Rust/SpacetimeDB 日志:
```bash
npm run dev:spacetime:logs
```
后台管理前端:
```bash
npm run admin-web:dev
npm run admin-web:build
npm run admin-web:typecheck
```
SpacetimeDB bindings
SpacetimeDB bindings 生成
```bash
npm run spacetime:generate
```
## 常用检查
## 常用检查命令
- 后端通用用户行为埋点统一通过 `record_tracking_event_and_return` procedure、`SpacetimeRuntimeClient::record_tracking_event(...)` 与 api-server `tracking` 中间件写入 `tracking_event` / `tracking_daily_stat`;后台、RPG、大鱼吃小鱼、Visual Novel、Story、Combat 默认排除;作品级游玩埋点统一使用 `work_play_start`,详细事件清单见 `docs/technical/BACKEND_TRACKING_EVENT_COVERAGE_2026-05-09.md`
编码检查:
```bash
npm run check:encoding
npm run check:spacetime-schema
npm run check:server-rs-ddd
npm run lint:eslint
npm run typecheck
npm run test
npm run build
npm run check:content
```
综合检查
ESLint
```bash
npm run lint:eslint
```
类型检查:
```bash
npm run typecheck
```
综合 lint
```bash
npm run lint
```
测试:
```bash
npm run test
```
生产构建:
```bash
npm run build
```
内容检查:
```bash
npm run check:data
npm run check:overrides
npm run check:smoke
npm run check:content
```
全量检查:
```bash
npm run check
```
视觉小说门禁
DDD 边界检查
```bash
npm run check:visual-novel-vn11
npm run check:visual-novel-vn12
npm run check:server-rs-ddd
```
## 后端默认验证
## 后端相关默认验证
后端代码修改后按范围选择
后端修改后,按 DDD 文档中的验收命令执行。涉及 API smoke 时
- `cargo test -p <crate> --manifest-path server-rs/Cargo.toml`
- `cargo check -p api-server --manifest-path server-rs/Cargo.toml`
- `cargo check -p spacetime-client --manifest-path server-rs/Cargo.toml`
- `cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml`
- `npm run check:server-rs-ddd`
- `npm run api-server` 后请求 `/healthz`
- 使用 `npm run dev:api-server` 重新拉起后端。
- 禁止使用 `npm run api-server:maincloud``npm.cmd run api-server:maincloud` 或任何 `GENARRATIVE_SPACETIME_MAINCLOUD_*` 口径;这些只属于历史残留。
- 检查 `/healthz`
- 执行对应自动测试。
- 涉及 SpacetimeDB 表、reducer、procedure、row shape 或绑定变化时,同步更新 `migration.rs`、表目录和生成绑定。
- SpacetimeDB 已有表新增字段必须放在 Rust 表结构体最后,并设置明确默认值;需要修改字段名时,先询问用户并确认迁移计划,再同步更新 `server-rs/crates/spacetime-module/src/migration.rs`、表目录和生成绑定。
- 修改 SpacetimeDB schema 后运行 `npm run check:spacetime-schema`,用自动检查拦截缺 default、插入中间、字段删除/改名/重排/改类型,以及漏改迁移、表目录或绑定。
涉及 SpacetimeDB table、reducer、procedure、row shape 或 bindings 时,还必须运行
关键文档
```bash
npm run spacetime:generate
npm run check:spacetime-schema
```
- `docs/technical/CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.md`
- `docs/technical/SERVER_RS_DDD_FULL_REFACTOR_2026-04-28.md`
- `docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md`
- `docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md`
- `docs/technical/SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md`
- `docs/technical/SPACETIMEDB_TABLE_CATALOG.md`
- `docs/technical/MAINCLOUD_REFERENCE_REMOVAL_POLICY_2026-05-06.md`
禁止使用旧 `api-server:maincloud` 或任何 `GENARRATIVE_SPACETIME_MAINCLOUD_*` 口径。
## 前端相关默认验证
## 前端默认验证
前端修改后按范围选择:
前端修改后,应根据修改范围选择:
- `npm run check:encoding`
- `npm run lint:eslint`
- `npm run typecheck`
- `npm run test -- <具体测试文件>`
- `npm run test`
- 页面交互 smoke
- 移动端视口检查
UI 相关修改重点检查
前端原则
- 390px 左右移动端宽度不横向溢出
- 输入法弹出时平台画布不被压缩
- 弹窗、抽屉和独立面板没有实现成当前面板下方展开
- UI 不包含默认规则说明长文
- 私有图片和音频不裸请求 `/generated-*`
- 移动端优先,再兼容网页端
- 页面只展示后端返回的状态,不自行计算结论型业务状态
- 创作中心入口配置事实源在 SpacetimeDB,通过 `GET /api/creation-entry/config` 下发;前端只在 `platformEntryCreationTypes.ts` 做展示派生,api-server 路由熔断也使用同一份配置,禁止恢复前端硬编码入口配置文件
- 优先复用现有面板、抽屉、弹窗,不新建独立大系统
- 不在 UI 中默认写功能说明类文本
- 弹出独立面板的交互不要实现成在当前面板下方追加内容。
## 文档更新
## 文档更新规则
- 工程修改要同步更新当前 `docs/` 文档。
- 新增稳定知识优先合并进现有 4 份文档;只有现文档无法容纳时才新增带 `【标签名】` 的 Markdown
- `.hermes/shared-memory/` 只记录高频、长期、团队共享的摘要和索引。
- 阶段性流水账、一次性计划和已关闭 TODO 不再作为长期仓库文档依据。
- 工程修改要同步更新对应文档。
- 如果没有现文档,新文档统一放入 `docs/` 下合适分类
- `.hermes/shared-memory/` 只记录高频、长期、团队共享的摘要和索引,不替代完整 PRD/技术文档
- 如果 `.hermes/shared-memory/` 与代码或 `docs/` 冲突,以代码和最新 `docs/` 为准,并同步修正共享记忆。
## 提交前建议让 Hermes 执行
```text
请检查当前 git diff,指出:
1. 是否违反 AGENTS.md 或 .hermes/shared-memory 约定;
2. 是否需要补充 docs
3. 是否有长期知识需要写入 .hermes/shared-memory
4. 建议的测试命令和提交信息。
```
File diff suppressed because it is too large Load Diff
+70 -34
View File
@@ -1,64 +1,100 @@
# 团队协作约定
更新时间:`2026-05-15`
> 用途:约定 3 名开发人员在各自本地 Hermes 中协作开发、共享项目记忆的方式。
## 基本模式
- 每位开发人员在自己的电脑上使用本地 Hermes。
- 团队共享内容放在仓库 `.hermes/``docs/` 中,通过 Git 同步
- 不共享个人 `~/.hermes` 目录、密钥、会话、Token、Cookie、认证文件、本地私密路径或构建产物
- 每位开发人员本地拉取同一个项目仓库,独立修改代码、运行测试、提交分支
- 团队共享内容优先放在本仓库 `.hermes/` `docs/` 中,通过 Git 同步
- 不共享个人 `~/.hermes` 目录。
## 共享与禁止共享
推荐共享:
- `.hermes/shared-memory/` 团队级长期记忆
- `.hermes/plans/` 阶段性实施计划
- `.hermes/todos/` 已确定需要执行、但尚未进入实施的共享 TODO 计划
- `.hermes/skills/` 未来可复用仓库级 skills
- `docs/` 中 PRD、设计、技术、经验、审计、查询手册
- `AGENTS.md` 项目级 Agent 约束
禁止提交:
- 个人 `~/.hermes/config.yaml`
- 个人 `~/.hermes/.env`
- 个人 `~/.hermes/sessions/`
- API Key、Token、Cookie、认证文件
- 个人本地私密路径和个人隐私信息
- 构建产物、日志、缓存、数据库 dump
## 开发前
1. 拉取最新代码。
2. 阅读 `AGENTS.md`
3. 阅读 `.hermes/shared-memory/` 中与任务相关的文件。
4. 阅读 `docs/README.md` 4 份当前融合文档中与任务相关的部分
5. 如果文档不足以指导编码,先补充或修正文档。
当前文档入口:
- `docs/【项目基线】当前产品与工程约束-2026-05-15.md`
- `docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`
- `docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`
- `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`
4. 阅读 `docs/README.md`任务相关分类 README
5. 阅读对应 PRD、设计、技术、经验或审计文档。
6. 如果文档不足以指导编码,先补充或修正文档。
## 开发中
- 保持修改范围聚焦,不做无关重构。
- 优先复用、修改、扩展现有系统、页面、组件和弹层
- 新增 Markdown 文档时,文件名必须以分类标签开头,格式为 `【标签名】中文标题-YYYY-MM-DD.md`
- 阶段性计划、一次性 TODO 和已关闭实验不要长期沉淀为仓库文档;仍有效内容合并进当前 `docs/``.hermes/shared-memory/`
- 复用、修改、扩展现有系统优先,避免新建重复系统或页面
- 新增 Markdown 文档时,文件名必须以分类标签开头,格式为 `【标签名】中文标题-日期.md`;只在任务需要时重命名历史文档,避免无关大 diff
- 涉及中文文本时注意 UTF-8 编码和乱码排查。
- 涉及后端时遵循 DDD 分层,不把业务真相下沉到前端或临时兼容层。
- `maincloud` / `Maincloud` / `MAINCLOUD` 相关代码、脚本、测试、环境变量、命令和文档要求均视为历史残留,禁止新增、运行或引用。
- API smoke 统一使用 `npm run api-server` `/healthz`
- 涉及 SpacetimeDB 表结构、发布或迁移时,先看当前后端架构文档的 schema 变更规则和表目录
- 涉及生产发布、服务器配置、Jenkins Job 重建或回滚时,先看当前开发运维文档。
- `maincloud` / `Maincloud` / `MAINCLOUD` 相关代码、脚本、测试、环境变量、命令和文档要求均视为历史残留,禁止新增、运行或引用API smoke 统一使用 `npm run dev:api-server``/healthz`
- 涉及 SpacetimeDB 表结构、发布或迁移时,先看 `SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md` `SPACETIMEDB_TABLE_CATALOG.md`
- 涉及生产发布、服务器配置、Jenkins Job 重建或回滚时,先看 `PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md`
## 开发后
1. 运行与修改范围匹配的测试或验证命令。
2. 更新相关 `docs/` 当前文档。
3. 若产生长期有效知识,更新 `.hermes/shared-memory/`
4.形成可复用流程,考虑沉淀到 `.hermes/skills/`
5. 提交信息区分代码变更、文档变更和共享记忆变更
2. 更新相关 `docs/` 文档。
3. 新增或沉淀 Markdown 文档时,确认文件名已使用 `【标签名】` 前缀
4.产生长期有效知识,更新 `.hermes/shared-memory/`
5. 若形成可复用流程,考虑沉淀到 `.hermes/skills/`
6. 在提交信息中区分代码变更与文档/记忆变更。
## 文档阅读顺序
通用任务建议:
1. `README.md`
2. `AGENTS.md`
3. `.hermes/shared-memory/`
4. `docs/README.md`
5. `docs/experience/README.md`
6. `docs/audits/README.md`
7. 任务所属分类:`docs/design/``docs/technical/``docs/planning/``docs/prd/``docs/reference/``docs/tracking/``docs/operations/`
后端任务建议:
1. `docs/technical/CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.md`
2. `docs/technical/SERVER_RS_DDD_FULL_REFACTOR_2026-04-28.md`
3. `docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md`
4. `docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md`
5. `docs/technical/SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md`
6. `docs/technical/SPACETIMEDB_TABLE_CATALOG.md`
7. `docs/technical/MAINCLOUD_REFERENCE_REMOVAL_POLICY_2026-05-06.md`
## 共享记忆更新准则
适合更新:
- 稳定架构约定
- 长期开发流程
- 已验证的踩坑和排障步骤
- 重要接口契约变化
- 团队协作规范变化
- 当前文档索引或阅读顺序变化
- 新增稳定架构约定
- 新增长期开发流程
- 已验证的踩坑和排障步骤
- 重要接口契约变化
- 团队协作规范变化
- 文档索引或阅读顺序变化
不适合更新:
- 一次性临时计划
- 未验证猜测
- 个人偏好和个人路径
- 敏感信息
- 大段聊天记录
- 一次性临时计划
- 未验证猜测
- 个人偏好和个人路径
- 敏感信息
- 大段聊天记录
@@ -146,8 +146,8 @@ metadata:
```bash
# 只看后台页面 + api-server,不要求 SpacetimeDB 真实数据
npm run api-server
npm run admin-web:dev -- --host 127.0.0.1
npm run dev:api-server
npm run dev:admin-web -- --admin-web-host 127.0.0.1
# 完整 Rust 本地栈:SpacetimeDB + 发布模块 + api-server + 主站 + 后台
npm run dev
@@ -155,19 +155,19 @@ npm run dev
验证地址通常为:
- `npm run api-server` 单独启动:api-server `http://127.0.0.1:3100/healthz`,后台前端 `http://127.0.0.1:5173/admin/`
- `npm run dev:api-server` 单独启动:api-server 默认 `http://127.0.0.1:8082/healthz`,后台前端默认 `http://127.0.0.1:3102/admin/`
- `npm run dev` 完整栈:SpacetimeDB `http://127.0.0.1:3101/v1/ping`api-server `http://127.0.0.1:8082/healthz`,主站 `http://127.0.0.1:3000/`,后台 `http://127.0.0.1:3102/admin/`
注意:
- `npm run api-server` 首次启动可能先编译 Rust,后台进程短时间内无完整日志;等待编译完成后再查端口。
- 不要默认用 `3200` 验证 api-server;当前脚本环境变量常见为 `GENARRATIVE_API_PORT=3100`。不确定时 `ss -ltnp | grep api-server` 或读取进程环境核对,敏感值输出必须打码。
- `npm run dev:api-server` 首次启动可能先编译 Rust,后台进程短时间内无完整日志;等待编译完成后再查端口。
- 不要默认用 `3200` 验证 api-server;当前脚本默认 `GENARRATIVE_API_PORT=8082`,但会按端口占用情况漂移。不确定时 `[dev] api-server:` 日志为准,或读取进程环境核对,敏感值输出必须打码。
- `admin-web``/` 可能返回 302 跳转到 `/admin/`;验证前端时直接请求 `/admin/`
- api-server 启动日志中 SpacetimeDB `127.0.0.1:3101` 连接被拒绝,不一定代表 api-server 没起来;只表示依赖的本地 SpacetimeDB 不可用。后台中需要读 SpacetimeDB 的页面(如埋点明细、表查询)要等 SpacetimeDB 可用后才能返回真实数据。
- `npm run dev` 依赖 `spacetime` CLI;先用 `command -v spacetime && spacetime --version` 确认可用。
- 本地和人工排障不再使用 `spacetime --root-dir`。如果看到 `bin/current/spacetimedb-cli` 缺失类错误,优先确认是否仍在运行旧脚本或旧发布包;本地开发应使用 `npm run dev` / `npm run dev:rust`,通过项目脚本和 `--data-dir` 隔离 SpacetimeDB 数据目录,不再把用户级 SpacetimeDB 安装同步到项目目录。
- 本地和人工排障不再使用 `spacetime --root-dir`。如果看到 `bin/current/spacetimedb-cli` 缺失类错误,优先确认是否仍在运行旧脚本或旧发布包;本地开发应使用 `npm run dev` / `npm run dev:spacetime`,通过项目脚本和 `--data-dir` 隔离 SpacetimeDB 数据目录,不再把用户级 SpacetimeDB 安装同步到项目目录。
- `scripts/dev-rust-stack.sh` 默认 `api timeout: 300s`. 合并 master 后首次 Rust 依赖/工作区重编译可能超过 300s,导致完整 `npm run dev` 在 api-server 就绪前超时并回收 SpacetimeDB。先让 Rust 编译完成,或临时用 `bash scripts/dev-rust-stack.sh --skip-spacetime --skip-publish --api-timeout-seconds 900` 预热 api-server 编译;之后再重新跑完整 `npm run dev`
- `scripts/dev.mjs` 默认 `api timeout: 600s`. 合并 master 后首次 Rust 依赖/工作区重编译可能超过默认等待窗口,导致完整 `npm run dev` 在 api-server 就绪前超时并回收 SpacetimeDB。先让 Rust 编译完成,或临时用 `npm run dev:api-server -- --api-timeout-seconds 900` 预热 api-server 编译;之后再重新跑完整 `npm run dev`
- 用户贴出的 Hermes background watch 通知可能来自已退出的旧 session。先用 `process poll` 查该 session 状态,再判断是否需要处理;不要把旧失败误判成当前服务失败。
## 测试与验证
@@ -222,4 +222,4 @@ npm install
- `references/spacetimedb-http-sql-sats-display.md`:通过 HTTP SQL 读取 private table 时,enum / Option / Timestamp 的 SATS 原始 rows 如何转换为后台列表、详情和 Excel 可读值。
- `references/daily-login-tracking-trigger-points.md`:排查后台 `daily_login` 埋点为何不是登录接口写入,而是任务中心读取/领奖兜底写入的触发点记录。
- `references/daily-login-auth-closure.md`:将方案A拆出的每日登录埋点入口接入真实认证成功链路时的推荐接入点、非阻断语义、测试和提交注意事项。
- `references/dev-rust-stack-startup-2026-05-08.md``npm run dev` / `scripts/dev-rust-stack.sh` 在 WSL/Linux 下改用用户级 SpacetimeDB CLI、项目数据目录和显式 publish server,避免项目级 `--root-dir` 与冷编译超时的修复记录。
- `references/dev-rust-stack-startup-2026-05-08.md``npm run dev` / `scripts/dev.mjs` 在 WSL/Linux 下改用用户级 SpacetimeDB CLI、项目数据目录和显式 publish server,避免项目级 `--root-dir` 与冷编译超时的修复记录。
@@ -1,7 +1,7 @@
---
name: genarrative-dev-stack-port-routing
short_description: 修改 Genarrative 本地 dev 启动端口、代理目标、端口冲突处理时使用。
description: 在 Genarrative 中修改 npm run dev / npm run dev:rust / npm run dev:web 的本地启动端口、端口可用性探测、端口漂移、SpacetimeDB publish server、api-server 环境变量、Vite 代理目标和后台 admin-web 启动串联时使用。
description: 在 Genarrative 中修改 npm run dev / dev:spacetime / dev:api-server / dev:web / dev:admin-web 的本地启动端口、端口可用性探测、端口漂移、SpacetimeDB publish server、api-server 环境变量、Vite 代理目标和后台 admin-web 启动串联时使用。
version: 1.0.0
author: Hermes Agent
license: MIT
@@ -13,11 +13,11 @@ metadata:
# Genarrative 本地 dev 启动端口与代理目标串联流程
用于维护 Genarrative 本地开发栈启动脚本,重点覆盖 `npm run dev` / `npm run dev:rust` / `npm run dev:web` 的端口检查、端口漂移和后续流程目标传递。
用于维护 Genarrative 本地开发栈启动脚本,重点覆盖 `npm run dev` 与四个 `dev:*` 单模块命令的端口检查、端口漂移和后续流程目标传递。
## 适用场景
- 修改 `scripts/dev-rust-stack.sh``scripts/dev-web-rust.mjs``scripts/dev-stack-port-utils.mjs`
- 修改 `scripts/dev.mjs``scripts/dev-utils.mjs``scripts/dev-stack-port-utils.mjs`
- 处理 `3000``3101``3102``8082` 等端口被占用导致本地开发栈启动失败。
- 排查 Vite 代理仍指向旧 api-server 端口、前端打开了旧 dev server、后台代理错配。
- 调整 SpacetimeDB standalone、publish、Rust `api-server`、主站 Vite、后台 Vite 的启动顺序。
@@ -37,23 +37,21 @@ metadata:
## 实现入口
- `package.json`
- `dev``dev:rust`:执行 `node scripts/run-bash-script.mjs scripts/dev-rust-stack.sh`
- `dev:web`:执行 `node scripts/dev-web-rust.mjs`
- `dev`:执行 `node scripts/dev.mjs`,启动完整四模块
- `dev:spacetime` / `dev:api-server` / `dev:web` / `dev:admin-web`:执行 `node scripts/dev.mjs <module>`
- `scripts/dev-stack-port-utils.mjs`
- `isPortAvailable(...)`:探测端口是否可监听。
- `findAvailablePort(...)`:从优先端口向后寻找可用端口,`0` 表示申请临时端口。
- `resolveDevStackPorts(...)`:一次性解析 SpacetimeDB、api-server、主站 Vite、后台 Vite 端口,并避免本次解析结果互相冲突。
- CLI 模式:`node scripts/dev-stack-port-utils.mjs resolve-dev-stack spacetime:127.0.0.1:3101 api:127.0.0.1:8082 web:0.0.0.0:3000 adminWeb:127.0.0.1:3102`
- `scripts/dev-rust-stack.sh`
- 解析 CLI 参数后,先计算 `API_TARGET_HOST``ADMIN_WEB_TARGET_HOST`
- 调用 `resolve_dev_stack_ports` 覆盖 `SPACETIME_PORT``API_PORT``WEB_PORT``ADMIN_WEB_PORT`
- 再构造 `SPACETIME_SERVER``RUST_SERVER_TARGET`
- `scripts/dev-web-rust.mjs`
- 单独启动主站前端时,也先用 `findAvailablePort` 检查 `WEB_PORT` / 默认 `3000`
- `scripts/dev.mjs`
- 解析 CLI 参数后统一计算 client host、端口、`SPACETIME_SERVER``RUST_SERVER_TARGET`
- 完整栈按 SpacetimeDB、publish、api-server、主站 Vite、后台 Vite 顺序启动
- 单模块命令复用同一套参数和 env 解析
## 必须保持的传递链路
`npm run dev` / `npm run dev:rust` 中端口解析后,必须同步到以下位置:
`npm run dev` 和四个 `dev:*` 单模块命令中端口解析后,必须同步到以下位置:
1. SpacetimeDB 启动:`spacetime start --listen-addr "${SPACETIME_HOST}:${SPACETIME_PORT}"`
2. SpacetimeDB 发布:`spacetime publish ... --server "${SPACETIME_SERVER}"`
@@ -61,7 +59,7 @@ metadata:
4. api-server 健康检查:`wait_for_api_server "${RUST_SERVER_TARGET}/healthz" ...`
5. 主站 Vite`RUST_SERVER_TARGET``GENARRATIVE_RUNTIME_SERVER_TARGET``ADMIN_WEB_TARGET``ADMIN_WEB_PORT``--port=${WEB_PORT}``--host=${WEB_HOST}`
6. 后台 Vite`ADMIN_API_TARGET``GENARRATIVE_API_TARGET``GENARRATIVE_API_PORT``--port=${ADMIN_WEB_PORT}`
7. 控制台日志:`[dev:ports]``[dev:rust] web/admin web/rust api/spacetime` 必须显示最终实际地址。
7. 控制台日志:`[dev:ports]``[dev] web/admin web/api-server/spacetime` 必须显示最终实际地址。
如果只改了其中一段,通常会出现:浏览器打开的前端可用,但 `/api/*` 代理到旧端口;后台页面可用但后台 API 失败;SpacetimeDB 启动在新端口但 publish 仍发往旧端口。
@@ -69,17 +67,13 @@ metadata:
1. 先读当前脚本和文档:
- `scripts/dev-stack-port-utils.mjs`
- `scripts/dev-rust-stack.sh`
- `scripts/dev-web-rust.mjs`
- `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`
- `scripts/dev.mjs`
- `scripts/dev-utils.mjs`
- `docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md`
- `.hermes/shared-memory/pitfalls.md`
2. 优先改公共端口工具,不要把端口探测逻辑复制到多个脚本。
3. 对 Bash 脚本只做局部补丁,避免整文件重写导致中文注释或换行大面积变化
4. 修改 `dev-rust-stack.sh` 时确认变量顺序:
- 先有 `REPO_ROOT`
- 再计算 `API_TARGET_HOST` / `ADMIN_WEB_TARGET_HOST`
- 再调用端口解析工具。
- 再构造 `SPACETIME_SERVER` / `RUST_SERVER_TARGET`
3. 修改 `scripts/dev.mjs` 时确认变量顺序:先解析参数和端口,再构造 `SPACETIME_SERVER` / `RUST_SERVER_TARGET`,最后启动对应 service
4. 修改 watch 时保持模块边界:SpacetimeDB 只监听 `spacetime-module` 且改动后重新 publish,不重启 standalone 宿主;api-server 排除 `spacetime-module`web/admin-web 源码变化交给 Vite 自身 HMR,外层调度器不要再监听前端目录重启 Vite。
5. 修改 `dev:web` 时不要自动改后端目标策略;`dev:web` 只负责主站 Vite 端口可用性与已有后端目标选择。
6. 同步更新技术文档和团队共享记忆。
@@ -88,7 +82,7 @@ metadata:
最小验证:
```bash
bash -n scripts/dev-rust-stack.sh
node --check scripts/dev.mjs
npm run test -- scripts/dev-stack-port-utils.test.ts
npm run check:encoding
node scripts/dev-stack-port-utils.mjs resolve-dev-stack spacetime:127.0.0.1:0 api:127.0.0.1:0 web:0.0.0.0:0 adminWeb:127.0.0.1:0
@@ -101,8 +95,8 @@ node scripts/dev-stack-port-utils.mjs resolve-dev-stack spacetime:127.0.0.1:0 ap
3. 调用 `resolveDevStackPorts`,断言四个结果互不相同。
4. 如果实际启动完整栈,观察控制台:
- `[dev:ports] ... 不可用,改用 ...`
- `[dev:rust] rust api: http://...:<actual-api-port>`
- `[dev:rust] spacetime: http://...:<actual-spacetime-port>`
- `[dev] api-server: http://...:<actual-api-port>`
- `[dev] spacetime: http://...:<actual-spacetime-port>`
- 主站和后台 Vite 启动端口与日志一致。
完整启动属于长驻进程。需要 smoke 时用 background 方式启动,并另开命令检查 `/healthz``/v1/ping` 和页面端口;不要等待 `npm run dev` 自然退出。
@@ -119,9 +113,9 @@ node scripts/dev-stack-port-utils.mjs resolve-dev-stack spacetime:127.0.0.1:0 ap
## 验收清单
- [ ] 端口工具有测试覆盖端口被占用和多端口互斥解析。
- [ ] `dev-rust-stack.sh` 通过 `bash -n`
- [ ] `npm run dev` / `npm run dev:rust` 的 SpacetimeDB、publish、api-server、主站 Vite、后台 Vite 都使用实际端口。
- [ ] `scripts/dev.mjs` 通过 `node --check`
- [ ] `npm run dev` 的 SpacetimeDB、publish、api-server、主站 Vite、后台 Vite 都使用实际端口。
- [ ] `npm run dev:web` 在主站端口不可用时能切换到可用端口。
- [ ] 文档同步更新 `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`
- [ ] 文档同步更新 `docs/technical/RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md`
- [ ] 长期踩坑同步更新 `.hermes/shared-memory/pitfalls.md`
- [ ] 修改中文文件后运行 `npm run check:encoding`
Generated
-1
View File
@@ -1 +0,0 @@
mod.rs
+2 -1
View File
@@ -46,7 +46,8 @@ npm run dev
- `npm run dev` 会启动 SpacetimeDB standalone、Rust `api-server`、主站 Vite 与后台 Vite,适合完整联调。
- 主站默认地址是 `http://127.0.0.1:3000`,后台可从 `http://127.0.0.1:3000/admin/` 进入,也可直连 `http://127.0.0.1:3102`
- 如果只想单独启动前端页面,可使用 `npm run dev:web`,默认代理到本地 Rust `api-server`
- 四个模块可独立启动:`npm run dev:spacetime``npm run dev:api-server``npm run dev:web``npm run dev:admin-web`
- 如需自动刷新后端模块,使用 `npm run dev -- --watch`;其中 `spacetime-module` 改动后只会重新发布模块,不会重启 standalone,`api-server` 改动后会重启 Rust 进程。主站和后台前端源码变化交给 Vite 自身 HMR,不由外层 watcher 重启。非 watch 模式下可在 `npm run dev` 终端输入 `rs api-server``rs web``rs admin-web``rs spacetime``rs all`,其中 `rs spacetime` 也是只重新发布模块。
构建生产包:
+49 -13
View File
@@ -1,19 +1,55 @@
# Genarrative 文档入口
# 文档总览
本目录已经在 `2026-05-15` 完成压缩整理:旧 PRD、阶段计划、修复记录、审计报告、技术方案和查询手册中的仍有效内容,统一融合到下列当前文档。旧文档不再作为实现依据
`docs/` 现在按主题拆成了 6 类;旧后端路线文档开始聚合和删除,后续实现以 Rust / SpacetimeDB 当前基线为准
## 当前文档
## 快速入口
- [【项目基线】当前产品与工程约束-2026-05-15.md](./【项目基线】当前产品与工程约束-2026-05-15.md):产品定位、平台入口、UI 约束、协作规则和废弃路线
- [【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md](./【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md)server-rs DDD 边界、API 分组、SpacetimeDB schema 变更规则和当前表目录
- [【玩法创作】平台入口与玩法链路-2026-05-15.md](./【玩法创作】平台入口与玩法链路-2026-05-15.md):创作 Tab、草稿架、拼图、抓大鹅、视觉小说、方洞、大鱼、汪汪声浪和儿童向玩法的当前口径
- [【开发运维】本地开发验证与生产运维-2026-05-15.md](./【开发运维】本地开发验证与生产运维-2026-05-15.md):本地启动、测试、编码检查、SpacetimeDB 变更流程、生产运维、埋点和运营查询
- [经验沉淀](./experience/README.md):项目开发经验、UI 交接、历史实现经验
- [审计与复盘](./audits/README.md):工程审查、文本/乱码审计、专项落地审计
- [系统设计](./design/README.md):玩法、关系、物品与对话设计
- [技术方案](./technical/README.md):动画、服务端、外部产品形态拆解
- [规划与优先级](./planning/README.md):当前阶段的迭代排序与落地优先级。
- [参考目录](./reference/README.md):脚本/Function 速查入口。
重点补充:RPG 创作与运行时脚本职责地图见 [RPG_CREATION_AND_RUNTIME_SCRIPT_RESPONSIBILITY_MAP_2026-04-28.md](./reference/RPG_CREATION_AND_RUNTIME_SCRIPT_RESPONSIBILITY_MAP_2026-04-28.md)。
- [埋点查询](./tracking/README.md):埋点原始事件与聚合投影的本地 SQL 查询。
- [运营查询](./operations/README.md):任务、领奖、钱包对账等后台核查查询。
- [PRD](./prd/README.md):产品需求与阶段计划;参考 MOKU / 幕间类 AI 文游的陶泥儿 `text-game` 模板口径见 [AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_2026-05-05.md](./prd/AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_2026-05-05.md),视觉小说模板 TXT 玩法平台化接入口径见 [AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md](./prd/AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md),创意互动内容 Agent Phase 1 的 LangChain-Rust PoC、拼图闭环和并行任务拆分见 [CREATIVE_INTERACTIVE_AGENT_PHASE1_LANGCHAIN_RUST_PUZZLE_LOOP_PRD_2026-05-05.md](./prd/CREATIVE_INTERACTIVE_AGENT_PHASE1_LANGCHAIN_RUST_PUZZLE_LOOP_PRD_2026-05-05.md),幸存者类模板闭环见 [AI_NATIVE_SURVIVOR_CREATOR_AND_GAMEPLAY_SYSTEM_PRD_2026-05-05.md](./prd/AI_NATIVE_SURVIVOR_CREATOR_AND_GAMEPLAY_SYSTEM_PRD_2026-05-05.md),后台管理独立前端工程见 [ADMIN_WEB_CONSOLE_PRD_2026-04-30.md](./prd/ADMIN_WEB_CONSOLE_PRD_2026-04-30.md),新增 RPG 开场动画方案见 [AI_NATIVE_RPG_OPENING_ANIMATION_PRD_2026-04-25.md](./prd/AI_NATIVE_RPG_OPENING_ANIMATION_PRD_2026-04-25.md),新增抓大鹅 Match3D 玩法方案见 [AI_NATIVE_MATCH3D_CREATOR_AND_GAMEPLAY_SYSTEM_PRD_2026-04-30.md](./prd/AI_NATIVE_MATCH3D_CREATOR_AND_GAMEPLAY_SYSTEM_PRD_2026-04-30.md),方洞挑战创作、发布与试玩闭环见 [AI_NATIVE_SQUARE_HOLE_CREATOR_AND_GAMEPLAY_SYSTEM_PRD_2026-05-04.md](./prd/AI_NATIVE_SQUARE_HOLE_CREATOR_AND_GAMEPLAY_SYSTEM_PRD_2026-05-04.md)。
新增稳定知识优先合并到上述文档。只有上述文档无法容纳某个长期主题时,才新增同目录的 `【标签名】中文标题-YYYY-MM-DD.md` 文档
生产部署切换到 systemd + Nginx + SpacetimeDB 自托管的总方案见 [PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md](./technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md),该文档也是当前生产 Jenkinsfile 的唯一入口。SpacetimeDB 表结构变更、自动迁移边界和保留旧数据的分阶段迁移流程见 [SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md](./technical/SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md)private 表迁移 JSON 导入导出、HTTP 413 分片导入和旧数据库迁移流水线经验见 [SPACETIMEDB_JSON_STRING_MIGRATION_PROCEDURE_2026-04-27.md](./technical/SPACETIMEDB_JSON_STRING_MIGRATION_PROCEDURE_2026-04-27.md) 与 [JENKINS_SPACETIMEDB_DATABASE_MIGRATION_PIPELINES_2026-04-29.md](./technical/JENKINS_SPACETIMEDB_DATABASE_MIGRATION_PIPELINES_2026-04-29.md);后台管理独立前端工程技术方案见 [ADMIN_WEB_CONSOLE_TECHNICAL_SOLUTION_2026-04-30.md](./technical/ADMIN_WEB_CONSOLE_TECHNICAL_SOLUTION_2026-04-30.md)
## 维护规则
SpacetimeDB 表结构变更、自动迁移边界和保留旧数据的分阶段迁移流程见 [SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md](./technical/SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md)。
1. 代码和当前文档冲突时,以代码为准,并立即修正文档
2. 新增 Markdown 文件名必须使用 `【标签名】中文标题-YYYY-MM-DD.md`
3.`server-node`、Express、PostgreSQL、Go 服务端、`maincloud`、人工 `spacetime --root-dir`、Match3D 新草稿 Rodin/GLB、拼图和抓大鹅音频生成入口,均不再作为当前实现依据
4. 涉及 SpacetimeDB 表、reducer、procedure、row shape 或 bindings 时,必须同步修改代码、迁移、生成绑定和 [后端架构文档](./【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md) 的表目录。
创作 Agent 问答流式失败时保留已显示回复、并透出更具体上游错误的契约见 [CREATION_AGENT_STREAM_FAILURE_RETENTION_FIX_2026-05-05.md](./technical/CREATION_AGENT_STREAM_FAILURE_RETENTION_FIX_2026-05-05.md)
`maincloud` 相关脚本、环境变量、测试名和文档要求已统一判定为历史残留,后续禁止新增、运行或引用;当前后端 smoke 使用 `npm run dev:api-server``/healthz`,详细规则见 [MAINCLOUD_REFERENCE_REMOVAL_POLICY_2026-05-06.md](./technical/MAINCLOUD_REFERENCE_REMOVAL_POLICY_2026-05-06.md)
基于 LangChain-Rust,以“感知—思考—记忆—行动—反思—协作”闭环完成图文创意理解、拼图模板选择、积分范围确认、拼图草稿字段填充、立即试玩和自然语言修订草稿字段的 Agent 方案见 [CREATIVE_INTERACTIVE_CONTENT_AGENT_TECHNICAL_SOLUTION_2026-05-05.md](./technical/CREATIVE_INTERACTIVE_CONTENT_AGENT_TECHNICAL_SOLUTION_2026-05-05.md)。
创意互动内容 Agent Phase 1 的产品边界、实现细节、SpacetimeDB 落点、前端接入和可并行任务拆分见 [CREATIVE_INTERACTIVE_AGENT_PHASE1_LANGCHAIN_RUST_PUZZLE_LOOP_PRD_2026-05-05.md](./prd/CREATIVE_INTERACTIVE_AGENT_PHASE1_LANGCHAIN_RUST_PUZZLE_LOOP_PRD_2026-05-05.md)。
视觉小说模板接入以 [AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md](./prd/AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md) 为最新口径:只吸收外部 TXT 玩法的模板创作与运行经验,禁止迁入外部平台功能,并删除回放。
AI 文字游戏模板接入以 [AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_2026-05-05.md](./prd/AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_2026-05-05.md) 为最新口径:只吸收 MOKU / 幕间类 AI 文游的剧本游乐场、自由行动、AI GM、记忆和模拟器强反馈经验,禁止迁入外部社区、支付、榜单、私有存档或回放。
## 推荐阅读顺序
1. 先看 [经验沉淀](./experience/README.md),快速建立这个项目的开发共识。
2. 再看 [工程审查总览](./audits/engineering/README.md) 和 [文本审计总览](./audits/text/README.md),了解当前风险。
3. 需要排期时看 [规划与优先级](./planning/README.md)。
4. 需要补方案时进入 [系统设计](./design/README.md) / [技术方案](./technical/README.md);涉及后端先看 [当前后端实现基线](./technical/CURRENT_BACKEND_IMPLEMENTATION_BASELINE_2026-04-25.md),涉及生产发布链路先看 [生产部署计划](./technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md),涉及 SpacetimeDB 表结构变更时再看 [表结构变更约束](./technical/SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md)。
5. 需要对齐目标边界时再进入 [PRD](./prd)。
## 分类规则
- `experience/`:偏方法论、交接经验、长期有效的开发结论。
- `audits/`:偏“现状扫描 / 问题定位 / 是否达标”的审查类文档。
- `design/`:偏玩法机制、叙事关系、系统结构设计。
- `technical/`:偏技术选型、实现路线、竞品/产品形态拆解。
- `planning/`:偏阶段优先级与推进顺序。
- `reference/`:偏目录、速查、检索辅助。
- `tracking/`:偏埋点原始事实和聚合投影查询,不放任务进度或钱包对账。
- `operations/`:偏后台运营核查、对账和排障查询。
## 文档命名规则
后续新增的 Markdown 文档文件名必须以分类标签开头,格式为 `【标签名】中文标题-日期.md`。标签用于跨目录检索,不替代上面的目录分类;历史文档不要求批量重命名,除非本次任务明确涉及该文档。
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,121 @@
# 宝贝识物寓教于乐模板 PRD 2026-05-11
## 1. 目标
新增寓教于乐内容线的创作模板:
```text
宝贝识物
```
创作者必须通过该模板创作并发布作品后,用户才能在寓教于乐板块体验对应关卡。
本模板只服务儿童动作 Demo 内容线,不把普通教育题材作品自动归入寓教于乐。
## 2. 创作输入
创作者必须填写两个物品名称:
1. 物品 A 名称;
2. 物品 B 名称。
两个名称都必须去除首尾空白后非空。当前阶段不新增题材、难度、计时、失败次数、分数、体力或递增规则。
## 3. 生成规则
提交后生成一份宝贝识物草稿,草稿包含:
1. 模板 ID`baby-object-match`
2. 模板名称:`宝贝识物`
3. 两个物品;
4. 两个物品图;
5. 游戏视觉主题包;
6. 作品标签。
素材使用 VectorEngine `gpt-image-2-all` / image-2 生成。图片生成只能走后端接口,前端不得读取、拼接或暴露 `VECTOR_ENGINE_API_KEY`
为降低生成成本,创作提交后只生成两张原始图片:一张 `2x2` 素材 sheet 和一张单独场景背景图。`2x2` 素材 sheet 固定包含左上物品 A、右上物品 B、左下篮子、右下礼物盒。服务端必须按固定格切图,并把物品、篮子和礼物盒转成透明 PNG。只有透明抠图后的两个物品素材才允许写入草稿 `itemAssets` 并进入游戏运行态。左右手位置指示器属于运行态默认规则,使用项目内置静态素材,不在每次创作时生成。
同一次创作还必须生成游戏视觉主题包,必需资源为背景环境、礼物盒、篮子。主题包必须继续保持寓教于乐插画风,并根据用户填写的两个物品关键词匹配主题:例如关键词偏动漫角色或玩具时,背景环境和元素可使用动漫、玩具主题;关键词偏水果时,背景环境和元素可匹配果园、自然主题;其它关键词按其语义匹配合适主题。主题包不得改变关卡玩法规则,不新增文字说明、额外按钮或额外判定规则。
视觉主题包的资源边界:
1. 背景环境图不做透明抠图,但必须保证屏幕中间、中下方和底部左右篮子区域清爽,不遮挡放大后的物品、礼物盒和篮子;
2. 礼物盒资源从 `2x2` 素材 sheet 右下格切出,输出为透明 PNG,运行态按当前礼盒视觉的 2 倍尺寸展示,素材主体必须饱满清晰;
3. 篮子资源从 `2x2` 素材 sheet 左下格切出,输出为透明 PNG,运行态按当前篮子视觉的 1.5 倍尺寸展示,左右篮子仍固定为两个物品对应选项,篮子造型资源可以复用同一张主题篮子图;篮子切图不得保留手柄、篮口或边缘处的白底描边和抠图毛边;
4. 运行态左右手位置指示器使用内置默认静态素材,姿势为用户第一人称看到的半抓握手,不随创作关键词重新生成;
5. 礼物盒打开时的烟雾弹出特效由运行态 CSS 动效兜底;历史草稿如果已有 `smoke-puff` 资源可继续兼容读取,但新生成链路不再单独生成该资源。
当前本地 Demo 阶段已接入真实 image-2 资源链路。创作提交必须成功获得 `generationProvider = "vector-engine-gpt-image-2"` 的两个物品透明 PNG、背景环境图、礼物盒和篮子后,才能进入结果页、试玩或发布;若后端接口、登录态、VectorEngine 配置或上游生成失败,前端必须停留在生成失败状态并展示错误,不得静默回退为占位图。历史草稿中若仍存在 `generationProvider = "placeholder"` 的占位资源,结果页必须提示重新生成,试玩和发布前必须先补齐 image-2 资源。
## 4. 标签规则
发布作品必须携带精确标签:
```text
寓教于乐
```
标签识别只接受精确等于 `寓教于乐`。不接受 `儿童教育``动作教育``寓教于乐 ` 等近似标签。
宝贝识物草稿与发布 payload 中都必须保留该标签。发布后的公开展示、搜索、深链和入口开关继续遵循 `CHILD_MOTION_EDUTAINMENT_DISCOVER_ENTRY_2026-05-09.md`
## 5. 结果页能力
结果页展示:
1. 作品名称;
2. 两个物品名称;
3. 两个物品图;
4. 标签;
5. 保存草稿;
6. 发布;
7. 试玩。
结果页不展示长规则说明文案。试玩按钮直接进入宝贝识物首关本地运行态。
试玩按钮进入宝贝识物首关运行态,运行态消费当前草稿中的两个物品名称和两张物品图,不重新生成或改写物品内容。
若草稿包含视觉主题包,运行态还必须消费该主题包中的背景环境、礼物盒和篮子资源;左右手位置指示器始终使用内置默认静态素材。旧草稿或接口失败时允许回退到当前 CSS 绘本风兜底。历史草稿中若已有 UI 装饰、左右手或烟雾弹出特效资源,运行态仅做兼容读取或忽略,不作为新链路必需资源。
## 6. 发布后体验
发布完成后作品应进入寓教于乐内容线,并在寓教于乐入口开启时可被板块消费。
入口关闭时,发布作品完全不可见,不能通过推荐、发现普通频道、搜索、作品号、公开详情深链或浏览历史访问。
## 7. 与运行时线程的边界
本 PRD 同步约束首关运行态,已确认规则包括:
1. 进入关卡后先展示两个目标物品:物品 A 居中展示 2 秒,名称 UI 与字体约为默认大小的 2 倍,随后物品和名称飞入左侧篮子预设位置,并在飞行过程中恢复为默认大小;左侧就绪后等待 1 秒,再展示物品 B 并飞入右侧篮子预设位置;全部就绪后等待 1 秒再进入礼物盒入场。
2. 目标展示完成后,首次礼物盒自动打开并弹出首个随机物品;后续每次正确反馈完全结束后重新进入礼物盒入场。
3. 每轮仅中间礼物盒跳出的物品随机;左右两侧篮子固定为当前草稿两个物品的顺序;
4. 下一关按钮当前占位;
5. 不新增用户未确认的计时、失败次数、分数、体力或难度递增。
6. 屏幕中上方字幕固定为“将物品放入对应的篮子里”。
7. 礼物盒位于屏幕中下方并按当前视觉放大一倍,首次进入关卡和每次正确反馈结束后的新轮次都从上方落下后自动打开。
8. 屏幕下方左侧和右侧分别展示两个固定篮子,左侧固定使用草稿第一个物品图,右侧固定使用草稿第二个物品图。
9. 左右篮子按当前视觉放大 50%,物品图标与篮子中心尽量对齐,物品图标下方展示对应物品名称 UI。
10. 礼物盒打开时播放烟雾特效,中央物品从烟雾特效中弹出;物品弹出后礼物盒从舞台移除。
11. 中央物品 UI 和左右篮子上方物品图标都使用固定正方形槽位,生成素材只在槽位内等比缩放;长条形物品不得拉伸外层 UI 框。
12. 运行态实时展示用户左右手位置;任意一只手先接触中央物品 UI 后,中央物品绑定并跟随该手移动,手带物品进入左侧或右侧篮子区域时代表选择对应篮子;选篮不使用动作名判定,也不再使用左手固定选左篮、右手固定选右篮的规则。
13. 正确时展示“真棒”字幕和正确特效;错误时展示“再想一想吧”字幕和错误特效,物品回到中央。
14. 成功 20 次后展示“恭喜你!小朋友!”字幕和特效,并展示“再来一次”和“下一关”按钮。
15. 当前本地 Demo 阶段音效与语音播报接口只预留调用点,不在前端写死外部硬件或服务接口。
## 8. 验收
1. 创作入口显示 `宝贝识物` 并可进入模板表单。
2. 未填写任一物品名称时不能生成草稿。
3. 生成草稿后进入结果页,展示两个物品名称和物品图。
4. 生成草稿后包含视觉主题包,主题包含背景环境、礼物盒、篮子三类必需资源。
5. 草稿标签中始终包含精确 `寓教于乐`
6. 发布 payload 始终包含精确 `寓教于乐`
7. 发布完成后出现分享弹窗或发布完成状态。
8. 前端不读取或暴露 VectorEngine 密钥。
9. 结果页试玩进入宝贝识物运行态,不再显示“试玩关卡正在接入中”。
10. 运行态通过鼠标左键映射左手位置、鼠标右键映射右手位置;调试输入也必须先触碰中央物品,再拖入任一篮子完成选择。
11. 成功 20 次后出现“再来一次”和“下一关”按钮。
12. 使用长条形物品素材时,中央物品 UI 和篮子物品图标仍保持固定正方形槽位,只缩放物品本体。
13. 运行态开局先完成两个目标物品的居中展示和飞入篮子动画,之后才出现礼物盒并进入首轮随机物品。
@@ -0,0 +1,274 @@
# 宝贝识物创作发布实现方案 2026-05-11
## 1. 范围
本方案对应第 2 线程:创作发布线程。
本线程落地:
1. 创作入口配置;
2. 模板表单;
3. 本地草稿生成 service
4. 结果页;
5. 发布 payload 约束;
6. 本地 Demo 运行态;
7. 后端 image-2 / 作品持久化 / 运行态接口预留形状。
本阶段运行态先做浏览器本地 Demo,并消费现有本地 mocap 动作数据源;正式硬件接口和摄像头调教在后续接口稳定后继续接入。
## 2. 前端接入点
新增玩法 ID
```text
baby-object-match
```
用户展示名:
```text
宝贝识物
```
工程接入文件:
1. `server-rs/crates/spacetime-module/src/runtime/creation_entry_config.rs`
2. `src/components/platform-entry/platformEntryCreationTypes.ts`
3. `src/components/platform-entry/PlatformEntryCreationTypeModal.tsx`
4. `src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`
`src/config/newWorkEntryConfig.ts` 已迁移删除,不再作为入口事实源。`baby-object-match` 必须存在于 SpacetimeDB `creation_entry_type_config` 默认种子中,默认展示名为 `宝贝识物``visible=true``open=true``sortOrder=90`;前端只通过 `GET /api/creation-entry/config` 读取后端配置并在 `platformEntryCreationTypes.ts` 做展示派生。
`baby-object-match` 必须复用 `VITE_ENABLE_EDUTAINMENT_ENTRY` 开关;开关关闭时,创作类型弹层不展示 `宝贝识物`,创作页作品架不展示本地宝贝识物草稿或已发布作品卡,公开发现、搜索、详情、作品号和浏览历史也继续完全不可见。
新增阶段:
```text
baby-object-match-workspace
baby-object-match-generating
baby-object-match-result
baby-object-match-runtime
```
## 3. 契约
前端共享契约放在:
```text
packages/shared/src/contracts/edutainmentBabyObject.ts
```
核心字段:
1. `BabyObjectMatchDraft.templateId = "baby-object-match"`
2. `BabyObjectMatchDraft.templateName = "宝贝识物"`
3. `BabyObjectMatchDraft.themeTags` 必须包含精确 `寓教于乐`
4. `BabyObjectMatchItemAsset.generationProvider` 首版允许为 `vector-engine-gpt-image-2``placeholder`
5. `BabyObjectMatchDraft.visualPackage` 可选承载背景环境、礼物盒和篮子三类必需视觉资源;历史草稿中的 `ui-frame``smoke-puff``left-hand``right-hand` 仅保留运行态兼容读取或忽略;
6. `BabyObjectMatchPublishRequest.draft.themeTags` 发布前必须归一化补齐 `寓教于乐`
## 4. Service 边界
前端 service 放在:
```text
src/services/edutainment-baby-object/babyObjectMatchClient.ts
```
首版提供:
1. `createBabyObjectMatchDraft(payload)`
2. `saveBabyObjectMatchDraft(draft)`
3. `publishBabyObjectMatchWork(payload)`
4. `deleteLocalBabyObjectMatchDraft(profileId)`
5. `regenerateBabyObjectMatchDraftAssets(draft)`
6. `hasBabyObjectMatchPlaceholderAssets(draft)`
当前后端正式作品持久化接口未在本线程扩表落地,因此 service 仍使用本地 Demo 存储草稿和发布状态。由于 image-2 会返回多张 base64 PNG 大图,本地 Demo 草稿必须优先写入 IndexedDB `genarrative-edutainment-baby-object-drafts/drafts`,不得把完整草稿 JSON 写入 `localStorage``localStorage` 仅作为旧版小草稿迁移读取来源,读取后迁移到 IndexedDB 并清理旧 key,避免触发浏览器 `Storage` 配额错误。
物品图片生成已接入后端 image-2 接口:
```text
POST /api/creation/edutainment/baby-object-match/assets
```
请求体:
```json
{
"itemNames": ["苹果", "香蕉"]
}
```
响应体:
```json
{
"assets": [
{
"itemId": "baby-object-item-1",
"itemName": "苹果",
"imageSrc": "data:image/png;base64,...",
"assetObjectId": null,
"generationProvider": "vector-engine-gpt-image-2",
"prompt": "..."
}
],
"visualPackage": {
"themePrompt": "...",
"assets": [
{
"assetId": "baby-object-visual-background",
"assetKind": "background",
"imageSrc": "data:image/png;base64,...",
"assetObjectId": null,
"generationProvider": "vector-engine-gpt-image-2",
"prompt": "..."
}
]
}
}
```
该接口返回从同一张 `2x2` 素材 sheet 切出的两个物品透明 PNG、礼物盒透明 PNG、篮子透明 PNG,以及单独生成的一张背景环境图。本地 Demo 阶段暂不写入 OSS 或 SpacetimeDB `asset_object`。当前创作链路必须真实拿到 `generationProvider = "vector-engine-gpt-image-2"` 的两个物品图和必需视觉主题包后才允许进入结果页;若本地未配置 VectorEngine、登录态失效、接口返回 401/5xx、上游生成失败或响应缺少任一必需资源,前端 service 必须抛出错误并停留在生成失败状态,不得静默回退到占位图。左右手位置指示器是运行态默认静态素材,不在该接口中生成。
为了降低 image-2 调用成本,一次创作只发起两次图片生成:一次 `1024x1024``2x2` 素材 sheet,固定包含左上物品 A、右上物品 B、左下篮子、右下礼物盒;一次 `1536x1024` 的场景背景图。前端 `babyObjectMatchClient` 对该 POST 使用 10 分钟请求超时,且不做自动重试,避免第一次生成仍在后端执行时又发起第二次重复生成。后端并发启动两张图生成,并把该路由的 VectorEngine 单图请求等待预算提升到至少 8 分钟,避免某张图 3 分钟附近仍在生成时被后端提前断开。后端日志记录资源开始、完成和耗时,排查时优先按同一次 HTTP 请求查看 `宝贝识物 image-2 2x2 素材 sheet 生成完成``宝贝识物 image-2 场景资源生成完成``VectorEngine 图片生成上游错误`
历史本地草稿中若已保存 `generationProvider = "placeholder"` 的旧占位资源,结果页必须提示“重新生成 image-2 资源”,并禁用试玩和发布。用户点击重新生成、发布或试玩前,前端统一调用 `regenerateBabyObjectMatchDraftAssets(draft)` 补齐资源;补齐失败时保留在结果页并展示错误。
后续正式作品持久化接入时,应补齐:
```text
POST /api/creation/edutainment/baby-object-match/drafts
PUT /api/creation/edutainment/baby-object-match/drafts/{draftId}
POST /api/creation/edutainment/baby-object-match/drafts/{draftId}/publish
```
图片生成必须在后端调用 VectorEngine `gpt-image-2-all`,不得从前端直接调用外部图片接口。
后端 `2x2` 素材 sheet prompt 约束:
1. 锁定寓教于乐板块统一的明亮卡通绘本插画风;
2. 固定四格布局:左上格物品 A,右上格物品 B,左下格篮子,右下格礼物盒;
3. 两个物品格只能围绕对应关键词生成单一主体,不生成背景、场景、人物、手、篮子、礼物盒、文字、水印或 UI;
4. 篮子格只生成一个主体饱满、开口清晰的大号篮子,不放入待分类物品,手柄和篮口镂空处不得留下白底描边或毛边;
5. 礼物盒格只生成一个主体饱满、中心构图的大号礼物盒;
6. 每格使用纯白或接近纯白背景,不绘制网格线、标签、按钮或边框;
7. 服务端按 `2x2` 固定格切图,并按单格边缘采样背景色转透明 PNG,返回的物品、篮子和礼物盒素材必须已完成透明背景后处理;
8. 篮子切图在通用透明背景处理后,还必须额外清理近白、低饱和的白底毛边,优先覆盖手柄镂空、篮口镂空和边缘残留白底;该处理仅应用于篮子格,不应用于两个物品格,避免误伤白色物品主体。
后端场景背景 prompt 约束:
1. 背景图单独生成,总风格继续锁定寓教于乐明亮卡通绘本插画风;
2. 若关键词偏动漫角色、玩具或公仔,背景环境匹配动漫、玩具主题;若关键词偏水果,匹配果园、自然主题;其它关键词按语义匹配合适主题;
3. 背景环境图使用非透明横向图,但必须保证中间、中下方和底部左右篮子区域清爽,给放大后的礼物盒、中央物品和左右篮子预留空间;
4. 背景图不画入礼物盒、篮子、物品、人物、文字或操作 UI;
5. 左右篮子的固定选项规则不受主题包影响,运行态只把 `basket` 作为篮子造型包装复用。
运行态左右手位置指示器不随创作生成。默认素材保存在 `public/edutainment-baby-object/image2-picture-book-hands/baby-object-left-hand-v8-transparent.png``public/edutainment-baby-object/image2-picture-book-hands/baby-object-right-hand-v8-transparent.png`,姿势沿用图1的圆形手与斜向手臂结构,并按寓教于乐明亮绘本插画风完成 image2 填色和风格化处理。后续若要替换默认手型,应更新这两个静态资源和运行态 CSS 默认变量,而不是恢复每次创作的左右手 image-2 生成。
## 5. UI 边界
工作台只展示两个必填输入和生成按钮。
结果页只展示草稿核心信息、两个物品、保存草稿、发布、试玩。不在 UI 内写玩法说明长文案。
移动端优先:表单和结果页使用单列布局,桌面端自然扩展为双列。
## 6. 运行态边界
前端运行态放在:
```text
src/components/edutainment-runtime/BabyObjectMatchRuntimeShell.tsx
```
运行态直接消费 `BabyObjectMatchDraft`,必须使用草稿中的两个物品名称和物品图。
每轮只随机当前从礼物盒跳出的物品;左右篮子不随机交换,左侧固定为草稿 `itemAssets[0]`,右侧固定为草稿 `itemAssets[1]`
若草稿包含 `visualPackage`,运行态通过背景图片层、CSS 变量和图片节点消费:
1. `background`:作为舞台最底层 `ResolvedAssetImage` 背景图;存在该资源时必须关闭默认草地兜底层,避免生成场景被 CSS 草地遮住或弱化;
2. `gift-box`:替换 CSS 礼物盒主体,按旧视觉约 2 倍尺寸展示,只在礼盒入场和打开阶段存在;
3. `basket`:替换篮子主体造型,按旧视觉约 1.5 倍尺寸展示,左右两侧复用同一张主题篮子图;
4. 左右手位置指示器:始终使用运行态默认静态素材;历史草稿中若带有 `left-hand` / `right-hand` 资源,不再作为视觉包完整性或运行时皮肤来源。
左右篮子的选项 UI 必须以篮子中心线为基准居中展示:物品图标位于篮子上方,图标下方展示对应物品名称短标签,左侧固定展示草稿第一个物品,右侧固定展示草稿第二个物品。该名称标签是运行态 UI 的一部分,用于后续只看图案或只看名称的玩法变体预留,但当前不新增额外规则。
历史草稿若包含 `ui-frame``smoke-puff`,运行态继续兼容读取;新生成链路不再把这两类资源作为必需 image-2 产物。礼物盒打开烟雾特效优先使用 CSS 动效兜底,避免为了单个特效额外增加生图调用。
旧草稿或接口失败时 `visualPackage = null`,运行态继续使用现有 CSS 绘本风兜底。
中央物品 UI 与左右篮子上方物品图标必须使用固定正方形槽位,不允许因为生成物品是手机、长条玩具等窄长形状而拉伸外层 UI 框。素材图片在槽位内使用等比 `contain` 缩放,长条形状只缩小主体,不改变圆形 UI 框尺寸。
首关状态机:
1. `intro-left-showing`:物品 A 居中展示 2 秒,名称 UI 和字体约为默认大小的 2 倍,不接受动作判定;
2. `intro-left-flying`:物品 A 和名称飞入左侧篮子预设位置,飞行过程中名称 UI 和字体恢复为默认大小,不接受动作判定;
3. `intro-left-ready`:左侧目标就绪后等待 1 秒,不接受动作判定;
4. `intro-right-showing`:物品 B 居中展示 2 秒,名称 UI 和字体约为默认大小的 2 倍,不接受动作判定;
5. `intro-right-flying`:物品 B 和名称飞入右侧篮子预设位置,飞行过程中名称 UI 和字体恢复为默认大小,不接受动作判定;
6. `intro-right-ready`:右侧目标就绪后等待 1 秒,不接受动作判定;
7. `gift-entering`:礼物盒从上方落下入场动画阶段,不接受动作判定;首次进入该状态必须发生在两个目标展示完成后,后续正确反馈结束后直接进入该状态;
8. `gift-opening`:礼物盒打开并播放烟雾特效阶段,不接受动作判定;
9. `item-appearing`:礼物盒从舞台移除,当前物品从烟雾中出现并停稳,不接受动作判定;
10. `active`:物品彻底出现后才开放选篮判定;
11. `correct`:展示“真棒”反馈,对应篮筐播放正确特效并停顿,成功次数加 1;特效完全结束后重新进入 `gift-entering`,下一轮礼物盒从上方落下,不重复目标展示;
12. `wrong`:展示“再想一想吧”反馈,物品弹回中央;反馈结束后回到 `active`,不重新随机物品;
13. `complete`:成功次数达到 20,展示“恭喜你!小朋友!”和按钮。
动作输入:
1. 运行态实时展示左右手位置,手部位置来自 `useMocapInput` 的明确左/右手坐标;
2. 任意一只手先接触中央物品 UI 后,当前物品绑定到该手并跟随移动;
3. 绑定手带物品进入左侧篮子区域时选择左篮,进入右侧篮子区域时选择右篮;
4. 正确时沿用“真棒”反馈和对应篮筐特效,错误时物品弹回中央并回到可再次抓取状态;
5. 物品被某只手持有时,手部指示器不再压在物品图标中心;左手吸附到当前物品图标左下角,右手吸附到当前物品图标右下角,保持图案主体可读;
6. 不再使用“左手固定选左篮、右手固定选右篮”的规则,也不再使用连续横向轨迹阈值直接选篮。
运行态直接通过 `useMocapInput` 消费本地 mocap WebSocket `/stream`。宝贝识物优先使用 `general.limb_nodes` / `limb_nodes` 里的骨架手腕节点作为左右手指示器、抓取和选篮坐标;若当前帧没有骨架手腕,再回退到每只手的 `wrist` 挂点,最后才回退到 `hand.x / hand.y`。该策略只让 `useMocapInput` 额外暴露 `bodyJoints.leftWrist/rightWrist`,不修改全局掌心派生点规则,避免影响拼图、热身关和其它运行态。选篮不再通过 `wave_left_hand``wave_right_hand``wave` 等动作名触发;侧别为 `unknown` 的手部轨迹不参与抓取或选篮。动作判定只在 `active` 阶段开放,礼盒入场、礼盒打开、物品出现、正确反馈和错误反馈阶段收到的动作包必须清空持有状态并忽略,不允许跨阶段补判定。当前本地 mocap 输出的 handedness 按摄像头视角标记,宝贝识物运行态必须先换算为用户身体视角:骨架 `rightWrist` / `rightHand.wrist` / `rightHand` 坐标映射玩家左手,骨架 `leftWrist` / `leftHand.wrist` / `leftHand` 坐标映射玩家右手;换算只用于展示和抓取手身份,不再决定只能选择哪一侧篮子。草稿试玩、发布后正式体验和热身关后的本地 Demo 都复用同一个运行态,因此三条入口都必须具备同一套动作控制能力。
开发者调试输入:
1. 鼠标左键按下并拖动:映射左手位置;
2. 鼠标右键按下并拖动:映射右手位置;
3. 调试输入同样必须先触碰中央物品,物品绑定到目标手后,再拖入左侧或右侧篮子完成选择。
运行态控制按钮不参与调试输入和选篮判定。左上角返回按钮、完成弹层按钮以及后续新增的运行态控制元素,其 `pointerdown` 不得被舞台拖拽逻辑 `preventDefault` 或指针捕获吞掉,保证游戏进行中仍可直接点击返回。
当前篮子判定仍只认篮子主体附近区域,但在上一版核心区基础上扩大约 50%;命中阈值为左篮 `x <= 0.36 && y >= 0.62`、右篮 `x >= 0.64 && y >= 0.62`,既避免物品尚未贴近篮子主体就提前判定,也避免贴到篮子边缘后仍难以命中。
运行态不得新增计时、失败次数、分数、体力或难度递增规则。
音效和语音播报当前只保留接口预留边界,正式语音接口后续接入。
## 7. 发布约束
发布前必须执行:
1. 两个物品名非空;
2. 两个物品名对应的 asset 存在;
3. 标签补齐精确 `寓教于乐`
4. `publicationStatus``draft` 变为 `published`
发布后首版本地响应返回 `publicWorkCode`,用于分享弹窗;正式后端接入时 public code 生成规则需要纳入统一作品号服务。
## 8. 热身关衔接
`/child-motion-demo` 热身完成后的“开始游戏”按钮进入同一个 `BabyObjectMatchRuntimeShell`
热身关独立 Demo 没有创作者草稿上下文,因此使用固定本地 Demo 草稿承载两个物品,仅用于热身关后验证首关体验;正式平台体验仍必须从 `宝贝识物` 模板创作发布后进入寓教于乐板块。
## 9. 验收命令
```bash
npm run test -- src/components/platform-entry/platformEntryCreationTypes.test.ts src/components/edutainment-creation/BabyObjectMatchWorkspace.test.tsx src/components/edutainment-result/BabyObjectMatchResultView.test.tsx src/components/edutainment-runtime/BabyObjectMatchRuntimeShell.test.tsx src/components/child-motion-demo/ChildMotionWarmupDemo.test.tsx src/services/edutainment-baby-object/babyObjectMatchClient.test.ts
cargo test -p api-server edutainment_baby_object --manifest-path server-rs/Cargo.toml
npx vitest run src/components/platform-entry/platformEdutainmentVisibility.test.ts src/components/platform-entry/PlatformWorkDetailView.test.tsx src/components/custom-world-home/creationWorkShelf.test.ts src/services/useMocapInput.test.ts src/services/child-motion-demo/childMotionDebugInput.test.ts src/routing/appRoutes.test.ts
npx eslint src/components/platform-entry/platformEntryCreationTypes.ts src/components/platform-entry/platformEntryCreationTypes.test.ts src/components/platform-entry/PlatformEntryFlowShellImpl.tsx --ext .ts,.tsx --max-warnings 0
npm run check:encoding
npm run typecheck
npm run build:raw
```
若后续接入真实 Rust API 和 SpacetimeDB 表,再补充 `npm run api-server``/healthz`、Rust contract / api-server / spacetime-client 定向测试和 migration 表目录更新。
File diff suppressed because it is too large Load Diff
@@ -105,6 +105,9 @@ npm run check:server-rs-ddd
3. 充值中心、下单校验和支付确认入账都读取 `profile_recharge_product_config`。历史订单保留下单时写入的商品标题、金额、渠道、状态和 provider transaction id,不随配置改动回写。
4. 泥点首充资格按 `user_id + product_id` 的历史 `paid` 订单独立判断。某个档位已支付后,只隐藏该档位的首充赠送;其它未购买档位仍展示和结算首充赠送。
5. `hasPointsRecharged` 只保留为账号是否发生过任一泥点充值的兼容字段,不得驱动所有商品展示隐藏或结算金额计算。前端只渲染后端返回的商品快照。
6. `paymentChannel` 缺失、未知或和设备不匹配时必须拒绝;真实微信渠道只允许 `wechat_mp``wechat_h5``wechat_native`,生产配置不得把真实支付静默降级为 `mock`
7. access JWT 只携带最小设备快照 `device.client_type``device.client_runtime``device.client_platform`。充值下单按该快照拦截渠道:小程序只允许 `wechat_mp`,手机微信内网页只允许 `wechat_h5`,桌面微信内网页只允许 `wechat_native`
8. 所有微信真实渠道都以微信支付通知或服务端查单确认 `SUCCESS` 为到账事实;小程序、H5 跳转和 Native 二维码返回都不能直接发放泥点或会员。
## 外部服务与资产
@@ -135,6 +135,7 @@ UI 相关修改要重点验证:
3. 发布目标必须显式 `--server` / `--server-url`
4. 身份问题先查 `spacetime login show``spacetime server list` 和目标库权限,不通过切回旧 Node / PostgreSQL 绕过。
5. 旧库迁移或 private 表数据保留走 `migration.rs` 的 JSON 导入导出和分片导入思路。
6. Jenkins 数据库导入 / 导出流水线会先加载 `scripts/jenkins-prepare-toolchain-env.sh`,显式补齐 Jenkins 用户的 Node、Cargo、SpacetimeDB 工具链目录;如果目标机器安装路径不同,用 `GENARRATIVE_JENKINS_TOOL_PATHS` 传入额外 `bin` 目录。
## 生产运维
@@ -45,8 +45,10 @@ Genarrative / 陶泥儿是一个 AI 原生互动内容与小游戏平台。当
2. 泥点默认档位为 `60 / 180 / 300 / 680 / 1280 / 3280`,会员默认档位为月卡、季卡、年卡;实际展示、下单校验和支付确认都以后端返回的充值商品配置为准。
3. 首充双倍按泥点商品档位独立计算。用户买过 `points_60` 后,只影响 `points_60` 的首充展示和结算,其它未购买档位仍保留各自首充权益。
4. 前端不得用 `hasPointsRecharged` 统一隐藏所有泥点档位首充权益;该字段只表示账号是否发生过任一泥点充值。
5. 小程序 WebView 充值使用 `wechat_mp` 渠道时,H5 只跳转 native 支付页并在返回后请求服务端查单确认;只有微信通知或查单确认 `SUCCESS` 后才刷新余额或会员状态
6. 后台“充值商品”页维护泥点和会员商品配置,保存后影响新的充值中心快照、下单和支付确认;历史订单保留下单时快照
5. 充值支付渠道只允许由设备平台隔离层解析为 `wechat_mp``wechat_h5``wechat_native`;生产真实支付不得默认落到 `mock`,缺失或未知 `paymentChannel` 必须拒绝
6. 小程序 WebView 充值使用 `wechat_mp` 渠道时,H5 只跳转 native 支付页并在返回后请求服务端查单确认;手机微信内网页使用 `wechat_h5` 跳转微信 H5 支付;桌面微信内网页使用 `wechat_native` 二维码。只有微信通知或查单确认 `SUCCESS` 后才刷新余额或会员状态
7. 后端必须按 access JWT 中的最小设备快照拦截真实微信充值路径,不能只依赖前端隐藏入口或请求体传入的 `paymentChannel`
8. 后台“充值商品”页维护泥点和会员商品配置,保存后影响新的充值中心快照、下单和支付确认;历史订单保留下单时快照。
## 唯一后端路线
@@ -131,6 +131,9 @@ pipeline {
bash -lc '
set -euo pipefail
chmod +x scripts/jenkins-prepare-toolchain-env.sh
source scripts/jenkins-prepare-toolchain-env.sh
chmod +x scripts/deploy/maintenance-on.sh scripts/deploy/maintenance-off.sh
database="${DATABASE:?DATABASE 不能为空}"
@@ -225,6 +225,9 @@ pipeline {
bash -lc '
set -euo pipefail
chmod +x scripts/jenkins-prepare-toolchain-env.sh
source scripts/jenkins-prepare-toolchain-env.sh
chmod +x scripts/deploy/maintenance-on.sh scripts/deploy/maintenance-off.sh
database="${DATABASE:?DATABASE 不能为空}"
+434 -16
View File
File diff suppressed because it is too large Load Diff
+8 -6
View File
@@ -4,16 +4,16 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "node scripts/run-bash-script.mjs scripts/dev-rust-stack.sh",
"dev:rust": "node scripts/run-bash-script.mjs scripts/dev-rust-stack.sh",
"dev:rust:logs": "node scripts/run-bash-script.mjs scripts/spacetime-logs-local.sh",
"dev:web": "node scripts/dev-web-rust.mjs",
"admin-web:dev": "npm --prefix apps/admin-web run dev --",
"dev": "node scripts/dev.mjs",
"dev:spacetime": "node scripts/dev.mjs spacetime",
"dev:api-server": "node scripts/dev.mjs api-server",
"dev:web": "node scripts/dev.mjs web",
"dev:admin-web": "node scripts/dev.mjs admin-web",
"dev:spacetime:logs": "node scripts/run-bash-script.mjs scripts/spacetime-logs-local.sh",
"admin-web:build": "node scripts/admin-web-build.mjs build",
"admin-web:typecheck": "node scripts/admin-web-build.mjs typecheck",
"admin-web:preview": "npm --prefix apps/admin-web run preview --",
"spacetime:generate": "node scripts/generate-spacetime-bindings.mjs",
"api-server": "node scripts/api-server-dev.mjs",
"check:api-server-env": "node scripts/check-api-server-env.mjs",
"deploy:rust:remote": "node scripts/run-bash-script.mjs scripts/deploy-rust-remote.sh",
"build:production-release": "node scripts/run-bash-script.mjs scripts/build-production-release.sh",
@@ -55,6 +55,7 @@
"dotenv": "^17.2.3",
"lucide-react": "^0.546.0",
"motion": "^12.23.24",
"qrcode": "^1.5.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"three": "^0.184.0",
@@ -64,6 +65,7 @@
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
"@types/node": "^22.14.0",
"@types/qrcode": "^1.5.6",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@types/three": "^0.184.0",
@@ -24,7 +24,9 @@ export type BabyObjectMatchVisualAssetKind =
| 'ui-frame'
| 'gift-box'
| 'basket'
| 'smoke-puff';
| 'smoke-puff'
| 'left-hand'
| 'right-hand';
export type BabyObjectMatchVisualAsset = {
assetId: string;
+16 -1
View File
@@ -147,6 +147,14 @@ export type WechatMiniProgramPayParams = {
paySign: string;
};
export type WechatH5Payment = {
h5Url: string;
};
export type WechatNativePayment = {
codeUrl: string;
};
export type CreateProfileRechargeOrderRequest = {
productId: string;
paymentChannel?: string;
@@ -156,6 +164,8 @@ export type CreateProfileRechargeOrderResponse = {
order: ProfileRechargeOrder;
center: ProfileRechargeCenterResponse;
wechatMiniProgramPayParams?: WechatMiniProgramPayParams | null;
wechatH5Payment?: WechatH5Payment | null;
wechatNativePayment?: WechatNativePayment | null;
};
export type ConfirmWechatProfileRechargeOrderResponse = {
@@ -242,7 +252,12 @@ export type RedeemProfileRewardCodeResponse = {
export type ProfileTaskCycle = 'daily';
export type TrackingScopeKind = 'site' | 'work' | 'module' | 'user';
export type AnalyticsGranularity = 'day' | 'week' | 'month' | 'quarter' | 'year';
export type AnalyticsGranularity =
| 'day'
| 'week'
| 'month'
| 'quarter'
| 'year';
export type ProfileTaskStatus =
| 'incomplete'
| 'claimable'

Some files were not shown because too many files have changed in this diff Show More