# Analytics Date Dimension 与个人任务埋点范围收口记录(2026-05-04) ## 背景 本记录用于收口 `.hermes/plans/2026-05-04_022223-analytics-time-dimension-mapping.md` 中当前阶段已经落地的内容,并明确尚未执行的后续范围。 本阶段目标不是完整上线运营统计查询,而是先完成两件基础工作: 1. 收紧个人任务系统的埋点范围,避免运营或接口把个人任务错误配置为 `site`、`module`、`work` 等非用户维度。 2. 新增统一日期维表 `analytics_date_dimension`,为后续按周、月、季、年聚合埋点数据提供稳定的日期 bucket 映射。 ## 当前已完成范围 ### 1. 个人任务埋点范围锁定为 User 当前个人任务系统首版只支持用户维度埋点。 已完成: - Admin 任务配置页不再展示“埋点范围”选择。 - Admin 保存任务配置时固定传 `scopeKind: 'user'`。 - API 层拒绝非 `user` 的个人任务配置。 - 领域输入构造层拒绝非 `User` 的个人任务配置。 - `Work => user_id` 的错误映射已移除。 - 任务进度刷新、任务中心快照、领奖链路遇到非 `User` 的异常个人任务配置时显式报错,不再静默按 0 进度处理。 相关文件: ```text apps/admin-web/src/pages/AdminTaskConfigPage.tsx apps/admin-web/src/api/adminApiTypes.ts server-rs/crates/api-server/src/runtime_profile.rs server-rs/crates/module-runtime/src/commands.rs server-rs/crates/module-runtime/src/errors.rs server-rs/crates/spacetime-module/src/runtime/profile.rs ``` ### 2. 日期维表领域模型与纯函数 已在 `module-runtime` 中补充日期维表快照和纯函数。 日期维表使用现有北京时间业务日 `day_key` 语义: ```text date_key = floor((occurred_at_micros + 8h) / 1d) ``` 已完成能力: - 从 `YYYY-MM-DD` 解析业务日 `date_key`。 - 从 `date_key` 构造日期维表快照。 - 生成 ISO weekday:周一=1,周日=7。 - 生成 ISO week key:`YYYYWW`,跨年周按 ISO week-year。 - 生成 week/month/quarter/year 的 key 和起止 `date_key`。 - 限制日期维表支持范围为: - `2000-01-01` - 到 `2100-12-31` 相关文件: ```text server-rs/crates/module-runtime/src/domain.rs server-rs/crates/module-runtime/src/application.rs server-rs/crates/module-runtime/src/lib.rs ``` ### 3. SpacetimeDB 日期维表与 reducer 已新增 SpacetimeDB 表: ```text analytics_date_dimension ``` 表字段包括: ```text date_key calendar_date weekday iso_week_key week_start_date_key week_end_date_key month_key month_start_date_key month_end_date_key quarter_key quarter_start_date_key quarter_end_date_key year_key year_start_date_key year_end_date_key created_at updated_at ``` 已新增索引: ```text iso_week_key month_key quarter_key year_key ``` 已新增 reducer: ```text ensure_analytics_date_dimension_for_date seed_analytics_date_dimensions ``` 当前 reducer 行为: - `ensure` 单日幂等补齐。 - `seed` 按日期范围幂等补齐。 - `seed` 拒绝 `start_date > end_date`。 - `seed` 单次最多允许 `ANALYTICS_DATE_DIMENSION_MAX_SEED_DAYS = 3660` 天。 - 裸 `date_key` 进入 ensure 前先做支持范围校验,避免极端整数进入日历算法。 相关文件: ```text server-rs/crates/spacetime-module/src/runtime/analytics_date_dimension.rs server-rs/crates/spacetime-module/src/runtime/mod.rs server-rs/crates/spacetime-module/src/migration.rs docs/technical/SPACETIMEDB_TABLE_CATALOG.md ``` ### 4. SpacetimeDB Rust client bindings 已按项目脚本生成 Rust bindings,并在生成参数中显式包含 private tables/functions: ```bash PATH="/tmp/spacetime-bin:$PATH" npm run spacetime:generate -- --rust-only ``` 本次已修改生成脚本: ```text scripts/generate-spacetime-bindings.mjs ``` 在 `spacetime generate` 参数中加入: ```text --include-private ``` 说明:SpacetimeDB CLI 2.1.0 的参数名是 `--include-private`,不是 `--non-private`。该参数含义是将 private tables/functions 也包含进生成代码,满足 api-server 通过 Rust bindings 访问 module private table/reducer 的需求。 ```text spacetimedb tool version 2.1.0; spacetimedb-lib version 2.1.0 ``` 生成脚本: ```text scripts/generate-spacetime-bindings.mjs ``` 已新增 analytics date dimension 相关 bindings: ```text server-rs/crates/spacetime-client/src/module_bindings/analytics_date_dimension_ensure_input_type.rs server-rs/crates/spacetime-client/src/module_bindings/analytics_date_dimension_seed_input_type.rs server-rs/crates/spacetime-client/src/module_bindings/analytics_date_dimension_type.rs server-rs/crates/spacetime-client/src/module_bindings/analytics_date_dimension_table.rs server-rs/crates/spacetime-client/src/module_bindings/ensure_analytics_date_dimension_for_date_reducer.rs server-rs/crates/spacetime-client/src/module_bindings/seed_analytics_date_dimensions_reducer.rs ``` 并更新了: ```text server-rs/crates/spacetime-client/src/module_bindings/mod.rs ``` 注意: - `analytics_date_dimension` 表当前是 private table;由于生成脚本已加 `--include-private`,本次 codegen 已生成 `analytics_date_dimension_table.rs`,可通过 `ctx.db.analytics_date_dimension()` 访问 client cache / query builder。 - bindings 目录是自动生成产物,本次以项目脚本整体刷新,除新增 analytics 文件外,也带来了大量已存在 table/reducer/procedure 文件的格式化/生成器输出差异。 ### 5. 测试覆盖 已新增测试: ```text server-rs/crates/module-runtime/tests/analytics_date_dimension.rs server-rs/crates/module-runtime/tests/profile_task_scope.rs server-rs/crates/shared-contracts/tests/profile_task_contract.rs ``` 覆盖重点: - `2024-02-29` 闰年。 - `2025-12-29` ISO week 跨年。 - `2026-01-01` 跨年周。 - `2026-03-31` Q1 结束。 - `2026-04-01` Q2 开始。 - `2026-12-31` 年末。 - 非法日期解析失败。 - 超出日期维表支持范围失败。 - 个人任务 `scopeKind=user` 成功。 - 个人任务 `scopeKind=site/module/work` 失败。 - `work` scope 不会静默映射到 `user_id`。 - Admin 个人任务配置 contract 保持 `scopeKind: user`。 ## 已验证命令 从 `server-rs/` 执行: ```bash cargo fmt -p module-runtime -p spacetime-module -p spacetime-client cargo test -p spacetime-client --no-run cargo test -p spacetime-module --no-run cargo test -p module-runtime --test analytics_date_dimension cargo test -p module-runtime --test profile_task_scope cargo test -p shared-contracts --test profile_task_contract ``` 从项目根目录执行: ```bash npm run admin-web:typecheck ``` 当前结果: - `spacetime-client --no-run` 编译通过。 - `spacetime-module --no-run` 编译通过。 - `analytics_date_dimension` 测试通过:8 passed。 - `profile_task_scope` 测试通过:3 passed。 - `profile_task_contract` 测试通过:2 passed。 - `admin-web:typecheck` 通过。 已知非本阶段阻塞: - 完整运行 `cargo test -p spacetime-module` 时,曾出现既有 puzzle 测试失败: ```text puzzle::tests::puzzle_preview_is_publishable_with_complete_draft FAILED assertion failed: preview.publish_ready ``` 该失败与当前埋点范围和日期维表改动无直接关系,本阶段以 `cargo test -p spacetime-module --no-run` 作为编译门禁。 ## 当前未完成 / 暂缓项 ### 1. 暂未新增 spacetime-client facade 当前没有新增: ```text SpacetimeClient::ensure_analytics_date_dimension_for_date SpacetimeClient::seed_analytics_date_dimensions ``` 原因: - 生成脚本已加入 `--include-private`,private reducer/type/table bindings 已可用于后续 facade 实现。 - 但 Step 7/8/9 暂缓,尚未由 `api-server` 或统计查询链路调用该能力。 - 如后续只是 SpacetimeDB module 内部写入统计时 ensure,可以直接复用 module 内部 helper,不一定需要远程 client facade。 - 若后续需要由 API 或运维接口触发 seed/ensure,可基于本次已生成的 reducer bindings 再补 facade。 ### 2. Step 7/8/9 暂缓 本阶段未接入: - 事件写入链路自动 ensure 日期维表。 - 聚合查询 API 的 `granularity = day | week | month | quarter | year`。 - shared contracts / 前端 analytics contracts。 - 历史事件回填。 这些应作为后续阶段单独设计和落地。 ## 后续建议顺序 1. 如需提交本阶段改动,确认是否接受 `module_bindings` 整体刷新带来的大量生成文件 diff。 2. 如希望 diff 更小,可评估仅提交 analytics date dimension 相关生成文件与 `mod.rs`;但需要非常谨慎,因为 `module_bindings` 是自动生成产物。 3. 如需要由 `api-server` 触发 seed/ensure,再补 `spacetime-client` facade。 4. 进入 Step 7/8/9:事件写入链路、聚合查询 API、前端 contracts。 ## Step 7/8/9 后续接入记录(2026-05-04) 本次继续推进此前暂缓的 Step 7/8/9 中“按日期维度聚合查询 API / contracts / client facade”部分。 ### 已新增能力 1. `module-runtime` 新增 analytics metric 聚合领域类型与纯函数: - `AnalyticsGranularity = day | week | month | quarter | year` - `AnalyticsMetricQueryInput` - `AnalyticsBucketMetric` - `AnalyticsMetricQueryResponse` - `aggregate_runtime_tracking_daily_stats(...)` 2. `spacetime-module` 新增 `query_analytics_metric` procedure,直接聚合 tracking daily stat,输出按 bucket 排序的统计结果。 3. `spacetime-client` 新增 facade: ```rust SpacetimeClient::query_analytics_metric(event_key, scope_kind, scope_id, granularity) ``` 4. `api-server` 新增登录态接口: ```http GET /api/profile/analytics/metric?eventKey=...&scopeKind=user&scopeId=...&granularity=day ``` 请求参数: | 参数 | 说明 | | --- | --- | | `eventKey` | 埋点事件 key,必填 | | `scopeKind` | `site | work | module | user` | | `scopeId` | 对应范围 ID,必填 | | `granularity` | `day | week | month | quarter | year` | 响应 data: ```ts type AnalyticsMetricQueryResponse = { buckets: Array<{ bucketKey: string; bucketStartDateKey: number; bucketEndDateKey: number; value: number; }>; }; ``` 5. shared contracts / 前端 shared contracts 已新增 analytics query 类型: - `AnalyticsMetricQueryRequest` - `AnalyticsMetricQueryResponse` - `AnalyticsBucketMetricResponse` / `AnalyticsBucketMetric` - `AnalyticsGranularity` ### 本次验证 从 `server-rs/` 执行通过: ```bash cargo test -p module-runtime --test analytics_granularity cargo check -p spacetime-module cargo check -p spacetime-client cargo check -p api-server ``` 验证结果: - `analytics_granularity` 测试通过:3 passed。 - `spacetime-module` 编译通过,仅存在既有 dead_code warnings。 - `spacetime-client` 编译通过。 - `api-server` 编译通过,仅存在既有 prompt dead_code warnings。 ### 注意事项 当前环境未检测到 `spacetime` / `spacetimedb` CLI,因此 analytics metric 相关 `module_bindings` 是按现有生成物结构手动补齐的临时生成物。后续有 CLI 的开发机应优先通过项目脚本重新生成 bindings,并复核手写生成物是否可被正式生成输出覆盖。 --- ## 阶段结论 当前阶段已经完成“个人任务埋点范围收紧”和“日期维表 module 侧能力”的核心落地,并已生成 SpacetimeDB Rust client bindings。 剩余工作不再是 bindings 环境阻塞,而是后续业务接入范围:是否增加 `spacetime-client` facade,以及是否继续推进事件写入链路、聚合查询 API 和前端 analytics contracts。