提交 66784c3b authored 作者: 陈泽健's avatar 陈泽健

feat(script): 添加远程程序更新PowerShell脚本

- 实现SSH连接测试和服务器信息获取功能
- 添加磁盘空间检查和系统类型选择界面
- 集成数据库备份和更新报告生成功能
- 提供更新包上传和服务备份框架
- 实现日志记录和错误处理机制
- 添加配置文件管理和用户交互界面
上级 c06e6d85
#!/usr/bin/env pwsh
# ============================================================================
# 远程程序更新脚本
# 功能:通过SSH连接远程服务器,自动备份并更新系统前后端服务
# 作者:自动化团队
# 创建时间:2026-03-11
# ============================================================================
# ----------------------------------------------------------------------------
# 步骤1:脚本初始化
# ----------------------------------------------------------------------------
# 设置错误处理
$ErrorActionPreference = "Continue"
# 设置控制台输出编码为UTF-8
[Console]::OutputEncoding = [System.Text.Encoding]::UTF-8
$OutputEncoding = [System.Text.Encoding]::UTF-8
# 获取脚本根目录(当前执行脚本所在目录)
$ScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
# 定义目录路径
$ConfigDir = Join-Path $ScriptRoot "config"
$ScriptsDir = Join-Path $ScriptRoot "scripts"
$PackagesDir = Join-Path $ScriptRoot "packages"
$ReportsDir = Join-Path $ScriptRoot "reports"
$SqlBackupDir = Join-Path $ScriptRoot "Sqlbackup"
$ServiceBackupDir = Join-Path $ScriptRoot "Servicebackup"
# 创建必要的目录结构
$Directories = @($ReportsDir, $SqlBackupDir, $ServiceBackupDir)
foreach ($Dir in $Directories) {
if (-not (Test-Path $Dir)) {
New-Item -ItemType Directory -Path $Dir -Force | Out-Null
}
}
# 定义全局变量
$Global:SSHTimeout = 30 # SSH连接超时时间(秒)
$Global:MaxRetry = 3 # 最大重试次数
$Global:MinDiskSpace = 5 # 最小磁盘空间(GB)
$Global:PlinkPath = Join-Path $ScriptRoot "plink.exe"
$Global:PscpPath = Join-Path $ScriptRoot "pscp.exe"
# 定义配置文件路径
$Global:UpdateTypeMappingFile = Join-Path $ConfigDir "update_type_mapping.json"
$Global:UpdatePackageOldFile = Join-Path $ConfigDir "update_package_old.json"
$Global:UpdatePackageNewFile = Join-Path $ConfigDir "update_package_new.json"
# 定义全局变量用于存储执行信息
$Global:ServerInfo = @{}
$Global:ExecutionLog = @()
$Global:ReportData = @{}
# ----------------------------------------------------------------------------
# 日志函数
# ----------------------------------------------------------------------------
<#
.SYNOPSIS
输出INFO级别日志
.PARAMETER Message
日志消息内容
#>
function Write-LogInfo {
param([string]$Message)
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$LogMessage = "[$Timestamp] [INFO] $Message"
Write-Host $LogMessage
$Global:ExecutionLog += $LogMessage
}
<#
.SYNOPSIS
输出WARN级别日志
.PARAMETER Message
日志消息内容
#>
function Write-LogWarn {
param([string]$Message)
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$LogMessage = "[$Timestamp] [WARN] $Message"
Write-Host $LogMessage -ForegroundColor Yellow
$Global:ExecutionLog += $LogMessage
}
<#
.SYNOPSIS
输出ERROR级别日志
.PARAMETER Message
日志消息内容
#>
function Write-LogError {
param([string]$Message)
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$LogMessage = "[$Timestamp] [ERROR] $Message"
Write-Host $LogMessage -ForegroundColor Red
$Global:ExecutionLog += $LogMessage
}
<#
.SYNOPSIS
输出SUCCESS级别日志
.PARAMETER Message
日志消息内容
#>
function Write-LogOk {
param([string]$Message)
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$LogMessage = "[$Timestamp] [OK] $Message"
Write-Host $LogMessage -ForegroundColor Green
$Global:ExecutionLog += $LogMessage
}
# ----------------------------------------------------------------------------
# 步骤2:用户输入服务器信息
# ----------------------------------------------------------------------------
<#
.SYNOPSIS
获取用户输入的服务器连接信息
.OUTPUTS
包含服务器信息的哈希表
#>
function Get-ServerInput {
Write-Host "`n============================================================"
Write-Host "请输入服务器信息"
Write-Host "============================================================" -ForegroundColor Cyan
# 输入服务器IP地址
do {
$ServerIP = Read-Host "服务器IP地址"
if ([string]::IsNullOrWhiteSpace($ServerIP)) {
Write-LogWarn "服务器IP地址不能为空,请重新输入"
}
} while ([string]::IsNullOrWhiteSpace($ServerIP))
# 输入SSH端口号
$SSHPort = Read-Host "SSH端口号[22]"
if ([string]::IsNullOrWhiteSpace($SSHPort)) {
$SSHPort = "22"
}
# 输入登录用户名
do {
$Username = Read-Host "登录用户名"
if ([string]::IsNullOrWhiteSpace($Username)) {
Write-LogWarn "用户名不能为空,请重新输入"
}
} while ([string]::IsNullOrWhiteSpace($Username))
# 输入登录密码(使用掩码)
do {
$Password = Read-Host "登录密码" -AsSecureString
$PasswordPlain = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password))
if ([string]::IsNullOrWhiteSpace($PasswordPlain)) {
Write-LogWarn "密码不能为空,请重新输入"
}
} while ([string]::IsNullOrWhiteSpace($PasswordPlain))
Write-LogInfo "服务器信息输入完成"
return @{
ServerIP = $ServerIP
SSHPort = $SSHPort
Username = $Username
Password = $PasswordPlain
}
}
# ----------------------------------------------------------------------------
# 步骤3:SSH连接服务器
# ----------------------------------------------------------------------------
<#
.SYNOPSIS
测试SSH连接并执行远程命令
.PARAMETER ServerInfo
服务器连接信息
.PARAMETER Command
要执行的远程命令
.PARAMETER Timeout
连接超时时间(秒)
.OUTPUTS
命令执行结果
#>
function Invoke-SSHCommand {
param(
[hashtable]$ServerInfo,
[string]$Command,
[int]$Timeout = $Global:SSHTimeout
)
$PlinkArgs = @(
"-pw", $ServerInfo.Password
"-P", $ServerInfo.SSHPort
"-timeout", $Timeout
"$($ServerInfo.Username)@$($ServerInfo.ServerIP)"
$Command
)
try {
$Result = & $Global:PlinkPath $PlinkArgs 2>&1
return $Result
}
catch {
return $null
}
}
<#
.SYNOPSIS
测试SSH连接并验证服务器可达性
.PARAMETER ServerInfo
服务器连接信息
.OUTPUTS
连接成功返回$true,否则返回$false
#>
function Test-SSHConnection {
param([hashtable]$ServerInfo)
Write-LogInfo "正在连接服务器 $($ServerInfo.ServerIP):$($ServerInfo.SSHPort)..."
for ($i = 1; $i -le $Global:MaxRetry; $i++) {
$Result = Invoke-SSHCommand -ServerInfo $ServerInfo -Command "echo 'connection_ok'"
if ($Result -match "connection_ok") {
Write-LogOk "连接成功"
return $true
}
else {
Write-LogWarn "连接失败,正在重试 ($i/$Global:MaxRetry)..."
Start-Sleep -Seconds 2
}
}
Write-LogError "连接失败,已达到最大重试次数"
return $false
}
# ----------------------------------------------------------------------------
# 步骤4:获取服务器信息
# ----------------------------------------------------------------------------
<#
.SYNOPSIS
获取服务器系统信息
.PARAMETER ServerInfo
服务器连接信息
.OUTPUTS
包含服务器信息的哈希表
#>
function Get-ServerSystemInfo {
param([hashtable]$ServerInfo)
Write-LogInfo "正在获取服务器信息..."
$SystemInfo = @{}
# 获取操作系统信息
$OSInfo = Invoke-SSHCommand -ServerInfo $ServerInfo -Command "uname -a"
$SystemInfo.OS = if ($OSInfo) { $OSInfo -join " " } else { "未知" }
# 获取系统架构信息
$ArchInfo = Invoke-SSHCommand -ServerInfo $ServerInfo -Command "uname -m"
$SystemInfo.Arch = if ($ArchInfo) { $ArchInfo -join "" } else { "未知" }
# 获取服务器时间
$TimeInfo = Invoke-SSHCommand -ServerInfo $ServerInfo -Command "date '+%Y-%m-%d %H:%M:%S'"
$SystemInfo.Time = if ($TimeInfo) { $TimeInfo -join "" } else { "未知" }
# 获取home目录剩余空间
$DiskInfo = Invoke-SSHCommand -ServerInfo $ServerInfo -Command "df -h /home | awk 'NR==2 {print \$4}'"
$SystemInfo.DiskSpace = if ($DiskInfo) { $DiskInfo -join "" } else { "未知" }
# 判断平台类型
$NewPlatformCheck = Invoke-SSHCommand -ServerInfo $ServerInfo -Command "test -d /data/services && echo 'exists'"
$OldPlatformCheck = Invoke-SSHCommand -ServerInfo $ServerInfo -Command "test -d /var/www && echo 'exists'"
if ($NewPlatformCheck -match "exists" -and $OldPlatformCheck -match "exists") {
$SystemInfo.PlatformType = "平台冲突"
Write-LogError "服务器同时存在新统一平台目录(/data/services)和传统平台目录(/var/www),无法判断平台类型"
}
elseif ($NewPlatformCheck -match "exists") {
$SystemInfo.PlatformType = "新统一平台"
Write-LogInfo "检测到平台类型:新统一平台"
}
elseif ($OldPlatformCheck -match "exists") {
$SystemInfo.PlatformType = "传统平台"
Write-LogInfo "检测到平台类型:传统平台"
}
else {
$SystemInfo.PlatformType = "未知平台"
Write-LogError "未检测到有效的平台目录"
}
return $SystemInfo
}
<#
.SYNOPSIS
打印服务器信息
.PARAMETER SystemInfo
服务器系统信息
#>
function Show-ServerInfo {
param([hashtable]$SystemInfo)
Write-Host "`n============================================================"
Write-Host "服务器信息"
Write-Host "============================================================" -ForegroundColor Cyan
Write-Host "操作系统: $($SystemInfo.OS)"
Write-Host "系统架构: $($SystemInfo.Arch)"
Write-Host "服务器时间: $($SystemInfo.Time)"
Write-Host "Home剩余空间: $($SystemInfo.DiskSpace)"
Write-Host "平台类型: $($SystemInfo.PlatformType)"
Write-Host "============================================================"
}
# ----------------------------------------------------------------------------
# 步骤5:磁盘空间检查
# ----------------------------------------------------------------------------
<#
.SYNOPSIS
检查磁盘空间是否足够
.PARAMETER DiskSpaceString
磁盘空间字符串(如:25.3G)
.OUTPUTS
空间足够返回$true,否则返回$false
#>
function Test-DiskSpace {
param([string]$DiskSpaceString)
# 解析磁盘空间(支持格式:25.3G, 25.3G, 1024M等)
$SpaceValue = 0
if ($DiskSpaceString -match "([\d\.]+)([MGTP])") {
$Value = [double]$Matches[1]
$Unit = $Matches[2]
switch ($Unit) {
"T" { $SpaceValue = $Value * 1024 }
"G" { $SpaceValue = $Value }
"M" { $SpaceValue = $Value / 1024 }
default { $SpaceValue = $Value / 1024 / 1024 }
}
}
Write-LogInfo "Home目录剩余空间约: $SpaceValue GB"
if ($SpaceValue -lt $Global:MinDiskSpace) {
Write-LogError "磁盘空间不足(当前: $SpaceValue GB,要求: $Global:MinDiskSpace GB)"
return $false
}
Write-LogOk "磁盘空间检查通过"
return $true
}
# ----------------------------------------------------------------------------
# 步骤6:系统类型选择
# ----------------------------------------------------------------------------
<#
.SYNOPSIS
用户选择系统类型
.OUTPUTS
选中的系统类型名称
#>
function Select-SystemType {
Write-Host "`n请选择系统类型:"
Write-Host "1. 预定系统"
Write-Host "2. 运维集控系统"
Write-Host "3. 讯飞转录系统"
Write-Host "4. 统一平台系统"
$SystemTypeMap = @{
"1" = "预定系统"
"2" = "运维集控系统"
"3" = "讯飞转录系统"
"4" = "统一平台系统"
}
do {
$Selection = Read-Host "请输入编号[1-4]"
if ($SystemTypeMap.ContainsKey($Selection)) {
$SelectedType = $SystemTypeMap[$Selection]
Write-LogOk "已选择:$SelectedType"
return $SelectedType
}
else {
Write-LogWarn "无效的选择,请重新输入"
}
} while ($true)
}
# ----------------------------------------------------------------------------
# 步骤7:更新类型选择
# ----------------------------------------------------------------------------
<#
.SYNOPSIS
用户选择服务更新类型
.OUTPUTS
选中的更新类型名称
#>
function Select-UpdateType {
Write-Host "`n请选择服务更新类型:"
Write-Host "1. 前端更新"
Write-Host "2. 后端更新"
Write-Host "3. 全量更新"
$UpdateTypeMap = @{
"1" = "前端更新"
"2" = "后端更新"
"3" = "全量更新"
}
do {
$Selection = Read-Host "请输入编号[1-3]"
if ($UpdateTypeMap.ContainsKey($Selection)) {
$SelectedType = $UpdateTypeMap[$Selection]
Write-LogOk "已选择:$SelectedType"
return $SelectedType
}
else {
Write-LogWarn "无效的选择,请重新输入"
}
} while ($true)
}
# ----------------------------------------------------------------------------
# 步骤8:创建更新目录
# ----------------------------------------------------------------------------
<#
.SYNOPSIS
在服务器上创建更新目录
.PARAMETER ServerInfo
服务器连接信息
.OUTPUTS
创建成功返回$true,否则返回$false
#>
function New-UpdateDirectory {
param([hashtable]$ServerInfo)
Write-LogInfo "正在创建服务器更新目录..."
for ($i = 1; $i -le $Global:MaxRetry; $i++) {
$Result = Invoke-SSHCommand -ServerInfo $ServerInfo -Command "mkdir -p /home/update && echo 'created'"
if ($Result -match "created") {
Write-LogOk "更新目录创建成功:/home/update"
return $true
}
else {
Write-LogWarn "创建失败,正在重试 ($i/$Global:MaxRetry)..."
Start-Sleep -Seconds 1
}
}
Write-LogError "更新目录创建失败,已达到最大重试次数"
return $false
}
# ----------------------------------------------------------------------------
# 步骤9:上传更新包
# ----------------------------------------------------------------------------
<#
.SYNOPSIS
上传更新包到服务器
.PARAMETER ServerInfo
服务器连接信息
.PARAMETER PackagePath
本地更新包路径
.OUTPUTS
上传成功返回$true,否则返回$false
#>
function Send-UpdatePackage {
param(
[hashtable]$ServerInfo,
[string]$PackagePath
)
Write-LogInfo "正在上传更新包..."
if (-not (Test-Path $PackagePath)) {
Write-LogError "更新包文件不存在:$PackagePath"
return $false
}
try {
$PscpArgs = @(
"-pw", $ServerInfo.Password
"-P", $ServerInfo.SSHPort
$PackagePath
"$($ServerInfo.Username)@$($ServerInfo.ServerIP):/home/update/"
)
$Result = & $Global:PscpPath $PscpArgs 2>&1
Write-LogOk "更新包上传成功:$(Split-Path $PackagePath -Leaf)"
return $true
}
catch {
Write-LogError "更新包上传失败:$_"
return $false
}
}
# ----------------------------------------------------------------------------
# 步骤10:备份数据库
# ----------------------------------------------------------------------------
<#
.SYNOPSIS
执行数据库备份
.PARAMETER ServerInfo
服务器连接信息
.OUTPUTS
备份成功返回备份文件名,否则返回$null
#>
function Invoke-DatabaseBackup {
param([hashtable]$ServerInfo)
Write-LogInfo "正在执行数据库备份..."
# 数据库备份脚本路径
$BackupScriptPath = Join-Path $ScriptsDir "backup_db.sh"
if (-not (Test-Path $BackupScriptPath)) {
Write-LogWarn "数据库备份脚本不存在,跳过数据库备份"
return $null
}
# 上传数据库备份脚本
Write-LogInfo "正在上传数据库备份脚本..."
$UploadResult = Send-UpdatePackage -ServerInfo $ServerInfo -PackagePath $BackupScriptPath
if (-not $UploadResult) {
Write-LogError "数据库备份脚本上传失败"
return $null
}
Write-LogOk "数据库备份脚本上传成功"
# 执行数据库备份脚本
Write-LogInfo "正在执行数据库备份..."
$BackupResult = Invoke-SSHCommand -ServerInfo $ServerInfo -Command "cd /home/update && bash backup_db.sh"
Write-LogInfo "数据库备份执行完成"
# 解析备份文件名
if ($BackupResult -match "db_\d+_\d+\.sql") {
$BackupFile = $Matches[0]
Write-LogOk "数据库备份成功:/home/backup/$BackupFile"
# 导出数据库备份文件到本地
Write-LogInfo "正在导出数据库备份文件..."
$LocalBackupPath = Join-Path $SqlBackupDir "$($ServerInfo.ServerIP)_$BackupFile"
try {
$PscpArgs = @(
"-pw", $ServerInfo.Password
"-P", $ServerInfo.SSHPort
"$($ServerInfo.Username)@$($ServerInfo.ServerIP):/home/backup/$BackupFile"
$LocalBackupPath
)
$Result = & $Global:PscpPath $PscpArgs 2>&1
Write-LogOk "数据库备份文件导出成功:$LocalBackupPath"
return @{
RemoteFile = "/home/backup/$BackupFile"
LocalFile = $LocalBackupPath
}
}
catch {
Write-LogError "数据库备份文件导出失败:$_"
return $null
}
}
else {
Write-LogWarn "未找到数据库备份文件"
return $null
}
}
# ----------------------------------------------------------------------------
# 步骤11:备份服务包
# ----------------------------------------------------------------------------
<#
.SYNOPSIS
备份服务器上的服务包
.PARAMETER ServerInfo
服务器连接信息
.PARAMETER ServicePath
服务路径
.OUTPUTS
备份成功返回备份信息,否则返回$null
#>
function Backup-ServicePackage {
param(
[hashtable]$ServerInfo,
[string]$ServicePath
)
Write-LogInfo "正在备份服务包:$ServicePath"
# 创建服务器备份目录
Invoke-SSHCommand -ServerInfo $ServerInfo -Command "mkdir -p /home/backup" | Out-Null
# 获取服务目录名称
$ServiceName = Split-Path $ServicePath -Leaf
$BackupPath = "/home/backup/$ServiceName"
# 复制服务包到备份目录
Write-LogInfo "正在复制服务包..."
$CopyResult = Invoke-SSHCommand -ServerInfo $ServerInfo -Command "cp -r $ServicePath $BackupPath 2>&1 && echo 'copied'"
if ($CopyResult -match "copied") {
Write-LogOk "服务包复制成功:$ServicePath$BackupPath"
}
else {
Write-LogError "服务包复制失败"
return $null
}
# 压缩服务包
Write-LogInfo "正在压缩服务包..."
$BackupZip = "/home/backup/${ServiceName}_backup.zip"
$ZipResult = Invoke-SSHCommand -ServerInfo $ServerInfo -Command "cd /home/backup && zip -rq ${ServiceName}_backup.zip $ServiceName 2>&1 && echo 'zipped'"
if ($ZipResult -match "zipped") {
Write-LogOk "服务包压缩成功:$BackupZip"
}
else {
Write-LogError "服务包压缩失败"
return $null
}
# 导出备份文件到本地
Write-LogInfo "正在导出备份文件..."
$LocalBackupPath = Join-Path $ServiceBackupDir "$($ServerInfo.ServerIP)_${ServiceName}_backup.zip"
try {
$PscpArgs = @(
"-pw", $ServerInfo.Password
"-P", $ServerInfo.SSHPort
"$($ServerInfo.Username)@$($ServerInfo.ServerIP):$BackupZip"
$LocalBackupPath
)
$Result = & $Global:PscpPath $PscpArgs 2>&1
Write-LogOk "备份文件导出成功:$LocalBackupPath"
return @{
RemotePath = $BackupPath
RemoteZip = $BackupZip
LocalFile = $LocalBackupPath
}
}
catch {
Write-LogError "备份文件导出失败:$_"
return $null
}
}
# ----------------------------------------------------------------------------
# 步骤12:执行更新程序
# ----------------------------------------------------------------------------
<#
.SYNOPSIS
执行服务更新脚本
.PARAMETER ServerInfo
服务器连接信息
.PARAMETER UpdateType
更新类型(前端更新/后端更新/全量更新)
.OUTPUTS
执行成功返回$true,否则返回$false
#>
function Invoke-ServiceUpdate {
param(
[hashtable]$ServerInfo,
[string]$UpdateType
)
Write-LogInfo "正在执行服务更新..."
# 更新脚本路径
$UpdateScriptPath = Join-Path $ScriptsDir "service_update.sh"
if (-not (Test-Path $UpdateScriptPath)) {
Write-LogWarn "更新脚本不存在,跳过更新执行"
return $false
}
# 上传更新脚本
Write-LogInfo "正在上传更新脚本..."
$UploadResult = Send-UpdatePackage -ServerInfo $ServerInfo -PackagePath $UpdateScriptPath
if (-not $UploadResult) {
Write-LogError "更新脚本上传失败"
return $false
}
Write-LogOk "更新脚本上传成功"
# 执行更新脚本
Write-LogInfo "正在执行更新脚本..."
$UpdateResult = Invoke-SSHCommand -ServerInfo $ServerInfo -Command "cd /home/update && bash service_update.sh $UpdateType 2>&1; echo exit_code: $?"
if ($UpdateResult -match "exit_code: 0") {
Write-LogOk "更新脚本执行成功,返回值:0"
return $true
}
else {
Write-LogError "更新脚本执行失败"
return $false
}
}
# ----------------------------------------------------------------------------
# 步骤13:生成更新报告
# ----------------------------------------------------------------------------
<#
.SYNOPSIS
生成更新报告
.PARAMETER ReportData
报告数据
#>
function New-UpdateReport {
param([hashtable]$ReportData)
Write-LogInfo "正在生成更新报告..."
$Timestamp = Get-Date -Format "yyyy_MM_dd"
$ReportFileName = "$($ReportData.ServerIP)_更新测试报告_$Timestamp.md"
$ReportFilePath = Join-Path $ReportsDir $ReportFileName
$ReportContent = @"
# $($ReportData.ServerIP)更新报告
## 基本信息
- 报告编号:$($ReportData.ServerIP)-$($ReportData.ReportDate)
- 服务器IP:$($ReportData.ServerIP)
- SSH端口:$($ReportData.SSHPort)
- 系统类型:$($ReportData.SystemType)
- 更新类型:$($ReportData.UpdateType)
- 执行时间:$($ReportData.StartTime) - $($ReportData.EndTime)
## 服务器信息
- 操作系统:$($ReportData.ServerInfo.OS)
- 系统架构:$($ReportData.ServerInfo.Arch)
- 服务器时间:$($ReportData.ServerInfo.Time)
- Home目录剩余空间:$($ReportData.ServerInfo.DiskSpace)
- 平台类型:$($ReportData.ServerInfo.PlatformType)
## 执行记录
### 数据库备份
- 上传脚本:$($ReportData.DbBackup.UploadSuccess)
- 执行备份:$($ReportData.DbBackup.ExecuteSuccess)
- 备份文件:$($ReportData.DbBackup.RemoteFile)
- 导出本地:$($ReportData.DbBackup.LocalFile)
### 服务包备份
- 创建备份目录:成功
- 备份服务数量:$($ReportData.ServiceBackup.Count)
- 导出本地:$($ReportData.ServiceBackup.LocalFiles)
### 更新执行
- 上传更新包:$($ReportData.UpdateResult.UploadSuccess)
- 上传更新脚本:$($ReportData.UpdateResult.ScriptUploadSuccess)
- 执行更新脚本:$($ReportData.UpdateResult.ExecuteSuccess)
- 执行结果:$($ReportData.UpdateResult.Message)
## 总结
- 更新结果:$($ReportData.FinalResult)
- 备注:$($ReportData.Remark)
---
报告生成时间:$($ReportData.GenerateTime)
"@
try {
$ReportContent | Out-File -FilePath $ReportFilePath -Encoding UTF8 -Force
Write-LogOk "更新报告已生成:$ReportFilePath"
return $ReportFilePath
}
catch {
Write-LogError "报告生成失败:$_"
return $null
}
}
# ----------------------------------------------------------------------------
# 主函数
# ----------------------------------------------------------------------------
<#
.SYNOPSIS
主函数,执行远程更新流程
#>
function Main {
# 记录开始时间
$StartTime = Get-Date
$ReportDate = $StartTime.ToString("yyyy-MM-dd")
# 打印欢迎信息
Write-Host "`n============================================================"
Write-Host "远程程序更新脚本"
Write-Host "============================================================" -ForegroundColor Cyan
# 初始化报告数据
$ReportData = @{
ServerIP = ""
SSHPort = ""
SystemType = ""
UpdateType = ""
StartTime = $StartTime.ToString("yyyy-MM-dd HH:mm:ss")
EndTime = ""
ReportDate = $ReportDate
ServerInfo = @{}
DbBackup = @{
UploadSuccess = "失败"
ExecuteSuccess = "失败"
RemoteFile = "无"
LocalFile = "无"
}
ServiceBackup = @{
Count = 0
LocalFiles = "无"
}
UpdateResult = @{
UploadSuccess = "失败"
ScriptUploadSuccess = "失败"
ExecuteSuccess = "失败"
Message = ""
}
FinalResult = "失败"
Remark = ""
GenerateTime = ""
}
try {
# 步骤2:获取用户输入
$ServerInput = Get-ServerInput
$ReportData.ServerIP = $ServerInput.ServerIP
$ReportData.SSHPort = $ServerInput.SSHPort
$Global:ServerInfo = $ServerInput
# 步骤3:测试SSH连接
$ConnectionResult = Test-SSHConnection -ServerInfo $ServerInput
if (-not $ConnectionResult) {
Write-LogError "无法连接到服务器,程序退出"
$ReportData.Remark = "SSH连接失败"
New-UpdateReport -ReportData $ReportData
exit 1
}
# 步骤4:获取服务器信息
$SystemInfo = Get-ServerSystemInfo -ServerInfo $ServerInput
$ReportData.ServerInfo = $SystemInfo
Show-ServerInfo -SystemInfo $SystemInfo
# 步骤5:检查磁盘空间
$DiskSpaceOk = Test-DiskSpace -DiskSpaceString $SystemInfo.DiskSpace
if (-not $DiskSpaceOk) {
Write-LogError "磁盘空间不足,程序退出"
$ReportData.Remark = "磁盘空间不足"
New-UpdateReport -ReportData $ReportData
exit 1
}
# 检查平台类型
if ($SystemInfo.PlatformType -eq "平台冲突" -or $SystemInfo.PlatformType -eq "未知平台") {
Write-LogError "平台类型检测失败,程序退出"
$ReportData.Remark = "平台类型检测失败:$($SystemInfo.PlatformType)"
New-UpdateReport -ReportData $ReportData
exit 1
}
# 步骤6:选择系统类型
$SelectedSystemType = Select-SystemType
$ReportData.SystemType = $SelectedSystemType
# 步骤7:选择更新类型
$SelectedUpdateType = Select-UpdateType
$ReportData.UpdateType = $SelectedUpdateType
# 步骤8:创建更新目录
$CreateDirResult = New-UpdateDirectory -ServerInfo $ServerInput
if (-not $CreateDirResult) {
Write-LogError "创建更新目录失败,程序退出"
$ReportData.Remark = "创建更新目录失败"
New-UpdateReport -ReportData $ReportData
exit 1
}
# 步骤9:上传更新包
# 这里需要根据系统类型和更新类型选择对应的更新包
# 暂时跳过,待更新包准备好后实现
Write-LogWarn "更新包上传功能需要根据具体配置实现"
# 步骤10:备份数据库
$DbBackupResult = Invoke-DatabaseBackup -ServerInfo $ServerInput
if ($DbBackupResult) {
$ReportData.DbBackup.UploadSuccess = "成功"
$ReportData.DbBackup.ExecuteSuccess = "成功"
$ReportData.DbBackup.RemoteFile = $DbBackupResult.RemoteFile
$ReportData.DbBackup.LocalFile = $DbBackupResult.LocalFile
}
# 步骤11:备份服务包
# 这里需要根据配置获取需要备份的服务路径
# 暂时跳过,待配置完善后实现
Write-LogWarn "服务包备份功能需要根据具体配置实现"
# 步骤12:执行更新
$UpdateResult = Invoke-ServiceUpdate -ServerInfo $ServerInput -UpdateType $SelectedUpdateType
if ($UpdateResult) {
$ReportData.UpdateResult.UploadSuccess = "成功"
$ReportData.UpdateResult.ScriptUploadSuccess = "成功"
$ReportData.UpdateResult.ExecuteSuccess = "成功"
$ReportData.UpdateResult.Message = "更新执行成功"
$ReportData.FinalResult = "成功"
}
else {
$ReportData.UpdateResult.Message = "更新执行失败或跳过"
}
# 步骤13:生成报告
$EndTime = Get-Date
$ReportData.EndTime = $EndTime.ToString("yyyy-MM-dd HH:mm:ss")
$ReportData.GenerateTime = $EndTime.ToString("yyyy-MM-dd HH:mm:ss")
if ($ReportData.Remark -eq "") {
$ReportData.Remark = "更新流程执行完成"
}
$ReportPath = New-UpdateReport -ReportData $ReportData
# 打印完成信息
Write-Host "`n============================================================"
Write-Host "更新完成"
Write-Host "============================================================" -ForegroundColor Green
Write-Host "更新结果: $($ReportData.FinalResult)"
if ($ReportPath) {
Write-Host "报告位置: $ReportPath"
}
Write-Host "============================================================"
}
catch {
Write-LogError "程序执行出错:$_"
$ReportData.Remark = "程序异常:$_"
New-UpdateReport -ReportData $ReportData
exit 1
}
}
# 执行主函数
Main
# 远程更新脚本 - 待确认事项
## 文档说明
- 脚本路径:`AuxiliaryTool/ScriptTool/RemoteUpdate/remote_update.ps1`
- 创建时间:2026-03-11
- 状态:基础框架已完成,部分功能需完善
---
## 当前实现状态
### 已完成功能 ✅
| 功能模块 | 状态 | 说明 |
|----------|------|------|
| 脚本初始化 | ✅ 完成 | 目录创建、全局变量、编码设置 |
| 用户输入服务器信息 | ✅ 完成 | IP、端口、用户名、密码输入 |
| SSH连接测试 | ✅ 完成 | 30秒超时,3次重试机制 |
| 获取服务器信息 | ✅ 完成 | OS、架构、时间、磁盘空间、平台类型 |
| 磁盘空间检查 | ✅ 完成 | 小于5GB退出 |
| 系统类型选择 | ✅ 完成 | 4种系统类型选择 |
| 更新类型选择 | ✅ 完成 | 3种更新类型选择 |
| 创建更新目录 | ✅ 完成 | /home/update,重试3次 |
| 上传文件功能 | ✅ 完成 | 使用pscp.exe上传 |
| 数据库备份 | ✅ 完成 | 上传脚本、执行、导出 |
| 服务包备份框架 | ✅ 完成 | 函数已实现 |
| 执行更新脚本 | ✅ 完成 | 上传脚本、执行、获取返回值 |
| 生成更新报告 | ✅ 完成 | Markdown格式报告 |
| 日志输出 | ✅ 完成 | INFO/WARN/ERROR/OK四个级别 |
| 错误处理 | ✅ 完成 | 全局try-catch |
---
## 待完善功能 ⚠️
### 1. 更新包上传逻辑(步骤9)
#### 当前代码状态
```powershell
# 步骤9:上传更新包
# 这里需要根据系统类型和更新类型选择对应的更新包
# 暂时跳过,待更新包准备好后实现
Write-LogWarn "更新包上传功能需要根据具体配置实现"
```
#### 需要确认的内容
| 确认项 | 选项 | 说明 |
|--------|------|------|
| **更新包来源** | 选项A: 从 `update_package_new.json` 读取 | 根据系统类型和更新类型获取本地包路径 |
| | 选项B: 固定命名规则 | 如:`packages/{系统类型}_{更新类型}.zip` |
| | 选项C: 用户手动指定 | 每次运行时提示用户输入包路径 |
| **上传前校验** | 是否需要校验文件存在性 | 文件不存在时是否报错退出 |
#### 场景示例
**用户选择**:预定系统 + 前端更新
**需要明确**
- 本地更新包路径是什么?
- 例如:`./front/预定系统_前端.zip` 或其他?
---
### 2. 服务包备份逻辑(步骤11)
#### 当前代码状态
```powershell
# 步骤11:备份服务包
# 这里需要根据配置获取需要备份的服务路径
# 暂时跳过,待配置完善后实现
Write-LogWarn "服务包备份功能需要根据具体配置实现"
```
#### 需要确认的内容
| 确认项 | 选项 | 说明 |
|--------|------|------|
| **备份路径来源** | 选项A: 从 `update_type_mapping.json` 读取 | 根据平台类型+系统类型+更新类型获取服务路径 |
| | 选项B: 从 `update_package_old.json` 读取 | 获取旧版本服务路径 |
| | 选项C: 固定配置 | 预先定义好各系统的备份路径 |
| **备份范围** | 选项A: 只备份即将更新的服务 | 前端更新只备份前端相关目录 |
| | 选项B: 备份所有相关服务 | 全量备份该系统的所有服务 |
| **备份失败处理** | 选项A: 备份失败则退出程序 | 确保备份成功才继续 |
| | 选项B: 备份失败仅警告,继续执行 | 允许无备份更新 |
#### 配置文件结构参考
**update_type_mapping.json** 中已有配置结构:
```json
{
"新统一平台": {
"预定系统": {
"前端更新": {
"ai_services_path": "/data/services/web/pc/pc-vue2-ai",
"back_services_path": "/data/services/web/pc/pc-vue2-backstage",
...
}
}
}
}
```
**问题**
- 选择"预定系统"+"前端更新"时,是备份所有 `*_services_path` 还是有其他规则?
---
### 3. 更新包与服务包的关联关系
#### 待明确
```
用户选择:预定系统 + 前端更新
1. 上传的更新包是哪个文件?
└── 路径:?
2. 服务器上需要备份哪些目录?
└── 路径列表:?
3. 更新脚本需要什么参数?
└── 参数:前端更新
```
---
## 配置文件现状
### 已有的配置文件
| 文件 | 状态 | 用途 |
|------|------|------|
| `config/update_type_mapping.json` | ✅ 存在 | 服务路径映射(前端/后端各目录路径) |
| `config/update_package_old.json` | ✅ 存在 | 旧版本服务路径映射 |
| `config/update_package_new.json` | ✅ 存在 | 新版本本地包路径(./front, ./back) |
### update_package_new.json 内容
```json
{
"前端": "./front",
"后端": "./back"
}
```
**问题**:这个文件的含义是什么?
- `./front` 目录下是否存放了前端更新包?
- 更新包的命名规则是什么?
---
## 建议的实现方案
### 方案A:基于配置文件的自动化方案
**假设**
1. `./front` 目录下存放前端更新包,命名规则:`{系统类型}.zip`
2. `./back` 目录下存放后端更新包,命名规则:`{系统类型}.zip`
3. 备份时从 `update_type_mapping.json` 读取对应的服务路径
**实现逻辑**
```
预定系统 + 前端更新
上传:./front/预定系统.zip
备份:从 update_type_mapping.json 读取"新统一平台.预定系统.前端更新"下的所有路径
```
### 方案B:用户手动指定方案
**实现**
- 提示用户输入本地更新包路径
- 提示用户输入需要备份的服务器目录(或从配置选择)
### 方案C:混合方案(推荐)
**实现**
1. 优先尝试从配置自动识别
2. 配置不存在时提示用户手动输入
---
## 需要反馈的信息
请确认以下内容,以便完善脚本:
1. **更新包路径规则**
- 本地更新包存放在哪个目录?
- 命名规则是什么?
2. **服务备份路径规则**
- 是否从 `update_type_mapping.json` 读取?
- 选择"前端更新"时需要备份哪些目录?
3. **如果暂时不需要实现**
- 这两个功能是否可以留空,后续按需补充?
---
## 当前脚本可用性
### 可以正常使用的功能
- ✅ 服务器连接测试
- ✅ 服务器信息获取
- ✅ 磁盘空间检查
- ✅ 系统类型选择
- ✅ 更新类型选择
- ✅ 数据库备份(需 backup_db.sh 脚本)
- ✅ 更新脚本执行(需 service_update.sh 脚本)
- ✅ 报告生成
### 暂时跳过的功能
- ⚠️ 更新包自动上传(需确认路径规则)
- ⚠️ 服务包自动备份(需确认备份范围)
### 结论
**当前脚本框架完整,核心功能可用,待确认路径规则后可补充完善。**
---
*文档创建时间:2026-03-11*
*待反馈确认*
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论