Prune stale docs and update .hermes content
Delete a large set of outdated documentation (many files under docs/ and .hermes/plans/, including audits, design, prd, technical, planning, assets, and todos). Update and consolidate .hermes content: refresh shared-memory pages (decision-log, development-workflow, document-map, pitfalls, project-overview, team-conventions) and several skills/references under .hermes/skills. Also modify AGENTS.md, README.md, UI_CODING_STANDARD.md, docs/README.md and .encoding-check-ignore. Purpose: clean up stale planning/audit material and keep current hermes documentation and related top-level docs in sync.
This commit is contained in:
@@ -1,299 +0,0 @@
|
||||
# M3:browse history Axum + SpacetimeDB 落地设计
|
||||
|
||||
日期:`2026-04-21`
|
||||
|
||||
关联任务:
|
||||
|
||||
- [../../backend-rewrite-tasklist/02_M3_RUNTIME_PROFILE.md](../../backend-rewrite-tasklist/02_M3_RUNTIME_PROFILE.md)
|
||||
- [./M3_RUNTIME_SETTINGS_AXUM_SPACETIMEDB_DESIGN_2026-04-21.md](./M3_RUNTIME_SETTINGS_AXUM_SPACETIMEDB_DESIGN_2026-04-21.md)
|
||||
|
||||
关联现状:
|
||||
|
||||
- `server-node/src/routes/rpg-profile/rpgProfileRoutes.ts`
|
||||
- `server-node/src/repositories/runtimeRepository.ts`
|
||||
- `packages/shared/src/contracts/runtime.ts`
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
`02_M3_RUNTIME_PROFILE.md` 已经把 `user_browse_history` 和 `browse history` facade 列入 M3,但还没有冻结到可直接编码的字段、去重规则、路由兼容方式和错误语义。
|
||||
|
||||
本文只解决 `browse history` 这一个最小闭环切片:
|
||||
|
||||
1. `user_browse_history` 真相表
|
||||
2. `GET /api/runtime/profile/browse-history`
|
||||
3. `POST /api/runtime/profile/browse-history`
|
||||
4. `DELETE /api/runtime/profile/browse-history`
|
||||
5. `/api/profile/browse-history` 兼容路径
|
||||
|
||||
本文不新建 checklist,不替代 [../../backend-rewrite-tasklist/02_M3_RUNTIME_PROFILE.md](../../backend-rewrite-tasklist/02_M3_RUNTIME_PROFILE.md),只补足本轮编码所需的冻结口径。
|
||||
|
||||
## 2. 旧实现冻结口径
|
||||
|
||||
当前 Node 侧行为来自:
|
||||
|
||||
- `server-node/src/routes/rpg-profile/rpgProfileRoutes.ts`
|
||||
- `server-node/src/repositories/runtimeRepository.ts`
|
||||
|
||||
冻结行为如下。
|
||||
|
||||
### 2.1 路由
|
||||
|
||||
主路径与兼容路径都必须保留:
|
||||
|
||||
1. `GET /api/runtime/profile/browse-history`
|
||||
2. `POST /api/runtime/profile/browse-history`
|
||||
3. `DELETE /api/runtime/profile/browse-history`
|
||||
4. `GET /api/profile/browse-history`
|
||||
5. `POST /api/profile/browse-history`
|
||||
6. `DELETE /api/profile/browse-history`
|
||||
|
||||
所有路径都要求 Bearer JWT。
|
||||
|
||||
### 2.2 数据字段
|
||||
|
||||
单条浏览记录字段与 `packages/shared/src/contracts/runtime.ts` 保持一致:
|
||||
|
||||
1. `ownerUserId`
|
||||
2. `profileId`
|
||||
3. `worldName`
|
||||
4. `subtitle`
|
||||
5. `summaryText`
|
||||
6. `coverImageSrc`
|
||||
7. `themeMode`
|
||||
8. `authorDisplayName`
|
||||
9. `visitedAt`
|
||||
|
||||
### 2.3 POST 请求体兼容
|
||||
|
||||
`POST` 同时支持两种形态:
|
||||
|
||||
1. 单条对象
|
||||
2. `{ entries: [...] }` 批量对象
|
||||
|
||||
批量最多 `100` 条。
|
||||
|
||||
### 2.4 归一化规则
|
||||
|
||||
旧 Node 仓储不是严格校验,而是宽松归一化:
|
||||
|
||||
1. `ownerUserId`、`profileId`、`worldName` 去首尾空白后必须非空,否则该条忽略。
|
||||
2. `subtitle`、`summaryText`、`coverImageSrc` 去首尾空白,空串按空值处理。
|
||||
3. `themeMode` 不做严格枚举校验,未知值统一回落到 `mythic`。
|
||||
4. `authorDisplayName` 空值回落到 `玩家`。
|
||||
5. `visitedAt` 缺失时回落到当前时间。
|
||||
|
||||
### 2.5 去重与排序规则
|
||||
|
||||
旧 Node 仓储的关键行为必须保持:
|
||||
|
||||
1. 去重键:`ownerUserId + profileId`
|
||||
2. 同一批写入时,先按 `visitedAt DESC` 排序,再去重,只保留最新一条
|
||||
3. 表内最终查询结果按 `visitedAt DESC` 返回
|
||||
|
||||
### 2.6 清空行为
|
||||
|
||||
`DELETE` 清空当前用户的全部浏览历史,并返回:
|
||||
|
||||
```json
|
||||
{
|
||||
"entries": []
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Rust 落位决议
|
||||
|
||||
### 3.1 crate 分工
|
||||
|
||||
本切片固定按以下边界落位:
|
||||
|
||||
1. `crates/module-runtime`
|
||||
- 定义 `browse history` DTO、字段校验、去重排序与宽松归一化规则。
|
||||
2. `crates/spacetime-module`
|
||||
- 定义 `user_browse_history` 表。
|
||||
- 提供 `list / upsert / clear` 三个 procedure。
|
||||
3. `crates/spacetime-client`
|
||||
- 提供 `list_platform_browse_history`
|
||||
- 提供 `upsert_platform_browse_history_entries`
|
||||
- 提供 `clear_platform_browse_history`
|
||||
4. `crates/shared-contracts`
|
||||
- 冻结 Axum facade 的请求/响应 DTO。
|
||||
5. `crates/api-server`
|
||||
- 提供双路径兼容 facade。
|
||||
- 保持 envelope / 错误格式与当前 `runtime settings` 一致。
|
||||
|
||||
### 3.2 身份边界
|
||||
|
||||
本轮仍沿用 Axum Bearer JWT 作为唯一鉴权边界:
|
||||
|
||||
1. `require_bearer_auth` 校验 token。
|
||||
2. 从 claims 中提取 `user_id`。
|
||||
3. `user_id` 作为 procedure 入参传入 SpacetimeDB。
|
||||
|
||||
当前阶段不把浏览历史直接暴露给前端直连订阅。
|
||||
|
||||
## 4. SpacetimeDB 表设计
|
||||
|
||||
### 4.1 表名
|
||||
|
||||
`user_browse_history`
|
||||
|
||||
### 4.2 字段
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `browse_history_id` | `String` | 主键,格式为 `user_id:owner_user_id:profile_id` |
|
||||
| `user_id` | `String` | 当前登录用户 |
|
||||
| `owner_user_id` | `String` | 被浏览世界所属用户 |
|
||||
| `profile_id` | `String` | 被浏览世界 profile |
|
||||
| `world_name` | `String` | 世界名 |
|
||||
| `subtitle` | `String` | 副标题 |
|
||||
| `summary_text` | `String` | 摘要 |
|
||||
| `cover_image_src` | `Option<String>` | 封面图 |
|
||||
| `theme_mode` | `RuntimeBrowseHistoryThemeMode` | 主题枚举 |
|
||||
| `author_display_name` | `String` | 作者显示名 |
|
||||
| `visited_at` | `Timestamp` | 最近访问时间 |
|
||||
| `created_at` | `Timestamp` | 首次写入时间 |
|
||||
| `updated_at` | `Timestamp` | 最近更新时间 |
|
||||
|
||||
### 4.3 索引
|
||||
|
||||
至少保留以下访问路径:
|
||||
|
||||
1. 主键 `browse_history_id`
|
||||
2. `(user_id, visited_at)` 用于当前用户按时间倒序列出
|
||||
3. `(user_id, owner_user_id, profile_id)` 用于幂等 upsert
|
||||
|
||||
### 4.4 设计决议
|
||||
|
||||
1. 不额外引入自增 ID,直接把幂等键收口成主键。
|
||||
2. `visited_at` 单独持久化成 `Timestamp`,避免把时间排序退回字符串比较。
|
||||
3. `theme_mode` 在表内固定为枚举,但 Axum 输入仍宽松接受字符串。
|
||||
|
||||
## 5. Procedure 设计
|
||||
|
||||
### 5.1 procedure 列表
|
||||
|
||||
1. `list_platform_browse_history`
|
||||
2. `upsert_platform_browse_history_and_return`
|
||||
3. `clear_platform_browse_history_and_return`
|
||||
|
||||
统一返回:
|
||||
|
||||
```text
|
||||
RuntimeBrowseHistoryProcedureResult {
|
||||
ok
|
||||
entries
|
||||
error_message
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 行为约束
|
||||
|
||||
`list_platform_browse_history`
|
||||
|
||||
1. 校验 `user_id`
|
||||
2. 读取当前用户所有记录
|
||||
3. 按 `visited_at DESC` 返回
|
||||
|
||||
`upsert_platform_browse_history_and_return`
|
||||
|
||||
1. 校验 `user_id`
|
||||
2. 接受单批最多 `100` 条
|
||||
3. 先按旧 Node 规则宽松归一化
|
||||
4. 先按 `visitedAt DESC` 排序,再按 `ownerUserId + profileId` 去重
|
||||
5. 用 `browse_history_id` 幂等 upsert
|
||||
6. 返回当前用户完整倒序列表
|
||||
|
||||
`clear_platform_browse_history_and_return`
|
||||
|
||||
1. 校验 `user_id`
|
||||
2. 删除当前用户全部记录
|
||||
3. 返回空列表
|
||||
|
||||
## 6. Axum facade 设计
|
||||
|
||||
### 6.1 双路径兼容
|
||||
|
||||
两组路径必须共用同一组 handler:
|
||||
|
||||
1. `/api/runtime/profile/browse-history`
|
||||
2. `/api/profile/browse-history`
|
||||
|
||||
只允许路由名不同,不允许行为分叉。
|
||||
|
||||
### 6.2 GET
|
||||
|
||||
行为:
|
||||
|
||||
1. Bearer JWT 校验
|
||||
2. 读取 claims 中的 `user_id`
|
||||
3. 调 `spacetime_client.list_platform_browse_history`
|
||||
4. 返回 `PlatformBrowseHistoryResponse`
|
||||
|
||||
### 6.3 POST
|
||||
|
||||
行为:
|
||||
|
||||
1. Bearer JWT 校验
|
||||
2. 通过 `serde(untagged)` 同时接单条和批量 shape
|
||||
3. 不对 `themeMode` 做严格 400 拒绝
|
||||
4. 对 `ownerUserId`、`profileId`、`worldName` 的缺失或空串按旧 Node 路由规则直接返回 `400`
|
||||
5. 写入成功后返回最新完整列表
|
||||
|
||||
### 6.4 DELETE
|
||||
|
||||
行为:
|
||||
|
||||
1. Bearer JWT 校验
|
||||
2. 清空当前用户全部记录
|
||||
3. 返回 `entries: []`
|
||||
|
||||
### 6.5 错误映射
|
||||
|
||||
1. JSON 解析失败:`400 BAD_REQUEST`
|
||||
2. DTO 构建失败:`400 BAD_REQUEST`
|
||||
3. SpacetimeDB 调用失败:`502 BAD_GATEWAY`
|
||||
4. JWT 缺失或失效:沿用当前 `401 UNAUTHORIZED`
|
||||
|
||||
错误 `details.provider` 固定为:
|
||||
|
||||
1. `browse-history`
|
||||
2. `spacetimedb`
|
||||
|
||||
## 7. 测试策略
|
||||
|
||||
### 7.1 必跑测试
|
||||
|
||||
1. `module-runtime`
|
||||
- 宽松 theme 归一化
|
||||
- `visitedAt` 默认值
|
||||
- 去重与倒序逻辑
|
||||
2. `api-server`
|
||||
- 未登录返回 `401`
|
||||
- 兼容路径与主路径一致
|
||||
- `POST` 同时支持单条和批量
|
||||
- envelope 打开时错误结构稳定
|
||||
|
||||
### 7.2 可选联调测试
|
||||
|
||||
保留 `#[ignore]` 的本地 SpacetimeDB 集成测试:
|
||||
|
||||
1. `POST -> GET`
|
||||
2. `DELETE -> GET`
|
||||
|
||||
## 8. 本文完成定义
|
||||
|
||||
当以下条件成立时,本设计视为完成:
|
||||
|
||||
1. `user_browse_history` 表字段、主键和排序规则已冻结。
|
||||
2. 双路径 facade、请求 shape 和错误契约已冻结。
|
||||
3. 后续编码不再需要猜测:
|
||||
- `themeMode` 是否严格校验
|
||||
- `POST` 是否支持单条/批量双 shape
|
||||
- 去重时机与排序依据
|
||||
|
||||
## 9. 2026-04-22 实际落地进度
|
||||
|
||||
1. `module-runtime` 已切换为“API 入口严格校验 + 领域层静默过滤”的旧 Node 对齐模式。
|
||||
2. `api-server` 已补齐双路径 browse history handler,并补 `401`、`400`、批量 shape、兼容路径一致性测试。
|
||||
3. 剩余阻塞主要在工作树内其他并行任务带来的 Rust 编译占用与跨模块联调,不属于 browse history 方案本身。
|
||||
Reference in New Issue
Block a user