feat: 添加 Analytics 分析功能(行为追踪、错误码、协议更新)
This commit is contained in:
@@ -545,8 +545,8 @@ app.mount("/analytics", StaticFiles(directory="web/dist", html=True), name="anal
|
||||
1. 前端登录页输入密码
|
||||
2. 调用 `POST /api/v1/analytics/login` 验证
|
||||
3. 后端读取 `.env` 中 `ANALYTICS_PASSWORD` 验证
|
||||
4. 验证成功返回 HMAC Token(5分钟有效),前端存 sessionStorage
|
||||
5. 后续请求带 Token,后端验证
|
||||
4. 验证成功返回 HMAC Token(5分钟有效)和数据读取基地址,前端存 sessionStorage
|
||||
5. 后续请求带 Bearer Token,后端验证后返回对应日期 JSONL 内容
|
||||
|
||||
### 6.5 页面设计
|
||||
|
||||
@@ -569,9 +569,9 @@ app.mount("/analytics", StaticFiles(directory="web/dist", html=True), name="anal
|
||||
|
||||
### 6.6 数据读取
|
||||
|
||||
- 前端通过 `GET /api/v1/analytics/summary` 获取聚合数据
|
||||
- 后端解析 `backend/data/analytics/*.jsonl` 文件并聚合
|
||||
- 提供 `GET /api/v1/analytics/daily` 等查询接口
|
||||
- 前端登录成功后获取 `data_base_url`(当前为 `/api/v1/analytics/data`)
|
||||
- 前端按日期请求 `GET /api/v1/analytics/data/{YYYY-MM-DD}` 获取 JSONL 文本并在页面聚合
|
||||
- 后端读取 `backend/data/analytics/*.jsonl` 原始数据返回
|
||||
|
||||
---
|
||||
|
||||
@@ -601,7 +601,9 @@ SOCIAL_ANALYTICS__PASSWORD=your-secure-password
|
||||
**响应(成功):**
|
||||
```json
|
||||
{
|
||||
"token": "jwt-token-here"
|
||||
"success": true,
|
||||
"token": "signed-token",
|
||||
"data_base_url": "/api/v1/analytics/data"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ data: <json>
|
||||
"type": "STEP_STARTED",
|
||||
"threadId": "...",
|
||||
"runId": "...",
|
||||
"stepName": "router" | "worker" | "memory"
|
||||
"stepName": "router" | "worker"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -121,7 +121,7 @@ data: <json>
|
||||
"type": "STEP_FINISHED",
|
||||
"threadId": "...",
|
||||
"runId": "...",
|
||||
"stepName": "router" | "worker" | "memory"
|
||||
"stepName": "router" | "worker"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -137,7 +137,7 @@ data: <json>
|
||||
"messageId": "...",
|
||||
"toolCallId": "...",
|
||||
"toolCallName": "...",
|
||||
"stage": "worker" | "memory"
|
||||
"stage": "worker"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -152,7 +152,7 @@ data: <json>
|
||||
"toolCallId": "...",
|
||||
"toolCallName": "...",
|
||||
"args": {},
|
||||
"stage": "worker" | "memory"
|
||||
"stage": "worker"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -166,7 +166,7 @@ data: <json>
|
||||
"messageId": "...",
|
||||
"toolCallId": "...",
|
||||
"toolCallName": "...",
|
||||
"stage": "worker" | "memory"
|
||||
"stage": "worker"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -179,7 +179,7 @@ data: <json>
|
||||
"runId": "...",
|
||||
"messageId": "...",
|
||||
"role": "tool",
|
||||
"stage": "worker" | "memory",
|
||||
"stage": "worker",
|
||||
"tool_name": "...",
|
||||
"tool_call_id": "...",
|
||||
"tool_call_args": {},
|
||||
@@ -239,7 +239,7 @@ SSE 协议中的工具名字段保持后端原样,不做服务端翻译:
|
||||
"runId": "...",
|
||||
"messageId": "...",
|
||||
"role": "assistant",
|
||||
"stage": "worker" | "memory",
|
||||
"stage": "worker",
|
||||
"status": "success" | "partial_success" | "failed",
|
||||
"answer": "...",
|
||||
"key_points": [],
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
# Analytics Events And Dashboard Protocol
|
||||
|
||||
本文档定义 analytics 采集与查询协议,覆盖移动端/前端事件上报、Dashboard 登录与按日数据读取。
|
||||
|
||||
## 1. Scope
|
||||
|
||||
- 事件写入接口:`POST /api/v1/analytics/events`
|
||||
- Dashboard 登录接口:`POST /api/v1/analytics/login`
|
||||
- 按日数据读取接口:`GET /api/v1/analytics/data/{YYYY-MM-DD}`
|
||||
- 存储介质:服务端 JSONL 文件(由 `SOCIAL_ANALYTICS__DATA_PATH` 指定)
|
||||
|
||||
## 2. Event Ingestion
|
||||
|
||||
### Endpoint
|
||||
|
||||
- Method: `POST`
|
||||
- Path: `/api/v1/analytics/events`
|
||||
|
||||
### Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"client_time": "2026-04-02T10:00:00Z",
|
||||
"sdk_version": "1.0.0",
|
||||
"events": [
|
||||
{
|
||||
"event_id": "evt-1",
|
||||
"event_type": "page_view",
|
||||
"timestamp": "2026-04-02T10:00:00Z",
|
||||
"user_id": "user-1",
|
||||
"device_id": "device-1",
|
||||
"session_id": "session-1",
|
||||
"platform": "android",
|
||||
"app_version": "0.1.2",
|
||||
"app_build": "5",
|
||||
"env": "prod",
|
||||
"page_name": "home",
|
||||
"trace_id": "trace-1",
|
||||
"request_id": "request-1",
|
||||
"attributes": {
|
||||
"from": "banner"
|
||||
},
|
||||
"metrics": {
|
||||
"latency_ms": 123
|
||||
},
|
||||
"context": {
|
||||
"network_type": "wifi",
|
||||
"os_version": "Android 14",
|
||||
"device_model": "Pixel",
|
||||
"locale": "zh-CN",
|
||||
"timezone": "Asia/Shanghai"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Response Body
|
||||
|
||||
```json
|
||||
{
|
||||
"received": 1,
|
||||
"queued": true
|
||||
}
|
||||
```
|
||||
|
||||
语义说明:
|
||||
|
||||
- `received` 表示本次接收的事件条数。
|
||||
- `queued=true` 表示事件已被服务端接收并触发写入流程;该字段不承诺具体调度方式(请求内写入或队列写入)。
|
||||
|
||||
## 3. Dashboard Login
|
||||
|
||||
### Endpoint
|
||||
|
||||
- Method: `POST`
|
||||
- Path: `/api/v1/analytics/login`
|
||||
|
||||
### Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"password": "<analytics-password>"
|
||||
}
|
||||
```
|
||||
|
||||
### Response Body
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data_base_url": "/api/v1/analytics/data",
|
||||
"token": "<bearer-token>"
|
||||
}
|
||||
```
|
||||
|
||||
语义说明:
|
||||
|
||||
- `token` 为短时效 Bearer Token。
|
||||
- `data_base_url` 为前端读取按日数据的基路径。
|
||||
|
||||
## 4. Read Daily Data
|
||||
|
||||
### Endpoint
|
||||
|
||||
- Method: `GET`
|
||||
- Path: `/api/v1/analytics/data/{date}`
|
||||
- Header: `Authorization: Bearer <token>`
|
||||
- `date` 格式要求:`YYYY-MM-DD`
|
||||
|
||||
### Success Response
|
||||
|
||||
- Status: `200`
|
||||
- Content-Type: `application/x-ndjson`
|
||||
- Body: 当日 JSONL 文本(每行一个事件 JSON)
|
||||
|
||||
## 5. Error Contract
|
||||
|
||||
错误响应必须遵循 RFC7807 + 稳定错误码规范,错误码注册表见:
|
||||
|
||||
- `docs/protocols/common/http-error-codes.md`
|
||||
|
||||
analytics 相关错误码包括:
|
||||
|
||||
- `ANALYTICS_LOGIN_PASSWORD_INVALID`
|
||||
- `ANALYTICS_AUTH_HEADER_MISSING`
|
||||
- `ANALYTICS_AUTH_SCHEME_INVALID`
|
||||
- `ANALYTICS_AUTH_TOKEN_MISSING`
|
||||
- `ANALYTICS_TOKEN_MALFORMED`
|
||||
- `ANALYTICS_TOKEN_SIGNATURE_INVALID`
|
||||
- `ANALYTICS_TOKEN_PAYLOAD_INVALID`
|
||||
- `ANALYTICS_TOKEN_EXPIRED`
|
||||
- `ANALYTICS_DATE_FORMAT_INVALID`
|
||||
- `ANALYTICS_FILE_NOT_FOUND`
|
||||
|
||||
## 6. Storage Contract
|
||||
|
||||
- 后端写入路径由 `SOCIAL_ANALYTICS__DATA_PATH` 控制,默认:`backend/data/analytics`。
|
||||
- 按日存储文件名:`{YYYY-MM-DD}.jsonl`。
|
||||
- 生产部署推荐将宿主机目录挂载到容器内该路径,避免容器重建导致数据丢失。
|
||||
|
||||
## 7. Compatibility Strategy
|
||||
|
||||
- 策略:**backward-compatible additive change**。
|
||||
- 允许在事件对象中新增可选字段(`attributes` / `metrics` / `context` 内部字段)。
|
||||
- 不允许移除现有必填字段或修改既有字段语义;若必须变更,需新增版本化协议文档并提供迁移说明。
|
||||
@@ -84,6 +84,16 @@ When creating/modifying/deprecating any code, this table must be updated in the
|
||||
| `AUTH_REFRESH_TOKEN_MISSING` | auth | 401 | Refresh token is missing for logout/refresh |
|
||||
| `AUTH_USER_NOT_FOUND` | auth | 404 | User lookup by phone returns no match |
|
||||
| `AUTH_UNAUTHORIZED` | auth | 401 | Authorization header or token is invalid |
|
||||
| `ANALYTICS_LOGIN_PASSWORD_INVALID` | analytics | 401 | Analytics dashboard password is invalid |
|
||||
| `ANALYTICS_AUTH_HEADER_MISSING` | analytics | 401 | Authorization header is missing when reading analytics data |
|
||||
| `ANALYTICS_AUTH_SCHEME_INVALID` | analytics | 401 | Authorization scheme is invalid; Bearer token required |
|
||||
| `ANALYTICS_AUTH_TOKEN_MISSING` | analytics | 401 | Bearer token is missing |
|
||||
| `ANALYTICS_TOKEN_MALFORMED` | analytics | 401 | Analytics token format is malformed |
|
||||
| `ANALYTICS_TOKEN_SIGNATURE_INVALID` | analytics | 401 | Analytics token signature verification failed |
|
||||
| `ANALYTICS_TOKEN_PAYLOAD_INVALID` | analytics | 401 | Analytics token payload cannot be parsed |
|
||||
| `ANALYTICS_TOKEN_EXPIRED` | analytics | 401 | Analytics token is expired |
|
||||
| `ANALYTICS_DATE_FORMAT_INVALID` | analytics | 400 | Analytics date must use YYYY-MM-DD format |
|
||||
| `ANALYTICS_FILE_NOT_FOUND` | analytics | 404 | Analytics day file does not exist |
|
||||
| `JWT_VERIFIER_NOT_CONFIGURED` | auth | 503 | JWT verifier configuration is missing |
|
||||
| `AUTOMATION_JOB_LIMIT_EXCEEDED` | automation_jobs | 400 | User-created automation jobs exceed allowed limit |
|
||||
| `AUTOMATION_SYSTEM_JOB_MODIFICATION_FORBIDDEN` | automation_jobs | 403 | System bootstrap job cannot be modified |
|
||||
@@ -150,29 +160,13 @@ When creating/modifying/deprecating any code, this table must be updated in the
|
||||
| `FRIENDSHIP_NOT_FOUND` | friendships | 404 | Friendship record not found |
|
||||
| `FRIENDSHIP_REMOVE_REQUIRES_ACCEPTED` | friendships | 400 | Only accepted friendships can be removed |
|
||||
|
||||
## Registry Coverage Check Script
|
||||
## Registry Coverage Check
|
||||
|
||||
Use the checker script to ensure this registry and frontend code mapping stay aligned:
|
||||
当前仓库未内置自动校验脚本,维护流程按以下约束执行:
|
||||
|
||||
```bash
|
||||
python3 scripts/check_error_code_registry.py
|
||||
```
|
||||
|
||||
Optional arguments:
|
||||
|
||||
- `--doc`: custom registry markdown path
|
||||
- `--mapper`: custom frontend mapper path (default: `apps/lib/core/network/error_code_mapper.dart`)
|
||||
|
||||
Output always includes three result groups:
|
||||
|
||||
- doc has code but frontend has no mapping
|
||||
- frontend maps code but doc has no such code
|
||||
- duplicate codes
|
||||
|
||||
Exit code policy:
|
||||
|
||||
- `0`: no inconsistency found
|
||||
- non-`0`: at least one inconsistency found or input path invalid
|
||||
- 更新本文件错误码时,同步检查前端映射文件:`apps/lib/data/network/error_code_mapper.dart`
|
||||
- 任何新增/变更/废弃错误码必须在同一 PR 中完成「协议文档 + 前端映射 + 后端返回码」三方对齐
|
||||
- 若后续补充自动校验脚本,需在本节追加命令与输出约定
|
||||
|
||||
## Agent Error Code Set
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ scheduler computation, and Flutter settings pages.
|
||||
- `owner_id`: UUID
|
||||
- `title`: string
|
||||
- `bootstrap_key`: string | null (引导配置键,用于标识预设任务模板)
|
||||
- `is_system`: boolean (`bootstrap_key != null` 时为 `true`,只读派生字段)
|
||||
- `config`: object
|
||||
- `input_template`: string
|
||||
- `enabled_tools`: string[]
|
||||
|
||||
@@ -60,7 +60,7 @@ Base URL: `/api/v1/inbox/messages`
|
||||
"phone": "string | null"
|
||||
},
|
||||
"summary": "string",
|
||||
"permission": "int (1=view, 4=edit, 8=invite)",
|
||||
"permission": "int (1=view, 2=invite, 4=edit, 8=delete, 15=owner)",
|
||||
"action": "pending"
|
||||
}
|
||||
```
|
||||
@@ -141,7 +141,6 @@ Base URL: `/api/v1/inbox/messages`
|
||||
"message_type": "InboxMessageType",
|
||||
"schedule_item_id": "uuid | null",
|
||||
"friendship_id": "uuid | null",
|
||||
"group_id": "uuid | null",
|
||||
"content": "CalendarInviteContent | CalendarUpdateContent | CalendarDeleteContent | FriendshipContent | null",
|
||||
"is_read": "boolean",
|
||||
"status": "InboxMessageStatus",
|
||||
|
||||
@@ -4,6 +4,18 @@
|
||||
|
||||
Defines the backend/frontend data contract for `/api/v1/todos`.
|
||||
|
||||
## Endpoints
|
||||
|
||||
| Method | Path | Description |
|
||||
|---|---|---|
|
||||
| POST | `/api/v1/todos` | Create todo |
|
||||
| GET | `/api/v1/todos` | List todos (supports status/priority filter) |
|
||||
| GET | `/api/v1/todos/{todo_id}` | Get todo detail |
|
||||
| PATCH | `/api/v1/todos/reorder` | Batch reorder todos |
|
||||
| PATCH | `/api/v1/todos/{todo_id}` | Update todo |
|
||||
| POST | `/api/v1/todos/{todo_id}/complete` | Mark todo completed |
|
||||
| DELETE | `/api/v1/todos/{todo_id}` | Delete todo |
|
||||
|
||||
## Field Definitions
|
||||
|
||||
- `id`: string (UUID)
|
||||
@@ -31,6 +43,24 @@ Defines the backend/frontend data contract for `/api/v1/todos`.
|
||||
- optional: `title`, `description`, `priority`, `order`, `status`, `schedule_item_ids`
|
||||
- `order` is interpreted inside the todo's final `priority` quadrant.
|
||||
|
||||
### List Todos (`GET /api/v1/todos`)
|
||||
|
||||
- query `status`: `pending | done | canceled` (optional)
|
||||
- query `priority`: integer in `[1, 4]` (optional)
|
||||
|
||||
### Reorder Todos (`PATCH /api/v1/todos/reorder`)
|
||||
|
||||
- body: `{ items: Array<{ id, priority, order }> }`
|
||||
- each item requires:
|
||||
- `id`: UUID
|
||||
- `priority`: integer in `[1, 4]`
|
||||
- `order`: integer `>= 0`
|
||||
|
||||
### Complete Todo (`POST /api/v1/todos/{todo_id}/complete`)
|
||||
|
||||
- body: `{}`
|
||||
- effect: sets todo status to `done` and updates `completed_at`
|
||||
|
||||
## Ordering Rules
|
||||
|
||||
- Todo list API returns items sorted by `priority ASC`, then `order ASC`.
|
||||
|
||||
@@ -12,6 +12,7 @@ Base URL: `/api/v1/users`
|
||||
|---|---|---|
|
||||
| GET | `/me` | 获取当前用户信息 |
|
||||
| PATCH | `/me` | 更新当前用户信息 |
|
||||
| POST | `/me/avatar` | 上传头像图片并更新头像地址 |
|
||||
| POST | `/search` | 搜索用户 |
|
||||
| GET | `/{user_id}` | 获取指定用户信息 |
|
||||
|
||||
@@ -106,7 +107,31 @@ Base URL: `/api/v1/users`
|
||||
|
||||
---
|
||||
|
||||
## 4) GET `/{user_id}`
|
||||
## 4) POST `/me/avatar`
|
||||
|
||||
上传头像(`multipart/form-data`)。
|
||||
|
||||
### Request
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `file` | file | 是 | 图片文件(`image/jpeg` / `image/png` / `image/webp`) |
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"url": "https://..."
|
||||
}
|
||||
```
|
||||
|
||||
说明:
|
||||
- 上传成功后后端会同步更新当前用户 `avatar_url`。
|
||||
- 文件大小上限由后端配置 `storage.avatar.max_size_mb` 控制。
|
||||
|
||||
---
|
||||
|
||||
## 5) GET `/{user_id}`
|
||||
|
||||
获取指定用户信息。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user