提供基本前后端骨架

This commit is contained in:
hisatri
2026-01-06 23:49:23 +08:00
parent 84d4ccc226
commit 06f8176e23
89 changed files with 19293 additions and 2 deletions

156
app/schemas/user.py Normal file
View 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