This commit is contained in:
170
docs/technical/ADMIN_TRACKING_EVENT_DETAIL_EXPORT_2026-05-07.md
Normal file
170
docs/technical/ADMIN_TRACKING_EVENT_DETAIL_EXPORT_2026-05-07.md
Normal file
@@ -0,0 +1,170 @@
|
||||
# 后台埋点数据明细与 Excel 导出方案
|
||||
|
||||
> **For Hermes:** Use subagent-driven-development skill to implement this plan task-by-task.
|
||||
|
||||
**Goal:** 在百梦后台新增“埋点数据”页,展示每条埋点原始事件的详细字段,并支持导出为 Excel 可直接打开的表格文件。
|
||||
|
||||
**Architecture:** 后端继续由 `api-server` 作为后台 BFF,经 SpacetimeDB HTTP SQL 只读查询 `tracking_event`,不改变表结构和 reducer。前端在 `apps/admin-web` 中新增独立路由与页面,页面渲染后端返回的原始明细,并在浏览器侧导出 Excel 兼容的 `.xls` HTML 表格,避免新增依赖。
|
||||
|
||||
**Tech Stack:** Rust Axum、SpacetimeDB HTTP SQL、shared-contracts、React 19、TypeScript、Vite。
|
||||
|
||||
---
|
||||
|
||||
## 范围
|
||||
|
||||
本次只做后台只读能力:
|
||||
|
||||
- 展示 `tracking_event` 原始事件明细。
|
||||
- 每条埋点展示:事件 ID、Event Key、事件名称、Scope、Scope ID、Day Key、用户 ID、作品拥有者、Profile ID、模块、metadata、发生时间。
|
||||
- 支持按 Event Key、用户 ID、Scope Kind、Scope ID 筛选。
|
||||
- 支持导出当前筛选结果为 Excel 可打开文件。
|
||||
|
||||
不做:
|
||||
|
||||
- 不新增或修改 SpacetimeDB 表结构。
|
||||
- 不在后台写入或删除埋点。
|
||||
- 不把埋点聚合口径下沉到前端计算。
|
||||
|
||||
## 后端契约
|
||||
|
||||
新增接口:
|
||||
|
||||
```text
|
||||
GET /admin/api/tracking/events?eventKey=&userId=&scopeKind=&scopeId=&limit=
|
||||
```
|
||||
|
||||
鉴权:复用后台 `require_admin_auth`。
|
||||
|
||||
返回:
|
||||
|
||||
```json
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"eventId": "daily-login:user:xxx:123",
|
||||
"eventKey": "daily_login",
|
||||
"eventTitle": "每日登录",
|
||||
"scopeKind": "user",
|
||||
"scopeId": "xxx",
|
||||
"dayKey": 20580,
|
||||
"userId": "xxx",
|
||||
"ownerUserId": null,
|
||||
"profileId": null,
|
||||
"moduleKey": "profile",
|
||||
"metadataJson": "{}",
|
||||
"occurredAt": "2026-05-07T00:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
后端实现要点:
|
||||
|
||||
1. DTO 放在 `shared-contracts/src/admin.rs`,避免 Rust 与前端口径分叉。
|
||||
2. Handler 放在 `api-server/src/admin.rs`,使用当前已有 SpacetimeDB HTTP SQL helper 思路。
|
||||
3. SQL 只读 `tracking_event`,固定白名单列;由于 SpacetimeDB 2.2 HTTP SQL 不支持 `ORDER BY`,后端取回默认 200 / 最大 1000 条后在 API 层按 `occurred_at` 倒序排序。
|
||||
4. 查询条件只通过字符串转义函数拼接,禁止直接拼接未转义用户输入。
|
||||
5. `eventTitle` 由后端根据已知事件 key 映射,未知事件返回 `eventKey`。
|
||||
|
||||
## 前端页面
|
||||
|
||||
新增路由:`#tracking`,导航标题为“埋点数据”。
|
||||
|
||||
页面能力:
|
||||
|
||||
1. 顶部筛选区:Event Key、用户 ID、Scope Kind、Scope ID、刷新、导出 Excel。
|
||||
2. 列表区:移动端可横向滚动,桌面端表格展示。
|
||||
3. 详情区:每行有“详情”按钮,弹出独立面板展示完整字段与格式化后的 metadata JSON。
|
||||
4. 导出:导出当前页面已加载结果,文件名形如 `tracking-events-2026-05-07.xls`。
|
||||
|
||||
导出实现:
|
||||
|
||||
- 使用 HTML table + Excel MIME:`application/vnd.ms-excel;charset=utf-8`。
|
||||
- 文件扩展名使用 `.xls`,Excel/WPS 可直接打开。
|
||||
- 所有单元格做 HTML 转义。
|
||||
- metadata 保留原始 JSON 文本,便于运营继续筛选。
|
||||
|
||||
## 验收命令
|
||||
|
||||
```bash
|
||||
npm run check:encoding
|
||||
npm run admin-web:typecheck
|
||||
cargo test -p shared-contracts -p api-server admin_tracking -- --nocapture
|
||||
```
|
||||
|
||||
如后端接口改动较大,再补充:
|
||||
|
||||
```bash
|
||||
npm run api-server
|
||||
curl http://127.0.0.1:<port>/healthz
|
||||
```
|
||||
|
||||
## 实施任务
|
||||
|
||||
### Task 1: 补充 shared-contracts 后台埋点 DTO
|
||||
|
||||
**Files:**
|
||||
- Modify: `server-rs/crates/shared-contracts/src/admin.rs`
|
||||
|
||||
**Steps:**
|
||||
1. 新增 `AdminTrackingEventListQuery`。
|
||||
2. 新增 `AdminTrackingEventEntryPayload`。
|
||||
3. 新增 `AdminTrackingEventListResponse`。
|
||||
4. 为 DTO 添加中文注释。
|
||||
|
||||
### Task 2: 增加后端后台埋点查询接口
|
||||
|
||||
**Files:**
|
||||
- Modify: `server-rs/crates/api-server/src/admin.rs`
|
||||
- Modify: `server-rs/crates/api-server/src/app.rs`
|
||||
|
||||
**Steps:**
|
||||
1. 在 `admin.rs` 新增 query 解析与 SQL 构造。
|
||||
2. 复用 SpacetimeDB HTTP SQL 调用风格读取 rows。
|
||||
3. 新增 `admin_list_tracking_events` handler。
|
||||
4. 在 `app.rs` 挂载 `/admin/api/tracking/events`。
|
||||
5. 添加单元测试覆盖 SQL 字符串转义、limit clamp、SQL 响应解析。
|
||||
|
||||
### Task 3: 增加前端 API 类型与客户端方法
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/admin-web/src/api/adminApiTypes.ts`
|
||||
- Modify: `apps/admin-web/src/api/adminApiClient.ts`
|
||||
|
||||
**Steps:**
|
||||
1. 新增埋点 entry/list/query 类型。
|
||||
2. 新增 `listAdminTrackingEvents(token, query)`。
|
||||
3. 使用 `URLSearchParams` 拼接非空查询字段。
|
||||
|
||||
### Task 4: 新增后台埋点数据页面
|
||||
|
||||
**Files:**
|
||||
- Create: `apps/admin-web/src/pages/AdminTrackingEventsPage.tsx`
|
||||
- Modify: `apps/admin-web/src/styles/admin.css`
|
||||
|
||||
**Steps:**
|
||||
1. 实现筛选、刷新、错误状态。
|
||||
2. 实现明细表格。
|
||||
3. 实现独立详情面板。
|
||||
4. 实现 Excel `.xls` 导出。
|
||||
5. 保持 UI 简洁,不添加说明类大段文案。
|
||||
|
||||
### Task 5: 接入后台路由与导航
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/admin-web/src/app/adminRoutes.ts`
|
||||
- Modify: `apps/admin-web/src/app/AdminShell.tsx`
|
||||
- Modify: `apps/admin-web/src/app/AdminApp.tsx`
|
||||
|
||||
**Steps:**
|
||||
1. 增加 `tracking` 路由。
|
||||
2. 导航增加图标。
|
||||
3. `AdminApp` 渲染新页面。
|
||||
|
||||
### Task 6: 验证并提交
|
||||
|
||||
**Steps:**
|
||||
1. 运行 `npm run check:encoding`。
|
||||
2. 运行 `npm run admin-web:typecheck`。
|
||||
3. 运行后端相关 cargo test。
|
||||
4. 修复问题后提交并推送当前分支。
|
||||
69
docs/technical/ALIYUN_SMS_TIMESTAMP_FORMAT_FIX_2026-05-07.md
Normal file
69
docs/technical/ALIYUN_SMS_TIMESTAMP_FORMAT_FIX_2026-05-07.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# 短信验证码阿里云时间戳格式修复(2026-05-07)
|
||||
|
||||
## 背景
|
||||
|
||||
使用阿里云短信验证码真实 provider 发送验证码时,接口返回:
|
||||
|
||||
```text
|
||||
短信验证码发送失败:Specified time stamp or date value is not well formatted.
|
||||
```
|
||||
|
||||
该错误来自阿里云 OpenAPI 网关对签名请求头 `x-acs-date` 的格式校验。
|
||||
|
||||
## 根因
|
||||
|
||||
`server-rs/crates/platform-auth/src/lib.rs` 中阿里云 ACS3 签名逻辑会构造 `x-acs-date` 请求头。
|
||||
|
||||
原实现使用 `time::format_description::well_known::Rfc3339`,当 `OffsetDateTime::now_utc()` 带纳秒时会生成形如:
|
||||
|
||||
```text
|
||||
2026-05-07T14:23:59.364767Z
|
||||
```
|
||||
|
||||
阿里云 ACS3 签名要求 `x-acs-date` 使用不带小数秒的 UTC ISO 8601 格式:
|
||||
|
||||
```text
|
||||
yyyy-MM-dd'T'HH:mm:ss'Z'
|
||||
```
|
||||
|
||||
即:
|
||||
|
||||
```text
|
||||
2026-05-07T14:23:59Z
|
||||
```
|
||||
|
||||
带小数秒的时间戳会被阿里云网关判定为格式非法,从而返回 `Specified time stamp or date value is not well formatted.`。
|
||||
|
||||
## 修复方案
|
||||
|
||||
将 `current_aliyun_timestamp()` 改为手动输出不带小数秒的 UTC ISO 8601 格式:
|
||||
|
||||
```text
|
||||
yyyy-MM-dd'T'HH:mm:ss'Z'
|
||||
```
|
||||
|
||||
并新增单元测试,确保:
|
||||
|
||||
- 长度等于 `2026-05-07T12:34:56Z`;
|
||||
- 固定位置包含 `-`、`T`、`:`、`Z`;
|
||||
- 不包含小数点;
|
||||
- 除固定分隔符外均为数字。
|
||||
|
||||
## 影响范围
|
||||
|
||||
- 仅影响阿里云短信验证码 provider 的请求签名头 `x-acs-date`。
|
||||
- 不改动短信模板、签名、验证码业务参数。
|
||||
- 不改动 mock 短信 provider。
|
||||
- 不涉及前端接口契约变化。
|
||||
|
||||
## 验收
|
||||
|
||||
执行:
|
||||
|
||||
```bash
|
||||
cd server-rs
|
||||
cargo test -p platform-auth aliyun -- --nocapture
|
||||
cargo fmt -p platform-auth --check
|
||||
```
|
||||
|
||||
预期:相关测试通过,格式检查通过。
|
||||
Reference in New Issue
Block a user