# Genarrative daily_login 埋点触发点排查记录 ## 背景 用户在后台“埋点数据”页看到 `daily_login` 事件后询问:为什么每日登录埋点看起来只有在用户领取每日登录任务奖励后才记录,而不是登录时记录。 ## 结论 当前代码口径里,`daily_login` 不是认证登录成功瞬间写入的事件。它挂在个人任务链路: - `GET /api/profile/tasks`:读取任务中心时会记录当日 `daily_login`,并刷新任务进度。 - `POST /api/profile/tasks/{task_id}/claim`:领取任务奖励时,如果任务配置是 daily_login,会兜底记录当日 `daily_login`。 因为 `record_daily_login_tracking_event` 用 `daily-login::` 作为 event id,并先查重,所以同一用户同一北京自然日最多写一条。 ## 关键文件 - `docs/technical/PROFILE_TASK_AND_TRACKING_SYSTEM_2026-05-03.md` - 第 47 行左右写明:用户打开任务中心时后端幂等记录当日 `daily_login`;点击领取时校验进度和领奖记录。 - 接口说明中写明 `GET /api/profile/tasks` 会读取任务中心并记录当日登录埋点。 - `server-rs/crates/api-server/src/runtime_profile.rs` - `get_profile_task_center` 调用 `state.spacetime_client().get_profile_task_center(user_id)`。 - `claim_profile_task_reward` 调用 `state.spacetime_client().claim_profile_task_reward(user_id, task_id)`。 - `server-rs/crates/spacetime-module/src/runtime/profile.rs` - `get_profile_task_center_snapshot(..., record_login_event: bool)` 在 `record_login_event` 为 true 时调用 `record_daily_login_tracking_event`。 - `claim_profile_task_reward_record` 对 daily_login 任务调用 `record_daily_login_tracking_event` 作为兜底。 - `record_daily_login_tracking_event` 负责生成 event id、查重、写入 `tracking_event` 和更新 `tracking_daily_stat`。 - `server-rs/crates/api-server/src/phone_auth.rs` - 手机号登录成功后做验证码校验、新用户奖励、邀请码绑定、session 签发、认证快照同步;当前没有写入 `daily_login`,也没有调用任务中心接口。 ## 排查方法 1. 不要只看后台埋点页。先搜事件 key 和任务接口: ```bash git grep -n "daily_login\|tracking_event\|get_profile_task_center\|claim_profile_task_reward" -- server-rs apps docs ``` 2. 对照设计文档中的事件口径: ```bash sed -n '35,58p' docs/technical/PROFILE_TASK_AND_TRACKING_SYSTEM_2026-05-03.md ``` 3. 追 API handler 到 SpacetimeDB reducer: - `api-server/src/runtime_profile.rs` - `spacetime-client` 对应 procedure wrapper - `spacetime-module/src/runtime/profile.rs` 4. 再看真实登录接口是否写入同一事件。手机号登录入口是: - `server-rs/crates/api-server/src/phone_auth.rs::phone_login` ## 常见误判 - 后台只是在展示 `tracking_event`,不是事件产生点。 - “每日登录”这个中文名容易让人以为它必然在 auth 登录成功时写入;当前实现不是这样。 - 如果用户登录后没有打开“我的/任务中心”,只在领奖时触发 claim 接口,就会表现为“领奖时才出现埋点”。 - 领取接口里的写入是兜底,避免用户直接点击领取时因为未先打开任务中心而无法完成每日任务。 ## 后续方案A落地记录 在后续修复中,采用“方案A”:把“读取任务中心时顺手记录每日登录埋点”拆成独立 SpacetimeDB procedure,使任务中心读取只负责读取/刷新进度,避免后台查看或刷新任务中心时污染埋点数据。 关键变化: - `server-rs/crates/module-runtime/src/domain.rs` - 新增 `RuntimeTrackingEventProcedureResult { ok, error_message }`,用于返回纯事件写入结果。 - `server-rs/crates/spacetime-module/src/runtime/profile.rs` - 新增 `record_daily_login_tracking_event_and_return(ctx, input)` procedure。 - `get_profile_task_center` 注释和行为调整为只读取/刷新任务进度,不再作为每日登录埋点产生点。 - `server-rs/crates/spacetime-client/src/runtime.rs` - 新增 `record_daily_login_tracking_event(user_id)` client 方法,调用新 procedure。 - `server-rs/crates/spacetime-client/src/mapper.rs` - 新增 `map_runtime_tracking_event_procedure_result`,把 `ok=false` 映射为 `procedure_failed`。 落地注意: - 这一步只拆出后端写入入口,不等于所有登录方式已经接入;接入手机号/微信/密码等认证成功链路前,需确认产品口径:统计登录成功是否应覆盖所有登录方式,以及事件失败是否阻断登录。 - 修改 `spacetime-module` procedure 后,通常需要重新生成/同步 SpacetimeDB 绑定;若直接手补 `spacetime-client/src/module_bindings`,要非常谨慎,因为该目录声明为自动生成。 - patch 工具可能对 Rust 单文件使用 2015 edition lint,看到 `async fn is not permitted in Rust 2015` 时不要立即按该误报改代码,应以 `cd server-rs && cargo test/check ...` 为准。 验证记录: ```bash cd server-rs cargo test -p module-runtime runtime_profile_task_status_matches_progress_and_claim -- --nocapture ``` 该测试通过可验证任务中心领域进度/领取逻辑未被破坏;完整接入认证链路后还应补 api-server 层登录成功埋点测试。 ## 后续设计建议 如果产品口径要求“登录成功就算每日登录”,应把 `daily_login` 写入点前移到统一 auth 登录成功链路,并覆盖手机号/微信/密码等登录方式;任务中心只读取进度或最多保留幂等兜底。 如果需要同时分析真实登录和任务完成,建议新增独立事件,例如 `auth_login_success` / `user_login_success`,让 `daily_login` 继续表示每日任务完成条件。