246 lines
11 KiB
Markdown
246 lines
11 KiB
Markdown
# 后端用户行为埋点覆盖方案
|
||
|
||
更新时间:`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 不包含凭证、请求体或敏感业务字段。
|