提供基本前后端骨架
This commit is contained in:
156
app/schemas/user.py
Normal file
156
app/schemas/user.py
Normal file
@@ -0,0 +1,156 @@
|
||||
"""
|
||||
用户相关 Schema
|
||||
|
||||
定义用户数据的验证和序列化规则。
|
||||
"""
|
||||
|
||||
import re
|
||||
from datetime import datetime
|
||||
from typing import Annotated
|
||||
|
||||
from pydantic import EmailStr, Field, field_validator
|
||||
|
||||
from app.core.config import settings
|
||||
from app.schemas.base import BaseSchema
|
||||
|
||||
|
||||
# 用户名正则:字母开头,只允许字母、数字、下划线
|
||||
USERNAME_PATTERN = re.compile(r"^[a-zA-Z][a-zA-Z0-9_]*$")
|
||||
|
||||
|
||||
class UserBase(BaseSchema):
|
||||
"""用户基础字段"""
|
||||
|
||||
username: Annotated[
|
||||
str,
|
||||
Field(
|
||||
min_length=settings.username_min_length,
|
||||
max_length=settings.username_max_length,
|
||||
description="用户名(字母开头,只允许字母、数字、下划线)",
|
||||
examples=["john_doe"],
|
||||
),
|
||||
]
|
||||
email: Annotated[
|
||||
EmailStr | None,
|
||||
Field(
|
||||
default=None,
|
||||
description="邮箱地址",
|
||||
examples=["user@example.com"],
|
||||
),
|
||||
]
|
||||
nickname: Annotated[
|
||||
str | None,
|
||||
Field(
|
||||
default=None,
|
||||
max_length=64,
|
||||
description="昵称",
|
||||
examples=["John"],
|
||||
),
|
||||
]
|
||||
|
||||
@field_validator("username")
|
||||
@classmethod
|
||||
def validate_username(cls, v: str) -> str:
|
||||
"""验证用户名格式"""
|
||||
if not USERNAME_PATTERN.match(v):
|
||||
raise ValueError("用户名必须以字母开头,只能包含字母、数字和下划线")
|
||||
return v.lower() # 统一转小写
|
||||
|
||||
|
||||
class UserCreate(UserBase):
|
||||
"""用户注册请求"""
|
||||
|
||||
password: Annotated[
|
||||
str,
|
||||
Field(
|
||||
min_length=settings.password_min_length,
|
||||
max_length=settings.password_max_length,
|
||||
description="密码",
|
||||
examples=["SecurePass123"],
|
||||
),
|
||||
]
|
||||
|
||||
@field_validator("password")
|
||||
@classmethod
|
||||
def validate_password(cls, v: str) -> str:
|
||||
"""验证密码强度"""
|
||||
errors: list[str] = []
|
||||
|
||||
if settings.password_require_uppercase and not re.search(r"[A-Z]", v):
|
||||
errors.append("至少包含一个大写字母")
|
||||
|
||||
if settings.password_require_lowercase and not re.search(r"[a-z]", v):
|
||||
errors.append("至少包含一个小写字母")
|
||||
|
||||
if settings.password_require_digit and not re.search(r"\d", v):
|
||||
errors.append("至少包含一个数字")
|
||||
|
||||
if settings.password_require_special and not re.search(r"[!@#$%^&*(),.?\":{}|<>]", v):
|
||||
errors.append("至少包含一个特殊字符")
|
||||
|
||||
if errors:
|
||||
raise ValueError("密码强度不足:" + ";".join(errors))
|
||||
|
||||
return v
|
||||
|
||||
|
||||
class UserUpdate(BaseSchema):
|
||||
"""用户信息更新请求"""
|
||||
|
||||
nickname: Annotated[
|
||||
str | None,
|
||||
Field(
|
||||
default=None,
|
||||
max_length=64,
|
||||
description="昵称",
|
||||
),
|
||||
]
|
||||
email: Annotated[
|
||||
EmailStr | None,
|
||||
Field(
|
||||
default=None,
|
||||
description="邮箱地址",
|
||||
),
|
||||
]
|
||||
avatar_url: Annotated[
|
||||
str | None,
|
||||
Field(
|
||||
default=None,
|
||||
max_length=512,
|
||||
description="头像 URL",
|
||||
),
|
||||
]
|
||||
bio: Annotated[
|
||||
str | None,
|
||||
Field(
|
||||
default=None,
|
||||
max_length=500,
|
||||
description="个人简介",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
class UserResponse(BaseSchema):
|
||||
"""用户信息响应"""
|
||||
|
||||
id: str
|
||||
username: str
|
||||
email: str | None
|
||||
nickname: str | None
|
||||
avatar_url: str | None
|
||||
bio: str | None
|
||||
is_active: bool
|
||||
created_at: datetime
|
||||
last_login_at: datetime | None
|
||||
|
||||
|
||||
class UserProfileResponse(BaseSchema):
|
||||
"""用户公开资料响应(不包含敏感信息)"""
|
||||
|
||||
id: str
|
||||
username: str
|
||||
nickname: str | None
|
||||
avatar_url: str | None
|
||||
bio: str | None
|
||||
created_at: datetime
|
||||
|
||||
Reference in New Issue
Block a user