Files
meida_backend/apps
xsl 64175a1ea1 feat(analytics): POST /events/batch with fail-close validation
ob_event 落库通道:形状校验 + props 字节上限 + client_ts 合理性 +
PII 键剔除,整批原子(FR-SYS-06 服务端部分)。

实现:packages/analytics/schemas.py(EventIn/EventBatchRequest/
EventBatchResponse + 事件名形状正则常量);service.py 拆出纯函数
validate_batch(批量/props 字节/事件名形状/client_ts 窗口/PII 剔除,
任一不过整批 40020),AnalyticsService.record 组装 ObEvent 逐条
add + 一次 flush;routers/events.py 提供 POST /events/batch
(/api/v1 与 /v1 别名,认证必需)。

测试:单测 tests/unit/test_events_validation.py 8 例(合法批、超批量、
props 超字节/不可序列化、事件名形状含尾随换行、client_ts 双边界、
naive 时区按 UTC、PII 键含大小写变体剔除);集成
tests/integration/test_events.py 3 例(两前缀可用+落库断言、
非法批 40020 且库无半条、无 token 40100)。门禁全绿:ruff
check/format、mypy(T2 列表 + domain strict)、lint-imports 4 契约、
单测 177 通过、集成 6 通过。

偏差说明:event_name 正则未写成 Field(pattern=...),而是作为
EVENT_NAME_PATTERN 常量放 schemas.py、由服务层 validate_batch 强制——
若挂在请求体校验上,非法事件名会先被 RequestValidationError 拦截、
返回通用 40001/422 信封,无法满足任务契约 §4「大写 event_name →
40020」的集成行为。正则本体与文档一致。
2026-08-14 16:16:26 +08:00
..

apps/ — 可部署的进程

目录 进程
api/ FastAPI 应用:路由、依赖注入、中间件
worker/ Celery 任务定义与四队列配置(gpu / cpu / io / beat)
admin/ 极简运营后台(占位,FR-ADM-01~05)

API 层的职责边界(架构 §3.1 L3)

只做编排:认证鉴权、参数校验、幂等、配额校验与预扣、统一响应、OpenAPI。

不能放什么

  • 业务规则。路由函数里出现 if 判断业务条件,几乎总是应该下沉到 packages/<域>/service.py 或 packages/domain/。
  • 直接写 SQL 或用 ORM 查表。走域的 service。
  • 直接调用外部 API。LLM 走 packages/llm;未来的审核、视觉、换装同理。
  • 裸 dict 返回。所有响应必须经过 packages.shared.response.ok() / fail()。

契约先行(NFR-MAINT-01)

先在 apps/api/schemas.py 定义 Pydantic schema,再写实现。客户端类型由 OpenAPI 生成,schema 一变客户端立刻可见。

Worker 的规矩

  • 每个长耗时任务都必须落 tk_task,走 packages/task 的状态机。 不要自己维护状态字段。
  • 失败必须退还配额(billing.refund(),FR-BILL-05)。 apps/worker/tasks.py 里的 ping_task 是形状示例。
  • 队列按资源画像分:GPU 推理进 gpu,视觉/渲染进 cpu,外部调用进 io。 混队列会让「换装排队把报告拖慢」。