#Requires -Version 5.1 <# .SYNOPSIS 还原 MES Cloud 全量备份(gzip) .EXAMPLE powershell -ExecutionPolicy Bypass -File .\restore-db.ps1 powershell -ExecutionPolicy Bypass -File .\restore-db.ps1 -BackupFile "E:\MES_DB_Backup\mes_cloud_20260821.sql.gz" #> param( [string]$BackupFile, [switch]$Force ) $ErrorActionPreference = "Stop" . (Join-Path $PSScriptRoot "db-config.ps1") $dbCfg = Read-MesDbConfig $DbHost = [string]$dbCfg.host $DbPort = [int]$dbCfg.port $DbName = [string]$dbCfg.database $DbUser = [string]$dbCfg.user $DbPassword = [string]$dbCfg.password $BackupRoot = "E:\MES_DB_Backup" $CnfFile = Join-Path $env:TEMP ("mes_restore_{0}.cnf" -f $PID) function Find-MysqlTool { param([string]$Name) $cmd = Get-Command $Name -ErrorAction SilentlyContinue if ($cmd -and $cmd.Source) { return $cmd.Source } $binRoots = @( "C:\Program Files\MySQL", "C:\Program Files (x86)\MySQL", "C:\mysql", "D:\MySQL", "E:\MySQL", "C:\Program Files\MariaDB" ) foreach ($root in $binRoots) { if (-not (Test-Path $root)) { continue } $hit = Get-ChildItem -Path $root -Filter ($Name + ".exe") -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.Directory.Name -eq "bin" } | Select-Object -First 1 if ($hit) { return $hit.FullName } } return $null } try { if (-not $BackupFile) { $latest = Get-ChildItem -Path $BackupRoot -File -Filter "mes_cloud_*.sql.gz" -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1 if (-not $latest) { throw "找不到备份文件,目录: $BackupRoot" } $BackupFile = $latest.FullName } if (-not (Test-Path $BackupFile)) { throw "找不到备份文件: $BackupFile" } if (-not $Force) { Write-Host "即将把备份还原到 ${DbHost}:${DbPort} / $DbName" Write-Host "备份文件: $BackupFile" Write-Host "现有数据会被覆盖。" $ans = Read-Host "确认还原请输入 YES" if ($ans -ne "YES") { Write-Host "已取消" exit 0 } } $mysql = Find-MysqlTool "mysql" if (-not $mysql) { throw "找不到 mysql.exe,请安装 MySQL 客户端并把 bin 目录加入 PATH" } Write-MesMysqlCnf -Config $dbCfg -CnfPath $CnfFile $isGzip = $false $fsHead = [System.IO.File]::OpenRead($BackupFile) try { $b0 = $fsHead.ReadByte() $b1 = $fsHead.ReadByte() $isGzip = ($b0 -eq 0x1F -and $b1 -eq 0x8B) } finally { $fsHead.Dispose() } Write-Host $(if ($isGzip) { "正在还原(gzip 压缩包)..." } else { "正在还原(SQL 文本,文件较大时请耐心等待)..." }) $psi = New-Object System.Diagnostics.ProcessStartInfo $psi.FileName = $mysql $psi.Arguments = ('"--defaults-extra-file={0}" "--max-allowed-packet=1G" "--default-character-set=utf8mb4"' -f $CnfFile) $psi.UseShellExecute = $false $psi.RedirectStandardInput = $true $psi.RedirectStandardError = $true $psi.CreateNoWindow = $true $proc = New-Object System.Diagnostics.Process $proc.StartInfo = $psi if (-not $proc.Start()) { throw "无法启动 mysql" } $fs = [System.IO.File]::OpenRead($BackupFile) $gz = $null try { $inStream = $fs if ($isGzip) { $gz = New-Object System.IO.Compression.GZipStream($fs, [System.IO.Compression.CompressionMode]::Decompress) $inStream = $gz } $inStream.CopyTo($proc.StandardInput.BaseStream) $proc.StandardInput.Close() } finally { if ($gz) { $gz.Dispose() } $fs.Dispose() } $err = $proc.StandardError.ReadToEnd() $proc.WaitForExit() if ($proc.ExitCode -ne 0) { throw ("还原失败: {0}" -f $err) } Write-Host "还原完成" exit 0 } catch { Write-Host ("还原失败: {0}" -f $_.Exception.Message) -ForegroundColor Red exit 1 } finally { if (Test-Path $CnfFile) { Remove-Item $CnfFile -Force -ErrorAction SilentlyContinue } }