- 通用 DTO:Money(最小货币单位整数)、CursorPage 泛型、9 个平台 ID Annotated 类型(前缀+UUIDv7/v4 正则,与 openapi 契约对齐) - db 基础:naming_convention Base(pk_/uq_/ix_/fk_,AGENTS 8.6)+ async engine + 事务作用域 session - 首批 6 张表 typed mapping:user/personal_domain/session/service_account/audit_event/outbox_event;Session ID 为 CSPRNG opaque;审计与 Outbox 不可变 - Alembic async 迁移(含表注释);docker 起 hunjing-postgres 并 upgrade 验证:7 表、约束/索引命名全部符合约定 - 按 database-design skill 清单交叉检查:外键索引全覆盖(复合唯一最左前缀)、审计聚合时间线与 Outbox 投递扫描均为专用复合索引 - 集成测试 8 项(唯一约束、FK restrict/cascade、检查约束、同事务提交)连 PG 测试库运行;pytest 15 项、ruff、mypy strict 全绿
44 lines
1.6 KiB
Python
44 lines
1.6 KiB
Python
"""通用 DTO:与 openapi/openapi.yaml 契约对齐的 Pydantic 类型。
|
|
|
|
对齐由 tests/test_dto_contract.py 用 bundled 契约校验关键字段;
|
|
禁止在此复制会漂移的完整值集(以契约为准)。
|
|
"""
|
|
|
|
from typing import Annotated
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field
|
|
|
|
|
|
def _id_pattern(prefix: str, version: int) -> str:
|
|
ver = "7" if version == 7 else "4"
|
|
tail = r"[0-9a-f]{12}-[0-9a-f]{4}-" + ver + r"[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
|
return f"^{prefix}" + tail
|
|
|
|
|
|
UserId = Annotated[str, Field(pattern=_id_pattern("user_", 7))]
|
|
PersonalDomainId = Annotated[str, Field(pattern=_id_pattern("domain_", 7))]
|
|
ConversationId = Annotated[str, Field(pattern=_id_pattern("conversation_", 7))]
|
|
BranchId = Annotated[str, Field(pattern=_id_pattern("branch_", 7))]
|
|
MessageId = Annotated[str, Field(pattern=_id_pattern("message_", 7))]
|
|
ContentBlockId = Annotated[str, Field(pattern=_id_pattern("block_", 7))]
|
|
RunId = Annotated[str, Field(pattern=_id_pattern("run_", 7))]
|
|
TurnId = Annotated[str, Field(pattern=_id_pattern("turn_", 7))]
|
|
RequestId = Annotated[str, Field(pattern=_id_pattern("request_", 4))]
|
|
|
|
|
|
class Money(BaseModel):
|
|
"""金额:最小货币单位整数 + ISO 4217 币种;禁止浮点(契约第 4 节)。"""
|
|
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
amount_minor: int = Field(ge=0)
|
|
currency: str = Field(pattern=r"^[A-Z]{3}$")
|
|
|
|
|
|
class CursorPage[T](BaseModel):
|
|
"""opaque cursor 分页(契约第 4.1 节);客户端不得解析 cursor。"""
|
|
|
|
data: list[T]
|
|
next_cursor: str | None
|
|
has_more: bool
|