You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
265 lines
15 KiB
265 lines
15 KiB
from datetime import datetime
|
|
from enum import Enum
|
|
from typing import Optional
|
|
|
|
from sqlalchemy import Boolean, CheckConstraint, DateTime, Float, ForeignKey, Integer, String, Text, UniqueConstraint
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
|
|
from .database import Base
|
|
|
|
|
|
class UserStatus(str, Enum):
|
|
pending = "pending"
|
|
active = "active"
|
|
suspended = "suspended"
|
|
|
|
|
|
class User(Base):
|
|
__tablename__ = "users"
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
|
email_or_phone: Mapped[Optional[str]] = mapped_column(String(320), unique=True, index=True)
|
|
password_hash: Mapped[Optional[str]] = mapped_column(String(256))
|
|
status: Mapped[UserStatus] = mapped_column(String(20), default=UserStatus.active)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
profile: Mapped[Optional["UserProfile"]] = relationship(back_populates="user", cascade="all, delete-orphan", uselist=False)
|
|
location: Mapped[Optional["UserLocation"]] = relationship(back_populates="user", cascade="all, delete-orphan", uselist=False)
|
|
identities: Mapped[list["AuthIdentity"]] = relationship(back_populates="user", cascade="all, delete-orphan")
|
|
media: Mapped[list["UserMedia"]] = relationship(back_populates="user", cascade="all, delete-orphan")
|
|
posts: Mapped[list["Post"]] = relationship(back_populates="user", cascade="all, delete-orphan")
|
|
post_likes: Mapped[list["PostLike"]] = relationship(back_populates="user", cascade="all, delete-orphan")
|
|
post_comments: Mapped[list["PostComment"]] = relationship(back_populates="user", cascade="all, delete-orphan")
|
|
sent_messages: Mapped[list["ChatMessage"]] = relationship(back_populates="sender", cascade="all, delete-orphan")
|
|
sessions: Mapped[list["UserSession"]] = relationship(back_populates="user", cascade="all, delete-orphan")
|
|
question_answers: Mapped[list["UserQuestionAnswer"]] = relationship(back_populates="user", cascade="all, delete-orphan")
|
|
interests: Mapped[list["UserInterest"]] = relationship(back_populates="user", cascade="all, delete-orphan", order_by="UserInterest.id")
|
|
|
|
|
|
class AuthIdentity(Base):
|
|
__tablename__ = "auth_identities"
|
|
__table_args__ = (UniqueConstraint("provider", "provider_user_id"),)
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
|
provider: Mapped[str] = mapped_column(String(30))
|
|
provider_user_id: Mapped[str] = mapped_column(String(255))
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
|
user: Mapped[User] = relationship(back_populates="identities")
|
|
|
|
|
|
class UserProfile(Base):
|
|
__tablename__ = "user_profiles"
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), unique=True)
|
|
full_name: Mapped[Optional[str]] = mapped_column(String(120))
|
|
gender: Mapped[Optional[str]] = mapped_column(String(30))
|
|
birth_date: Mapped[Optional[str]] = mapped_column(String(10))
|
|
city: Mapped[Optional[str]] = mapped_column(String(120))
|
|
county: Mapped[Optional[str]] = mapped_column(String(120))
|
|
country: Mapped[Optional[str]] = mapped_column(String(120))
|
|
relationship_goal: Mapped[Optional[str]] = mapped_column(String(30))
|
|
bio: Mapped[Optional[str]] = mapped_column(Text)
|
|
occupation: Mapped[Optional[str]] = mapped_column(String(120))
|
|
education: Mapped[Optional[str]] = mapped_column(String(120))
|
|
height_cm: Mapped[Optional[int]] = mapped_column(Integer)
|
|
beliefs: Mapped[Optional[str]] = mapped_column(String(120))
|
|
smoking: Mapped[Optional[str]] = mapped_column(String(50))
|
|
drinking: Mapped[Optional[str]] = mapped_column(String(50))
|
|
pets: Mapped[Optional[str]] = mapped_column(String(120))
|
|
exercise: Mapped[Optional[str]] = mapped_column(String(50))
|
|
user: Mapped[User] = relationship(back_populates="profile")
|
|
|
|
|
|
class UserInterest(Base):
|
|
__tablename__ = "user_interests"
|
|
__table_args__ = (UniqueConstraint("user_id", "name", name="uq_user_interest_name"),)
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
|
name: Mapped[str] = mapped_column(String(120))
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
|
user: Mapped[User] = relationship(back_populates="interests")
|
|
|
|
|
|
class UserLocation(Base):
|
|
__tablename__ = "user_locations"
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), unique=True, index=True)
|
|
latitude: Mapped[float] = mapped_column(Float)
|
|
longitude: Mapped[float] = mapped_column(Float)
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
user: Mapped[User] = relationship(back_populates="location")
|
|
|
|
|
|
class UserSession(Base):
|
|
__tablename__ = "user_sessions"
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
|
refresh_token_hash: Mapped[str] = mapped_column(String(256), unique=True)
|
|
expires_at: Mapped[datetime] = mapped_column(DateTime, index=True)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
|
revoked_at: Mapped[Optional[datetime]] = mapped_column(DateTime)
|
|
user: Mapped[User] = relationship(back_populates="sessions")
|
|
|
|
|
|
class UserMedia(Base):
|
|
__tablename__ = "user_media"
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
|
media_type: Mapped[str] = mapped_column(String(20))
|
|
storage_path: Mapped[str] = mapped_column(String(500))
|
|
original_name: Mapped[Optional[str]] = mapped_column(String(255))
|
|
mime_type: Mapped[Optional[str]] = mapped_column(String(100))
|
|
review_status: Mapped[str] = mapped_column(String(20), default="pending")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
|
user: Mapped[User] = relationship(back_populates="media")
|
|
|
|
|
|
class Swipe(Base):
|
|
__tablename__ = "swipes"
|
|
__table_args__ = (
|
|
UniqueConstraint("actor_id", "target_id", name="uq_swipe_actor_target"),
|
|
CheckConstraint("actor_id <> target_id", name="ck_swipe_not_self"),
|
|
CheckConstraint("direction IN ('like', 'pass', 'super_like')", name="ck_swipe_direction"),
|
|
)
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
actor_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
|
target_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
|
direction: Mapped[str] = mapped_column(String(10))
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
|
|
class Match(Base):
|
|
__tablename__ = "matches"
|
|
__table_args__ = (
|
|
UniqueConstraint("user1_id", "user2_id", name="uq_match_user_pair"),
|
|
CheckConstraint("user1_id < user2_id", name="ck_match_canonical_order"),
|
|
)
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
user1_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
|
user2_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
|
matched_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
|
conversation: Mapped[Optional["Conversation"]] = relationship(back_populates="match", uselist=False, cascade="all, delete-orphan", single_parent=True)
|
|
|
|
|
|
class Conversation(Base):
|
|
__tablename__ = "conversations"
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
match_id: Mapped[int] = mapped_column(ForeignKey("matches.id", ondelete="CASCADE"), unique=True, index=True)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
match: Mapped[Match] = relationship(back_populates="conversation")
|
|
messages: Mapped[list["ChatMessage"]] = relationship(back_populates="conversation", cascade="all, delete-orphan")
|
|
|
|
|
|
class ChatMessage(Base):
|
|
__tablename__ = "chat_messages"
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
conversation_id: Mapped[int] = mapped_column(ForeignKey("conversations.id", ondelete="CASCADE"), index=True)
|
|
sender_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
|
content: Mapped[str] = mapped_column(Text)
|
|
message_type: Mapped[str] = mapped_column(String(20), default="text")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, index=True)
|
|
deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime)
|
|
conversation: Mapped[Conversation] = relationship(back_populates="messages")
|
|
sender: Mapped[User] = relationship(back_populates="sent_messages")
|
|
|
|
|
|
class Post(Base):
|
|
__tablename__ = "posts"
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
|
content: Mapped[str] = mapped_column(Text)
|
|
post_type: Mapped[str] = mapped_column(String(20), default="text")
|
|
content_format: Mapped[str] = mapped_column(String(10), default="plain", server_default="plain")
|
|
feeling: Mapped[Optional[str]] = mapped_column(String(50))
|
|
location_name: Mapped[Optional[str]] = mapped_column(String(255))
|
|
location_latitude: Mapped[Optional[float]] = mapped_column(Float)
|
|
location_longitude: Mapped[Optional[float]] = mapped_column(Float)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, index=True)
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
user: Mapped[User] = relationship(back_populates="posts")
|
|
likes: Mapped[list["PostLike"]] = relationship(back_populates="post", cascade="all, delete-orphan")
|
|
comments: Mapped[list["PostComment"]] = relationship(back_populates="post", cascade="all, delete-orphan")
|
|
media: Mapped[list["PostMedia"]] = relationship(
|
|
back_populates="post", cascade="all, delete-orphan", order_by="PostMedia.position"
|
|
)
|
|
|
|
|
|
class PostMedia(Base):
|
|
__tablename__ = "post_media"
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
post_id: Mapped[int] = mapped_column(ForeignKey("posts.id", ondelete="CASCADE"), index=True)
|
|
media_type: Mapped[str] = mapped_column(String(20))
|
|
storage_path: Mapped[str] = mapped_column(String(500))
|
|
original_name: Mapped[Optional[str]] = mapped_column(String(255))
|
|
mime_type: Mapped[str] = mapped_column(String(100))
|
|
size_bytes: Mapped[int] = mapped_column(Integer)
|
|
position: Mapped[int] = mapped_column(Integer, default=0)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
|
post: Mapped[Post] = relationship(back_populates="media")
|
|
|
|
|
|
class PostLike(Base):
|
|
__tablename__ = "post_likes"
|
|
__table_args__ = (UniqueConstraint("post_id", "user_id", name="uq_post_like"),)
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
post_id: Mapped[int] = mapped_column(ForeignKey("posts.id", ondelete="CASCADE"), index=True)
|
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
|
post: Mapped[Post] = relationship(back_populates="likes")
|
|
user: Mapped[User] = relationship(back_populates="post_likes")
|
|
|
|
|
|
class PostComment(Base):
|
|
__tablename__ = "post_comments"
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
post_id: Mapped[int] = mapped_column(ForeignKey("posts.id", ondelete="CASCADE"), index=True)
|
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
|
parent_comment_id: Mapped[Optional[int]] = mapped_column(
|
|
ForeignKey("post_comments.id", ondelete="CASCADE"), index=True
|
|
)
|
|
content: Mapped[str] = mapped_column(Text)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, index=True)
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
post: Mapped[Post] = relationship(back_populates="comments")
|
|
user: Mapped[User] = relationship(back_populates="post_comments")
|
|
parent: Mapped[Optional["PostComment"]] = relationship(remote_side=[id], back_populates="replies")
|
|
replies: Mapped[list["PostComment"]] = relationship(back_populates="parent", cascade="all, delete-orphan")
|
|
likes: Mapped[list["CommentLike"]] = relationship(back_populates="comment", cascade="all, delete-orphan")
|
|
|
|
|
|
class CommentLike(Base):
|
|
__tablename__ = "comment_likes"
|
|
__table_args__ = (UniqueConstraint("comment_id", "user_id", name="uq_comment_like"),)
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
comment_id: Mapped[int] = mapped_column(ForeignKey("post_comments.id", ondelete="CASCADE"), index=True)
|
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
|
comment: Mapped["PostComment"] = relationship(back_populates="likes")
|
|
user: Mapped[User] = relationship()
|
|
|
|
|
|
class FeedQuestion(Base):
|
|
__tablename__ = "feed_questions"
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
code: Mapped[str] = mapped_column(String(100), unique=True)
|
|
question_text: Mapped[str] = mapped_column(String(500))
|
|
question_text_zh: Mapped[Optional[str]] = mapped_column(String(500))
|
|
answer_type: Mapped[str] = mapped_column(String(20), default="boolean")
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True, index=True)
|
|
sort_order: Mapped[int] = mapped_column(Integer, default=0)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
|
answers: Mapped[list["UserQuestionAnswer"]] = relationship(back_populates="question", cascade="all, delete-orphan")
|
|
|
|
|
|
class UserQuestionAnswer(Base):
|
|
__tablename__ = "user_question_answers"
|
|
__table_args__ = (UniqueConstraint("user_id", "question_id", name="uq_user_question_answer"),)
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
|
question_id: Mapped[int] = mapped_column(ForeignKey("feed_questions.id", ondelete="CASCADE"), index=True)
|
|
boolean_answer: Mapped[Optional[bool]] = mapped_column(Boolean)
|
|
text_answer: Mapped[Optional[str]] = mapped_column(Text)
|
|
answered_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
user: Mapped[User] = relationship(back_populates="question_answers")
|
|
question: Mapped[FeedQuestion] = relationship(back_populates="answers")
|
|
|