Files
meida/docs/开发任务包/T3-埋点上报.md
T
xsl e511b6a66f 收尾两批并行开发的文档回写与任务包
- Sprint 执行计划 v1.1:§1.1 补第一批交付实据;T9 SPIKE 结论回写 Sprint 1 表
  (T-P0-16 通过 2.8ms;T-P0-12 有条件;T-P0-10 原型 6/6 + 真实照片重验前置);
  Sprint 2 表标注 T-P2-01~06 交付;新增 §9 两批交付记录
- 开发任务拆解 v1.5:交付回写 + 新风险登记(RTMPose 域差异、BiRefNet 异步化)
- 新增 开发任务包/:三批多 agent 并行的协作规程与 14 份任务契约
- 新增 前置开通清单-云资源与资质.md
- 清理已用毕的评审报告(文档评审 2026-08-13、项目整体评审 2026-08-14),
  README 中相应引用改为留痕表述
2026-08-14 21:07:20 +08:00

85 lines
5.4 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.
# T3 · 客户端埋点批量上报
> 对应任务:`T-P1-10`(埋点事件字典/上报通道的服务端部分)| 需求:`FR-SYS-06`、架构 §9.3 | 分支:`task/t3-events` | worktree:`/home/xsl/meida_backend-t3`
> 先读《README.md(协作规程)》,遵守全部纪律。
## 1. 目标
实现 `POST /events/batch`:客户端批量上报埋点事件,落 `ob_event` 表。这是 V1 全链路埋点(FR-SYS-06)与后续三张看板(TD-24)的数据入口。
## 2. 已就绪的契约(协调提交已提供)
### 表(迁移 `0004` 已建,ORM 已加,**你不需要碰 models.py / migrations**)
`ob_event`(架构 §6.2):
- `event_id` bigint PK(雪花)、`user_id` bigint NOT NULL、`event_name` varchar(64)、`props` JSONB、`client_ts` timestamptz NULL、`server_ts` timestamptz NOT NULL default now()、
- 索引:`(event_name, server_ts)`、`(user_id, server_ts)`。
- 架构原文"按月分区"——**V1 先普通表**(与 ob_ai_call 同样处理),代码注释写明"分区在数据量上来后再做,先不预支复杂度"。
### 配置(`config.py` 已加字段)
```python
events_max_batch: int = 50 # env: EVENTS_MAX_BATCH
events_props_max_bytes: int = 8192 # env: EVENTS_PROPS_MAX_BYTES
```
### 错误码(`errors.py` 已加)
- `EVENTS_BATCH_REJECTED = 40020`:400「埋点数据格式有误」,hint=null(对客户端静默降级即可,不打扰用户)。
### 骨架(协调提交已建,直接填充)
- `packages/analytics/__init__.py`、`packages/analytics/README.md`、`packages/analytics/service.py`(空骨架)
- `apps/api/routers/events.py`(空骨架 router,**已注册进 main.py**,prefix `/events`)
- `apps/api/deps.py` 已有 `AnalyticsServiceDep`(构造 `AnalyticsService(session)`)——**deps.py 禁改,签名如不满足需要,用路由函数内自建服务对象的方式,不改 deps**。
### 分层位置
`packages.analytics` 被协调提交放进了 import-linter 的"共享能力层"(与 identity/billing/task 同层)——它只能 import `packages.shared`/`packages.domain`,**不得** import 其它业务包或 llm。
## 3. 实现要点
允许改动:`packages/analytics/**`、`apps/api/routers/events.py`、测试文件。
1. **`packages/analytics/schemas.py`**(新建):
- `EventIn`:`event_name: str`(`pattern=r"^[a-z][a-z0-9_.]{1,63}$"`)、`props: dict[str, Any] = {}`、`client_ts: datetime | None = None`;
- `EventBatchRequest`:`events: list[EventIn]`(`min_length=1`)、
- `EventBatchResponse`:`accepted: int`。
- **事件字典(event dictionary)**:V1 不做服务端枚举校验(客户端事件名集合由 TD-23 埋点字典维护,尚未定稿)——用正则约束形状即可,代码注释写明"字典校验等 TD-23 定稿后再收紧"。
2. **`packages/analytics/service.py`**:
- `AnalyticsService(session)`:`async def record(user_id: int, batch: list[EventIn]) -> int`;
- 校验(fail-close,任一不过 → 整批拒绝 `AppError(EVENTS_BATCH_REJECTED)`,**不部分落库**):批量 ≤ `events_max_batch`;每条 props 序列化后 ≤ `events_props_max_bytes`(`len(json.dumps(props, ensure_ascii=False).encode())`);`client_ts` 若给出:不晚于 server_now+5min、不早于 7 天前;
- 组装 `ObEvent(event_id=next_id(), user_id=..., server_ts=datetime.now(UTC))` 逐条 add,`flush` 一次,返回条数;(`from packages.domain.ids import next_id`,参考 identity/service.py 用法。)
- **不含 PII 清洗**:props 里若有 `phone`/`token`/`authorization` 键 → 直接删除该键并 `logger.warning("events_pii_dropped", ...)`(NFR-SEC-03 日志脱敏的同族要求)。
3. **`apps/api/routers/events.py`**:
- `POST /batch`,`user_id: CurrentUserId`,`analytics: AnalyticsServiceDep`(若 Dep 不可用则函数内 `AnalyticsService(session)` with `SessionDep`);
- `ok(EventBatchResponse(...))`;服务层抛的 `AppError` 自然走统一异常处理,不需要额外 catch。
## 4. 测试要求
- 单测 `tests/unit/test_events_validation.py`(纯校验逻辑,不连库——把校验拆成模块级纯函数 `validate_batch(events, *, max_batch, max_bytes, now) -> list[ObEvent-like]` 便于无 DB 测试):
- 合法批量通过;超批量拒;props 超字节拒;event_name 正则(大写/数字开头/超长拒);client_ts 未来>5min 拒、7 天前拒;PII 键被剔除。
- 集成测试 `tests/integration/test_events.py`(新文件,允许跑):dev-token → POST /v1/events/batch 两条事件 → 200 accepted=2;查库两行 `ob_event`;再 POST 非法批(event_name 大写)→ 40020 且**库里没有半条**。
## 5. 禁改清单(通用禁改之外)
`packages/llm/**`、`packages/billing/**`、`packages/identity/**`、`packages/storage/**`、`packages/agent/**`、`apps/api/routers/{agent,auth,quota,tasks,uploads}.py`、`tests/integration/test_vertical_slice.py`。
## 6. 验收标准
1. `POST /api/v1/events/batch`(及 `/v1` 别名)可用,认证必需(无 token → 40100);
2. 整批原子:要么全落、要么全拒;
3. 校验规则全部有测试覆盖;
4. 全部检查绿(mypy 范围同 T2 列表)。
## 7. 提交信息
```
feat(analytics): POST /events/batch with fail-close validation
ob_event 落库通道:形状校验 + props 字节上限 + client_ts 合理性 +
PII 键剔除,整批原子(FR-SYS-06 服务端部分)。
偏差说明:<如有写在这里,无则写"无">
```