# 数据库模型定义 from flask_sqlalchemy import SQLAlchemy from datetime import datetime import re db = SQLAlchemy() class User(db.Model): """管理员用户表""" __tablename__ = "users" id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(50), unique=True, nullable=False) password_hash = db.Column(db.String(200), nullable=False) role = db.Column(db.String(20), default="user") # admin / user created_at = db.Column(db.DateTime, default=datetime.now) def set_password(self, password): """设置密码(验证复杂度后hash)""" if not self.validate_password(password): raise ValueError("密码不符合要求") import hashlib self.password_hash = hashlib.sha256(password.encode()).hexdigest() def check_password(self, password): """验证密码""" import hashlib return self.password_hash == hashlib.sha256(password.encode()).hexdigest() @staticmethod def validate_password(password): """验证密码复杂度:大写+小写+数字+特殊字符,8位以上""" if len(password) < 8: return False if not re.search(r"[A-Z]", password): return False if not re.search(r"[a-z]", password): return False if not re.search(r"[0-9]", password): return False if not re.search(r"['!@#$%^&*(),.?\":{}|<>\-_]", password): return False return True def to_dict(self): return { "id": self.id, "username": self.username, "role": self.role, "created_at": self.created_at.strftime("%Y-%m-%d %H:%M") if self.created_at else None, } class Class(db.Model): """班级表""" __tablename__ = "classes" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) description = db.Column(db.Text) active = db.Column(db.Boolean, default=True) # 进行中 created_at = db.Column(db.DateTime, default=datetime.now) # 关联 - 在Student模型中定义backref def to_dict(self): # 直接查询学员数量,避免relationship问题 from app.models import Student student_count = Student.query.filter_by(class_id=self.id).count() return { "id": self.id, "name": self.name, "description": self.description, "active": self.active if self.active is not None else True, "student_count": student_count, "created_at": self.created_at.strftime("%Y-%m-%d %H:%M") if self.created_at else None, } class Student(db.Model): """学员表""" __tablename__ = "students" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) phone = db.Column(db.String(20)) wechat_nickname = db.Column(db.String(100)) # 微信昵称 practice_time = db.Column(db.String(20), default="30分钟") # 每日练习时间 notes = db.Column(db.Text) # 备注 class_id = db.Column(db.Integer, db.ForeignKey("classes.id")) # 班级 created_at = db.Column(db.DateTime, default=datetime.now) # 关联 problems = db.relationship( "StudentProblem", backref="student", lazy="dynamic", cascade="all, delete-orphan", ) plans = db.relationship( "PracticePlan", backref="student", lazy="dynamic", cascade="all, delete-orphan" ) class_obj = db.relationship("Class", backref="students") def to_dict(self): return { "id": self.id, "name": self.name, "phone": self.phone, "wechat_nickname": self.wechat_nickname, "practice_time": self.practice_time, "notes": self.notes, "class_id": self.class_id, "class_name": self.class_obj.name if self.class_obj else None, "created_at": self.created_at.strftime("%Y-%m-%d %H:%M") if self.created_at else None, "problem_count": self.problems.count(), "plan_count": self.plans.count(), } class StudentProblem(db.Model): """学员问题记录表""" __tablename__ = "student_problems" id = db.Column(db.Integer, primary_key=True) student_id = db.Column(db.Integer, db.ForeignKey("students.id"), nullable=False) problem_id = db.Column(db.String(50), nullable=False) # 如 "01_手小" problem_name = db.Column(db.String(100), nullable=False) # 如 "手小" severity = db.Column(db.String(10), nullable=False) # 轻微/中等/严重 level = db.Column(db.String(20)) # 启蒙/入门/进阶/熟练/精通 created_at = db.Column(db.DateTime, default=datetime.now) def to_dict(self): return { "id": self.id, "problem_id": self.problem_id, "problem_name": self.problem_name, "severity": self.severity, "level": self.level, } class PracticePlan(db.Model): """练习方案表""" __tablename__ = "practice_plans" id = db.Column(db.Integer, primary_key=True) student_id = db.Column(db.Integer, db.ForeignKey("students.id"), nullable=False) content = db.Column(db.Text, nullable=False) # JSON格式存储方案内容 created_at = db.Column(db.DateTime, default=datetime.now) def to_dict(self): return { "id": self.id, "student_id": self.student_id, "student_name": self.student.name if self.student else "", "created_at": self.created_at.strftime("%Y-%m-%d %H:%M") if self.created_at else None, "content": self.content, } class Template(db.Model): """模板配置表""" __tablename__ = "templates" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(50), unique=True, nullable=False) # 模板名称 type = db.Column(db.String(20), nullable=False) # ai_prompt / report content = db.Column(db.Text, nullable=False) # 模板内容 description = db.Column(db.String(200)) # 模板描述 sort_order = db.Column(db.Integer, default=0) # 排序,数字越小越靠前 created_at = db.Column(db.DateTime, default=datetime.now) updated_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now) def to_dict(self): return { "id": self.id, "name": self.name, "type": self.type, "content": self.content, "description": self.description, "sort_order": self.sort_order, "created_at": self.created_at.strftime("%Y-%m-%d %H:%M") if self.created_at else None, "updated_at": self.updated_at.strftime("%Y-%m-%d %H:%M") if self.updated_at else None, } # 问题配置(从文件读取) PROBLEM_LIST = [ {"id": "01_手小", "name": "手小", "category": "技术类(生理限制)"}, {"id": "02_识谱慢", "name": "识谱慢", "category": "识谱类"}, {"id": "03_节奏感差", "name": "节奏感差", "category": "综合类"}, {"id": "04_压手腕", "name": "压手腕", "category": "技术类(手型)"}, {"id": "05_掌关节支撑差", "name": "掌关节支撑差", "category": "技术类(手型)"}, {"id": "06_第一关节支撑差", "name": "第一关节支撑差", "category": "技术类(手型)"}, {"id": "07_对键盘不熟悉", "name": "对键盘不熟悉", "category": "识谱类"}, {"id": "08_手指僵硬_紧张", "name": "手指僵硬、紧张", "category": "技术类(手型)"}, {"id": "09_手指不会跑动", "name": "手指不会跑动", "category": "技术类"}, {"id": "10_力度不会把握", "name": "力度不会把握", "category": "技术类"}, {"id": "11_左右手不协调", "name": "左右手不协调", "category": "综合类"}, {"id": "12_不会用节拍器", "name": "不会用节拍器", "category": "识谱类"}, {"id": "13_不会编配指法", "name": "不会编配指法", "category": "综合类"}, {"id": "14_基本功练习", "name": "基本功练习", "category": "综合类"}, {"id": "15_练习缺乏监督", "name": "练习缺乏监督", "category": "综合类"}, ] SEVERITY_LEVELS = ["轻微", "中等", "严重"] LEVEL_OPTIONS = ["启蒙", "入门", "进阶", "熟练", "精通"] PRACTICE_TIME_OPTIONS = [ "15分钟", "30分钟", "45分钟", "60分钟", "90分钟", "120分钟", "150分钟以上", ] ROLE_OPTIONS = ["admin", "user"]