Files
piano-plan/docs/MODELS.md
T
hmo 18351212e8 feat: 问题数据迁移到数据库;学员详情页URL导航改造;侧边栏统一
- 问题从文件系统迁移到数据库 problems 表
- 移除 PROBLEMS_DIR 配置和文件读取逻辑
- student.html 完整重写:编辑/添加/删除问题,生成方案进度显示
- 学员详情页支持独立URL访问 (/student/<id>)
- 统一侧边栏到 base.html
- 更新文档:DEPLOYMENT_SOP, MODELS, STRUCTURE, FRONTEND_ARCH
- 部署到生产环境 v1.2.0
2026-04-23 06:35:32 +08:00

8.1 KiB

数据模型说明

概述

本系统使用 SQLite 数据库,ORM 框架为 Flask-SQLAlchemy。

数据库文件: data/piano_plans.db


数据表

0. Problem (问题定义)

系统预定义的15种常见钢琴学习问题。

字段 类型 说明
id Integer 主键,自增
code String(50) 问题编号,如 "05_掌关节支撑差"
name String(100) 问题名称,如 "掌关节支撑差"
category String(20) 分类:技术类/认知类/节奏类/习惯类/综合类
created_at DateTime 创建时间

⚠️ 问题数据已从文件系统迁移到数据库。student_problems 表通过 problem_id 外键关联到此表。


1. User (用户)

系统用户,用于登录认证和权限管理。

字段 类型 说明
id Integer 主键,自增
username String(50) 用户名,唯一,必填
password_hash String(200) 密码哈希值
role String(20) 角色:admin/user,默认 user
created_at DateTime 创建时间

角色说明:

  • admin: 管理员,拥有所有权限
  • user: 普通用户,受限权限

密码规则: 8位以上,包含大小写字母、数字和特殊字符


2. Student (学员)

字段 类型 说明
id Integer 主键,自增
name String(100) 学员姓名,必填
phone String(20) 手机号,可选
wechat_nickname String(100) 微信昵称,可选
practice_time String(20) 每日练习时间,默认"30分钟"
notes Text 备注信息,可选
class_id Integer 外键,关联 Class,可选
created_at DateTime 创建时间

练习时间选项: 15分钟, 30分钟, 45分钟, 60分钟, 90分钟, 120分钟, 150分钟以上

关系:

  • problems: 与 StudentProblem 一对多
  • plans: 与 PracticePlan 一对多
  • class: 与 Class 多对一

3. StudentProblem (学员问题记录)

字段 类型 说明
id Integer 主键,自增
student_id Integer 外键,关联 Student
problem_id Integer 外键,关联 Problem.id
severity String(10) 严重程度:轻微/中等/严重
level String(20) 级别:启蒙/入门/进阶/熟练/精通
created_at DateTime 创建时间

⚠️ problem_id 现为数字外键,关联 Problem.id。通过 student_problem.problem 关系获取问题名称。


4. Class (班级)

字段 类型 说明
id Integer 主键,自增
name String(100) 班级名称,必填
description Text 班级描述,可选
created_at DateTime 创建时间

关系:

  • students: 与 Student 一对多

5. PracticePlan (练习方案)

字段 类型 说明
id Integer 主键,自增
student_id Integer 外键,关联 Student
content Text 方案内容(JSON格式)
created_at DateTime 创建时间

content 字段结构:

{
  "student_name": "张三",
  "practice_time": "30分钟",
  "total_daily_minutes": 30,
  "problems": [
    {
      "name": "手小",
      "severity": "中等",
      "level": "入门",
      "focus": {"basic": 15, "tech": 10, "piece": 20}
    }
  ],
  "daily_schedule": [
    {
      "phase": "热身",
      "duration": "3分钟",
      "content": "手部放松操 + 呼吸调节",
      "purpose": "放松肌肉,进入状态"
    }
  ],
  "ai_report": "## 个性化练习方案报告\n\n..."
}

ER 关系图

┌─────────────┐       ┌──────────────────┐       ┌───────────────┐
│    User     │       │    Student       │       │    Class      │
├─────────────┤       ├──────────────────┤       ├───────────────┤
│ id          │       │ id               │◄──────│ id            │
│ username    │       │ name             │       │ name          │
│ password    │       │ phone            │       │ description   │
│ role        │       │ wechat_nickname  │       │ created_at    │
│ created_at  │       │ practice_time    │       └───────────────┘
└─────────────┘       │ notes            │
                      │ class_id         │──┐
                      │ created_at       │  │
                      └──────────────────┘  │
                              │             │
                              ▼             │
                      ┌──────────────────┐  │
                      │  StudentProblem  │  │
                      ├──────────────────┤  │
                      │ id               │  │
                      │ student_id  ─────┘  │
                      │ problem_id        │◄─┼──► Problem
                      │ severity          │
                      │ level             │
                      │ created_at        │
                      └──────────────────┘
                              │
                              ▼
                      ┌──────────────────┐
                      │  PracticePlan    │
                      ├──────────────────┤
                      │ id               │
                      │ student_id  ─────┼──► Student
                      │ content          │
                      │ created_at       │
                      └──────────────────┘

常用查询

查询用户角色

from app.models import User
user = User.query.filter_by(username="admin").first()
print(user.role)  # "admin" or "user"

查询学员及其问题

student = Student.query.get(1)
for sp in student.problems:
    print(sp.problem.name, sp.problem.code, sp.severity, sp.level)

查询班级及其学员

cls = Class.query.get(1)
for student in cls.students:
    print(student.name)

查询学员及其方案

student = Student.query.get(1)
for plan in student.plans:
    print(plan.created_at)

获取最新方案

from app.models import PracticePlan
latest_plan = PracticePlan.query.order_by(
    PracticePlan.created_at.desc()
).first()

数据库管理

初始化数据库

首次运行应用时会自动创建所有表:

from app import create_app
from app.models import db

app = create_app()
with app.app_context():
    db.create_all()

备份数据库

# 停止服务后复制数据库文件
copy data\piano_plans.db backup\piano_plans_backup.db

查看数据库内容

# 使用 SQLite 命令行
sqlite3 data\piano_plans.db

# 查看表
.schema

# 查询数据
SELECT * FROM users;

数据字典

User.role 取值

说明
admin 管理员,拥有所有权限
user 普通用户,受限权限

StudentProblem.severity 取值

说明
轻微 问题不明显,日常练习即可改善
中等 需要针对性练习,建议重点关注
严重 需要系统训练,建议额外辅导

StudentProblem.level 取值

说明
启蒙 初期入门阶段
入门 基础学习阶段
进阶 技能提升阶段
熟练 技术熟练阶段
精通 高级演奏阶段

Student.practice_time 取值

说明
15分钟 初级学员
30分钟 进阶学员
45分钟 中级学员
60分钟 中高级学员
90分钟 高级学员
120分钟 专业学员
150分钟以上 竞技水平