Files
piano-plan/docs/DEPLOYMENT_SOP.md
T

465 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 钢琴练习方案系统 - 部署 SOP
> 版本:v1.5.2
> 日期:2026-04-28
> 核心原则:**不删除,只备份后新增/替换**
---
## 重要更新(v1.5.2
### ✨ 导出预览功能
在方案详情页新增「预览」按钮,点击弹出模态框展示套用模板导出后的最终效果(所见即所得)。
**功能特点:**
- 预览内容与 PDF 导出效果一致(字体、标题层级、表格样式)
- 支持水印预览(如已配置)
- 内容超出时可滚动查看
- 支持 `<para alignment="center">` 居中语法(预览和 PDF 导出均支持)
**模板居中写法:**
```html
<para alignment="center">【{generated_by} 撰写于 {generated_at}】</para>
```
**技术实现:**
- 后端:`GET /api/plans/<id>/preview` 返回渲染后的 HTML
- 前端:Bootstrap 模态框 + CSS 镜像 PDF 样式
- 新增依赖:`markdown` Python 包(Markdown → HTML 转换)
---
## 重要更新(v1.5.1
### ⚠️ 问题文件已迁移到数据库
**历史**`/app/个性化方案/*.md`15个问题文件)
**现状**:所有问题数据已迁移到 `problems` 表,不再需要挂载问题文件目录。
**影响**
- 部署时不再检查问题文件数量
- 不再需要 `/opt/piano-plan/个性化方案` 挂载
- 验证清单中"问题文件数量"检查已废弃
### ⚠️ Docker 构建需要代理
**本地代理端口**`15000`
构建命令:
```powershell
$env:HTTP_PROXY="http://127.0.0.1:15000"
$env:HTTPS_PROXY="http://127.0.0.1:15000"
docker build -t piano-plan:latest .
```
### ⚠️ PDF 水印配置
PDF 水印文本在 API 设置页面(`/api-settings`)中配置,存储在 `api_config.json``watermark_text` 字段。
- 留空 = 不显示水印
- 填写文字 = 每页中央斜向显示该文字(56pt,25%透明度)
### ⚠️ 中文字体
Docker 镜像已内置 `fonts-wqy-microhei`,无需额外挂载。
---
## 一、部署原则(铁律)
| 操作 | 允许? | 说明 |
|------|--------|------|
| 删除容器 | ❌ 禁止 | 停止即可,容器配置是资产 |
| 删除 volume | ❌ 禁止 | 数据资产,不可恢复 |
| 删除 host 文件 | ❌ 禁止 | 先备份到 `/tmp/backup_YYYYMMDD/` |
| 覆盖文件 | ⚠️ 先备份 | 任何覆盖操作前必须先备份 |
| 停止容器 | ✅ 允许 | stop 是安全的 |
| 启动新容器 | ✅ 允许 | 配合正确的挂载配置 |
### 脚本优先原则(铁律)
> **使脚本执行失败时:修复脚本,而非绕过脚本。**
### 双备份先行原则(铁律 - 新增)
> **在双备份(本地 `releases/v{version}/bk/` + 服务器 `/opt/piano-plan/releases/v{version}/bk/`)全部完成之前,禁止执行任何实质性的部署操作。**
以下操作在双备份完成前**严禁**执行:
- ❌ 更新/修改生产数据库 schema
- ❌ 停止旧 Docker 容器
- ❌ 启动新 Docker 容器
- ❌ 上传/覆盖生产环境数据
- ❌ 加载新镜像到服务器
正确流程:
1. ✅ 完成本地 `releases/v{version}/bk/` 备份
2. ✅ 完成服务器 `/opt/piano-plan/releases/v{version}/bk/` 备份
3.**验证两份备份均存在且完整**
4.**方可执行部署操作**
| 错误行为 | 正确行为 |
|---------|---------|
| 脚本报错 → `docker rm` 手动清理 | 脚本报错 → 查看日志 → 修复脚本问题 → 重跑脚本 |
| 挂载丢失 → 手动指定新挂载 | 挂载丢失 → 更新脚本的挂载配置 → 重跑脚本 |
| 镜像加载失败 → `docker rmi` 清理 | 镜像加载失败 → 检查错误 → 重跑脚本 |
| 容器启动失败 → `docker rm` 重来 | 容器启动失败 → 查看 `docker logs` → 修复配置 → 重跑脚本 |
**一旦开始用脚本部署,就要坚持用到底,中途放弃脚本去做手动操作,等于打开了破坏系统的潘多拉魔盒。**
---
## 二、部署前检查清单
```
[ ] 确认本地代码已验证通过
[ ] 确认无未提交的代码
[ ] 确认获得用户的明确同意(用户说"部署吧"或"可以部署"
[ ] 确认需要保留的挂载点列表(见下方)
[ ] 确认 Docker Desktop 已启动(Windows
```
### 必须保留的挂载点
| 类型 | 源 | 容器内 | 说明 |
|------|-----|--------|------|
| 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 版本目录结构
每个版本发布前,在本地 `releases/` 目录下创建:
```
releases/
└── v1.3.0/
├── bk/ # 上次生产环境备份(来自服务器)
│ ├── piano_plans.db # 上次生产数据库
│ ├── docker/ # 上次版本的 docker 镜像 tar
│ │ └── piano-plan.tar
│ └── config/ # 上次版本的配置备份
│ └── api_config.json
└── toRelease/ # 本次发布文件
├── program/ # 本次发布的程序包
│ └── piano-plan.tar
├── schema.sql # 数据库 schema(仅结构,不含数据)
├── scripts/ # 发布脚本
│ └── deploy.sh
└── config/ # 本次配置(如有变更)
└── api_config.json
```
### 3.2 生成 schema.sql(仅结构)
```powershell
cd "D:\F\NewI\opencode\daily-workspace\projects\青年钢琴集体课\练习方案系统"
# 进入版本目录
mkdir -p releases/v1.3.0/toRelease/scripts
# 导出数据库结构(不含数据)
docker exec piano-plan-for-export sqlite3 /app/data/piano_plans.db ".schema" > releases/v1.3.0/toRelease/schema.sql
# 或使用 python
python -c "
import sqlite3
conn = sqlite3.connect('data/piano_plans.db')
with open('releases/v1.3.0/toRelease/schema.sql', 'w', encoding='utf-8') as f:
for line in conn.iterdump():
if 'CREATE' in line or 'CREATE' in line.upper():
f.write(line + '\n')
"
```
### 3.3 构建 Docker 镜像并打包
```powershell
cd "D:\F\NewI\opencode\daily-workspace\projects\青年钢琴集体课\练习方案系统"
# 0. 配置代理(必须!)
$env:HTTP_PROXY="http://127.0.0.1:15000"
$env:HTTPS_PROXY="http://127.0.0.1:15000"
# 1. 构建镜像
docker build -t piano-plan:v1.4.0 .
# 2. 保存镜像
docker save piano-plan:v1.4.0 -o releases/v1.4.0/toRelease/program/piano-plan.tar
```
---
## 四、服务器部署步骤
### 4.1 SSH 到服务器
```bash
ssh -i ~/.ssh/id_rsa root@47.115.32.206
```
### 4.2 创建版本目录并备份上次生产环境
```bash
# 创建版本目录
mkdir -p /opt/piano-plan/releases/v1.3.0/bk
mkdir -p /opt/piano-plan/releases/v1.3.0/toRelease
# 备份当前生产环境到 bk/
# 1. 备份数据库
docker cp piano-plan:/app/data/piano_plans.db /opt/piano-plan/releases/v1.3.0/bk/piano_plans.db.$(date +%Y%m%d)
# 2. 备份 docker 镜像(如果存在)
docker save piano-plan:$(docker inspect piano-plan --format '{{.Config.Image}}' | sed 's/piano-plan://') -o /opt/piano-plan/releases/v1.3.0/bk/docker/piano-plan.tar 2>/dev/null || true
# 3. 备份配置
cp -r /opt/piano-plan/config /opt/piano-plan/releases/v1.3.0/bk/config.bak.$(date +%Y%m%d)
```
### 4.3 上传本次发布文件
```bash
# 在本地执行上传
exit # 先退出服务器 SSH
# 上传发布包
scp -i ~/.ssh/id_rsa -r releases/v1.3.0/toRelease/* root@47.115.32.206:/opt/piano-plan/releases/v1.3.0/toRelease/
```
### 4.4 服务器执行部署
```bash
# SSH 再次连接
ssh -i ~/.ssh/id_rsa root@47.115.32.206
cd /opt/piano-plan/releases/v1.3.0
# 1. 停止旧容器
docker stop piano-plan
docker rm piano-plan
# 2. 加载新镜像
docker load -i toRelease/program/piano-plan.tar
# 3. 执行数据库迁移(仅 schema,不覆盖数据)
# 方法A:使用 sqlite3 直接执行 schema(如果有 ALTER TABLE 新增字段)
docker cp toRelease/schema.sql piano-plan:/tmp/schema.sql
docker exec piano-plan sqlite3 /app/data/piano_plans.db < /tmp/schema.sql
# 方法B:如果 schema 变更较大,先备份再重建表结构(不丢失数据)
# 具体看本次变更内容决定
# 4. 启动新容器
docker run -d \
--name piano-plan \
-p 5001:5001 \
--restart unless-stopped \
-e FLASK_ENV=production \
-v piano-plan-data:/app/data \
-v piano-plan-output:/app/output \
-v /opt/piano-plan/config:/app/config \
piano-plan:v1.3.0
```
### 4.5 验证
```bash
# 检查容器状态
docker ps --filter name=piano-plan
# 检查日志
docker logs piano-plan --tail 30
# 验证数据库表结构
docker exec piano-plan sqlite3 /app/data/piano_plans.db ".tables"
# 验证服务
curl -I http://localhost:5001/
```
---
## 五、数据保护规范
### 5.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 表 |
| 目标数据 | piano-plan-data:/app/data | goals, goal_relations, student_goals 表 |
| 问题数据 | piano-plan-data:/app/data | problems 表 |
### 5.2 本次发布 schema 变更
> 本次发布涉及新增目标管理模块,需要执行数据库迁移:
> - 新增 `goals` 表
> - 新增 `goal_relations` 表
> - 新增 `student_goals` 表
> - 修改 `problems` 表:更新分类体系
> - 修改 `students` 表:无变更
### 5.3 双备份保障
| 备份位置 | 内容 | 说明 |
|----------|------|------|
| `/opt/piano-plan/releases/v1.3.0/bk/` | 上次生产环境完整备份 | 版本回退用 |
| `/opt/piano-plan/releases/v1.3.1/bk/` | 本次发布前备份 | 下次发布时自动创建 |
---
## 六、回滚流程
### 6.1 回滚到上一版本
```bash
# 停止当前容器
docker stop piano-plan
docker rm piano-plan
# 使用 bk 中的旧镜像(如果有)
docker load -i /opt/piano-plan/releases/v1.3.0/bk/docker/piano-plan.tar
# 启动旧容器
docker run -d \
--name piano-plan \
-p 5001:5001 \
--restart unless-stopped \
-e FLASK_ENV=production \
-v piano-plan-data:/app/data \
-v piano-plan-output:/app/output \
-v /opt/piano-plan/config:/app/config \
piano-plan:旧版本标签
```
### 6.2 数据库紧急回滚
```bash
# 停止容器
docker stop piano-plan
# 从 bk 恢复数据库
cp /opt/piano-plan/releases/v1.3.0/bk/piano_plans.db.上次的日期 piano-plan:/app/data/piano_plans.db
# 重启
docker start piano-plan
```
---
## 七、服务信息
| 项目 | 值 |
|------|-----|
| 生产地址 | https://piano.yoin.fun |
| SSH | `ssh -i ~/.ssh/id_rsa root@47.115.32.206` |
| 容器名 | piano-plan |
| 端口 | 5001 |
| 数据库位置 | piano-plan-data volume |
| API配置位置 | /opt/piano-plan/config |
| 发布包位置 | /opt/piano-plan/releases/ |
### Volume 列表
| Volume | 容器内路径 | 说明 |
|--------|------------|------|
| piano-plan-data | /app/data | SQLite 数据库 |
| piano-plan-output | /app/output | PDF 导出 |
### Bind Mount 列表
| 源 | 容器内路径 | 说明 |
|-----|------------|------|
| /opt/piano-plan/config | /app/config | API 配置 |
---
## 八、API 配置说明
### 8.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 |
### 8.2 API 配置存储
- 配置存储在 `/opt/piano-plan/config/api_config.json`
- 每个 provider 的 key 存储在 `api_keys` 映射中
- 切换 provider 时自动使用对应的 key
---
## 九、常见问题
### Q: 数据库 schema 迁移失败?
A: 检查 sqlite3 版本兼容性,确保 .schema 导出的 SQL 兼容目标数据库版本
### Q: 容器无法启动?
A: 检查日志 `docker logs piano-plan`,常见原因:端口被占用、volume 权限问题
### Q: 目标管理功能报错?
A: 检查数据库是否成功执行了 schema 迁移,新增了 goals, goal_relations, student_goals 表
### Q: SSE 不完整?
A: nginx 需要为 SSE 配置特定的代理设置,参考之前文档
### Q: v1.3.2 部署后评估功能不工作?
A: 检查是否执行了 migrate_goals_v3.py 迁移脚本,该脚本创建 student_goal_evaluations 表
---
## 十、检查清单(部署完成后必填)
```
[ ] 容器状态:running
[ ] 服务响应:HTTP 200/302
[ ] 数据库表完整:users, students, classes, student_problems, practice_plans, templates, problems, goals, goal_relations, student_goals, student_goal_evaluations
[ ] practice_plans 表有新字段:created_by, updated_by, updated_at, template_id, is_typical
[ ] 目标管理功能正常:创建目标、分配目标、评估目标
[ ] 时间线正常显示阶段评估和最终评估
[ ] API 配置正确
[ ] 功能验证:能生成练习方案
[ ] 方案列表支持删除
[ ] 学员列表"暂无方案/问题"样式正常
[ ] PDF 水印功能正常(配置后导出可见)
[ ] 数据统计页面正常显示
[ ] 导出PDF时 {student_goals} 正常显示学员目标
[ ] 导出预览功能正常:预览按钮、模态框、水印显示
[ ] 模板支持 <para alignment="center"> 居中语法(预览和PDF均有效)
```
---
## 十一、版本历史
| 版本 | 日期 | 变更 |
|------|------|------|
| v1.5.2 | 2026-04-28 | 导出预览功能(预览按钮+模态框+水印);目标内容换行修复;支持<para alignment="center">居中语法;隐藏MD下载按钮 |
| v1.5.1 | 2026-04-28 | PDF水印配置保存修复(3处漏改);{student_goals}占位符修复;移除目标导出时的"内容:"标签 |
| v1.5.0 | 2026-04-27 | 数据统计页面(问题/级别分布可视化);PDF水印配置(可自定义文本);编辑页按钮吸底;侧边栏顺序调整;Linux中文字体路径修复 |
| v1.4.0 | 2026-04-27 | 典型方案采纳;推荐方案列表;方案编辑/详情页导航优化(bfcache处理);审计字段完善(created_by/updated_by/updated_at);方案列表支持删除;学员列表"暂无方案/问题"样式统一 |
| v1.3.6 | 2026-04-24 | 方案详情导航优化;典型方案开关移至方案详情;方案列表显示问题级别+严重程度 |
| v1.3.5 | 2026-04-24 | 班级班主任字段;用户姓名name字段;班级/学员/方案增加"我的"筛选 |
| v1.3.4 | 2026-04-24 | 方案编辑按钮;问题增量添加;teachers API公开 |
| v1.3.3 | 2026-04-24 | 评估日期编辑;最终评估关联 StudentGoal 同步 |
| v1.3.2 | 2026-04-24 | StudentGoal 新增 status 字段;新增 StudentGoalEvaluation 表 |
| v1.3.1 | 2026-04-24 | DRY 规范;Fragment 复用方案;班级批量分配目标 |
| v1.3 | 2026-04-24 | 目标管理模块:Goal/GoalRelation/StudentGoal;问题分类重构 |
| v1.2 | 2026-04-23 | 问题迁移到数据库;移除个性化方案挂载 |
| v1.1 | 2026-04-20 | 模板管理;API配置界面 |
| v1.0 | 2026-04-17 | 初始版本 |
---
> **最后更新**2026-04-28
> **更新原因**:v1.5.2 - 导出预览功能;目标换行修复;居中语法支持