Files
SatoNano/app/models/user.py
2026-01-06 23:49:23 +08:00

142 lines
3.3 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 datetime import datetime, timezone
from typing import TYPE_CHECKING
from uuid import uuid4
from sqlalchemy import Boolean, DateTime, String, Text, func
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base
if TYPE_CHECKING:
from app.models.balance import UserBalance
def generate_uuid() -> str:
"""生成 UUID 字符串"""
return str(uuid4())
def utc_now() -> datetime:
"""获取当前 UTC 时间"""
return datetime.now(timezone.utc)
class User(Base):
"""用户模型"""
__tablename__ = "users"
# 主键:使用 UUID 字符串
id: Mapped[str] = mapped_column(
String(36),
primary_key=True,
default=generate_uuid,
comment="用户唯一标识",
)
# 账户信息
username: Mapped[str] = mapped_column(
String(32),
unique=True,
index=True,
nullable=False,
comment="用户名",
)
email: Mapped[str | None] = mapped_column(
String(255),
unique=True,
index=True,
nullable=True,
comment="邮箱地址",
)
hashed_password: Mapped[str | None] = mapped_column(
String(255),
nullable=True, # OAuth2 用户可能没有密码
comment="密码哈希",
)
# OAuth2 关联信息
oauth_provider: Mapped[str | None] = mapped_column(
String(32),
nullable=True,
index=True,
comment="OAuth2 提供商(如 linuxdo",
)
oauth_user_id: Mapped[str | None] = mapped_column(
String(128),
nullable=True,
index=True,
comment="OAuth2 用户 ID",
)
# 用户状态
is_active: Mapped[bool] = mapped_column(
Boolean,
default=True,
nullable=False,
comment="是否激活",
)
is_superuser: Mapped[bool] = mapped_column(
Boolean,
default=False,
nullable=False,
comment="是否为超级管理员",
)
# 个人信息
nickname: Mapped[str | None] = mapped_column(
String(64),
nullable=True,
comment="昵称",
)
avatar_url: Mapped[str | None] = mapped_column(
String(512),
nullable=True,
comment="头像 URL",
)
bio: Mapped[str | None] = mapped_column(
Text,
nullable=True,
comment="个人简介",
)
# 时间戳
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
default=utc_now,
server_default=func.now(),
nullable=False,
comment="创建时间",
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
default=utc_now,
onupdate=utc_now,
server_default=func.now(),
nullable=False,
comment="更新时间",
)
last_login_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True),
nullable=True,
comment="最后登录时间",
)
# 关系
balance_account: Mapped["UserBalance | None"] = relationship(
"UserBalance",
back_populates="user",
uselist=False,
lazy="selectin",
)
def __repr__(self) -> str:
return f"<User(id={self.id!r}, username={self.username!r})>"