18351212e8
- 问题从文件系统迁移到数据库 problems 表 - 移除 PROBLEMS_DIR 配置和文件读取逻辑 - student.html 完整重写:编辑/添加/删除问题,生成方案进度显示 - 学员详情页支持独立URL访问 (/student/<id>) - 统一侧边栏到 base.html - 更新文档:DEPLOYMENT_SOP, MODELS, STRUCTURE, FRONTEND_ARCH - 部署到生产环境 v1.2.0
297 lines
8.1 KiB
Markdown
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分钟以上 | 竞技水平 | |