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

297 lines
8.1 KiB
Markdown

# 数据模型说明
## 概述
本系统使用 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 字段结构**:
```json
{
"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 │
└──────────────────┘
```
---
## 常用查询
### 查询用户角色
```python
from app.models import User
user = User.query.filter_by(username="admin").first()
print(user.role) # "admin" or "user"
```
### 查询学员及其问题
```python
student = Student.query.get(1)
for sp in student.problems:
print(sp.problem.name, sp.problem.code, sp.severity, sp.level)
```
### 查询班级及其学员
```python
cls = Class.query.get(1)
for student in cls.students:
print(student.name)
```
### 查询学员及其方案
```python
student = Student.query.get(1)
for plan in student.plans:
print(plan.created_at)
```
### 获取最新方案
```python
from app.models import PracticePlan
latest_plan = PracticePlan.query.order_by(
PracticePlan.created_at.desc()
).first()
```
---
## 数据库管理
### 初始化数据库
首次运行应用时会自动创建所有表:
```python
from app import create_app
from app.models import db
app = create_app()
with app.app_context():
db.create_all()
```
### 备份数据库
```bash
# 停止服务后复制数据库文件
copy data\piano_plans.db backup\piano_plans_backup.db
```
### 查看数据库内容
```bash
# 使用 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分钟以上 | 竞技水平 |