"""埋点上报的请求/响应模型(架构文档 §9.3 events,FR-SYS-06 服务端部分)。 统一信封(§9.2)之外,这里只描述 ``data`` 载荷。事件字典(event dictionary) V1 不做服务端枚举校验 —— 客户端事件名集合由 TD-23 埋点字典维护、尚未定稿, 先只用形状正则兜底,字典定稿后再收紧。 """ from __future__ import annotations import re from datetime import datetime from typing import Any from pydantic import BaseModel, Field #: 事件名形状:小写字母开头,仅 [a-z0-9_.],总长 2~64(架构 §6.2 event_name)。 #: #: 注意它刻意不写成 ``Field(pattern=...)`` —— 请求体校验失败会落进通用的 #: 40001/422 信封,而埋点链路要求 fail-close 地返回域错误 40020(客户端据此 #: 丢弃本地缓存、静默降级)。因此正则在 service 层 ``validate_batch`` 里强制。 EVENT_NAME_PATTERN = re.compile(r"^[a-z][a-z0-9_.]{1,63}$") class EventIn(BaseModel): """单条埋点事件(客户端时钟不可信,client_ts 仅作参考)。""" event_name: str = Field(description="事件名;形状见 EVENT_NAME_PATTERN,服务层 fail-close 校验") props: dict[str, Any] = Field( default_factory=dict, description="事件属性;PII 键会被服务端剔除" ) client_ts: datetime | None = Field( default=None, description="客户端事件时间(ISO 8601);缺省时仅用服务端时间" ) class EventBatchRequest(BaseModel): """整批上报:任一条非法则整批拒绝,不部分落库。""" events: list[EventIn] = Field(min_length=1, description="本批事件,上限见 EVENTS_MAX_BATCH") class EventBatchResponse(BaseModel): accepted: int = Field(description="本批成功落库的事件条数;失败走 40020,不会返回部分值")