1.9
This commit is contained in:
@@ -16,6 +16,11 @@ from app.models.sync_seed import SyncQuestion, SyncSeed
|
||||
from app.models.chat_climate import ChatClimate
|
||||
from app.models.flash_event import FlashEvent, FlashParticipation
|
||||
from app.models.user_streak import UserStreak
|
||||
from app.models.group_announcement import GroupAnnouncement
|
||||
from app.models.message_reaction import MessageReaction
|
||||
from app.models.user_block import UserBlock
|
||||
from app.models.password_reset import PasswordResetToken
|
||||
from app.models.friend_tag import FriendTag, FriendTagAssignment
|
||||
|
||||
__all__ = [
|
||||
"User",
|
||||
@@ -38,4 +43,10 @@ __all__ = [
|
||||
"FlashEvent",
|
||||
"FlashParticipation",
|
||||
"UserStreak",
|
||||
"GroupAnnouncement",
|
||||
"MessageReaction",
|
||||
"UserBlock",
|
||||
"PasswordResetToken",
|
||||
"FriendTag",
|
||||
"FriendTagAssignment",
|
||||
]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import String, DateTime, ForeignKey, Text
|
||||
from sqlalchemy import String, DateTime, ForeignKey, Text, Boolean
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
@@ -17,6 +17,7 @@ class Conversation(Base):
|
||||
avatar_url: Mapped[str | None] = mapped_column(String(500), nullable=True) # 群头像
|
||||
description: Mapped[str | None] = mapped_column(String(500), nullable=True) # 群描述
|
||||
creator_id: Mapped[str | None] = mapped_column(String(36), ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
||||
mute_all: Mapped[bool] = mapped_column(Boolean, default=False) # 全员禁言(仅成员不能发,管理员可以)
|
||||
last_message_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
|
||||
last_message_preview: Mapped[str | None] = mapped_column(String(200), nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, default=lambda: datetime.utcnow())
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import String, DateTime, ForeignKey, UniqueConstraint
|
||||
from sqlalchemy import String, DateTime, Boolean, ForeignKey, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
@@ -24,6 +24,9 @@ class ConversationMember(Base):
|
||||
role: Mapped[str] = mapped_column(String(20), default="member") # owner / admin / member
|
||||
nickname: Mapped[str | None] = mapped_column(String(50), nullable=True) # 群内昵称
|
||||
last_read_message_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
|
||||
is_pinned: Mapped[bool] = mapped_column(Boolean, default=False) # 置顶
|
||||
pinned_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
|
||||
is_muted: Mapped[bool] = mapped_column(Boolean, default=False) # 免打扰
|
||||
joined_at: Mapped[datetime] = mapped_column(DateTime, default=lambda: datetime.utcnow())
|
||||
left_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
"""好友分组/标签模型"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import String, DateTime, ForeignKey, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class FriendTag(Base):
|
||||
"""好友标签(一个好友可有多个标签)"""
|
||||
__tablename__ = "friend_tags"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True)
|
||||
user_id: Mapped[str] = mapped_column(String(36), ForeignKey("users.id", ondelete="CASCADE")) # 标签所有者
|
||||
name: Mapped[str] = mapped_column(String(30), nullable=False)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, default=lambda: datetime.utcnow())
|
||||
|
||||
|
||||
class FriendTagAssignment(Base):
|
||||
"""好友-标签 关联"""
|
||||
__tablename__ = "friend_tag_assignments"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("friend_id", "tag_id", name="uq_friend_tag"),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True)
|
||||
friend_id: Mapped[str] = mapped_column(String(36), ForeignKey("friends.id", ondelete="CASCADE"))
|
||||
tag_id: Mapped[str] = mapped_column(String(36), ForeignKey("friend_tags.id", ondelete="CASCADE"))
|
||||
@@ -0,0 +1,25 @@
|
||||
"""群公告模型"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import String, DateTime, ForeignKey
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class GroupAnnouncement(Base):
|
||||
__tablename__ = "group_announcements"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True)
|
||||
conversation_id: Mapped[str] = mapped_column(
|
||||
String(36), ForeignKey("conversations.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
author_id: Mapped[str] = mapped_column(String(36), ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
||||
content: Mapped[str] = mapped_column(String(1000), nullable=False)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, default=lambda: datetime.utcnow())
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime, default=lambda: datetime.utcnow(), onupdate=lambda: datetime.utcnow()
|
||||
)
|
||||
|
||||
author = relationship("User", foreign_keys=[author_id])
|
||||
@@ -21,12 +21,15 @@ class Message(Base):
|
||||
sender_id: Mapped[str] = mapped_column(
|
||||
String(36), ForeignKey("users.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
type: Mapped[str] = mapped_column(String(20), default="text") # text / image / file / system
|
||||
type: Mapped[str] = mapped_column(String(20), default="text") # text / image / file / voice / system
|
||||
content: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
reply_to_id: Mapped[str | None] = mapped_column(
|
||||
String(36), ForeignKey("messages.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
mentions: Mapped[str | None] = mapped_column(Text, nullable=True) # JSON: 被@的用户ID列表
|
||||
is_deleted: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
is_recalled: Mapped[bool] = mapped_column(Boolean, default=False) # 撤回
|
||||
recalled_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime, default=lambda: datetime.utcnow(), index=True
|
||||
)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
"""消息表情回应模型"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import String, DateTime, ForeignKey, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class MessageReaction(Base):
|
||||
__tablename__ = "message_reactions"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("message_id", "user_id", "emoji", name="uq_msg_reaction"),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True)
|
||||
message_id: Mapped[str] = mapped_column(String(36), ForeignKey("messages.id", ondelete="CASCADE"))
|
||||
user_id: Mapped[str] = mapped_column(String(36), ForeignKey("users.id", ondelete="CASCADE"))
|
||||
emoji: Mapped[str] = mapped_column(String(10), nullable=False)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, default=lambda: datetime.utcnow())
|
||||
|
||||
message = relationship("Message")
|
||||
user = relationship("User", foreign_keys=[user_id])
|
||||
@@ -0,0 +1,21 @@
|
||||
"""密码重置令牌模型"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import String, DateTime, ForeignKey
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class PasswordResetToken(Base):
|
||||
__tablename__ = "password_reset_tokens"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True)
|
||||
user_id: Mapped[str] = mapped_column(String(36), ForeignKey("users.id", ondelete="CASCADE"))
|
||||
token_hash: Mapped[str] = mapped_column(String(64), nullable=False) # 验证码的 hash
|
||||
expires_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
|
||||
used: Mapped[bool] = mapped_column(default=False)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, default=lambda: datetime.utcnow())
|
||||
|
||||
user = relationship("User", foreign_keys=[user_id])
|
||||
@@ -18,7 +18,11 @@ class User(Base):
|
||||
password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
avatar_url: Mapped[str | None] = mapped_column(String(500), nullable=True)
|
||||
bio: Mapped[str | None] = mapped_column(String(200), nullable=True)
|
||||
status: Mapped[str] = mapped_column(String(20), default="offline") # online/offline/away
|
||||
status: Mapped[str] = mapped_column(String(20), default="offline") # online/offline/away (presence)
|
||||
custom_status: Mapped[str | None] = mapped_column(String(50), nullable=True) # 个人心情状态文字
|
||||
status_emoji: Mapped[str | None] = mapped_column(String(10), nullable=True) # 心情 emoji
|
||||
status_expires_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) # 状态到期自动清空
|
||||
email_verified: Mapped[bool] = mapped_column(Boolean, default=False) # 邮箱是否已验证
|
||||
is_admin: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
is_banned: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
banned_reason: Mapped[str | None] = mapped_column(String(500), nullable=True)
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
"""用户拉黑模型"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import String, DateTime, ForeignKey, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class UserBlock(Base):
|
||||
__tablename__ = "user_blocks"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("blocker_id", "blocked_id", name="uq_user_block"),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True)
|
||||
blocker_id: Mapped[str] = mapped_column(String(36), ForeignKey("users.id", ondelete="CASCADE"))
|
||||
blocked_id: Mapped[str] = mapped_column(String(36), ForeignKey("users.id", ondelete="CASCADE"))
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, default=lambda: datetime.utcnow())
|
||||
|
||||
blocker = relationship("User", foreign_keys=[blocker_id])
|
||||
blocked = relationship("User", foreign_keys=[blocked_id])
|
||||
Reference in New Issue
Block a user