Files
Genarrative/.hermes/skills/genarrative-admin-backoffice/SKILL.md
kdletters 10ed4fa051
Some checks failed
CI / verify (push) Has been cancelled
docs: clarify SpacetimeDB root-dir usage
2026-05-11 14:27:33 +08:00

14 KiB
Raw Blame History

name, short_description, description, version, author, license, metadata
name short_description description version author license metadata
genarrative-admin-backoffice 在 Genarrative/百梦后台新增或修改管理页、后台只读/写接口、导出能力时使用。 在 Genarrative/百梦后台新增或修改管理页、后台 BFF 接口、shared-contracts/admin DTO、admin-web 路由导航、Excel/表格导出与验证发布时使用。 1.0.0 Hermes Agent MIT
hermes
tags related_skills
Genarrative
百梦后台
admin-web
后台接口
Excel导出
Rust
Axum
SpacetimeDB
genarrative-play-type-integration

Genarrative / 百梦后台管理功能接入流程

用于在 Genarrative 项目中新增或修改百梦后台管理端能力,包括后台页面、后台 API、管理端 DTO、导航路由、表格明细、导出、鉴权与验证。

适用场景

  • 新增百梦后台页面或导航项,例如“埋点数据”“任务配置”“邀请码”。
  • 新增 /admin/api/* 接口。
  • 修改 apps/admin-web 的后台页面、API client、路由、Shell 导航。
  • 在后台展示 SpacetimeDB 表明细或统计数据。
  • 新增“总览 → 单表查询”这类表统计跳转与查询页联动能力时,优先复用现有总览页的表统计作为入口,不另造第二套表目录。
  • 后台导出 CSV / Excel / .xls 表格文件。
  • 后台数据页中与业务事件、任务、登录等链路相关的问题,不能只看后台页面;要追到对应前台/API/reducer 写入点,确认“数据何时产生”。例如排查 daily_login 时,不要假设它一定由认证登录接口写入;先核对当前分支实现。历史实现曾在 GET /api/profile/tasks 打开任务中心时写入、POST /api/profile/tasks/{task_id}/claim 领奖时兜底写入后续方案A把“任务中心读取写埋点”拆出为独立 procedure任务中心只读取/刷新进度,登录成功链路应显式调用每日登录埋点入口。

标准落地顺序

0. 先确认现有后台入口

在新增后台页或回答“后台某个数据在哪里”前,先核对是否已有入口,避免重复造页:

  • 数据库表统计当前在后台“总览”页,不是独立页面:apps/admin-web/src/pages/AdminOverviewPage.tsx 的“表统计”面板。
  • 表统计行可直接跳转到表查询页:点击后设置 window.location.hash = #tables?table=<tableName>,由单独的 #tables 页接收参数并查询。
  • #tables 页应在首次加载和 hashchange 时都重新读取 table 参数,避免只在初次 mount 时生效。
  • 前端通过 apps/admin-web/src/api/adminApiClient.tsgetAdminOverview(token) 请求 GET /admin/api/overview
  • 后端路由在 server-rs/crates/api-server/src/app.rs 挂载 /admin/api/overviewhandler 为 admin_overview
  • 表统计逻辑在 server-rs/crates/api-server/src/admin.rsfetch_database_overview:先读 SpacetimeDB schema 表名,再逐表执行 SELECT COUNT(*) AS row_count FROM {table_name}private 或当前身份不可见会显示“不可统计private 或当前身份不可见)”。
  • DTO 在 server-rs/crates/shared-contracts/src/admin.rsAdminOverviewResponse / AdminDatabaseOverviewPayload / AdminDatabaseTableStatPayload,前端对应类型在 apps/admin-web/src/api/adminApiTypes.ts
  • 如果本次需求是“每张表都能查”,优先新增 GET /admin/api/database/tablesGET /admin/api/database/tables/{tableName}/rows 两个只读接口,并在前端新建统一的表查询页,而不是把查询逻辑塞回总览页。

1. 先补技术方案文档

项目要求工程修改前先检查/补充落地文档。若没有明确文档,先写到 docs/technical/,至少说明:

  • 后台页面目标。
  • 后端接口路径、鉴权、query/body、response。
  • 数据来源和是否修改 SpacetimeDB schema。
  • 前端页面字段、筛选项、导出格式。
  • 验收命令。

示例参考:

  • references/admin-tracking-events-export-2026-05-07.md
  • references/admin-database-table-query-2026-05-08.md

2. 后端 DTO 放 shared-contracts/admin

文件:

  • server-rs/crates/shared-contracts/src/admin.rs

做法:

  • 新增 request/query/response DTO。
  • 使用 #[serde(rename_all = "camelCase")]
  • 添加中文注释。
  • 字段名与前端管理端类型保持一致。

如果 apps/admin-web 当前没有直接消费 Rust shared-contracts 生成物,还要同步:

  • apps/admin-web/src/api/adminApiTypes.ts

3. 后端 handler 放 api-server/admin.rs

文件:

  • server-rs/crates/api-server/src/admin.rs
  • server-rs/crates/api-server/src/app.rs

要求:

  • Handler 使用 Extension(_admin): Extension<AuthenticatedAdmin>,并在 router 中套 require_admin_auth
  • 只读接口也必须走后台鉴权。
  • query 参数使用 Query<T>
  • 返回 json_success_body(Some(&request_context), payload)
  • app.rs 挂到 /admin/api/...

4. 读取 SpacetimeDB 表明细时优先 HTTP SQL 只读

适合后台只读运营页:

  • 不改表结构。
  • 不新增 reducer。
  • API Server 通过 SpacetimeDB HTTP SQL 读取真实数据。

注意:

  • SQL 字段固定白名单,不要 SELECT *
  • 用户输入只允许有限筛选字段,手动 trim、白名单枚举、字符串转义。
  • limit 必须 clamp例如默认 200、最大 1000。
  • SpacetimeDB 2.2 HTTP SQL 不支持 ORDER BY如果后台需要倒序展示明细SQL 中不要拼 ORDER BY,先查有限 LIMIT,再在 api-server 内按时间字段排序,否则会返回 HTTP 400 Unsupported: SELECT ... ORDER BY ... LIMIT ...
  • 如果 HTTP SQL 返回 no such table ... If the table exists, it may be marked private,不要急着改表名或新增 reducer先确认本地 CLI 是否以当前 standalone 的 identity/token 登录。清空本地数据库或重建 standalone 后,旧 CLI token 可能看不到 private table。按“本地 private table SQL 权限修复”流程用 /v1/identity 获取 tokenspacetime login --token 登录。
  • SQL 解析要兼容 SpacetimeDB HTTP SQL 的 statement array + rows 形态。
  • SpacetimeDB HTTP SQL 读取 private table 时enum / Option / Timestamp 可能以 SATS 原始 JSON 返回,例如 scope_kind=[3,[]]Some("user")=[0,"user"]None=[1,[]]Timestamp=[1778207451731746]。后台列表、详情弹窗和 Excel 导出不要直接展示这些原始形态;应在 api-server 解析层或前端展示层转换为人可读值enum 映射为业务字符串Option 的 None 显示 -,微秒级 Timestamp 格式化为本地可读时间。
  • 可复用已有 /v1/database/{db}/sql 请求风格和 token 配置。

5. 前端接入 admin-web

常改文件:

  • apps/admin-web/src/api/adminApiTypes.ts
  • apps/admin-web/src/api/adminApiClient.ts
  • apps/admin-web/src/app/adminRoutes.ts
  • apps/admin-web/src/app/AdminShell.tsx
  • apps/admin-web/src/app/AdminApp.tsx
  • apps/admin-web/src/pages/<AdminXxxPage>.tsx
  • apps/admin-web/src/styles/admin.css

接入步骤:

  1. adminApiTypes.ts 增加 query/entry/list 类型。
  2. adminApiClient.ts 增加 API 方法;用 URLSearchParams 拼非空 query。
  3. adminRoutes.ts 增加 route id、label、hash。
  4. AdminShell.tsx 增加 route iconrouteIcons 必须覆盖全部 AdminRouteId
  5. AdminApp.tsx import 并按 routeId 渲染页面。
  6. 新增页面组件,保持 UI 简洁,不写大段规则说明。
  7. 如果页面通过 hash 携带子参数,路由解析和页内参数解析要分开:resolveAdminRoute() 只负责路由片段,页面组件自己解析 ?table= 之类的查询参数;同时要监听 hashchange,避免切页后参数不同步。
  8. 列表行点击跳转优先用 hash不要额外引入全局路由库或重新发明一套页面状态系统。

Excel 导出推荐做法

后台运营导出不一定要引入 xlsx 依赖;简单表格可用浏览器端 HTML table + .xls

  • Blob MIMEapplication/vnd.ms-excel;charset=utf-8
  • 文件扩展名:.xls
  • 文本前加 UTF-8 BOM / <meta charset="UTF-8">
  • 所有单元格做 HTML escape。
  • ID、大数字、日期类字段使用 mso-number-format:'\@'; 保持文本格式,避免 Excel 科学计数法。
  • 导出当前筛选结果,避免后端新增 Excel 库依赖。

本地启动与联调

后台改完后如需本地查看页面和接口,优先按本次联调范围选择脚本:

# 只看后台页面 + api-server不要求 SpacetimeDB 真实数据
npm run api-server
npm run admin-web:dev -- --host 127.0.0.1

# 完整 Rust 本地栈SpacetimeDB + 发布模块 + api-server + 主站 + 后台
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 完整栈SpacetimeDB http://127.0.0.1:3101/v1/pingapi-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 或读取进程环境核对,敏感值输出必须打码。

  • 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 安装同步到项目目录。

  • 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

  • 用户贴出的 Hermes background watch 通知可能来自已退出的旧 session。先用 process poll 查该 session 状态,再判断是否需要处理;不要把旧失败误判成当前服务失败。

测试与验证

常用命令:

# Rust 格式化检查
cd server-rs
cargo fmt -p api-server -p shared-contracts --check

# 后端相关测试,按测试名过滤
cargo test -p api-server admin_tracking -- --nocapture

# 前端后台类型检查 / 构建
cd ..
npm run admin-web:typecheck
npm run admin-web:build

# 中文/编码检查
npm run check:encoding

# diff 空白检查
git diff --check

如果 npm run admin-web:typecheckCannot find module .../node_modules/typescript/bin/tsc,说明当前 worktree 未安装 npm 依赖;先运行:

npm install

不要把该错误误判成 TypeScript 代码错误。

常见坑

  1. 只在 app.rs import handler 不够,必须实际 .route(...) 挂载,并套 require_admin_auth
  2. cargo fmt --manifest-path server-rs/Cargo.toml 在该 workspace 可能报 Failed to find targets;进入 server-rs 后用 cargo fmt --allcargo fmt -p api-server -p shared-contracts --check
  3. cargo fmt --all 可能格式化不相关 Rust 文件;提交前用 git status 检查并 revert 非本任务文件。
  4. patch 工具对 Rust 单文件 lint 可能用 Rust 2015 edition 误报 async fn is not permitted in Rust 2015;以 cargo test/check 为准。
  5. adminRoutes 新增 route id 后,AdminShell.routeIcons 必须同步,否则 TypeScript 会因 satisfies Record<AdminRouteId, ...> 报错。
  • 后台页面中的中文和 JSON 预览要避免整文件重写导致编码问题;修改后运行 npm run check:encoding
  • 后台数据页移动端要保证表格横向滚动,不要让整页布局撑坏。
  • 若用户追问“之前不是说要把 npm run dev 修好吗”这类已承诺的 dev 启动问题,不要只解释;先复现 npm run dev再按启动日志修脚本并验证到服务就绪。WSL/Linux 下本地开发应走 spacetime start --data-dir=server-rs/.spacetimedb/local/data 这一类数据目录隔离,不再用项目级 --root-dir,详见 references/dev-rust-stack-startup-2026-05-08.md
  • 涉及敏感配置、token、密码、连接串时输出和文档中统一写 [REDACTED]

参考资料

  • references/admin-database-table-query-2026-05-08.md:本次后台数据库表查询接入的实现要点、校验规则与验证结果。
  • references/admin-tracking-events-export-2026-05-07.md本次新增后台“埋点数据”页、SpacetimeDB HTTP SQL 只读明细、前端 .xls 导出的实现细节。
  • references/private-table-sql-token-refresh.md:本地清库/重建 standalone 后,用 /v1/identity + spacetime login --token 刷新 CLI token以便 HTTP SQL 读取 private table。
  • 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.mdnpm run dev / scripts/dev-rust-stack.sh 在 WSL/Linux 下改用用户级 SpacetimeDB CLI、项目数据目录和显式 publish server避免项目级 --root-dir 与冷编译超时的修复记录。