Your Name 4caafdb50f feat: 添加后端核心模块
用户认证:
- User 模型(支持邮箱/手机号登录)
- 双 Token JWT 认证(accessToken + refreshToken)
- 注册/登录/刷新 Token API

组织模型:
- Brand(品牌方)、Agency(代理商)、Creator(达人)
- 多对多关系:品牌方↔代理商、代理商↔达人

项目与任务:
- Project 模型(品牌方发布)
- Task 模型(完整审核流程追踪)
- Brief 模型(解析后的结构化内容)

文件上传:
- 阿里云 OSS 直传签名服务
- 支持分片上传,最大 500MB

数据库迁移:
- 003_user_org_project_task.py

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-09 13:47:36 +08:00

81 lines
2.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
用户模型
"""
from typing import TYPE_CHECKING, Optional
from datetime import datetime
from sqlalchemy import String, Boolean, DateTime, Enum as SQLEnum
from sqlalchemy.orm import Mapped, mapped_column, relationship
import enum
from app.models.base import Base, TimestampMixin
if TYPE_CHECKING:
from app.models.organization import Brand, Agency, Creator
class UserRole(str, enum.Enum):
"""用户角色"""
BRAND = "brand" # 品牌方
AGENCY = "agency" # 代理商
CREATOR = "creator" # 达人
class User(Base, TimestampMixin):
"""用户表"""
__tablename__ = "users"
id: Mapped[str] = mapped_column(String(64), primary_key=True)
# 登录凭证(邮箱和手机号都可以登录)
email: Mapped[Optional[str]] = mapped_column(String(255), unique=True, nullable=True, index=True)
phone: Mapped[Optional[str]] = mapped_column(String(20), unique=True, nullable=True, index=True)
password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
# 用户信息
name: Mapped[str] = mapped_column(String(100), nullable=False)
avatar: Mapped[Optional[str]] = mapped_column(String(2048), nullable=True)
# 角色
role: Mapped[UserRole] = mapped_column(
SQLEnum(UserRole, name="user_role_enum"),
nullable=False,
index=True,
)
# 状态
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
is_verified: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
# 最后登录
last_login_at: Mapped[Optional[datetime]] = mapped_column(
DateTime(timezone=True),
nullable=True,
)
# Refresh Token用于 JWT 刷新)
refresh_token: Mapped[Optional[str]] = mapped_column(String(512), nullable=True)
refresh_token_expires_at: Mapped[Optional[datetime]] = mapped_column(
DateTime(timezone=True),
nullable=True,
)
# 关联的组织(根据角色不同,关联到不同的组织)
brand: Mapped[Optional["Brand"]] = relationship(
"Brand",
back_populates="user",
uselist=False,
)
agency: Mapped[Optional["Agency"]] = relationship(
"Agency",
back_populates="user",
uselist=False,
)
creator: Mapped[Optional["Creator"]] = relationship(
"Creator",
back_populates="user",
uselist=False,
)
def __repr__(self) -> str:
return f"<User(id={self.id}, email={self.email}, role={self.role})>"