""" 认证相关 Schema """ from typing import Optional from pydantic import BaseModel, EmailStr, Field from app.models.user import UserRole # ===== 请求 ===== class SendEmailCodeRequest(BaseModel): """发送邮箱验证码请求""" email: EmailStr purpose: str = Field("register", pattern=r"^(register|login|reset_password)$") class Config: json_schema_extra = { "example": { "email": "user@example.com", "purpose": "register" } } class RegisterRequest(BaseModel): """注册请求""" email: EmailStr phone: Optional[str] = Field(None, pattern=r"^1[3-9]\d{9}$") password: str = Field(..., min_length=6, max_length=128) name: str = Field(..., min_length=1, max_length=100) role: UserRole email_code: str = Field(..., min_length=4, max_length=8, description="邮箱验证码") class Config: json_schema_extra = { "example": { "email": "user@example.com", "password": "password123", "name": "张三", "role": "creator", "email_code": "123456" } } class LoginRequest(BaseModel): """登录请求(支持邮箱+密码 或 邮箱+验证码)""" email: Optional[EmailStr] = None phone: Optional[str] = None password: Optional[str] = None email_code: Optional[str] = None # 邮箱验证码 class Config: json_schema_extra = { "example": { "email": "user@example.com", "password": "password123" } } class RefreshTokenRequest(BaseModel): """刷新 Token 请求""" refresh_token: str class SendSmsCodeRequest(BaseModel): """发送短信验证码请求""" phone: str = Field(..., pattern=r"^1[3-9]\d{9}$") class BindPhoneRequest(BaseModel): """绑定手机号请求""" phone: str = Field(..., pattern=r"^1[3-9]\d{9}$") sms_code: str class BindEmailRequest(BaseModel): """绑定邮箱请求""" email: EmailStr password: str = Field(..., min_length=6, max_length=128) class ResetPasswordRequest(BaseModel): """重置密码请求(通过邮箱验证码)""" email: EmailStr email_code: str = Field(..., min_length=4, max_length=8) new_password: str = Field(..., min_length=6, max_length=128) class Config: json_schema_extra = { "example": { "email": "user@example.com", "email_code": "123456", "new_password": "newpassword123" } } class ChangePasswordRequest(BaseModel): """修改密码请求""" old_password: str new_password: str = Field(..., min_length=6, max_length=128) # ===== 响应 ===== class UserResponse(BaseModel): """用户信息响应""" id: str email: Optional[str] = None phone: Optional[str] = None name: str avatar: Optional[str] = None role: UserRole is_verified: bool # 根据角色返回对应的组织 ID brand_id: Optional[str] = None agency_id: Optional[str] = None creator_id: Optional[str] = None # 当前所属租户(品牌方)- 用于数据隔离 tenant_id: Optional[str] = None tenant_name: Optional[str] = None class Config: from_attributes = True class TokenResponse(BaseModel): """Token 响应""" access_token: str refresh_token: str token_type: str = "bearer" expires_in: int = 900 # 15 分钟 = 900 秒 class LoginResponse(BaseModel): """登录响应""" access_token: str refresh_token: str token_type: str = "bearer" expires_in: int = 900 user: UserResponse class RefreshTokenResponse(BaseModel): """刷新 Token 响应""" access_token: str token_type: str = "bearer" expires_in: int = 900