feat: 添加migrate-prod-to-dev.sh数据迁移脚本(自动处理列名差异)

This commit is contained in:
2026-05-30 09:19:28 +08:00
parent 49b32a8b2d
commit 414d35c19b

113
scripts/migrate-prod-to-dev.sh Executable file
View File

@@ -0,0 +1,113 @@
#!/bin/bash
# ===========================================
# 数据迁移脚本: milkydata → milkydata_dev
# 用途: 将生产库 milkydata 的数据迁移到 milkydata_dev
# 自动处理列名差异 (is_paid → is_member, paid_expires_at → membership_expires_at)
# 注意: 目标库 milkydata_dev 中已存在的数据会被清空
# ===========================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
DB_CONTAINER="1Panel-postgresql-FtMo"
DB_USER="milkydata"
SRC_DB="milkydata"
DST_DB="milkydata_dev"
BACKUP_DIR="${PROJECT_ROOT}/backups"
mkdir -p "$BACKUP_DIR"
LOG_FILE="${BACKUP_DIR}/migration_$(date +%Y%m%d_%H%M%S).log"
log() { echo "[$(date '+%H:%M:%S')] $1" | tee -a "$LOG_FILE"; }
echo "═══════════════════════════════════════"
echo " 数据迁移: ${SRC_DB}${DST_DB}"
echo "═══════════════════════════════════════"
# ---------- 步骤 0: 确认 ----------
echo ""
echo "源库: ${SRC_DB}"
docker exec "$DB_CONTAINER" psql -U "$DB_USER" -d "$SRC_DB" -tAc "
SELECT CONCAT(' users: ', COUNT(*), ' 行') FROM users;
SELECT CONCAT(' weather_data: ', COUNT(*), ' 行') FROM weather_data;
SELECT CONCAT(' payment_orders: ', COUNT(*), ' 行') FROM payment_orders;
"
echo ""
echo "目标库: ${DST_DB}(数据将被清空并重新导入)"
echo ""
read -p "确认迁移? [y/N] " -n 1 -r; echo
[[ ! $REPLY =~ ^[Yy]$ ]] && { echo "已取消"; exit 0; }
# ---------- 步骤 1: 备份目标库 ----------
log "备份目标库 ${DST_DB}..."
docker exec "$DB_CONTAINER" pg_dump -U "$DB_USER" -d "$DST_DB" -Fc \
> "${BACKUP_DIR}/${DST_DB}_pre_migration_$(date +%Y%m%d_%H%M%S).dump" 2>> "$LOG_FILE"
log " 目标库备份完成 ✅"
# ---------- 步骤 2: 清空目标库 ----------
log "清空目标库 ${DST_DB}..."
docker exec "$DB_CONTAINER" psql -U "$DB_USER" -d "$DST_DB" -tAc "
TRUNCATE weather_data CASCADE;
TRUNCATE payment_orders CASCADE;
TRUNCATE notifications CASCADE;
TRUNCATE payment_audit_log CASCADE;
DELETE FROM users CASCADE;
" 2>> "$LOG_FILE"
log " 清空完成 ✅"
# ---------- 步骤 3: 导出源库数据到 SQL 文件 ----------
log "导出源库数据..."
TMP_SQL="/tmp/migrate_$$.sql"
# users 表(列名转换: is_paid → is_member, paid_expires_at → membership_expires_at
docker exec "$DB_CONTAINER" pg_dump -U "$DB_USER" -d "$SRC_DB" \
--data-only --table=users --column-inserts 2>/dev/null \
| grep -v '^--' | grep -v '^$' \
| sed 's/ is_paid/ is_member/g' \
| sed 's/ paid_expires_at/ membership_expires_at/g' \
> "$TMP_SQL"
log " users 导出并转换列名 ✅"
# weather_data
docker exec "$DB_CONTAINER" pg_dump -U "$DB_USER" -d "$SRC_DB" \
--data-only --table=weather_data --column-inserts 2>/dev/null \
| grep -v '^--' | grep -v '^$' >> "$TMP_SQL"
log " weather_data 导出 ✅"
# payment_orders
docker exec "$DB_CONTAINER" pg_dump -U "$DB_USER" -d "$SRC_DB" \
--data-only --table=payment_orders --column-inserts 2>/dev/null \
| grep -v '^--' | grep -v '^$' >> "$TMP_SQL"
log " payment_orders 导出 ✅"
log "$(wc -l < "$TMP_SQL") 条 SQL 语句"
# ---------- 步骤 4: 导入到目标库 ----------
log "导入数据到 ${DST_DB}..."
docker exec -i "$DB_CONTAINER" psql -U "$DB_USER" -d "$DST_DB" < "$TMP_SQL" 2>> "$LOG_FILE"
log " 导入完成 ✅"
# ---------- 清理 ----------
rm -f "$TMP_SQL"
# ---------- 验证 ----------
echo ""
log "═════════════════════════════════"
log "迁移完成!源库 vs 目标库数据量:"
log "═════════════════════════════════"
for table in users weather_data payment_orders; do
src=$(docker exec "$DB_CONTAINER" psql -U "$DB_USER" -d "$SRC_DB" -tAc "SELECT COUNT(*) FROM ${table};" 2>/dev/null)
dst=$(docker exec "$DB_CONTAINER" psql -U "$DB_USER" -d "$DST_DB" -tAc "SELECT COUNT(*) FROM ${table};" 2>/dev/null)
status="✅"
[ "$src" != "$dst" ] && status="❌ 不匹配!"
log " ${table}: ${src}${dst} ${status}"
done
echo ""
echo "日志: ${LOG_FILE}"
echo "备份: ${BACKUP_DIR}/${DST_DB}_pre_migration_*.dump"
echo ""
echo "⚠️ 如需回滚gunzip -c ${BACKUP_DIR}/*.dump.gz | docker exec -i ... pg_restore"