"""Validated external assessment inputs; no caller-supplied scores or roles.""" from datetime import date from typing import Annotated, Literal from pydantic import ConfigDict, Field, StringConstraints, field_validator, model_validator from app.schemas.common import ApiSchema class IntegrationSchema(ApiSchema): model_config = ConfigDict(extra="forbid", str_strip_whitespace=True) class IntegrationOperator(IntegrationSchema): dingtalk_union_id: str | None = Field(default=None, min_length=1, max_length=64) external_user_id: str | None = Field(default=None, min_length=1, max_length=128) dingtalk_user_id: str | None = Field(default=None, min_length=1, max_length=50) employee_no: str | None = Field(default=None, max_length=64) name: str | None = Field(default=None, max_length=50) @model_validator(mode="after") def identity_source(self): if bool(self.dingtalk_union_id) == bool(self.external_user_id): raise ValueError("dingtalkUnionId 和 externalUserId 必须且只能提供一个") if self.external_user_id and self.dingtalk_user_id: raise ValueError("外部账号模式不能同时提供 dingtalkUserId") return self class IntegrationProduct(IntegrationSchema): subject_id: str = Field(min_length=1, max_length=128) product_name: str | None = Field(default=None, max_length=200) product_category: str = Field(min_length=1, max_length=100) specification: str | None = Field(default=None, max_length=100) material_type: str = Field(min_length=1, max_length=200) supplier: str | None = Field(default=None, max_length=300) factory: str | None = Field(default=None, max_length=300) stage_new: Literal["需求发现", "产品立项", "产品开发", "量产上市", "产品生命管理"] | None = None defect_level: Literal["A", "B", "C"] | None = None description: str | None = Field(default=None, max_length=2000) as_of_date: date | None = None @field_validator("as_of_date", mode="before") @classmethod def iso_date(cls, value): # noqa: ANN001 if value is not None and (not isinstance(value, str) or len(value) != 10): raise ValueError("asOfDate 必须为 YYYY-MM-DD") if value is not None and date.fromisoformat(value).year < 1900: raise ValueError("asOfDate 不早于 1900-01-01") return value RequestId = Annotated[str, StringConstraints(min_length=1, max_length=128, pattern=r"^[A-Za-z0-9_.:-]+$")] class IntegrationRiskRequest(IntegrationSchema): request_id: RequestId source_system: str = Field(min_length=1, max_length=64, pattern=r"^[A-Za-z0-9_-]+$") operator: IntegrationOperator assessment: IntegrationProduct