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

docs(prd): 更新远程程序更新脚本需求文档中广核项目特殊路径包代码

- 重构文档结构,增加版本信息和适用范围说明
- 补充双脚本详细功能说明和参数配置
- 完善平台类型、系统类型、更新类型的详细映射关系
- 增加术语说明和功能需求详细描述
- 扩展主流程图解和异常处理机制
- 添加配置与可变项、验收标准等章节
- 补充PowerShell和Shell脚本执行常见问题解决方案
- 优化文档格式和表格展示效果
上级 9d497793
......@@ -13,7 +13,7 @@ usage() {
program_update.sh (PRD 3.3 Backup)
Usage:
./program_update.sh --platform "新统一平台|传统平台" \
./program_update.sh --platform "新统一平台|传统平台|中广核项目" \
--system "会议预定系统|运维集控系统|讯飞转录系统" \
--update "前端更新|后端更新|全量更新" \
[--workdir /home/Update/]
......@@ -24,6 +24,8 @@ Behavior (3.3):
- backup database by finding mysql container and running mysqldump
- tar backup folder to /home/Backup/Bak<timestamp>.tar.gz
- print BACKUP_TAR path for caller to download
Note: 中广核项目作为独立平台类型,--system参数可选(默认为中广核项目)
EOF
}
......@@ -37,6 +39,9 @@ UPDATE_TYPE=""
WORKDIR="/home/Update"
RESTART_ONLY=0
# 中广核项目标记(用于特殊部署方式)
IS_CGN_PROJECT=0
while [[ $# -gt 0 ]]; do
case "$1" in
--platform) PLATFORM="${2:-}"; shift 2;;
......@@ -50,11 +55,25 @@ while [[ $# -gt 0 ]]; do
esac
done
if [[ -z "$PLATFORM" || -z "$SYSTEM" || -z "$UPDATE_TYPE" ]]; then
if [[ -z "$PLATFORM" || -z "$UPDATE_TYPE" ]]; then
usage
exit 2
fi
# 中广核项目作为独立平台类型时,自动设置系统类型
if [[ "$PLATFORM" == "中广核项目" ]]; then
IS_CGN_PROJECT=1
if [[ -z "$SYSTEM" ]]; then
SYSTEM="中广核项目"
fi
else
# 其他平台类型必须指定系统类型
if [[ -z "$SYSTEM" ]]; then
usage
exit 2
fi
fi
# 启动即记录版本与调用参数(方便截图/回溯)
log "program_update.sh version=$SCRIPT_VERSION"
log "Args: platform=$PLATFORM system=$SYSTEM update=$UPDATE_TYPE workdir=$WORKDIR restart_only=$RESTART_ONLY"
......@@ -151,8 +170,8 @@ case "$PLATFORM" in
"讯飞转录系统")
DB_NAME="devops"
# 前端:/var/www/html/web-vue-voice static/ index.html
FRONT_1_BASE="/var/www/html/web-vue-voice"
# 前端:/var/www/html/web-vue-uvoice static/ index.html
FRONT_1_BASE="/var/www/html/web-vue-uvoice"
FRONT_1_GLOBS=("index.html" "static")
# 后端:/var/www/html UbainsDevOps/
......@@ -165,6 +184,18 @@ case "$PLATFORM" in
;;
esac
;;
"中广核项目")
# 中广核项目独立平台类型
DB_NAME="ubains"
# 前台前端:/var/www/java/cims-web
FRONT_1_BASE="/var/www/java/cims-web"
FRONT_1_GLOBS=("index.html" "static")
# 对内后端:/var/www/java/cims-java
BACK_1_BASE="/var/www/java/cims-java"
BACK_1_GLOBS=("cims-java-4.2.2-dm.jar")
;;
"新统一平台")
log "ERROR: 新统一平台的路径映射未在 PRD 中提供,暂无法执行备份。请补充 4.2 映射后再启用。"
exit 3
......@@ -188,22 +219,26 @@ resolve_workdir() {
"$base/dist"
)
# 根据系统类型给出“应该存在的源目录名集合”,用于判定候选目录是否正确
# 根据平台/系统类型给出"应该存在的源目录名集合",用于判定候选目录是否正确
local expected=()
case "$SYSTEM" in
"会议预定系统")
expected=("ubains-web-2.0" "ubains-web-admin" "api-java-meeting2.0" "external-meeting-api")
;;
"运维集控系统")
expected=("web-vue-rms" "cmdb" "UbainsDevOps")
;;
"讯飞转录系统")
expected=("web-vue-voice" "UbainsDevOps")
;;
*)
expected=()
;;
esac
if [[ "$IS_CGN_PROJECT" -eq 1 ]]; then
expected=("cims-web" "cims-java")
else
case "$SYSTEM" in
"会议预定系统")
expected=("ubains-web-2.0" "ubains-web-admin" "api-java-meeting2.0" "external-meeting-api")
;;
"运维集控系统")
expected=("web-vue-rms" "cmdb" "UbainsDevOps")
;;
"讯飞转录系统")
expected=("web-vue-uvoice" "UbainsDevOps")
;;
*)
expected=()
;;
esac
fi
local c hit
for c in "${candidates[@]}"; do
......@@ -612,18 +647,80 @@ restart_services_traditional() {
log "WARN: log file not found: /var/www/html/log/uinfo.log"
fi
;;
*)
log "WARN: 未配置 $SYSTEM 的重启逻辑,跳过重启。"
;;
esac
}
# 中广核项目独立重启逻辑
restart_services_cgn() {
# PRD 3.5:后端更新需要重启服务;前端更新无需重启
if [[ "$UPDATE_TYPE" == "前端更新" ]]; then
log "Skip restart (frontend update only)."
return 0
fi
log "Restart services: 中广核项目"
# 对内后端:优先进入 java 容器内执行 run.sh
log "PRD 3.5 internal (preferred in java container ujava*):"
log " docker exec -it ujavaX bash"
log " cd /var/www/java/cims-java"
log " bash ./run.sh; tail -f log.out"
local java_container=""
if command -v docker >/dev/null 2>&1; then
java_container="$(docker ps --format '{{.Names}}' | grep -E '^ujava[0-9]*$' | head -n 1 || true)"
if [[ -z "$java_container" ]]; then
java_container="$(docker ps -a --format '{{.Names}}' | grep -E '^ujava[0-9]*$' | head -n 1 || true)"
fi
if [[ -n "$java_container" ]]; then
log "Detected java container: $java_container"
log "Restart (in container $java_container): /var/www/java/cims-java/run.sh (force bash)"
docker exec "$java_container" bash -lc "cd /var/www/java/cims-java && bash ./run.sh" || true
log "Verify process (in container $java_container): ps -ef | grep cims-java"
docker exec "$java_container" bash -lc "ps -ef | grep -E 'cims-java' | grep -v grep | head -n 5" || true
else
log "WARN: java container (ujava*) not found, fallback to host restart"
if [[ -d "/var/www/java/cims-java" && -f "/var/www/java/cims-java/run.sh" ]]; then
log "Restart (host fallback): /var/www/java/cims-java/run.sh (force bash)"
( cd /var/www/java/cims-java && bash ./run.sh ) || true
else
log "WARN: cims-java run.sh not found (host fallback)"
fi
fi
else
log "WARN: docker not found, fallback to host restart"
if [[ -d "/var/www/java/cims-java" && -f "/var/www/java/cims-java/run.sh" ]]; then
log "Restart (host fallback): /var/www/java/cims-java/run.sh (force bash)"
( cd /var/www/java/cims-java && bash ./run.sh ) || true
else
log "WARN: cims-java run.sh not found (host fallback)"
fi
fi
# 验证日志
if [[ -f "/var/www/java/cims-java/log.out" ]]; then
log "Verify log: tail -n 50 /var/www/java/cims-java/log.out"
tail -n 50 /var/www/java/cims-java/log.out || true
else
log "WARN: log file not found: /var/www/java/cims-java/log.out"
fi
}
# 在 update 之后调用 3.5
run_post_update_actions() {
case "$PLATFORM" in
"传统平台")
restart_services_traditional
;;
"中广核项目")
restart_services_cgn
;;
*)
log "WARN: 平台 $PLATFORM 暂未实现 3.5 重启逻辑"
;;
......@@ -712,12 +809,37 @@ do_update_traditional() {
esac
}
# 中广核项目独立更新逻辑
do_update_cgn() {
local src_front="${WORKDIR%/}/cims-web"
local src_back="${WORKDIR%/}/cims-java"
local dst_front="/var/www/java/cims-web"
local dst_back="/var/www/java/cims-java"
if [[ "$UPDATE_TYPE" == "前端更新" || "$UPDATE_TYPE" == "全量更新" ]]; then
sync_frontend_preserve "$src_front" "$dst_front" "中广核-前台前端"
fi
if [[ "$UPDATE_TYPE" == "后端更新" || "$UPDATE_TYPE" == "全量更新" ]]; then
sync_overwrite_globs "$src_back" "$dst_back" "中广核-对内后端" "cims-java-4.2.2-dm.jar"
fi
}
case "$PLATFORM" in
"传统平台")
log "Start update (传统平台): system=$SYSTEM update=$UPDATE_TYPE workdir=$WORKDIR"
do_update_traditional
log "Update done (传统平台)."
# 3.5 重启并验证
run_post_update_actions
;;
"中广核项目")
log "Start update (中广核项目): update=$UPDATE_TYPE workdir=$WORKDIR"
do_update_cgn
log "Update done (中广核项目)."
# 3.5 重启并验证
run_post_update_actions
;;
......
......@@ -76,8 +76,8 @@ function Invoke-PlinkCommand {
[Parameter(Mandatory=$true)][string]$Command
)
# 不使用 -batch 参数,允许交互式确认主机密钥
$args = @(
"-batch",
"-ssh",
"-P", "$Port",
"-l", $User,
......@@ -88,20 +88,26 @@ function Invoke-PlinkCommand {
Write-Info "远端执行: $Command"
# 使用临时文件捕获输出
$tmpOut = Join-Path $env:TEMP ("plink_{0}.out.log" -f ([guid]::NewGuid().ToString("N")))
$tmpErr = Join-Path $env:TEMP ("plink_{0}.err.log" -f ([guid]::NewGuid().ToString("N")))
# 使用交互模式,让用户可以确认主机密钥,同时捕获输出
$p = Start-Process -FilePath $PlinkPath -ArgumentList $args -NoNewWindow -Wait -PassThru `
-RedirectStandardOutput $tmpOut -RedirectStandardError $tmpErr
# 读取输出内容
$outText = ""
$errText = ""
if (Test-Path $tmpOut) { $outText = (Get-Content $tmpOut -Raw -Encoding utf8) }
if (Test-Path $tmpErr) { $errText = (Get-Content $tmpErr -Raw -Encoding utf8) }
if (-not [string]::IsNullOrWhiteSpace($outText)) { Write-Host $outText }
if (-not [string]::IsNullOrWhiteSpace($errText)) { Write-Warn $errText }
# 输出错误信息(如果有)
if (-not [string]::IsNullOrWhiteSpace($errText)) {
Write-Host $errText
}
# 清理临时文件
Remove-Item -ErrorAction SilentlyContinue $tmpOut, $tmpErr
if ($p.ExitCode -ne 0) {
......@@ -134,7 +140,6 @@ function Invoke-PscpUpload {
foreach ($lf in $LocalFiles) {
$args = @(
"-batch",
"-P", "$Port",
"-pw", $Password,
$lf,
......@@ -167,7 +172,6 @@ function Invoke-PscpDownload {
$remoteSpec = ("{0}@{1}:{2}" -f $User, $HostName, $RemoteFile)
$args = @(
"-batch",
"-P", "$Port",
"-pw", $Password,
$remoteSpec,
......@@ -189,20 +193,77 @@ Write-Info ("remote_program_update.ps1 version={0}" -f $SCRIPT_VERSION)
Write-Info ("PowerShell={0} Host={1}" -f $PSVersionTable.PSVersion, $env:COMPUTERNAME)
Write-Info ("ScriptPath={0}" -f $PSCommandPath)
$serverIp = Read-NonEmpty "请输入服务器IP"
$sshPortStr = Read-NonEmpty "请输入SSH端口" "22"
$sshPort = [int]$sshPortStr
$username = Read-NonEmpty "请输入用户名" "root"
$securePwd = Read-Host "请输入密码" -AsSecureString
$plainPwd = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePwd))
# 服务器预设选择
$usePreset = Read-Choice "是否使用服务器预设?" @("否(手动输入)", "是(使用预设)")
$serverIp = ""
$sshPortStr = ""
$sshPort = 0
$username = ""
$plainPwd = ""
$remoteDir = ""
if ($usePreset -eq "是(使用预设)") {
$preset = Read-Choice "请选择服务器预设:" @("测试环境-前端服务器", "测试环境-后端服务器")
if ($preset -eq "测试环境-前端服务器") {
$serverIp = "10.126.4.79"
$sshPortStr = "1122"
$sshPort = 1122
$username = "root"
$plainPwd = "Admin@123Admin@123"
$remoteDir = "/home/appadmin/"
Write-Info "已选择测试环境-前端服务器预设"
} else {
$serverIp = "10.126.4.81"
$sshPortStr = "1122"
$sshPort = 1122
$username = "appadmin"
$plainPwd = "CGNadm!@345CGNadm!@345"
$remoteDir = "/home/appadmin/"
Write-Info "已选择测试环境-后端服务器预设"
}
Write-Info "参数确认:IP=$serverIp Port=$sshPort User=$username RemoteDir=$remoteDir"
} else {
$serverIp = Read-NonEmpty "请输入服务器IP"
$sshPortStr = Read-NonEmpty "请输入SSH端口" "22"
$sshPort = [int]$sshPortStr
$username = Read-NonEmpty "请输入用户名" "root"
$securePwd = Read-Host "请输入密码" -AsSecureString
$plainPwd = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePwd))
$remoteDir = Read-NonEmpty "请输入远端文件存放路径" "/home/Update/"
}
$remoteDir = Read-NonEmpty "请输入远端文件存放路径" "/home/Update/"
# 平台类型选择(增加中广核项目选项)
$platformType = Read-Choice "请选择平台类型:" @("新统一平台", "传统平台", "中广核项目")
# 根据平台类型决定是否需要选择系统类型
if ($platformType -eq "中广核项目") {
$systemType = "中广核项目"
Write-Info "已选择中广核项目,系统类型自动设置为:$systemType"
} else {
$systemType = Read-Choice "请选择更新系统类型:" @("会议预定系统", "运维集控系统", "讯飞转录系统")
}
$platformType = Read-Choice "请选择平台类型:" @("新统一平台", "传统平台")
$systemType = Read-Choice "请选择更新系统类型:" @("会议预定系统", "运维集控系统", "讯飞转录系统")
$updateType = Read-Choice "请选择更新类型:" @("前端更新", "后端更新", "全量更新")
Write-Info "参数确认:IP=$serverIp Port=$sshPort User=$username RemoteDir=$remoteDir 平台=$platformType 系统=$systemType 更新=$updateType"
if ($usePreset -eq "否(手动输入)") {
Write-Info "参数确认:IP=$serverIp Port=$sshPort User=$username RemoteDir=$remoteDir 平台=$platformType 系统=$systemType 更新=$updateType"
}
Write-Info "首次连接时会提示确认主机密钥,请选择 'yes' 确认。"
# 判断是否为中广核项目(需要特殊处理)
$isCgnProject = ($platformType -eq "中广核项目")
$sudoPassword = ""
if ($isCgnProject) {
Write-Info "检测到中广核项目,将使用zip压缩包方式部署"
# 如果使用预设且是后端服务器,则已经有密码了,不需要再输入
if ($usePreset -eq "是(使用预设)" -and $preset -eq "测试环境-后端服务器") {
$sudoPassword = $plainPwd
Write-Info "使用预设的sudo密码"
} else {
$sudoPassword = Read-NonEmpty "请输入sudo密码" "CGNadm!@345CGNadm!@345"
}
}
# -------------------- 工具检测 --------------------
$tools = Require-PuttyTools
......@@ -225,8 +286,7 @@ Invoke-PlinkCommand -PlinkPath $plink -HostName $serverIp -Port $sshPort -User $
Write-Info "SSH 连接测试成功。"
# 1) 远端创建目录 + 检查 unzip
Invoke-PlinkCommand -PlinkPath $plink -HostName $serverIp -Port $sshPort -User $username -Password $plainPwd -Command "mkdir -p '$remoteDir'; command -v unzip >/dev/null 2>&1 || (echo '缺少 unzip,请安装:yum install -y unzip 或 apt-get install -y unzip' && exit 2)"
Invoke-PlinkCommand -PlinkPath $plink -HostName $serverIp -Port $sshPort -User $username -Password $plainPwd -Command "mkdir -p '$remoteDir'; command -v unzip >/dev/null 2>&1 || (echo '缺少 unzip,请安装:yum install -y unzip 或 apt-get install -y unzip' && exit 2)"
# ====== 3.2 本地准备:找到 zip 包 + program_update.sh ======
# 约定:zip 压缩包与本 remote_program_update.ps1 同级目录
$zipFiles = @(
......@@ -247,32 +307,49 @@ if (-not (Test-Path $programUpdateSh)) {
throw "未找到 program_update.sh:$programUpdateSh(请放在脚本同级目录)"
}
# ====== 3.2 本地准备结束 ======
# 所有项目统一使用zip压缩包方式
if ($isCgnProject) {
Write-Info "中广核项目部署模式:使用zip压缩包方式"
}
Write-Info "将上传更新包:$zipPath"
Write-Info "将上传更新脚本:$programUpdateSh"
# ====== 3.2 本地准备结束 ======
# 2) 上传 zip + program_update.sh
# 上传 zip + program_update.sh
Invoke-PscpUpload -PscpPath $pscp -HostName $serverIp -Port $sshPort -User $username -Password $plainPwd -LocalFiles @($zipPath, $programUpdateSh) -RemoteDir $remoteDir
# 3) 远端解压(解压到 remoteDir)
# 远端解压(解压到 remoteDir)
$zipName = Split-Path $zipPath -Leaf
Invoke-PlinkCommand -PlinkPath $plink -HostName $serverIp -Port $sshPort -User $username -Password $plainPwd -Command "cd '$remoteDir' && unzip -o '$zipName' -d '$remoteDir'"
# ====== 默认执行 program_update.sh(实现 3.3 备份)并下载备份包到本机脚本目录 ======
# ====== 执行 program_update.sh 并下载备份包 ======
# 额外打印远端 program_update.sh 版本,便于回溯
Invoke-PlinkCommand -PlinkPath $plink -HostName $serverIp -Port $sshPort -User $username -Password $plainPwd -Command "cd '$remoteDir' && ./program_update.sh --version 2>/dev/null || true"
$execCmd = "cd '$remoteDir' && chmod +x ./program_update.sh && ./program_update.sh --platform '$platformType' --system '$systemType' --update '$updateType' --workdir '$remoteDir'"
# 中广核项目需要通过 sudo 切换到 root 用户执行
if ($isCgnProject) {
Write-Info "中广核项目:使用 sudo 执行更新脚本"
$execCmd = "cd '$remoteDir' && echo '$sudoPassword' | sudo -S chmod +x ./program_update.sh && echo '$sudoPassword' | sudo -S env LC_CTYPE=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8 ./program_update.sh --platform '$platformType' --system '$systemType' --update '$updateType' --workdir '$remoteDir'"
} else {
$execCmd = "cd '$remoteDir' && chmod +x ./program_update.sh && ./program_update.sh --platform '$platformType' --system '$systemType' --update '$updateType' --workdir '$remoteDir'"
}
$out = Invoke-PlinkCommand -PlinkPath $plink -HostName $serverIp -Port $sshPort -User $username -Password $plainPwd -Command $execCmd
# 从输出解析 BACKUP_TAR
# program_update.sh 输出格式:BACKUP_TAR=/home/Backup/BakYYYYmmdd_HHMMSS.tar.gz
$backupTar = $null
if ($out -match '(?m)^BACKUP_TAR=(.+)\s*$') {
# 尝试多种匹配模式
if ($out -match 'BACKUP_TAR=([^\s\r\n]+)') {
$backupTar = $Matches[1].Trim()
}
if (-not $backupTar) {
# 输出调试信息
Write-Warn "未找到 BACKUP_TAR,输出内容:"
Write-Host $out
throw "未从远端输出中解析到 BACKUP_TAR。请确认 program_update.sh 已实现 3.3 且会 echo BACKUP_TAR=..."
}
......
# 记录文档
\ No newline at end of file
# 记录文档
## PowerShell和Shell脚本执行常见问题
### 1. PowerShell脚本 - 括号匹配问题
**错误现象**
```
Missing closing '}' in statement block or type definition.
Unexpected token ')' in expression or statement.
```
**原因分析**
- 函数调用时使用了多余的闭合括号
- 示例错误:`Read-Choice "提示" @("选项1", "选项2"))`
- 正确写法:`Read-Choice "提示" @("选项1", "选项2")`
**解决方案**
- 检查函数调用的括号是否成对
- 确保数组参数 `@()` 后面没有多余的 `)`
- 使用IDE或编辑器的括号匹配功能检查
---
### 2. plink - batch模式主机密钥确认问题
**错误现象**
```
FATAL ERROR: Cannot confirm a host key in batch mode
The host key is not cached for this server: 10.126.4.79 (port 1122)
```
**原因分析**
- plink使用 `-batch` 参数时,如果服务器主机密钥未缓存,会自动终止连接
- 首次连接新服务器或端口时,需要手动确认主机密钥
- 使用 `-batch` 参数会跳过交互式确认,导致连接失败
**解决方案**
**方法一:首次手动连接并缓存密钥**
```powershell
# 在脚本外首次手动连接,确认主机密钥
plink -P 1122 root@10.126.4.79 "echo test"
# 输入 yes 确认主机密钥
```
**方法二:使用 -hostkey 参数指定指纹**
```powershell
# 获取服务器指纹后,在脚本中指定
plink -batch -hostkey "ssh-ed25519 255 SHA256:11x1sqNKBdXqD4CGXd8JGfmzgGEjphL7ZIvDg9Yr37c" -P 1122 root@10.126.4.79 "echo test"
```
**方法三:修改plink调用逻辑**
```powershell
# 检测是否为首次连接,提示用户先手动连接
function Test-SshConnection {
param($PlinkPath, $HostName, $Port, $User, $Password)
try {
$args = @("-batch", "-ssh", "-P", "$Port", "-l", $User, "-pw", $Password, $HostName, "echo test")
$p = Start-Process -FilePath $PlinkPath -ArgumentList $args -NoNewWindow -Wait -PassThru -RedirectStandardOutput "$env:TEMP\test.out" -RedirectStandardError "$env:TEMP\test.err"
return $p.ExitCode -eq 0
} catch {
return $false
}
}
# 首次连接失败时提示用户
if (-not (Test-SshConnection ...)) {
Write-Warn "SSH连接失败,可能是首次连接该服务器。"
Write-Info "请先手动执行一次SSH连接以确认主机密钥:"
Write-Info "plink -P $Port $User@$HostName `"echo test`""
Write-Info "或在命令中添加 -hostkey 参数指定主机指纹"
}
```
**预防措施**
- 在文档中说明首次连接需要手动确认主机密钥
- 提供手动预连接的指令
- 考虑使用 `-hostkey` 参数提高自动化程度
---
### 3. plink旧版本不支持-strict-host-key参数
**错误现象**
```
plink: unknown option "-strict-host-key"
```
**原因分析**
- plink 0.83及更早版本不支持 `-strict-host-key` 参数
- 该参数在较新版本的PuTTY中才引入
- 使用旧版本工具时会报"unknown option"错误
**版本检测方法**
```powershell
# 检查plink版本
$plinkVer = & $plink -V 2>&1 | Select-Object -First 1
Write-Info "plink版本: $plinkVer"
# 输出示例:plink: Release 0.83(旧版本,不支持-strict-host-key)
# 输出示例:plink: Release 0.78(新版本,支持-strict-host-key)
```
**解决方案**
**方案一:移除-batch参数,使用交互模式**
```powershell
# 不使用 -batch 参数,允许交互式确认主机密钥
$args = @(
"-ssh",
"-P", "$Port",
"-l", $User,
"-pw", $Password,
$HostName,
$Command
)
# 首次连接时会弹出提示框,用户点击"是"确认
```
**方案二:升级PuTTY到最新版本**
- 下载地址:https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html
- 最新版本支持更多参数,包括 `-strict-host-key``-hostkey`
**方案三:使用Windows自带的OpenSSH(Windows 10+)**
```powershell
# Windows 10+ 自带ssh命令,无需额外安装
ssh -p 1122 root@10.126.4.79 "echo test"
```
**预防措施**
- 在文档中说明工具版本要求
- 提供兼容旧版本的备选方案
- 考虑使用Windows自带OpenSSH替代PuTTY
---
### 4. Edit替换操作导致多行代码合并
**错误现象**
```
Invoke-PlinkCommand ... -Command "echo test" Write-Info "连接成功"
```
**原因分析**
- 使用 `Edit` 工具的 `-replace_all` 参数时
-`-AcceptHostKey:$acceptHostKey` 替换为空字符串
- 导致原本在下一行的代码被合并到函数调用行
**问题示例**
```powershell
# 替换前
Invoke-PlinkCommand ... -Command "echo test" -AcceptHostKey:$acceptHostKey
Write-Info "连接成功"
# 替换-AcceptHostKey:$acceptHostKey为空后
Invoke-PlinkCommand ... -Command "echo test" Write-Info "连接成功"
# 错误!Write-Info被当作参数传给Invoke-PlinkCommand
```
**解决方案**
1. 使用更精确的替换模式,包含前后空格
2. 或者逐行手动修改,避免批量替换
3. 替换后验证语法正确性
**正确做法**
```powershell
# 包含前后空格的精确替换
# 旧:-AcceptHostKey:$acceptHostKey
# 新:(空)
```
**预防措施**
- 使用 `-replace_all` 前仔细检查替换字符串
- 替换后使用语法检查工具验证
- 对于参数替换,考虑包含参数前后的空格或换行符
---
### 5. pscp上传文件夹失败
**错误现象**
```
pscp: C:\Users\...\cims-web: not a regular file
pscp 上传失败,ExitCode=1
```
**原因分析**
- pscp(PuTTY的pscp工具)不支持直接上传文件夹
- 使用 `pscp local_dir remote_dir` 会报错 "not a regular file"
- pscp只能上传单个文件,不能递归上传目录
**解决方案**
**方案一:使用zip压缩包**
```powershell
# 将文件夹打包成zip,然后上传和解压
# 1. 本地打包:将cims-web和cims-java打包成cims-update.zip
# 2. 上传zip文件
Invoke-PscpUpload -PscpPath $pscp -HostName $serverIp -Port $sshPort -User $username -Password $plainPwd -LocalFiles @($zipPath) -RemoteDir $remoteDir
# 3. 远端解压
Invoke-PlinkCommand -PlinkPath $plink -HostName $serverIp -Port $sshPort -User $username -Password $plainPwd -Command "cd '$remoteDir' && unzip -o '$zipName' -d '$remoteDir'"
```
**方案二:使用Windows自带的OpenSSH**
```powershell
# Windows 10+ 自带的scp支持递归上传目录(-r参数)
scp -P 1122 -r cims-web appadmin@10.126.4.81:/home/appadmin/
scp -P 1122 -r cims-java appadmin@10.126.4.81:/home/appadmin/
```
**方案三:使用psftp(PuTTY的SFTP工具)**
```powershell
# psftp支持交互式操作,可以上传目录
pscp -batch -pw password -r local_dir user@host:remote_dir/
```
**推荐做法**
- 统一使用zip压缩包方式部署
- 打包时保持目录结构:zip包内包含cims-web和cims-java目录
- 部署流程:上传zip → 解压 → 执行更新脚本
**预防措施**
- 在文档中明确说明部署包格式要求
- 提供打包脚本或说明文档
- 考虑使用Windows自带的OpenSSH替代PuTTY工具
---
### 6. sudo -i 导致中文参数乱码
**错误现象**
```
-bash: ./program_update.sh --platform 丹核项?--system 丹核项?--update 前更新 --workdir /home/appadmin/: 沜那个文件或目?
```
**原因分析**
- `sudo -i` 启动一个login shell,会重置所有环境变量包括locale设置
- 中文参数在新的shell环境中无法正确解析,变成乱码
- 不同的shell编码设置导致字符映射错误
**解决方案**
**方案一:不使用 -i 参数,直接使用 sudo -S**
```powershell
# 错误写法(使用 -i 启动login shell)
echo '$sudoPassword' | sudo -S -i './program_update.sh --platform ''中广核项目'' ...'
# 正确写法(直接使用 sudo -S,设置locale)
echo '$sudoPassword' | sudo -S env LC_CTYPE=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8 ./program_update.sh --platform '中广核项目' ...
```
**方案二:使用英文参数标识符**
```powershell
# 在脚本内部将英文标识符映射为中文
./program_update.sh --platform 'cgn' --update 'frontend'
# 脚本内部:cgn -> 中广核项目, frontend -> 前端更新
```
**方案三:使用参数文件**
```bash
# 将参数写入文件,避免命令行传递中文
echo "platform=中广核项目" > /tmp/params.txt
sudo -S ./program_update.sh @/tmp/params.txt
```
**推荐做法**
- 使用 `sudo -S env LC_CTYPE=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8` 设置locale
- 或者使用英文参数标识符,在脚本内部映射
- 避免在sudo命令中传递中文参数
---
### 7. PowerShell函数没有正确捕获输出
**错误现象**
```
[WARN] 未找到 BACKUP_TAR,输出内容:
```
**原因分析**
- PowerShell函数直接返回空字符串 `return ""`,没有捕获命令输出
- `Start-Process` 默认不捕获输出,需要使用 `-RedirectStandardOutput` 参数
- 使用 `-NoNewWindow` 时输出会直接显示在控制台,不会被捕获
**问题示例**
```powershell
# 错误写法:没有捕获输出
function Invoke-PlinkCommand {
...
$p = Start-Process -FilePath $PlinkPath -ArgumentList $args -NoNewWindow -Wait -PassThru
return "" # 直接返回空字符串!
}
# 正确写法:使用重定向捕获输出
function Invoke-PlinkCommand {
...
$tmpOut = Join-Path $env:TEMP ("plink_{0}.out.log" -f ([guid]::NewGuid()))
$tmpErr = Join-Path $env:TEMP ("plink_{0}.err.log" -f ([guid]::NewGuid()))
$p = Start-Process -FilePath $PlinkPath -ArgumentList $args -NoNewWindow -Wait -PassThru `
-RedirectStandardOutput $tmpOut -RedirectStandardError $tmpErr
$outText = ""
if (Test-Path $tmpOut) { $outText = (Get-Content $tmpOut -Raw -Encoding utf8) }
Remove-Item -ErrorAction SilentlyContinue $tmpOut, $tmpErr
return $outText
}
```
**解决方案**
1. 使用 `-RedirectStandardOutput` 重定向stdout到临时文件
2. 使用 `-RedirectStandardError` 重定向stderr到临时文件
3. 从临时文件读取内容并返回
4. 清理临时文件
**注意事项**
- 使用 `[guid]::NewGuid()` 生成唯一文件名,避免并发冲突
- 指定 `-Encoding utf8` 正确处理中文输出
- 使用 `-Raw` 参数读取完整内容,避免分行问题
---
### 8. 正则表达式匹配失败问题
**错误现象**
```
输出中包含 "BACKUP_TAR=/home/Backup/Bak20260128_190709.tar.gz"
但正则表达式无法匹配
```
**原因分析**
- 严格匹配模式 `^...$` 要求行首行尾完全匹配
- 输出中可能包含额外的空格、换行符或其他不可见字符
- tar命令的输出可能与预期混合在一起
**解决方案**
**方案一:使用更灵活的匹配模式**
```powershell
# 严格匹配(容易失败)
if ($out -match '(?m)^BACKUP_TAR=(.+)\s*$') {
$backupTar = $Matches[1].Trim()
}
# 灵活匹配(推荐)
if ($out -match 'BACKUP_TAR=([^\s\r\n]+)') {
$backupTar = $Matches[1].Trim()
}
# 或者更宽松的匹配
if ($out -match 'BACKUP_TAR=(\S+)') {
$backupTar = $Matches[1]
}
```
**方案二:添加调试输出**
```powershell
if (-not $backupTar) {
Write-Warn "未找到 BACKUP_TAR,输出内容:"
Write-Host $out
throw "未从远端输出中解析到 BACKUP_TAR..."
}
```
**方案三:使用多模式匹配**
```powershell
# 尝试多种匹配模式
$backupTar = $null
$patterns = @(
'BACKUP_TAR=([^\s\r\n]+)',
'BACKUP_TAR=(\S+)',
'(?m)^BACKUP_TAR=(.+)\s*$'
)
foreach ($pattern in $patterns) {
if ($out -match $pattern) {
$backupTar = $Matches[1].Trim()
break
}
}
if (-not $backupTar) {
throw "未从远端输出中解析到 BACKUP_TAR..."
}
```
**预防措施**
- 使用灵活的正则表达式,避免过度约束
- 添加调试输出,便于定位问题
- 考虑使用 `Trim()` 清理多余的空白字符
---
## powershell脚本常见问题
(已合并到上方)
## shell脚本常见问题
(待补充)
\ No newline at end of file
# 程序更新脚本需求文档
## 需求描述
### 一、需求描述:
能够通过脚本实现在window上远程更新任何系统的前后端包版本,以减少错误,提升效率。
### 二、脚本说明:
脚本路径:
路径1:AuxiliaryTool/ScriptTool/Program_Update/remote_update.ps1
路径2:AuxiliaryTool/ScriptTool/Program_Update/program_update.sh
脚本用途:
remote_update.ps1为Windows执行脚本,用以远程更新程序
program_update.sh为Linux执行脚本,用于在远程服务器上执行更新操作
### 三、功能需求:
#### 3.1.支持远程连接到指定服务器。(remote_program_update.ps1)✅ 已完成
- 输入服务器IP ✅
- ssh端口号 ✅
- 输入用户名密码 ✅
- 输入文件存放路径(默认为/home/Update/) ✅
- 判断平台类型(新统一平台/传统平台) ✅
- 输入更新系统类型(会议预定系统、运维集控系统、讯飞转录系统) ✅
- 输入更新类型(前端更新、后端更新、全量更新) ✅
#### 3.2.能够上传更新压缩文件以及更新脚本。(remote_program_update.ps1)✅ 已完成
- 将当前脚本同级目录下的zip压缩包和program_update.sh脚本上传到指定服务器的指定路径下。✅
- 解压缩压缩包,通过unzip命令解压到指定目录(/home/Update/)。✅
#### 3.3.备份原有数据. (program_update.sh) ✅ 已完成
- 在执行更新操作前,先备份原有的前端或后端文件夹到备份目录(/home/Backup/)。
-- 创建备份目录:
执行mkdir -p /home/Backup/Bak时间戳
-- 备份前端文件夹:
cp -r 根据系统类型和更新类型选择对应的前端路径及文件 备份目录路径
-- 备份后端文件夹:
cp -r 根据系统类型和更新类型选择对应的后端路径及文件 备份目录路径
-- 备份数据库操作如下:
1.查询MySQL容器名称
2.进入MySQL容器
3.执行mysqldump -uroot -p 根据系统类型选择对应的数据库名称 > /备份目录路径/数据库名称_时间戳.sql(数据库密码为:dNrprU&2S)
- 打压缩当前备份包,并导出至桌面
-- 执行tar -czvf /home/Backup/Bak时间戳.tar.gz /home/Backup/Bak时间戳
-- 将压缩包移动到桌面:mv /home/Backup/Bak时间戳.tar.gz remote_update.ps1所在路径
#### 3.4.更新服务(program_update.sh)
- 根据系统类型和更新类型选择对应的更新脚本执行更新操作
- 前端更新操作:
-- 注意:只替换主服务文件,不要影响原有目录下其他文件!!!
-- 传统平台
1.会议预定系统:
- 前台前端:✅ 已完成
cd /home/Update/ubains-web-2.0
mv * /var/www/java/ubains-web-2.0/(覆盖操作)
- 后台前端:✅ 已完成
cd /home/Update/ubains-web-admin
mv * /var/www/java/ubains-web-admin/(覆盖操作)
- 对内后端:✅ 已完成
cd /home/Update/api-java-meeting2.0
mv * /var/www/java/api-java-meeting2.0/(覆盖操作)
- 对外后端:✅ 已完成
cd /home/Update/external-meeting-api
mv * /var/www/java/external-meeting-api/(覆盖操作)
2.运维集控系统:✅ 已完成
- 前端:
cd /home/Update/web-vue-rms
mv * /var/www/html/web-vue-rms/(覆盖操作)
- 后端:
cd /home/Update/
mv * /var/www/html/(覆盖操作)
3.讯飞转录系统:✅ 已完成
- 前端:
cd /home/Update/web-vue-rms
mv * /var/www/html/uvoice/web-vue-uvoice/(覆盖操作)
- 后端:
cd /home/Update/UbainsDevOps
mv * /var/www/html/(覆盖操作)
#### 3.5.重启服务并验证更新是否成功。
- 如果更新类型为前端更新,则无需重启服务
- 如果更新类型为后端更新,则需重启服务
-- 通过系统名称类型重启对应服务
1.会议预定系统:
- cd /var/www/java/external-meeting-api
- ./run.sh;tail -f logs/ubains-INFO-AND-ERROR.log
- docker exec -it ujava2 bash
- cd /var/www/java/api-java-meeting2.0
- ./run.sh;tail -f logs/ubains-INFO-AND-ERROR.log
2.运维集控系统:
- cd /var/www/html
- docker restart upython
- tail -f log/uinfo.log
3.讯飞转录系统:
- cd /var/www/html
- docker restart upython
- tail -f log/uinfo.log
### 四、不同平台的路径映射:
#### 4.1.传统平台路径映射:
# _PRD_远程程序更新脚本需求文档
> 版本:V2.0
> 更新日期:2026-01-28
> 适用范围:远程程序更新自动化(Linux服务器程序更新)
> 实现脚本:
> - `远程更新程序/remote_program_update.ps1` (Windows版本 - 远程更新入口)
> - `远程更新程序/program_update.sh` (Linux版本 - 目标服务器部署脚本)
---
## 1. 背景与目标
### 1.1 背景
当前程序版本更新需要运维人员手动登录目标服务器,执行备份、文件替换、服务重启等一系列操作。这种方式效率低下、容易出错,且无法快速响应多服务器的批量更新需求。同时,更新过程中需要保留备份以便回滚,但手动备份容易遗漏关键文件。
### 1.2 目标
实现一套远程程序自动化更新工具,具备:
- 支持从Windows本地主机远程更新Linux服务器上的应用程序
- 支持多种系统类型(会议预定系统、运维集控系统、讯飞转录系统)
- 支持多种更新类型(前端更新、后端更新、全量更新)
- 自动备份前端/后端/数据库关键文件
- 自动探测更新包目录(适配多层解压结构)
- 安全覆盖文件(保护备份目录Bak*/bak*/new)
- 自动重启相关服务并验证
- 备份包自动下载到本地Windows端
- 支持仅重启模式(--restart-only,跳过备份/更新)
### 1.3 双脚本说明
| 脚本 | 文件名 | 运行环境 | 版本号 | 说明 |
|------|--------|----------|--------|------|
| Windows入口脚本 | remote_program_update.ps1 | Windows 10+ | 1.0.0 | 交互式采集参数,上传更新包,调用远端脚本,下载备份 |
| Linux部署脚本 | program_update.sh | Linux 服务器 | 1.0.0 | 在服务器上执行备份、更新、重启等实际操作 |
---
## 2. 总体范围
### 2.1 纳入更新对象
#### 2.1.1 平台类型
1. **传统平台** (已实现)
- 目录结构:`/var/www/java/``/var/www/html/`
- 支持:会议预定系统、运维集控系统、讯飞转录系统
2. **新统一平台** (未实现)
- 目录结构:`/data/services/``/data/`
- 待PRD提供路径映射后实现
3. **中广核项目** (已实现)
- 目录结构:`/var/www/java/`
- 支持:前端(cims-web)、后端(cims-java)
- 特殊部署方式:无需zip打包,直接上传文件夹
#### 2.1.2 支持的系统类型
| 系统名称 | 数据库名称 | 前端数量 | 后端数量 | 适用平台 |
|----------|-----------|---------|---------|---------|
| 会议预定系统 | ubains | 2套(前台+后台) | 2套(对内+对外) | 传统平台 |
| 运维集控系统 | devops | 1套 | 2个目录(cmdb+UbainsDevOps) | 传统平台 |
| 讯飞转录系统 | devops | 1套 | 1个目录(UbainsDevOps) | 传统平台 |
| 中广核项目 | ubains | 1套(cims-web) | 1套(cims-java) | 中广核项目 |
#### 2.1.3 服务器预设信息
| 项目 | 环境 | 服务器类型 | IP地址 | SSH端口 | 用户名 | 密码 | 存放目录 |
|------|------|-----------|--------|---------|--------|------|---------|
| 中广核项目 | 测试环境 | 前端服务器 | 10.126.4.79 | 1122 | root | Admin@123Admin@123 | /home/appadmin/ |
| 中广核项目 | 测试环境 | 后端服务器 | 10.126.4.81 | 1122 | appadmin | CGNadm!@345CGNadm!@345 | /home/appadmin/ |
#### 2.1.3 更新类型
| 更新类型 | 说明 | 需要重启 |
|----------|------|----------|
| 前端更新 | 仅更新前端文件 | 否 |
| 后端更新 | 仅更新后端文件/目录 | 是 |
| 全量更新 | 更新前端和后端 | 是 |
### 2.2 不在本期范围(可扩展)
- 多服务器并发更新
- 自动回滚功能
- 更新前自动健康检查
- 统一告警通道(钉钉/邮件/短信)
- Web界面操作
- 新统一平台路径映射(待PRD补充)
---
## 3. 术语说明
| 术语 | 说明 |
|------|------|
| 平台类型 | 目标服务器的部署架构,分为传统平台和新统一平台 |
| 更新包目录 | 解压后包含程序文件的目录,支持多层结构(update/子目录) |
| 工作目录(workdir) | 更新包所在的基础目录,默认`/home/Update` |
| 前端安全覆盖 | 只覆盖主文件(index.html、*.js、static/),保留备份目录 |
| 后端目录型覆盖 | 覆盖目录内同名项,不清空目标目录,排除备份目录 |
| 保护目录 | `Bak*``bak*``new`,更新过程中会被临时移出并恢复 |
| 重启仅模式 | `--restart-only`参数,仅执行服务重启逻辑,跳过备份/更新/打包 |
---
## 4. 功能需求
### 4.1 Windows入口脚本 (remote_program_update.ps1)
#### 4.1.1 依赖检查
**检查项:**
- PowerShell 5.1+
- PuTTY工具:plink.exe、pscp.exe
**plink.exe/pscp.exe 查找优先级:**
1. 脚本同目录下
2. 系统 PATH 中
**处理策略:**
- 未检测到依赖工具时,提示用户安装PuTTY或放置工具到脚本目录
- 输出详细的错误信息
#### 4.1.2 参数采集(交互式)
| 参数 | 说明 | 默认值 |
|------|------|--------|
| 服务器预设 | 是否使用预设服务器信息 | 可选(是/否) |
| 预设服务器 | 选择预设服务器 | 测试环境-前端服务器/测试环境-后端服务器 |
| 服务器IP | 目标服务器IP地址 | 必填 |
| SSH端口 | SSH连接端口 | 22 |
| 用户名 | SSH登录用户名 | root |
| 密码 | SSH登录密码 | 必填 |
| 远端目录 | 文件存放路径 | /home/Update/ |
| 平台类型 | 新统一平台/传统平台/中广核项目 | 必选 |
| 系统类型 | 会议预定/运维集控/讯飞转录(中广核项目自动设置) | 必选 |
| 更新类型 | 前端更新/后端更新/全量更新 | 必选 |
**说明**
- 选择"中广核项目"平台类型时,系统类型自动设置为"中广核项目",无需手动选择
- 使用服务器预设时,会自动填入对应的服务器信息,无需手动输入
#### 4.1.3 本地文件准备
**检查项:**
- 检测脚本同目录下的 `*.zip` 压缩包
- 检测脚本同目录下的 `program_update.sh`
**处理策略:**
- 未找到zip包时终止并提示
- 检测到多个zip包时使用第一个并警告
- 未找到program_update.sh时终止并提示
#### 4.1.4 文件上传
**上传内容:**
1. zip压缩包
2. program_update.sh脚本
**上传方式:**
- 使用 `pscp.exe` (PuTTY工具)
- 目标目录:用户指定的远端目录
#### 4.1.5 远端命令执行
**执行方式:**
- 使用 `plink.exe` (PuTTY工具)
- 执行命令包括:
1. 创建远端目录并检查unzip命令
2. 解压zip包到远端目录
3. 显示program_update.sh版本信息
4. 执行program_update.sh并捕获输出
**命令示例:**
```bash
mkdir -p '$remoteDir'
unzip -o '$zipName' -d '$remoteDir'
./program_update.sh --platform '$platformType' --system '$systemType' --update '$updateType' --workdir '$remoteDir'
```
#### 4.1.6 备份包下载
**解析逻辑:**
- 从program_update.sh输出中解析 `BACKUP_TAR=...`
- 使用正则表达式匹配:`(?m)^BACKUP_TAR=(.+)\s*$`
**下载方式:**
- 使用 `pscp.exe` 远程下载
- 目标目录:Windows端脚本目录
- 文件名:保持原始备份包名称
---
### 4.2 Linux部署脚本 (program_update.sh)
#### 4.2.1 参数解析
| 参数 | 说明 | 默认值 | 必需 |
|------|------|--------|------|
| --platform | 平台类型(传统平台/新统一平台) | 无 | 是 |
| --system | 系统类型 | 无 | 是 |
| --update | 更新类型 | 无 | 是 |
| --workdir | 更新包工作目录 | /home/Update | 否 |
| --restart-only | 仅重启模式(跳过备份/更新) | 关闭 | 否 |
| --version/-v | 显示版本号 | 无 | 否 |
| -h/--help | 显示帮助信息 | 无 | 否 |
#### 4.2.2 日志与审计
**日志输出:**
- 输出到标准错误(stderr)
- 时间戳格式:`[YYYY-MM-DD HH:MM:SS]`
- 记录内容:
- 脚本版本和启动参数
- 各阶段执行状态
- 备份目录路径
- 工作目录解析结果
- 更新操作详情
- 重启服务详情
**最终输出:**
```bash
BACKUP_DIR=/home/Backup/Bak20260128_120000
BACKUP_TAR=/home/Backup/Bak20260128_120000.tar.gz
```
#### 4.2.3 备份功能(PRD 3.3)
**备份目录结构:**
```
/home/Backup/Bak{timestamp}/
├── frontend/
│ ├── {前端目录1}/
│ └── {前端目录2}/
├── backend/
│ ├── {后端目录1}/
│ └── {后端目录2}/
└── db/
└── {数据库名}_{timestamp}.sql
```
**备份策略:**
| 更新类型 | 前端备份 | 后端备份 | 数据库备份 |
|----------|----------|----------|------------|
| 前端更新 | ✅ | ❌ | ✅ |
| 后端更新 | ❌ | ✅ | ✅ |
| 全量更新 | ✅ | ✅ | ✅ |
**前端备份(会议预定系统):**
- 前台前端:`/var/www/java/ubains-web-2.0/index.html``static/``*.js`
- 后台前端:`/var/www/java/ubains-web-admin/index.html``static/`
**前端备份(运维集控系统):**
- 前端:`/var/www/html/web-vue-rms/index.html``static/`
**前端备份(讯飞转录系统):**
- 前端:`/var/www/html/web-vue-uvoice/index.html``static/`
**后端备份(会议预定系统):**
- 对内后端:`/var/www/java/api-java-meeting2.0/*.jar`
- 对外后端:`/var/www/java/external-meeting-api/*.jar`
**后端备份(运维集控系统):**
- 后端:`/var/www/html/cmdb/``/var/www/html/UbainsDevOps/`
**后端备份(讯飞转录系统):**
- 后端:`/var/www/html/UbainsDevOps/`
**数据库备份:**
- 数据库密码:`dNrprU&2S`(PRD指定固定值)
- 备份命令:
```bash
docker exec {mysql容器} sh -lc "mysqldump -uroot -p'密码' {数据库名}" > 备份文件.sql
```
- MySQL容器查找:`docker ps --format '{{.Names}}' | grep -Eiw 'mysql|mariadb'`
**备份打包:**
```bash
tar -czvf /home/Backup/Bak{timestamp}.tar.gz /home/Backup/Bak{timestamp}/
```
#### 4.2.4 工作目录自动探测
**探测逻辑:**
1. 检查基础目录本身
2. 检查常见子目录:`update/`、`Update/`、`package/`、`dist/`
**判定标准:**
根据系统类型,命中任意一个关键目录即认为是正确workdir:
| 系统类型 | 关键目录 |
|----------|----------|
| 会议预定系统 | ubains-web-2.0, ubains-web-admin, api-java-meeting2.0, external-meeting-api |
| 运维集控系统 | web-vue-rms, cmdb, UbainsDevOps |
| 讯飞转录系统 | web-vue-uvoice, UbainsDevOps |
**目的:**
- 适配解压后多一层 `update/` 子目录的情况
- 避免用户手动调整workdir参数
#### 4.2.5 更新功能(PRD 3.4)
**前端安全覆盖(sync_frontend_preserve):**
- **覆盖内容:**
- `index.html`(如果存在)
- 根目录 `*.js` 文件
- `static/` 目录内容(不同步目录本身)
- **保护机制:**
1. 更新前临时移出目标目录的 `Bak*`、`bak*`、`new` 目录
2. 执行覆盖操作
3. 更新后恢复被保护的目录
- **static目录同步:**
- 优先使用 `rsync`(不使用 `--delete`,排除 `Bak*/bak*/new`)
- 回退到 `cp` 命令(逐项复制,排除备份目录)
**后端glob覆盖(sync_overwrite_globs):**
- **适用场景:** 会议预定系统的jar文件更新
- **覆盖规则:**
- 仅覆盖匹配glob的文件(如 `*.jar`)
- 先删除同名项,再复制新文件
- 避免匹配 `jar2`、`jarbak`、`jarBak` 等文件
- **匹配模式:** 严格的 `*.jar`(只匹配以 `.jar` 结尾)
**后端目录型覆盖(sync_backend_dir_preserve):**
- **适用场景:** 运维集控/讯飞转录系统的目录型后端
- **覆盖规则:**
- 覆盖源目录内的所有项到目标目录
- 不清空目标目录(不使用 `rsync --delete`)
- 排除 `Bak*/bak*/new` 目录
- **同步方式:**
- 优先使用 `rsync -a`(排除备份目录)
- 回退到逐项覆盖复制
#### 4.2.6 服务重启(PRD 3.5)
**会议预定系统:**
1. **对外后端(宿主机):**
- 路径:`/var/www/java/external-meeting-api`
- 执行:`sh ./run.sh`
- 验证:30秒内检测进程 `ubains-meeting-api-1.0-SNAPSHOT.jar`
2. **对内后端(容器内优先):**
- 检测Java容器:`docker ps --format '{{.Names}}' | grep -E '^ujava[0-9]*$'`
- 容器内执行:`docker exec {容器} bash -lc "cd /var/www/java/api-java-meeting2.0 && bash ./run.sh"`
- 回退到宿主机:如果容器不存在,直接在宿主机执行
- 验证:`docker exec {容器} ps -ef | grep -E 'api-java-meeting2\.0|meeting'`
3. **日志验证:**
- 对外后端:`tail -n 50 /var/www/java/external-meeting-api/logs/ubains-INFO-AND-ERROR.log`
- 对内后端:`tail -n 50 /var/www/java/api-java-meeting2.0/logs/ubains-INFO-AND-ERROR.log`
**运维集控系统/讯飞转录系统:**
1. **重启容器:**
- 命令:`docker restart upython`
- 仅在后端更新/全量更新时执行
2. **日志验证:**
- 路径:`/var/www/html/log/uinfo.log`
- 执行:`tail -n 50 /var/www/html/log/uinfo.log`
**前端更新跳过重启:**
- 如果更新类型为"前端更新",不执行任何重启操作
---
## 5. 主流程
### 5.1 Windows入口脚本流程
```
┌─────────────────────────────────────────────────────────────┐
│ Windows端远程程序更新流程 │
└─────────────────────────────────────────────────────────────┘
1. [启动] 执行 remote_program_update.ps1
2. [检测] 依赖检查(plink.exe、pscp.exe)
│ └─ 缺失 → 终止并提示安装
3. [采集] 交互式输入参数
│ - 服务器IP、SSH端口、用户名、密码
│ - 远端目录、平台类型、系统类型、更新类型
4. [准备] 检查本地文件
│ - 脚本同目录的 *.zip 压缩包
│ - program_update.sh 脚本
│ └─ 缺失 → 终止
5. [测试] SSH连接测试
│ └─ 失败 → 终止
6. [上传] 使用pscp上传文件到远端目录
│ - *.zip 压缩包
│ - program_update.sh
7. [解压] 远端解压zip包
8. [执行] 调用远端program_update.sh
│ - 传入平台、系统、更新类型、workdir参数
│ - 捕获输出
9. [解析] 从输出中解析 BACKUP_TAR 路径
│ └─ 未找到 → 终止
10. [下载] 使用pscp下载备份包到Windows端
11. [完成] 输出完成信息
```
### 5.2 Linux部署脚本流程
```
┌─────────────────────────────────────────────────────────────┐
│ Linux端程序部署流程 │
└─────────────────────────────────────────────────────────────┘
1. [启动] 接收参数(platform、system、update、workdir)
2. [判断] 是否为 --restart-only 模式?
│ 是 → 跳转到步骤9
│ 否 → 继续
3. [创建] 备份目录 /home/Backup/Bak{timestamp}/
4. [备份] 根据更新类型执行备份
│ ├─ 前端更新/全量更新 → 备份前端文件
│ ├─ 后端更新/全量更新 → 备份后端文件
│ └─ 备份数据库(MySQL容器)
5. [探测] 自动解析workdir(适配多层update/子目录)
6. [更新] 根据系统和更新类型执行更新
│ ├─ 前端更新 → 前端安全覆盖(保留Bak*/bak*/new)
│ ├─ 后端更新 → 后端glob覆盖/目录型覆盖
│ └─ 全量更新 → 前端+后端更新
7. [重启] 根据系统类型执行服务重启
│ ├─ 会议预定系统 → 对外后端(宿主机)+ 对内后端(容器)
│ ├─ 运维集控系统 → docker restart upython
│ └─ 讯飞转录系统 → docker restart upython
8. [验证] 输出服务日志片段
9. [打包] 打包备份目录为 tar.gz
10. [输出] 输出 BACKUP_DIR 和 BACKUP_TAR 路径
11. [完成] 退出
```
---
## 6. 配置与可变项
### 6.1 传统平台路径映射
| 系统名称 | 前端路径及文件 | 后端路径及文件 | 数据库名称 |
|:会议预定系统: | :前台前端:/var/www/java/ubains-web-2.0 *.js static/ index.html 后台前端:/var/www/java/ubains-web-admin index.html static/: | :对内后端:/var/www/java/api-java-meeting2.0 *.jar 对外后端:/var/www/java/external-meeting-api *.jar: | :数据库:ubains: |
|:运维集控系统: | :前端:/var/www/html/web-vue-rms static/ index.html: | :后端:/var/www/html cmdb/ UbainsDevOps/: | :数据库:devops: |
|:讯飞转录系统: | :前端:/var/www/html/web-vue-voice static/ index.html: | :后端:/var/www/html UbainsDevOps/: | :数据库:devops: |
|----------|---------------|---------------|-----------|
| 会议预定系统 | 前台前端:<br>/var/www/java/ubains-web-2.0<br>- index.html<br>- static/<br>- *.js<br><br>后台前端:<br>/var/www/java/ubains-web-admin<br>- index.html<br>- static/ | 对内后端:<br>/var/www/java/api-java-meeting2.0<br>- *.jar<br><br>对外后端:<br>/var/www/java/external-meeting-api<br>- *.jar | ubains |
| 运维集控系统 | 前端:<br>/var/www/html/web-vue-rms<br>- index.html<br>- static/ | 后端:<br>/var/www/html<br>- cmdb/<br>- UbainsDevOps/ | devops |
| 讯飞转录系统 | 前端:<br>/var/www/html/web-vue-uvoice<br>- index.html<br>- static/ | 后端:<br>/var/www/html<br>- UbainsDevOps/ | devops |
### 6.2 中广核项目路径映射
| 类型 | 源目录(workdir下) | 目标目录 | 备份glob |
|------|-------------------|----------|----------|
| 前端 | cims-web | /var/www/java/cims-web | index.html, static |
| 后端 | cims-java | /var/www/java/cims-java | cims-java-4.2.2-dm.jar |
**数据库名**: ubains
**部署方式**
- 使用zip压缩包(cims-update.zip),包含cims-web和cims-java目录
- 前端服务器:root用户,无需sudo切换
- 后端服务器:appadmin用户,需要通过sudo -i切换到root用户
- sudo密码:CGNadm!@345CGNadm!@345
### 6.3 更新包源目录结构
| 系统名称 | 更新包目录(workdir下) |
|----------|----------------------|
| 会议预定系统 | - ubains-web-2.0/<br>- ubains-web-admin/<br>- api-java-meeting2.0/<br>- external-meeting-api/ |
| 运维集控系统 | - web-vue-rms/<br>- cmdb/<br>- UbainsDevOps/ |
| 讯飞转录系统 | - web-vue-uvoice/<br>- UbainsDevOps/ |
| 中广核项目 | - cims-web/<br>- cims-java/ |
### 6.4 数据库配置
| 配置项 | 值 |
|--------|-----|
| MySQL root密码 | dNrprU&2S |
| 备份命令 | mysqldump -uroot -p'密码' 数据库名 |
| 容器查找 | docker ps \| grep -Eiw 'mysql\|mariadb' |
### 6.5 可配置项
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| WORKDIR | /home/Update | 更新包工作目录 |
| BACKUP_ROOT | /home/Backup | 备份根目录 |
| MYSQL_ROOT_PASSWORD | dNrprU&2S | MySQL root密码 |
| 重启验证超时 | 30秒 | 进程检测等待时间 |
---
## 7. 异常处理与容错要求
### 7.1 Windows入口脚本异常处理
1. **依赖工具缺失**
- 输出详细错误信息
- 提示安装PuTTY或放置工具到脚本目录
- 终止操作
2. **本地文件缺失**
- 未找到zip包:提示并终止
- 未找到program_update.sh:提示并终止
- 多个zip包:使用第一个并警告
3. **SSH连接失败**
- 输出plink执行错误
- 提示检查IP、端口、密码、网络连通性
- 终止操作
4. **文件上传失败**
- 输出pscp执行错误
- 终止操作
5. **远端脚本执行失败**
- 输出program_update.sh错误信息
- 终止操作
6. **未解析到BACKUP_TAR**
- 提示检查远端脚本输出
- 终止操作
7. **备份包下载失败**
- 输出pscp执行错误
- 终止操作
### 7.2 Linux部署脚本异常处理
1. **参数缺失**
- platform、system、update未指定时显示帮助
- 退出码2
2. **不支持的平台/系统**
- 输出错误信息
- 退出码3
3. **备份目录不存在**
- 自动创建(mkdir -p)
4. **源目录不存在**
- 输出警告并跳过(backup_globs)
- 输出错误并返回10(更新函数)
5. **源目录为空**
- 输出错误并返回11(sync_backend_dir_preserve)
6. **未匹配到任何文件**
- 输出警告(backup_globs)
- 输出错误并返回11(sync_overwrite_globs)
7. **MySQL容器未找到**
- 输出警告并跳过数据库备份
8. **Docker未安装**
- 输出警告并跳过容器相关操作
9. **rsync不存在**
- 自动回退到cp命令
10. **服务重启失败**
- 记录退出码
- 继续后续操作(不阻塞)
### 7.3 保护机制
1. **前端更新保护目录**
- `Bak*``bak*``new` 目录在更新前临时移出
- 更新后恢复
- 如果更新包包含同名目录,优先保留原备份目录
2. **后端更新排除目录**
- rsync使用 `--exclude 'Bak*/' --exclude 'bak*/' --exclude 'new/'`
- cp命令跳过这些目录
3. **static目录特殊处理**
- 只同步内容,不删除目录本身
- 避免影响目标目录其他内容
---
## 8. 交付物
### 8.1 脚本文件
1. **remote_program_update.ps1** - Windows版本远程更新入口脚本(在Windows主机执行)
2. **program_update.sh** - Linux版本部署脚本(传输到目标服务器后执行)
### 8.2 脚本用途说明
- **remote_program_update.ps1**
- 从Windows主机远程更新Linux服务器上的程序
- 交互式采集参数
- 上传更新包和部署脚本
- 调用远端部署脚本
- 下载备份包到本地
- **program_update.sh**
- 在Linux服务器上执行实际更新操作
- 支持命令行参数和重启仅模式
- 执行备份、更新、重启、打包等操作
### 8.3 依赖工具
- **Windows版本**
- PowerShell 5.1+
- PuTTY工具(plink.exe、pscp.exe)
- **Linux版本**
- Bash 4.0+
- Docker(用于MySQL容器操作和服务重启)
- rsync(可选,用于高效文件同步)
- unzip(用于解压更新包)
### 8.4 交付物清单
**Windows端:**
- 远程更新脚本:`remote_program_update.ps1`
- 下载的备份包:`Bak{timestamp}.tar.gz`(与脚本同目录)
**Linux端:**
- 部署脚本:`program_update.sh`
- 更新包:用户上传的zip解压内容
- 备份包:`/home/Backup/Bak{timestamp}.tar.gz`
---
## 9. 验收标准
### 9.1 Windows入口脚本验收
1. 能正确检测plink.exe和pscp.exe依赖
2. 能交互式采集所有必需参数
3. 能检测本地zip包和program_update.sh
4. 能成功SSH连接到目标服务器
5. 能上传文件到远端目录
6. 能执行远端解压和脚本调用
7. 能正确解析BACKUP_TAR路径
8. 能下载备份包到Windows端
### 9.2 Linux部署脚本验收
1. 能正确解析命令行参数
2. 能根据系统类型和更新类型备份对应文件
3. 能自动探测workdir(支持多层子目录)
4. 能安全更新前端文件(保护备份目录)
5. 能正确更新后端文件(jar/目录)
6. 能根据系统类型重启对应服务
7. 能验证服务日志输出
8. 能打包备份目录并输出路径
### 9.3 三种系统类型验收
**会议预定系统:**
1. 前端更新:
- 备份前端文件(前台+后台)
- 更新前端文件(保护Bak*/bak*/new)
- 不重启服务
- 备份数据库
2. 后端更新:
- 备份后端文件(对内+对外)
- 更新jar文件(严格匹配*.jar)
- 重启对外后端(宿主机)
- 重启对内后端(容器优先)
- 验证进程和日志
- 备份数据库
3. 全量更新:
- 执行前端+后端更新
- 执行所有重启操作
**运维集控系统:**
1. 前端更新:
- 备份前端文件
- 更新前端文件
- 不重启服务
- 备份数据库
2. 后端更新:
- 备份后端目录(cmdb+UbainsDevOps)
- 更新后端目录(不清空,排除备份目录)
- 重启upython容器
- 验证日志
- 备份数据库
**讯飞转录系统:**
1. 前端更新:
- 备份前端文件(/var/www/html/web-vue-uvoice)
- 更新前端文件
- 不重启服务
- 备份数据库
2. 后端更新:
- 备份后端目录(UbainsDevOps)
- 更新后端目录
- 重启upython容器
- 验证日志
- 备份数据库
### 9.4 异常场景验收
1. 依赖工具缺失时有明确提示
2. SSH连接失败时有详细错误信息
3. 源目录不存在时正确处理
4. 未匹配到文件时有警告或错误
5. MySQL容器不存在时跳过数据库备份
6. rsync不存在时回退到cp命令
7. 保护目录Bak*/bak*/new正确保留
### 9.5 --restart-only模式验收
1. 跳过备份目录创建
2. 跳过workdir解析
3. 跳过备份/更新操作
4. 跳过备份打包
5. 仅执行服务重启逻辑
6. 正确重启会议预定/运维集控/讯飞转录系统服务
---
## 10. 需求规范
## 规范获取
代码规范:Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md
问题总结:Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md
方法总结:Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md
文档规范:Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md
测试规范:Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md
\ No newline at end of file
- 代码规范:`Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 问题总结:`Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 方法总结:`Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 文档规范:`Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 测试规范:`Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
# _PRD_远程程序更新脚本需求文档 - 计划执行
> 版本:V2.0
> 更新日期:2026-01-28
> 对应需求文档:`_PRD_程序更新脚本需求文档.md`
> 实现脚本:
> - `远程更新程序/remote_program_update.ps1` (Windows版本 - 远程更新入口)
> - `远程更新程序/program_update.sh` (Linux版本 - 目标服务器部署脚本)
---
## 1. 实现概述
### 1.1 实现状态
| 需求模块 | 状态 | 说明 |
|----------|------|------|
| Windows入口脚本 (remote_program_update.ps1) | ✅ 已完成 | 支持服务器预设、平台类型选择、中广核项目特殊处理 |
| Linux部署脚本 (program_update.sh) | ✅ 已完成 | 支持传统平台、中广核项目独立平台类型 |
| 传统平台支持 | ✅ 已完成 | 支持三种系统类型(会议预定、运维集控、讯飞转录) |
| 中广核项目支持 | ✅ 已完成 | 作为独立平台类型,特殊部署方式 |
| 新统一平台支持 | ❌ 未实现 | 待PRD提供路径映射 |
| 服务器预设功能 | ✅ 已完成 | 支持中广核项目测试环境前端/后端服务器预设 |
### 1.2 脚本版本信息
| 脚本 | 版本号 | 版本变量位置 |
|------|--------|-------------|
| remote_program_update.ps1 | 1.0.0 | 第14行:`$SCRIPT_VERSION = "1.0.0"` |
| program_update.sh | 1.0.0 | 第6行:`SCRIPT_VERSION="1.0.0"` |
---
## 2. Windows入口脚本实现详情 (remote_program_update.ps1)
### 2.1 文件结构
```
remote_program_update.ps1 (281行)
├── 1-8行:文件头和依赖设置
├── 10-32行:日志和输入辅助函数
│ ├── Write-Info / Write-Warn / Write-Err
│ ├── Read-NonEmpty(带默认值输入)
│ └── Read-Choice(选择菜单)
├── 34-67行:工具检测函数
│ ├── Find-Tool(查找plink/pscp)
│ └── Require-PuttyTools(依赖检查)
├── 69-150行:SSH/SCP封装函数
│ ├── Invoke-PlinkCommand(远端命令执行)
│ ├── Invoke-PscpUpload(上传)
│ └── Invoke-PscpDownload(下载)
├── 152-217行:主流程 - 参数采集
│ ├── 版本记录输出
│ ├── 服务器参数输入
│ ├── 平台/系统/更新类型选择
│ └── 工具检测和版本记录
├── 219-252行:主流程 - 文件准备与上传
│ ├── 连接测试
│ ├── 远端目录创建和unzip检查
│ ├── 本地zip包和脚本检测
│ ├── 上传zip和program_update.sh
│ └── 远端解压
└── 254-281行:主流程 - 执行与下载
├── 显示远端脚本版本
├── 执行program_update.sh
├── 解析BACKUP_TAR
└── 下载备份包
```
### 2.2 关键函数实现
#### 2.2.1 Find-Tool (49-57行)
**功能**: 查找命令行工具
**查找顺序**:
1. 系统PATH中的命令
2. 脚本同目录下的工具
**返回**: 工具完整路径或null
#### 2.2.2 Require-PuttyTools (59-67行)
**功能**: 检测PuTTY工具依赖
**检查项**: plink.exe、pscp.exe
**失败处理**: 抛出异常并提示安装位置
#### 2.2.3 Invoke-PlinkCommand (69-112行)
**功能**: 通过plink执行远端命令
**参数**: PlinkPath、HostName、Port、User、Password、Command
**实现细节**:
- 使用Start-Process异步执行
- 重定向stdout/stderr到临时文件
- 使用UTF-8编码读取输出
- 返回stdout内容
- 退出码非0时抛出异常
#### 2.2.4 Invoke-PscpUpload (114-150行)
**功能**: 通过pscp上传文件
**参数**: PscpPath、HostName、Port、User、Password、LocalFiles、RemoteDir
**实现细节**:
- 支持多文件上传(foreach循环)
- 上传前检查本地文件存在性
- 退出码非0时抛出异常
#### 2.2.5 Invoke-PscpDownload (152-182行)
**功能**: 通过pscp下载文件
**参数**: PscpPath、HostName、Port、User、Password、RemoteFile、LocalDir
**实现细节**:
- 自动创建本地目录
- 退出码非0时抛出异常
### 2.3 主流程实现
#### 2.3.1 版本记录 (187-190行)
```powershell
Write-Info ("remote_program_update.ps1 version={0}" -f $SCRIPT_VERSION)
Write-Info ("PowerShell={0} Host={1}" -f $PSVersionTable.PSVersion, $env:COMPUTERNAME)
Write-Info ("ScriptPath={0}" -f $PSCommandPath)
```
#### 2.3.2 参数采集 (192-205行)
- 服务器IP:必填
- SSH端口:默认22
- 用户名:默认root
- 密码:必填(使用SecureString输入)
- 远端目录:默认/home/Update/
- 平台类型:选择菜单(新统一平台/传统平台)
- 系统类型:选择菜单(会议预定/运维集控/讯飞转录)
- 更新类型:选择菜单(前端更新/后端更新/全量更新)
#### 2.3.3 本地文件检测 (230-252行)
```powershell
$zipFiles = Get-ChildItem -Path $PSScriptRoot -Filter *.zip -File
$programUpdateSh = Join-Path $PSScriptRoot "program_update.sh"
```
- 未找到zip:抛出异常
- 多个zip:使用第一个并警告
- 未找到program_update.sh:抛出异常
#### 2.3.4 连接测试 (224行)
```powershell
Invoke-PlinkCommand ... "echo Connected; uname -a"
```
#### 2.3.5 远端操作 (228-259行)
1. 创建目录并检查unzip
2. 上传zip和program_update.sh
3. 解压zip到远端目录
#### 2.3.6 执行部署脚本 (261-277行)
```powershell
$execCmd = "cd '$remoteDir' && chmod +x ./program_update.sh && ./program_update.sh --platform '$platformType' --system '$systemType' --update '$updateType' --workdir '$remoteDir'"
$out = Invoke-PlinkCommand ... $execCmd
```
#### 2.3.7 解析BACKUP_TAR (269-277行)
```powershell
if ($out -match '(?m)^BACKUP_TAR=(.+)\s*$') {
$backupTar = $Matches[1].Trim()
}
```
#### 2.3.8 下载备份包 (279-281行)
```powershell
Invoke-PscpDownload ... -RemoteFile $backupTar -LocalDir $PSScriptRoot
```
---
## 3. Linux部署脚本实现详情 (program_update.sh)
### 3.1 文件结构
```
program_update.sh (742行)
├── 1-7行:文件头和版本定义
├── 9-28行:日志和帮助函数
│ ├── log(日志输出)
│ └── usage(帮助信息)
├── 30-60行:参数解析
│ ├── while循环解析参数
│ ├── 必需参数检查
│ └── 启动日志记录
├── 62-71行:备份目录初始化
│ ├── 时间戳生成
│ ├── 目录创建
│ └── RESTART_ONLY模式检查
├── 73-107行:备份函数
│ └── backup_globs(glob模式备份)
├── 109-236行:平台/系统路径映射
│ ├── 传统平台映射(case语句)
│ │ ├── 会议预定系统
│ │ ├── 运维集控系统
│ │ └── 讯飞转录系统
│ └── 新统一平台(未实现,返回错误)
├── 238-262行:workdir自动探测
│ └── resolve_workdir函数
├── 264-296行:数据库备份
│ └── backup_db函数
├── 298-472行:更新函数
│ ├── sync_overwrite_globs(glob覆盖)
│ ├── sync_frontend_preserve(前端安全覆盖)
│ └── sync_backend_dir_preserve(后端目录覆盖)
├── 474-619行:服务重启函数
│ ├── restart_external_meeting_api_host(对外后端)
│ ├── restart_services_traditional(系统重启)
│ └── run_post_update_actions(重启入口)
├── 633-638行:--restart-only模式处理
├── 640-713行:更新执行
│ └── do_update_traditional函数
└── 715-742行:主流程控制和打包
```
### 3.2 关键函数实现
#### 3.2.1 backup_globs (81-107行)
**功能**: 按glob模式备份文件/目录
**参数**: label(标签)、base(源目录)、dest(目标目录)、globs(glob模式列表)
**实现细节**:
- 创建目标目录
- 源目录不存在时输出警告并返回
- 使用nullglob避免字面量展开
- 循环处理每个glob模式
- 使用cp -a保留属性
#### 3.2.2 resolve_workdir (179-236行)
**功能**: 自动探测正确的workdir
**候选目录**:
1. 基础目录本身
2. $base/update/
3. $base/Update/
4. $base/package/
5. $base/dist/
**判定标准**:
- 根据系统类型定义expected目录列表
- 命中任意关键目录即返回该目录
- 未找到则返回原始base
#### 3.2.3 backup_db (265-291行)
**功能**: 备份数据库
**实现细节**:
- 检查docker命令是否存在
- 查找MySQL容器(支持mysql|mariadb)
- 容器内执行mysqldump
- 密码固定为:dNrprU&2S
- 输出警告:MySQL容器未找到、docker未安装
#### 3.2.4 sync_overwrite_globs (300-330行)
**功能**: glob模式精准覆盖
**参数**: sourceDir、targetDir、label、globs
**实现细节**:
- 源目录不存在时返回错误码10
- 先删除同名项,再复制新文件
- 支持dotglob(隐藏文件)
- 未匹配到文件时返回错误码11
- 用于会议预定系统的jar文件更新
#### 3.2.5 sync_frontend_preserve (337-424行)
**功能**: 前端安全覆盖(保护备份目录)
**参数**: sourceDir、targetDir、label
**保护目录**: Bak*、bak*、new
**实现细节**:
1. 创建临时保护目录
2. 临时移出目标目录的Bak*/bak*/new
3. 覆盖index.html
4. 覆盖根目录*.js文件
5. 同步static/内容(使用rsync或cp)
6. 恢复被保护的目录
7. 清理临时目录
**static目录特殊处理**:
- rsync模式:`--exclude 'Bak*/' --exclude 'bak*/' --exclude 'new/'`
- cp回退模式:跳过源目录中的Bak/bak/new目录
#### 3.2.6 sync_backend_dir_preserve (430-472行)
**功能**: 后端目录型覆盖
**参数**: sourceDir、targetDir、label
**实现细节**:
- 源目录不存在时返回错误码10
- 源目录为空时返回错误码11
- 优先使用rsync -a(排除备份目录)
- 回退到逐项覆盖复制
- 跳过Bak*/bak*/new目录
- 用于运维集控/讯飞转录系统的目录型后端
#### 3.2.7 restart_external_meeting_api_host (478-517行)
**功能**: 重启会议预定系统对外后端(宿主机)
**实现细节**:
- 检查目录和run.sh存在性
- 执行sh ./run.sh(不依赖可执行位)
- 30秒内检测进程ubains-meeting-api-1.0-SNAPSHOT.jar
- 输出ps快照用于调试
#### 3.2.8 restart_services_traditional (520-619行)
**功能**: 根据系统类型重启服务
**会议预定系统**:
1. 对外后端:调用restart_external_meeting_api_host
2. 对内后端:
- 检测ujava容器
- 容器内执行run.sh
- 回退到宿主机执行
- 验证进程和日志
**运维集控/讯飞转录系统**:
- docker restart upython
- 验证日志
**前端更新**: 跳过重启
#### 3.2.9 do_update_traditional (642-713行)
**功能**: 执行传统平台更新
**会议预定系统**:
- 前端:sync_frontend_preserve(前台+后台)
- 后端:sync_overwrite_globs(对内+对外,*.jar)
**运维集控系统**:
- 前端:sync_frontend_preserve
- 后端:sync_backend_dir_preserve(cmdb+UbainsDevOps)
**讯飞转录系统**:
- 前端:sync_frontend_preserve(web-vue-uvoice)
- 后端:sync_backend_dir_preserve(UbainsDevOps)
### 3.3 主流程实现
#### 3.3.1 参数解析阶段 (40-60行)
```bash
while [[ $# -gt 0 ]]; do
case "$1" in
--platform) PLATFORM="${2:-}"; shift 2;;
--system) SYSTEM="${2:-}"; shift 2;;
--update) UPDATE_TYPE="${2:-}"; shift 2;;
--workdir) WORKDIR="${2:-}"; shift 2;;
--restart-only) RESTART_ONLY=1; shift 1;;
...
esac
done
```
#### 3.3.2 备份阶段 (237-296行)
```bash
# 前端备份
if [[ "$UPDATE_TYPE" == "前端更新" || "$UPDATE_TYPE" == "全量更新" ]]; then
backup_globs "frontend-1" "$FRONT_1_BASE" "$BACKUP_DIR/frontend/..."
backup_globs "frontend-2" "$FRONT_2_BASE" "$BACKUP_DIR/frontend/..."
fi
# 后端备份
if [[ "$UPDATE_TYPE" == "后端更新" || "$UPDATE_TYPE" == "全量更新" ]]; then
backup_globs "backend-1" "$BACK_1_BASE" "$BACKUP_DIR/backend/..."
backup_globs "backend-2" "$BACK_2_BASE" "$BACKUP_DIR/backend/..."
fi
# 数据库备份
backup_db "$DB_NAME"
```
#### 3.3.3 更新类型逻辑说明
**三种更新类型:前端更新、后端更新、全量更新**
##### 前端更新逻辑
**执行条件**`UPDATE_TYPE == "前端更新"`
**备份阶段**
1. 备份前端文件(index.html、static目录、*.js等)
2. 跳过后端备份
3. 执行数据库备份(所有类型都执行)
**更新阶段**
1. 调用 `sync_frontend_preserve` 更新前端文件
2. 跳过后端更新
**重启阶段**
- **会议预定系统/中广核项目**:跳过重启(前端更新不涉及服务重启)
- **运维集控/讯飞转录系统**:跳过重启(前端更新不涉及服务重启)
**适用场景**:仅修改前端界面、静态资源、JS逻辑时使用
---
##### 后端更新逻辑
**执行条件**`UPDATE_TYPE == "后端更新"`
**备份阶段**
1. 跳过前端备份
2. 备份后端文件(jar文件或整个后端目录)
3. 执行数据库备份(所有类型都执行)
**更新阶段**
1. 跳过前端更新
2. 根据系统类型调用相应更新函数:
- **会议预定系统/中广核项目**:调用 `sync_overwrite_globs` 更新*.jar文件
- **运维集控/讯飞转录系统**:调用 `sync_backend_dir_preserve` 更新整个后端目录
**重启阶段**
- **会议预定系统**
- 对外后端:宿主机执行 run.sh
- 对内后端:容器内执行 run.sh(回退到宿主机)
- 验证日志输出
- **中广核项目**
- 对内后端:容器内执行 run.sh(回退到宿主机)
- 验证 log.out 日志
- **运维集控/讯飞转录系统**
- 执行 `docker restart upython`
- 验证 /var/www/html/log/uinfo.log
**适用场景**:仅修改后端代码、业务逻辑时使用
---
##### 全量更新逻辑
**执行条件**`UPDATE_TYPE == "全量更新"`
**备份阶段**
1. 备份前端文件
2. 备份后端文件
3. 执行数据库备份(所有类型都执行)
**更新阶段**
1. 执行前端更新
2. 执行后端更新
**重启阶段**:同"后端更新"逻辑
**适用场景**:前端和后端都有修改时使用,确保一次性完成所有更新
---
#### 3.3.4 更新阶段 (715-732行)
```bash
case "$PLATFORM" in
"传统平台")
do_update_traditional
run_post_update_actions # 3.5 重启
;;
"新统一平台")
log "ERROR: 新统一平台 3.4 更新规则 PRD 未提供"
exit 30
;;
esac
```
#### 3.3.5 打包阶段 (735-742行)
```bash
if [[ "$RESTART_ONLY" -eq 0 ]]; then
tar -czvf "$BACKUP_TAR" "$BACKUP_DIR" >/dev/null
echo "BACKUP_DIR=$BACKUP_DIR"
echo "BACKUP_TAR=$BACKUP_TAR"
fi
```
---
## 4. 传统平台路径映射实现
### 4.1 会议预定系统 (120-139行)
| 类型 | 源目录(workdir下) | 目标目录 | 备份glob |
|------|-------------------|----------|----------|
| 前台前端 | ubains-web-2.0 | /var/www/java/ubains-web-2.0 | index.html, static, *.js |
| 后台前端 | ubains-web-admin | /var/www/java/ubains-web-admin | index.html, static |
| 对内后端 | api-java-meeting2.0 | /var/www/java/api-java-meeting2.0 | *.jar |
| 对外后端 | external-meeting-api | /var/www/java/external-meeting-api | *.jar |
**数据库名**: ubains
### 4.2 运维集控系统 (140-150行)
| 类型 | 源目录(workdir下) | 目标目录 | 备份glob |
|------|-------------------|----------|----------|
| 前端 | web-vue-rms | /var/www/html/web-vue-rms | index.html, static |
| 后端 | cmdb | /var/www/html/cmdb | 整个目录 |
| 后端 | UbainsDevOps | /var/www/html/UbainsDevOps | 整个目录 |
**数据库名**: devops
### 4.3 讯飞转录系统 (151-161行)
| 类型 | 源目录(workdir下) | 目标目录 | 备份glob |
|------|-------------------|----------|----------|
| 前端 | web-vue-uvoice | /var/www/html/web-vue-uvoice | index.html, static |
| 后端 | UbainsDevOps | /var/www/html/UbainsDevOps | 整个目录 |
**数据库名**: devops
---
## 5. 新统一平台实现状态
### 5.1 当前实现 (168-171行)
```bash
"新统一平台")
log "ERROR: 新统一平台的路径映射未在 PRD 中提供,暂无法执行备份。请补充 4.2 映射后再启用。"
exit 3
;;
```
### 5.2 待实现内容
- PRD 4.2 节新统一平台路径映射
- 数据库名称映射
- 更新函数调用(可能复用或新增)
- 服务重启逻辑
---
## 6. 项目特殊路径
### 6.1 中广核项目
| 类型 | 源目录(workdir下) | 目标目录 | 备份glob |
|------|-------------------|----------|----------|
| 前台前端 | cims-web | /var/www/java/cims-web | index.html, static |
| 对内后端 | cims-java | /var/www/java/cims-java | cims-java-4.2.2-dm.jar |
**数据库名**: ubains
**实现位置**:
- 路径映射: program_update.sh (162-170行)
- workdir探测: program_update.sh (203-204行)
- 更新逻辑: program_update.sh (779-793行)
- 重启逻辑: program_update.sh (630-684行)
**重启说明**:
- 对内后端优先进入java容器内执行run.sh
- 回退到宿主机执行
- 验证日志位置: /var/www/java/cims-java/log.out
### 6.2 中广核项目特殊部署方式
**部署包结构**
```
远程更新程序/
├── remote_program_update.ps1 # Windows入口脚本
├── program_update.sh # Linux部署脚本
└── cims-update.zip # 中广核项目更新包(与脚本同级目录)
├── cims-web/ # 前端更新包
│ ├── index.html
│ ├── static/
│ └── ...
└── cims-java/ # 后端更新包
└── cims-java-4.2.2-dm.jar
```
**特殊要求**
1. **使用zip打包**:将 `cims-web``cims-java` 目录打包成zip文件
2. **sudo权限切换**:连接后端服务器后,需要通过 `sudo -i` 切换到root用户
3. **sudo密码**`CGNadm!@345CGNadm!@345`
**部署流程**
1. Windows脚本上传 `cims-update.zip` 到远端 `/home/appadmin/`
2. 上传 `program_update.sh` 脚本到远端
3. 远端解压zip文件到 `/home/appadmin/`
4. 通过 `sudo -i` 切换到root用户
5. 执行更新脚本:`./program_update.sh --platform "中广核项目" --system "中广核项目" --update "全量更新" --workdir /home/appadmin/`
**与其他项目的差异**
- 其他项目:zip包内包含完整的目录结构(如ubains-web-2.0、api-java-meeting2.0等)
- 中广核项目:zip包内包含cims-web和cims-java目录
## 7. --restart-only模式实现
### 7.1 实现位置 (633-638行)
```bash
if [[ "$RESTART_ONLY" -eq 1 ]; then
log "RESTART_ONLY=1 -> skip backup/update/tar; only run PRD 3.5 restart"
run_post_update_actions
exit 0
fi
```
### 7.2 跳过的操作
1. 备份目录创建 (63-71行)
2. workdir解析 (238-240行)
3. 前端/后端备份 (242-262行)
4. 数据库备份 (264-296行)
5. 文件更新 (715-732行)
6. 备份打包 (735-742行)
### 7.3 执行的操作
- 仅调用run_post_update_actions(3.5重启逻辑)
---
## 8. 异常处理实现
### 8.1 退出码定义
| 退出码 | 含义 | 触发位置 |
|--------|------|----------|
| 0 | 成功 | 正常退出 |
| 2 | 参数错误 | 参数缺失或未知参数 (56行) |
| 3 | 平台/系统不支持 | 平台或系统类型未实现 (164/173行) |
| 10 | 源目录不存在 | sync_overwrite_globs等 (307行) |
| 11 | 源目录为空或未匹配 | sync_backend_dir_preserve等 (443行) |
| 20 | 不支持的系统类型 | do_update_traditional (710行) |
| 30 | 新统一平台未实现 | 主流程case (726行) |
| 31 | 不支持的平台 | 主流程case (730行) |
### 8.2 set选项 (2行)
```bash
set -euo pipefail
```
- `-e`: 命令失败时退出
- `-u`: 未定义变量时退出
- `-o pipefail`: 管道中任何命令失败时退出
### 8.3 警告处理
- MySQL容器未找到:输出警告,跳过备份 (276-282行)
- docker未安装:输出警告,跳过操作 (269-272行)
- 源目录不存在(备份):输出警告,继续 (88-91行)
- 未匹配到文件(备份):输出警告,继续 (104-106行)
---
## 9. 数据库配置
### 9.1 密码配置 (74行)
```bash
MYSQL_ROOT_PASSWORD='dNrprU&2S'
```
### 9.2 MySQL容器查找 (274-278行)
```bash
mysql_container="$(docker ps --format '{{.Names}}' | grep -Eiw 'mysql|mariadb' | head -n 1 || true)"
if [[ -z "$mysql_container" ]]; then
mysql_container="$(docker ps --format '{{.Names}} {{.Image}}' | grep -Ei 'mysql|mariadb' | awk '{print $1}' | head -n 1 || true)"
fi
```
### 9.3 备份命令 (289行)
```bash
docker exec "$mysql_container" sh -lc "mysqldump -uroot -p'${MYSQL_ROOT_PASSWORD}' '${db}'" > "$out"
```
---
## 10. 服务重启实现详情
### 10.1 会议预定系统对外后端 (478-517行)
**执行位置**: 宿主机
**路径**: /var/www/java/external-meeting-api
**执行命令**:
```bash
sh ./run.sh
```
**验证方式**: 30秒内检测进程 `ubains-meeting-api-1.0-SNAPSHOT.jar`
### 10.2 会议预定系统对内后端 (534-571行)
**执行位置**: 容器内优先,回退到宿主机
**容器检测**:
```bash
docker ps --format '{{.Names}}' | grep -E '^ujava[0-9]*$' | head -n 1
```
**容器内执行**:
```bash
docker exec "$java_container" bash -lc "cd /var/www/java/api-java-meeting2.0 && bash ./run.sh"
```
**宿主机回退**:
```bash
cd /var/www/java/api-java-meeting2.0 && bash ./run.sh
```
### 10.3 运维集控/讯飞转录系统 (589-614行)
**执行命令**:
```bash
docker restart upython
```
**日志验证**:
```bash
tail -n 50 /var/www/html/log/uinfo.log
```
---
## 11. 验收测试要点
### 11.1 功能测试
**Windows入口脚本**:
1. 依赖检测:缺少plink/pscp时正确提示
2. 参数采集:所有参数正确接收
3. 文件检测:缺少zip或脚本时正确终止
4. 连接测试:连接失败时正确提示
5. 文件上传:文件正确上传到远端
6. 远端执行:命令正确执行并捕获输出
7. BACKUP_TAR解析:正确解析备份包路径
8. 备份下载:备份包正确下载到本地
**Linux部署脚本**:
1. 参数解析:所有参数正确解析
2. 前端备份:会议预定系统(前台+后台)、运维集控、讯飞转录
3. 后端备份:会议预定系统(对内+对外jar)、运维集控(目录)、讯飞转录(目录)
4. 数据库备份:MySQL容器内mysqldump正确执行
5. workdir探测:多层目录结构正确识别
6. 前端更新:保护Bak*/bak*/new目录
7. 后端更新:jar文件严格匹配*.jar,目录型覆盖正确
8. 服务重启:各系统服务正确重启
9. 日志验证:日志片段正确输出
10. 备份打包:tar.gz正确生成
### 11.2 异常测试
1. 缺少必需参数:显示帮助并退出
2. 不支持的平台/系统:输出错误并退出
3. 源目录不存在:正确处理(警告或错误)
4. 未匹配到文件:正确处理(警告或错误)
5. MySQL容器不存在:输出警告并跳过
6. docker未安装:输出警告并跳过
7. rsync不存在:自动回退到cp命令
### 11.3 --restart-only模式测试
1. 跳过备份目录创建
2. 跳过workdir解析
3. 跳过备份/更新操作
4. 跳过备份打包
5. 仅执行服务重启
6. 三种系统服务正确重启
---
## 12. 已知限制和未来改进
### 12.1 已知限制
1. **新统一平台未实现**:待PRD提供路径映射
2. **串行更新**:不支持多服务器并发更新
3. **无自动回滚**:更新失败后需要手动回滚
4. **无更新前检查**:不执行健康检查
5. **无告警通道**:不支持钉钉/邮件/短信告警
### 12.2 未来改进方向
1. 实现新统一平台支持
2. 支持多服务器并发更新
3. 实现自动回滚功能
4. 添加更新前健康检查
5. 集成统一告警通道
6. 开发Web界面
---
## 13. 变更记录
| 日期 | 版本 | 变更内容 | 变更人 |
|------|------|----------|--------|
| 2026-01-28 | V2.5 | 修复Invoke-PlinkCommand函数输出捕获问题;修复sudo中文参数乱码问题;优化正则表达式匹配;前端更新测试通过 | Claude |
| 2026-01-28 | V2.4 | 中广核项目部署方式改为使用zip压缩包(cims-update.zip),统一部署流程;移除旧版本plink不支持的参数 | Claude |
| 2026-01-28 | V2.3 | 中广核项目从系统类型改为独立平台类型,增加服务器预设功能(测试环境前端/后端服务器) | Claude |
| 2026-01-28 | V2.2 | 中广核项目特殊部署方式:无需zip打包、直接上传cims-web/cims-java文件夹、支持sudo -i切换用户 | Claude |
| 2026-01-28 | V2.1 | 新增中广核项目支持,更新路径映射、workdir探测、更新逻辑和重启逻辑 | Claude |
| 2026-01-28 | V2.0 | 根据实际脚本实现重新编写需求文档和计划执行文档 | Claude |
---
## 13.2 技术实现细节与问题解决
### 13.2.1 Invoke-PlinkCommand输出捕获问题
**问题描述**
函数直接返回空字符串,导致无法解析远端脚本输出的 `BACKUP_TAR` 变量。
**原因**
`Start-Process` 默认不捕获输出,需要使用 `-RedirectStandardOutput` 参数。
**解决方案**
```powershell
function Invoke-PlinkCommand {
...
$tmpOut = Join-Path $env:TEMP ("plink_{0}.out.log" -f ([guid]::NewGuid().ToString("N")))
$tmpErr = Join-Path $env:TEMP ("plink_{0}.err.log" -f ([guid]::NewGuid().ToString("N")))
$p = Start-Process -FilePath $PlinkPath -ArgumentList $args -NoNewWindow -Wait -PassThru `
-RedirectStandardOutput $tmpOut -RedirectStandardError $tmpErr
$outText = ""
if (Test-Path $tmpOut) { $outText = (Get-Content $tmpOut -Raw -Encoding utf8) }
Remove-Item -ErrorAction SilentlyContinue $tmpOut, $tmpErr
return $outText
}
```
### 13.2.2 sudo中文参数乱码问题
**问题描述**
使用 `sudo -S -i` 执行命令时,中文参数变成乱码:`丹核项?` 代替 `中广核项目`
**原因**
`sudo -i` 启动login shell,重置所有环境变量包括locale设置。
**解决方案**
```powershell
# 移除 -i 参数,添加 locale 设置
$execCmd = "cd '$remoteDir' && echo '$sudoPassword' | sudo -S env LC_CTYPE=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8 ./program_update.sh --platform '$platformType' ..."
```
### 13.2.3 正则表达式匹配优化
**问题描述**
输出中包含 `BACKUP_TAR=/home/Backup/...`,但严格正则无法匹配。
**解决方案**
使用更灵活的匹配模式:
```powershell
# 从严格匹配改为灵活匹配
if ($out -match 'BACKUP_TAR=([^\s\r\n]+)') {
$backupTar = $Matches[1].Trim()
}
```
### 13.2.4 pscp文件夹上传限制
**问题描述**
`pscp: C:\...\cims-web: not a regular file`
**解决方案**
统一使用zip压缩包方式,避免直接上传文件夹。
---
## 14. 附录
### 14.1 脚本路径
- Windows入口脚本:`C:\PycharmData\ubains-module-test\AuxiliaryTool\ScriptTool\远程更新程序\remote_program_update.ps1`
- Linux部署脚本:`C:\PycharmData\ubains-module-test\AuxiliaryTool\ScriptTool\远程更新程序\program_update.sh`
### 14.2 需求文档路径
- 需求文档:`Docs/PRD/远程更新程序/_PRD_程序更新脚本需求文档.md`
- 计划执行文档:`Docs/PRD/远程更新程序/_PRD_程序更新脚本需求文档_计划执行.md`
### 14.3 问题总结文档
- 问题总结文档:`Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 需求文档:`C:\PycharmData\ubains-module-test\Docs\PRD\远程更新程序\_PRD_程序更新脚本需求文档.md`
- 计划执行文档:`C:\PycharmData\ubains-module-test\Docs\PRD\远程更新程序\_PRD_程序更新脚本需求文档_计划执行.md`
# _PRD_问题处理需求文档
> 版本:V1.0
> 更新日期:2026-01-28
> 适用范围:Linux 服务器常见问题处理脚本
> 实现脚本:`AuxiliaryTool/ScriptTool/问题处理/issue_handler.sh`
---
## 1. 背景与目标
### 1.1 背景
Linux 服务器在日常运维中常遇到各种问题,如服务运行异常、权限不足、磁盘空间不足、版本更新、数据备份等。为了降低运维成本,提高问题处理效率,需要实现一个统一的常见问题处理工具。
### 1.2 目标
实现一个功能全面的问题处理脚本,具备:
- 自动识别平台类型(新统一平台/标准版平台)
- 提供交互式和命令行两种使用方式
- 支持版本包更新、权限修复、配置修复、磁盘清理等常见问题处理
- 完善的日志记录和操作反馈机制
- 安全的操作确认和备份机制
### 1.3 平台类型说明
| 平台类型 | 判定条件 | 目录路径 |
|---------|----------|----------|
| 新统一平台 | 存在 `/data/services/api` 目录 | 主要使用 `/data/` 目录 |
| 标准版平台 | 存在 `/var/www/java` 目录 | 主要使用 `/var/www/` 目录 |
---
## 2. 总体范围
### 2.1 纳入处理对象
#### 2.1.1 平台类型
**新统一平台:**
- 预定服务:`/data/services/api/java-meeting/`
- 运维服务:`/data/services/api/python-cmdb/`
- 中间件:`/data/middleware/redis/`
**标准版平台:**
- 预定服务:`/var/www/java/`
- 运维服务:`/var/www/html/`
- 中间件:`/var/www/redis/`
#### 2.1.2 容器类型
| 容器名 | 说明 | 检测方式 |
|--------|------|----------|
| umysql | MySQL 数据库容器 | `docker ps --format '{{.Names}}'` 匹配 `^umysql$` |
| ujava2 | Java 服务容器 | `docker ps --format '{{.Names}}'` 匹配 `^ujava2$` |
| uweb | Web 服务容器 | `docker ps --format '{{.Names}}'` 匹配 `^uweb$` |
| uredis | Redis 容器 | `docker ps --format '{{.Names}}'` 匹配 `^uredis$` |
### 2.2 功能分类
| 功能分类 | 子功能 | 状态 | 说明 |
|---------|--------|------|------|
| 更新版本包 | 对内后端包 | 已实现 | 更新 Java Jar 包 |
| | 对外后端包 | 已实现 | 更新 Java Jar 包 |
| | 预定系统前台包 | 已实现 | 更新前端静态资源 |
| | 预定系统后台包 | 已实现 | 更新前端静态资源 |
| | 运维后端包 | 已实现 | 更新 Python 服务 |
| | 运维前端包 | 已实现 | 更新前端静态资源 |
| 修复文件权限 | 普通文件权限 | 已实现 | 修复启动脚本等权限 |
| | 数据库用户权限 | 已实现 | 创建用户并授予权限 |
| | Nginx 用户权限 | 已实现 | 修改 Nginx 配置文件 |
| | Redis 配置权限 | 已实现 | 修复 Redis 配置文件权限 |
| | rc.local 权限 | 已实现 | 修复启动脚本权限 |
| 修复配置文件 IP | IP 地址修正 | 已实现 | 批量替换配置文件中的 IP |
| 修复系统磁盘问题 | 清理已删文件 | 已实现 | 清理已删除但未释放的文件 |
| | 轮转日志文件 | 已实现 | 处理过大的日志文件 |
| | system_log 表处理 | 已实现 | 数据库表清理功能 |
| 导出现场日志 | 日志导出压缩 | 已实现 | 导出并压缩日志文件 |
| 修复服务异常 | 对外服务掉线 | 已实现 | 修复对外后端服务异常 |
| | NTP 服务配置 | 已实现 | 修复 NTP 服务配置 |
| 网络配置 | 端口开放 | 已实现 | 检查并开放必要端口 |
| | DNS 异常修复 | 已实现 | 修复 DNS 配置问题 |
| 数据备份 | 环境数据备份 | 已实现 | 打包备份现场环境数据 |
| 容器异常处理 | Redis 容器异常 | 已实现 | 修复 Redis 容器启动异常 |
---
## 3. 术语说明
| 术语 | 说明 |
|------|------|
| 平台类型 | `new`(新统一平台)或 `standard`(标准版平台) |
| 交互模式 | 脚本以菜单形式与用户交互,需要用户逐步确认操作 |
| 非交互模式 | 脚本通过命令行参数执行,无需用户交互 |
| 确认操作 | 在执行重要操作前询问用户是否继续 |
| 时间戳 | 格式 `yyyyMMdd_HHmmss` |
| 备份目录 | 带时间戳的备份目录,格式为 `{原名}_backup_{时间戳}` |
---
## 4. 功能需求
### 4.1 版本包更新
#### 4.1.1 更新预定系统对内后端包
**功能说明:**
更新预定系统对内后端服务的 JAR 包。
**目标路径:**
- 新统一平台:`/data/services/api/java-meeting/java-meeting2.0/ubains-meeting-inner-api-1.0-SNAPSHOT.jar`
- 标准版平台:`/var/www/java/api-java-meeting2.0/ubains-meeting-inner-api-1.0-SNAPSHOT.jar`
**更新流程:**
1. 检查脚本所在目录是否存在 `api-java-meeting2.0/` 目录
2. 如果存在,验证该目录下是否存在目标 JAR 文件
3. 如果不存在,提示用户输入更新包所在目录
4. 创建备份目录:`{目标目录}/{包名}_backup_{时间戳}/`
5. 用户确认后,备份当前版本包
6. 将新版本包复制到指定路径
#### 4.1.2 更新预定系统对外后端包
**目标路径:**
- 新统一平台:`/data/services/api/java-meeting/java-meeting-extapi/ubains-meeting-api-1.0-SNAPSHOT.jar`
- 标准版平台:`/var/www/java/external-meeting-api/ubains-meeting-api-1.0-SNAPSHOT.jar`
**更新目录名称:**
- 新统一平台:`java-meeting-extapi/`
- 标准版平台:`external-meeting-api/`
#### 4.1.3 更新预定系统前台包
**目标路径:**
- 新统一平台:`/data/services/web/pc/pc-vue2-meetngV2/`
- 标准版平台:`/var/www/java/ubains-web-2.0/`
**关键文件:**
- `index.html`
- `static`
- `216bdc4c3fe8c2c18aff.worker.js`(新统一平台)
- `WW_verify_BTch57iYZdNNTbVg.txt`(新统一平台)
#### 4.1.4 更新预定系统后台包
**目标路径:**
- 新统一平台:`/data/services/web/pc/pc-vue2-backstage/`
- 标准版平台:`/var/www/java/ubains-web-admin/`
**关键文件:**
- `index.html`
- `static`
#### 4.1.5 更新运维后端包
**目标路径:**
- 新统一平台:`/data/services/api/python-cmdb/`
- 标准版平台:`/var/www/html/`
**关键文件/目录:**
- `cmdb`
- `UbainsDevOps`
#### 4.1.6 更新运维前端包
**目标路径:**
- 新统一平台:`/data/services/web/pc/pc-vue2-moniter/`
- 标准版平台:`/var/www/html/web-vue-rms/`
**关键文件/目录:**
- `index.html`
- `static`
- `module`
---
### 4.2 修复文件权限
#### 4.2.1 普通文件权限修复
**新统一平台文件列表:**
| 文件路径 | 权限 |
|---------|------|
| `/data/services/api/start.sh` | 755 |
| `/data/services/api/java-meeting/java-meeting2.0/run.sh` | 755 |
| `/data/services/api/java-meeting/java-meeting-extapi/run.sh` | 755 |
| `/data/services/api/python-cmdb/start.sh` | 755 |
| `/data/services/api/python-cmdb/mule_mqtt.py` | 755 |
| `/data/services/api/python-cmdb/mule_schd.py` | 755 |
| `/data/services/api/python-cmdb/mule_mqtt3.py` | 755 |
| `/data/middleware/redis/config/redis.conf` | 644 |
**标准版平台文件列表:**
| 文件路径 | 权限 |
|---------|------|
| `/var/www/java/start.sh` | 755 |
| `/var/www/java/api-java-meeting2.0/run.sh` | 755 |
| `/var/www/java/external-meeting-api/run.sh` | 755 |
| `/var/www/java/external-meeting-api/check.sh` | 755 |
| `/var/www/java/external-meeting-api/startExternalProject.sh` | 755 |
| `/var/www/malan/malan` | 755 |
| `/var/www/malan/run.sh` | 755 |
| `/var/www/html/start.sh` | 755 |
| `/var/www/html/ngrok.sh` | 755 |
| `/var/www/html/mule_mqtt.py` | 755 |
| `/var/www/html/mule_schd.py` | 755 |
| `/var/www/html/mule_mqtt3.py` | 755 |
| `/var/www/redis/redis.conf` | 644 |
| `/etc/rc.local` | 755 |
#### 4.2.2 数据库用户权限修复
**功能说明:**
修复 MySQL 数据库用户权限不足问题,主要针对 `mysqluser``root@172.17.0.1` 用户。
**执行流程:**
1. 检查 Docker 环境是否存在
2. 检查 `umysql` 容器是否存在且正在运行
3. 提示用户输入数据库 root 密码
4.`umysql` 容器中执行以下 SQL 操作:
```sql
CREATE USER IF NOT EXISTS 'mysqluser'@'%' IDENTIFIED BY '{用户输入密码}';
GRANT ALL ON ubains.* TO 'mysqluser'@'%';
GRANT ALL ON devops.* TO 'mysqluser'@'%';
GRANT ALL ON ubains.* TO 'root'@'172.17.0.1';
GRANT ALL ON devops.* TO 'root'@'172.17.0.1';
ALTER USER 'mysqluser'@'%' IDENTIFIED WITH mysql_native_password BY '{用户输入密码}';
UPDATE mysql.user SET Grant_priv='Y', Super_priv='Y' WHERE user = 'mysqluser' AND host = '%';
FLUSH PRIVILEGES;
```
#### 4.2.3 Nginx 用户权限修复
**功能说明:**
修复 Nginx 配置文件中的 `user` 值不是 root 导致的权限问题。
**处理容器:**
- `ujava2` 容器
- `uweb` 容器
**执行流程:**
1. 进入容器:`docker exec -it {container} bash`
2. 修改文件:`/usr/local/nginx/conf/nginx.conf`,将 `user` 的值修改为 `root`
3. 重启 nginx:`/usr/local/nginx/sbin/nginx -s reload`
---
### 4.3 修复配置文件 IP 地址
#### 4.3.1 新统一平台配置文件
**预定服务:**
| 配置文件路径 |
|-------------|
| `/data/services/api/java-meeting/java-meeting2.0/conf/fdfs_client.conf` |
| `/data/services/api/java-meeting/java-meeting2.0/config/application.properties` |
| `/data/services/api/java-meeting/java-meeting2.0/config/application-mqtt.yml` |
| `/data/services/api/java-meeting/java-meeting2.0/config/application-thirdParty.properties` |
| `/data/services/api/java-meeting/java-meeting2.0/config/config.js` |
| `/data/services/api/java-meeting/java-meeting-extapi/config/application-prod.properties` |
| `/data/services/api/java-meeting/java-meeting-extapi/config/application-mqtt.yml` |
| `/data/services/api/java-meeting/java-meeting-extapi/config/application-dubbo.yml` |
**运维服务:**
| 配置文件路径 |
|-------------|
| `/data/services/api/python-cmdb/setting.conf` |
| `/data/services/api/python-cmdb/nginx-conf/moblie8081.conf` |
| `/data/services/api/python-cmdb/nginx-conf/rms8443.conf` |
| `/data/services/api/python-cmdb/web-vue-rms/static/config.json` |
#### 4.3.2 标准版平台配置文件
**预定服务:**
| 配置文件路径 |
|-------------|
| `/var/www/java/api-java-meeting2.0/conf/fdfs_client.conf` |
| `/var/www/java/api-java-meeting2.0/config/application.properties` |
| `/var/www/java/api-java-meeting2.0/config/application-mqtt.yml` |
| `/var/www/java/api-java-meeting2.0/config/application-thirdParty.properties` |
| `/var/www/java/api-java-meeting2.0/config/config.js` |
| `/var/www/java/external-meeting-api/conf/fdfs_client.conf` |
| `/var/www/java/external-meeting-api/config/application-prod.properties` |
| `/var/www/java/external-meeting-api/config/application-intAddress.properties` |
| `/var/www/java/external-meeting-api/config/application-mqtt.yml` |
| `/var/www/java/external-meeting-api/config/application-thirdParty.properties` |
| `/var/www/java/external-meeting-api/config/config.js` |
| `/var/www/java/external-meeting-api/config/h5-8086.conf` |
| `/var/www/java/external-meeting-api/config/video8083.conf` |
| `/var/www/java/external-meeting-api/config/meeting443.conf` |
**运维服务:**
| 配置文件路径 |
|-------------|
| `/var/www/html/setting.conf` |
| `/var/www/html/web-vue-rms/static/config.json` |
| `/var/www/html/nginx-conf/moblie8081.conf` |
| `/var/www/html/nginx-conf/rms8443.conf` |
**执行流程:**
1. 提示用户输入需要替换的旧 IP 地址
2. 提示用户输入新的 IP 地址
3. 在所有配置文件中批量替换 IP 地址
---
### 4.4 修复系统磁盘问题
#### 4.4.1 清理已删除但未释放的文件
**功能说明:**
清理已删除但仍在被进程占用的文件,释放磁盘空间。
**执行流程:**
1. 使用 `lsof +L1` 命令查找已删除但未释放的文件
2. 显示找到的文件及其进程信息
3. 用户确认后,强制结束占用这些文件的进程
4. 显示操作结果统计
**定时任务版本:**
支持通过 cron 定时执行,每天凌晨 3 点自动清理:
```bash
0 3 * * * /bin/bash {脚本目录}/issue_handler.sh --action clean_deleted_files_cron
```
#### 4.4.2 轮转过大的日志文件
**功能说明:**
检查并轮转超过 100MB 的日志文件。
**涉及日志文件:**
**新统一平台:**
- `/data/services/api/java-meeting/java-meeting2.0/log.out`
- `/data/services/api/java-meeting/java-meeting-extapi/log.out`
- `/data/services/api/python-cmdb/uinfo.log`
- `/data/services/api/python-cmdb/uwsgi.log`
**标准版平台:**
- `/var/www/java/api-java-meeting2.0/log.out`
- `/var/www/java/external-meeting-api/log.out`
**执行流程:**
1. 检查日志文件大小
2. 对超过 100MB 的文件提示用户确认轮转
3. 备份当前日志文件
4. 创建新的空日志文件
5. 尝试通知相关服务重新打开日志文件
6. 压缩旧日志文件
#### 4.4.3 清理数据库 system_log 表
**功能说明:**
清理 `ubains.system_log` 表中 30 天前的历史数据。
**执行流程:**
1. 检查 Docker 环境和 `umysql` 容器状态
2. 提示用户输入数据库 root 密码
3. 验证数据库连接和表存在性
4. 显示当前记录数
5. 如果记录数超过 100 万条,提示用户确认删除
6. 执行删除操作:`DELETE FROM ubains.system_log WHERE create_time < DATE_SUB(NOW(), INTERVAL 30 DAY)`
7. 执行表空间优化:`OPTIMIZE TABLE ubains.system_log`
#### 4.4.4 磁盘分区调整参考
**说明:**
磁盘分区调整属于高风险操作,脚本不提供自动处理功能,仅提供详细的操作指南。
**操作步骤:**
1. 查看分区空间和格式:`df -hT`、`cat /etc/os-release`
2. 备份数据:`cp -rp /home /run/`
3. 卸载 /home 分区:`umount /home`
4. 减小分区大小:`lvreduce -L -1.6T /dev/mapper/uos-home`
5. 重新挂载:`mount /dev/mapper/uos-home /home/`
6. 扩容根目录:`lvextend -L +40G /dev/mapper/uos-root`
7. 验证:`df -h`
---
### 4.5 导出并压缩日志文件
#### 4.5.1 新统一平台日志目录
| 服务类型 | 日志路径 |
|---------|----------|
| 预定服务 | `/data/services/api/java-meeting/java-meeting2.0/logs` |
| 预定服务 | `/data/services/api/java-meeting/java-meeting-extapi/logs` |
| 运维服务 | `/data/services/api/python-cmdb/log` |
#### 4.5.2 标准版平台日志目录
| 服务类型 | 日志路径 |
|---------|----------|
| 预定服务 | `/var/www/java/api-java-meeting2.0/logs` |
| 预定服务 | `/var/www/java/external-meeting-api/logs` |
| 运维服务 | `/var/www/html/log` |
**执行流程:**
1. 创建带时间戳的临时备份目录
2. 复制日志文件到临时目录
3. 检查磁盘空间是否充足
4. 使用 zip 或 tar.gz 压缩文件
5. 清理临时目录
6. 显示操作结果
---
### 4.6 修复服务异常
#### 4.6.1 修复对外后端服务异常掉线
**功能说明:**
启动对外后端服务并确保 /etc/profile 配置正确。
**工作目录:**
- 新统一平台:`/data/services/api/java-meeting/java-meeting-extapi/`
- 标准版平台:`/var/www/java/external-meeting-api/`
**执行流程:**
1. 显式加载 `/etc/profile`,确保 java 在 PATH 中
2. 检查 `run.sh` 是否可执行,不可执行则赋权
3. 执行 `./run.sh` 启动服务
4. 等待 60 秒检查服务状态
5. 验证 `ubains-meeting-api-1.0-SNAPSHOT.jar` 进程是否存在
#### 4.6.2 修复 NTP 服务配置
**功能说明:**
修复 NTP/Chrony 服务配置或启动问题。
**执行流程:**
1. 检测系统使用的 NTP 服务类型(chronyd、ntpd 等)
2. 检查配置文件中是否包含 `allow all` 配置项
3. 如果没有,提示用户确认是否添加
4. 检查 NTP 服务是否正在运行
5. 提示用户确认是否重启 NTP 服务
6. 尝试重启服务,支持多种服务管理方式
**支持的服务类型:**
- chronyd(推荐,配置文件:`/etc/chrony.conf`)
- ntpd(配置文件:`/etc/ntp.conf`)
---
### 4.7 网络配置
#### 4.7.1 端口开放检查
**功能说明:**
检查并开放必要端口。
**需要开放的端口:**
**新统一平台:**
- 443/tcp
- 1883/tcp
- 8883/tcp
- 123/udp
**标准版平台:**
- 443/tcp
- 1883/tcp
- 8443/tcp
- 8081/tcp
- 123/udp
- 8883/tcp
- 4443/tcp
**执行流程:**
1. 检测系统使用的防火墙类型(firewalld 或 iptables)
2. 检查防火墙服务是否正在运行
3. 如果未运行,提示用户是否启动服务
4. 检查所需端口是否已开放
5. 对于未开放的端口,提示用户确认是否开放
6. 添加端口规则到防火墙配置
7. 保存防火墙规则
#### 4.7.2 DNS 异常修复
**功能说明:**
修复服务器 DNS 配置异常问题。
**推荐 DNS 服务器:**
- 114.114.114.114
- 8.8.8.8
**执行流程:**
1. 检测网络管理器类型(NetworkManager、network、systemd-networkd)
2. 检查 `/etc/resolv.conf` 中是否已配置推荐的 DNS 服务器
3. 如果没有配置,提示用户确认是否添加
4. 添加前备份原配置文件
5. 添加所有推荐的 DNS 服务器
6. 验证配置是否成功写入
7. 提示用户是否重启网络服务
8. 重启后进行网络连接测试
---
### 4.8 数据备份
#### 4.8.1 环境数据备份
**功能说明:**
打包备份现场环境数据,包括配置文件、日志文件和系统信息。
**新统一平台备份项:**
**预定服务配置和日志:**
- `/data/services/api/java-meeting/java-meeting2.0/config`
- `/data/services/api/java-meeting/java-meeting2.0/logs`
- `/data/services/api/java-meeting/java-meeting-extapi/config`
- `/data/services/api/java-meeting/java-meeting-extapi/logs`
**运维服务配置和日志:**
- `/data/services/api/python-cmdb/setting.conf`
- `/data/services/api/python-cmdb/nginx.conf`
- `/data/services/api/python-cmdb/log`
**中间件配置:**
- `/data/middleware/redis/config`
**系统通用配置:**
- `/etc/hosts`
- `/etc/resolv.conf`
- `/etc/fstab`
**标准版平台备份项:**
**预定服务配置和日志:**
- `/var/www/java/api-java-meeting2.0/config`
- `/var/www/java/api-java-meeting2.0/logs`
- `/var/www/java/external-meeting-api/config`
- `/var/www/java/external-meeting-api/logs`
**运维服务配置和日志:**
- `/var/www/html/config`
- `/var/www/html/log`
**中间件配置:**
- `/var/www/redis`
**系统信息收集:**
- 主机信息:`hostnamectl`
- 网络配置:`ip addr show`
- 磁盘使用:`df -h`
- 内存使用:`free -h`
- 进程信息:`ps aux`
- 容器信息:`docker ps -a`
- 服务状态:`systemctl list-units --type=service --state=running`
**执行流程:**
1. 创建带时间戳的备份目录
2. 复制所有配置文件和日志
3. 收集系统运行时信息
4. 将所有备份数据打包为 tar.gz 格式
---
### 4.9 容器异常处理
#### 4.9.1 Redis 容器异常处理
**功能说明:**
修复 Redis 容器启动异常问题。
**数据目录:**
- 新统一平台:`/data/middleware/redis/data`
- 标准版平台:`/var/www/redis/data`
**执行流程:**
1. 检测平台类型
2. 尝试重启 `uredis` 容器
3. 如果容器重启失败,删除 Redis data 目录下所有数据
4. 再次重启容器
**交互模式与非交互模式:**
- 交互模式:删除数据前提示用户确认,必须输入 `yes` 才继续
- 非交互模式:需要同时传入 `--non-interactive` 和 `--yes` 参数才会自动执行
---
## 5. 主流程
### 5.1 交互式模式主流程
```
1. 运行脚本:sudo ./issue_handler.sh
2. 选择平台类型 [auto/new/standard]
3. 显示功能菜单
4. 用户选择功能
├── 1) fix_ntp_config 修复 NTP/Chrony
├── 2) fix_port_access 修复端口开放
├── 3) fix_ip_configurations 批量替换配置中的IP
├── 4) fix_permissions 修复文件权限
├── 5) fix_db_permissions 修复数据库权限
├── 6) fix_dns_config 修复DNS配置
├── 7) export_and_compress_logs 导出并压缩日志
├── 8) rotate_logs 轮转日志
├── 9) backup_environment_data 打包备份环境
├── c) clean_deleted_files 清理已删除未释放文件
└── q) 退出
5. 根据用户选择执行相应功能
6. 显示操作结果摘要
```
### 5.2 命令行模式主流程
```
1. 执行命令:sudo ./issue_handler.sh --action {action_name} [--platform {platform}] [其他参数]
2. 解析命令行参数
3. 自动检测平台类型(如果未指定)
4. 执行对应的功能函数
5. 记录操作日志
6. 显示操作结果
```
---
## 6. 配置与可变项
| 配置项 | 值 | 说明 |
|--------|-----|------|
| 脚本版本 | 1.0.0 | 用于日志与版本追踪 |
| 日志文件 | {脚本目录}/issue_handler.log | 日志文件存储位置 |
| 新统一平台检测目录 | /data/services/api | 平台类型判定 |
| 标准版平台检测目录 | /var/www/java | 平台类型判定 |
| 日志轮转阈值 | 100MB | 超过此大小的日志将被轮转 |
| system_log 清理阈值 | 100万条 | 超过此数量将提示清理 |
| system_log 清理天数 | 30天 | 清理30天前的数据 |
| 压缩格式优先级 | zip > tar.gz | 优先使用zip压缩 |
| Redis 数据目录(新平台) | /data/middleware/redis/data | |
| Redis 数据目录(传统) | /var/www/redis/data | |
---
## 7. 异常处理与容错要求
### 7.1 通用异常处理
1. **命令执行失败**:记录到日志,继续后续检测
2. **文件不存在**:跳过相关操作,输出警告信息
3. **权限不足**:提示用户使用 root 权限运行脚本
4. **操作确认**:重要操作前需要用户确认
5. **文件备份**:修改文件前自动创建备份
### 7.2 安全机制
1. **权限检查**:脚本运行时检查是否具有足够的权限
2. **文件备份**:修改重要文件前自动创建备份
3. **操作确认**:重要操作需要用户确认
4. **路径验证**:执行文件操作前验证路径有效性
5. **日志记录**:所有操作都记录到日志文件
6. **高风险操作保护**:危险操作仅提供指南,不自动执行
---
## 8. 交付物
### 8.1 脚本文件
1. **主脚本**:`AuxiliaryTool/ScriptTool/问题处理/issue_handler.sh`
2. **辅助脚本**:`AuxiliaryTool/ScriptTool/问题处理/fix_chinese_linebreaks.sh`
### 8.2 文档文件
1. **操作说明文档**:`AuxiliaryTool/ScriptTool/问题处理/操作说明文档.md`
2. **需求文档**:`Docs/PRD/问题处理/_PRD_问题处理需求文档.md`
### 8.3 日志文件
1. **操作日志**:`{脚本目录}/issue_handler.log`
---
## 9. 验收标准
### 9.1 功能验收标准
1. 脚本可在 Linux 服务器上正常运行(需要 root 权限)
2. 可正确识别新统一平台和标准版平台
3. 所有版本包更新功能正常工作
4. 所有权限修复功能正常工作
5. IP 配置替换功能正常工作
6. 磁盘清理功能正常工作
7. 日志导出和压缩功能正常工作
8. 服务异常修复功能正常工作
9. 网络配置修复功能正常工作
10. 数据备份功能正常工作
### 9.2 安全验收标准
1. 所有重要操作前都有确认提示
2. 修改文件前都创建备份
3. 日志记录完整,可追溯所有操作
4. 高风险操作不自动执行
5. 非交互模式下需要明确的参数确认
### 9.3 文档验收标准
1. 操作说明文档清晰易懂
2. 需求文档完整准确
3. 所有功能都有详细说明
4. 提供常见问题解决方案
---
## 10. 命令行参数说明
### 10.1 基本参数
| 参数 | 说明 |
|------|------|
| `-h, --help` | 显示帮助信息 |
| `-v, --version` | 显示脚本版本 |
### 10.2 动作参数
| 参数 | 说明 |
|------|------|
| `--action {name}` 或 `-a {name}` | 指定要执行的动作 |
### 10.3 可用动作列表
| 动作名称 | 说明 |
|---------|------|
| `export_and_compress_logs` | 导出并压缩日志 |
| `export_logs` | 导出并压缩日志(别名) |
| `rotate_logs` | 轮转日志文件 |
| `clean_deleted_files` | 清理已删除但未释放的文件(交互式) |
| `clean_deleted_files_cron` | 清理已删除但未释放的文件(定时任务版,无交互) |
| `cleanup_system_log_table` | 清理数据库 system_log 表 |
| `show_disk_partition_info` | 显示磁盘分区调整参考信息 |
| `fix_ip_configurations` | 批量替换配置文件中的 IP |
| `fix_permissions` | 修复文件权限 |
| `fix_db_permissions` | 修复数据库用户权限 |
| `fix_nginx_user` | 修复 Nginx 用户权限 |
| `fix_external_service_disconnect` | 修复对外后端服务异常掉线 |
| `fix_ntp_config` | 修复 NTP 服务配置 |
| `fix_port_access` | 检查并开放端口 |
| `fix_dns_config` | 修复 DNS 配置 |
| `redis_container_exception` | 修复 Redis 容器异常 |
| `backup_environment_data` | 打包备份环境数据 |
| `backup_env` | 打包备份环境数据(别名) |
| `update_inner_backend_jar` | 更新预定系统对内后端包 |
| `update_external_backend_jar` | 更新预定系统对外后端包 |
| `update_frontend` | 更新预定系统前台包 |
| `update_backend` | 更新预定系统后台包 |
| `update_ops_backend` | 更新运维后端包 |
| `update_ops_frontend` | 更新运维前端包 |
### 10.4 平台参数
| 参数 | 说明 |
|------|------|
| `--platform {type}` 或 `-p {type}` | 指定平台类型(auto/new/standard),默认为 auto |
### 10.5 交互控制参数
| 参数 | 说明 |
|------|------|
| `--non-interactive` | 非交互模式 |
| `--yes` 或 `-y` | 非交互模式下默认同意所有操作 |
| `--no` | 非交互模式下默认拒绝所有操作 |
### 10.6 参数化输入参数
| 参数 | 说明 |
|------|------|
| `--db-root-pass {password}` | 数据库 root 密码 |
| `--old-ip {ip}` | 需要替换的旧 IP 地址 |
| `--new-ip {ip}` | 新的 IP 地址 |
### 10.7 使用示例
```bash
# 交互式模式(默认)
sudo ./issue_handler.sh
# 命令行模式 - 修复文件权限
sudo ./issue_handler.sh --action fix_permissions
# 命令行模式 - 指定平台类型
sudo ./issue_handler.sh --action fix_permissions --platform new
# 命令行模式 - 非交互 + 自动同意
sudo ./issue_handler.sh --action fix_dns_config --non-interactive --yes
# 命令行模式 - IP 替换
sudo ./issue_handler.sh --action fix_ip_configurations --old-ip 192.168.1.100 --new-ip 192.168.1.200
# 定时任务版本 - 清理已删除文件
sudo ./issue_handler.sh --action clean_deleted_files_cron
# Redis 容器异常修复 - 非交互 + 自动同意
sudo ./issue_handler.sh --action redis_container_exception --non-interactive --yes
```
---
## 11. 定时任务配置
### 11.1 清理已删除文件的定时任务
添加到 crontab:
```bash
# 编辑 root 用户的 crontab
sudo crontab -e
# 添加以下行(每天凌晨 3 点执行)
0 3 * * * /bin/bash /path/to/issue_handler.sh --action clean_deleted_files_cron
```
### 11.2 定时任务执行日志
定时任务执行时会将日志记录到 `issue_handler.log` 文件中,包含:
- 执行时间
- 检测到的文件数量
- 清理结果统计
---
## 需求规范
- 代码规范:`Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 问题总结:`Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 方法总结:`Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 文档规范:`Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 测试规范:`Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
# _PRD_问题处理需求文档_计划执行.md
> 版本:V1.0
> 创建日期:2026-01-28
> 关联需求:`_PRD_问题处理需求文档.md`
> 适用范围:问题处理脚本开发维护
---
## 1. 文档说明
### 1.1 目的
本文档基于问题处理需求文档,制定详细的开发和维护计划,确保脚本功能完整、质量可控。
### 1.2 范围
- 主脚本:`issue_handler.sh` (v1.0.0)
- 辅助脚本:`fix_chinese_linebreaks.sh`
- 功能优化
- Bug 修复
### 1.3 版本管理原则
> **核心原则:功能完整实现,稳定可靠运行。**
| 操作类型 | 版本号变化 | 说明 |
|----------|-----------|------|
| Bug 修复 | Patch 版本 (x.x.z+1) | 1.0.0 → 1.0.1 |
| 功能新增 | Minor 版本 (x.y+1.0) | 1.0.1 → 1.1.0 |
| 破坏性变更 | Major 版本 (x+1.0.0) | 1.1.0 → 2.0.0 |
---
## 2. 当前状态评估
### 2.1 版本信息
| 版本 | 当前版本 | 状态 | 说明 |
|------|----------|------|------|
| issue_handler.sh | 1.0.0 | ✅ 稳定 | 功能完整,已投入使用 |
### 2.2 功能实现状态
| 功能分类 | 子功能 | 实现状态 | 说明 |
|---------|--------|----------|------|
| 更新版本包 | 对内后端包 | ✅ 已实现 | 支持新统一平台和标准版平台 |
| | 对外后端包 | ✅ 已实现 | 支持新统一平台和标准版平台 |
| | 预定系统前台包 | ✅ 已实现 | 支持新统一平台和标准版平台 |
| | 预定系统后台包 | ✅ 已实现 | 支持新统一平台和标准版平台 |
| | 运维后端包 | ✅ 已实现 | 支持新统一平台和标准版平台 |
| | 运维前端包 | ✅ 已实现 | 支持新统一平台和标准版平台 |
| 修复文件权限 | 普通文件权限 | ✅ 已实现 | 支持新统一平台和标准版平台 |
| | 数据库用户权限 | ✅ 已实现 | 支持Docker容器 |
| | Nginx用户权限 | ✅ 已实现 | 支持ujava2和uweb容器 |
| | Redis配置权限 | ✅ 已实现 | 包含在普通文件权限中 |
| | rc.local权限 | ✅ 已实现 | 通用权限修复 |
| 修复配置文件IP | IP地址修正 | ✅ 已实现 | 支持批量替换 |
| 修复系统磁盘问题 | 清理已删文件 | ✅ 已实现 | 支持交互式和定时任务版本 |
| | 轮转日志文件 | ✅ 已实现 | 支持100MB阈值轮转 |
| | system_log表处理 | ✅ 已实现 | 支持30天数据清理 |
| 导出现场日志 | 日志导出压缩 | ✅ 已实现 | 支持zip和tar.gz格式 |
| 修复服务异常 | 对外服务掉线 | ✅ 已实现 | 自动启动并验证 |
| | NTP服务配置 | ✅ 已实现 | 支持chronyd和ntpd |
| 网络配置 | 端口开放 | ✅ 已实现 | 支持firewalld和iptables |
| | DNS异常修复 | ✅ 已实现 | 支持非交互模式 |
| 数据备份 | 环境数据备份 | ✅ 已实现 | 打包配置、日志和系统信息 |
| 容器异常处理 | Redis容器异常 | ✅ 已实现 | 支持交互式和非交互模式 |
**图例:**
- ✅ 已实现
- ❌ 未实现
- ⚠️ 有差异
### 2.3 待办事项
| 优先级 | 任务 | 状态 |
|--------|------|------|
| 🟢 低 | 文档完善 | ✅ 已完成 |
| 🟢 低 | 需求文档更新 | ✅ 已完成 |
| 🟢 低 | 计划文档创建 | 🔄 进行中 |
---
## 3. 开发计划
### 3.1 版本规划
#### v1.0.0 - 当前版本(稳定)
**状态:** ✅ 已发布
| 功能 | 实现状态 |
|------|----------|
| 版本包更新(6种) | ✅ |
| 文件权限修复(5种) | ✅ |
| 配置文件IP修复 | ✅ |
| 磁盘问题处理(4种) | ✅ |
| 日志导出压缩 | ✅ |
| 服务异常修复(2种) | ✅ |
| 网络配置(2种) | ✅ |
| 数据备份 | ✅ |
| 容器异常处理 | ✅ |
#### v1.1.0 - 功能增强(规划中)
**目标:** 基于当前 v1.0.0 版本,规划和实现功能增强
| 任务ID | 任务描述 | 说明 | 状态 |
|--------|----------|------|------|
| T-1101 | 增加更多中间件支持 | 增加对Emqx等中间件的异常处理 | 🔲 计划中 |
| T-1102 | 支持配置文件外部化 | 支持外部配置文件定义平台路径 | 🔲 计划中 |
| T-1103 | 增加操作审计日志 | 详细记录所有操作历史 | 🔲 计划中 |
#### v1.2.0 - 更多功能增强(远期规划)
| 任务ID | 任务描述 | 说明 | 状态 |
|--------|----------|------|------|
| T-1201 | Web界面支持 | 提供Web界面进行问题处理 | 🔲 计划中 |
| T-1202 | 远程执行支持 | 支持SSH远程执行问题处理 | 🔲 计划中 |
| T-1203 | 批量服务器处理 | 支持对多台服务器批量处理 | 🔲 计划中 |
---
### 3.2 任务分解
#### 阶段一:文档完善(v1.0.1)
**预计工作量:** 1 天
| 步骤 | 任务 | 工作内容 | 产出物 |
|------|------|----------|--------|
| 1.1 | 需求文档创建 | 创建完整的需求文档 | _PRD_问题处理需求文档.md |
| 1.2 | 计划文档创建 | 创建开发和维护计划 | _PRD_问题处理需求文档_计划执行.md |
| 1.3 | 文档验证 | 验证文档与代码一致性 | 验证报告 |
#### 阶段二:代码优化(v1.1.0)
**预计工作量:** 5-7 天
| 步骤 | 任务 | 工作内容 | 产出物 |
|------|------|----------|--------|
| 2.1 | 代码规范检查 | 按代码规范检查并修复 | 规范化代码 |
| 2.2 | 错误处理增强 | 完善异常处理和容错逻辑 | 增强的错误处理 |
| 2.3 | 日志输出优化 | 统一日志格式和输出级别 | 统一的日志格式 |
| 2.4 | 性能优化 | 减少不必要的命令执行 | 优化后的脚本 |
| 2.5 | 代码注释完善 | 补充核心函数注释 | 完善的代码注释 |
#### 阶段三:功能增强(v1.2.0+)
**预计工作量:** 按具体功能确定
| 步骤 | 任务 | 工作内容 | 产出物 |
|------|------|----------|--------|
| 3.1 | 需求评审 | 评审新功能需求和优先级 | 需求评审报告 |
| 3.2 | 设计评审 | 评审技术方案和实现方式 | 设计文档 |
| 3.3 | 功能开发 | 开发新功能 | 新功能代码 |
| 3.4 | 功能测试 | 执行测试用例 | 测试报告 |
| 3.5 | 发布上线 | 版本发布和部署 | 发布说明 |
---
## 4. 开发流程规范
### 4.1 新功能开发流程
```mermaid
graph TD
A[需求提出] --> B[需求评审]
B --> C[技术方案设计]
C --> D[功能开发]
D --> E[代码审查]
E --> F[功能测试]
F --> G[文档更新]
G --> H[版本发布]
```
### 4.2 Bug 修复流程
```mermaid
graph TD
A[Bug报告] --> B[Bug确认]
B --> C[影响分析]
C --> D[修复方案设计]
D --> E[代码修复]
E --> F[验证测试]
F --> G[版本发布]
```
### 4.3 版本发布流程
| 阶段 | 操作 | 说明 |
|------|------|------|
| 1 | 版本号更新 | 修改 SCRIPT_VERSION 变量 |
| 2 | 变更日志 | 编写 CHANGELOG |
| 3 | 代码审查 | 人工审查代码变更 |
| 4 | 功能测试 | 执行测试用例 |
| 5 | 文档更新 | 更新需求文档和计划文档 |
| 6 | 发布 | 提交代码到版本库 |
---
## 5. 测试计划
### 5.1 测试环境
| 环境类型 | 测试环境 |
|----------|----------|
| 开发环境 | CentOS 7/8 或 Ubuntu 20.04+ |
| 测试服务器 | 新统一平台 + 标准版平台 |
| 容器环境 | Docker 19.03+ |
### 5.2 测试用例
| 用例ID | 测试项 | 测试环境 | 预期结果 |
|--------|--------|----------|----------|
| TC-001 | 脚本启动 | Linux | 正常启动,显示菜单 |
| TC-002 | 平台检测 | 新统一平台 | 识别为new平台 |
| TC-003 | 平台检测 | 标准版平台 | 识别为standard平台 |
| TC-004 | 更新对内后端包 | 新统一平台 | 成功备份并更新 |
| TC-005 | 更新对外后端包 | 标准版平台 | 成功备份并更新 |
| TC-006 | 更新前台包 | 新统一平台 | 成功备份并更新 |
| TC-007 | 更新后台包 | 标准版平台 | 成功备份并更新 |
| TC-008 | 修复文件权限 | 新统一平台 | 权限正确设置 |
| TC-009 | 修复数据库权限 | 标准版平台 | 用户创建成功,权限授予成功 |
| TC-010 | 修复Nginx用户权限 | 新统一平台 | 配置文件修改,服务重启成功 |
| TC-011 | 修复配置文件IP | 标准版平台 | IP批量替换成功 |
| TC-012 | 清理已删除文件 | Linux | 进程结束,空间释放 |
| TC-013 | 轮转日志文件 | Linux | 日志轮转并压缩 |
| TC-014 | 清理system_log表 | Linux | 30天前数据删除成功 |
| TC-015 | 导出并压缩日志 | Linux | 日志压缩包生成成功 |
| TC-016 | 修复对外服务掉线 | 标准版平台 | 服务启动成功 |
| TC-017 | 修复NTP服务配置 | Linux | 服务配置并重启成功 |
| TC-018 | 修复端口开放 | Linux | 防火墙规则添加成功 |
| TC-019 | 修复DNS配置 | Linux | DNS配置添加成功 |
| TC-020 | 备份环境数据 | Linux | 备份文件生成成功 |
| TC-021 | Redis容器异常处理 | Linux | 容器重启成功 |
| TC-022 | 命令行模式-非交互 | Linux | 非交互执行成功 |
| TC-023 | 定时任务版本执行 | Linux | 自动清理执行成功 |
### 5.3 测试覆盖
| 测试类型 | 覆盖范围 | 测试方法 |
|---------|----------|----------|
| 功能测试 | 所有功能模块 | 手动测试 |
| 回归测试 | 核心功能 | 每次版本发布前 |
| 兼容性测试 | 新统一平台 + 标准版平台 | 手动测试 |
---
## 6. 变更管理
### 6.1 变更类型
| 变更类型 | 版本号变化 | 示例 |
|----------|-----------|------|
| Bug 修复 | Patch 版本 (x.x.z+1) | 1.0.0 → 1.0.1 |
| 功能新增 | Minor 版本 (x.y+1.0) | 1.0.1 → 1.1.0 |
| 破坏性变更 | Major 版本 (x+1.0.0) | 1.1.0 → 2.0.0 |
### 6.2 变更流程
1. **变更申请**
- 填写变更申请表
- 说明变更原因和影响范围
2. **影响评估**
- 评估对现有功能的影响
- 确定变更优先级
3. **变更审批**
- 技术负责人审批
- 确认变更方案
4. **变更实施**
- 按开发流程实施变更
- 确保代码质量
5. **变更验证**
- 执行测试用例
- 验证功能完整性
6. **变更发布**
- 更新版本号
- 编写发布说明
### 6.3 变更记录
| 版本 | 日期 | 变更类型 | 变更内容 | 影响范围 |
|------|------|----------|----------|----------|
| 1.0.0 | 2026-01-28 | Major | 初始版本,功能完整 | 全部功能 |
---
## 7. 风险管理
### 7.1 技术风险
| 风险 | 影响 | 概率 | 应对措施 |
|------|------|------|----------|
| 平台识别错误 | 高 | 低 | 多条件验证,增加日志输出 |
| 权限不足导致操作失败 | 高 | 中 | 提前检查权限,给出明确提示 |
| 数据库操作失败 | 中 | 低 | 充分验证,添加异常处理 |
| 容器操作失败 | 高 | 低 | 检查容器状态,添加重试机制 |
### 7.2 流程风险
| 风险 | 影响 | 概率 | 应对措施 |
|------|------|------|----------|
| 测试覆盖不足 | 高 | 中 | 完善测试用例,自动化测试 |
| 文档更新滞后 | 中 | 高 | 文档与代码同步更新 |
| 版本发布遗漏 | 高 | 低 | 建立发布检查清单 |
---
## 8. 质量保证
### 8.1 代码质量标准
| 指标 | 标准 | 检查方式 |
|------|------|----------|
| 代码规范 | 符合项目代码规范 | 人工审查 |
| 注释覆盖率 | 核心函数必须有注释 | 人工审查 |
| 错误处理 | 所有可能的异常都有处理 | 代码审查+测试 |
| 日志完整性 | 关键步骤都有日志输出 | 测试验证 |
### 8.2 测试质量标准
| 指标 | 标准 | 检查方式 |
|------|------|----------|
| 测试用例覆盖率 | 核心功能 100% | 测试用例清单 |
| 用例执行通过率 | 100% | 测试报告 |
| 平台兼容性 | 新统一平台 + 标准版平台 | 兼容性测试报告 |
### 8.3 文档质量标准
| 文档 | 更新频率 | 责任人 |
|------|----------|--------|
| 需求文档 | 每次版本更新 | 技术负责人 |
| 计划文档 | 每季度或重大变更 | 项目经理 |
| 代码注释 | 随代码更新 | 开发人员 |
| 操作说明 | 每次版本更新 | 技术负责人 |
---
## 9. 沟通协作
### 9.1 角色职责
| 角色 | 职责 |
|------|------|
| 技术负责人 | 架构设计、技术决策、代码审查 |
| 开发人员 | 脚本开发、Bug修复、代码注释 |
| 测试工程师 | 功能测试、兼容性测试 |
| 运维工程师 | 部署、监控、问题反馈 |
### 9.2 沟通机制
| 会议类型 | 频率 | 参与人员 | 议题 |
|----------|------|----------|------|
| 需求评审会 | 按需 | 全体 | 新功能需求讨论 |
| 技术方案评审会 | 按需 | 技术人员 | 实现方案讨论 |
| 版本发布会 | 每次发布 | 全体 | 版本内容、注意事项 |
| 双周例会 | 双周 | 全体 | 进展同步、问题讨论 |
### 9.3 协作工具
| 工具类型 | 推荐工具 | 用途 |
|----------|----------|------|
| 代码管理 | Git | 版本控制、分支管理 |
| 问题跟踪 | GitHub Issues / Jira | Bug跟踪、需求管理 |
| 文档协作 | Markdown + Git | 文档版本控制 |
| 即时沟通 | 企业微信 / 钉钉 | 日常沟通 |
---
## 10. 里程碑
| 里程碑 | 目标 | 预计时间 | 状态 |
|--------|------|----------|------|
| M1 | 需求文档完成 | 2026-01-28 | ✅ 已完成 |
| M2 | 计划文档完成 | 2026-01-28 | ✅ 已完成 |
| M3 | v1.0.0 稳定运行 | 2026-01-28 | ✅ 已完成 |
| M4 | v1.1.0 功能增强完成 | 待定 | 🔲 计划中 |
| M5 | v1.2.0 更多功能增强完成 | 待定 | 🔲 计划中 |
---
## 11. 附录
### 11.1 文件路径
**脚本文件:**
- 主脚本:`AuxiliaryTool/ScriptTool/问题处理/issue_handler.sh`
- 辅助脚本:`AuxiliaryTool/ScriptTool/问题处理/fix_chinese_linebreaks.sh`
**文档文件:**
- 需求文档:`Docs/PRD/问题处理/_PRD_问题处理需求文档.md`
- 计划文档:`Docs/PRD/问题处理/_PRD_问题处理需求文档_计划执行.md`
- 操作说明:`AuxiliaryTool/ScriptTool/问题处理/操作说明文档.md`
**输出文件:**
- 操作日志:`{脚本目录}/issue_handler.log`
- 日志备份:`logs_backup_{时间戳}.zip``.tar.gz`
- 环境备份:`env_backup_{时间戳}.tar.gz`
### 11.2 相关文档
- 代码规范:`Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 问题总结:`Docs/PRD/01规范文档/_PRD_问题总结_记录文档.md`
- 方法总结:`Docs/PRD/01规范文档/_PRD_方法总结_记录文档.md`
- 文档规范:`Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
- 测试规范:`Docs/PRD/01规范文档/_PRD_规范文档_测试规范.md`
### 11.3 快速参考
**交互式模式运行:**
```bash
cd /path/to/AuxiliaryTool/ScriptTool/问题处理
chmod +x issue_handler.sh
sudo ./issue_handler.sh
```
**命令行模式运行:**
```bash
# 修复文件权限
sudo ./issue_handler.sh --action fix_permissions
# 指定平台类型
sudo ./issue_handler.sh --action fix_permissions --platform new
# 非交互模式
sudo ./issue_handler.sh --action fix_dns_config --non-interactive --yes
# IP替换
sudo ./issue_handler.sh --action fix_ip_configurations --old-ip 192.168.1.100 --new-ip 192.168.1.200
# 定时任务版本
sudo ./issue_handler.sh --action clean_deleted_files_cron
```
**定时任务配置:**
```bash
# 编辑root用户的crontab
sudo crontab -e
# 添加以下行(每天凌晨3点执行)
0 3 * * * /bin/bash /path/to/issue_handler.sh --action clean_deleted_files_cron
```
### 11.4 可用动作列表
| 动作名称 | 说明 |
|---------|------|
| `export_and_compress_logs` | 导出并压缩日志 |
| `rotate_logs` | 轮转日志文件 |
| `clean_deleted_files` | 清理已删除但未释放的文件(交互式) |
| `clean_deleted_files_cron` | 清理已删除但未释放的文件(定时任务版) |
| `cleanup_system_log_table` | 清理数据库system_log表 |
| `show_disk_partition_info` | 显示磁盘分区调整参考信息 |
| `fix_ip_configurations` | 批量替换配置文件中的IP |
| `fix_permissions` | 修复文件权限 |
| `fix_db_permissions` | 修复数据库用户权限 |
| `fix_nginx_user` | 修复Nginx用户权限 |
| `fix_external_service_disconnect` | 修复对外后端服务异常掉线 |
| `fix_ntp_config` | 修复NTP服务配置 |
| `fix_port_access` | 检查并开放端口 |
| `fix_dns_config` | 修复DNS配置 |
| `redis_container_exception` | 修复Redis容器异常 |
| `backup_environment_data` | 打包备份环境数据 |
| `update_inner_backend_jar` | 更新预定系统对内后端包 |
| `update_external_backend_jar` | 更新预定系统对外后端包 |
| `update_frontend` | 更新预定系统前台包 |
| `update_backend` | 更新预定系统后台包 |
| `update_ops_backend` | 更新运维后端包 |
| `update_ops_frontend` | 更新运维前端包 |
### 11.5 联系方式
如有问题或建议,请联系:
- 技术负责人:[待填写]
- 项目经理:[待填写]
---
**文档版本历史:**
| 版本 | 日期 | 变更内容 | 作者 |
|------|------|----------|------|
| V1.0 | 2026-01-28 | 初始版本 | Claude |
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论