"""认证模块请求/响应模型""" import re from pydantic import BaseModel, Field, field_validator, model_validator PASSWORD_RE = re.compile(r"^(?=.*[A-Za-z])(?=.*\d).{8,32}$") PHONE_RE = re.compile(r"^1\d{10}$") EMAIL_RE = re.compile(r"^[\w.+-]+@[\w-]+\.[\w.]+$") def _check_password(v: str) -> str: if not PASSWORD_RE.match(v): raise ValueError("密码须 8-32 位且包含字母和数字") return v class SmsSendReq(BaseModel): phone: str scene: str = Field(pattern="^(register|login|reset)$") @field_validator("phone") @classmethod def _v_phone(cls, v: str) -> str: if not PHONE_RE.match(v): raise ValueError("手机号格式不正确") return v class EmailSendReq(BaseModel): email: str scene: str = Field(pattern="^(register|login|reset)$") @field_validator("email") @classmethod def _v_email(cls, v: str) -> str: if not EMAIL_RE.match(v): raise ValueError("邮箱格式不正确") return v class RegisterReq(BaseModel): type: str = Field(pattern="^(phone|email)$") phone: str | None = None email: str | None = None code: str = Field(min_length=4, max_length=8) password: str nickname: str | None = Field(default=None, max_length=20) invite_code: str | None = Field(default=None, max_length=10) @field_validator("password") @classmethod def _v_pwd(cls, v: str) -> str: return _check_password(v) @model_validator(mode="after") def _v_target(self): if self.type == "phone" and not (self.phone and PHONE_RE.match(self.phone)): raise ValueError("手机号格式不正确") if self.type == "email" and not (self.email and EMAIL_RE.match(self.email)): raise ValueError("邮箱格式不正确") return self class LoginReq(BaseModel): type: str = Field(pattern="^(phone_password|phone_code|email_password)$") phone: str | None = None email: str | None = None password: str | None = None code: str | None = None revoke: bool = False # 注销冻结期内:确认撤销注销 @model_validator(mode="after") def _v_fields(self): if self.type.startswith("phone") and not self.phone: raise ValueError("手机号必填") if self.type.startswith("email") and not self.email: raise ValueError("邮箱必填") if self.type.endswith("password") and not self.password: raise ValueError("密码必填") if self.type.endswith("code") and not self.code: raise ValueError("验证码必填") return self class RefreshReq(BaseModel): refresh_token: str = Field(min_length=10) class PasswordResetReq(BaseModel): type: str = Field(pattern="^(phone|email)$") phone: str | None = None email: str | None = None code: str = Field(min_length=4, max_length=8) new_password: str @field_validator("new_password") @classmethod def _v_pwd(cls, v: str) -> str: return _check_password(v) @model_validator(mode="after") def _v_target(self): if self.type == "phone" and not (self.phone and PHONE_RE.match(self.phone)): raise ValueError("手机号格式不正确") if self.type == "email" and not (self.email and EMAIL_RE.match(self.email)): raise ValueError("邮箱格式不正确") return self