""" 用户数据传输对象定义 定义用户相关API请求和响应的数据结构 需求: 3.4, 9.2, 9.3 """ from datetime import datetime, date from typing import Optional from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_validator class UserCreate(BaseModel): """用户注册请求""" username: str = Field(..., min_length=3, max_length=50, description="用户名", examples=["testuser"]) password: str = Field(..., min_length=6, max_length=100, description="密码(默认为加密密码,Swagger测试时传明文并设置encrypted=false)", examples=["password123"]) nickname: str = Field(..., min_length=1, max_length=100, description="昵称", examples=["测试用户"]) email: Optional[EmailStr] = Field(None, description="邮箱", examples=["test@example.com"]) phone: Optional[str] = Field(None, max_length=20, description="手机号") sms_code: Optional[str] = Field(None, description="手机验证码(普通注册时必填)") email_code: Optional[str] = Field(None, description="邮箱验证码(填写邮箱时必填)") encrypted: Optional[bool] = Field(True, description="密码是否已加密(默认true,Swagger测试时设为false使用明文)") @field_validator("email", mode="before") @classmethod def normalize_email(cls, value): # Treat blank strings from external SSO providers as missing email. if value is None: return None if isinstance(value, str): normalized = value.strip() return normalized or None return value class UserLogin(BaseModel): """用户登录请求""" username: str = Field(..., description="用户名或手机号", examples=["testuser"]) password: str = Field(..., description="密码(默认为加密密码,Swagger测试时传明文并设置encrypted=false)", examples=["password123"]) encrypted: Optional[bool] = Field(True, description="密码是否已加密(默认true,Swagger测试时设为false使用明文)") class UserUpdate(BaseModel): """用户信息更新请求""" nickname: Optional[str] = Field(None, min_length=1, max_length=100, description="昵称") phone: Optional[str] = Field(None, max_length=20, description="手机号") email: Optional[EmailStr] = Field(None, description="邮箱") avatar: Optional[str] = Field(None, description="头像URL") apikey: Optional[str] = Field(None, max_length=255, description="API密钥") @field_validator("email", mode="before") @classmethod def normalize_email(cls, value): if value is None: return None if isinstance(value, str): normalized = value.strip() return normalized or None return value class UserVerifyRequest(BaseModel): """用户实名认证请求""" real_name: str = Field(..., min_length=2, max_length=100, description="真实姓名") id_card: str = Field(..., min_length=15, max_length=18, description="身份证号") class UserResponse(BaseModel): """用户信息响应(不包含密码)""" id: str username: Optional[str] = None nickname: str phone: Optional[str] = None email: Optional[str] = None avatar: Optional[str] = None real_name: Optional[str] = None is_verified: str = "unverified" verified_at: Optional[datetime] = None registration_date: Optional[date] = None created_at: datetime updated_at: datetime model_config = ConfigDict(from_attributes=True) class TokenResponse(BaseModel): """登录令牌响应""" access_token: str token_type: str = "bearer" user: UserResponse class TokenVerifyResponse(BaseModel): """Token验证响应""" valid: bool user: UserResponse