Initial commit: skills library
- 70 skills with code and documentation - Add .gitignore (ignore __pycache__, output/, temp/, venv/) - Clean up test intermediates and caches
This commit is contained in:
@@ -0,0 +1,471 @@
|
||||
---
|
||||
name: piano-practice-sys
|
||||
description: 钢琴练习方案生成系统的运维技能。用于系统的开发,维护、部署、数据备份恢复等操作。当用户提到钢琴方案系统的任何运维工作(查看数据、修改配置、备份恢复、部署上线等)时使用此技能。
|
||||
---
|
||||
|
||||
# 钢琴练习方案生成系统 - 运维技能
|
||||
|
||||
> 版本:v1.3 | 更新日期:2026-04-26
|
||||
> **核心原则:不删除,只备份后新增/替换**
|
||||
> **脚本优先原则:脚本报错 → 修复脚本,而非绕过脚本**
|
||||
|
||||
## 系统信息
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|-----|
|
||||
| **项目路径** | `D:\F\NewI\opencode\daily-workspace\projects\青年钢琴集体课\练习方案系统` |
|
||||
| **本地启动** | 双击 `run.bat` |
|
||||
| **本地访问** | http://127.0.0.1:5001 |
|
||||
| **生产环境** | https://piano.yoin.fun |
|
||||
| **生产服务器** | 47.115.32.206 (新) |
|
||||
| **旧服务器** | 47.106.65.108 (仅保留 Gitea/FRP) |
|
||||
| **数据库** | SQLite (`data/piano_plans.db`) |
|
||||
| **备份目录** | `/opt/piano-plan/backups/` |
|
||||
|
||||
## 登录凭据
|
||||
|
||||
**必须先登录才能调用API!**
|
||||
|
||||
```python
|
||||
import requests
|
||||
s = requests.Session()
|
||||
s.post('https://piano.yoin.fun/api/login', json={
|
||||
'username': 'hmo',
|
||||
'password': 'Dm19000o1st!'
|
||||
})
|
||||
# 后续请求用这个session
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 部署原则(铁律)
|
||||
|
||||
| 操作 | 允许? | 说明 |
|
||||
|------|--------|------|
|
||||
| 删除容器 | ❌ 禁止 | 停止即可,容器配置是资产 |
|
||||
| 删除 volume | ❌ 禁止 | 数据资产,不可恢复 |
|
||||
| 删除 host 文件 | ❌ 禁止 | 先备份到 `/tmp/backup_YYYYMMDD/` |
|
||||
| 覆盖文件 | ⚠️ 先备份 | 任何覆盖操作前必须先备份 |
|
||||
| 停止容器 | ✅ 允许 | stop 是安全的 |
|
||||
| 启动新容器 | ✅ 允许 | 配合正确的挂载配置 |
|
||||
|
||||
### 脚本优先原则(铁律)
|
||||
|
||||
> **当脚本执行失败时:修复脚本,而非绕过脚本。**
|
||||
|
||||
| 错误行为 | 正确行为 |
|
||||
|---------|---------|
|
||||
| 脚本报错 → `docker rm` 手动清理 | 脚本报错 → 查看日志 → 修复脚本问题 → 重跑脚本 |
|
||||
| 挂载丢失 → 手动指定新挂载 | 挂载丢失 → 更新脚本的挂载配置 → 重跑脚本 |
|
||||
| 镜像加载失败 → `docker rmi` 清理 | 镜像加载失败 → 检查错误 → 重跑脚本 |
|
||||
| 容器启动失败 → `docker rm` 重来 | 容器启动失败 → 查看 `docker logs` → 修复配置 → 重跑脚本 |
|
||||
|
||||
---
|
||||
|
||||
## 生产环境部署
|
||||
|
||||
### 架构
|
||||
|
||||
```
|
||||
用户 → https://piano.yoin.fun → Nginx (容器) → piano容器:5001
|
||||
```
|
||||
|
||||
### 关键路径
|
||||
|
||||
| 类型 | 宿主机/源 | 容器内路径 | 说明 |
|
||||
|------|-----------|------------|------|
|
||||
| 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 配置 |
|
||||
|
||||
### 部署步骤(使用脚本!)
|
||||
|
||||
**重要:遵循 DEPLOYMENT_SOP.md 的规范!**
|
||||
|
||||
#### 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.115.32.206:/opt/piano-plan/
|
||||
```
|
||||
|
||||
#### 3.3 服务器部署(使用脚本!)
|
||||
|
||||
```bash
|
||||
# 7. SSH 到服务器
|
||||
ssh -i ~/.ssh/id_rsa root@47.115.32.206
|
||||
|
||||
# 8. 使用自动化部署脚本(会自动完成所有步骤并验证)
|
||||
bash /opt/piano-plan/deploy.sh /opt/piano-plan/piano-plan.tar
|
||||
```
|
||||
|
||||
**脚本会自动完成**:
|
||||
- 检查镜像文件
|
||||
- 停止并删除旧容器
|
||||
- 加载新镜像
|
||||
- 备份数据库
|
||||
- 启动新容器(正确的挂载配置)
|
||||
- 验证部署
|
||||
|
||||
### 验证清单(部署完成后必填)
|
||||
|
||||
```
|
||||
[ ] 容器状态:running
|
||||
[ ] 服务响应:HTTP 200/302
|
||||
[ ] 问题文件数量:15个 md 文件
|
||||
[ ] 数据库记录:users, students, classes, student_problems, practice_plans 完整
|
||||
[ ] templates 表存在且包含 AI提示词模板、报告导出模板
|
||||
[ ] API 配置:provider, model, api_key 正确
|
||||
[ ] 功能验证:能生成练习方案
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 证书管理
|
||||
|
||||
```bash
|
||||
# 申请Let's Encrypt证书(停止nginx后执行)
|
||||
certbot certonly --standalone -d piano.yoin.fun
|
||||
|
||||
# 证书位置
|
||||
/etc/letsencrypt/live/piano.yoin.fun/
|
||||
|
||||
# 复制证书到nginx容器内
|
||||
docker cp /etc/letsencrypt/live/piano.yoin.fun/cert.pem nginx_server:/etc/letsencrypt/live/piano.yoin.fun/
|
||||
docker cp /etc/letsencrypt/live/piano.yoin.fun/chain.pem nginx_server:/etc/letsencrypt/live/piano.yoin.fun/
|
||||
docker cp /etc/letsencrypt/live/piano.yoin.fun/fullchain.pem nginx_server:/etc/letsencrypt/live/piano.yoin.fun/
|
||||
docker cp /etc/letsencrypt/live/piano.yoin.fun/privkey.pem nginx_server:/etc/letsencrypt/live/piano.yoin.fun/
|
||||
```
|
||||
|
||||
### Nginx配置
|
||||
|
||||
```nginx
|
||||
# /etc/nginx/conf.d/piano.yoin.fun.conf
|
||||
server {
|
||||
server_name piano.yoin.fun;
|
||||
location / {
|
||||
proxy_pass http://172.17.0.1:5001;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
# SSE (Server-Sent Events) 支持 - 生成方案进度需要
|
||||
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;
|
||||
}
|
||||
listen 443 ssl;
|
||||
ssl_certificate /etc/letsencrypt/live/piano.yoin.fun/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/piano.yoin.fun/privkey.pem;
|
||||
}
|
||||
```
|
||||
|
||||
### 故障排查
|
||||
|
||||
```bash
|
||||
# 检查容器状态
|
||||
docker ps -a | grep piano
|
||||
|
||||
# 查看容器日志
|
||||
docker logs piano-plan --tail 50
|
||||
|
||||
# 进入容器
|
||||
docker exec -it piano-plan sh
|
||||
|
||||
# 检查挂载
|
||||
docker inspect piano-plan --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{end}}'
|
||||
|
||||
# 检查问题文件
|
||||
docker exec piano-plan ls /app/个性化方案/
|
||||
|
||||
# 重启nginx
|
||||
docker exec nginx_server nginx -s reload
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 回滚流程
|
||||
|
||||
### 快速回滚(推荐)
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### 从备份恢复
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 数据保护规范
|
||||
|
||||
### 必须保护的数据(绝对不删除)
|
||||
|
||||
| 数据类型 | 存储位置 | 说明 |
|
||||
|----------|----------|------|
|
||||
| 用户数据 | 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文件 |
|
||||
|
||||
### 备份操作
|
||||
|
||||
```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/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 数据同步
|
||||
|
||||
### 生产环境数据导出
|
||||
|
||||
```python
|
||||
# 在服务器容器内执行
|
||||
import sqlite3
|
||||
conn = sqlite3.connect('/app/data/piano_plans.db')
|
||||
c = conn.cursor()
|
||||
|
||||
# 导出用户
|
||||
for row in c.execute("SELECT * FROM users"):
|
||||
print(row)
|
||||
|
||||
# 导出班级
|
||||
for row in c.execute("SELECT * FROM classes"):
|
||||
print(row)
|
||||
|
||||
# 导出学生
|
||||
for row in c.execute("SELECT * FROM students"):
|
||||
print(row)
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### 本地数据导入生产
|
||||
|
||||
```python
|
||||
# 创建同步脚本 sync_data.py
|
||||
import sqlite3
|
||||
|
||||
users = [
|
||||
('hyh', 'hash值', 'user', '时间戳'),
|
||||
# ...
|
||||
]
|
||||
|
||||
classes = [
|
||||
('26春(1)', '', 1, '时间戳'),
|
||||
# ...
|
||||
]
|
||||
|
||||
students = [
|
||||
('张三', '手机号', '微信昵称', '30分钟', '', 1, '时间戳'),
|
||||
# ...
|
||||
]
|
||||
|
||||
conn = sqlite3.connect('/app/data/piano_plans.db')
|
||||
c = conn.cursor()
|
||||
|
||||
for u in users:
|
||||
c.execute("INSERT INTO users (username, password_hash, role, created_at) VALUES (?, ?, ?, ?)", u)
|
||||
|
||||
for cls in classes:
|
||||
c.execute("INSERT INTO classes (name, description, active, created_at) VALUES (?, ?, ?, ?)", cls)
|
||||
|
||||
for s in students:
|
||||
c.execute("INSERT INTO students (name, phone, wechat_nickname, practice_time, notes, class_id, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)", s)
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
```
|
||||
|
||||
上传到服务器执行:
|
||||
```bash
|
||||
docker cp sync_data.py piano-plan:/tmp/
|
||||
docker exec piano-plan python /tmp/sync_data.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 从生产环境同步数据到本地开发
|
||||
|
||||
### 警告
|
||||
- **生产环境数据只读**:不得修改生产环境数据
|
||||
- **本地会被覆盖**:执行后本地数据会被生产数据完全替代
|
||||
- **先备份本地**:每次同步前自动备份
|
||||
|
||||
### 同步步骤
|
||||
|
||||
#### 1. 同步数据库(生产→本地)
|
||||
|
||||
```powershell
|
||||
# 1.1 备份本地数据库(手动)
|
||||
Copy-Item "data\piano_plans.db" "data\piano_plans.db.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
|
||||
|
||||
# 1.2 从生产容器复制数据库到服务器
|
||||
ssh -i ~/.ssh/id_rsa root@47.115.32.206 "docker cp piano-plan:/app/data/piano_plans.db /tmp/piano_plans_prod.db"
|
||||
|
||||
# 1.3 下载到本地
|
||||
scp -i ~/.ssh/id_rsa root@47.115.32.206:/tmp/piano_plans_prod.db data\
|
||||
|
||||
# 1.4 覆盖本地数据库
|
||||
Copy-Item "data\piano_plans_prod.db" "data\piano_plans.db" -Force
|
||||
|
||||
# 1.5 添加本地特有的字段(如果生产数据库没有)
|
||||
# 本地开发环境可能有 template_id, is_typical 等生产没有的字段
|
||||
```
|
||||
|
||||
```python
|
||||
# add_local_cols.py - 添加本地特有字段
|
||||
import sqlite3
|
||||
conn = sqlite3.connect('data/piano_plans.db')
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 检查并添加 template_id
|
||||
cursor.execute("PRAGMA table_info(practice_plans)")
|
||||
columns = [col[1] for col in cursor.fetchall()]
|
||||
if 'template_id' not in columns:
|
||||
cursor.execute("ALTER TABLE practice_plans ADD COLUMN template_id INTEGER")
|
||||
if 'is_typical' not in columns:
|
||||
cursor.execute("ALTER TABLE practice_plans ADD COLUMN is_typical INTEGER DEFAULT 0")
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
```
|
||||
|
||||
#### 2. 同步问题文件(生产→本地)
|
||||
|
||||
```powershell
|
||||
# 2.1 确保本地目录存在
|
||||
New-Item -ItemType Directory -Path "D:\F\NewI\opencode\daily-workspace\个性化方案\针对性练习(拆分为单独文件)" -Force
|
||||
|
||||
# 2.2 从服务器同步问题文件
|
||||
scp -i ~/.ssh/id_rsa -r root@47.106.65.108:/opt/piano-plan/个性化方案/* "D:\F\NewI\opencode\daily-workspace\个性化方案\针对性练习(拆分为单独文件)\"
|
||||
```
|
||||
|
||||
### 一键同步脚本
|
||||
|
||||
```python
|
||||
# sync_prod_to_local.py
|
||||
import sqlite3
|
||||
import shutil
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
LOCAL_DB = 'data/piano_plans.db'
|
||||
PROD_DB = 'data/piano_plans_prod.db'
|
||||
|
||||
def sync_from_production():
|
||||
# 1. 备份本地
|
||||
backup = f"{LOCAL_DB}.backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
||||
shutil.copy2(LOCAL_DB, backup)
|
||||
print(f"本地已备份: {backup}")
|
||||
|
||||
# 2. 覆盖本地数据库
|
||||
shutil.copy2(PROD_DB, LOCAL_DB)
|
||||
print("生产数据库已覆盖本地")
|
||||
|
||||
# 3. 添加本地特有字段
|
||||
conn = sqlite3.connect(LOCAL_DB)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("PRAGMA table_info(practice_plans)")
|
||||
columns = [col[1] for col in cursor.fetchall()]
|
||||
if 'template_id' not in columns:
|
||||
cursor.execute("ALTER TABLE practice_plans ADD COLUMN template_id INTEGER")
|
||||
print("添加 template_id 字段")
|
||||
if 'is_typical' not in columns:
|
||||
cursor.execute("ALTER TABLE practice_plans ADD COLUMN is_typical INTEGER DEFAULT 0")
|
||||
print("添加 is_typical 字段")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print("完成")
|
||||
|
||||
if __name__ == '__main__':
|
||||
sync_from_production()
|
||||
```
|
||||
|
||||
### 验证同步结果
|
||||
|
||||
```python
|
||||
# verify_sync.py
|
||||
import sqlite3
|
||||
conn = sqlite3.connect('data/piano_plans.db')
|
||||
cursor = conn.cursor()
|
||||
for table in ['classes', 'students', 'student_problems', 'practice_plans']:
|
||||
cursor.execute(f"SELECT COUNT(*) FROM {table}")
|
||||
print(f" {table}: {cursor.fetchone()[0]} 条")
|
||||
conn.close()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 快速命令参考
|
||||
|
||||
| 操作 | 命令 |
|
||||
|------|------|
|
||||
| 生产环境SSH | `ssh -i ~/.ssh/id_rsa root@47.115.32.206` |
|
||||
| 旧服务器SSH | `ssh -i ~/.ssh/id_rsa root@47.106.65.108` |
|
||||
| 查看容器 | `docker ps -a | grep piano` |
|
||||
| 查看日志 | `docker logs piano-plan --tail 50` |
|
||||
| 重启容器 | `docker restart piano-plan` |
|
||||
| 停止容器 | `docker stop piano-plan` |
|
||||
| 查看证书 | `docker exec nginx_server ls /etc/letsencrypt/live/` |
|
||||
| 测试HTTPS | `curl https://piano.yoin.fun` |
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [完整API文档](../../projects/青年钢琴集体课/练习方案系统/docs/API.md)
|
||||
- [项目README](../../projects/青年钢琴集体课/练习方案系统/README.md)
|
||||
- [部署SOP(必读)](../../projects/青年钢琴集体课/练习方案系统/docs/DEPLOYMENT_SOP.md)
|
||||
@@ -0,0 +1,239 @@
|
||||
# 备份与恢复 API 详细文档
|
||||
|
||||
## 概述
|
||||
|
||||
系统内置备份功能,支持数据备份、查看、恢复,以及操作日志查看。
|
||||
|
||||
## 备份目录结构
|
||||
|
||||
```
|
||||
bk/
|
||||
├── backup_20260419_143000/ # 备份文件夹
|
||||
│ ├── meta.json # 备份元信息
|
||||
│ ├── classes.json # 班级数据
|
||||
│ ├── students.json # 学员数据
|
||||
│ ├── problems/ # 问题配置文件
|
||||
│ │ ├── 01_手小.md
|
||||
│ │ ├── 04_压手腕.md
|
||||
│ │ └── ...
|
||||
│ └── api_config.json # API配置
|
||||
├── backup_20260420_100000/ # 另一个备份
|
||||
├── logs/
|
||||
│ ├── 2026-04-19.jsonl # 操作日志
|
||||
│ └── 2026-04-20.jsonl
|
||||
```
|
||||
|
||||
## API 接口
|
||||
|
||||
### 1. 创建备份
|
||||
|
||||
**接口**: `POST /api/backup`
|
||||
|
||||
**权限**: 管理员
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"description": "修改压手腕问题前"
|
||||
}
|
||||
```
|
||||
|
||||
**响应**:
|
||||
```json
|
||||
{
|
||||
"message": "备份成功",
|
||||
"backup_id": "backup_20260419_143000",
|
||||
"includes": ["classes", "students", "problems", "config"]
|
||||
}
|
||||
```
|
||||
|
||||
**示例**:
|
||||
```bash
|
||||
curl -X POST http://localhost:5000/api/backup \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"description": "修改压手腕问题前"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 查看备份列表
|
||||
|
||||
**接口**: `GET /api/backup`
|
||||
|
||||
**权限**: 登录用户
|
||||
|
||||
**响应**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "backup_20260419_143000",
|
||||
"created_at": "2026-04-19T14:30:00",
|
||||
"operator": "admin",
|
||||
"description": "修改压手腕问题前",
|
||||
"includes": ["classes", "students", "problems", "config"]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**示例**:
|
||||
```bash
|
||||
curl http://localhost:5000/api/backup
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 查看备份详情
|
||||
|
||||
**接口**: `GET /api/backup/<backup_id>`
|
||||
|
||||
**权限**: 登录用户
|
||||
|
||||
**响应**:
|
||||
```json
|
||||
{
|
||||
"meta": {
|
||||
"backup_id": "backup_20260419_143000",
|
||||
"created_at": "2026-04-19T14:30:00",
|
||||
"operator": "admin",
|
||||
"description": "修改压手腕问题前",
|
||||
"includes": ["classes", "students", "problems", "config"]
|
||||
},
|
||||
"classes": [...],
|
||||
"students": [...],
|
||||
"problems": ["01_手小.md", "04_压手腕.md", ...]
|
||||
}
|
||||
```
|
||||
|
||||
**示例**:
|
||||
```bash
|
||||
curl http://localhost:5000/api/backup/backup_20260419_143000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 恢复备份
|
||||
|
||||
**接口**: `POST /api/backup/<backup_id>/restore`
|
||||
|
||||
**权限**: 管理员
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"confirm": "确认恢复"
|
||||
}
|
||||
```
|
||||
|
||||
**⚠️ 安全要求**: 必须输入 exactly `确认恢复` 才能执行
|
||||
|
||||
**响应**:
|
||||
```json
|
||||
{
|
||||
"message": "恢复成功",
|
||||
"restored": ["classes", "students", "problems"],
|
||||
"auto_backup": "auto_before_restore_20260419_150000"
|
||||
}
|
||||
```
|
||||
|
||||
**注意**: 恢复前会自动创建当前数据的备份(auto_before_restore_xxx)
|
||||
|
||||
**示例**:
|
||||
```bash
|
||||
curl -X POST http://localhost:5000/api/backup/backup_20260419_143000/restore \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"confirm": "确认恢复"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. 查看操作日志
|
||||
|
||||
**接口**: `GET /api/logs`
|
||||
|
||||
**参数**:
|
||||
- `date`: 日期,格式 `YYYY-MM-DD`(默认今天)
|
||||
|
||||
**权限**: 管理员
|
||||
|
||||
**响应**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"timestamp": "2026-04-19T14:30:00",
|
||||
"action": "backup_create",
|
||||
"operator": "admin",
|
||||
"target_type": "system",
|
||||
"target_id": "backup_20260419_143000",
|
||||
"detail": "创建备份: 修改压手腕问题前",
|
||||
"ip": "127.0.0.1"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-04-19T15:00:00",
|
||||
"action": "problem_update",
|
||||
"operator": "admin",
|
||||
"target_type": "problem",
|
||||
"target_id": "04",
|
||||
"detail": "更新问题: 压手腕",
|
||||
"ip": "127.0.0.1"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**示例**:
|
||||
```bash
|
||||
# 查看今天的日志
|
||||
curl http://localhost:5000/api/logs
|
||||
|
||||
# 查看指定日期的日志
|
||||
curl "http://localhost:5000/api/logs?date=2026-04-19"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 安全机制
|
||||
|
||||
| 机制 | 说明 |
|
||||
|------|------|
|
||||
| 权限控制 | 备份/恢复/日志需要管理员权限 |
|
||||
| 二次确认 | 恢复必须输入"确认恢复" |
|
||||
| 自动备份 | 恢复前自动备份当前数据 |
|
||||
| 操作日志 | 所有操作记录可追溯 |
|
||||
|
||||
## 工作流程示例
|
||||
|
||||
### 场景:修改问题配置
|
||||
|
||||
```bash
|
||||
# 1. 先看看当前的问题配置
|
||||
curl http://localhost:5000/api/settings/problems
|
||||
|
||||
# 2. 讨论后确认要修改"压手腕"问题
|
||||
|
||||
# 3. 修改前创建备份
|
||||
curl -X POST http://localhost:5000/api/backup \
|
||||
-d '{"description": "修改压手腕问题前"}'
|
||||
|
||||
# 4. 执行修改(通过Web界面或API)
|
||||
|
||||
# 5. 验证修改结果
|
||||
curl http://localhost:5000/api/settings/problems/04
|
||||
```
|
||||
|
||||
### 场景:数据恢复
|
||||
|
||||
```bash
|
||||
# 发现修改出问题,想要恢复
|
||||
|
||||
# 1. 查看备份列表
|
||||
curl http://localhost:5000/api/backup
|
||||
|
||||
# 2. 查看想要恢复的备份内容
|
||||
curl http://localhost:5000/api/backup/backup_20260419_143000
|
||||
|
||||
# 3. 恢复备份(需要确认)
|
||||
curl -X POST http://localhost:5000/api/backup/backup_20260419_143000/restore \
|
||||
-d '{"confirm": "确认恢复"}'
|
||||
|
||||
# 4. 验证恢复结果
|
||||
curl http://localhost:5000/api/students
|
||||
```
|
||||
Reference in New Issue
Block a user