diff --git a/docs/DEPLOYMENT_SOP.md b/docs/DEPLOYMENT_SOP.md index b43d2ef..8715b33 100644 --- a/docs/DEPLOYMENT_SOP.md +++ b/docs/DEPLOYMENT_SOP.md @@ -1,7 +1,7 @@ # 钢琴练习方案系统 - 部署 SOP -> 版本:v1.2 -> 日期:2026-04-23 +> 版本:v1.3 +> 日期:2026-04-24 > 核心原则:**不删除,只备份后新增/替换** --- @@ -19,7 +19,7 @@ ### 脚本优先原则(铁律) -> **当脚本执行失败时:修复脚本,而非绕过脚本。** +> **使脚本执行失败时:修复脚本,而非绕过脚本。** | 错误行为 | 正确行为 | |---------|---------| @@ -50,59 +50,129 @@ | Volume | `piano-plan-output` | `/app/output` | PDF 输出 | | Bind Mount | `/opt/piano-plan/config` | `/app/config` | API 配置 | -> ⚠️ **已移除**:`/opt/piano-plan/个性化方案` 挂载点(问题文件已迁移到数据库 `problems` 表) +--- + +## 三、本地发布包准备 + +### 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\青年钢琴集体课\练习方案系统" + +# 1. 构建镜像 +docker build -t piano-plan:v1.3.0 . + +# 2. 保存镜像 +docker save piano-plan:v1.3.0 -o releases/v1.3.0/toRelease/program/piano-plan.tar +``` --- -## 三、部署步骤 +## 四、服务器部署步骤 -### 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 服务器部署 +### 4.1 SSH 到服务器 ```bash -# 7. SSH 到服务器 +ssh -i ~/.ssh/id_rsa root@47.106.65.108 +``` + +### 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.106.65.108:/opt/piano-plan/releases/v1.3.0/toRelease/ +``` + +### 4.4 服务器执行部署 + +```bash +# SSH 再次连接 ssh -i ~/.ssh/id_rsa root@47.106.65.108 -# 8. 创建带时间戳的备份目录 -mkdir -p /opt/piano-plan/backups/backup_$(date +%Y%m%d) +cd /opt/piano-plan/releases/v1.3.0 -# 9. 备份当前数据库和配置 -docker cp piano-plan:/app/data/piano_plans.db /opt/piano-plan/backups/backup_$(date +%Y%m%d)/ -cp /opt/piano-plan/config/api_config.json /opt/piano-plan/backups/backup_$(date +%Y%m%d)/ - -# 10. 停止旧容器 +# 1. 停止旧容器 docker stop piano-plan docker rm piano-plan -# 11. 加载新镜像 -docker load -i /opt/piano-plan/piano-plan.tar +# 2. 加载新镜像 +docker load -i toRelease/program/piano-plan.tar -# 12. 启动新容器(无个性化方案挂载!) +# 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 \ @@ -111,51 +181,30 @@ docker run -d \ -v piano-plan-data:/app/data \ -v piano-plan-output:/app/output \ -v /opt/piano-plan/config:/app/config \ - piano-plan:latest + piano-plan:v1.3.0 ``` -### 3.4 数据同步(特殊情况下从开发环境覆盖生产数据库) - -> ⚠️ **警告**:这是**特殊处理**,仅在开发环境和生产环境数据结构需要统一时执行。正常部署不应覆盖生产数据库。 +### 4.5 验证 ```bash -# 13. 停止容器 -docker stop piano-plan - -# 14. 上传开发环境数据库到服务器(在本地执行) -scp -i ~/.ssh/id_rsa data/piano_plans.db root@47.106.65.108:/opt/piano-plan/backups/ - -# 15. 覆盖生产数据库 -docker cp /opt/piano-plan/backups/piano_plans.db piano-plan:/app/data/piano_plans.db - -# 16. 重启容器 -docker start piano-plan -``` - -### 3.5 验证 - -```bash -# 17. 检查容器状态 +# 检查容器状态 docker ps --filter name=piano-plan -# 18. 检查日志 -docker logs piano-plan --tail 20 +# 检查日志 +docker logs piano-plan --tail 30 -# 19. 验证服务 +# 验证数据库表结构 +docker exec piano-plan sqlite3 /app/data/piano_plans.db ".tables" + +# 验证服务 curl -I http://localhost:5001/ - -# 20. 验证数据库表 -docker exec piano-plan ls /app/data/ - -# 21. 验证 API 配置 -docker exec piano-plan cat /app/config/api_config.json ``` --- -## 四、数据保护规范 +## 五、数据保护规范 -### 4.1 必须保护的数据(绝对不删除) +### 5.1 必须保护的数据(绝对不删除) | 数据类型 | 存储位置 | 说明 | |----------|----------|------| @@ -163,96 +212,40 @@ docker exec piano-plan cat /app/config/api_config.json | 学员数据 | 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 | problems 表(已从文件迁移到数据库) | +| 目标数据 | piano-plan-data:/app/data | goals, goal_relations, student_goals 表 | +| 问题数据 | piano-plan-data:/app/data | problems 表 | -### 4.2 新增/更新的数据 +### 5.2 本次发布 schema 变更 -| 数据类型 | 说明 | -|----------|------| -| templates 表 | AI提示词模板、报告导出模板 | -| api_config.json | API 配置(provider, model, api_key) | +> 本次发布涉及新增目标管理模块,需要执行数据库迁移: +> - 新增 `goals` 表 +> - 新增 `goal_relations` 表 +> - 新增 `student_goals` 表 +> - 修改 `problems` 表:更新分类体系 +> - 修改 `students` 表:无变更 -### 4.3 备份操作 +### 5.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" -``` +| 备份位置 | 内容 | 说明 | +|----------|------|------| +| `/opt/piano-plan/releases/v1.3.0/bk/` | 上次生产环境完整备份 | 版本回退用 | +| `/opt/piano-plan/releases/v1.3.1/bk/` | 本次发布前备份 | 下次发布时自动创建 | --- -## 五、回滚流程 +## 六、回滚流程 -### 5.1 从备份恢复数据库 +### 6.1 回滚到上一版本 ```bash -# 停止容器 -docker stop piano-plan - -# 从备份目录恢复(替换日期) -docker cp /opt/piano-plan/backups/backup_20260423/piano_plans.db piano-plan:/app/data/piano_plans.db - -# 重启容器 -docker start piano-plan -``` - -### 5.2 完整回滚(恢复旧镜像+数据库) - -```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 \ @@ -261,17 +254,25 @@ docker run -d \ -v piano-plan-data:/app/data \ -v piano-plan-output:/app/output \ -v /opt/piano-plan/config:/app/config \ - piano-plan:latest + piano-plan:旧版本标签 +``` -# 从备份恢复数据库 +### 6.2 数据库紧急回滚 + +```bash +# 停止容器 docker stop piano-plan -docker cp /opt/piano-plan/backups/backup_YYYYMMDD/piano_plans.db piano-plan:/app/data/piano_plans.db + +# 从 bk 恢复数据库 +cp /opt/piano-plan/releases/v1.3.0/bk/piano_plans.db.上次的日期 piano-plan:/app/data/piano_plans.db + +# 重启 docker start piano-plan ``` --- -## 六、服务信息 +## 七、服务信息 | 项目 | 值 | |------|-----| @@ -280,8 +281,8 @@ docker start piano-plan | 容器名 | piano-plan | | 端口 | 5001 | | 数据库位置 | piano-plan-data volume | -| 问题文件位置 | /opt/piano-plan/个性化方案 | | API配置位置 | /opt/piano-plan/config | +| 发布包位置 | /opt/piano-plan/releases/ | ### Volume 列表 @@ -296,13 +297,11 @@ docker start piano-plan |-----|------------|------| | /opt/piano-plan/config | /app/config | API 配置 | -> ⚠️ **已移除**:`/opt/piano-plan/个性化方案` 挂载(问题文件已迁移到数据库) - --- -## 七、API 配置说明 +## 八、API 配置说明 -### 7.1 支持的 Provider +### 8.1 支持的 Provider | Provider | Endpoint | 模型 | |----------|----------|------| @@ -310,7 +309,7 @@ docker start piano-plan | 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 配置存储 +### 8.2 API 配置存储 - 配置存储在 `/opt/piano-plan/config/api_config.json` - 每个 provider 的 key 存储在 `api_keys` 映射中 @@ -318,47 +317,45 @@ docker start piano-plan --- -## 八、常见问题 +## 九、常见问题 -### Q: 数据库是空的? -A: 检查 volume `piano-plan-data` 是否被错误覆盖,尝试从备份恢复 +### Q: 数据库 schema 迁移失败? +A: 检查 sqlite3 版本兼容性,确保 .schema 导出的 SQL 兼容目标数据库版本 ### Q: 容器无法启动? A: 检查日志 `docker logs piano-plan`,常见原因:端口被占用、volume 权限问题 -### Q: API 配置没生效? -A: 检查 `/opt/piano-plan/config/api_config.json` 是否存在且正确 +### Q: 目标管理功能报错? +A: 检查数据库是否成功执行了 schema 迁移,新增了 goals, goal_relations, student_goals 表 -### 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` +### Q: SSE 不完整? +A: nginx 需要为 SSE 配置特定的代理设置,参考之前文档 --- -## 九、检查清单(部署完成后必填) +## 十、检查清单(部署完成后必填) ``` [ ] 容器状态:running [ ] 服务响应:HTTP 200/302 -[ ] 数据库记录:users, students, classes, student_problems, practice_plans, problems 完整 -[ ] templates 表存在且包含 AI提示词模板、报告导出模板 -[ ] API 配置:provider, model, api_key 正确 +[ ] 数据库表完整:users, students, classes, student_problems, practice_plans, problems, goals, goal_relations, student_goals +[ ] 目标管理功能正常:创建目标、分配目标、评估目标 +[ ] API 配置正确 [ ] 功能验证:能生成练习方案 ``` --- -> **最后更新**:2026-04-23 -> **更新原因**:v1.2 部署更新;移除个性化方案挂载(问题已迁移到数据库);更新备份和回滚流程 +## 十一、版本历史 + +| 版本 | 日期 | 变更 | +|------|------|------| +| 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-24 +> **更新原因**:v1.3 目标管理模块发布;新增版本目录结构和双备份规范;schema 发布说明