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

fix(add): 输出AI远程自动化部署需求文档,调整服务自检脚本以及增加文件权限修复操作。

上级 8dad45fa
......@@ -143,7 +143,6 @@ $Global:UjavaServices = @{
"gateway" = "ubains-gateway.jar"
"system" = "ubains-modules-system.jar"
"meeting2.0" = "ubains-meeting-inner-api-1.0-SNAPSHOT.jar"
"meeting3.0" = "ubains-meeting-inner-api-1.0-SNAPSHOT.jar"
"mqtt" = "ubains-meeting-mqtt-1.0-SNAPSHOT.jar"
"quartz" = "ubains-meeting-quartz-1.0-SNAPSHOT.jar"
"message" = "ubains-meeting-message-scheduling-1.0-SNAPSHOT.jar"
......@@ -253,7 +252,6 @@ $NewPlatformLogs = @(
@{ Name = "gatway_log.out"; RemotePath = "/data/services/api/auth/auth-sso-gatway/log.out" },
@{ Name = "system_log.out"; RemotePath = "/data/services/api/auth/auth-sso-system/log.out" },
@{ Name = "对内2.0_ubains-INFO-AND-ERROR.log"; RemotePath = "/data/services/api/java-meeting/java-meeting2.0/logs/ubains-INFO-AND-ERROR.log" },
@{ Name = "对内3.0_ubains-INFO-AND-ERROR.log"; RemotePath = "/data/services/api/java-meeting/java-meeting3.0/logs/ubains-INFO-AND-ERROR.log" },
@{ Name = "对外服务_ubains-INFO-AND-ERROR.log"; RemotePath = "/data/services/api/java-meeting/java-meeting-extapi/logs/ubains-INFO-AND-ERROR.log" },
@{ Name = "信息调度_ubains-INFO-AND-ERROR.log"; RemotePath = "/data/services/api/java-meeting/java-message-scheduling/logs/ubains-INFO-AND-ERROR.log" },
@{ Name = "MQTT_ubains-INFO-AND-ERROR.log"; RemotePath = "/data/services/api/java-meeting/java-mqtt/logs/ubains-INFO-AND-ERROR.log" },
......@@ -1097,62 +1095,104 @@ function Test-MQTTConnection-Shell {
$results = @()
# Redis检测结果
# Redis检测结果(包含详细信息)
if ($data.redis) {
$redisStatus = $data.redis.status
$icon = if ($redisStatus -eq "running") { "[运行]" } else { "[停止]" }
$redisSuccess = ($redisStatus -eq "running")
# 构建详细信息字符串
$redisDetails = ""
if ($redisSuccess) {
$version = if ($data.redis.version) { $data.redis.version } else { "unknown" }
$memory = if ($data.redis.memory) { $data.redis.memory } else { "N/A" }
$clients = if ($data.redis.clients) { $data.redis.clients } else { "0" }
$redisDetails = "Redis服务完全正常(连接+读写+删除+信息采集) | 容器: $($data.redis.container) | 端口: $($data.redis.port) | 版本: $version | 内存: $memory | 连接数: $clients"
Write-Log -Level "INFO" -Message " $icon Redis ($($data.redis.container):$($data.redis.port)): $redisStatus | 版本: $version | 内存: $memory | 连接: $clients"
} else {
$redisDetails = if ($redisStatus -eq "stopped") { "容器未运行" } elseif ($redisStatus -eq "not_found") { "容器不存在" } else { "连接失败" }
Write-Log -Level "INFO" -Message " $icon Redis ($($data.redis.container):$($data.redis.port)): $redisStatus"
}
$results += [PSCustomObject]@{
Check = "Redis连接 ($($data.redis.container):$($data.redis.port))"
Check = "Redis连接检测"
Status = if ($redisSuccess) { "正常" } else { "异常" }
Details = if ($redisSuccess) { "运行中" } else { "未运行" }
Details = $redisDetails
Success = $redisSuccess
}
}
# MySQL检测结果
# MySQL检测结果(包含详细信息)
if ($data.mysql) {
$mysqlStatus = $data.mysql.status
$icon = if ($mysqlStatus -eq "running") { "[运行]" } else { "[停止]" }
$mysqlSuccess = ($mysqlStatus -eq "running")
# 构建详细信息字符串
$mysqlDetails = ""
if ($mysqlSuccess) {
$version = if ($data.mysql.version) { $data.mysql.version } else { "unknown" }
$mysqlDetails = "MySQL连接正常 | 容器: $($data.mysql.container) | 端口: $($data.mysql.port) | 版本: $version"
Write-Log -Level "INFO" -Message " $icon MySQL ($($data.mysql.container):$($data.mysql.port)): $mysqlStatus | 版本: $version"
} else {
$mysqlDetails = if ($mysqlStatus -eq "stopped") { "容器未运行" } elseif ($mysqlStatus -eq "not_found") { "容器不存在" } else { "连接失败" }
Write-Log -Level "INFO" -Message " $icon MySQL ($($data.mysql.container):$($data.mysql.port)): $mysqlStatus"
}
$results += [PSCustomObject]@{
Check = "MySQL连接 ($($data.mysql.container):$($data.mysql.port))"
Check = "MySQL连接检测"
Status = if ($mysqlSuccess) { "正常" } else { "异常" }
Details = if ($mysqlSuccess) { "运行中" } else { "未运行" }
Details = $mysqlDetails
Success = $mysqlSuccess
}
}
# EMQX检测结果
# EMQX检测结果(包含详细信息)
if ($data.emqx) {
$emqxStatus = $data.emqx.status
$icon = if ($emqxStatus -eq "running") { "[运行]" } else { "[停止]" }
$emqxSuccess = ($emqxStatus -eq "running")
# 构建详细信息字符串
$emqxDetails = ""
if ($emqxSuccess) {
$dashboardPort = if ($data.emqx.dashboard_port) { $data.emqx.dashboard_port } else { "N/A" }
$emqxDetails = "MQTT服务连接正常 | 容器: $($data.emqx.container) | 端口: $($data.emqx.port) | Dashboard: $dashboardPort"
Write-Log -Level "INFO" -Message " $icon EMQX ($($data.emqx.container):$($data.emqx.port)): $emqxStatus | Dashboard: $dashboardPort"
} else {
$emqxDetails = if ($emqxStatus -eq "stopped") { "容器未运行" } elseif ($emqxStatus -eq "not_found") { "容器不存在" } else { "连接失败" }
Write-Log -Level "INFO" -Message " $icon EMQX ($($data.emqx.container):$($data.emqx.port)): $emqxStatus"
}
$results += [PSCustomObject]@{
Check = "MQTT/EMQX连接 ($($data.emqx.container):$($data.emqx.port))"
Check = "MQTT/EMQX连接检测"
Status = if ($emqxSuccess) { "正常" } else { "异常" }
Details = if ($emqxSuccess) { "运行中" } else { "未运行" }
Details = $emqxDetails
Success = $emqxSuccess
}
}
# FastDFS检测结果
# FastDFS检测结果(包含详细信息)
if ($data.fastdfs) {
$fastdfsStatus = $data.fastdfs.status
$icon = if ($fastdfsStatus -eq "running") { "[运行]" } else { "[停止]" }
$fastdfsSuccess = ($fastdfsStatus -eq "running")
# 构建详细信息字符串
$fastdfsDetails = ""
if ($fastdfsSuccess) {
$network = if ($data.fastdfs.network) { $data.fastdfs.network } else { "unknown" }
$fastdfsDetails = "FastDFS存储服务正常 | 容器: $($data.fastdfs.container) | 网络模式: $network"
Write-Log -Level "INFO" -Message " $icon FastDFS ($($data.fastdfs.container)): $fastdfsStatus | 网络: $network"
} else {
$fastdfsDetails = if ($fastdfsStatus -eq "stopped") { "服务未运行" } elseif ($fastdfsStatus -eq "not_found") { "容器不存在" } else { "检测失败" }
Write-Log -Level "INFO" -Message " $icon FastDFS ($($data.fastdfs.container)): $fastdfsStatus"
}
$results += [PSCustomObject]@{
Check = "FastDFS存储 ($($data.fastdfs.container))"
Check = "FastDFS存储检测"
Status = if ($fastdfsSuccess) { "正常" } else { "异常" }
Details = if ($fastdfsSuccess) { "运行中" } else { "未运行" }
Details = $fastdfsDetails
Success = $fastdfsSuccess
}
}
......@@ -1781,7 +1821,7 @@ function Invoke-RemoteFilePermissionFix {
Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $cmd | Out-Null
# 上传issue_handler.sh
$uploadResult = Upload-ShellScript -Server $Server -ScriptName "issue_handler.sh" -RemotePath $remoteDir -ScriptDir "问题处理"
$uploadResult = Upload-ShellScript -Server $Server -ScriptName "issue_handler.sh" -RemotePath $remoteDir
if (-not $uploadResult) {
Write-Log -Level "ERROR" -Message "[PERM] 上传修复脚本失败"
......@@ -1833,7 +1873,6 @@ function Check-FilePermissions {
"/data/services/api/auth/auth-sso-gatway/run.sh",
"/data/services/api/auth/auth-sso-system/run.sh",
"/data/services/api/java-meeting/java-meeting2.0/run.sh",
"/data/services/api/java-meeting/java-meeting3.0/run.sh",
"/data/services/api/java-meeting/java-meeting-extapi/run.sh",
"/data/services/api/java-meeting/java-message-scheduling/run.sh",
"/data/services/api/java-meeting/java-mqtt/run.sh",
......@@ -1956,7 +1995,7 @@ function Check-FilePermissions {
}
}
return @{ Summary = $summaryText; Lines = lines }
return @{ Summary = $summaryText; Lines = $lines }
}
# ================================
......
......@@ -239,7 +239,6 @@ declare -A UJAVA_SERVICES=(
["gateway"]="ubains-gateway.jar"
["system"]="ubains-modules-system.jar"
["meeting2.0"]="ubains-meeting-inner-api-1.0-SNAPSHOT.jar"
["meeting3.0"]="ubains-meeting-inner-api-1.0-SNAPSHOT.jar"
["mqtt"]="ubains-meeting-mqtt-1.0-SNAPSHOT.jar"
["quartz"]="ubains-meeting-quartz-1.0-SNAPSHOT.jar"
["message"]="ubains-meeting-message-scheduling-1.0-SNAPSHOT.jar"
......@@ -296,8 +295,6 @@ test_ujava_services() {
if [[ -n "$container" ]]; then
if [[ "$svc" == "meeting2.0" ]]; then
n="$(count_proc_in_container "$container" "$jar" "| grep 'java-meeting2.0'")"
elif [[ "$svc" == "meeting3.0" ]]; then
n="$(count_proc_in_container "$container" "$jar" "| grep 'java-meeting3.0'")"
else
n="$(count_proc_in_container "$container" "$jar")"
fi
......@@ -306,8 +303,6 @@ test_ujava_services() {
if [[ "$n" -eq 0 ]]; then
if [[ "$svc" == "meeting2.0" ]]; then
n="$(count_proc_host "$jar" "| grep 'java-meeting2.0'")"
elif [[ "$svc" == "meeting3.0" ]]; then
n="$(count_proc_host "$jar" "| grep 'java-meeting3.0'")"
else
n="$(count_proc_host "$jar")"
fi
......@@ -315,8 +310,6 @@ test_ujava_services() {
else
if [[ "$svc" == "meeting2.0" ]]; then
n="$(count_proc_host "$jar" "| grep 'java-meeting2.0'")"
elif [[ "$svc" == "meeting3.0" ]]; then
n="$(count_proc_host "$jar" "| grep 'java-meeting3.0'")"
else
n="$(count_proc_host "$jar")"
fi
......@@ -837,7 +830,7 @@ check_file_permissions() {
local targets=()
if [[ "$platform" == "new" ]]; then
targets+=( "/data/services/api/auth/auth-sso-auth/run.sh" "/data/services/api/auth/auth-sso-gatway/run.sh" "/data/services/api/auth/auth-sso-system/run.sh"
"/data/services/api/java-meeting/java-meeting2.0/run.sh" "/data/services/api/java-meeting/java-meeting3.0/run.sh" "/data/services/api/java-meeting/java-meeting-extapi/run.sh"
"/data/services/api/java-meeting/java-meeting2.0/run.sh" "/data/services/api/java-meeting/java-meeting-extapi/run.sh"
"/data/services/api/java-meeting/java-message-scheduling/run.sh" "/data/services/api/java-meeting/java-mqtt/run.sh" "/data/services/api/java-meeting/java-quartz/run.sh"
"/data/services/api/start.sh" "/data/services/scripts/*.sh"
"/data/third_party/paperless/run.sh" "/data/third_party/paperless/start.sh"
......@@ -1255,7 +1248,6 @@ _cfgip_list_files_new() {
_cfgip_add_glob_files "/data/services/api/auth/auth-sso-gatway/config" yml yaml properties js json conf
_cfgip_add_glob_files "/data/services/api/auth/auth-sso-system/config" yml yaml properties js json conf
_cfgip_add_glob_files "/data/services/api/java-meeting/java-meeting2.0/config" yml yaml properties js json conf
_cfgip_add_glob_files "/data/services/api/java-meeting/java-meeting3.0/config" yml yaml properties js json conf
_cfgip_add_glob_files "/data/services/api/java-meeting/java-meeting-extapi/config" yml yaml properties js json conf
_cfgip_add_glob_files "/data/services/api/java-meeting/java-message-scheduling/config" yml yaml properties js json conf
_cfgip_add_glob_files "/data/services/api/java-meeting/java-mqtt/config" yml yaml properties js json conf
......@@ -2784,7 +2776,6 @@ export_logs() {
if [[ "$platform" == "new" ]]; then
# 1) ujava 主日志(ps1:/data/services/api/java-meeting/java-meeting2.0/log.out 等)
copy_if_exists "/data/services/api/java-meeting/java-meeting2.0/log.out" "$export_dir/java-meeting2.0_log.out" || true
copy_if_exists "/data/services/api/java-meeting/java-meeting3.0/log.out" "$export_dir/java-meeting3.0_log.out" || true
copy_if_exists "/data/services/api/java-meeting/java-meeting-extapi/log.out" "$export_dir/java-meeting-extapi_log.out" || true
copy_if_exists "/data/services/api/java-meeting/java-message-scheduling/log.out" "$export_dir/java-message-scheduling_log.out" || true
copy_if_exists "/data/services/api/java-meeting/java-mqtt/log.out" "$export_dir/java-mqtt_log.out" || true
......@@ -2800,7 +2791,6 @@ export_logs() {
copy_if_exists "/data/services/logs/auth-sso-system/ubains-INFO-AND-ERROR.log" "$export_dir/auth-sso-system_ubains-INFO-AND-ERROR.log" || true
copy_if_exists "/data/services/logs/java-meeting2.0/ubains-INFO-AND-ERROR.log" "$export_dir/java-meeting2.0_ubains-INFO-AND-ERROR.log" || true
copy_if_exists "/data/services/logs/java-meeting3.0/ubains-INFO-AND-ERROR.log" "$export_dir/java-meeting3.0_ubains-INFO-AND-ERROR.log" || true
copy_if_exists "/data/services/logs/java-meeting-extapi/ubains-INFO-AND-ERROR.log" "$export_dir/java-meeting-extapi_ubains-INFO-AND-ERROR.log" || true
copy_if_exists "/data/services/logs/java-message-scheduling/ubains-INFO-AND-ERROR.log" "$export_dir/java-message-scheduling_ubains-INFO-AND-ERROR.log" || true
copy_if_exists "/data/services/logs/java-mqtt/ubains-INFO-AND-ERROR.log" "$export_dir/java-mqtt_ubains-INFO-AND-ERROR.log" || true
......
......@@ -300,7 +300,6 @@ declare -A UJAVA_SERVICES=(
["gateway"]="ubains-gateway.jar"
["system"]="ubains-modules-system.jar"
["meeting2.0"]="ubains-meeting-inner-api-1.0-SNAPSHOT.jar"
["meeting3.0"]="ubains-meeting-inner-api-1.0-SNAPSHOT.jar"
["mqtt"]="ubains-meeting-mqtt-1.0-SNAPSHOT.jar"
["quartz"]="ubains-meeting-quartz-1.0-SNAPSHOT.jar"
["message"]="ubains-meeting-message-scheduling-1.0-SNAPSHOT.jar"
......@@ -357,8 +356,6 @@ test_ujava_services() {
if [[ -n "$container" ]]; then
if [[ "$svc" == "meeting2.0" ]]; then
n="$(count_proc_in_container "$container" "$jar" "| grep 'java-meeting2.0'")"
elif [[ "$svc" == "meeting3.0" ]]; then
n="$(count_proc_in_container "$container" "$jar" "| grep 'java-meeting3.0'")"
else
n="$(count_proc_in_container "$container" "$jar")"
fi
......@@ -367,8 +364,6 @@ test_ujava_services() {
if [[ "$n" -eq 0 ]]; then
if [[ "$svc" == "meeting2.0" ]]; then
n="$(count_proc_host "$jar" "| grep 'java-meeting2.0'")"
elif [[ "$svc" == "meeting3.0" ]]; then
n="$(count_proc_host "$jar" "| grep 'java-meeting3.0'")"
else
n="$(count_proc_host "$jar")"
fi
......@@ -376,8 +371,6 @@ test_ujava_services() {
else
if [[ "$svc" == "meeting2.0" ]]; then
n="$(count_proc_host "$jar" "| grep 'java-meeting2.0'")"
elif [[ "$svc" == "meeting3.0" ]]; then
n="$(count_proc_host "$jar" "| grep 'java-meeting3.0'")"
else
n="$(count_proc_host "$jar")"
fi
......@@ -898,7 +891,7 @@ check_file_permissions() {
local targets=()
if [[ "$platform" == "new" ]]; then
targets+=( "/data/services/api/auth/auth-sso-auth/run.sh" "/data/services/api/auth/auth-sso-gatway/run.sh" "/data/services/api/auth/auth-sso-system/run.sh"
"/data/services/api/java-meeting/java-meeting2.0/run.sh" "/data/services/api/java-meeting/java-meeting3.0/run.sh" "/data/services/api/java-meeting/java-meeting-extapi/run.sh"
"/data/services/api/java-meeting/java-meeting2.0/run.sh" "/data/services/api/java-meeting/java-meeting-extapi/run.sh"
"/data/services/api/java-meeting/java-message-scheduling/run.sh" "/data/services/api/java-meeting/java-mqtt/run.sh" "/data/services/api/java-meeting/java-quartz/run.sh"
"/data/services/api/start.sh" "/data/services/scripts/*.sh"
"/data/third_party/paperless/run.sh" "/data/third_party/paperless/start.sh"
......@@ -1316,7 +1309,6 @@ _cfgip_list_files_new() {
_cfgip_add_glob_files "/data/services/api/auth/auth-sso-gatway/config" yml yaml properties js json conf
_cfgip_add_glob_files "/data/services/api/auth/auth-sso-system/config" yml yaml properties js json conf
_cfgip_add_glob_files "/data/services/api/java-meeting/java-meeting2.0/config" yml yaml properties js json conf
_cfgip_add_glob_files "/data/services/api/java-meeting/java-meeting3.0/config" yml yaml properties js json conf
_cfgip_add_glob_files "/data/services/api/java-meeting/java-meeting-extapi/config" yml yaml properties js json conf
_cfgip_add_glob_files "/data/services/api/java-meeting/java-message-scheduling/config" yml yaml properties js json conf
_cfgip_add_glob_files "/data/services/api/java-meeting/java-mqtt/config" yml yaml properties js json conf
......@@ -2724,7 +2716,6 @@ export_logs() {
if [[ "$platform" == "new" ]]; then
# 1) ujava 主日志(ps1:/data/services/api/java-meeting/java-meeting2.0/log.out 等)
copy_if_exists "/data/services/api/java-meeting/java-meeting2.0/log.out" "$export_dir/java-meeting2.0_log.out" || true
copy_if_exists "/data/services/api/java-meeting/java-meeting3.0/log.out" "$export_dir/java-meeting3.0_log.out" || true
copy_if_exists "/data/services/api/java-meeting/java-meeting-extapi/log.out" "$export_dir/java-meeting-extapi_log.out" || true
copy_if_exists "/data/services/api/java-meeting/java-message-scheduling/log.out" "$export_dir/java-message-scheduling_log.out" || true
copy_if_exists "/data/services/api/java-meeting/java-mqtt/log.out" "$export_dir/java-mqtt_log.out" || true
......@@ -2740,7 +2731,6 @@ export_logs() {
copy_if_exists "/data/services/logs/auth-sso-system/ubains-INFO-AND-ERROR.log" "$export_dir/auth-sso-system_ubains-INFO-AND-ERROR.log" || true
copy_if_exists "/data/services/logs/java-meeting2.0/ubains-INFO-AND-ERROR.log" "$export_dir/java-meeting2.0_ubains-INFO-AND-ERROR.log" || true
copy_if_exists "/data/services/logs/java-meeting3.0/ubains-INFO-AND-ERROR.log" "$export_dir/java-meeting3.0_ubains-INFO-AND-ERROR.log" || true
copy_if_exists "/data/services/logs/java-meeting-extapi/ubains-INFO-AND-ERROR.log" "$export_dir/java-meeting-extapi_ubains-INFO-AND-ERROR.log" || true
copy_if_exists "/data/services/logs/java-message-scheduling/ubains-INFO-AND-ERROR.log" "$export_dir/java-message-scheduling_ubains-INFO-AND-ERROR.log" || true
copy_if_exists "/data/services/logs/java-mqtt/ubains-INFO-AND-ERROR.log" "$export_dir/java-mqtt_ubains-INFO-AND-ERROR.log" || true
......
#!/usr/bin/env bash
# -------------------- Version --------------------
# 脚本版本号(用于日志/截图回溯;需要时手工递增)
SCRIPT_VERSION="1.0.0"
# -------------------- /Version --------------------
# 设置标准环境变量,避免"参数列表过长"问题
export LANG=C
export LC_ALL=C
# 显示版本信息
show_version() {
echo "issue_handler.sh version: ${SCRIPT_VERSION}"
}
# 检查并修复执行环境问题
fix_execution_environment() {
# 检查脚本换行符
if command -v dos2unix >/dev/null 2>&1; then
# 检查是否需要修复换行符
if head -n 10 "$0" | grep -q $'\r'; then
echo "检测到Windows换行符,正在修复..."
dos2unix "$0" 2>/dev/null || true
echo "换行符已修复,重新执行脚本..."
exec bash "$0" "$@"
exit $?
fi
fi
# 检查BOM标记
if head -c 3 "$0" | od -An -tx1 | tr -d ' ' | grep -q '^efbbbf'; then
echo "检测到BOM标记,正在处理..."
tail -c +4 "$0" > "/tmp/issue_handler_fixed.sh"
chmod +x "/tmp/issue_handler_fixed.sh"
echo "BOM标记已处理,重新执行脚本..."
exec bash "/tmp/issue_handler_fixed.sh" "$@"
exit $?
fi
}
# 执行环境检查和修复
fix_execution_environment "$@"
# 检查并修复环境变量"参数列表过长"问题
fix_env_variable_issues() {
# 检查环境变量长度
if [[ ${#LANG} -gt 100 ]] || [[ ${#LC_ALL} -gt 100 ]]; then
echo "检测到环境变量过长,正在修复..."
export LANG=C
export LC_ALL=C
echo "环境变量已重置"
fi
# 检查环境变量中是否包含空字符
if echo "$LANG" | grep -q $'\0'; then
echo "检测到LANG变量包含空字符,正在修复..."
export LANG=C
fi
if echo "$LC_ALL" | grep -q $'\0'; then
echo "检测到LC_ALL变量包含空字符,正在修复..."
export LC_ALL=C
fi
}
# 处理可能存在的中文换行问题
remove_chinese_newlines() {
local file="$1"
# 检查并移除中文文本中的意外换行符
if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then
# Windows环境下可能存在的换行问题处理
sed -i 's/\r$//' "$file" 2>/dev/null || true
fi
}
# 常见问题处理脚本
# 根据PRD_问题处理文档.md中描述的问题和解决方案,提供自动化处理功能
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# 全局无交互与参数化输入
NON_INTERACTIVE=0 # 1=非交互模式
ASSUME_YES=0 # 1=默认yes, 0=默认no
ARG_DB_ROOT_PASS="" # --db-root-pass
ARG_OLD_IP="" # --old-ip
ARG_NEW_IP="" # --new-ip
# 保留兼容NTP的旧变量,后续与全局开关联动
NTP_AUTO_YES=0
# 日志文件路径
LOG_FILE="$(dirname "$0")/issue_handler.log"
# 日志函数
log_info() {
local message="[INFO] $(date '+%Y-%m-%d %H:%M:%S') $1"
# 处理可能的中文字符显示问题
echo -e "${GREEN}[INFO]${NC} $1" | iconv -f UTF-8 -t UTF-8//IGNORE 2>/dev/null || echo "[INFO] $1"
echo "$message" >> "$LOG_FILE"
}
log_warn() {
local message="[WARN] $(date '+%Y-%m-%d %H:%M:%S') $1"
# 处理可能的中文字符显示问题
echo -e "${YELLOW}[WARN]${NC} $1" | iconv -f UTF-8 -t UTF-8//IGNORE 2>/dev/null || echo "[WARN] $1"
echo "$message" >> "$LOG_FILE"
}
log_error() {
local message="[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $1"
# 处理可能的中文字符显示问题
echo -e "${RED}[ERROR]${NC} $1" | iconv -f UTF-8 -t UTF-8//IGNORE 2>/dev/null || echo "[ERROR] $1"
echo "$message" >> "$LOG_FILE"
}
log_debug() {
local message="[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') $1"
# 处理可能的中文字符显示问题
echo -e "${BLUE}[DEBUG]${NC} $1" | iconv -f UTF-8 -t UTF-8//IGNORE 2>/dev/null || echo "[DEBUG] $1"
echo "$message" >> "$LOG_FILE"
}
# 显示操作完成摘要
show_operation_summary() {
local operation_name="$1"
local status="$2"
echo
echo -e "${CYAN}===========================================${NC}" | iconv -f UTF-8 -t UTF-8//IGNORE 2>/dev/null || echo "==========================================="
echo -e "${CYAN}操作完成摘要${NC}" | iconv -f UTF-8 -t UTF-8//IGNORE 2>/dev/null || echo "操作完成摘要"
echo -e "${CYAN}===========================================${NC}" | iconv -f UTF-8 -t UTF-8//IGNORE 2>/dev/null || echo "==========================================="
echo -e "操作名称: ${PURPLE}${operation_name}${NC}" | iconv -f UTF-8 -t UTF-8//IGNORE 2>/dev/null || echo "操作名称: ${operation_name}"
echo -e "操作状态: $status" | iconv -f UTF-8 -t UTF-8//IGNORE 2>/dev/null || echo "操作状态: $status"
echo -e "${CYAN}===========================================${NC}" | iconv -f UTF-8 -t UTF-8//IGNORE 2>/dev/null || echo "==========================================="
echo
}
# 检测平台类型
detect_platform() {
# 检查是否存在新统一平台目录
if [ -d "/data/services/api" ]; then
log_debug "检测到新统一平台目录" >&2
echo "new"
# 检查是否存在标准版平台目录
elif [ -d "/var/www/java" ]; then
log_debug "检测到标准版平台目录" >&2
echo "standard"
else
log_debug "未检测到特定平台目录,返回unknown" >&2
echo "unknown"
fi
}
# 确认函数
confirm_action() {
local message="$1"
# 非交互模式直接按默认策略返回
if [ "$NON_INTERACTIVE" = "1" ]; then
if [ "$ASSUME_YES" = "1" ]; then
log_debug "非交互模式:$message -> 自动同意(yes)"
return 0
else
log_debug "非交互模式:$message -> 自动拒绝(no)"
return 1
fi
fi
local response
# 处理中文字符显示问题
echo -e -n "${YELLOW}$message (y/N): ${NC}" | iconv -f UTF-8 -t UTF-8//IGNORE 2>/dev/null || echo -n "$message (y/N): "
read response
case "$response" in
[yY][eE][sS]|[yY])
log_debug "用户对 '$message' 的回应: 是"
return 0
;;
*)
log_debug "用户对 '$message' 的回应: 否"
return 1
;;
esac
}
# 获取脚本所在目录
get_script_dir() {
local dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
log_debug "获取脚本所在目录: $dir"
echo "$dir"
}
# 检查并更新版本包
check_and_update_package() {
local package_name="$1"
local target_path="$2"
local package_parent_dir="$3"
local script_dir=$(get_script_dir)
local package_dir=""
log_info "检查更新包: package_name=$package_name, target_path=$target_path, package_parent_dir=$package_parent_dir"
# 优先检查脚本所在目录是否存在对应的目录
if [ -n "$package_parent_dir" ] && [ -d "$script_dir/$package_parent_dir" ]; then
package_dir="$script_dir/$package_parent_dir"
log_info "在脚本目录发现更新包上级目录: $package_dir"
# 检查指定的文件是否存在于该目录中
if [ ! -f "$package_dir/$package_name" ] && [ ! -d "$package_dir/$package_name" ]; then
log_warn "在 $package_dir 目录中未找到 $package_name"
return 1
fi
elif [ -d "$script_dir/$package_name" ]; then
package_dir="$script_dir/$package_name"
log_info "在脚本目录发现更新包: $package_dir"
else
# 如果脚本目录没有,则提示用户输入
echo -n "未在脚本目录找到更新包,请输入新的版本包目录名称: "
read package_dir_input
if [ -n "$package_dir_input" ]; then
# 判断是绝对路径还是相对路径
if [[ "$package_dir_input" == /* ]]; then
package_dir="$package_dir_input"
else
package_dir="$script_dir/$package_dir_input"
fi
fi
fi
log_info "最终使用的更新包目录: $package_dir"
# 检查目录是否存在
if [ -n "$package_dir" ] && [ -d "$package_dir" ]; then
log_info "找到更新包目录: $package_dir"
# 检查目录下是否有文件
if [ -n "$(ls -A "$package_dir")" ]; then
if confirm_action "确认更新 $package_name$target_path?"; then
log_info "用户确认更新操作"
# 检查目标路径是否存在
if [ -e "$target_path" ]; then
# 创建备份目录,使用文件名或目录名加时间戳
local timestamp=$(date +%Y%m%d%H%M%S)
local basename_target=$(basename "$target_path")
local backup_path="$(dirname "$target_path")/${basename_target}_backup_$timestamp"
mkdir -p "$backup_path"
log_info "创建备份目录: $backup_path"
# 根据PRD文档示例,直接移动整个文件或目录
if [ -f "$target_path" ]; then
# 如果目标路径是文件,则备份该文件
mv "$target_path" "$backup_path/"
log_info "已备份原文件到: $backup_path"
else
# 如果目标路径是目录,则备份整个目录内容
if [ -d "$target_path" ]; then
# 备份目录中的所有文件和子目录
for file in "$target_path"/* "$target_path"/.[^.]*; do
# 检查文件是否存在(避免通配符不匹配的情况)
if [ -e "$file" ] || [ -L "$file" ]; then
mv "$file" "$backup_path/"
fi
done
log_info "已备份原目录内容到: $backup_path"
fi
fi
else
# 目标路径不存在,确保父目录存在
mkdir -p "$(dirname "$target_path")"
log_info "目标路径不存在,创建父目录: $(dirname "$target_path")"
fi
# 根据PRD文档示例,复制文件到指定路径
# 示例1: cp /root/api-java-meeting2.0/ubains-meeting-inner-api-1.0-SNAPSHOT.jar /var/www/java/api-java-meeting2.0/
# 示例2: cp -rp /var/www/java/ubains-web-admin/index /var/www/java/ubains-web-admin/static /var/www/java/ubains-web-admin
if [ -f "$package_dir/$package_name" ]; then
# 如果是文件,直接复制文件
cp "$package_dir/$package_name" "$target_path"
log_info "成功更新文件 $package_name 到: $target_path"
else
# 如果是目录,复制目录中的所有内容(而不是目录本身)
mkdir -p "$target_path"
# 复制目录中的所有文件和子目录
for file in "$package_dir"/* "$package_dir"/.[^.]*; do
# 检查文件是否存在(避免通配符不匹配的情况)
if [ -e "$file" ] || [ -L "$file" ]; then
cp -rp "$file" "$target_path/"
fi
done
log_info "成功更新目录 $package_name 到: $target_path"
fi
log_info "成功更新 $package_name: $target_path"
else
log_info "用户取消更新 $package_name"
fi
else
log_warn "更新包目录为空: $package_dir"
return 1
fi
else
log_warn "未找到有效的更新包目录: $package_dir"
return 1
fi
}
# 更新预定系统对内后端包
update_inner_backend_jar() {
local platform=$1
local jar_path=""
local package_name="ubains-meeting-inner-api-1.0-SNAPSHOT.jar"
local package_dir=""
log_info "更新预定系统对内后端包"
if [ "$platform" = "new" ]; then
jar_path="/data/services/api/java-meeting/java-meeting2.0/ubains-meeting-inner-api-1.0-SNAPSHOT.jar"
package_dir="api-java-meeting2.0"
else
jar_path="/var/www/java/api-java-meeting2.0/ubains-meeting-inner-api-1.0-SNAPSHOT.jar"
package_dir="api-java-meeting2.0"
fi
check_and_update_package "$package_name" "$jar_path" "$package_dir"
}
# 更新预定系统对外后端包
update_external_backend_jar() {
local platform=$1
local jar_path=""
local package_name="ubains-meeting-api-1.0-SNAPSHOT.jar"
local package_dir=""
log_info "更新预定系统对外后端包"
if [ "$platform" = "new" ]; then
jar_path="/data/services/api/java-meeting/java-meeting-extapi/ubains-meeting-api-1.0-SNAPSHOT.jar"
package_dir="java-meeting-extapi"
else
jar_path="/var/www/java/external-meeting-api/ubains-meeting-api-1.0-SNAPSHOT.jar"
package_dir="external-meeting-api"
fi
check_and_update_package "$package_name" "$jar_path" "$package_dir"
}
# 更新预定系统前台包
update_frontend() {
local platform=$1
local frontend_path=""
local package_name=""
local package_dir=""
log_info "更新预定系统前台包"
if [ "$platform" = "new" ]; then
frontend_path="/data/services/web/pc/pc-vue2-meetngV2"
package_name="pc-vue2-meetngV2"
package_dir=""
else
frontend_path="/var/www/java/ubains-web-2.0"
package_name="ubains-web-2.0"
package_dir=""
fi
check_and_update_package "$package_name" "$frontend_path" "$package_dir"
}
# 更新预定系统后台包
update_backend() {
local platform=$1
local backend_path=""
local package_name=""
local package_dir=""
log_info "更新预定系统后台包"
if [ "$platform" = "new" ]; then
backend_path="/data/services/web/pc/pc-vue2-backstage"
package_name="pc-vue2-backstage"
package_dir=""
else
backend_path="/var/www/java/ubains-web-admin"
package_name="ubains-web-admin"
package_dir=""
fi
check_and_update_package "$package_name" "$backend_path" "$package_dir"
}
# 更新运维后端包
update_ops_backend() {
local platform=$1
local ops_backend_path=""
local package_name="python-cmdb"
local package_dir=""
log_info "更新运维后端包"
if [ "$platform" = "new" ]; then
ops_backend_path="/data/services/api/python-cmdb"
package_dir=""
else
ops_backend_path="/var/www/html"
package_dir=""
fi
check_and_update_package "$package_name" "$ops_backend_path" "$package_dir"
}
# 更新运维前端包
update_ops_frontend() {
local platform=$1
local ops_frontend_path=""
local package_name=""
local package_dir=""
log_info "更新运维前端包"
if [ "$platform" = "new" ]; then
ops_frontend_path="/data/services/web/pc/pc-vue2-moniter"
package_name="pc-vue2-moniter"
package_dir=""
else
ops_frontend_path="/var/www/html/web-vue-rms"
package_name="web-vue-rms"
package_dir=""
fi
check_and_update_package "$package_name" "$ops_frontend_path" "$package_dir"
}
# 修复文件权限
fix_permissions() {
local platform=$1
log_info "开始修复文件权限,平台类型: $platform"
# 通用权限修复
if [ -f "/etc/rc.local" ]; then
chmod 755 /etc/rc.local
log_info "已修复权限: /etc/rc.local -> 755"
fi
if [ "$platform" = "new" ]; then
# 新统一平台权限修复
log_info "修复新统一平台权限"
declare -A permissions_map=(
["/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
["/data/middleware/mysql/conf/my.cnf"]=644
)
else
# 标准版平台权限修复
log_info "修复标准版平台权限"
declare -A permissions_map=(
["/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
["/usr/local/docker/mysql/my.cnf"]=644
)
fi
# 应用权限修复
for file_path in "${!permissions_map[@]}"; do
if [ -f "$file_path" ]; then
chmod "${permissions_map[$file_path]}" "$file_path"
log_info "已修复权限: $file_path -> ${permissions_map[$file_path]}"
else
log_warn "文件不存在,跳过: $file_path"
fi
done
log_info "文件权限修复完成"
}
# 修复数据库用户权限
fix_db_permissions() {
log_info "开始修复数据库用户权限"
# 检查是否存在docker环境
if ! command -v docker >/dev/null 2>&1; then
log_error "系统中未安装docker,无法执行数据库权限修复"
return 1
fi
# 检查umysql容器是否存在
if ! docker ps -a --format '{{.Names}}' | grep -q '^umysql$'; then
log_error "未找到umysql容器,请确认数据库容器已创建"
return 1
fi
# 检查umysql容器是否正在运行
if ! docker ps --format '{{.Names}}' | grep -q '^umysql$'; then
log_error "umysql容器未运行,请先启动数据库容器"
return 1
fi
echo -n "请输入数据库root密码: "
read -s root_password
echo
if [ -z "$root_password" ]; then
log_warn "未提供数据库密码,跳过数据库权限修复"
return 1
fi
# 构造SQL命令(仅创建用户并赋予权限)
local sql_commands=""
# 1. 创建mysqluser用户(如果不存在)
sql_commands+="CREATE USER IF NOT EXISTS 'mysqluser'@'%' IDENTIFIED BY '${root_password}'; "
# 2. 授权mysqluser用户访问数据库的权限
sql_commands+="GRANT ALL ON ubains.* TO 'mysqluser'@'%'; "
sql_commands+="GRANT ALL ON devops.* TO 'mysqluser'@'%'; "
sql_commands+="ALTER USER 'mysqluser'@'%' IDENTIFIED WITH mysql_native_password BY '${root_password}'; "
sql_commands+="UPDATE mysql.user SET Grant_priv='Y', Super_priv='Y' WHERE user = 'mysqluser' AND host = '%'; "
# 3. 刷新权限
sql_commands+="FLUSH PRIVILEGES; "
log_info "准备执行数据库权限修复命令"
# 在umysql容器中执行SQL命令
if docker exec umysql mysql -uroot -p"$root_password" -e "$sql_commands" 2>/dev/null; then
log_info "数据库用户权限修复成功完成"
# 显示用户和权限信息
log_info "当前数据库用户信息:"
docker exec umysql mysql -uroot -p"$root_password" -e "USE mysql; SELECT host,user,plugin FROM user;" 2>/dev/null | while read line; do
log_info " $line"
done
else
log_error "数据库用户权限修复失败,请检查密码和数据库连接"
return 1
fi
log_info "数据库用户权限修复完成"
}
# 修复nginx用户权限
fix_nginx_user() {
log_info "开始修复nginx用户权限"
# 检查是否存在docker环境
if ! command -v docker >/dev/null 2>&1; then
log_error "系统中未安装docker,无法执行nginx用户权限修复"
return 1
fi
# 检查ujava2容器是否存在
if docker ps -a --format '{{.Names}}' | grep -q '^ujava2$'; then
# 检查ujava2容器是否正在运行
if docker ps --format '{{.Names}}' | grep -q '^ujava2$'; then
log_info "处理ujava2容器中的nginx配置"
# 修改nginx配置文件中的user值为root
if docker exec ujava2 sed -i 's/^user.*/user root;/' /usr/local/nginx/conf/nginx.conf 2>/dev/null; then
log_info "已更新ujava2容器中的nginx用户为root"
# 重启nginx服务
if docker exec ujava2 /usr/local/nginx/sbin/nginx -s reload 2>/dev/null; then
log_info "ujava2容器中的nginx服务已重启"
else
log_error "ujava2容器中的nginx服务重启失败"
return 1
fi
else
log_error "ujava2容器中的nginx配置文件更新失败"
return 1
fi
else
log_error "ujava2容器未运行,请先启动容器"
return 1
fi
fi
# 检查uweb容器是否存在
if docker ps -a --format '{{.Names}}' | grep -q '^uweb$'; then
# 检查uweb容器是否正在运行
if docker ps --format '{{.Names}}' | grep -q '^uweb$'; then
log_info "处理uweb容器中的nginx配置"
# 修改nginx配置文件中的user值为root
if docker exec uweb sed -i 's/^user.*/user root;/' /usr/local/nginx/conf/nginx.conf 2>/dev/null; then
log_info "已更新uweb容器中的nginx用户为root"
# 重启nginx服务
if docker exec uweb /usr/local/nginx/sbin/nginx -s reload 2>/dev/null; then
log_info "uweb容器中的nginx服务已重启"
else
log_error "uweb容器中的nginx服务重启失败"
return 1
fi
else
log_error "uweb容器中的nginx配置文件更新失败"
return 1
fi
else
log_error "uweb容器未运行,请先启动容器"
return 1
fi
fi
log_info "nginx用户权限修复完成"
}
# 清理数据库system_log表中的旧数据
cleanup_system_log_table() {
log_info "开始清理数据库system_log表中的旧数据"
# 检查是否存在docker环境
if ! command -v docker >/dev/null 2>&1; then
log_error "系统中未安装docker,无法执行数据库清理操作"
return 1
fi
# 检查umysql容器是否存在
if ! docker ps -a --format '{{.Names}}' | grep -q '^umysql$'; then
log_error "未找到umysql容器,请确认数据库容器已创建"
return 1
fi
# 检查umysql容器是否正在运行
if ! docker ps --format '{{.Names}}' | grep -q '^umysql$'; then
log_error "umysql容器未运行,请先启动数据库容器"
return 1
fi
echo -n "请输入数据库root密码: "
read -s root_password
echo
if [ -z "$root_password" ]; then
log_warn "未提供数据库密码,跳过数据库清理操作"
return 1
fi
# 检查数据库连接
if ! docker exec umysql mysql -uroot -p"$root_password" -e "SELECT 1 FROM ubains.system_log LIMIT 1;" >/dev/null 2>&1; then
log_error "无法连接到数据库或system_log表不存在"
return 1
fi
# 获取system_log表的记录数和大小
local record_count=$(docker exec umysql mysql -uroot -p"$root_password" -e "SELECT COUNT(*) FROM ubains.system_log;" 2>/dev/null | tail -n 1)
log_info "system_log表当前记录数: $record_count"
# 检查记录数量是否超过阈值(例如100万条)
if [ "$record_count" -gt 1000000 ]; then
log_warn "system_log表记录数超过100万条,建议清理旧数据"
if confirm_action "是否删除30天前的旧数据?"; then
# 删除30天前的数据
local delete_sql="DELETE FROM ubains.system_log WHERE create_time < DATE_SUB(NOW(), INTERVAL 30 DAY);"
if docker exec umysql mysql -uroot -p"$root_password" -e "$delete_sql"; then
log_info "已删除30天前的旧数据"
# 优化表空间
local optimize_sql="OPTIMIZE TABLE ubains.system_log;"
if docker exec umysql mysql -uroot -p"$root_password" -e "$optimize_sql" >/dev/null 2>&1; then
log_info "已优化system_log表空间"
else
log_warn "优化system_log表空间失败"
fi
# 显示删除后的记录数
local new_record_count=$(docker exec umysql mysql -uroot -p"$root_password" -e "SELECT COUNT(*) FROM ubains.system_log;" 2>/dev/null | tail -n 1)
log_info "system_log表删除后记录数: $new_record_count"
else
log_error "删除旧数据失败"
return 1
fi
else
log_info "用户取消清理操作"
fi
else
log_info "system_log表记录数正常,无需清理"
fi
log_info "数据库system_log表清理完成"
}
# 修复配置文件中的IP地址
fix_ip_configurations() {
local platform=$1
log_info "开始检查配置文件中的IP地址,平台类型: $platform"
if [ "$platform" = "new" ]; then
# 新统一平台配置文件路径
log_info "使用新统一平台配置文件路径"
local config_files=(
"/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"
)
else
# 标准版平台配置文件路径
log_info "使用标准版平台配置文件路径"
local config_files=(
"/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"
)
fi
# 获取替换IP:优先用命令行参数;非交互模式且未提供则跳过
local old_ip="${ARG_OLD_IP:-}"
local new_ip="${ARG_NEW_IP:-}"
if [ -z "$old_ip" ] || [ -z "$new_ip" ]; then
if [ "$NON_INTERACTIVE" = "1" ]; then
log_warn "非交互模式且未提供 --old-ip/--new-ip,跳过IP配置修复"
return 1
fi
echo -n "请输入需要替换的旧IP地址: "
read old_ip
echo -n "请输入新的IP地址: "
read new_ip
fi
if [ -n "$old_ip" ] && [ -n "$new_ip" ]; then
log_info "开始替换IP地址: $old_ip -> $new_ip"
# ...existing code(循环替换)...
else
log_warn "未提供有效的IP地址,跳过IP配置修复"
fi
log_info "IP地址配置修复完成"
}
# 清理已删除但未释放的文件
clean_deleted_files() {
log_info "开始清理已删除但未释放的文件"
if command -v lsof >/dev/null 2>&1; then
local result=$(lsof +L1)
if [ -n "$result" ]; then
echo "$result"
if confirm_action "是否强制结束这些进程以释放文件?"; then
log_info "用户确认强制结束进程"
# 自动结束这些进程
log_info "自动结束占用已删除文件的进程"
local pids=$(lsof +L1 | awk 'NR>1 {print $2}' | sort -u)
local success_count=0
local fail_count=0
for pid in $pids; do
if kill -9 "$pid" 2>/dev/null; then
log_info "已结束进程 PID: $pid"
((success_count++))
else
log_warn "无法结束进程 PID: $pid"
((fail_count++))
fi
done
log_info "已完成进程结束操作: 成功 $success_count 个,失败 $fail_count 个"
show_operation_summary "清理已删除文件" "${GREEN}完成${NC} (成功: $success_count, 失败: $fail_count)"
else
log_info "用户取消强制结束进程操作"
show_operation_summary "清理已删除文件" "${YELLOW}已取消${NC}"
fi
else
log_info "未发现已删除但未释放的文件"
show_operation_summary "清理已删除文件" "${GREEN}无需操作${NC}"
fi
else
log_error "系统中未找到lsof命令"
show_operation_summary "清理已删除文件" "${RED}失败${NC} (缺少lsof命令)"
fi
log_info "清理已删除但未释放的文件完成"
}
# 定时任务版本的清理已删除但未释放的文件(无交互)
clean_deleted_files_cron() {
log_info "开始执行定时任务版本的清理已删除但未释放的文件"
if command -v lsof >/dev/null 2>&1; then
local result=$(lsof +L1)
if [ -n "$result" ]; then
log_info "发现以下已删除但未释放的文件:"
echo "$result" | while read line; do
log_info "$line"
done
# 自动结束这些进程
log_info "自动结束占用已删除文件的进程"
local pids=$(lsof +L1 | awk 'NR>1 {print $2}' | sort -u)
for pid in $pids; do
if kill -9 "$pid" 2>/dev/null; then
log_info "已结束进程 PID: $pid"
else
log_warn "无法结束进程 PID: $pid"
fi
done
log_info "已完成进程结束操作"
else
log_info "未发现已删除但未释放的文件"
fi
else
log_error "系统中未找到lsof命令"
fi
log_info "定时任务版本的清理已删除但未释放的文件完成"
}
# 轮转过大的日志文件
rotate_logs() {
local platform=$1
log_info "开始轮转日志文件,平台类型: $platform"
if [ "$platform" = "new" ]; then
local log_files=(
"/data/services/api/java-meeting/java-meeting2.0/logs"
"/data/services/api/java-meeting/java-meeting-extapi/logs"
"/data/services/api/python-cmdb/log"
)
else
local log_files=(
"/var/www/java/api-java-meeting2.0/logs"
"/var/www/java/external-meeting-api/logs"
"/var/www/html/log"
)
fi
local timestamp=$(date +"%Y%m%d_%H%M%S")
local rotated_count=0
local skipped_count=0
local error_count=0
for log_file in "${log_files[@]}"; do
if [ -f "$log_file" ]; then
# 使用更兼容的方式获取文件大小
local size=""
if command -v stat >/dev/null 2>&1; then
# GNU/Linux 系统
if stat --version 2>&1 | grep -q "GNU"; then
size=$(stat -c%s "$log_file")
# BSD/macOS 系统
else
size=$(stat -f%z "$log_file")
fi
elif command -v wc >/dev/null 2>&1; then
size=$(wc -c < "$log_file" | tr -d ' ')
else
log_warn "无法获取文件大小: $log_file"
((error_count++))
continue
fi
log_info "日志文件 $log_file 大小: $size 字节"
if [ "$size" -gt 104857600 ]; then # 100MB
if confirm_action "日志文件 $log_file 超过100MB,是否轮转?"; then
log_info "用户确认轮转日志文件: $log_file"
local backup_name="${log_file}.${timestamp}"
# 检查目标目录是否有足够空间
local dir_space_req=$((size / 1024 / 1024 + 50)) # 额外50MB空间
local dir_path=$(dirname "$log_file")
local dir_space_avail=$(df "$dir_path" | awk 'NR==2 {print $4}')
if [ "$dir_space_avail" -gt "$dir_space_req" ]; then
# 执行轮转操作
if mv "$log_file" "$backup_name"; then
touch "$log_file"
log_info "已轮转日志文件: $log_file"
((rotated_count++))
# 尝试通知相关服务重新打开日志文件
local service_restarted=false
case "$log_file" in
*/java-meeting2.0/log.out)
if command -v systemctl >/dev/null 2>&1 && systemctl list-units | grep -q java-meeting; then
systemctl reload java-meeting2.0 2>/dev/null && service_restarted=true
fi
;;
*/java-meeting-extapi/log.out)
if command -v systemctl >/dev/null 2>&1 && systemctl list-units | grep -q java-meeting-extapi; then
systemctl reload java-meeting-extapi 2>/dev/null && service_restarted=true
fi
;;
esac
if [ "$service_restarted" = true ]; then
log_info "已通知相关服务重新打开日志文件"
else
log_info "请手动重启相关服务以使用新的日志文件"
fi
# 压缩旧日志文件以节省空间
if command -v gzip >/dev/null 2>&1; then
if gzip "$backup_name" 2>/dev/null; then
log_info "已压缩旧日志文件: ${backup_name}.gz"
else
log_warn "压缩旧日志文件失败: $backup_name"
fi
fi
else
log_error "轮转日志文件失败: $log_file"
((error_count++))
fi
else
log_error "磁盘空间不足,无法轮转日志文件: $log_file"
((error_count++))
fi
else
log_info "用户取消轮转日志文件: $log_file"
((skipped_count++))
fi
else
log_info "日志文件大小正常: $log_file"
((skipped_count++))
fi
else
log_warn "日志文件不存在: $log_file"
((skipped_count++))
fi
done
log_info "轮转日志文件完成"
show_operation_summary "轮转日志文件" "${GREEN}完成${NC} (轮转: $rotated_count, 跳过: $skipped_count, 错误: $error_count)"
}
# 导出并压缩日志文件
export_and_compress_logs() {
local platform=$1
log_info "开始导出并压缩日志文件,平台类型: $platform"
# 根据平台类型确定日志文件路径
local log_dirs=()
if [ "$platform" = "new" ]; then
log_dirs=(
"/data/services/api/java-meeting/java-meeting2.0/logs"
"/data/services/api/java-meeting/java-meeting-extapi/logs"
"/data/services/api/python-cmdb/log"
)
else
log_dirs=(
"/var/www/java/api-java-meeting2.0/logs"
"/var/www/java/external-meeting-api/logs"
"/var/www/html/log"
)
fi
# 创建临时目录用于存放日志文件
local timestamp=$(date +"%Y%m%d_%H%M%S")
local temp_dir="/tmp/logs_backup_${timestamp}"
# 尝试使用脚本所在目录而不是/tmp
local script_dir=$(dirname "$(readlink -f "$0")")
if [ -w "$script_dir" ] && [ -d "$script_dir" ]; then
temp_dir="${script_dir}/logs_backup_${timestamp}"
fi
if mkdir -p "$temp_dir"; then
log_info "创建临时目录: $temp_dir"
else
log_error "无法创建临时目录: $temp_dir"
return 1
fi
# 预估所需空间
local total_size=0
for log_dir in "${log_dirs[@]}"; do
if [ -d "$log_dir" ]; then
local dir_size=$(du -sb "$log_dir" 2>/dev/null | cut -f1)
total_size=$((total_size + dir_size))
fi
done
# 检查磁盘空间
local temp_dir_parent=$(dirname "$temp_dir")
local available_space=$(df "$temp_dir_parent" | awk 'NR==2 {print $4}')
local required_space=$((total_size * 2 / 1024 / 1024)) # 预留2倍空间
if [ "$available_space" -lt "$required_space" ]; then
log_warn "目标目录空间可能不足,所需空间: ${required_space}MB,可用空间: ${available_space}MB"
if ! confirm_action "是否继续执行?"; then
log_info "用户取消操作"
rm -rf "$temp_dir"
return 1
fi
fi
# 复制日志文件到临时目录
local copy_success=true
for log_dir in "${log_dirs[@]}"; do
if [ -d "$log_dir" ]; then
local dest_dir="${temp_dir}/${log_dir#/}"
local dest_parent=$(dirname "$dest_dir")
# 创建目标目录结构
if mkdir -p "$dest_parent"; then
if cp -r "$log_dir" "$dest_parent/"; then
log_info "已复制日志目录: $log_dir"
else
log_error "复制日志目录失败: $log_dir"
copy_success=false
fi
else
log_error "创建目标目录失败: $dest_parent"
copy_success=false
fi
else
log_warn "日志目录不存在: $log_dir"
fi
done
if [ "$copy_success" = true ]; then
# 压缩日志文件
local output_file="${temp_dir}.zip"
local compress_success=false
# 尝试使用zip压缩
if command -v zip >/dev/null 2>&1; then
if zip -r "$output_file" "$(basename "$temp_dir")" -j "$temp_dir"; then
log_info "已使用zip压缩日志文件: $output_file"
compress_success=true
else
log_warn "zip压缩失败"
fi
else
log_warn "系统中未找到zip命令"
fi
# 如果zip不可用或失败,尝试使用tar.gz
if [ "$compress_success" = false ]; then
output_file="${temp_dir}.tar.gz"
if command -v tar >/dev/null 2>&1; then
if tar -czf "$output_file" -C "$(dirname "$temp_dir")" "$(basename "$temp_dir")"; then
log_info "已使用tar.gz压缩日志文件: $output_file"
compress_success=true
else
log_error "tar.gz压缩失败"
fi
else
log_error "系统中未找到tar命令"
fi
fi
if [ "$compress_success" = true ]; then
# 清理临时目录
rm -rf "$temp_dir"
log_info "日志文件导出并压缩完成: $output_file"
show_operation_summary "导出并压缩日志" "${GREEN}完成${NC} (文件: $output_file)"
else
log_error "压缩失败,日志文件保留在临时目录: $temp_dir"
show_operation_summary "导出并压缩日志" "${RED}失败${NC} (文件保留在: $temp_dir)"
fi
else
log_error "日志文件复制失败,清理临时目录"
rm -rf "$temp_dir"
show_operation_summary "导出并压缩日志" "${RED}失败${NC}"
fi
log_info "导出并压缩日志文件完成"
}
# 修复对外后端服务异常掉线
fix_external_service_disconnect() {
local platform=$1
local work_dir=""
log_info "开始修复对外后端服务异常掉线,平台类型: $platform"
# 1. 根据平台确定工作目录
if [ "$platform" = "new" ]; then
work_dir="/data/services/api/java-meeting/java-meeting-extapi/"
else
work_dir="/var/www/java/external-meeting-api/"
fi
log_info "工作目录: $work_dir"
if [ ! -d "$work_dir" ]; then
log_error "工作目录不存在: $work_dir,无法执行修复"
show_operation_summary "修复对外后端服务异常掉线" "${RED}失败${NC} (工作目录不存在: $work_dir)"
return 1
fi
# 3. 显式加载 profile,确保 java 在 PATH 中(新增)
if [ -f /etc/profile ]; then
# 使用 . 而不是 source 以兼容 /bin/sh
. /etc/profile
log_info "已在脚本中执行: . /etc/profile"
fi
# 再检查一次 java 是否可用
if ! command -v java >/dev/null 2>&1; then
log_error "在当前非交互环境中未找到 java 命令,请检查 PATH/JAVA_HOME 配置"
log_error "当前 PATH: $PATH"
show_operation_summary "修复对外后端服务异常掉线" "${RED}失败${NC} (当前环境无法找到 java 命令)"
return 1
else
log_info "检测到 java 命令: $(command -v java)"
fi
# 4. 执行 run.sh 启动对外服务(你原来的第 3 步)
local run_script="${work_dir}/run.sh"
if [ ! -x "$run_script" ]; then
if [ -f "$run_script" ]; then
log_warn "run.sh 不可执行,尝试赋予执行权限: $run_script"
chmod +x "$run_script" || {
log_error "无法为 run.sh 赋权执行: $run_script"
show_operation_summary "修复对外后端服务异常掉线" "${RED}失败${NC} (run.sh 无执行权限且赋权失败)"
return 1
}
else
log_error "run.sh 脚本不存在: $run_script"
show_operation_summary "修复对外后端服务异常掉线" "${RED}失败${NC} (run.sh 不存在)"
return 1
fi
fi
if [ "$NON_INTERACTIVE" = "1" ] && [ "$ASSUME_YES" = "1" ]; then
log_info "非交互模式,自动执行: cd $work_dir && ./run.sh"
else
if ! confirm_action "是否启动对外后端服务 $work_dir?"; then
log_info "用户取消启动对外后端服务"
show_operation_summary "修复对外后端服务异常掉线" "${YELLOW}取消${NC} (用户取消启动服务)"
return 0
fi
fi
cd "$work_dir" || {
log_error "无法进入工作目录: $work_dir"
show_operation_summary "修复对外后端服务异常掉线" "${RED}失败${NC} (cd $work_dir 失败)"
return 1
}
log_info "执行启动脚本: ./run.sh"
if ./run.sh >> "$LOG_FILE" 2>&1; then
log_info "run.sh 执行完成,等待 60 秒检查服务状态..."
sleep 60
# 5. 简单检查进程是否已启动
if pgrep -f "ubains-meeting-api-1.0-SNAPSHOT.jar" >/dev/null 2>&1; then
log_info "对外后端服务进程已检测到: ubains-meeting-api-1.0-SNAPSHOT.jar"
show_operation_summary "修复对外后端服务异常掉线" "${GREEN}完成${NC} (服务进程已启动)"
return 0
else
log_warn "run.sh 执行后仍未检测到 ubains-meeting-api-1.0-SNAPSHOT.jar 进程"
show_operation_summary "修复对外后端服务异常掉线" "${YELLOW}完成${NC} (脚本已执行,但未检测到进程)"
return 1
fi
else
log_error "run.sh 执行失败,请检查日志: $LOG_FILE"
show_operation_summary "修复对外后端服务异常掉线" "${RED}失败${NC} (run.sh 执行失败)"
return 1
fi
}
# 修复ntp服务配置
fix_ntp_config() {
log_info "开始修复ntp服务配置"
local chrony_conf="/etc/chrony.conf"
local ntp_conf="/etc/ntp.conf"
local allow_all_line="allow all"
# 检查并配置chrony
if [ -f "$chrony_conf" ]; then
log_info "检测到 chrony 配置文件: $chrony_conf"
if ! grep -q "^$allow_all_line$" "$chrony_conf"; then
if confirm_action "/etc/chrony.conf中未找到allow all配置,是否添加?"; then
log_info "用户确认添加allow all配置"
echo "$allow_all_line" >> "$chrony_conf"
log_info "已在/etc/chrony.conf中添加allow all"
else
log_info "用户取消添加allow all配置"
fi
else
log_info "/etc/chrony.conf中已包含allow all配置"
fi
if confirm_action "是否重启 chrony 服务?"; then
log_info "用户确认重启 chrony 服务"
restart_ntp_service "chronyd"
else
log_info "用户取消重启 chrony 服务"
fi
# 检查并配置ntp
elif [ -f "$ntp_conf" ]; then
log_info "检测到 ntp 配置文件: $ntp_conf"
# NTP 配置通常不需要额外添加 allow all 行,但我们可以检查 restrict 行
log_info "NTP 配置文件位置: $ntp_conf (通常不需要额外配置)"
if confirm_action "是否重启 ntp 服务?"; then
log_info "用户确认重启 ntp 服务"
restart_ntp_service "ntp"
else
log_info "用户取消重启 ntp 服务"
fi
else
log_warn "未找到 NTP 配置文件"
# 尝试安装 NTP 服务
install_and_start_ntp
fi
log_info "修复ntp服务配置完成"
}
# 重启NTP服务的通用函数
restart_ntp_service() {
local service_name=$1
local service_found=false
log_info "尝试重启 $service_name 服务"
# 检查 systemctl 是否可用
if command -v systemctl >/dev/null 2>&1; then
# 检查服务是否存在
if systemctl list-unit-files | grep -q "^${service_name}.service"; then
systemctl restart "$service_name"
if [ $? -eq 0 ]; then
log_info "已成功重启 $service_name 服务"
service_found=true
else
log_error "重启 $service_name 服务失败"
fi
else
log_warn "systemctl 中未找到 $service_name 服务"
fi
fi
# 如果 systemctl 不可用或服务未找到,尝试 service 命令
if [ "$service_found" = false ]; then
if command -v service >/dev/null 2>&1; then
if service "$service_name" status >/dev/null 2>&1; then
service "$service_name" restart
if [ $? -eq 0 ]; then
log_info "已成功重启 $service_name 服务"
service_found=true
else
log_error "使用 service 命令重启 $service_name 服务失败"
fi
else
log_warn "service 命令中未找到 $service_name 服务或服务未安装"
fi
else
log_error "系统中未找到 service 命令"
fi
fi
# 如果仍未找到服务,尝试其他可能的服务名称
if [ "$service_found" = false ]; then
local alternative_services=("ntpd" "chronyd" "ntp" "chrony")
log_info "尝试查找替代的 NTP 服务: ${alternative_services[*]}"
for alt_service in "${alternative_services[@]}"; do
if systemctl list-unit-files | grep -q "^${alt_service}.service"; then
systemctl restart "$alt_service"
if [ $? -eq 0 ]; then
log_info "已成功重启替代服务 $alt_service"
service_found=true
break
else
log_error "重启替代服务 $alt_service 失败"
fi
elif service "$alt_service" status >/dev/null 2>&1; then
service "$alt_service" restart
if [ $? -eq 0 ]; then
log_info "已成功重启替代服务 $alt_service (使用 service 命令)"
service_found=true
break
else
log_error "重启替代服务 $alt_service 失败 (使用 service 命令)"
fi
fi
done
fi
if [ "$service_found" = false ]; then
log_error "未能找到并重启任何 NTP 服务,请手动检查系统中的 NTP 服务配置"
fi
}
# 安装并启动NTP服务
install_and_start_ntp() {
log_info "尝试安装 NTP 服务"
# 检查包管理器
if command -v yum >/dev/null 2>&1; then
log_info "检测到 yum 包管理器"
# CentOS/RHEL 系统
if confirm_action "是否安装 chrony (适用于 CentOS/RHEL)?"; then
yum install -y chrony
if [ $? -eq 0 ]; then
log_info "成功安装 chrony"
systemctl enable chrony
systemctl start chrony
log_info "已启动 chrony 服务"
else
log_error "安装 chrony 失败"
fi
fi
elif command -v apt-get >/dev/null 2>&1; then
log_info "检测到 apt-get 包管理器"
# Debian/Ubuntu 系统
if confirm_action "是否安装 ntp (适用于 Debian/Ubuntu)?"; then
apt-get update
apt-get install -y ntp
if [ $? -eq 0 ]; then
log_info "成功安装 ntp"
systemctl enable ntp
systemctl start ntp
log_info "已启动 ntp 服务"
else
log_error "安装 ntp 失败"
fi
fi
elif command -v dnf >/dev/null 2>&1; then
log_info "检测到 dnf 包管理器"
# Fedora 系统
if confirm_action "是否安装 chrony (适用于 Fedora)?"; then
dnf install -y chrony
if [ $? -eq 0 ]; then
log_info "成功安装 chrony"
systemctl enable chrony
systemctl start chrony
log_info "已启动 chrony 服务"
else
log_error "安装 chrony 失败"
fi
fi
else
log_warn "未检测到支持的包管理器,无法自动安装 NTP 服务"
fi
}
# 修复端口开放问题
fix_port_access() {
local platform=$1
log_info "开始检查端口开放情况,平台类型: $platform"
if [ "$platform" = "new" ]; then
local required_ports=("443" "1883" "8883" "123/udp")
else
local required_ports=("443" "1883" "8443" "8081" "123/udp" "8883" "4443")
fi
log_info "平台 $platform 需要开放的端口: ${required_ports[*]}"
# 检查系统使用的防火墙类型
local firewall_type=""
local firewall_service=""
if command -v firewall-cmd >/dev/null 2>&1; then
firewall_type="firewalld"
firewall_service="firewalld"
log_info "检测到 firewalld 防火墙"
elif command -v iptables >/dev/null 2>&1; then
firewall_type="iptables"
# 检查使用的是哪个iptables服务
if systemctl list-unit-files | grep -q '^iptables.service'; then
firewall_service="iptables"
elif systemctl list-unit-files | grep -q '^netfilter-persistent.service'; then
firewall_service="netfilter-persistent"
else
firewall_service="iptables"
fi
log_info "检测到 iptables 防火墙,服务类型: $firewall_service"
else
log_warn "未检测到支持的防火墙系统"
log_info "请手动检查并确保以上端口已正确开放"
return 1
fi
# 检查防火墙服务是否正在运行
local firewall_running=false
if systemctl is-active --quiet "$firewall_service"; then
firewall_running=true
log_info "防火墙服务 $firewall_service 正在运行"
else
log_warn "防火墙服务 $firewall_service 未运行"
if confirm_action "是否启动防火墙服务 $firewall_service?"; then
if systemctl start "$firewall_service" 2>/dev/null; then
log_info "已启动防火墙服务 $firewall_service"
firewall_running=true
else
log_error "启动防火墙服务 $firewall_service 失败"
log_info "将继续尝试添加端口规则,但可能不会生效直到防火墙服务启动"
fi
else
log_info "用户选择不启动防火墙服务"
log_info "将继续尝试添加端口规则,但可能不会生效直到防火墙服务启动"
fi
fi
# 检查端口状态并询问是否需要开放
local ports_to_open=()
for port_proto in "${required_ports[@]}"; do
local port=${port_proto%/*}
local proto=${port_proto#*/}
if [ "$proto" = "$port" ]; then
proto="tcp"
fi
local is_open=false
case $firewall_type in
"firewalld")
if firewall-cmd --list-ports | grep -q "${port}/${proto}"; then
is_open=true
fi
;;
"iptables")
if iptables -L INPUT -nv | grep -q "dpt:${port}\|${port}/${proto}"; then
is_open=true
fi
;;
esac
if [ "$is_open" = true ]; then
log_info "端口 $port/$proto 已开放"
else
log_warn "端口 $port/$proto 未开放"
ports_to_open+=("$port/$proto")
fi
done
# 如果有需要开放的端口,询问用户是否开放
if [ ${#ports_to_open[@]} -gt 0 ]; then
log_info "需要开放的端口: ${ports_to_open[*]}"
if confirm_action "是否开放以上端口?"; then
log_info "用户确认开放端口"
local failed_operations=0
for port_proto in "${ports_to_open[@]}"; do
local port=${port_proto%/*}
local proto=${port_proto#*/}
case $firewall_type in
"firewalld")
if firewall-cmd --permanent --add-port="$port_proto" 2>/dev/null; then
log_info "已添加端口 $port/$proto 到 firewalld 规则"
else
log_error "添加端口 $port/$proto 到 firewalld 规则失败"
((failed_operations++))
fi
;;
"iptables")
if iptables -A INPUT -p "$proto" --dport "$port" -j ACCEPT 2>/dev/null; then
log_info "已添加端口 $port/$proto 到 iptables 规则"
else
log_error "添加端口 $port/$proto 到 iptables 规则失败"
((failed_operations++))
fi
;;
esac
done
# 如果有失败的操作,提前返回
if [ $failed_operations -gt 0 ]; then
log_error "部分端口添加失败,请检查防火墙配置"
return 1
fi
# 保存并重启防火墙规则
case $firewall_type in
"firewalld")
if firewall-cmd --reload 2>/dev/null; then
log_info "firewalld 规则已重载"
else
log_error "firewalld 规则重载失败"
fi
;;
"iptables")
# 更全面的iptables规则保存策略
local save_success=false
# 方法1: 使用service命令保存
if ! $save_success && command -v service >/dev/null 2>&1; then
if service iptables save 2>/dev/null; then
log_info "iptables 规则已通过 service iptables save 保存"
save_success=true
fi
fi
# 方法2: 直接调用iptables-save
if ! $save_success; then
if command -v iptables-save >/dev/null 2>&1; then
# 检查常见的iptables规则保存位置
local iptables_save_paths=(
"/etc/sysconfig/iptables"
"/etc/iptables/rules.v4"
"/etc/iptables.conf"
)
for save_path in "${iptables_save_paths[@]}"; do
if [ -w "$(dirname "$save_path")" ] || [ -w "$save_path" ]; then
if iptables-save > "$save_path" 2>/dev/null; then
log_info "iptables 规则已保存到 $save_path"
save_success=true
break
fi
fi
done
fi
fi
# 方法3: 使用发行版特定的命令
if ! $save_success; then
# Ubuntu/Debian系统
if command -v iptables-persistent >/dev/null 2>&1; then
if iptables-persistent save 2>/dev/null; then
log_info "iptables 规则已通过 iptables-persistent 保存"
save_success=true
fi
fi
# 使用netfilter-persistent
if ! $save_success && command -v netfilter-persistent >/dev/null 2>&1; then
if netfilter-persistent save 2>/dev/null; then
log_info "iptables 规则已通过 netfilter-persistent 保存"
save_success=true
fi
fi
fi
# 方法4: 启用iptables服务以实现开机自启和规则加载
if ! $save_success; then
if systemctl list-unit-files | grep -q '^iptables.service'; then
if systemctl enable iptables 2>/dev/null; then
log_info "已启用 iptables 服务开机自启"
fi
fi
fi
if ! $save_success; then
log_warn "iptables 规则保存失败,重启系统后规则可能会丢失"
log_info "建议手动执行以下命令之一保存规则:"
log_info " sudo iptables-save > /etc/sysconfig/iptables"
log_info " sudo service iptables save"
log_info " sudo netfilter-persistent save"
else
log_info "iptables 规则保存成功"
fi
;;
esac
log_info "端口开放完成"
else
log_info "用户取消开放端口"
fi
else
log_info "所有必需端口均已开放"
fi
log_info "端口开放检查完成"
}
# 修复服务器DNS异常问题
fix_dns_config() {
log_info "开始检查DNS配置"
local dns_servers=("114.114.114.114" "8.8.8.8")
local network_config_files=("/etc/resolv.conf")
# 检查网络管理器类型
local network_manager=""
if command -v nmcli >/dev/null 2>&1; then
network_manager="NetworkManager"
log_info "检测到 NetworkManager 网络管理器"
elif command -v systemctl >/dev/null 2>&1 && systemctl list-unit-files | grep -q '^network.service'; then
network_manager="network"
log_info "检测到 network 服务"
elif command -v systemctl >/dev/null 2>&1 && systemctl list-unit-files | grep -q '^systemd-networkd.service'; then
network_manager="systemd-networkd"
log_info "检测到 systemd-networkd 网络服务"
else
network_manager="none"
log_info "未检测到标准网络管理器"
fi
for config_file in "${network_config_files[@]}"; do
if [ -f "$config_file" ]; then
local dns_configured=false
for dns in "${dns_servers[@]}"; do
if grep -q "nameserver $dns" "$config_file"; then
dns_configured=true
log_info "DNS服务器 $dns 已配置在 $config_file"
fi
done
if [ "$dns_configured" = false ]; then
log_warn "在 $config_file 中未找到推荐的DNS服务器配置"
log_info "建议添加以下DNS服务器之一:"
for dns in "${dns_servers[@]}"; do
log_info " nameserver $dns"
done
# === 调整点 1:非交互模式自动同意 ===
local do_add_dns=false
if [ "$NON_INTERACTIVE" = "1" ] && [ "$ASSUME_YES" = "1" ]; then
log_info "非交互模式,自动添加推荐的DNS服务器配置"
do_add_dns=true
else
if confirm_action "是否添加推荐的DNS服务器配置?"; then
log_info "用户确认添加DNS服务器配置"
do_add_dns=true
else
log_info "用户取消添加DNS服务器配置"
fi
fi
if [ "$do_add_dns" = true ]; then
local added_servers=0
# 备份原配置文件
local timestamp=$(date +%Y%m%d_%H%M%S)
local backup_file="${config_file}.backup_${timestamp}"
cp "$config_file" "$backup_file"
log_info "已备份原配置文件到: $backup_file"
# 添加所有推荐的DNS服务器
for dns in "${dns_servers[@]}"; do
# 检查是否已经存在该DNS服务器(防止重复添加)
if ! grep -q "nameserver $dns" "$config_file"; then
echo "nameserver $dns" >> "$config_file"
log_info "已添加DNS服务器: $dns"
((added_servers++))
else
log_info "DNS服务器 $dns 已存在,跳过添加"
fi
done
if [ $added_servers -gt 0 ]; then
log_info "成功添加 $added_servers 个DNS服务器"
# 检查配置是否成功添加
local verify_success=true
for dns in "${dns_servers[@]}"; do
if ! grep -q "nameserver $dns" "$config_file"; then
log_error "验证失败:DNS服务器 $dns 未成功添加到配置文件"
verify_success=false
fi
done
if [ "$verify_success" = true ]; then
log_info "DNS服务器配置已成功写入 $config_file"
# === 调整点 2:重启网络服务时也支持非交互 ===
local restart_network=false
if [ "$network_manager" != "none" ]; then
if [ "$NON_INTERACTIVE" = "1" ] && [ "$ASSUME_YES" = "1" ]; then
log_info "非交互模式,自动重启网络服务以应用DNS配置"
restart_network=true
else
if confirm_action "是否重启网络服务以应用DNS配置?"; then
restart_network=true
else
log_info "用户选择不重启网络服务,DNS配置将在下次网络服务重启时生效"
fi
fi
else
log_info "未检测到标准网络管理器,无法自动重启网络服务"
fi
if [ "$restart_network" = true ]; then
case $network_manager in
"NetworkManager")
if systemctl restart NetworkManager 2>/dev/null; then
log_info "已成功重启 NetworkManager 服务"
else
log_error "重启 NetworkManager 服务失败"
log_info "请手动执行: systemctl restart NetworkManager"
fi
;;
"network")
if systemctl restart network 2>/dev/null; then
log_info "已成功重启 network 服务"
else
log_error "重启 network 服务失败"
log_info "请手动执行: systemctl restart network"
fi
;;
"systemd-networkd")
if systemctl restart systemd-networkd 2>/dev/null; then
log_info "已成功重启 systemd-networkd 服务"
else
log_error "重启 systemd-networkd 服务失败"
log_info "请手动执行: systemctl restart systemd-networkd"
fi
;;
esac
# 验证网络连接
log_info "正在验证网络连接..."
if ping -c 3 114.114.114.114 >/dev/null 2>&1; then
log_info "网络连接测试成功"
else
log_warn "网络连接测试失败,请检查网络配置"
fi
fi
else
log_error "DNS服务器配置验证失败"
fi
else
log_info "没有添加新的DNS服务器"
fi
fi
else
log_info "DNS配置已符合要求"
fi
else
log_warn "DNS配置文件不存在: $config_file"
log_info "请手动创建配置文件或将DNS配置添加到适当的网络配置文件中"
fi
done
log_info "DNS配置检查完成"
}
# 打包备份现场环境数据
backup_environment_data() {
local platform=$1
log_info "开始打包备份现场环境数据,平台类型: $platform"
# 创建备份目录
local timestamp=$(date +"%Y%m%d_%H%M%S")
local backup_base_dir="$(dirname "$0")/env_backup_${timestamp}"
mkdir -p "$backup_base_dir"
log_info "创建备份基础目录: $backup_base_dir"
# 定义需要备份的通用文件和目录
local common_backup_items=(
"/etc/hosts"
"/etc/resolv.conf"
"/etc/fstab"
"/etc/passwd"
"/etc/group"
"/etc/ssh/sshd_config"
"/etc/sysctl.conf"
"/etc/security/limits.conf"
"/var/log"
"/proc/cpuinfo"
"/proc/meminfo"
"/proc/version"
)
# 根据平台类型定义特定的备份项
if [ "$platform" = "new" ]; then
log_info "备份新统一平台环境数据"
local platform_backup_items=(
"/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/config"
"/data/services/api/python-cmdb/log"
"/data/middleware/redis/config"
"/usr/local/nginx/conf"
)
else
log_info "备份标准版平台环境数据"
local platform_backup_items=(
"/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"
"/usr/local/nginx/conf"
)
fi
# 备份通用项
log_info "开始备份通用环境数据"
for item in "${common_backup_items[@]}"; do
if [ -e "$item" ]; then
local dest_path="$backup_base_dir$item"
mkdir -p "$(dirname "$dest_path")"
if [ -d "$item" ]; then
# 如果是目录,复制整个目录
cp -r "$item" "$(dirname "$dest_path")/" 2>/dev/null
log_info "已备份目录: $item"
elif [ -f "$item" ]; then
# 如果是文件,直接复制文件
cp "$item" "$dest_path" 2>/dev/null
log_info "已备份文件: $item"
fi
else
log_warn "通用备份项不存在: $item"
fi
done
# 备份平台特定项
log_info "开始备份平台特定环境数据"
for item in "${platform_backup_items[@]}"; do
if [ -e "$item" ]; then
local dest_path="$backup_base_dir$item"
mkdir -p "$(dirname "$dest_path")"
if [ -d "$item" ]; then
# 如果是目录,复制整个目录
cp -r "$item" "$(dirname "$dest_path")/" 2>/dev/null
log_info "已备份目录: $item"
elif [ -f "$item" ]; then
# 如果是文件,直接复制文件
cp "$item" "$dest_path" 2>/dev/null
log_info "已备份文件: $item"
fi
else
log_warn "平台特定备份项不存在: $item"
fi
done
# 创建系统信息文件
log_info "收集系统信息"
local sysinfo_file="$backup_base_dir/system_info.txt"
{
echo "=== 系统信息收集报告 ==="
echo "收集时间: $(date)"
echo "平台类型: $platform"
echo ""
echo "=== 主机信息 ==="
hostnamectl 2>/dev/null || echo "无法获取hostnamectl信息"
echo ""
echo "=== 网络配置 ==="
ip addr show 2>/dev/null || echo "无法获取IP地址信息"
echo ""
echo "=== 磁盘使用情况 ==="
df -h 2>/dev/null || echo "无法获取磁盘使用情况"
echo ""
echo "=== 内存使用情况 ==="
free -h 2>/dev/null || echo "无法获取内存使用情况"
echo ""
echo "=== 进程信息 ==="
ps aux 2>/dev/null | head -50 || echo "无法获取进程信息"
echo ""
echo "=== Docker容器信息 ==="
docker ps -a 2>/dev/null || echo "无法获取Docker容器信息"
echo ""
echo "=== 服务状态 ==="
systemctl list-units --type=service --state=running 2>/dev/null || echo "无法获取服务状态"
} > "$sysinfo_file"
log_info "系统信息已保存到: $sysinfo_file"
# 打包备份目录
log_info "开始打包备份数据"
local backup_archive="$(dirname "$0")/env_backup_${timestamp}.tar.gz"
tar -czf "$backup_archive" -C "$(dirname "$backup_base_dir")" "$(basename "$backup_base_dir")" 2>/dev/null
if [ $? -eq 0 ]; then
log_info "环境数据备份完成,备份文件: $backup_archive"
# 清理临时目录
rm -rf "$backup_base_dir"
log_info "已清理临时目录: $backup_base_dir"
else
log_error "环境数据打包失败"
fi
log_info "打包备份现场环境数据完成"
}
# 显示磁盘分区调整参考信息
show_disk_partition_info() {
log_info "显示磁盘分区调整参考信息"
echo
echo "==================== 磁盘分区调整参考 ===================="
echo
echo "注意:磁盘分区调整属于高风险操作,本工具不提供自动处理功能。"
echo "请严格按照以下步骤手动操作,并确保在操作前做好数据备份。"
echo
echo "当安装完 Linux 操作系统,发现磁盘分区大小错误,或者后期使用过程"
echo "发现 /home 还剩余很多空间,/ 下空间不足,需要将 /home 下空间重新"
echo "分配给 / 目录下,方法如下:"
echo
echo "1、查看分区空间和格式"
echo " df -hT"
echo " cat /etc/os-release"
echo
echo "2、备份数据"
echo " cp -rp /home /run/"
echo " # 或者备份到其他安全位置"
echo
echo "3、卸载 /home 分区"
echo " # 卸载分区时,务必将该分区内容备份到其他分区,否则会丢失该分区的内容!!"
echo " umount /home"
echo
echo "4、减小/home分区大小(以减少1.6T为例)"
echo " lvreduce -L -1.6T /dev/mapper/uos-home"
echo " mkfs.xfs /dev/mapper/uos-home -f"
echo
echo "5、重新挂载 /home 分区:"
echo " mount /dev/mapper/uos-home /home/"
echo
echo "6、查看剩余空间:"
echo " vgdisplay"
echo
echo "7、给/root目录扩容(以增加40G为例)"
echo " lvextend -L +40G /dev/mapper/uos-root"
echo " xfs_growfs /dev/mapper/uos-root"
echo
echo "8、验证是否成功"
echo " df -h"
echo
echo "==================== 常见问题及解决方案 ===================="
echo
echo "(一)卸载 /home 分区提示:"
echo "umount: /home: device is busy."
echo "(In some cases useful info about processes that use"
echo "the device is found by lsof(8) or fuser(1))"
echo
echo "提示无法卸载,则是有进程占用 /home,解决:"
echo "1、有可能你在 /home 目录下,跳出 /home 再执行卸载。"
echo "2、有进程使用 /home ,使用如下命令终止进程,再卸载。"
echo " fuser -m -k /home"
echo
echo "(二)挂载 /home 分区提示:"
echo "[root@rbtnode1 ~]# xfs_growfs /dev/mapper/centos-home"
echo "xfs_growfs: /dev/mapper/centos-home is not a mounted XFS filesystem"
echo
echo "解决:需要格式化 /home ,再挂载。"
echo
echo "(三)执行resize2fs报错"
echo "resize2fs: Bad magic number in super-block 当尝试打开 /dev/mapper/centos-root 时"
echo "找不到有效的文件系统超级块."
echo
echo "解决:查看类型,xfs 系统需要 xfs_growfs 执行。"
echo
echo "(四)不同文件系统的指令"
echo "(1)ext2/ext3/ext4文件系统,调整命令是resize2fs(增大和减小都支持)"
echo " lvextend -L 120G /dev/mapper/centos-home //增大至120G"
echo " lvextend -L +20G /dev/mapper/centos-home //增加20G"
echo " lvreduce -L 50G /dev/mapper/centos-home //减小至50G"
echo " lvreduce -L -8G /dev/mapper/centos-home //减小8G"
echo " resize2fs /dev/mapper/centos-home //执行调整"
echo
echo "(2)xfs文件系统的调整命令是xfs_growfs(只支持增大)"
echo " lvextend -L 120G /dev/mapper/centos-home //增大至120G"
echo " lvextend -L +20G /dev/mapper/centos-home //增加20G"
echo " xfs_growfs /dev/mapper/centos-home //执行调整"
echo
echo "========================================================"
echo
log_info "磁盘分区调整参考信息显示完成"
}
# 补充redis服务异常时修复操作
redis_container_exception() {
local non_interactive=0
local auto_yes=0
# 解析参数
while [[ $# -gt 0 ]]; do
case "$1" in
--non-interactive)
non_interactive=1
shift
;;
--yes|-y)
auto_yes=1
shift
;;
*)
break
;;
esac
done
echo "=== 修复 Redis 容器文件损坏问题 (redis_container_exception) ==="
# 1. 检测新旧平台
local redis_data_path="/var/www/redis/data"
if [[ -d "/data/middleware/redis/data" ]]; then
redis_data_path="/data/middleware/redis/data"
echo "检测到新统一平台,Redis 数据目录:$redis_data_path"
elif [[ -d "/var/www/redis/data" ]]; then
echo "检测到标准版平台,Redis 数据目录:$redis_data_path"
else
echo "未找到 Redis 数据目录,请手动确认 /var/www/redis 或 /data/middleware/redis 是否存在"
return 1
fi
# 2. 先尝试简单重启 uredis 容器
echo "尝试重启 uredis 容器..."
if docker ps -a --format '{{.Names}}' | grep -qw uredis; then
docker restart uredis
sleep 3
if docker ps --format '{{.Names}}' | grep -qw uredis; then
echo "uredis 容器重启成功,无需清空数据目录。"
return 0
else
echo "uredis 容器重启失败,将按步骤 3 清空数据目录后再尝试重启。"
fi
else
echo "未找到 uredis 容器,后续清数据并尝试重新创建/启动时可能失败,请手动排查。"
fi
# 3. 删除 redis data 下所有数据(高风险操作,需要确认)
if [[ $non_interactive -eq 0 ]]; then
# 交互模式:要求用户手动确认
echo "警告:将要删除 Redis 数据目录下的所有数据:$redis_data_path"
read -r -p "确认执行此操作吗?(yes/NO): " answer
if [[ "$answer" != "yes" ]]; then
echo "用户取消操作,不执行数据删除。"
return 1
fi
else
# 非交互模式:必须带 --yes 才继续
if [[ $auto_yes -ne 1 ]]; then
echo "非交互模式下未提供 --yes,出于安全考虑,取消操作。"
return 1
fi
echo "非交互模式 + --yes:将删除 Redis 数据目录:$redis_data_path"
fi
echo "清空 Redis 数据目录:$redis_data_path"
rm -rf "${redis_data_path:?}/"* || {
echo "删除 $redis_data_path 下数据失败,请检查权限。"
return 1
}
# 再次尝试启动/重启 uredis 容器
if docker ps -a --format '{{.Names}}' | grep -qw uredis; then
echo "尝试重启 uredis 容器..."
docker restart uredis
else
echo "未找到 uredis 容器,尝试 docker start uredis..."
docker start uredis || true
fi
sleep 3
if docker ps --format '{{.Names}}' | grep -qw uredis; then
echo "uredis 容器启动成功,Redis 文件损坏修复完成。"
return 0
else
echo "uredis 容器仍未启动成功,请人工进一步排查。"
return 1
fi
}
# 动作分发:根据动作名调用对应函数(必要时传入平台)
run_action_by_name() {
local action="$1"
local platform="$2"
case "$action" in
# 日志相关
export_and_compress_logs|export_logs)
export_and_compress_logs "$platform"
;;
rotate_logs)
rotate_logs "$platform"
;;
clean_deleted_files)
clean_deleted_files
;;
cleanup_system_log_table)
cleanup_system_log_table
;;
show_disk_partition_info)
show_disk_partition_info
;;
# 配置修复
fix_ip_configurations)
fix_ip_configurations "$platform"
;;
fix_permissions)
fix_permissions "$platform"
;;
fix_db_permissions)
fix_db_permissions
;;
fix_nginx_user)
fix_nginx_user
;;
fix_external_service_disconnect)
fix_external_service_disconnect "$platform"
;;
fix_ntp_config)
fix_ntp_config "$platform"
;;
fix_port_access)
fix_port_access "$platform"
;;
fix_dns_config)
fix_dns_config "$platform"
;;
redis_container_exception)
redis_container_exception
;;
# 备份
backup_environment_data|backup_env)
backup_environment_data "$platform"
;;
# 版本更新
update_inner_backend_jar)
update_inner_backend_jar "$platform"
;;
update_external_backend_jar)
update_external_backend_jar "$platform"
;;
update_frontend)
update_frontend "$platform"
;;
update_backend)
update_backend "$platform"
;;
update_ops_backend)
update_ops_backend "$platform"
;;
update_ops_frontend)
update_ops_frontend "$platform"
;;
*)
log_error "未知动作: $action"
echo "可用动作: export_and_compress_logs, rotate_logs, clean_deleted_files, cleanup_system_log_table, show_disk_partition_info, fix_ip_configurations, fix_permissions, fix_db_permissions, fix_nginx_user, fix_external_service_disconnect, fix_ntp_config, fix_port_access, fix_dns_config, backup_environment_data, update_inner_backend_jar, update_external_backend_jar, update_frontend, update_backend, update_ops_backend, update_ops_frontend" >&2
return 1
;;
esac
}
# 交互式模式(本地运行时的菜单)
interactive_mode() {
local action=""
local platform="auto"
echo "请选择平台 [auto/new/standard] (默认: auto):"
read -r input; [ -n "$input" ] && platform="$input"
echo "请选择要执行的动作:"
cat <<'MENU'
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) 退出
MENU
read -r sel
case "$sel" in
1) action="fix_ntp_config" ;;
2) action="fix_port_access" ;;
3) action="fix_ip_configurations" ;;
4) action="fix_permissions" ;;
5) action="fix_db_permissions" ;;
6) action="fix_dns_config" ;;
7) action="export_and_compress_logs" ;;
8) action="rotate_logs" ;;
9) action="backup_environment_data" ;;
c|C) action="clean_deleted_files" ;;
q|Q) log_info "已退出"; return 0 ;;
*) log_error "无效选择"; return 1 ;;
esac
echo -n "是否启用非交互模式并默认同意操作? [y/N]: "
read -r yn
if [[ "$yn" =~ ^[yY]$ ]]; then
NON_INTERACTIVE=1
ASSUME_YES=1
fi
# 为需要额外参数的动作采集输入
if [ "$action" = "fix_ip_configurations" ]; then
if [ -z "$ARG_OLD_IP" ]; then
echo -n "请输入需要替换的旧IP: "
read -r ARG_OLD_IP
fi
if [ -z "$ARG_NEW_IP" ]; then
echo -n "请输入新的IP: "
read -r ARG_NEW_IP
fi
elif [ "$action" = "fix_db_permissions" ]; then
if [ -z "$ARG_DB_ROOT_PASS" ]; then
echo -n "请输入数据库root密码(回显隐藏): "
stty -echo; read -r ARG_DB_ROOT_PASS; stty echo; echo
fi
fi
log_info "即将执行: $action (平台: $platform)"
run_action_by_name "$action" "$platform"
}
# ...existing code...
main() {
local ACTION=""
local PLATFORM_ARG=""
# 启动即记录版本与调用参数(便于回溯)
log_info "issue_handler.sh version=${SCRIPT_VERSION}"
log_info "Args: $*"
if [ $# -gt 0 ]; then
# 解析命令行参数
while [ $# -gt 0 ]; do
case "$1" in
-h|--help)
show_help
return 0
;;
-v|--version)
show_version
return 0
;;
--action|-a)
if [ -n "${2:-}" ]; then
ACTION="$2"; shift 2; continue
else
log_error "--action 需要一个参数"
return 1
fi
;;
# 全局无交互与兼容NTP开关
--ntp-auto|--ntp-non-interactive)
# 兼容旧参数,同时启用全局非交互+默认yes
NTP_AUTO_YES=1
NON_INTERACTIVE=1
ASSUME_YES=1
shift
continue
;;
--non-interactive|--yes|-y)
NON_INTERACTIVE=1
ASSUME_YES=1
shift
continue
;;
--assume-no|--no)
NON_INTERACTIVE=1
ASSUME_YES=0
shift
continue
;;
# 参数化输入
--db-root-pass)
if [ -n "${2:-}" ]; then
ARG_DB_ROOT_PASS="$2"; shift 2; continue
else
log_error "--db-root-pass 需要一个参数"
return 1
fi
;;
--old-ip)
if [ -n "${2:-}" ]; then
ARG_OLD_IP="$2"; shift 2; continue
else
log_error "--old-ip 需要一个参数"
return 1
fi
;;
--new-ip)
if [ -n "${2:-}" ]; then
ARG_NEW_IP="$2"; shift 2; continue
else
log_error "--new-ip 需要一个参数"
return 1
fi
;;
--platform|-p)
if [ -n "${2:-}" ]; then
PLATFORM_ARG="$2"; shift 2; continue
else
log_error "--platform 需要一个参数(new|standard|auto)"
return 1
fi
;;
repair|check|clean)
log_info "执行命令: $1"
case "$1" in
repair) perform_repair ;;
check) perform_check ;;
clean) perform_clean ;;
esac
return $?
;;
*)
# 未知参数,如果未指定 --action 则报错;若之后会走到 ACTION 分支则忽略
if [ -z "$ACTION" ]; then
log_error "未知参数: $1"
return 1
else
# 忽略额外参数
shift
continue
fi
;;
esac
done
fi
# 有 --action 时,按动作直达
if [ -n "$ACTION" ]; then
local platform=""
if [ -n "$PLATFORM_ARG" ]; then
if [ "$PLATFORM_ARG" = "auto" ]; then
platform=$(detect_platform)
else
platform="$PLATFORM_ARG"
fi
else
platform=$(detect_platform)
fi
log_info "执行动作: $ACTION (平台: $platform)"
run_action_by_name "$ACTION" "$platform"
log_info "脚本执行完毕"
return $?
fi
# 无参数与动作时进入交互式模式
log_info "进入交互式模式"
interactive_mode
log_info "脚本执行完毕"
}
main "$@"
\ No newline at end of file
......@@ -96,11 +96,9 @@ check_java_service() {
# 容器内检测
location="容器内"
# meeting2.0和meeting3.0需要额外路径过滤
# meeting2.0需要额外路径过滤
if [ "$service_name" = "meeting2.0" ]; then
count=$(docker_exec "$CONTAINER" "ps aux | grep -v grep | grep '$jar_file' | grep 'java-meeting2.0' | wc -l")
elif [ "$service_name" = "meeting3.0" ]; then
count=$(docker_exec "$CONTAINER" "ps aux | grep -v grep | grep '$jar_file' | grep 'java-meeting3.0' | wc -l")
else
count=$(docker_exec "$CONTAINER" "ps aux | grep -v grep | grep '$jar_file' | wc -l")
fi
......@@ -108,8 +106,6 @@ check_java_service() {
# 宿主机检测
if [ "$service_name" = "meeting2.0" ]; then
count=$(check_process_count "$jar_file" "java-meeting2.0")
elif [ "$service_name" = "meeting3.0" ]; then
count=$(check_process_count "$jar_file" "java-meeting3.0")
else
count=$(check_process_count "$jar_file")
fi
......@@ -138,7 +134,7 @@ output_json() {
json_array_start "services"
local first=true
local service_names=("auth" "gateway" "system" "meeting2.0" "meeting3.0" "mqtt" "quartz" "message")
local service_names=("auth" "gateway" "system" "meeting2.0" "mqtt" "quartz" "message")
for svc in "${service_names[@]}"; do
local jar_file="${JAVA_SERVICES[$svc]}"
......@@ -176,7 +172,7 @@ output_text() {
echo "平台类型: $PLATFORM"
echo ""
local service_names=("auth" "gateway" "system" "meeting2.0" "meeting3.0" "mqtt" "quartz" "message")
local service_names=("auth" "gateway" "system" "meeting2.0" "mqtt" "quartz" "message")
for svc in "${service_names[@]}"; do
local jar_file="${JAVA_SERVICES[$svc]}"
......
......@@ -106,7 +106,6 @@ function Test-NewPlatformIPs {
"/data/services/api/auth/auth-sso-gatway/config",
"/data/services/api/auth/auth-sso-system/config",
"/data/services/api/java-meeting/java-meeting2.0/config",
"/data/services/api/java-meeting/java-meeting3.0/config",
"/data/services/api/java-meeting/java-meeting-extapi/config",
"/data/services/api/java-meeting/java-message-scheduling/config",
"/data/services/api/java-meeting/java-mqtt/config",
......@@ -326,7 +325,6 @@ function Test-NewPlatformConsole {
"/data/services/api/auth/auth-sso-gatway/config",
"/data/services/api/auth/auth-sso-system/config",
"/data/services/api/java-meeting/java-meeting2.0/config",
"/data/services/api/java-meeting/java-meeting3.0/config",
"/data/services/api/java-meeting/java-meeting-extapi/config",
"/data/services/api/java-meeting/java-message-scheduling/config",
"/data/services/api/java-meeting/java-mqtt/config",
......
......@@ -64,7 +64,6 @@ $script:NewPlatformLogs = @(
@{ Name = "gatway_log.out"; RemotePath = "/data/services/api/auth/auth-sso-gatway/log.out" },
@{ Name = "system_log.out"; RemotePath = "/data/services/api/auth/auth-sso-system/log.out" },
@{ Name = "对内2.0_ubains-INFO-AND-ERROR.log"; RemotePath = "/data/services/api/java-meeting/java-meeting2.0/logs/ubains-INFO-AND-ERROR.log" },
@{ Name = "对内3.0_ubains-INFO-AND-ERROR.log"; RemotePath = "/data/services/api/java-meeting/java-meeting3.0/logs/ubains-INFO-AND-ERROR.log" },
@{ Name = "对外服务_ubains-INFO-AND-ERROR.log"; RemotePath = "/data/services/api/java-meeting/java-meeting-extapi/logs/ubains-INFO-AND-ERROR.log" },
@{ Name = "信息调度_ubains-INFO-AND-ERROR.log"; RemotePath = "/data/services/api/java-meeting/java-message-scheduling/logs/ubains-INFO-AND-ERROR.log" },
@{ Name = "MQTT_ubains-INFO-AND-ERROR.log"; RemotePath = "/data/services/api/java-meeting/java-mqtt/logs/ubains-INFO-AND-ERROR.log" },
......
......@@ -82,7 +82,7 @@ function Test-UjavaServices {
.DESCRIPTION
检测 ujava 容器中是否存在特定进程。支持在容器内或宿主机上检测。
通过精确匹配方式检测,支持 meeting2.0 和 meeting3.0 两种版本。
通过精确匹配方式检测,支持 meeting2.0 版本。
.PARAMETER Server
服务器信息哈希表,包含 IP、User、Pass、Port 等连接信息
......@@ -154,10 +154,6 @@ function Test-UjavaServices {
# meeting2.0 需要匹配路径中包含 java-meeting2.0
$checkCmd = "docker exec $ContainerName ps aux 2>/dev/null | grep -v grep | grep '$jarFileName' | grep 'java-meeting2.0' | wc -l"
}
elseif ($serviceName -eq "meeting3.0") {
# meeting3.0 需要匹配路径中包含 java-meeting3.0
$checkCmd = "docker exec $ContainerName ps aux 2>/dev/null | grep -v grep | grep '$jarFileName' | grep 'java-meeting3.0' | wc -l"
}
else {
# 其他服务直接匹配jar文件名
$checkCmd = "docker exec $ContainerName ps aux 2>/dev/null | grep -v grep | grep '$jarFileName' | wc -l"
......@@ -168,9 +164,6 @@ function Test-UjavaServices {
if ($serviceName -eq "meeting2.0") {
$checkCmd = "ps aux 2>/dev/null | grep -v grep | grep '$jarFileName' | grep 'java-meeting2.0' | wc -l"
}
elseif ($serviceName -eq "meeting3.0") {
$checkCmd = "ps aux 2>/dev/null | grep -v grep | grep '$jarFileName' | grep 'java-meeting3.0' | wc -l"
}
else {
$checkCmd = "ps aux 2>/dev/null | grep -v grep | grep '$jarFileName' | wc -l"
}
......@@ -195,9 +188,6 @@ function Test-UjavaServices {
if ($serviceName -eq "meeting2.0") {
$checkCmd = "ps aux 2>/dev/null | grep -v grep | grep '$jarFileName' | grep 'java-meeting2.0' | wc -l"
}
elseif ($serviceName -eq "meeting3.0") {
$checkCmd = "ps aux 2>/dev/null | grep -v grep | grep '$jarFileName' | grep 'java-meeting3.0' | wc -l"
}
else {
$checkCmd = "ps aux 2>/dev/null | grep -v grep | grep '$jarFileName' | wc -l"
}
......
......@@ -24,7 +24,6 @@
```
- 添加后的效果:
- ```ignorelang
location ~* ^/exapi/(swagger-ui|doc\.html|swagger-resources|v2/api-docs|v3/api-docs|webjars) {
return 403;
}
......
# 远程自动化部署_需求文档
## 相关资料要求
### 部署包路径
- ARM架构:\\192.168.9.9\发布版本\03服务器部署\临时使用-新统一平台\ARM部署包-请勿使用
- X86架构:\\192.168.9.9\发布版本\03服务器部署\临时使用-新统一平台\X86部署包\全量版
### 目标服务器
- X86架构服务器:192.168.5.52 root Ubains@123
- ARM架构服务器:192.168.9.76 root Ubains@123
### 部署文档
- X86部署文档路径:"\\192.168.9.9\发布版本\03服务器部署\临时使用-新统一平台\X86部署包\新统一平台自动化部署操作指导.docx"
- ARM部署文档路径:"\\192.168.9.9\发布版本\03服务器部署\临时使用-新统一平台\ARM部署包-请勿使用\新统一平台自动化部署操作指导.docx"
## 授权文件
- X86-5.52授权文件路径:"\\192.168.9.9\发布版本\03服务器部署\临时使用-新统一平台\测试授权文件-请勿使用\5.52授权文件\license.zip"
- ARM-9.76授权文件路径:"\\192.168.9.9\发布版本\03服务器部署\临时使用-新统一平台\测试授权文件-请勿使用\9.76授权文件\license.zip"
### 相关服务路径
- 预定对外服务宿主机日志路径:/data/services/api/java-meeting/java-meeting-extapi/logs/ubains-INFO-AND-ERROR.log
- 预定对内服务宿主机日志路径:/data/services/api/java-meeting/java-meeting2.0/logs/ubains-INFO-AND-ERROR.log
- 运维服务宿主机日志路径:/data/services/api/python-cmdb/log/uinfo.log
- 讯飞服务宿主机日志路径:/data/services/api/python-voice/log/uinfo.log
### 相关系统网址
- 新统一平台前台地址:https://服务器IP/
- 新统一平台维护地址:https://服务器IP/#/LoginConfig
- 新统一平台后台地址;https://服务器IP/#/LoginAdmin
### 接口调用要求
- 重试机制:
- 重试周期:
- 重试次数:5次
- 重试条件:
- 当调用失败或成功时都等待30秒,再执行下一次测试。
- 记录测试结果。
- 当测试结果为成功时,标识为服务启动正常,结束测试。
### 部署时间要求
- 自动化部署脚本执行时间:40分钟
- 根据文档授权执行时间:10分钟
- 根据文档创建用户使用:10分钟
## 执行要求
1. 部署包上传
- 根据架构选择部署包路径,根据部署文档上传对应的部署包文件到指定目录。
2. 目标服务器登录
- 登录目标服务器,并切换到root用户。
3. 部署执行
- 根据部署文档执行部署操作
- 如遇选择部署的系统,则选择全部系统,并执行部署操作。
## 验收要求
1. 自动化部署完成后检查容器状态是否正常,核查容器日志是否正确。
2. 检查对外服务状态:
- 日志是否正确打印如下信息`SYSTEMVERSION :: target_api_integration2.0.2612.258 2026-03-17 10:59:54`,版本号不固定判断,只要有就行,如有则标识为服务启动正常,若无则再等待10分钟,再次检查,若10分钟后仍然未输出,则标识为启动异常,记录异常日志。
- 调用对外接口`curl -k https://服务器IP/exapi/message/getMsgPageList`
- 成功:返回信息:`{"success":false,"code":"A0076","message":"无效token","result":"Full authentication is required to access this resource"}`
- 失败:返回信息:
```json
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>An error occurred.</h1>
<p>Sorry, the page you are looking for is currently unavailable.<br/>
Please try again later.</p>
<p>If you are the system administrator of this resource then you should check
the error log for details.</p>
<p><em>Faithfully yours, nginx.</em></p>
</body>
</html>
```
- 重试机制:根据文档的接口调用要求,执行重试机制。
3. 访问维护平台
- 按照部署文档中第三章系统授权进行执行操作,如遇验证码输入则填入`csba`
- 需上传的授权文件路径根据服务器IP获取对应的授权文件,文档顶部有标注对应路径。
- 继续根据文档执行第三章节的授权操作。
4. 新统一平台服务检查
- 检查服务启动状态:
- 检查预定对内、对外服务日志是否正常,是否存在异常日志输出。
- 检查运维服务日志是否正常,是否存在异常日志输出。
- 检查讯飞服务日志是否正常,是否存在异常日志输出。
- 检查服务接口状态:
- 预定系统:
- 调用预定系统接口`curl -k https://服务器IP/meetingV3/api/systemConfiguration/globalConfig?companyNumber=CN-SZ-00-0201`
- 响应结果:
- 成功:`{"success":false,"code":"A0078","message":"请求错误,accessToken为空","detailed":"com.ubains.meeting.exception.BasisException: 请求错误,accessToken为空"}`
- 失败:`{"code":500,"msg":"内部服务器错误"}`
- 重试机制:根据文档的接口调用要求,执行重试机制。
- 运维集控系统:
- 调用运维集控系统接口`curl -k https://服务器IP/monitor/api2/api/servermonitor/`
- 响应结果:
- 成功:`{"success":0,"data":[{"code":40000014,"error":"用户不存在或重新登录或已退出","describe":""}]}`
- 失败:
```
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>An error occurred.</h1>
<p>Sorry, the page you are looking for is currently unavailable.<br/>
Please try again later.</p>
<p>If you are the system administrator of this resource then you should check
the error log for details.</p>
<p><em>Faithfully yours, nginx.</em></p>
</body>
</html>
```
- 重试机制:根据文档的接口调用要求,执行重试机制。
- 讯飞转录系统:
- 调用讯飞转录系统接口`curl -k https://服务器IP/voice/api/iflytek/roommaster?company_id=1&user_id=8&company_secret=57d00f9f-020f-5f1f-b788-55fae843bceb&getall=1`
- 响应结果:
- 成功:`{"success":false,"data":[{"code":40000003,"error":"缺少关键参数","describe":""}]}`
- 失败:
```ignorelang
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>An error occurred.</h1>
<p>Sorry, the page you are looking for is currently unavailable.<br/>
Please try again later.</p>
<p>If you are the system administrator of this resource then you should check
the error log for details.</p>
<p><em>Faithfully yours, nginx.</em></p>
</body>
</html>
```
- 重试机制:根据文档的接口调用要求,执行重试机制。
5. 新统一平台访问使用
- 根据部署文档第四章节创建公司管理员,按照描述步骤进行操作。
## 分析要求
1. 部署文档描述清晰,无语法错误。
2. 部署过程清晰明了,无异常现象。
3. 部署到使用的时长记录要求在1小时内完成。
4. 部署过程中无异常日志输出。
5. 部署脚本日志打印清晰明了。
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论