feat: 初始提交 v1.2.0 - 钢琴练习方案生成系统
This commit is contained in:
+556
@@ -0,0 +1,556 @@
|
||||
# API 接口文档
|
||||
|
||||
## 基础信息
|
||||
|
||||
- **Base URL**: `http://127.0.0.1:5000`
|
||||
- **Content-Type**: `application/json`
|
||||
|
||||
---
|
||||
|
||||
## 认证接口
|
||||
|
||||
### 检查登录状态
|
||||
|
||||
```
|
||||
GET /api/check-login
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"logged_in": true,
|
||||
"username": "admin",
|
||||
"role": "admin"
|
||||
}
|
||||
```
|
||||
|
||||
### 登录
|
||||
|
||||
```
|
||||
POST /api/login
|
||||
```
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"username": "admin",
|
||||
"password": "Admin@123"
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"message": "登录成功"
|
||||
}
|
||||
```
|
||||
|
||||
### 登出
|
||||
|
||||
```
|
||||
POST /api/logout
|
||||
```
|
||||
|
||||
### 初始设置(首次)
|
||||
|
||||
```
|
||||
GET /setup
|
||||
```
|
||||
|
||||
### 初始设置提交
|
||||
|
||||
```
|
||||
POST /api/setup
|
||||
```
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"username": "admin",
|
||||
"password": "Admin@123",
|
||||
"confirm_password": "Admin@123"
|
||||
}
|
||||
```
|
||||
|
||||
### 修改当前用户密码
|
||||
|
||||
```
|
||||
POST /api/users/change-password
|
||||
```
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"old_password": "Old@123",
|
||||
"new_password": "New@123",
|
||||
"confirm_password": "New@123"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 用户管理(仅管理员)
|
||||
|
||||
### 获取用户列表
|
||||
|
||||
```
|
||||
GET /api/users
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"username": "admin",
|
||||
"role": "admin",
|
||||
"created_at": "2026-04-17 10:00"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"username": "teacher1",
|
||||
"role": "user",
|
||||
"created_at": "2026-04-18 10:00"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 新增用户
|
||||
|
||||
```
|
||||
POST /api/users
|
||||
```
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"username": "newuser",
|
||||
"password": "User@123",
|
||||
"role": "user"
|
||||
}
|
||||
```
|
||||
|
||||
**角色选项**: `admin`, `user`
|
||||
|
||||
### 编辑用户
|
||||
|
||||
```
|
||||
PUT /api/users/<id>
|
||||
```
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"role": "user"
|
||||
}
|
||||
```
|
||||
|
||||
### 重置用户密码
|
||||
|
||||
```
|
||||
POST /api/users/<id>/reset-password
|
||||
```
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"new_password": "Reset@123"
|
||||
}
|
||||
```
|
||||
|
||||
### 删除用户
|
||||
|
||||
```
|
||||
DELETE /api/users/<id>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 班级管理
|
||||
|
||||
### 获取班级列表
|
||||
|
||||
```
|
||||
GET /api/classes
|
||||
```
|
||||
|
||||
**权限**: 登录用户
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "钢琴初级班",
|
||||
"description": "入门学员",
|
||||
"student_count": 5,
|
||||
"created_at": "2026-04-17 10:00"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 新增班级
|
||||
|
||||
```
|
||||
POST /api/classes
|
||||
```
|
||||
|
||||
**权限**: 管理员
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"name": "钢琴中级班",
|
||||
"description": "进阶学员"
|
||||
}
|
||||
```
|
||||
|
||||
### 编辑班级
|
||||
|
||||
```
|
||||
PUT /api/classes/<id>
|
||||
```
|
||||
|
||||
**权限**: 管理员
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"name": "钢琴中级班(2026)",
|
||||
"description": "进阶学员"
|
||||
}
|
||||
```
|
||||
|
||||
### 删除班级
|
||||
|
||||
```
|
||||
DELETE /api/classes/<id>
|
||||
```
|
||||
|
||||
**权限**: 管理员
|
||||
|
||||
### 获取班级学员列表
|
||||
|
||||
```
|
||||
GET /api/classes/<id>/students
|
||||
```
|
||||
|
||||
**权限**: 登录用户
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "张三",
|
||||
"phone": "13800138000",
|
||||
"practice_time": "30分钟"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 分配学员到班级
|
||||
|
||||
```
|
||||
POST /api/classes/<id>/assign
|
||||
```
|
||||
|
||||
**权限**: 登录用户
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"student_ids": [1, 2, 3]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 学员管理
|
||||
|
||||
### 获取学员列表
|
||||
|
||||
```
|
||||
GET /api/students
|
||||
```
|
||||
|
||||
**权限**: 登录用户
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "张三",
|
||||
"phone": "13800138000",
|
||||
"wechat_nickname": "小张",
|
||||
"practice_time": "30分钟",
|
||||
"class_id": 1,
|
||||
"class_name": "钢琴初级班",
|
||||
"notes": "",
|
||||
"problem_count": 2,
|
||||
"plan_count": 1,
|
||||
"created_at": "2026-04-17 10:00"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 创建学员
|
||||
|
||||
```
|
||||
POST /api/students
|
||||
```
|
||||
|
||||
**权限**: 登录用户
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"name": "学员姓名",
|
||||
"phone": "手机号",
|
||||
"wechat_nickname": "微信昵称",
|
||||
"practice_time": "30分钟",
|
||||
"class_id": 1,
|
||||
"notes": "备注信息"
|
||||
}
|
||||
```
|
||||
|
||||
### 获取学员详情
|
||||
|
||||
```
|
||||
GET /api/students/<id>
|
||||
```
|
||||
|
||||
### 更新学员
|
||||
|
||||
```
|
||||
PUT /api/students/<id>
|
||||
```
|
||||
|
||||
**权限**: 登录用户
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"name": "新姓名",
|
||||
"phone": "新手机号",
|
||||
"wechat_nickname": "新昵称",
|
||||
"practice_time": "45分钟",
|
||||
"class_id": 2,
|
||||
"notes": "新备注"
|
||||
}
|
||||
```
|
||||
|
||||
### 删除学员
|
||||
|
||||
```
|
||||
DELETE /api/students/<id>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 问题记录
|
||||
|
||||
### 获取学员的问题列表
|
||||
|
||||
```
|
||||
GET /api/students/<student_id>/problems
|
||||
```
|
||||
|
||||
### 添加问题
|
||||
|
||||
```
|
||||
POST /api/students/<student_id>/problems
|
||||
```
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"problem_id": "01_手小",
|
||||
"problem_name": "手小",
|
||||
"severity": "中等",
|
||||
"level": "入门"
|
||||
}
|
||||
```
|
||||
|
||||
**严重程度选项**: `轻微`, `中等`, `严重`
|
||||
|
||||
**级别选项**: `启蒙`, `入门`, `进阶`, `熟练`, `精通`
|
||||
|
||||
### 删除问题
|
||||
|
||||
```
|
||||
DELETE /api/students/<student_id>/problems/<problem_id>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 方案生成
|
||||
|
||||
### 生成练习方案
|
||||
|
||||
```
|
||||
POST /api/generate-plan
|
||||
```
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"student_id": 1,
|
||||
"use_ai": true
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明**:
|
||||
- `student_id`: 学员ID
|
||||
- `use_ai`: 是否使用AI生成个性化报告(默认true)
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"plan_id": 1,
|
||||
"ai_report": "## 个性化练习方案报告\n\n..."
|
||||
}
|
||||
```
|
||||
|
||||
### 获取方案详情
|
||||
|
||||
```
|
||||
GET /api/plans/<plan_id>
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"student_id": 1,
|
||||
"student_name": "张三",
|
||||
"created_at": "2026-04-17 10:30",
|
||||
"content": {
|
||||
"student_name": "张三",
|
||||
"practice_time": "30分钟",
|
||||
"total_daily_minutes": 30,
|
||||
"problems": [...],
|
||||
"daily_schedule": [...],
|
||||
"ai_report": "..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 获取学员的方案列表
|
||||
|
||||
```
|
||||
GET /api/students/<student_id>/plans
|
||||
```
|
||||
|
||||
### 导出PDF
|
||||
|
||||
```
|
||||
GET /api/plans/<plan_id>/pdf
|
||||
```
|
||||
|
||||
**返回**: PDF文件下载
|
||||
|
||||
### 微信卡片展示
|
||||
|
||||
```
|
||||
GET /plans/<plan_id>/wechat
|
||||
```
|
||||
|
||||
**返回**: HTML页面,用于微信分享
|
||||
|
||||
### 删除方案
|
||||
|
||||
```
|
||||
DELETE /api/plans/<plan_id>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 问题配置(仅管理员)
|
||||
|
||||
### 获取问题列表
|
||||
|
||||
```
|
||||
GET /api/problems
|
||||
```
|
||||
|
||||
### 获取问题详情
|
||||
|
||||
```
|
||||
GET /api/problems/<problem_id>
|
||||
```
|
||||
|
||||
### 创建问题
|
||||
|
||||
```
|
||||
POST /api/problems
|
||||
```
|
||||
|
||||
### 更新问题
|
||||
|
||||
```
|
||||
PUT /api/problems/<problem_id>
|
||||
```
|
||||
|
||||
### 删除问题
|
||||
|
||||
```
|
||||
DELETE /api/problems/<problem_id>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API设置(仅管理员)
|
||||
|
||||
### 获取API配置
|
||||
|
||||
```
|
||||
GET /api/config
|
||||
```
|
||||
|
||||
### 更新API配置
|
||||
|
||||
```
|
||||
POST /api/config
|
||||
```
|
||||
|
||||
### 测试API连接
|
||||
|
||||
```
|
||||
POST /api/config/test
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 权限说明
|
||||
|
||||
| 接口 | 管理员 | 普通用户 |
|
||||
|------|--------|----------|
|
||||
| 用户管理 | ✅ | ❌ |
|
||||
| 班级增删改 | ✅ | ❌ |
|
||||
| 班级查询/分配学员 | ✅ | ✅ |
|
||||
| 学员管理 | ✅ | ✅ |
|
||||
| 问题记录 | ✅ | ✅ |
|
||||
| 方案生成 | ✅ | ✅ |
|
||||
| 系统设置 | ✅ | ❌ |
|
||||
| 修改自己密码 | ✅ | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 错误响应
|
||||
|
||||
所有接口的错误响应格式:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "错误信息描述"
|
||||
}
|
||||
```
|
||||
|
||||
状态码:
|
||||
- `400` - 请求参数错误
|
||||
- `401` - 未登录或认证失败
|
||||
- `403` - 权限不足
|
||||
- `404` - 资源不存在
|
||||
- `500` - 服务器内部错误
|
||||
@@ -0,0 +1,250 @@
|
||||
# 部署指南
|
||||
|
||||
## 目标服务器
|
||||
|
||||
- **服务器**: 阿里云 ECS (CentOS 7)
|
||||
- **IP**: 47.106.65.108
|
||||
- **连接**: `ssh -i ~/.ssh/id_rsa root@47.106.65.108`
|
||||
|
||||
---
|
||||
|
||||
## 部署架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────┐
|
||||
│ 阿里云 ECS (CentOS 7) │
|
||||
│ │
|
||||
│ ┌─────────────────────┐ │
|
||||
│ │ Docker Container │ │
|
||||
│ │ piano-plan:5000 │ │
|
||||
│ └─────────────────────┘ │
|
||||
│ ▲ │ │
|
||||
│ │ │ │
|
||||
│ ┌──────┴───┴──────────┐ │
|
||||
│ │ 数据卷 (宿主机目录) │ │
|
||||
│ │ /data │ │
|
||||
│ │ /output │ │
|
||||
│ │ /config │ │
|
||||
│ └──────────────────────┘ │
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 数据分离(关键)
|
||||
|
||||
| 目录 | 容器内 | 宿主机 | 说明 |
|
||||
|------|--------|--------|------|
|
||||
| 数据库 | /app/data | ./data | SQLite数据库 |
|
||||
| PDF输出 | /app/output | ./output | 导出的PDF |
|
||||
| API配置 | /app/config | ./config | LLM配置 |
|
||||
|
||||
**优势**:
|
||||
- 容器删除重建,数据不丢失
|
||||
- 备份只需备份宿主机目录
|
||||
|
||||
---
|
||||
|
||||
## 部署步骤
|
||||
|
||||
### 1. 上传代码
|
||||
|
||||
```bash
|
||||
# 在本地打包
|
||||
cd projects/青年钢琴集体课/练习方案系统
|
||||
tar -czvf piano-plan.tar.gz \
|
||||
--exclude=venv \
|
||||
--exclude=__pycache__ \
|
||||
--exclude=.git \
|
||||
-f - .
|
||||
|
||||
# 上传到服务器
|
||||
scp -i ~/.ssh/id_rsa piano-plan.tar.gz root@47.106.65.108:/opt/
|
||||
|
||||
# SSH登录服务器
|
||||
ssh -i ~/.ssh/id_rsa root@47.106.65.108
|
||||
```
|
||||
|
||||
### 2. 服务器准备
|
||||
|
||||
```bash
|
||||
# 安装 Docker(如果未安装)
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
systemctl enable docker
|
||||
systemctl start docker
|
||||
|
||||
# 安装 docker-compose(CentOS 7)
|
||||
curl -L "https://github.com/docker/compose/releases/download/v2.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||
chmod +x /usr/local/bin/docker-compose
|
||||
|
||||
cd /opt
|
||||
mkdir -p piano-plan && cd piano-plan
|
||||
tar -xzf ../piano-plan.tar.gz --strip-components=1
|
||||
|
||||
# 创建数据目录
|
||||
mkdir -p data output config
|
||||
|
||||
# 开放端口
|
||||
firewall-cmd --permanent --add-port=5000/tcp
|
||||
firewall-cmd --reload
|
||||
```
|
||||
|
||||
### 3. 启动容器
|
||||
|
||||
```bash
|
||||
# 方式一:使用 docker-compose(推荐)
|
||||
docker-compose up -d --build
|
||||
|
||||
# 方式二:手动 docker run
|
||||
docker build -t piano-plan .
|
||||
docker run -d \
|
||||
--name piano-plan \
|
||||
-p 5000:5000 \
|
||||
-v $(pwd)/data:/app/data \
|
||||
-v $(pwd)/output:/app/output \
|
||||
-v $(pwd)/config:/app/config \
|
||||
-e FLASK_ENV=production \
|
||||
piano-plan
|
||||
```
|
||||
|
||||
### 4. 验证部署
|
||||
|
||||
```bash
|
||||
# 检查容器状态
|
||||
docker ps | grep piano-plan
|
||||
|
||||
# 检查日志
|
||||
docker logs piano-plan
|
||||
|
||||
# 访问测试
|
||||
curl http://localhost:5000/
|
||||
```
|
||||
|
||||
### 5. 配置防火墙
|
||||
|
||||
```bash
|
||||
# 开放5000端口(如果需要)
|
||||
firewall-cmd --permanent --add-port=5000/tcp
|
||||
firewall-cmd --reload
|
||||
```
|
||||
|
||||
### 6. 配置 Nginx 反向代理(可选)
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name piano.yourdomain.com; # 替换为实际域名
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 备份与恢复
|
||||
|
||||
### 备份
|
||||
|
||||
```bash
|
||||
# 停止服务
|
||||
docker-compose stop
|
||||
|
||||
# 打包数据
|
||||
tar -czvf piano-plan-backup-$(date +%Y%m%d).tar.gz data/ output/ config/
|
||||
|
||||
# 重启服务
|
||||
docker-compose start
|
||||
```
|
||||
|
||||
### 恢复
|
||||
|
||||
```bash
|
||||
# 停止服务
|
||||
docker-compose stop
|
||||
|
||||
# 解压备份
|
||||
tar -xzf piano-plan-backup-20260418.tar.gz
|
||||
|
||||
# 重启服务
|
||||
docker-compose start
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 更新部署
|
||||
|
||||
```bash
|
||||
# 拉取新代码(方式一:git pull)
|
||||
git pull
|
||||
|
||||
# 或 上传新代码(方式二)
|
||||
# 重复"上传代码"步骤
|
||||
|
||||
# 重新构建并启动
|
||||
docker-compose up -d --build
|
||||
|
||||
# 数据不会丢失(挂载卷)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常用命令
|
||||
|
||||
```bash
|
||||
# 查看日志
|
||||
docker logs -f piano-plan
|
||||
|
||||
# 重启服务
|
||||
docker-compose restart
|
||||
|
||||
# 停止服务
|
||||
docker-compose stop
|
||||
|
||||
# 删除容器(数据不丢失)
|
||||
docker-compose down
|
||||
|
||||
# 完全删除(包括镜像)
|
||||
docker-compose down --rmi local
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 容器启动失败
|
||||
|
||||
```bash
|
||||
# 查看日志
|
||||
docker logs piano-plan
|
||||
|
||||
# 检查端口
|
||||
netstat -tlnp | grep 5000
|
||||
```
|
||||
|
||||
### 数据库问题
|
||||
|
||||
```bash
|
||||
# 进入容器检查
|
||||
docker exec -it piano-plan sh
|
||||
|
||||
# 检查数据目录
|
||||
ls -la /app/data/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本信息
|
||||
|
||||
| 日期 | 版本 | 说明 |
|
||||
|------|------|------|
|
||||
| 2026-04-18 | V1.2 | 初始部署版本,含用户/角色/班级管理 |
|
||||
|
||||
---
|
||||
|
||||
## 联系
|
||||
|
||||
部署完成后访问: `http://47.106.65.108:5000`
|
||||
@@ -0,0 +1,348 @@
|
||||
# 钢琴练习方案系统 - 部署 SOP
|
||||
|
||||
> 版本:v1.1
|
||||
> 日期:2026-04-21
|
||||
> 核心原则:**不删除,只备份后新增/替换**
|
||||
|
||||
---
|
||||
|
||||
## 一、部署原则(铁律)
|
||||
|
||||
| 操作 | 允许? | 说明 |
|
||||
|------|--------|------|
|
||||
| 删除容器 | ❌ 禁止 | 停止即可,容器配置是资产 |
|
||||
| 删除 volume | ❌ 禁止 | 数据资产,不可恢复 |
|
||||
| 删除 host 文件 | ❌ 禁止 | 先备份到 `/tmp/backup_YYYYMMDD/` |
|
||||
| 覆盖文件 | ⚠️ 先备份 | 任何覆盖操作前必须先备份 |
|
||||
| 停止容器 | ✅ 允许 | stop 是安全的 |
|
||||
| 启动新容器 | ✅ 允许 | 配合正确的挂载配置 |
|
||||
|
||||
### 脚本优先原则(铁律)
|
||||
|
||||
> **当脚本执行失败时:修复脚本,而非绕过脚本。**
|
||||
|
||||
| 错误行为 | 正确行为 |
|
||||
|---------|---------|
|
||||
| 脚本报错 → `docker rm` 手动清理 | 脚本报错 → 查看日志 → 修复脚本问题 → 重跑脚本 |
|
||||
| 挂载丢失 → 手动指定新挂载 | 挂载丢失 → 更新脚本的挂载配置 → 重跑脚本 |
|
||||
| 镜像加载失败 → `docker rmi` 清理 | 镜像加载失败 → 检查错误 → 重跑脚本 |
|
||||
| 容器启动失败 → `docker rm` 重来 | 容器启动失败 → 查看 `docker logs` → 修复配置 → 重跑脚本 |
|
||||
|
||||
**一旦开始用脚本部署,就要坚持用到底,中途放弃脚本去做手动操作,等于打开了破坏系统的潘多拉魔盒。**
|
||||
|
||||
---
|
||||
|
||||
## 二、部署前检查清单
|
||||
|
||||
```
|
||||
[ ] 确认本地代码已验证通过
|
||||
[ ] 确认无未提交的代码
|
||||
[ ] 确认获得用户的明确同意(用户说"部署吧"或"可以部署")
|
||||
[ ] 确认需要保留的挂载点列表(见下方)
|
||||
[ ] 确认 Docker Desktop 已启动(Windows)
|
||||
```
|
||||
|
||||
### 必须保留的挂载点
|
||||
|
||||
| 类型 | 源 | 容器内 | 说明 |
|
||||
|------|-----|--------|------|
|
||||
| Bind Mount | `/opt/piano-plan/个性化方案` | `/app/个性化方案` | 问题文件(15个md) |
|
||||
| Volume | `piano-plan-data` | `/app/data` | SQLite 数据库 |
|
||||
| Volume | `piano-plan-output` | `/app/output` | PDF 输出 |
|
||||
| Bind Mount | `/opt/piano-plan/config` | `/app/config` | API 配置 |
|
||||
|
||||
---
|
||||
|
||||
## 三、部署步骤
|
||||
|
||||
### 3.1 本地构建
|
||||
|
||||
```powershell
|
||||
# 1. 启动 Docker Desktop(Windows)
|
||||
Start-Process "C:\Program Files\Docker\Docker\Docker Desktop.exe"
|
||||
|
||||
# 2. 等待 Docker 就绪
|
||||
docker version # 看到 Server: Docker Desktop 即为就绪
|
||||
|
||||
# 3. 进入项目目录
|
||||
cd "D:\F\NewI\opencode\daily-workspace\projects\青年钢琴集体课\练习方案系统"
|
||||
|
||||
# 4. 构建 Docker 镜像
|
||||
docker build -t piano-plan:latest .
|
||||
|
||||
# 5. 保存镜像为 tar 文件
|
||||
docker save piano-plan:latest -o piano-plan.tar
|
||||
```
|
||||
|
||||
### 3.2 上传到服务器
|
||||
|
||||
```powershell
|
||||
# 6. 上传到服务器临时目录
|
||||
scp -i ~/.ssh/id_rsa piano-plan.tar root@47.106.65.108:/opt/piano-plan/
|
||||
```
|
||||
|
||||
### 3.3 服务器部署(使用脚本!)
|
||||
|
||||
```bash
|
||||
# 7. SSH 到服务器
|
||||
ssh -i ~/.ssh/id_rsa root@47.106.65.108
|
||||
|
||||
# 8. 使用自动化部署脚本(会自动完成所有步骤并验证)
|
||||
bash /path/to/deploy.sh /opt/piano-plan/piano-plan.tar
|
||||
|
||||
# 或者手动部署(不推荐):
|
||||
# 8a. 确认当前容器挂载配置
|
||||
docker inspect piano-plan --format '{{json .Mounts}}'
|
||||
|
||||
# 9. 创建备份
|
||||
mkdir -p /opt/piano-plan/backups
|
||||
docker cp piano-plan:/app/data/piano_plans.db /opt/piano-plan/backups/piano_plans.db.bak
|
||||
|
||||
# 10. 停止旧容器
|
||||
docker stop piano-plan
|
||||
docker rm piano-plan
|
||||
|
||||
# 11. 加载新镜像
|
||||
docker load -i /opt/piano-plan/piano-plan.tar
|
||||
|
||||
# 12. 启动新容器(挂载配置必须完全正确!)
|
||||
docker run -d \
|
||||
--name piano-plan \
|
||||
-p 5001:5001 \
|
||||
--restart unless-stopped \
|
||||
-e FLASK_ENV=production \
|
||||
-v /opt/piano-plan/个性化方案:/app/个性化方案 \
|
||||
-v piano-plan-data:/app/data \
|
||||
-v piano-plan-output:/app/output \
|
||||
-v /opt/piano-plan/config:/app/config \
|
||||
piano-plan:latest
|
||||
```
|
||||
|
||||
### 3.4 验证
|
||||
|
||||
```bash
|
||||
# 13. 检查容器状态
|
||||
docker ps --filter name=piano-plan
|
||||
|
||||
# 14. 检查日志
|
||||
docker logs piano-plan --tail 20
|
||||
|
||||
# 15. 验证服务
|
||||
curl -I http://localhost:5001/
|
||||
|
||||
# 16. 验证问题文件(应该看到15个md文件)
|
||||
docker exec piano-plan ls /app/个性化方案/
|
||||
|
||||
# 17. 验证数据库(应该看到 templates 表)
|
||||
docker exec piano-plan python -c "import sqlite3; conn=sqlite3.connect('/app/data/piano_plans.db'); print([r[0] for r in conn.execute('SELECT name FROM sqlite_master WHERE type=\"table\"')])"
|
||||
|
||||
# 18. 验证 API 配置
|
||||
docker exec piano-plan cat /app/config/api_config.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、数据保护规范
|
||||
|
||||
### 4.1 必须保护的数据(绝对不删除)
|
||||
|
||||
| 数据类型 | 存储位置 | 说明 |
|
||||
|----------|----------|------|
|
||||
| 用户数据 | piano-plan-data:/app/data | users 表 |
|
||||
| 学员数据 | piano-plan-data:/app/data | students, student_problems 表 |
|
||||
| 班级数据 | piano-plan-data:/app/data | classes 表 |
|
||||
| 练习方案 | piano-plan-data:/app/data | practice_plans 表 |
|
||||
| 问题文件 | /opt/piano-plan/个性化方案 | 15个md文件 |
|
||||
|
||||
### 4.2 新增/更新的数据
|
||||
|
||||
| 数据类型 | 说明 |
|
||||
|----------|------|
|
||||
| templates 表 | AI提示词模板、报告导出模板 |
|
||||
| api_config.json | API 配置(provider, model, api_key) |
|
||||
|
||||
### 4.3 备份操作
|
||||
|
||||
```bash
|
||||
# 备份数据库(volume)
|
||||
docker cp piano-plan:/app/data/piano_plans.db /opt/piano-plan/backups/piano_plans.db.$(date +%Y%m%d)
|
||||
|
||||
# 备份 API 配置
|
||||
cp /opt/piano-plan/config/api_config.json /opt/piano-plan/backups/
|
||||
|
||||
# 列出所有备份
|
||||
ls -la /opt/piano-plan/backups/
|
||||
```
|
||||
|
||||
### 4.4 更新模板数据(部署后必做)
|
||||
|
||||
当代码中的模板内容更新时,需要手动更新生产数据库:
|
||||
|
||||
```python
|
||||
# update_templates.py - 在服务器容器内执行
|
||||
import sqlite3
|
||||
|
||||
NEW_AI_PROMPT = '''你是一位资深的钢琴教师。请根据学员的具体问题详情,生成一份个性化练习方案报告。
|
||||
|
||||
## 学员基本信息
|
||||
- **姓名**: {student_name}
|
||||
- **微信昵称**: {wechat_nickname}
|
||||
- **每日可练习时间**: {practice_time}
|
||||
|
||||
## 学员被诊断的问题
|
||||
{student_problems}
|
||||
|
||||
## 每个问题的详细信息和练习方法(请务必基于这些内容生成方案)
|
||||
|
||||
{problems}
|
||||
|
||||
## 任务要求
|
||||
请根据上述学员的问题诊断和详细信息,生成一份针对性的练习方案报告:
|
||||
1. 先简述该学员当前存在的主要问题
|
||||
2. 给出一个每日练习安排建议
|
||||
3. 针对每个问题给出具体的日常练习方法
|
||||
4. 给出3-5条重点注意事项
|
||||
|
||||
请使用Markdown格式,语言专业、简洁、有鼓励性。'''
|
||||
|
||||
conn = sqlite3.connect('/app/data/piano_plans.db')
|
||||
conn.execute("UPDATE templates SET content = ? WHERE type = 'ai_prompt'", (NEW_AI_PROMPT,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
```
|
||||
|
||||
上传脚本到服务器执行:
|
||||
```bash
|
||||
scp update_templates.py root@47.106.65.108:/tmp/
|
||||
ssh -i ~/.ssh/id_rsa root@47.106.65.108 "docker cp /tmp/update_templates.py piano-plan:/tmp/ && docker exec piano-plan python /tmp/update_templates.py"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、回滚流程
|
||||
|
||||
### 5.1 快速回滚(推荐)
|
||||
|
||||
```bash
|
||||
# 停止当前容器
|
||||
docker stop piano-plan
|
||||
docker rm piano-plan
|
||||
|
||||
# 使用旧镜像重新启动(如果镜像还在)
|
||||
docker run -d \
|
||||
--name piano-plan \
|
||||
-p 5001:5001 \
|
||||
--restart unless-stopped \
|
||||
-e FLASK_ENV=production \
|
||||
-v /opt/piano-plan/个性化方案:/app/个性化方案 \
|
||||
-v piano-plan-data:/app/data \
|
||||
-v piano-plan-output:/app/output \
|
||||
-v /opt/piano-plan/config:/app/config \
|
||||
piano-plan:latest
|
||||
```
|
||||
|
||||
### 5.2 从备份恢复
|
||||
|
||||
```bash
|
||||
# 恢复数据库
|
||||
docker stop piano-plan
|
||||
cp /opt/piano-plan/backups/piano_plans.db.bak /var/lib/docker/volumes/piano-plan-data/_data/piano_plans.db
|
||||
docker start piano-plan
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、服务信息
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|-----|
|
||||
| 生产地址 | https://piano.yoin.fun |
|
||||
| SSH | `ssh -i ~/.ssh/id_rsa root@47.106.65.108` |
|
||||
| 容器名 | piano-plan |
|
||||
| 端口 | 5001 |
|
||||
| 数据库位置 | piano-plan-data volume |
|
||||
| 问题文件位置 | /opt/piano-plan/个性化方案 |
|
||||
| API配置位置 | /opt/piano-plan/config |
|
||||
|
||||
### Volume 列表
|
||||
|
||||
| Volume | 容器内路径 | 说明 |
|
||||
|--------|------------|------|
|
||||
| piano-plan-data | /app/data | SQLite 数据库 |
|
||||
| piano-plan-output | /app/output | PDF 导出 |
|
||||
|
||||
### Bind Mount 列表
|
||||
|
||||
| 源 | 容器内路径 | 说明 |
|
||||
|-----|------------|------|
|
||||
| /opt/piano-plan/个性化方案 | /app/个性化方案 | 问题文件 |
|
||||
| /opt/piano-plan/config | /app/config | API 配置 |
|
||||
|
||||
---
|
||||
|
||||
## 七、API 配置说明
|
||||
|
||||
### 7.1 支持的 Provider
|
||||
|
||||
| Provider | Endpoint | 模型 |
|
||||
|----------|----------|------|
|
||||
| minimax | https://api.minimaxi.com/anthropic/v1 | MiniMax-M2.7-highspeed |
|
||||
| volcengine | https://ark.cn-beijing.volces.com/api/coding/v3 | doubao-seed-2.0-pro |
|
||||
| deepseek | https://api.deepseek.com | deepseek-chat |
|
||||
|
||||
### 7.2 API 配置存储
|
||||
|
||||
- 配置存储在 `/opt/piano-plan/config/api_config.json`
|
||||
- 每个 provider 的 key 存储在 `api_keys` 映射中
|
||||
- 切换 provider 时自动使用对应的 key
|
||||
|
||||
---
|
||||
|
||||
## 八、常见问题
|
||||
|
||||
### Q: 部署后问题文件看不到?
|
||||
A: 检查挂载 `/opt/piano-plan/个性化方案:/app/个性化方案` 是否正确
|
||||
|
||||
### Q: 数据库是空的?
|
||||
A: 检查 volume `piano-plan-data` 是否被错误覆盖,尝试从备份恢复
|
||||
|
||||
### Q: 容器无法启动?
|
||||
A: 检查日志 `docker logs piano-plan`,常见原因:端口被占用、volume 权限问题
|
||||
|
||||
### Q: API 配置没生效?
|
||||
A: 检查 `/opt/piano-plan/config/api_config.json` 是否存在且正确
|
||||
|
||||
### Q: SSE 不完整,提示词显示不出来或卡在95%?
|
||||
A: nginx 需要为 SSE (Server-Sent Events) 配置特定的代理设置:
|
||||
```nginx
|
||||
location /api/generate-plan {
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
tcp_nodelay on;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection '';
|
||||
proxy_buffers 8 32k;
|
||||
proxy_buffer_size 32k;
|
||||
proxy_max_temp_file_size 1024m;
|
||||
}
|
||||
```
|
||||
配置文件位置:`/srv/nginx/conf/conf.d/piano.yoin.fun.conf`
|
||||
|
||||
---
|
||||
|
||||
## 九、检查清单(部署完成后必填)
|
||||
|
||||
```
|
||||
[ ] 容器状态:running
|
||||
[ ] 服务响应:HTTP 200/302
|
||||
[ ] 问题文件数量:15个 md 文件
|
||||
[ ] 数据库记录:users, students, classes, student_problems, practice_plans 完整
|
||||
[ ] templates 表存在且包含 AI提示词模板、报告导出模板
|
||||
[ ] API 配置:provider, model, api_key 正确
|
||||
[ ] 功能验证:能生成练习方案
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **最后更新**:2026-04-21
|
||||
> **更新原因**:更新部署流程,添加数据保护规范,明确挂载点配置;添加 SSE 问题排查
|
||||
@@ -0,0 +1,146 @@
|
||||
# 钢琴练习方案系统 - 开发规范
|
||||
|
||||
## 版本命名规范
|
||||
|
||||
### 语义化版本 (SemVer)
|
||||
|
||||
采用 `MAJOR.MINOR.PATCH` 格式:
|
||||
|
||||
- **MAJOR**: 不兼容的API变更
|
||||
- **MINOR**: 向后兼容的功能新增
|
||||
- **PATCH**: 向后兼容的问题修复
|
||||
|
||||
**示例**:
|
||||
```
|
||||
v1.0.0 - 初始版本
|
||||
v1.1.0 - 新增功能(学员班级管理)
|
||||
v1.1.1 - 修复bug
|
||||
v2.0.0 - 重大重构(不兼容)
|
||||
```
|
||||
|
||||
### 禁止的命名方式
|
||||
|
||||
❌ 禁止使用以下命名:
|
||||
- `final`, `last_final`, `final_v2`
|
||||
- `new`, `new2`, `new_final`
|
||||
- `backup`, `backup2`
|
||||
- `test`, `test2`
|
||||
|
||||
✅ 正确示例:
|
||||
- `v1.2.0.tar` → `piano-plan-v1.2.0.tar`
|
||||
- `docker镜像` → `piano-plan:v1.2.0`
|
||||
|
||||
---
|
||||
|
||||
## 文件组织规范
|
||||
|
||||
### 目录结构
|
||||
|
||||
```
|
||||
piano-plan/
|
||||
├── app/ # Flask应用核心代码
|
||||
├── config/ # 配置文件
|
||||
├── data/ # 数据库(不提交git)
|
||||
├── docs/ # 项目文档
|
||||
├── releases/ # 部署包(版本化)
|
||||
├── scripts/ # 临时调试脚本(开发用)
|
||||
├── output/ # PDF输出(自动生成)
|
||||
├── app/templates/ # 前端模板
|
||||
├── app/routes/ # 路由
|
||||
├── app/services/ # 业务逻辑
|
||||
│
|
||||
├── run.py # 应用入口
|
||||
├── run.bat # Windows启动
|
||||
├── deploy.sh # 部署脚本
|
||||
├── docker-compose.yml
|
||||
├── Dockerfile
|
||||
├── requirements.txt
|
||||
└── README.md
|
||||
```
|
||||
|
||||
### 各类文件处理
|
||||
|
||||
| 类型 | 位置 | Git | 说明 |
|
||||
|------|------|------|------|
|
||||
| 核心代码 | `app/` | ✅ | 必须 |
|
||||
| 配置文件 | `config/` | ✅ | API配置等 |
|
||||
| 数据库 | `data/` | ❌ | 不提交 |
|
||||
| 文档 | `docs/` | ✅ | 项目文档 |
|
||||
| 部署包 | `releases/` | ❌ | 版本化后删除本地副本 |
|
||||
| 临时脚本 | `scripts/` | ❌ | 开发调试用,完成后删除 |
|
||||
| 输出文件 | `output/` | ❌ | 自动生成 |
|
||||
| 依赖 | `venv/` | ❌ | 不提交 |
|
||||
| tar包 | `releases/` | ❌ | 版本化命名 |
|
||||
|
||||
---
|
||||
|
||||
## 部署包规范
|
||||
|
||||
### 发布流程
|
||||
|
||||
1. **开发完成** → 本地测试通过
|
||||
2. **构建镜像** → `docker build -t piano-plan:v1.2.0 .`
|
||||
3. **打包部署文件** → 创建 `releases/v1.2.0/` 目录,放入:
|
||||
- `piano-plan-v1.2.0.tar.gz` - Docker镜像
|
||||
- `piano-nginx.conf` - Nginx配置(从服务器获取最新)
|
||||
- `docker-compose.yml` - 部署编排
|
||||
4. **上传** → 传到服务器 load 镜像
|
||||
5. **部署** → docker-compose up -d
|
||||
6. **清理** → 本地 tar 包可删除(git已管理版本)
|
||||
|
||||
### 版本化部署包命名
|
||||
|
||||
```
|
||||
piano-plan-v1.2.0.tar.gz
|
||||
```
|
||||
|
||||
格式:`{项目名}-v{版本}.tar.gz`
|
||||
|
||||
---
|
||||
|
||||
## 临时脚本规范
|
||||
|
||||
### scripts/ 目录
|
||||
|
||||
用于存放开发过程中的临时调试脚本:
|
||||
|
||||
- `check_*.py` - 数据库/配置检查
|
||||
- `test_*.py` - API/功能测试
|
||||
- `debug_*.py` - 调试用
|
||||
|
||||
**规则**:
|
||||
1. 开发时放在 `scripts/`
|
||||
2. 功能稳定后 **删除**
|
||||
3. 不要提交到 git
|
||||
|
||||
---
|
||||
|
||||
## Git 提交规范
|
||||
|
||||
### Commit Message 格式
|
||||
|
||||
```
|
||||
<type>: <subject>
|
||||
|
||||
<body>
|
||||
```
|
||||
|
||||
**Type**:
|
||||
- `feat`: 新功能
|
||||
- `fix`: 修复bug
|
||||
- `docs`: 文档
|
||||
- `refactor`: 重构
|
||||
- `deploy`: 部署
|
||||
|
||||
**示例**:
|
||||
```
|
||||
feat: 添加学员管理功能
|
||||
|
||||
fix: 修复移动端侧边栏遮挡问题
|
||||
|
||||
deploy: v1.2.0 生产环境部署
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*最后更新:2026-04-21*
|
||||
@@ -0,0 +1,239 @@
|
||||
# 前端架构规范
|
||||
|
||||
> 本文档定义钢琴练习方案系统的模板架构设计,作为前端开发的遵循规范。
|
||||
|
||||
## 1. 模板架构
|
||||
|
||||
### 1.1 继承模式
|
||||
|
||||
所有业务页面必须继承自 `base.html`:
|
||||
|
||||
```
|
||||
base.html (基础模板)
|
||||
├── index.html (学员管理)
|
||||
├── settings.html (问题配置)
|
||||
├── classes.html (班级管理)
|
||||
├── users.html (用户管理)
|
||||
├── templates.html (模板管理)
|
||||
└── api_settings.html (API设置)
|
||||
```
|
||||
|
||||
### 1.2 base.html 结构
|
||||
|
||||
`base.html` 统一管理以下公共部分:
|
||||
|
||||
| 部分 | 说明 |
|
||||
|------|------|
|
||||
| CDN 引入 | Bootstrap 5.3, Bootstrap Icons, EasyMDE, Tabulator |
|
||||
| 基础 CSS | body, sidebar, main-content, card 样式 |
|
||||
| 移动端响应式 | 汉堡菜单、侧边栏展开/收起 |
|
||||
| 顶部导航栏 | 移动端显示(固定在顶部) |
|
||||
| 侧边栏结构 | 桌面端显示 |
|
||||
| 公共 JS | toggleMobileNav, logout, showChangePasswordModal |
|
||||
| 修改密码弹窗 | 统一放在 base 中 |
|
||||
|
||||
### 1.3 Jinja2 Blocks
|
||||
|
||||
| Block 名称 | 位置 | 用途 |
|
||||
|------------|------|------|
|
||||
| `title` | `<head><title>` | 页面标题 |
|
||||
| `extra_css` | `<head>` 末尾 | 额外 CSS(如 CDN 样式链接) |
|
||||
| `page_css` | `<style>` 末尾 | 页面特定 CSS |
|
||||
| `sidebar_nav` | 侧边栏 `<nav>` 内 | 导航链接 |
|
||||
| `content` | 主内容区 | 页面主要内容 HTML |
|
||||
| `extra_js` | `</body>` 前 | 额外 JS(如 CDN 脚本、页面业务逻辑) |
|
||||
|
||||
## 2. 新页面创建规范
|
||||
|
||||
### 2.1 最小模板示例
|
||||
|
||||
```html
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}页面标题 - 钢琴练习方案系统{% endblock %}
|
||||
|
||||
{% block page_css %}
|
||||
<style>
|
||||
/* 仅放置页面特有的 CSS */
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block sidebar_nav %}
|
||||
<!-- 复制 base.html 的导航结构,根据当前页面的 active 状态调整 -->
|
||||
<a class="nav-link" href="/">
|
||||
<i class="bi bi-people"></i> 学员管理
|
||||
</a>
|
||||
<!-- ... 其他链接 ... -->
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- 页面主要内容 HTML -->
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/xxx/xxx.min.js"></script>
|
||||
<script>
|
||||
// 页面业务逻辑 JS
|
||||
</script>
|
||||
{% endblock %}
|
||||
```
|
||||
|
||||
### 2.2 侧边栏导航 active 状态
|
||||
|
||||
当前页面对应的链接需要添加 `active` class:
|
||||
|
||||
```html
|
||||
<a class="nav-link active" href="/settings">
|
||||
<i class="bi bi-gear"></i> 问题配置
|
||||
</a>
|
||||
```
|
||||
|
||||
### 2.3 用户信息显示
|
||||
|
||||
base.html 在移动端和桌面端都有用户信息占位符,页面 JS 需要在 DOMContentLoaded 中设置:
|
||||
|
||||
```javascript
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
fetch('/api/check-login').then(r => r.json()).then(data => {
|
||||
const userDisplay = data.username + ' (' + (data.role === 'admin' ? '管理员' : '普通用户') + ')';
|
||||
document.getElementById('currentUserDisplay').textContent = userDisplay;
|
||||
const mobileDisplay = document.getElementById('mobileUserDisplay');
|
||||
if (mobileDisplay) mobileDisplay.textContent = userDisplay;
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## 3. 移动端响应式设计
|
||||
|
||||
### 3.1 断点
|
||||
|
||||
| 断点 | 屏幕宽度 | 布局 |
|
||||
|------|----------|------|
|
||||
| 桌面端 | ≥ 768px | 侧边栏固定在左侧,主内容区在右侧 |
|
||||
| 移动端 | < 768px | 顶部导航栏 + 隐藏侧边栏(汉堡按钮展开) |
|
||||
|
||||
### 3.2 CSS 规范
|
||||
|
||||
所有移动端样式统一写在 `base.html` 的 `@media (max-width: 767.98px)` 块中,各页面不需要重复定义。
|
||||
|
||||
侧边栏移动端样式(base.html):
|
||||
|
||||
```css
|
||||
/* 移动端响应式 */
|
||||
@media (max-width: 767.98px) {
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 60px; /* 向下偏移,避免被顶部导航栏挡住 */
|
||||
left: 0;
|
||||
width: 100%;
|
||||
min-height: auto;
|
||||
max-height: calc(100vh - 60px); /* 最大高度为屏幕高度减去导航栏 */
|
||||
z-index: 1040;
|
||||
overflow-y: auto;
|
||||
transform: translateY(-100%); /* 默认隐藏 */
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
.sidebar.collapsed {
|
||||
transform: translateY(-100%); /* 隐藏状态 */
|
||||
}
|
||||
.sidebar:not(.collapsed) {
|
||||
transform: translateY(0); /* 显示状态 */
|
||||
}
|
||||
.main-content {
|
||||
margin-top: 60px;
|
||||
padding: 10px;
|
||||
}
|
||||
.mobile-nav-toggle {
|
||||
display: flex !important;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 移动端导航栏 HTML(base.html)
|
||||
|
||||
```html
|
||||
<nav class="mobile-nav-toggle navbar navbar-dark bg-dark d-flex d-md-none"
|
||||
style="position:fixed;top:0;left:0;right:0;z-index:1050;padding:10px;">
|
||||
<div class="container-fluid d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<span class="navbar-brand mb-0"><i class="bi bi-music-note-beamed"></i> 钢琴方案</span>
|
||||
<small id="mobileUserDisplay" class="d-block text-white-50" style="font-size:10px;"></small>
|
||||
</div>
|
||||
<button class="btn btn-outline-light btn-sm" onclick="toggleMobileNav()">
|
||||
<i class="bi bi-list"></i>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
```
|
||||
|
||||
**注意**:
|
||||
- `mobile-nav-toggle` 类控制显示/隐藏(桌面端隐藏,移动端显示)
|
||||
- `z-index: 1050` 确保在侧边栏之上(侧边栏是 1040)
|
||||
|
||||
## 4. JavaScript 规范
|
||||
|
||||
### 4.1 公共函数(base.html 提供)
|
||||
|
||||
| 函数 | 用途 |
|
||||
|------|------|
|
||||
| `toggleMobileNav()` | 切换侧边栏展开/收起 |
|
||||
| `logout()` | 退出登录 |
|
||||
| `showChangePasswordModal()` | 显示修改密码弹窗 |
|
||||
|
||||
### 4.2 页面 JS 放置
|
||||
|
||||
- 公共函数(如 `showToast`)放在 `extra_js` block 中
|
||||
- 页面初始化逻辑放在 `document.addEventListener('DOMContentLoaded', ...)` 中
|
||||
- 业务函数在 `extra_js` block 中定义
|
||||
|
||||
### 4.3 修改密码
|
||||
|
||||
base.html 已包含修改密码弹窗 HTML 和 JS。各页面不需要重复定义 `showChangePasswordModal()` 和相关 DOM 绑定。
|
||||
|
||||
## 5. 避免重复代码
|
||||
|
||||
### 5.1 禁止重复的内容
|
||||
|
||||
以下内容**禁止**在各页面模板中重复定义:
|
||||
|
||||
- `<head>` 标签内容(meta, title, CDN 链接)
|
||||
- 基础 CSS(body, sidebar, card 等)
|
||||
- 移动端响应式 CSS
|
||||
- 移动端顶部导航栏 HTML
|
||||
- 侧边栏 HTML 结构
|
||||
- Bootstrap JS CDN
|
||||
- `toggleMobileNav()`, `logout()`, `showChangePasswordModal()` 函数
|
||||
- 修改密码弹窗 HTML 和 JS
|
||||
|
||||
### 5.2 正确做法
|
||||
|
||||
| 如果需要... | 应该... |
|
||||
|-------------|---------|
|
||||
| 页面特定 CSS | 在 `{% block page_css %}` 中添加 |
|
||||
| 额外 JS 库 | 在 `{% block extra_js %}` 开头引入 |
|
||||
| 页面业务逻辑 | 在 `{% block extra_js %}` 中定义 |
|
||||
| 导航 active 状态 | 在 `{% block sidebar_nav %}` 中设置 |
|
||||
|
||||
## 6. 文件结构
|
||||
|
||||
```
|
||||
app/templates/
|
||||
├── base.html # 基础模板(核心)
|
||||
├── index.html # 学员管理
|
||||
├── settings.html # 问题配置
|
||||
├── classes.html # 班级管理
|
||||
├── users.html # 用户管理
|
||||
├── templates.html # 模板管理
|
||||
├── api_settings.html # API设置
|
||||
├── login.html # 登录页(独立,无侧边栏)
|
||||
├── setup.html # 初始化页(独立)
|
||||
└── wechat_card.html # 微信卡片(独立)
|
||||
```
|
||||
|
||||
> 注意:`login.html`、`setup.html`、`wechat_card.html` 是独立页面,不继承 base.html。
|
||||
|
||||
## 7. 更新日志
|
||||
|
||||
| 日期 | 版本 | 变更内容 |
|
||||
|------|------|----------|
|
||||
| 2026-04-21 | v1.0 | 初始文档,定义 base.html 模板继承模式 |
|
||||
+281
@@ -0,0 +1,281 @@
|
||||
# 数据模型说明
|
||||
|
||||
## 概述
|
||||
|
||||
本系统使用 SQLite 数据库,ORM 框架为 Flask-SQLAlchemy。
|
||||
|
||||
**数据库文件**: `data/piano_plans.db`
|
||||
|
||||
---
|
||||
|
||||
## 数据表
|
||||
|
||||
### 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 | String(50) | 问题编号,如 "01_手小" |
|
||||
| problem_name | String(100) | 问题名称,如 "手小" |
|
||||
| severity | String(10) | 严重程度:轻微/中等/严重 |
|
||||
| level | String(20) | 级别:启蒙/入门/进阶/熟练/精通 |
|
||||
| created_at | DateTime | 创建时间 |
|
||||
|
||||
---
|
||||
|
||||
### 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_name │
|
||||
│ 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 problem in student.problems:
|
||||
print(problem.problem_name, problem.severity, problem.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分钟以上 | 竞技水平 |
|
||||
@@ -0,0 +1,279 @@
|
||||
# 项目结构说明
|
||||
|
||||
## 目录概览
|
||||
|
||||
```
|
||||
练习方案系统/
|
||||
├── docs/ # 项目文档
|
||||
│ ├── API.md # API接口文档
|
||||
│ ├── MODELS.md # 数据模型说明
|
||||
│ └── STRUCTURE.md # 本文件 - 项目结构
|
||||
│
|
||||
├── app/ # Flask应用主目录
|
||||
│ ├── __init__.py # 应用工厂
|
||||
│ ├── config.py # 配置管理
|
||||
│ ├── models.py # 数据库模型
|
||||
│ │
|
||||
│ ├── routes/ # 路由模块
|
||||
│ │ ├── __init__.py # 蓝图注册
|
||||
│ │ ├── auth.py # 登录认证
|
||||
│ │ ├── students.py # 学员管理API
|
||||
│ │ ├── problems.py # 问题记录API
|
||||
│ │ ├── plans.py # 方案生成API
|
||||
│ │ ├── settings.py # 系统设置API
|
||||
│ │ └── classes.py # 班级管理API(新增)
|
||||
│ │
|
||||
│ ├── services/ # 业务逻辑
|
||||
│ │ ├── plan_generator.py # 方案生成器
|
||||
│ │ └── pdf_generator.py # PDF生成器
|
||||
│ │
|
||||
│ └── templates/ # 前端模板
|
||||
│ ├── base.html # 基础模板(所有页面继承)
|
||||
│ ├── index.html # 学员管理页面(继承base)
|
||||
│ ├── settings.html # 问题配置页面(继承base)
|
||||
│ ├── login.html # 登录页面(独立)
|
||||
│ ├── setup.html # 初始设置页面(独立)
|
||||
│ ├── users.html # 用户管理页面(继承base)
|
||||
│ ├── classes.html # 班级管理页面(继承base)
|
||||
│ ├── templates.html # 模板管理页面(继承base)
|
||||
│ ├── api_settings.html # API设置页面(继承base)
|
||||
│ └── wechat_card.html # 微信卡片模板(独立)
|
||||
│
|
||||
├── data/ # 数据目录(运行时创建)
|
||||
│ └── piano_plans.db # SQLite数据库
|
||||
│
|
||||
├── output/ # PDF输出目录(运行时创建)
|
||||
│
|
||||
├── config/ # 配置目录(运行时创建)
|
||||
│ └── api_config.json # API配置文件
|
||||
│
|
||||
├── 个性化方案/ # 练习方案内容
|
||||
│ └── 针对性练习(拆分为单独文件)/
|
||||
│ ├── 01_手小.md
|
||||
│ ├── 02_识谱慢.md
|
||||
│ └── ...
|
||||
│
|
||||
├── run.py # 应用入口
|
||||
├── run.bat # 启动脚本
|
||||
├── requirements.txt # Python依赖
|
||||
└── README.md # 项目说明
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 核心模块
|
||||
|
||||
### app/__init__.py
|
||||
|
||||
应用工厂,负责:
|
||||
- 创建Flask应用实例
|
||||
- 加载配置
|
||||
- 初始化数据库
|
||||
- 注册蓝图
|
||||
- 创建必要目录
|
||||
|
||||
```python
|
||||
def create_app():
|
||||
app = Flask(__name__)
|
||||
# 加载配置...
|
||||
db.init_app(app)
|
||||
app.register_blueprint(main_bp)
|
||||
# 创建目录...
|
||||
return app
|
||||
```
|
||||
|
||||
### app/config.py
|
||||
|
||||
配置文件,包含:
|
||||
- 数据库路径
|
||||
- 问题文件目录
|
||||
- PDF输出目录
|
||||
- API配置加载/保存函数
|
||||
- 权限配置
|
||||
|
||||
### app/models.py
|
||||
|
||||
数据库模型定义:
|
||||
- `User` - 用户(登录认证、权限管理)
|
||||
- `Student` - 学员
|
||||
- `StudentProblem` - 问题记录
|
||||
- `Class` - 班级(新增)
|
||||
- `PracticePlan` - 练习方案
|
||||
|
||||
---
|
||||
|
||||
## 路由详解
|
||||
|
||||
### routes/auth.py
|
||||
|
||||
| 路由 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `/login` | GET | 登录页面 |
|
||||
| `/api/login` | POST | 登录API |
|
||||
| `/api/logout` | POST | 登出API |
|
||||
| `/api/check-login` | GET | 检查登录状态 |
|
||||
| `/setup` | GET | 初始设置页面 |
|
||||
| `/api/setup` | POST | 初始设置API |
|
||||
|
||||
### routes/students.py
|
||||
|
||||
| 路由 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `/api/students` | GET | 获取学员列表 |
|
||||
| `/api/students` | POST | 创建学员 |
|
||||
| `/api/students/<id>` | GET | 获取学员详情 |
|
||||
| `/api/students/<id>` | PUT | 更新学员 |
|
||||
| `/api/students/<id>` | DELETE | 删除学员 |
|
||||
|
||||
### routes/problems.py
|
||||
|
||||
| 路由 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `/api/students/<id>/problems` | GET | 获取学员问题 |
|
||||
| `/api/students/<id>/problems` | POST | 添加问题 |
|
||||
| `/api/students/<id>/problems/<pid>` | DELETE | 删除问题 |
|
||||
|
||||
### routes/plans.py
|
||||
|
||||
| 路由 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `/api/generate-plan` | POST | 生成练习方案 |
|
||||
| `/api/plans/<id>` | GET | 获取方案详情 |
|
||||
| `/api/plans/<id>/pdf` | GET | 导出PDF |
|
||||
| `/plans/<id>/wechat` | GET | 微信卡片 |
|
||||
| `/api/plans/<id>` | DELETE | 删除方案 |
|
||||
|
||||
### routes/settings.py
|
||||
|
||||
| 路由 | 方法 | 说明 | 权限 |
|
||||
|------|------|------|------|
|
||||
| `/settings` | GET | 设置页面 | 管理员 |
|
||||
| `/api/problems` | GET | 获取问题列表 | 管理员 |
|
||||
| `/api/problems` | POST | 创建问题 | 管理员 |
|
||||
| `/api/problems/<id>` | GET | 问题详情 | 管理员 |
|
||||
| `/api/problems/<id>` | PUT | 更新问题 | 管理员 |
|
||||
| `/api/problems/<id>` | DELETE | 删除问题 | 管理员 |
|
||||
| `/api/config` | GET | 获取API配置 | 管理员 |
|
||||
| `/api/config` | POST | 更新API配置 | 管理员 |
|
||||
| `/api/config/test` | POST | 测试API连接 | 管理员 |
|
||||
|
||||
### routes/classes.py(新增)
|
||||
|
||||
| 路由 | 方法 | 说明 | 权限 |
|
||||
|------|------|------|------|
|
||||
| `/api/classes` | GET | 班级列表 | 登录用户 |
|
||||
| `/api/classes` | POST | 新增班级 | 管理员 |
|
||||
| `/api/classes/<id>` | PUT | 编辑班级 | 管理员 |
|
||||
| `/api/classes/<id>` | DELETE | 删除班级 | 管理员 |
|
||||
| `/api/classes/<id>/students` | GET | 班级学员 | 登录用户 |
|
||||
| `/api/classes/<id>/assign` | POST | 分配学员 | 登录用户 |
|
||||
|
||||
### routes/users.py(新增)
|
||||
|
||||
| 路由 | 方法 | 说明 | 权限 |
|
||||
|------|------|------|------|
|
||||
| `/users` | GET | 用户管理页面 | 管理员 |
|
||||
| `/api/users` | GET | 用户列表 | 管理员 |
|
||||
| `/api/users` | POST | 新增用户 | 管理员 |
|
||||
| `/api/users/<id>` | PUT | 编辑用户 | 管理员 |
|
||||
| `/api/users/<id>` | DELETE | 删除用户 | 管理员 |
|
||||
| `/api/users/<id>/reset-password` | POST | 重置密码 | 管理员 |
|
||||
| `/api/users/change-password` | POST | 修改自己密码 | 登录用户 |
|
||||
|
||||
---
|
||||
|
||||
## 服务层
|
||||
|
||||
### services/plan_generator.py
|
||||
|
||||
核心业务逻辑:
|
||||
|
||||
```python
|
||||
# 生成基础练习方案
|
||||
generate_practice_plan(student_name, problems, problems_dir)
|
||||
|
||||
# 生成AI报告(调用LLM)
|
||||
generate_ai_report(student_name, problems, practice_time, time_config)
|
||||
```
|
||||
|
||||
### services/pdf_generator.py
|
||||
|
||||
使用 reportlab 生成中文PDF:
|
||||
|
||||
```python
|
||||
generate_pdf(plan_id, student_name, content, output_dir)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 权限系统
|
||||
|
||||
### 角色
|
||||
|
||||
| 角色 | 说明 |
|
||||
|------|------|
|
||||
| admin | 管理员,拥有所有权限 |
|
||||
| user | 普通用户,受限权限 |
|
||||
|
||||
### 权限矩阵
|
||||
|
||||
| 模块 | 管理员 | 普通用户 |
|
||||
|------|--------|----------|
|
||||
| 用户管理 | 增删改查 | ❌ |
|
||||
| 班级管理 | 增删改 | 查询+分配 |
|
||||
| 学员管理 | 增删改查 | 增删改查 |
|
||||
| 问题记录 | 增删改查 | 增删改查 |
|
||||
| 方案生成 | ✅ | ✅ |
|
||||
| 系统设置 | ✅ | ❌ |
|
||||
| 修改密码 | ✅ | ✅ |
|
||||
|
||||
### 权限装饰器
|
||||
|
||||
```python
|
||||
# 后端权限装饰器
|
||||
@admin_required # 仅管理员
|
||||
@login_required # 需登录
|
||||
|
||||
# 前端根据 role 动态显示菜单和按钮
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 启动流程
|
||||
|
||||
1. 双击 `run.bat`
|
||||
2. 脚本自动:
|
||||
- 删除旧venv(如有)
|
||||
- 创建新venv
|
||||
- 安装依赖
|
||||
- 启动Flask服务
|
||||
3. 首次访问 `/setup` 创建管理员
|
||||
4. 登录后使用系统
|
||||
|
||||
---
|
||||
|
||||
## 扩展开发
|
||||
|
||||
### 新增功能步骤
|
||||
|
||||
1. **数据模型**:在 `app/models.py` 添加新模型
|
||||
2. **路由**:在 `app/routes/` 添加新路由文件
|
||||
3. **业务逻辑**:在 `app/services/` 添加服务
|
||||
4. **前端**:在 `app/templates/` 添加模板
|
||||
5. **文档**:更新 `docs/` 目录
|
||||
|
||||
### 添加新依赖
|
||||
|
||||
1. 修改 `requirements.txt`
|
||||
2. 运行 `pip install -r requirements.txt`
|
||||
3. 更新 `run.bat` 中的安装命令
|
||||
|
||||
---
|
||||
|
||||
## 版本历史
|
||||
|
||||
| 版本 | 日期 | 说明 |
|
||||
|------|------|------|
|
||||
| V1.0 | 2026-04-17 | 初始版本:学员管理、问题记录、方案生成 |
|
||||
| V1.1 | 2026-04-17 | 添加用户登录认证系统 |
|
||||
| V1.2 | 2026-04-18 | 添加用户管理、角色权限、班级管理 |
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user