feat: 初始提交 v1.2.0 - 钢琴练习方案生成系统

This commit is contained in:
hmo
2026-04-21 20:00:33 +08:00
commit fd593bddf4
44 changed files with 10936 additions and 0 deletions
+236
View File
@@ -0,0 +1,236 @@
# 数据库模型定义
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"]