Files
Genarrative/docs/technical/BACKEND_TRACKING_EVENT_COVERAGE_2026-05-09.md
2026-05-09 20:10:18 +08:00

246 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 后端用户行为埋点覆盖方案
更新时间:`2026-05-09`
## 1. 范围
本方案用于补齐后端可直接观测的用户行为埋点入口,统一写入 SpacetimeDB 的 `tracking_event``tracking_daily_stat`,为任务系统、运营看板与后续漏斗分析提供事实数据。
本轮明确不纳入以下范围:
- 后台管理入口:`/admin/...`
- RPG 相关入口
- 大鱼吃小鱼相关入口
- Visual Novel 相关入口
- Story 相关入口
- Combat 相关入口
上述范围后续若需要埋点,应单独定义事件口径,避免把后台运营审计或特定玩法内行为混入本轮通用用户行为埋点。
## 2. 写入链路
### 2.1 SpacetimeDB 通用 procedure
新增通用 procedure
- `record_tracking_event_and_return(input: RuntimeTrackingEventInput)`
该入口复用既有运行态埋点写入能力:
1.`occurred_at_micros` 计算北京时间业务日 `day_key`
2. 按同一 `day_key` 幂等补齐 `analytics_date_dimension`,保证周/月/季/年聚合查询有日期 bucket 映射。
3. 写入原始事实 `tracking_event`
4. 更新聚合投影 `tracking_daily_stat`
5. 触发依赖事件进度的个人任务刷新。
每日登录 `daily_login` 也必须走该通用 procedure认证链路仍保留 `record_daily_login_tracking_event_after_auth_success(...)` 作为业务语义 helper但 helper 内部构造 `TrackingEventDraft` 后调用 `record_tracking_event_after_success(...)`,不再绕到每日登录专用 SpacetimeDB procedure。
### 2.2 spacetime-client 封装
`spacetime-client` 提供薄封装:
- `SpacetimeRuntimeClient::record_tracking_event(...)`
API Server 只依赖该 facade不在 handler 中直接拼接 SpacetimeDB procedure 调用。
### 2.3 api-server helper 与中间件
API Server 新增统一 helper
- `tracking::TrackingEventDraft`
- `tracking::record_tracking_event_after_success(...)`
- `tracking::record_route_tracking_event_after_success(...)`
路由级中间件 `record_api_tracking_after_success` 挂在最终响应链路上,只在最终 HTTP status 为 2xx 时写入埋点。埋点失败只写 `warn` 日志,不阻断认证、充值、发布、任务领取等主业务流程。
## 3. metadata 口径
当前通用路由埋点仅记录低敏字段:
| 字段 | 含义 |
| --- | --- |
| `route` | 请求路径,不包含 query string |
| `method` | HTTP Method |
| `status` | 最终成功响应状态码 |
| `operation` | `RequestContext` 中的操作名 |
| `asset` | 仅资产类事件写入的低敏资产/操作信息,包含 `operation``operationFamily``assetObjectId``assetKind``objectKey``bucket``contentType``contentLength``version``bindingId``entityKind``entityId``slot``ownerUserId``profileId` 等可用于定位资产事实的字段;不写签名 URL、表单签名、OSS policy、token 或完整请求体。 |
| `assetOperation` | 资产类路由兜底事件的操作 key用于不读取请求体时仍能按操作族聚合。 |
禁止在通用埋点 metadata 中写入手机号、token、cookie、邀请码、请求体、密钥、连接串、外部凭证、OSS 签名 URL、PostObject policy 或签名表单字段。
### 3.1 作品级游玩埋点
所有已接入后端正式试玩/播放入口的作品类型统一写 `work_play_start`
- `scope_kind = work`
- `scope_id = 稳定作品 ID`,优先使用 `profile_id`;大鱼吃小鱼沿用 `session_id` 作为作品 ID。
- `user_id = 当前认证用户`
- `owner_user_id = 作品作者/拥有者`,无法从入口直接确认作者时可为空,但 `metadata.userId` 仍保留当前玩家。
- `profile_id = 作品 profile_id`,大鱼吃小鱼这类 session 型作品可为空。
- `module_key = play_type`,例如 `puzzle``match3d``square-hole``custom-world``big-fish``visual-novel`
- `metadata` 固定包含 `operation = work_play_start``playType``workId``sourceRoute`,并按入口补充 `runId``ownerUserId``profileId``levelId``mode` 等低敏字段。
该事件用于“某个作品被多少不同用户玩过”等作品级分析;权威去重统计仍建议优先使用业务投影(如 `profile_played_world`),埋点侧用于分析与漏斗联动。
## 4. 事件清单
### 4.1 认证与会话
| 事件 | 入口 |
| --- | --- |
| `auth_login_options_view` | `GET /api/auth/login-options` |
| `auth_phone_code_send` | `POST /api/auth/phone/send-code` |
| `daily_login` | 认证成功与 refresh 续期后由 `record_daily_login_tracking_event_after_auth_success(...)` 主动写入,事件 ID 按 `daily-login:{user_id}:{day_key}` 幂等 |
| `auth_phone_login_success` | `POST /api/auth/phone/login` |
| `auth_me_view` | `GET /api/auth/me` |
| `auth_sessions_view` | `GET /api/auth/sessions` |
| `auth_refresh_success` | `POST /api/auth/refresh` |
| `auth_logout` | `POST /api/auth/logout` |
| `auth_logout_all` | `POST /api/auth/logout-all` |
| `auth_wechat_bind_phone_success` | `POST /api/auth/wechat/bind-phone` |
### 4.2 个人中心、账户运营与任务
| 事件 | 入口 |
| --- | --- |
| `profile_identity_update` | `PATCH /api/profile/me` |
| `profile_dashboard_view` | `GET /api/profile/dashboard` |
| `wallet_ledger_view` | `GET /api/profile/wallet-ledger` |
| `recharge_center_view` | `GET /api/profile/recharge-center` |
| `recharge_order_create` | `POST /api/profile/recharge/orders` |
| `feedback_submit` | `POST /api/profile/feedback` |
| `invite_center_view` | `GET /api/profile/referrals/invite-center` |
| `referral_invite_code_redeem` | `POST /api/profile/referrals/redeem-code` |
| `redeem_code_submit` | `POST /api/profile/redeem-codes/redeem` |
| `task_center_view` | `GET /api/profile/tasks` |
| `task_reward_claim` | `POST /api/profile/tasks/{task_id}/claim` |
| `save_archive_list_view` | `GET /api/profile/save-archives` |
| `save_archive_detail_view` | `GET /api/profile/save-archives/{archive_id}` |
| `browse_history_view` | `GET /api/profile/browse-history` |
| `browse_history_record` | `POST /api/profile/browse-history` |
| `browse_history_clear` | `DELETE /api/profile/browse-history` |
| `play_stats_view` | `GET /api/profile/play-stats` |
| `profile_analytics_metric_view` | `GET /api/profile/analytics/metric` |
### 4.3 AI、资产、LLM 与语音
资产操作统一按用户级事件写入:`scope_kind = user``scope_id = 当前认证 user_id``user_id/owner_user_id = 当前认证 user_id`。其中 `asset_upload_ticket_create``asset_upload_confirm``asset_bind` 在 handler 成功后主动记录资产 metadata避免只依赖路由兜底其余资产工坊入口通过路由级兜底保留用户级操作事实。
| 事件 | 入口 |
| --- | --- |
| `ai_task_create` | `POST /api/ai/tasks` |
| `ai_task_start` | `POST /api/ai/tasks/{task_id}/start` |
| `ai_task_stage_start` | `POST /api/ai/tasks/{task_id}/stages/{stage_id}/start` |
| `ai_task_chunk_append` | `POST /api/ai/tasks/{task_id}/chunks` |
| `ai_task_stage_complete` | `POST /api/ai/tasks/{task_id}/stages/{stage_id}/complete` |
| `ai_task_reference_attach` | `POST /api/ai/tasks/{task_id}/references` |
| `ai_task_complete` | `POST /api/ai/tasks/{task_id}/complete` |
| `ai_task_fail` | `POST /api/ai/tasks/{task_id}/fail` |
| `ai_task_cancel` | `POST /api/ai/tasks/{task_id}/cancel` |
| `asset_upload_ticket_create` | `POST /api/assets/direct-upload-tickets` |
| `asset_sts_credentials_create` | `POST /api/assets/sts-upload-credentials` |
| `asset_upload_confirm` | `POST /api/assets/objects/confirm` |
| `asset_bind` | `POST /api/assets/objects/bind` |
| `asset_character_visual_generate` | `POST /api/assets/character-visual/generate` |
| `asset_character_visual_publish` | `POST /api/assets/character-visual/publish` |
| `asset_character_animation_generate` | `POST /api/assets/character-animation/generate` |
| `asset_character_animation_publish` | `POST /api/assets/character-animation/publish` |
| `asset_character_animation_import` | `POST /api/assets/character-animation/import-video` |
| `asset_character_workflow_cache_save` | `POST /api/assets/character-workflow-cache` |
| `asset_history_view` | `GET /api/assets/history` |
| `llm_request` | `POST /api/llm/chat/completions` |
| `speech_config_view` | `GET /api/speech/volcengine/config` |
| `asr_stream_start` | `GET /api/speech/volcengine/asr/stream` |
| `tts_bidirection_start` | `GET /api/speech/volcengine/tts/bidirection` |
| `tts_sse_start` | `POST /api/speech/volcengine/tts/sse` |
### 4.4 运行态与创作入口
| 事件 | 入口 |
| --- | --- |
| `runtime_settings_view` | `GET /api/runtime/settings` |
| `runtime_settings_update` | `PUT /api/runtime/settings` |
| `runtime_snapshot_view` | `GET /api/runtime/save/snapshot` |
| `runtime_snapshot_save` | `PUT /api/runtime/save/snapshot` |
| `runtime_snapshot_delete` | `DELETE /api/runtime/save/snapshot` |
| `puzzle_route_success` | `/api/runtime/puzzle/...` 成功响应兜底 |
| `match3d_route_success` | `/api/creation/match3d/...``/api/runtime/match3d/...` 成功响应兜底 |
| `square_hole_route_success` | `/api/creation/square-hole/...``/api/runtime/square-hole/...` 成功响应兜底 |
| `custom_world_route_success` | `/api/runtime/custom-world...` 成功响应兜底 |
| `creative_agent_route_success` | `/api/runtime/creative-agent...` 成功响应兜底 |
| `work_play_start` | 拼图、抓大鹅、方洞挑战、自定义世界、大鱼吃小鱼、Visual Novel 的正式开始游玩/播放入口;写 `scope_kind = work``scope_id = 作品 ID` |
2048、Survivor、Moku 等未被排除的模板/玩法,如果经由上述 runtime、creative、custom-world、puzzle、match3d 或 square-hole 后端入口,会被路由级兜底事件覆盖。
## 5. 查询与验收建议
按每日登录核查原始事实:
```sql
SELECT event_id, event_key, scope_kind, scope_id, user_id, module_key, metadata_json, occurred_at
FROM tracking_event
WHERE event_key = 'daily_login'
ORDER BY occurred_at DESC
LIMIT 20;
```
按作品级游玩核查原始事实:
```sql
SELECT event_key, scope_kind, scope_id, user_id, owner_user_id, profile_id, module_key, metadata_json, occurred_at
FROM tracking_event
WHERE event_key = 'work_play_start'
ORDER BY occurred_at DESC
LIMIT 20;
```
按某个作品统计不同游玩用户:
```sql
SELECT scope_id, COUNT(DISTINCT user_id) AS player_count
FROM tracking_event
WHERE event_key = 'work_play_start'
AND scope_kind = 'work'
AND scope_id = '<profile_id_or_work_id>'
GROUP BY scope_id;
```
按资产操作核查原始事实:
```sql
SELECT event_key, scope_kind, scope_id, user_id, owner_user_id, module_key, metadata_json, occurred_at
FROM tracking_event
WHERE module_key = 'asset'
ORDER BY occurred_at DESC
LIMIT 20;
```
按事件核查原始事实:
```sql
SELECT event_key, scope_kind, scope_id, user_id, module_key, metadata_json, occurred_at
FROM tracking_event
WHERE event_key = 'task_center_view'
ORDER BY occurred_at DESC
LIMIT 20;
```
按日聚合核查:
```sql
SELECT day_key, event_key, scope_kind, scope_id, count
FROM tracking_daily_stat
WHERE event_key = 'task_center_view'
ORDER BY day_key DESC
LIMIT 20;
```
验收重点:
1. 成功请求写入 `tracking_event` 并刷新 `tracking_daily_stat`
2. `daily_login` 由认证成功/refresh 续期链路主动写入,且走 `record_tracking_event_and_return` 通用 procedure。
3. 非 2xx 响应不记录通用成功事件。
4. 后台、RPG、大鱼吃小鱼、Visual Novel、Story、Combat 路由不写入本轮通用埋点。
5. 埋点写入失败时主接口仍返回原业务结果,只记录后端 warning。
6. metadata 不包含凭证、请求体或敏感业务字段。