7/30 Database Backup & Production Deployment & Teacher Stats Fix

数据库自动备份系统

给 MFS 项目加了一套完整的数据库备份/恢复方案:

  • scripts/db-backup.sh — 通过 SSH 远程备份 ubsrv 上的 MySQL(dev + test 两套)
  • 支持 backup / restore / list / cron 四个子命令
  • 备份文件 gzip 压缩,存放在 db/backups/
  • 自动清理超过 7 天的旧备份
  • crontab 凌晨 3:00 自动执行,无需人工干预

实测两套环境各 92K,备份秒级完成。

生产环境部署

把 MFS 推上了生产,跑在 jimmyuser2 VM(Ubuntu 25.04, 2G RAM),通过 cpolar 暴露公网。

架构

1
2
3
4
cpolar (公网穿透, HTTPS)
└── Nginx (:80) ← mfs-prod-web
└── /api → Gunicorn (:5000) ← mfs-prod-api
└── MySQL 8.0 (:3306) ← mfs-prod-mysql

3 个 Docker 容器,docker-compose.prod.yml 编排。生产机上没有源码,只有镜像 + compose + .env + 初始化 SQL。

Build → Ship → Run

不在生产机 git clone 整个仓库(里面有 dev/test/prod 三套配置 + 源码 + e2e 测试),采用镜像交付:

  1. Build:jimmyuser1(开发机)docker compose build --no-cache api web
  2. Ship:docker save | ssh prod 'docker load' 管道直传,不落盘
  3. Run:docker compose up -d

生产机文件结构:

1
2
3
4
5
6
~/mfs-prod/
├── docker-compose.yml # image: 引用,无 build
├── .env # MYSQL_ROOT_PASSWORD
└── db/
├── students_db.sql
└── seed_data.sql

一键部署命令

1
2
3
4
ssh ubsrv "cd ~/mfs && \
docker compose -f docker-compose.prod.yml build --no-cache api web && \
docker save mfs-prod-api mfs-prod-web | ssh prod 'docker load' && \
ssh prod 'cd ~/mfs-prod && docker compose up -d'"

jimmyuser1 → jimmyuser2 已配好 SSH 免密(别名 prod)。

cpolar 公网穿透

VM 在 Hyper-V 内网(172.30.x.x),没有公网 IP,用 cpolar 做隧道:

  • cpolar 3.3.12,systemd 持久化,开机自启
  • 免费版:URL 每次重启随机分配,带宽 1Mbps,个人用够
  • 执行方式:cpolar http 80 -dashboard=on(不是 yml 配置隧道,免费版不支持)

VM 分工定型

VM 用途
ubuntuserver (jimmy) 写博客、跑 Qwen Code
ubsrv (jimmyuser1) 开发环境,改代码、跑测试、构建镜像
jimmyuser2 生产环境,Docker 3 容器 + cpolar

踩坑记录

问题 原因 解决
Docker 官方源装不上 download.docker.com 被墙 Ubuntu 自带 docker.io 包
新功能没上生产 Docker build 缓存,COPY . . 层未更新 --no-cache 强制全量构建
jimmyuser1 连不上 jimmyuser2 DHCP 重启后 IP 变了 SSH config 别名 + 更新 IP
cpolar 隧道不启动 systemd 跑 start-all 但免费版不支持 yml 隧道配置 改 ExecStart 为 cpolar http 80
sudo + heredoc SSH 执行失败 管道和 heredoc 冲突 printf > tmpfile 再 sudo cp
cpolar.com 下载超时 GFW 本机下载 scp 过去

老师端统计 Tab 数据修复

老师端”班级统计”和”同科对比”两个 Tab 只有框架没有数据,定位并修复。

根因:前端 getLatestDate() 传当天日期(2026-07-30),但 DB 里 exam_date 是数据生成时的日期(2026-02 ~ 05)。WHERE exam_date = '2026-07-30' 匹配不到任何记录。

修复:

  • 后端新增 resolve_exam_date(),exam_date 改为可选,不传时自动 SELECT MAX(exam_date) WHERE type = ?
  • 前端去掉 getLatestDate(),只传 type

4 个统计接口验证通过:班级排名、分数段、各科前三、同科对比。

全量代码推送 GitHub

把 ubsrv 上积累的所有改动同步回 git 仓库:

1
2
197 files changed, +22,310 / -702
a231c9d..628e11d main -> main

Docker 三环境、赋分制、老师端四 Tab、API 蓝图重组等一次性全部推上去了。

Janet paused