14 KiB
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 |
|
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.ts的getAdminOverview(token)请求GET /admin/api/overview。 - 后端路由在
server-rs/crates/api-server/src/app.rs挂载/admin/api/overview,handler 为admin_overview。 - 表统计逻辑在
server-rs/crates/api-server/src/admin.rs的fetch_database_overview:先读 SpacetimeDB schema 表名,再逐表执行SELECT COUNT(*) AS row_count FROM {table_name};private 或当前身份不可见会显示“不可统计(private 或当前身份不可见)”。 - DTO 在
server-rs/crates/shared-contracts/src/admin.rs的AdminOverviewResponse/AdminDatabaseOverviewPayload/AdminDatabaseTableStatPayload,前端对应类型在apps/admin-web/src/api/adminApiTypes.ts。 - 如果本次需求是“每张表都能查”,优先新增
GET /admin/api/database/tables与GET /admin/api/database/tables/{tableName}/rows两个只读接口,并在前端新建统一的表查询页,而不是把查询逻辑塞回总览页。
1. 先补技术方案文档
项目要求工程修改前先检查/补充落地文档。若没有明确文档,先写到 docs/technical/,至少说明:
- 后台页面目标。
- 后端接口路径、鉴权、query/body、response。
- 数据来源和是否修改 SpacetimeDB schema。
- 前端页面字段、筛选项、导出格式。
- 验收命令。
示例参考:
references/admin-tracking-events-export-2026-05-07.mdreferences/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.rsserver-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获取 token,再spacetime 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.tsapps/admin-web/src/api/adminApiClient.tsapps/admin-web/src/app/adminRoutes.tsapps/admin-web/src/app/AdminShell.tsxapps/admin-web/src/app/AdminApp.tsxapps/admin-web/src/pages/<AdminXxxPage>.tsxapps/admin-web/src/styles/admin.css
接入步骤:
- 在
adminApiTypes.ts增加 query/entry/list 类型。 - 在
adminApiClient.ts增加 API 方法;用URLSearchParams拼非空 query。 - 在
adminRoutes.ts增加 route id、label、hash。 - 在
AdminShell.tsx增加 route icon,routeIcons必须覆盖全部AdminRouteId。 - 在
AdminApp.tsximport 并按 routeId 渲染页面。 - 新增页面组件,保持 UI 简洁,不写大段规则说明。
- 如果页面通过 hash 携带子参数,路由解析和页内参数解析要分开:
resolveAdminRoute()只负责路由片段,页面组件自己解析?table=之类的查询参数;同时要监听hashchange,避免切页后参数不同步。 - 列表行点击跳转优先用 hash,不要额外引入全局路由库或重新发明一套页面状态系统。
Excel 导出推荐做法
后台运营导出不一定要引入 xlsx 依赖;简单表格可用浏览器端 HTML table + .xls:
- Blob MIME:
application/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-serverhttp://127.0.0.1:3100/healthz,后台前端http://127.0.0.1:5173/admin/。npm run dev完整栈:SpacetimeDBhttp://127.0.0.1:3101/v1/ping,api-serverhttp://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依赖spacetimeCLI;先用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:typecheck 报 Cannot find module .../node_modules/typescript/bin/tsc,说明当前 worktree 未安装 npm 依赖;先运行:
npm install
不要把该错误误判成 TypeScript 代码错误。
常见坑
- 只在
app.rsimport handler 不够,必须实际.route(...)挂载,并套require_admin_auth。 cargo fmt --manifest-path server-rs/Cargo.toml在该 workspace 可能报Failed to find targets;进入server-rs后用cargo fmt --all或cargo fmt -p api-server -p shared-contracts --check。cargo fmt --all可能格式化不相关 Rust 文件;提交前用git status检查并 revert 非本任务文件。- patch 工具对 Rust 单文件 lint 可能用 Rust 2015 edition 误报
async fn is not permitted in Rust 2015;以cargo test/check为准。 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.md:npm run dev/scripts/dev-rust-stack.sh在 WSL/Linux 下改用用户级 SpacetimeDB CLI、项目数据目录和显式 publish server,避免项目级--root-dir与冷编译超时的修复记录。