from pydantic import BaseModel, Field from pydantic import field_validator from decimal import Decimal class RefundPaymentAccountSave(BaseModel): name: str = Field(min_length=1, max_length=128) alipay_account: str = Field(min_length=1, max_length=128) balance: Decimal = Field(ge=0, le=9999999.99, decimal_places=2, allow_inf_nan=False) single_limit: Decimal = Field(gt=0, le=9999999.99, decimal_places=2, allow_inf_nan=False) is_active: bool = True @field_validator("name", "alipay_account", mode="before") @classmethod def strip_text(cls, value): return value.strip() if isinstance(value, str) else value class RefundFundAdjustment(BaseModel): adjustment_type: str = Field(pattern=r"^(追加资金|资金调整)$") amount: Decimal = Field(ge=-9999999.99, le=9999999.99, decimal_places=2, allow_inf_nan=False) note: str = Field(default="", max_length=255) @field_validator("note", mode="before") @classmethod def strip_note(cls, value): return value.strip() if isinstance(value, str) else value @field_validator("amount") @classmethod def reject_zero_amount(cls, value): if value == 0: raise ValueError("调整金额不能为 0") return value class LoginRequest(BaseModel): username: str = Field(min_length=1, max_length=64) password: str = Field(min_length=1, max_length=128) class PaymentAccountAuthorization(BaseModel): account_id: int | None = Field(..., gt=0, strict=True) class RegisterRequest(BaseModel): username: str = Field(min_length=3, max_length=64, pattern=r"^[A-Za-z0-9_.-]+$") password: str = Field(min_length=8, max_length=128) display_name: str = Field(min_length=1, max_length=64) class AdminUserCreate(BaseModel): username: str = Field(min_length=3, max_length=64, pattern=r"^[A-Za-z0-9_.-]+$") password: str = Field(min_length=8, max_length=128) display_name: str = Field(min_length=1, max_length=64) role_code: str = Field(default="operator", pattern=r"^(super_admin|dept_manager|operator)$") class AdminDingTalkUserLookup(BaseModel): identifier: str = Field(min_length=1, max_length=128) class AdminDingTalkUserCreate(BaseModel): identifier: str = Field(min_length=1, max_length=128) role_code: str | None = Field(default=None, pattern=r"^(super_admin|dept_manager|operator)$") class AdminUserUpdate(BaseModel): password: str | None = Field(default=None, min_length=8, max_length=128) display_name: str | None = Field(default=None, min_length=1, max_length=64) role_code: str | None = Field(default=None, pattern=r"^(super_admin|dept_manager|operator)$") is_active: bool | None = None class QAGenerateRequest(BaseModel): product_name: str = Field(default="", max_length=200) category: str = Field(default="", max_length=100) benefits: str = Field(default="", max_length=1000) selling_points: str = Field(default="", max_length=2000) language_style: str = Field(default="口语(亲切随性)", max_length=40) target_group: str = Field(default="女性", max_length=100) usage_scene: str = Field(default="居家", max_length=100) purchase_channel: str = Field(default="电商平台", max_length=100) question_count: int = Field(default=5, ge=1) answers_per_question: int = Field(default=2, ge=1, le=50) answer_requirement: str = Field(default="", max_length=1000) reference_qa: str = Field(default="", max_length=20000) seed_style: bool = True class QABulkAnswerRequest(BaseModel): product_name: str = Field(default="", max_length=200) category: str = Field(default="", max_length=100) benefits: str = Field(default="", max_length=1000) selling_points: str = Field(default="", max_length=2000) language_style: str = Field(default="口语(亲切随性)", max_length=40) target_group: str = Field(default="女性", max_length=100) usage_scene: str = Field(default="居家", max_length=100) purchase_channel: str = Field(default="电商平台", max_length=100) answers_per_question: int = Field(default=2, ge=1, le=20) answer_requirement: str = Field(default="", max_length=1000) seed_style: bool = True questions: list[str] = Field(min_length=1, max_length=500) class UserOut(BaseModel): id: int username: str display_name: str role_code: str class LoginResponse(BaseModel): success: bool = True data: dict