470 lines
23 KiB
Python
470 lines
23 KiB
Python
# models.py
|
||
from flask_sqlalchemy import SQLAlchemy
|
||
from datetime import datetime
|
||
import json
|
||
|
||
db = SQLAlchemy()
|
||
|
||
# 用户表
|
||
class User(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
name = db.Column(db.String(80), nullable=False)
|
||
email = db.Column(db.String(120), unique=True, nullable=True)
|
||
phone = db.Column(db.String(20), unique=True, nullable=True)
|
||
password = db.Column(db.String(128), nullable=False)
|
||
role = db.Column(db.String(20), default='student') # admin, teacher, student
|
||
avatar = db.Column(db.String(200), default='') # 头像URL
|
||
is_banned = db.Column(db.Boolean, default=False)
|
||
completed_exams = db.Column(db.Integer, default=0) # 已完成并批改的考试次数
|
||
name_changed_at = db.Column(db.DateTime, nullable=True) # 上次修改用户名时间
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
# 关系
|
||
exams_created = db.relationship('Exam', back_populates='creator', lazy=True)
|
||
submissions = db.relationship('Submission', back_populates='user', lazy=True)
|
||
drafts = db.relationship('Draft', back_populates='user', lazy=True)
|
||
posts = db.relationship('Post', back_populates='author', lazy=True)
|
||
replies = db.relationship('Reply', back_populates='author', lazy=True)
|
||
reactions = db.relationship('Reaction', back_populates='user', lazy=True)
|
||
bookmarks = db.relationship('Bookmark', back_populates='user', lazy=True)
|
||
reports = db.relationship('Report', backref='reporter', lazy=True)
|
||
notifications = db.relationship('Notification', backref='user', lazy=True)
|
||
contest_memberships = db.relationship('ContestMembership', back_populates='user', lazy=True)
|
||
contest_registrations = db.relationship('ContestRegistration', backref='user', lazy=True)
|
||
# teacher_applications 将通过 TeacherApplication 的 back_populates 自动创建
|
||
|
||
# 杯赛成员表(记录用户在特定杯赛中的角色)
|
||
class ContestMembership(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||
contest_id = db.Column(db.Integer, db.ForeignKey('contest.id'), nullable=False)
|
||
role = db.Column(db.String(20), nullable=False) # 'owner' 或 'teacher'
|
||
joined_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
user = db.relationship('User', back_populates='contest_memberships')
|
||
contest = db.relationship('Contest', back_populates='members')
|
||
|
||
__table_args__ = (db.UniqueConstraint('user_id', 'contest_id', name='uq_user_contest'),)
|
||
|
||
# 杯赛申请表(用于用户申请举办新杯赛)
|
||
class ContestApplication(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||
name = db.Column(db.String(100), nullable=False)
|
||
organizer = db.Column(db.String(100))
|
||
description = db.Column(db.Text)
|
||
contact = db.Column(db.String(100))
|
||
status = db.Column(db.String(20), default='pending') # pending, approved, rejected
|
||
applied_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
reviewed_at = db.Column(db.DateTime)
|
||
total_score = db.Column(db.Integer, default=150) # 杯赛默认满分
|
||
start_date = db.Column(db.String(20)) # 申请时填写的开始日期
|
||
end_date = db.Column(db.String(20)) # 申请时填写的结束日期
|
||
# 报备信息
|
||
responsible_person = db.Column(db.String(80)) # 责任人姓名
|
||
responsible_phone = db.Column(db.String(20)) # 责任人电话
|
||
responsible_email = db.Column(db.String(120)) # 责任人邮箱
|
||
organization = db.Column(db.String(100)) # 所属机构/学校
|
||
|
||
user = db.relationship('User', backref='contest_applications')
|
||
|
||
# 杯赛报名表(用户报名参加杯赛)
|
||
class ContestRegistration(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||
contest_id = db.Column(db.Integer, db.ForeignKey('contest.id'), nullable=False)
|
||
registered_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
__table_args__ = (db.UniqueConstraint('user_id', 'contest_id', name='uq_user_contest_reg'),)
|
||
|
||
# 杯赛表
|
||
class Contest(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
name = db.Column(db.String(100), nullable=False)
|
||
organizer = db.Column(db.String(100))
|
||
description = db.Column(db.Text)
|
||
start_date = db.Column(db.String(20))
|
||
end_date = db.Column(db.String(20))
|
||
status = db.Column(db.String(20), default='upcoming')
|
||
participants = db.Column(db.Integer, default=0)
|
||
created_by = db.Column(db.Integer, db.ForeignKey('user.id'))
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
past_papers = db.Column(db.Text) # JSON 存储历年真题 [{year, title, file}]
|
||
total_score = db.Column(db.Integer, default=150) # 杯赛默认考试满分
|
||
visible = db.Column(db.Boolean, default=True) # 是否对普通用户可见
|
||
# 报备信息
|
||
responsible_person = db.Column(db.String(80)) # 责任人姓名
|
||
responsible_phone = db.Column(db.String(20)) # 责任人电话
|
||
responsible_email = db.Column(db.String(120)) # 责任人邮箱
|
||
organization = db.Column(db.String(100)) # 所属机构/学校
|
||
|
||
creator = db.relationship('User', backref='contests_created')
|
||
members = db.relationship('ContestMembership', back_populates='contest', lazy=True, cascade='all, delete-orphan')
|
||
exams = db.relationship('Exam', backref='contest', lazy=True)
|
||
posts = db.relationship('Post', back_populates='contest', lazy=True, cascade='all, delete-orphan')
|
||
teacher_applications = db.relationship('TeacherApplication', back_populates='contest', lazy=True, cascade='all, delete-orphan')
|
||
|
||
def get_past_papers(self):
|
||
return json.loads(self.past_papers) if self.past_papers else []
|
||
|
||
def set_past_papers(self, papers):
|
||
self.past_papers = json.dumps(papers)
|
||
|
||
# 题库表(杯赛老师和负责人往题库添加题目,负责人选题组卷)
|
||
class QuestionBankItem(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
contest_id = db.Column(db.Integer, db.ForeignKey('contest.id'), nullable=False)
|
||
contributor_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||
type = db.Column(db.String(20), nullable=False) # 'choice', 'fill', 'essay'
|
||
content = db.Column(db.Text, nullable=False) # 题目内容
|
||
options = db.Column(db.Text) # JSON,选择题选项
|
||
answer = db.Column(db.Text) # 答案
|
||
score = db.Column(db.Integer, default=10) # 建议分值
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
contest = db.relationship('Contest', backref='question_bank_items')
|
||
contributor = db.relationship('User', backref='contributed_questions')
|
||
|
||
# 考试表
|
||
class Exam(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
contest_id = db.Column(db.Integer, db.ForeignKey('contest.id'), nullable=True)
|
||
title = db.Column(db.String(200), nullable=False)
|
||
subject = db.Column(db.String(50))
|
||
duration = db.Column(db.Integer)
|
||
total_score = db.Column(db.Integer)
|
||
creator_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
||
questions = db.Column(db.Text) # JSON
|
||
encrypted_questions = db.Column(db.Text) # 加密后的试卷内容
|
||
is_encrypted = db.Column(db.Boolean, default=False) # 是否加密
|
||
access_password = db.Column(db.String(128)) # 考试访问密码
|
||
scheduled_start = db.Column(db.DateTime, nullable=True) # 预定开始时间
|
||
scheduled_end = db.Column(db.DateTime, nullable=True) # 预定结束时间
|
||
score_release_time = db.Column(db.DateTime, nullable=True) # 成绩公布时间
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
status = db.Column(db.String(20), default='available')
|
||
|
||
creator = db.relationship('User', back_populates='exams_created')
|
||
submissions = db.relationship('Submission', back_populates='exam', lazy=True, cascade='all, delete-orphan')
|
||
drafts = db.relationship('Draft', back_populates='exam', lazy=True, cascade='all, delete-orphan')
|
||
|
||
def get_questions(self):
|
||
return json.loads(self.questions) if self.questions else []
|
||
|
||
def set_questions(self, questions):
|
||
self.questions = json.dumps(questions)
|
||
|
||
# 提交记录表
|
||
class Submission(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
exam_id = db.Column(db.Integer, db.ForeignKey('exam.id'))
|
||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
||
answers = db.Column(db.Text) # JSON
|
||
score = db.Column(db.Integer, default=0)
|
||
question_scores = db.Column(db.Text) # JSON
|
||
graded = db.Column(db.Boolean, default=False)
|
||
graded_by = db.Column(db.String(80))
|
||
submitted_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
exam = db.relationship('Exam', back_populates='submissions')
|
||
user = db.relationship('User', back_populates='submissions')
|
||
|
||
def get_answers(self):
|
||
return json.loads(self.answers) if self.answers else {}
|
||
|
||
def set_answers(self, answers):
|
||
self.answers = json.dumps(answers)
|
||
|
||
def get_question_scores(self):
|
||
return json.loads(self.question_scores) if self.question_scores else {}
|
||
|
||
def set_question_scores(self, scores):
|
||
self.question_scores = json.dumps(scores)
|
||
|
||
# 草稿表
|
||
class Draft(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
exam_id = db.Column(db.Integer, db.ForeignKey('exam.id'))
|
||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
||
answers = db.Column(db.Text) # JSON
|
||
saved_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||
|
||
exam = db.relationship('Exam', back_populates='drafts')
|
||
user = db.relationship('User', back_populates='drafts')
|
||
|
||
def get_answers(self):
|
||
return json.loads(self.answers) if self.answers else {}
|
||
|
||
def set_answers(self, answers):
|
||
self.answers = json.dumps(answers)
|
||
|
||
# 论坛帖子表
|
||
class Post(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
title = db.Column(db.String(200), nullable=False)
|
||
content = db.Column(db.Text, nullable=False)
|
||
author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
||
tag = db.Column(db.String(50), default='全部')
|
||
is_official = db.Column(db.Boolean, default=False)
|
||
pinned = db.Column(db.Boolean, default=False)
|
||
likes = db.Column(db.Integer, default=0)
|
||
replies_count = db.Column(db.Integer, default=0)
|
||
views = db.Column(db.Integer, default=0)
|
||
has_poll = db.Column(db.Boolean, default=False)
|
||
images = db.Column(db.Text) # JSON
|
||
contest_id = db.Column(db.Integer, db.ForeignKey('contest.id'), nullable=True)
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||
|
||
author = db.relationship('User', back_populates='posts')
|
||
replies = db.relationship('Reply', back_populates='post', lazy=True, cascade='all, delete-orphan')
|
||
poll = db.relationship('Poll', back_populates='post', uselist=False, cascade='all, delete-orphan')
|
||
reactions = db.relationship('Reaction', back_populates='post', lazy=True, cascade='all, delete-orphan')
|
||
bookmarks = db.relationship('Bookmark', back_populates='post', lazy=True, cascade='all, delete-orphan')
|
||
contest = db.relationship('Contest', back_populates='posts')
|
||
|
||
def get_images(self):
|
||
return json.loads(self.images) if self.images else []
|
||
|
||
def set_images(self, images):
|
||
self.images = json.dumps(images)
|
||
|
||
# 回复表
|
||
class Reply(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
|
||
author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
||
content = db.Column(db.Text, nullable=False)
|
||
likes = db.Column(db.Integer, default=0)
|
||
reply_to = db.Column(db.String(80))
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||
|
||
post = db.relationship('Post', back_populates='replies')
|
||
author = db.relationship('User', back_populates='replies')
|
||
reactions = db.relationship('Reaction', back_populates='reply', lazy=True, cascade='all, delete-orphan')
|
||
|
||
# 投票表
|
||
class Poll(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
|
||
question = db.Column(db.String(200), nullable=False)
|
||
options = db.Column(db.Text) # JSON
|
||
voters = db.Column(db.Text) # JSON
|
||
multi = db.Column(db.Boolean, default=False)
|
||
total_votes = db.Column(db.Integer, default=0)
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
post = db.relationship('Post', back_populates='poll')
|
||
|
||
def get_options(self):
|
||
return json.loads(self.options) if self.options else []
|
||
|
||
def set_options(self, options):
|
||
self.options = json.dumps(options)
|
||
|
||
def get_voters(self):
|
||
return json.loads(self.voters) if self.voters else {}
|
||
|
||
def set_voters(self, voters):
|
||
self.voters = json.dumps(voters)
|
||
|
||
# 表情反应表
|
||
class Reaction(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
||
post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=True)
|
||
reply_id = db.Column(db.Integer, db.ForeignKey('reply.id'), nullable=True)
|
||
reaction = db.Column(db.String(20))
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
user = db.relationship('User', back_populates='reactions')
|
||
post = db.relationship('Post', back_populates='reactions')
|
||
reply = db.relationship('Reply', back_populates='reactions')
|
||
|
||
__table_args__ = (
|
||
db.UniqueConstraint('user_id', 'post_id', name='uq_user_post_reaction'),
|
||
db.UniqueConstraint('user_id', 'reply_id', name='uq_user_reply_reaction'),
|
||
)
|
||
|
||
# 收藏表
|
||
class Bookmark(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
||
post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
user = db.relationship('User', back_populates='bookmarks')
|
||
post = db.relationship('Post', back_populates='bookmarks')
|
||
|
||
__table_args__ = (db.UniqueConstraint('user_id', 'post_id', name='uq_user_post_bookmark'),)
|
||
|
||
# 举报表
|
||
class Report(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
type = db.Column(db.String(20))
|
||
target_id = db.Column(db.Integer)
|
||
reporter_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
||
reason = db.Column(db.String(100))
|
||
detail = db.Column(db.Text)
|
||
status = db.Column(db.String(20), default='pending')
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
# reporter 已在 User 中通过 backref 定义
|
||
|
||
# 通知表
|
||
class Notification(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
||
type = db.Column(db.String(50))
|
||
content = db.Column(db.Text)
|
||
from_user = db.Column(db.String(80))
|
||
post_id = db.Column(db.Integer, nullable=True)
|
||
read = db.Column(db.Boolean, default=False)
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
# user 已在 User 中通过 backref 定义
|
||
|
||
# 系统公告表(管理员发布的全站通知)
|
||
class SystemNotification(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
title = db.Column(db.String(200), nullable=False)
|
||
content = db.Column(db.Text, nullable=False)
|
||
author_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||
pinned = db.Column(db.Boolean, default=False)
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||
|
||
author = db.relationship('User', backref='system_notifications')
|
||
|
||
# 编辑历史表(可选)
|
||
class EditHistory(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
|
||
title = db.Column(db.String(200))
|
||
content = db.Column(db.Text)
|
||
edited_by = db.Column(db.String(80))
|
||
edited_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
post = db.relationship('Post')
|
||
|
||
# 教师申请表(针对特定杯赛)
|
||
class TeacherApplication(db.Model):
|
||
"""教师申请记录(针对特定杯赛)"""
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||
contest_id = db.Column(db.Integer, db.ForeignKey('contest.id'), nullable=False)
|
||
name = db.Column(db.String(80), nullable=False) # 申请人姓名
|
||
email = db.Column(db.String(120), nullable=False) # 申请人邮箱
|
||
reason = db.Column(db.Text, nullable=False) # 申请理由
|
||
status = db.Column(db.String(20), default='pending') # pending, approved, rejected
|
||
applied_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
reviewed_at = db.Column(db.DateTime)
|
||
reviewed_by = db.Column(db.Integer, db.ForeignKey('user.id'))
|
||
|
||
# 关系
|
||
user = db.relationship('User', foreign_keys=[user_id], backref='teacher_applications')
|
||
contest = db.relationship('Contest', back_populates='teacher_applications')
|
||
reviewer = db.relationship('User', foreign_keys=[reviewed_by])
|
||
|
||
__table_args__ = (db.UniqueConstraint('user_id', 'contest_id', name='uq_user_contest_application'),)
|
||
|
||
# 好友表
|
||
class Friend(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||
friend_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||
status = db.Column(db.String(20), default='pending') # pending, accepted
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
user = db.relationship('User', foreign_keys=[user_id], backref=db.backref('friends_initiated', lazy='dynamic'))
|
||
friend = db.relationship('User', foreign_keys=[friend_id], backref=db.backref('friends_received', lazy='dynamic'))
|
||
|
||
# 试卷收藏表
|
||
class ExamBookmark(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||
exam_id = db.Column(db.Integer, db.ForeignKey('exam.id'), nullable=False)
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
user = db.relationship('User', backref=db.backref('exam_bookmarks', lazy='dynamic', cascade='all, delete-orphan'))
|
||
exam = db.relationship('Exam', backref=db.backref('bookmarked_by', lazy='dynamic', cascade='all, delete-orphan'))
|
||
|
||
__table_args__ = (db.UniqueConstraint('user_id', 'exam_id', name='uq_user_exam_bookmark'),)
|
||
|
||
# 聊天室表
|
||
class ChatRoom(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
type = db.Column(db.String(20), nullable=False) # 'private' | 'group' | 'contest'
|
||
name = db.Column(db.String(100), nullable=True)
|
||
avatar = db.Column(db.String(200), default='')
|
||
creator_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True)
|
||
contest_id = db.Column(db.Integer, db.ForeignKey('contest.id'), unique=True, nullable=True)
|
||
announcement = db.Column(db.Text, default='')
|
||
announcement_by = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True)
|
||
announcement_at = db.Column(db.DateTime, nullable=True)
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
creator = db.relationship('User', foreign_keys=[creator_id], backref='chatrooms_created')
|
||
announcement_user = db.relationship('User', foreign_keys=[announcement_by])
|
||
contest = db.relationship('Contest', backref=db.backref('chatroom', uselist=False))
|
||
members = db.relationship('ChatRoomMember', backref='room', lazy=True, cascade='all, delete-orphan')
|
||
messages = db.relationship('Message', backref='room', lazy=True, cascade='all, delete-orphan')
|
||
|
||
# 聊天室成员表
|
||
class ChatRoomMember(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
room_id = db.Column(db.Integer, db.ForeignKey('chat_room.id'), nullable=False)
|
||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||
role = db.Column(db.String(20), default='member') # 'admin' | 'member'
|
||
nickname = db.Column(db.String(50), default='')
|
||
muted = db.Column(db.Boolean, default=False)
|
||
last_read_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
joined_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
user = db.relationship('User', backref='chat_memberships')
|
||
|
||
__table_args__ = (db.UniqueConstraint('room_id', 'user_id', name='uq_room_user'),)
|
||
|
||
# 消息表
|
||
class Message(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
room_id = db.Column(db.Integer, db.ForeignKey('chat_room.id'), nullable=False)
|
||
sender_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||
type = db.Column(db.String(20), default='text') # 'text' | 'image' | 'file' | 'system' | 'voice'
|
||
content = db.Column(db.Text, default='')
|
||
file_url = db.Column(db.String(300), nullable=True)
|
||
file_name = db.Column(db.String(200), nullable=True)
|
||
recalled = db.Column(db.Boolean, default=False)
|
||
reply_to_id = db.Column(db.Integer, db.ForeignKey('message.id'), nullable=True)
|
||
mentions = db.Column(db.Text, default='') # JSON array of user IDs, e.g. "[1,2,3]" or "all"
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
sender = db.relationship('User', backref='messages_sent')
|
||
reply_to = db.relationship('Message', remote_side='Message.id', backref='replies')
|
||
reactions = db.relationship('MessageReaction', backref='message', lazy=True, cascade='all, delete-orphan')
|
||
|
||
# 消息表情回应表
|
||
class MessageReaction(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
message_id = db.Column(db.Integer, db.ForeignKey('message.id'), nullable=False)
|
||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||
emoji = db.Column(db.String(10), nullable=False)
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
user = db.relationship('User', backref='message_reactions')
|
||
|
||
__table_args__ = (db.UniqueConstraint('message_id', 'user_id', 'emoji', name='uq_msg_user_emoji'),)
|
||
|
||
# 邀请码表(教师申请审批通过后生成,老师输入邀请码激活身份)
|
||
class InviteCode(db.Model):
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
code = db.Column(db.String(32), unique=True, nullable=False)
|
||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||
application_id = db.Column(db.Integer, db.ForeignKey('teacher_application.id'), nullable=False)
|
||
used = db.Column(db.Boolean, default=False)
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
used_at = db.Column(db.DateTime, nullable=True)
|
||
|
||
user = db.relationship('User', backref='invite_codes')
|
||
application = db.relationship('TeacherApplication', backref='invite_code') |