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

feat(service-inspection): 优化服务自检功能增加配置文件与日志导出

- 替换原有的日志导出功能为配置文件与日志文件导出功能
- 添加新统一平台和传统平台的前后端、中间件、第三方服务配置文件导出
- 实现按服务名称分类存储配置文件和日志文件到临时目录
- 添加压缩打包功能,生成服务器IP_时间戳.tar.gz格式的归档文件
- 更新交互提示文本从"导出服务日志"改为"导出配置文件与日志文件"
- 创建新的需求文档说明ERP对接优化和配置文件导出功能要求
上级 9f0ba57b
{
"containers": "NAMES STATUS IMAGE\ncardtable Up 48 minutes uos-cardtable:v1\npaperless Up 49 minutes paperless:v1\nupython_voice Up 49 minutes 139.9.60.86:5000/upython:v13\nupython Up 52 minutes 139.9.60.86:5000/upython:v16\nujava2 Up 56 minutes 139.9.60.86:5000/ujava:v7\nunginx Up About an hour nginx:1.30.2\nungrok Up About an hour ngrok:v1\nustorage Up About an hour ufastdfs:v2\nutracker Up About an hour ufastdfs:v2\nunacos Up About an hour nacos-server:v2.5.2\nuemqx Up 3 minutes emqx/emqx:6.0.0\nuredis Up About an hour redis:8\numysql Up About an hour mysql:8.0\n",
"container_detail": "cardtable: Up 48 minutes\npaperless: Up 49 minutes\nupython_voice: Up 49 minutes\nupython: Up 52 minutes\nujava2: Up 56 minutes\nunginx: Up About an hour\nungrok: Up About an hour\nustorage: Up About an hour\nutracker: Up About an hour\nunacos: Up About an hour\nuemqx: Up 3 minutes\nuredis: Up About an hour\numysql: Up About an hour\n",
"disk": "文件系统 容量 已用 可用 已用% 挂载点\n/dev/mapper/klas-data 81G 67G 15G 83% /data\n",
"sec_headers": "X-Frame-Options: SAMEORIGIN\r\nX-Content-Type-Options: nosniff\r\nX-XSS-Protection: 1; mode=block\r\nStrict-Transport-Security: max-age=31536000; includeSubDomains\r\n",
"log_errors": {
"预定对外服务": 40,
"预定对内服务": 14,
"运维服务": 16,
"讯飞服务": 0
}
}
\ No newline at end of file
[
{
"类别": "SSH配置",
"漏洞": "允许Root直接登录",
"等级": "中",
"描述": "PermitRootLogin设置为yes,建议设置为no或prohibit-password"
},
{
"类别": "SSH配置",
"漏洞": "允许密码登录",
"等级": "中",
"描述": "建议使用密钥认证替代密码认证"
},
{
"类别": "文件权限",
"漏洞": "/etc/shadow权限为0",
"等级": "中",
"描述": "建议设置为640或600"
},
{
"类别": "Nginx配置",
"漏洞": "缺少X-Frame-Options头",
"等级": "低",
"描述": "建议添加X-Frame-Options: DENY或SAMEORIGIN防止点击劫持"
},
{
"类别": "Nginx配置",
"漏洞": "缺少X-Content-Type-Options头",
"等级": "低",
"描述": "建议添加X-Content-Type-Options: nosniff防止MIME类型嗅探"
},
{
"类别": "Nginx配置",
"漏洞": "缺少HSTS头",
"等级": "低",
"描述": "建议添加Strict-Transport-Security强制HTTPS"
},
{
"类别": "Nginx配置",
"漏洞": "缺少X-XSS-Protection头",
"等级": "低",
"描述": "建议添加X-XSS-Protection: 1; mode=block"
}
]
\ No newline at end of file
......@@ -358,7 +358,7 @@ $ExpectedFunctions = @(
"Test-TraditionalPlatformConsole",
"Test-NTPService",
"Test-AndroidDeviceHealth",
"Export-ServiceLogs",
"Export-ConfigAndLogs",
"Show-HealthReport"
)
......@@ -2693,17 +2693,17 @@ function Main {
Write-Log -Level "INFO" -Message "跳过现场数据备份 (已禁用)"
# }
# 询问是否导出日志
# 询问是否导出配置文件与日志
Write-Host ""
Write-Host "==================================================================" -ForegroundColor Cyan
$exportChoice = Read-Host "是否导出服务日志到本地? (y/n) [默认: n]"
$exportChoice = Read-Host "是否导出配置文件与日志文件? (y/n) [默认: n]"
$logExportResults = $null
if ($exportChoice -eq "y" -or $exportChoice -eq "Y") {
$logExportResults = Export-ServiceLogs -Server $server -PlatformType $platformType -SystemInfo $systemInfo
$logExportResults = Export-ConfigAndLogs -Server $server -PlatformType $platformType
}
else {
Write-Log -Level "INFO" -Message "跳过日志导出"
Write-Log -Level "INFO" -Message "跳过配置文件与日志导出"
}
# 安卓设备自检(按 PRD 15:手动输入设备IP,连接+拉取日志)
......
<#
.SYNOPSIS
日志导出模块
Config and Log Export Module
.DESCRIPTION
此 PowerShell 模块用于从远程服务器导出服务日志文件到本地。
支持新统一平台和传统平台两种部署模式。
主要功能:
- 导出 Java 服务日志(ujava 容器)
- 导出 Python 服务日志(upython 容器)
- 导出 Nginx 错误日志(从容器内)
- 使用 pscp 工具进行文件传输
依赖:
- Invoke-SSHCommand: 用于执行远程 SSH 命令的函数
- Write-Log: 用于记录日志信息的函数
- pscp.exe: PuTTY 的 SCP 工具(需放在脚本同目录或系统 PATH 中)
.EXAMPLE
# 导入模块
Import-Module LogExport.psm1
# 导出日志
$server = @{
IP = "192.168.1.100"
Port = 22
User = "root"
Pass = "password"
}
$systemInfo = @{ HasUjava = $true; HasUpython = $false }
Export-ServiceLogs -Server $server -PlatformType "new" -SystemInfo $systemInfo
Export config files and log files from remote server to local.
Supports new unified platform and traditional platform.
.NOTES
Version: 1.0.0
Author: Ubains Operations Team
Creation Date: 2024-01-01
Last Modified: 2026-02-24
Copyright (c) 2024 Ubains. All rights reserved.
Version: 2.0.0
Last Modified: 2026-06-05
#>
#region Module Initialization
# 获取当前模块所在目录
$ModuleDir = Split-Path -Parent $MyInvocation.MyCommand.Path
# 导入依赖的公共模块
$CommonModulePath = Join-Path $ModuleDir "Common.psm1"
if (Test-Path $CommonModulePath) {
Import-Module $CommonModulePath -Force -Global -ErrorAction SilentlyContinue
}
# 获取脚本目录(用于日志导出)
$ScriptDir = Split-Path -Parent $ModuleDir
#endregion Module Initialization
#region 日志导出配置
# 新统一平台日志路径配置
$script:NewPlatformLogs = @(
@{ Name = "auth_log.out"; RemotePath = "/data/services/api/auth/auth-sso-auth/log.out" },
@{ 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 = "对外服务_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" },
@{ Name = "定时任务_ubains-INFO-AND-ERROR.log"; RemotePath = "/data/services/api/java-meeting/java-quartz/logs/ubains-INFO-AND-ERROR.log" }
#region Service Config
$script:NewPlatformFrontend = @(
@{ Name = "pc-vue2-ai"; BaseDir = "/data/services/web/pc/pc-vue2-ai"; Files = @("static/config.json") }
@{ Name = "pc-vue2-backstage"; BaseDir = "/data/services/web/pc/pc-vue2-backstage"; Files = @("static/config.json") }
@{ Name = "pc-vue2-main"; BaseDir = "/data/services/web/pc/pc-vue2-main"; Files = @("static/config.json") }
@{ Name = "pc-vue2-meetngV2"; BaseDir = "/data/services/web/pc/pc-vue2-meetngV2"; Files = @("static/config.json") }
@{ Name = "pc-vue2-meetngV3"; BaseDir = "/data/services/web/pc/pc-vue2-meetngV3"; Files = @("static/config.json") }
@{ Name = "pc-vue2-meetingControl"; BaseDir = "/data/services/web/pc/pc-vue2-meetingControl"; Files = @("static/config.json") }
@{ Name = "pc-vue2-moniter"; BaseDir = "/data/services/web/pc/pc-vue2-moniter"; Files = @("static/config.json") }
@{ Name = "pc-vue2-platform"; BaseDir = "/data/services/web/pc/pc-vue2-platform"; Files = @("static/config.json") }
@{ Name = "pc-vue2-voice"; BaseDir = "/data/services/web/pc/pc-vue2-voice/pc-vue2-voice"; Files = @("static/config.json") }
@{ Name = "h5-uniapp-meeting"; BaseDir = "/data/services/web/h5/h5-uniapp-meeting"; Files = @("static/config.json") }
@{ Name = "h5-uniapp-moniter"; BaseDir = "/data/services/web/h5/h5-uniapp-moniter"; Files = @("static/config.json") }
@{ Name = "h5-platform-mobile"; BaseDir = "/data/services/web/h5/h5-uniapp-platform/meeting-mobile"; Files = @("static/config.json") }
@{ Name = "h5-platform-platform-mobile"; BaseDir = "/data/services/web/h5/h5-uniapp-platform/unified-platform-mobile"; Files = @("static/config.json") }
)
# 传统平台 ujava 日志路径配置
$script:OldPlatformUjavaLogs = @(
@{ Name = "对内后端_ubains-INFO-AND-ERROR.log"; RemotePath = "/var/www/java/api-java-meeting2.0/logs/ubains-INFO-AND-ERROR.log" },
@{ Name = "对外后端_ubains-INFO-AND-ERROR.log"; RemotePath = "/var/www/java/external-meeting-api/logs/ubains-INFO-AND-ERROR.log" }
$script:NewPlatformBackend = @(
@{ Name = "auth-sso-auth"; BaseDir = "/data/services/api/auth/auth-sso-auth"; Files = @("log.out"; "config") }
@{ Name = "auth-sso-gatway"; BaseDir = "/data/services/api/auth/auth-sso-gatway"; Files = @("log.out"; "config") }
@{ Name = "auth-sso-system"; BaseDir = "/data/services/api/auth/auth-sso-system"; Files = @("log.out"; "config") }
@{ Name = "java-meeting2.0"; BaseDir = "/data/services/api/java-meeting/java-meeting2.0"; Files = @("logs/ubains-INFO-AND-ERROR.log"; "logs/ubains-ERROR.log"; "config") }
@{ Name = "java-meeting-extapi"; BaseDir = "/data/services/api/java-meeting/java-meeting-extapi"; Files = @("logs/ubains-INFO-AND-ERROR.log"; "logs/ubains-ERROR.log"; "config") }
@{ Name = "java-message-scheduling"; BaseDir = "/data/services/api/java-meeting/java-message-scheduling"; Files = @("logs/ubains-INFO-AND-ERROR.log"; "logs/ubains-ERROR.log"; "config") }
@{ Name = "java-mqtt"; BaseDir = "/data/services/api/java-meeting/java-mqtt"; Files = @("logs/ubains-INFO-AND-ERROR.log"; "logs/ubains-ERROR.log"; "config") }
@{ Name = "java-quartz"; BaseDir = "/data/services/api/java-meeting/java-quartz"; Files = @("logs/ubains-INFO-AND-ERROR.log"; "logs/ubains-ERROR.log"; "config") }
@{ Name = "python-cmdb"; BaseDir = "/data/services/api/python-cmdb"; Files = @("log/uinfo.log"; "log/error.log"; "log/uwsgi.log"; "setting.conf"; "cmdb/bus/config/settingbus.conf") }
@{ Name = "python-voice"; BaseDir = "/data/services/api/python-voice"; Files = @("log/uinfo.log"; "log/uwsgi.log"; "setting.conf"; "uvoice/bus/config/settingbus.conf") }
)
# 传统平台 upython 日志路径配置
$script:OldPlatformUpythonLogs = @(
@{ Name = "运维集控_error.log"; RemotePath = "/var/www/html/log/error.log" },
@{ Name = "运维集控_uinfo.log"; RemotePath = "/var/www/html/log/uinfo.log" },
@{ Name = "运维集控_uwsgi.log"; RemotePath = "/var/www/html/log/uwsgi.log" }
$script:NewPlatformMiddleware = @(
@{ Name = "nginx"; BaseDir = "/data/middleware/nginx"; Files = @("log"; "config") }
@{ Name = "redis"; BaseDir = "/data/middleware/redis"; Files = @("config"; "log") }
@{ Name = "emqx"; BaseDir = "/data/middleware/emqx"; Files = @("config"; "log") }
@{ Name = "mysql"; BaseDir = "/data/middleware/mysql"; Files = @("log"; "conf") }
@{ Name = "nacos"; BaseDir = "/data/middleware/nacos"; Files = @("conf"; "logs/alipay-jraft.log"; "logs/cmdb-main.log"; "logs/config-dump.log"; "logs/config-fatal.log"; "logs/config-memory.log"; "logs/config-notify.log"; "logs/config-pull.log"; "logs/core-auth.log"; "logs/istio-main.log"; "logs/nacos.log"; "logs/nacos-persistence.log"; "logs/naming-distro.log"; "logs/naming-event.log"; "logs/naming-performance.log"; "logs/naming-push.log"; "logs/naming-raft.log"; "logs/naming-rt.log"; "logs/naming-server.log"; "logs/plugin-control.log"; "logs/plugin-control-tps.log"; "logs/protocol-distro.log"; "logs/protocol-raft.log"; "logs/remote-digest.log"; "logs/remote.log"; "logs/remote-push.log") }
)
#endregion 日志导出配置
#region Nginx 日志导出
<#
.SYNOPSIS
从 Docker 容器中导出 Nginx 错误日志
$script:NewPlatformThirdParty = @(
@{ Name = "paperless"; BaseDir = "/data/third_party/paperless"; Files = @("application.yml"; "nohup.out") }
@{ Name = "wifi-local"; BaseDir = "/data/third_party/wifi-local"; Files = @("nohup.out"; "config.ini") }
)
.DESCRIPTION
使用 docker cp 命令将容器内的 Nginx 错误日志复制到宿主机临时目录,
然后通过 pscp 下载到本地。
$script:OldPlatformFrontend = @(
@{ Name = "ubains-web-2.0"; BaseDir = "/var/www/java/ubains-web-2.0"; Files = @("static/config.json") }
@{ Name = "ubains-web-admin"; BaseDir = "/var/www/java/ubains-web-admin"; Files = @("static/config.json") }
@{ Name = "ubains-video-web-3.0"; BaseDir = "/var/www/java/ubains-video-web-3.0"; Files = @("static/config.json") }
@{ Name = "ubains-web-h5"; BaseDir = "/var/www/java/ubains-web-h5"; Files = @("static/h5/config.json") }
@{ Name = "web-vue-rms"; BaseDir = "/var/www/html/web-vue-rms"; Files = @("static/config.json") }
@{ Name = "web-vue-h5"; BaseDir = "/var/www/html/web-vue-h5"; Files = @("static/config.json") }
)
.PARAMETER Server
包含服务器连接信息的哈希表(IP、Port、User、Pass)
$script:OldPlatformBackend = @(
@{ Name = "api-java-meeting2.0"; BaseDir = "/var/www/java/api-java-meeting2.0"; Files = @("logs/ubains-INFO-AND-ERROR.log"; "logs/ubains-ERROR.log"; "config") }
@{ Name = "external-meeting-api"; BaseDir = "/var/www/java/external-meeting-api"; Files = @("logs/ubains-INFO-AND-ERROR.log"; "logs/ubains-ERROR.log"; "config") }
@{ Name = "html-ops"; BaseDir = "/var/www/html"; Files = @("setting.conf"; "log/uinfo.log"; "log/error.log"; "log/uwsgi.log") }
)
.PARAMETER ContainerName
容器名称
$script:OldPlatformMiddleware = @(
@{ Name = "nginx"; BaseDir = "/var/www/java/nginx-conf.d"; Files = @("meeting443.conf"; "nginx_log") }
@{ Name = "redis"; BaseDir = "/var/www/redis"; Files = @("*.conf"; "data") }
@{ Name = "emqx"; BaseDir = "/var/www/emqx"; Files = @("config"; "log") }
@{ Name = "mysql"; BaseDir = "/usr/local/docker/mysql"; Files = @("logs") }
@{ Name = "fdfs"; BaseDir = "/var/fdfs"; Files = @("storage/logs"; "tracker/logs") }
)
.PARAMETER ExportDir
本地导出目录
$script:OldPlatformThirdParty = @(
@{ Name = "paperless"; BaseDir = "/var/www/paperless"; Files = @("application.yml"; "nohup.out") }
@{ Name = "wifi-local"; BaseDir = "/var/www/wifi-local"; Files = @("config.ini"; "nohup.out") }
)
.PARAMETER ExportedFiles
导出成功的文件列表(引用传递)
#endregion Service Config
.PARAMETER FailedFiles
导出失败的文件列表(引用传递)
#region Export Function
.OUTPUTS
System.Void
函数通过引用参数更新 ExportedFiles 和 FailedFiles
#>
function Export-NginxErrorLogFromContainer {
function Export-ConfigAndLogs {
param(
[Parameter(Mandatory=$true)] [hashtable]$Server,
[Parameter(Mandatory=$true)] [string]$ContainerName,
[Parameter(Mandatory=$true)] [string]$ExportDir,
[ref]$ExportedFiles,
[ref]$FailedFiles
[hashtable]$Server,
[string]$PlatformType
)
if (-not $global:PSCP_PATH -or -not (Test-Path $global:PSCP_PATH)) {
Write-Log -Level "ERROR" -Message "[Nginx] pscp.exe 未找到,无法导出 nginx_error.log"
$FailedFiles.Value += @{
Name = "${ContainerName}_nginx_error.log"
RemotePath = "/usr/local/nginx/logs/error.log (in-container)"
Reason = "pscp.exe 未找到"
}
return
}
Write-Host ""
Write-Log -Level "INFO" -Message "[Nginx] 开始导出 Nginx error.log (容器: $ContainerName) ..."
$remoteTmpDir = "/tmp"
$remoteTmpFile = "$remoteTmpDir/nginx_error.log"
$localFileName = "${ContainerName}_nginx_error.log"
$localNginxLog = Join-Path $ExportDir $localFileName
# 使用单引号 here-string 构造远端脚本,避免 PowerShell 解析
$remoteCmd = @'
if docker ps --format "{{.Names}}" 2>/dev/null | grep -w "__CONTAINER__" >/dev/null 2>&1; then \
mkdir -p "__TMPDIR__" && \
echo "[Nginx] docker cp __CONTAINER__:/usr/local/nginx/logs/error.log -> __TMPFILE__" && \
docker cp "__CONTAINER__:/usr/local/nginx/logs/error.log" "__TMPFILE__" 2>/tmp/nginx_cp_err.log; \
retCode=$?; \
if [ $retCode -ne 0 ]; then \
echo "[Nginx] docker cp 失败,详情:"; \
if [ -f /tmp/nginx_cp_err.log ]; then cat /tmp/nginx_cp_err.log; fi; \
exit $retCode; \
fi; \
echo "[Nginx] 已拷贝到宿主机: __TMPFILE__"; \
else \
echo "[Nginx] 容器未运行或不存在: __CONTAINER__"; \
exit 1; \
fi
'@
# 占位符替换为实际值
$remoteCmd = $remoteCmd.Replace("__CONTAINER__", $ContainerName).
Replace("__TMPDIR__", $remoteTmpDir).
Replace("__TMPFILE__", $remoteTmpFile)
# 去掉 Windows 回车,避免 bash: $'\r' 未找到命令
$remoteCmd = $remoteCmd -replace "`r", ""
# 远端执行 docker cp 脚本
$cpRes = Invoke-SSHCommand -HostName $Server.IP `
-User $Server.User `
-Pass $Server.Pass `
-Port $Server.Port `
-Command $remoteCmd
$cpOutput = ($cpRes.Output | Out-String).Trim()
if ($cpOutput) {
Write-Log -Level "INFO" -Message ("[Nginx] 远端输出: {0}" -f ($cpOutput -replace '\s+', ' '))
}
Write-Log -Level "INFO" -Message "========== Config and Log Export =========="
if ($cpRes.ExitCode -ne 0) {
Write-Log -Level "ERROR" -Message "[Nginx] 远端 docker cp 执行失败,终止 Nginx 日志导出"
$FailedFiles.Value += @{
Name = "nginx_error.log"
RemotePath = "/usr/local/nginx/logs/error.log (in-container)"
Reason = "docker cp 失败,详见远端输出"
# Check pscp availability
if (-not $global:PSCP_PATH -or -not (Test-Path $global:PSCP_PATH)) {
Write-Log -Level "ERROR" -Message "pscp.exe not found"
return @{
Success = $false
ExportDir = $null
ArchiveFile = $null
ExportedServices = @()
SkippedServices = @()
FailedServices = @()
}
return
}
# 用 pscp 下载 /tmp/nginx_error.log -> 本地
Write-Log -Level "INFO" -Message "[Nginx] 下载: $remoteTmpFile -> $localNginxLog"
$pscpArgs = @(
"-scp",
"-batch",
"-P", $Server.Port,
"-l", $Server.User,
"-pw", $Server.Pass,
"$($Server.User)@$($Server.IP):$remoteTmpFile",
$localNginxLog
)
try {
$dlOut = & $global:PSCP_PATH @pscpArgs 2>&1
$dlCode = $LASTEXITCODE
if ($dlCode -ne 0 -and ($dlOut -match "host key" -or $dlOut -match "Cannot confirm")) {
$cmdLine = "echo y | `"$($global:PSCP_PATH)`" -scp -batch -P $($Server.Port) -l $($Server.User) -pw `"$($Server.Pass)`" `"$($Server.User)@$($Server.IP):$remoteTmpFile`" `"$localNginxLog`""
Write-Log -Level "WARN" -Message "[Nginx] 主机密钥提示,自动接受并重试"
$dlOut = cmd /c $cmdLine 2>&1
$dlCode = $LASTEXITCODE
}
if ($dlCode -eq 0 -and (Test-Path $localNginxLog)) {
$sz = (Get-Item $localNginxLog).Length
$szKB = [math]::Round($sz / 1024, 2)
Write-Log -Level "SUCCESS" -Message "[Nginx] $localFileName 导出成功 ($szKB KB): $localNginxLog"
$ExportedFiles.Value += @{
Name = $localFileName
RemotePath = "/usr/local/nginx/logs/error.log (via docker cp)"
LocalPath = $localNginxLog
Size = $sz
}
# Select service list by platform type
if ($PlatformType -eq "new") {
Write-Log -Level "INFO" -Message "Platform: New"
$allServices = $script:NewPlatformFrontend + $script:NewPlatformBackend + $script:NewPlatformMiddleware + $script:NewPlatformThirdParty
}
else {
Write-Log -Level "ERROR" -Message ("[Nginx] 下载失败,ExitCode={0}" -f $dlCode)
if ($dlOut) {
$oneLine = ($dlOut -join " ") -replace '\s+', ' '
Write-Log -Level "ERROR" -Message ("[Nginx] pscp 输出: {0}" -f $oneLine)
}
$FailedFiles.Value += @{
Name = $localFileName
RemotePath = $remoteTmpFile
Reason = "下载失败: ExitCode=$dlCode"
}
}
}
catch {
Write-Log -Level "ERROR" -Message "[Nginx] 下载异常: $($_.Exception.Message)"
$FailedFiles.Value += @{
Name = $localFileName
RemotePath = $remoteTmpFile
Reason = "异常: $($_.Exception.Message)"
}
Write-Log -Level "INFO" -Message "Platform: Old"
$allServices = $script:OldPlatformFrontend + $script:OldPlatformBackend + $script:OldPlatformMiddleware + $script:OldPlatformThirdParty
}
$remoteTmpBase = "/tmp/config_and_log"
$exportedServices = @()
$skippedServices = @()
$failedServices = @()
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$archiveName = "$($Server.IP)_${timestamp}.tar.gz"
$remoteArchive = "/tmp/$archiveName"
# Step 1: Create remote temp directory
Write-Log -Level "INFO" -Message "[Step 1] Create remote temp dir: $remoteTmpBase"
$mkdirCmd = "rm -rf $remoteTmpBase; mkdir -p $remoteTmpBase"
$mkdirResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $mkdirCmd
if ($mkdirResult.ExitCode -ne 0) {
Write-Log -Level "ERROR" -Message "[Step 1] Failed to create remote temp dir"
return @{
Success = $false
ExportDir = $null
ArchiveFile = $null
ExportedServices = @()
SkippedServices = @()
FailedServices = @()
}
finally {
$cleanCmd = "rm -f '$remoteTmpFile' 2>/dev/null || true"
[void](Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $cleanCmd)
}
}
Write-Log -Level "SUCCESS" -Message "[Step 1] Remote temp dir created"
#endregion Nginx 日志导出
# Step 2: Iterate services and copy files
Write-Log -Level "INFO" -Message "[Step 2] Scanning $($allServices.Count) services"
Write-Host ""
#region 服务日志导出
foreach ($svc in $allServices) {
$svcName = $svc.Name
$svcBase = $svc.BaseDir
$svcFiles = $svc.Files
$svcTmpDir = "$remoteTmpBase/$svcName"
<#
.SYNOPSIS
导出远程服务器上的服务日志到本地
# Check if service directory exists
$checkCmd = "[ -d '$svcBase' ] && echo EXISTS || echo NOT_EXISTS"
$checkResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $checkCmd
.DESCRIPTION
根据平台类型(新统一平台/传统平台)和系统信息(ujava/upython 容器),
导出相应的服务日志文件到本地目录。
支持的日志类型:
- Java 服务日志(ujava 容器)
- Python 服务日志(upython 容器)
- Nginx 错误日志(从容器内导出)
.PARAMETER Server
包含服务器连接信息的哈希表,需包含以下字段:
- IP: 服务器IP地址
- Port: SSH端口
- User: SSH用户名
- Pass: SSH密码
.PARAMETER PlatformType
平台类型:new(新统一平台)或 old(传统平台)
.PARAMETER SystemInfo
包含系统信息的哈希表,支持以下键:
- HasUjava: 是否存在 ujava 容器
- HasUpython: 是否存在 upython 容器
- UjavaContainer: ujava 容器名称
- UpythonContainer: upython 容器名称
.OUTPUTS
System.Collections.Hashtable
返回包含导出结果的哈希表:
- Success: 是否有文件成功导出
- ExportedFiles: 成功导出的文件列表
- FailedFiles: 导出失败的文件列表
- ExportDir: 本地导出目录
.EXAMPLE
$server = @{
IP = "192.168.1.100"
Port = 22
User = "root"
Pass = "password"
if ($checkResult.Output -match 'NOT_EXISTS') {
Write-Log -Level "WARN" -Message " [SKIP] $svcName - dir not found: $svcBase"
$skippedServices += @{
Name = $svcName
Reason = "dir not found: $svcBase"
}
$systemInfo = @{
HasUjava = $true
HasUpython = $false
UjavaContainer = "ujava"
continue
}
Export-ServiceLogs -Server $server -PlatformType "new" -SystemInfo $systemInfo
.NOTES
依赖:
- pscp.exe 必须可用(通过 $global:PSCP_PATH 指定)
- Invoke-SSHCommand 函数用于执行远程命令
- Write-Log 函数用于记录日志
#>
function Export-ServiceLogs {
param(
[hashtable]$Server,
[string]$PlatformType,
[hashtable]$SystemInfo
)
# Create service sub-directory
$mkSvcDir = "mkdir -p '$svcTmpDir'"
[void](Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $mkSvcDir)
Write-Host ""
Write-Log -Level "INFO" -Message "========== 服务日志导出 =========="
# 检查 pscp 是否可用
if (-not $global:PSCP_PATH -or -not (Test-Path $global:PSCP_PATH)) {
Write-Log -Level "ERROR" -Message "pscp.exe 未找到,无法导出日志"
Write-Log -Level "ERROR" -Message "请将 pscp.exe 放在脚本同目录下"
Write-Log -Level "ERROR" -Message "下载地址: https://the.earth.li/~sgtatham/putty/latest/w64/pscp.exe"
return @{
Success = $false
ExportedFiles = @()
FailedFiles = @()
ExportDir = $null
}
}
# Copy each file/directory
$copiedFiles = @()
$copyErrors = @()
# 创建导出目录(以服务器IP和时间戳命名)
$exportTimestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$exportDirName = "logs_$($Server.IP)_$exportTimestamp"
$exportDir = Join-Path $ScriptDir $exportDirName
foreach ($fileItem in $svcFiles) {
$srcPath = "$svcBase/$fileItem"
$dstPath = $svcTmpDir
if (-not (Test-Path $exportDir)) {
New-Item -ItemType Directory -Path $exportDir -Force | Out-Null
# Build remote copy command based on file type
if ($fileItem -match '\*') {
# Wildcard pattern
$copyCmd = "cp -r ${srcPath} ${dstPath}/ 2>/dev/null; echo EXIT:$?"
}
else {
# Check if file or dir, copy accordingly
$copyCmd = 'if [ -d "' + $srcPath + '" ]; then cp -r "' + $srcPath + '" "' + $dstPath + '/" 2>/dev/null; elif [ -f "' + $srcPath + '" ]; then mkdir -p "$(dirname "' + $dstPath + '/' + $fileItem + '")"; cp "' + $srcPath + '" "' + $dstPath + '/' + $fileItem + '" 2>/dev/null; fi; echo EXIT:$?'
}
Write-Log -Level "INFO" -Message "日志导出目录: $exportDir"
$exportedFiles = @()
$failedFiles = @()
$logsToExport = @()
# 根据平台类型选择要导出的日志
if ($PlatformType -eq "new") {
# 新统一平台
Write-Log -Level "INFO" -Message "平台类型: 新统一平台"
$copyResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $copyCmd
$copyOutput = ($copyResult.Output | Out-String).Trim()
if ($SystemInfo.HasUjava) {
Write-Log -Level "INFO" -Message "检测到 ujava 容器,准备导出 Java 服务日志..."
$logsToExport += $script:NewPlatformLogs
if ($copyOutput -match 'EXIT:0') {
$copiedFiles += $fileItem
}
else {
Write-Log -Level "WARN" -Message "未检测到 ujava 容器,跳过 Java 服务日志导出"
$copyErrors += $fileItem
}
}
else {
# 传统平台
Write-Log -Level "INFO" -Message "平台类型: 传统平台"
if ($SystemInfo.HasUjava) {
Write-Log -Level "INFO" -Message "检测到 ujava 容器,准备导出 Java 服务日志..."
$logsToExport += $script:OldPlatformUjavaLogs
# Record result
if ($copiedFiles.Count -gt 0) {
Write-Log -Level "SUCCESS" -Message " [OK] $svcName - exported $($copiedFiles.Count) items"
$exportedServices += @{
Name = $svcName
Files = $copiedFiles
}
else {
Write-Log -Level "WARN" -Message "未检测到 ujava 容器,跳过 Java 服务日志导出"
}
if ($SystemInfo.HasUpython) {
Write-Log -Level "INFO" -Message "检测到 upython 容器,准备导出 Python 服务日志..."
$logsToExport += $script:OldPlatformUpythonLogs
}
else {
Write-Log -Level "WARN" -Message "未检测到 upython 容器,跳过 Python 服务日志导出"
Write-Log -Level "WARN" -Message " [FAIL] $svcName - no files exported"
$failedServices += @{
Name = $svcName
Reason = "dir exists but no files: $($copyErrors -join ', ')"
}
}
if ($logsToExport.Count -eq 0) {
Write-Log -Level "WARN" -Message "没有需要导出的日志文件"
return @{
Success = $true
ExportedFiles = @()
FailedFiles = @()
ExportDir = $exportDir
}
}
Write-Log -Level "INFO" -Message "共 $($logsToExport.Count) 个日志文件待导出..."
Write-Host ""
Write-Log -Level "INFO" -Message "Scan done: $($exportedServices.Count) ok, $($skippedServices.Count) skipped, $($failedServices.Count) failed"
# 逐个导出日志文件
foreach ($logConfig in $logsToExport) {
$localFileName = $logConfig.Name
$remotePath = $logConfig.RemotePath
$localPath = Join-Path $exportDir $localFileName
# Step 3: Remote compress
Write-Log -Level "INFO" -Message "[Step 3] Compress: $remoteArchive"
$tarCmd = "cd /tmp; tar -czf '${archiveName}' config_and_log 2>/dev/null; if [ `$? -eq 0 ]; then echo TAR_OK; else echo TAR_FAIL; fi"
$tarResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $tarCmd
$tarOutput = ($tarResult.Output | Out-String).Trim()
Write-Log -Level "INFO" -Message "正在导出: $localFileName"
Write-Log -Level "INFO" -Message " 远程路径: $remotePath"
# 先检查远程文件是否存在
$checkCmd = "[ -f '$remotePath' ] && echo 'EXISTS' || echo 'NOT_EXISTS'"
$checkResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $checkCmd
if ($checkResult.Output -match 'NOT_EXISTS') {
Write-Log -Level "WARN" -Message " [跳过] 文件不存在: $remotePath"
$failedFiles += @{
Name = $localFileName
RemotePath = $remotePath
Reason = "文件不存在"
if ($tarOutput -notmatch 'TAR_OK') {
Write-Log -Level "ERROR" -Message "[Step 3] Compress failed: $tarOutput"
[void](Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command "rm -rf $remoteTmpBase")
return @{
Success = $false
ExportDir = $null
ArchiveFile = $null
ExportedServices = $exportedServices
SkippedServices = $skippedServices
FailedServices = $failedServices
}
continue
}
Write-Log -Level "SUCCESS" -Message "[Step 3] Compress OK"
# Step 4: Download to local
$localExportDir = Join-Path $ScriptDir "config_and_log"
if (-not (Test-Path $localExportDir)) {
New-Item -ItemType Directory -Path $localExportDir -Force | Out-Null
}
$localArchive = Join-Path $localExportDir $archiveName
Write-Log -Level "INFO" -Message "[Step 4] Download to: $localArchive"
# 使用 pscp 下载文件
$pscpArgs = @(
"-scp",
"-batch",
"-P", $Server.Port,
"-l", $Server.User,
"-pw", $Server.Pass,
"$($Server.User)@$($Server.IP):$remotePath",
$localPath
"$($Server.User)@$($Server.IP):$remoteArchive",
$localArchive
)
try {
$pscpResult = & $global:PSCP_PATH @pscpArgs 2>&1
$exitCode = $LASTEXITCODE
# 如果失败且是主机密钥问题,自动接受后重试一次
if ($exitCode -ne 0 -and ($pscpResult -match "host key" -or $pscpResult -match "Cannot confirm")) {
Write-Log -Level "WARN" -Message " 检测到主机密钥提示,自动接受并重试"
$cmdLine = "echo y | `"$($global:PSCP_PATH)`" -scp -batch -P $($Server.Port) -l $($Server.User) -pw `"$($Server.Pass)`" `"$($Server.User)@$($Server.IP):$remotePath`" `"$localPath`""
Write-Log -Level "WARN" -Message "[Step 4] Host key prompt, auto-accept and retry"
$cmdLine = "echo y | `"$($global:PSCP_PATH)`" -scp -batch -P $($Server.Port) -l $($Server.User) -pw `"$($Server.Pass)`" `"$($Server.User)@$($Server.IP):$remoteArchive`" `"$localArchive`""
$pscpResult = cmd /c $cmdLine 2>&1
$exitCode = $LASTEXITCODE
}
if ($exitCode -eq 0 -and (Test-Path $localPath)) {
$fileSize = (Get-Item $localPath).Length
$fileSizeKB = [math]::Round($fileSize / 1024, 2)
Write-Log -Level "SUCCESS" -Message " [成功] 已导出 ($fileSizeKB KB)"
$exportedFiles += @{
Name = $localFileName
RemotePath = $remotePath
LocalPath = $localPath
Size = $fileSize
}
if ($exitCode -eq 0 -and (Test-Path $localArchive)) {
$archiveSize = (Get-Item $localArchive).Length
$archiveSizeMB = [math]::Round($archiveSize / 1MB, 2)
$sizeMsg = "[Step 4] Download OK ($archiveSizeMB MB): $localArchive"
Write-Log -Level "SUCCESS" -Message $sizeMsg
}
else {
Write-Log -Level "ERROR" -Message (" [失败] 导出失败,ExitCode={0}" -f $exitCode)
if ($pscpResult) {
$oneLine = ($pscpResult -join " ") -replace '\s+', ' '
Write-Log -Level "ERROR" -Message (" [pscp] 输出: {0}" -f $oneLine)
}
$failedFiles += @{
Name = $localFileName
RemotePath = $remotePath
Reason = "下载失败: ExitCode=$exitCode"
Write-Log -Level "ERROR" -Message ("[Step 4] Download failed, ExitCode=" + $exitCode)
[void](Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command "rm -rf $remoteTmpBase /tmp/$archiveName")
return @{
Success = $false
ExportDir = $localExportDir
ArchiveFile = $null
ExportedServices = $exportedServices
SkippedServices = $skippedServices
FailedServices = $failedServices
}
}
}
catch {
Write-Log -Level "ERROR" -Message " [失败] 导出异常: $($_.Exception.Message)"
$failedFiles += @{
Name = $localFileName
RemotePath = $remotePath
Reason = "异常: $($_.Exception.Message)"
}
}
Write-Log -Level "ERROR" -Message "[Step 4] Download exception: $($_.Exception.Message)"
[void](Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command "rm -rf $remoteTmpBase /tmp/$archiveName")
return @{
Success = $false
ExportDir = $localExportDir
ArchiveFile = $null
ExportedServices = $exportedServices
SkippedServices = $skippedServices
FailedServices = $failedServices
}
# 传统平台 nginx 日志导出
if ($PlatformType -eq "old") {
# ujava 场景:从 ujava 容器导出 nginx error.log
if ($SystemInfo.HasUjava -and $SystemInfo.UjavaContainer) {
Export-NginxErrorLogFromContainer -Server $Server -ContainerName $SystemInfo.UjavaContainer -ExportDir $exportDir `
-ExportedFiles ([ref]$exportedFiles) -FailedFiles ([ref]$failedFiles)
}
# upython 场景:从 upython 容器导出 nginx error.log
if ($SystemInfo.HasUpython -and $SystemInfo.UpythonContainer) {
Export-NginxErrorLogFromContainer -Server $Server -ContainerName $SystemInfo.UpythonContainer -ExportDir $exportDir `
-ExportedFiles ([ref]$exportedFiles) -FailedFiles ([ref]$failedFiles)
}
}
# Step 5: Clean up remote temp files
Write-Log -Level "INFO" -Message "[Step 5] Clean up remote temp"
$cleanCmd = "rm -rf $remoteTmpBase /tmp/$archiveName"
[void](Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $cleanCmd)
Write-Log -Level "SUCCESS" -Message "[Step 5] Remote temp cleaned"
Write-Host ""
Write-Log -Level "INFO" -Message "日志导出完成: 成功 $($exportedFiles.Count) 个,失败 $($failedFiles.Count) 个"
if ($exportedFiles.Count -gt 0) {
Write-Log -Level "SUCCESS" -Message "导出目录: $exportDir"
}
Write-Log -Level "SUCCESS" -Message "========== Config and Log Export Done =========="
Write-Log -Level "INFO" -Message "Archive: $localArchive"
Write-Log -Level "INFO" -Message "Local dir: $localExportDir"
return @{
Success = ($exportedFiles.Count -gt 0)
ExportedFiles = $exportedFiles
FailedFiles = $failedFiles
ExportDir = $exportDir
Success = ($exportedServices.Count -gt 0)
ExportDir = $localExportDir
ArchiveFile = $localArchive
ExportedServices = $exportedServices
SkippedServices = $skippedServices
FailedServices = $failedServices
}
}
#endregion 服务日志导出
#endregion Export Function
# ==============================================================================
# 导出模块函数
# ==============================================================================
Export-ModuleMember -Function @(
'Export-ServiceLogs',
'Export-NginxErrorLogFromContainer'
)
Export-ModuleMember -Function @('Export-ConfigAndLogs')
......@@ -417,26 +417,63 @@ function Show-HealthReport {
elseif ($failedServices -gt 0) { Write-Host ""; Write-Host " 存在异常服务,请及时处理!" -ForegroundColor Red; $md += "- 结论: $overallIcon 存在异常服务,请及时处理!" }
elseif ($totalServices -eq 0) { Write-Host ""; Write-Host " 未检测到任何服务" -ForegroundColor Yellow; $md += "- 结论: $overallIcon 未检测到任何服务" }
# 日志导出
# 配置文件与日志导出
if ($LogExportResults) {
Write-Host "【日志导出结果】" -ForegroundColor Yellow
$md += ""; $md += "## 服务日志导出"
if ($LogExportResults.Success -and $LogExportResults.ExportedFiles.Count -gt 0) {
Write-Host "【配置文件与日志导出结果】" -ForegroundColor Yellow
$md += ""; $md += "## 配置文件与日志导出"
if ($LogExportResults.Success) {
Write-Host " 导出状态: 成功" -ForegroundColor Green
Write-Host " 导出目录: $($LogExportResults.ExportDir)" -ForegroundColor Cyan
Write-Host " 成功导出 $($LogExportResults.ExportedFiles.Count) 个文件:" -ForegroundColor Green
$md += "- 导出状态: ✅ 成功"
$md += "- 导出目录: $($LogExportResults.ExportDir)"
$md += "- 成功文件:"
foreach ($file in $LogExportResults.ExportedFiles) { $sizeKB = [math]::Round($file.Size / 1024, 2); Write-Host " - $($file.Name) ($sizeKB KB)" -ForegroundColor Gray; $md += " - $($file.Name) ($sizeKB KB)" }
} elseif ($LogExportResults.ExportedFiles.Count -eq 0 -and $LogExportResults.FailedFiles.Count -eq 0) {
Write-Host " 导出状态: 无需导出的日志文件" -ForegroundColor Yellow
$md += "- 导出状态: ℹ️ 无需导出的日志文件"
} else {
Write-Host " 导出状态: 部分失败" -ForegroundColor Yellow
$md += "- 导出状态: ⚠️ 部分失败"
if ($LogExportResults.FailedFiles.Count -gt 0) { $md += "- 失败文件:"; foreach ($file in $LogExportResults.FailedFiles) { $md += " - $($file.Name): $($file.Reason)" } }
}
else {
Write-Host " 导出状态: 失败或无文件导出" -ForegroundColor Red
$md += "- 导出状态: ❌ 失败或无文件导出"
}
# 压缩包路径
if ($LogExportResults.ArchiveFile) {
Write-Host " 压缩包: $($LogExportResults.ArchiveFile)" -ForegroundColor Cyan
$md += "- 压缩包: $($LogExportResults.ArchiveFile)"
}
# 本地目录
if ($LogExportResults.ExportDir) {
Write-Host " 本地目录: $($LogExportResults.ExportDir)" -ForegroundColor Cyan
$md += "- 本地目录: $($LogExportResults.ExportDir)"
}
# 已导出服务
if ($LogExportResults.ExportedServices -and $LogExportResults.ExportedServices.Count -gt 0) {
Write-Host " 已导出服务 ($($LogExportResults.ExportedServices.Count) 个):" -ForegroundColor Green
$md += "- 已导出服务 ($($LogExportResults.ExportedServices.Count) 个):"
foreach ($svc in $LogExportResults.ExportedServices) {
$fileList = ($svc.Files -join ', ')
Write-Host " - $($svc.Name): $($svc.Files.Count) 项 ($fileList)" -ForegroundColor Gray
$md += " - $($svc.Name): $($svc.Files.Count) 项 ($fileList)"
}
}
# 跳过的服务
if ($LogExportResults.SkippedServices -and $LogExportResults.SkippedServices.Count -gt 0) {
Write-Host " 跳过的服务 ($($LogExportResults.SkippedServices.Count) 个):" -ForegroundColor Yellow
$md += "- 跳过的服务 ($($LogExportResults.SkippedServices.Count) 个):"
foreach ($svc in $LogExportResults.SkippedServices) {
Write-Host " - $($svc.Name): $($svc.Reason)" -ForegroundColor DarkGray
$md += " - $($svc.Name): $($svc.Reason)"
}
}
# 失败的服务
if ($LogExportResults.FailedServices -and $LogExportResults.FailedServices.Count -gt 0) {
Write-Host " 失败的服务 ($($LogExportResults.FailedServices.Count) 个):" -ForegroundColor Red
$md += "- 失败的服务 ($($LogExportResults.FailedServices.Count) 个):"
foreach ($svc in $LogExportResults.FailedServices) {
Write-Host " - $($svc.Name): $($svc.Reason)" -ForegroundColor DarkGray
$md += " - $($svc.Name): $($svc.Reason)"
}
}
Write-Host ""
}
......
# _PRD_服务自检配置文件与日志导出_需求文档.md
> 版本:
> 更新日期:
> 适用范围:服务自检脚本(Windows 远程版本 + Linux 本机版本)
> 实现脚本:
> - Windows 版:`AuxiliaryTool\ScriptTool\ServiceSelfInspection\check_server_health.ps1`
> - Linux 版:`AuxiliaryTool\ScriptTool\ServiceSelfInspection\check_server_health.sh`
> 测试环境:
- 桌面测试目录:[C:\Users\UBAINS\Desktop\Test-module]
- 测试服务器为:192.168.5.44 root Ubains@123
- 代码实现后需自行验证,输出验证结果。
---
## 1. 背景与目标
### 1.1 背景
- 会议预定系统和统一平台部署在 Linux 服务器上,核心服务运行在 Docker 容器中(ujava/upython/upython_voice)。为降低运维成本,需要实现服务健康检查工具,并自动修复常见问题。
### 1.2 目标
- 实现将对应平台上的所有配置文件与日志文件进行压缩导出。
### 1.3 双版本说明
| 版本 | 文件名 | 运行环境 | 版本号 | 说明 |
|------|--------|----------|--------|------|
| Windows 版 | check_server_health.ps1 | Windows 10+ | | 通过 SSH 远程连接 Linux 服务器进行检测 |
| Linux 版 | check_server_health.sh | Linux 服务器 | | 直接在服务器本机运行,无需 SSH 连接 |
**重要说明:**
- 两版本核心功能保持一致,后续功能优化需**同步更新**两个脚本
- 差异部分见下节"版本差异说明"
---
## 2 需求说明
- 新增一个交互步骤,用于导出配置文件与日志文件。
### 新统一平台
#### 前端服务目录:
- ai包:/data/services/web/pc/pc-vue2-ai
- static文件夹下的config.json
- 后台包:/data/services/web/pc/pc-vue2-backstage
- static文件夹下的config.json
- main包:/data/services/web/pc/pc-vue2-main
- static文件夹下的config.json
- meetingV2包:/data/services/web/pc/pc-vue2-meetingV2
- static文件夹下的config.json
- meetingV3包:/data/services/web/pc/pc-vue2-meetingV3
- static文件夹下的config.json
- meetingControl:/data/services/web/pc/pc-vue2-meetingControl
- static文件夹下的config.json
- moniter包:/data/services/web/pc/pc-vue2-moniter
- static文件夹下的config.json
- platform包:/data/services/web/pc/pc-vue2-platform
- static文件夹下的config.json
- voice包:/data/services/web/pc/pc-vue2-voice/pc-vue2-voice
- static文件夹下的config.json
- h5-meeting:/data/services/web/h5/h5-uniapp-meeting
- static文件夹下的config.json
- h5-moniter:/data/services/web/h5/h5-uniapp-moniter
- static文件夹下的config.json
- h5-platform-mobile:/data/services/web/h5/h5-uniapp-platform/meeting-mobile
- static文件夹下的config.json
- h5-platform-platform-mobile:/data/services/web/h5/h5-uniapp-platform/unified-platform-mobile
- static文件夹下的config.json
#### 后端服务目录:
- auth包:/data/services/api/auth/auth-sso-auth
- log.out
- config目录
- gatway包:/data/services/api/auth/auth-sso-gatway
- log.out
- config目录
- system包:/data/services/api/auth/auth-sso-system
- log.out
- config目录
- java2.0包:/data/services/api/java-meeting/java-meeting2.0
- logs/ubains-INFO-AND-ERROR.log、logs/ubains-ERROR.log
- config目录
- java-extapi包:/data/services/api/java-meeting/java-meeting-extapi
- logs/ubains-INFO-AND-ERROR.log、logs/ubains-ERROR.log
- config目录
- java-scheduling包:/data/services/api/java-meeting/java-message-scheduling
- logs/ubains-INFO-AND-ERROR.log、logs/ubains-ERROR.log
- config目录
- java-mqtt包:/data/services/api/java-meeting/java-mqtt
- logs/ubains-INFO-AND-ERROR.log、logs/ubains-ERROR.log
- config目录
- java-quartz包:/data/services/api/java-meeting/java-quartz
- logs/ubains-INFO-AND-ERROR.log、logs/ubains-ERROR.log
- config目录
- cmdb包:/data/services/api/python-cmdb
- log/uinfo.log、log/error.log和log/uwsgi.log
- setting.conf
- cmdb/bus/config/settingbus.conf
- voice包:/data/services/api/python-voice
- log/uinfo.log和log/uwsgi.log
- setting.conf
- uvoice/bus/config/settingbus.conf
#### 中间件服务目录:
- nginx服务:/data/middleware/nginx
- log目录
- config目录
- redis服务:/data/middleware/redis
- config目录
- log目录
- emqx服务:/data/middleware/emqx
- config目录
- log目录
- mysql服务:/data/middleware/mysql
- log目录
- conf目录
- nacos服务:/data/middleware/nacos
- conf目录
- logs/alipay-jraft.log、logs/cmdb-main.log、logs/config-dump.log、logs/config-fatal.log、logs/config-memory.log、logs/config-notify.log、logs/config-pull.log、logs/core-auth.log、logs/istio-main.log、logs/nacos.log、logs/nacos-persistence.log、logs/naming-distro.log、logs/naming-event.log、logs/naming-performance.log、logs/naming-push.log、logs/naming-raft.log、logs/naming-rt.log、logs/naming-server.log、logs/plugin-control.log、logs/plugin-control-tps.log、logs/protocol-distro.log、logs/protocol-raft.log、logs/remote-digest.log、logs/remote.log、logs/remote-push.log
#### 第三方服务目录:
- paperless服务:/data/third_party/paperless
- application.yml
- nohup.out
- wifi-local服务:/data/third_party/wifi-local
- nohup.out
- config.ini
### 传统平台
#### 前端服务目录:
- 预定前台前端:/var/www/java/ubains-web-2.0
- static文件夹下的config.json
- 预定后台前端:/var/www/java/ubains-web-admin
- static文件夹下的config.json
- 预定视讯前端:/var/www/java/ubains-video-web-3.0
- static文件夹下的config.json
- 预定H5前端:/var/www/java/ubains-web-h5
- static/h5文件夹下的config.json
- 运维前台前端:/var/www/html/web-vue-rms
- static文件夹下的config.json
- 运维H5前端:/var/www/html/web-vue-h5
- static文件夹下的config.json
#### 后端服务目录:
- 预定2.0后端:/var/www/java/api-java-meeting2.0
- logs/ubains-INFO-AND-ERROR.log、logs/ubains-ERROR.log
- config目录
- 预定对外后端:/var/www/java/external-meeting-api
- logs/ubains-INFO-AND-ERROR.log、logs/ubains-ERROR.log
- config目录
- 运维后端:/var/www/html
- setting.conf
- log/uinfo.log、log/error.log和log/uwsgi.log
#### 中间件服务目录:
- nginx服务:/var/www/java/nginx-conf.d
- meeting443.conf
- nginx_log目录
- redis服务:/var/www/redis
- *.conf
- data目录
- emqx服务:/var/www/emqx
- config目录
- log目录
- mysql服务:/usr/local/docker/mysql
- logs目录
- fdfs服务:/var/fdfs
- storage/logs目录
- tracker/logs目录
#### 第三方服务目录:
- paperless服务:/var/www/paperless
- application.yml
- nohup.out
- wifi-local服务:/var/www/wifi-local
- config.ini
- nohup.out
### 补充说明
- **替换说明**:本需求将**删除**旧的"是否导出服务日志到本地?"交互及 `Export-ServiceLogs` 功能,**替换**为新的"是否导出配置文件与日志文件?"交互,在同一位置。
- **路径说明**:所有后端服务列出的文件路径均为**相对于服务目录**的路径。例如 cmdb 的 `setting.conf` 实际路径为 `/data/services/api/python-cmdb/setting.conf``cmdb/bus/config/settingbus.conf` 实际路径为 `/data/services/api/python-cmdb/cmdb/bus/config/settingbus.conf`
- **传统平台运维后端**`/var/www/html` 仅导出 `setting.conf` 文件和 `log/` 目录,不导出整个 `/var/www/html` 目录。
- **不存在目录处理**:如果某个服务的目录在服务器上不存在,则跳过该服务并在报告中记录,不影响其他服务的导出。
- **文件大小限制**:不对导出文件做大小限制,全量导出。
### 操作流程
1. 删除旧的"是否导出服务日志到本地?"交互步骤,替换为新的"是否导出配置文件与日志文件?"交互步骤,输入y则进行导出操作,输入n则不进行导出操作。
2. 按照对应平台进行配置文件与日志文件导出操作
- 新统一平台
- 传统平台
3. 将相关服务的配置文件与日志文件,先拷贝至/tmp/config_and_log/目录下,并根据服务名称进行分类存放。
4. 将/tmp目录下的config_and_log目录进行压缩操作,压缩成tar.gz格式。命名规则:服务器IP_时间.tar.gz
5. 将tar.gz文件下载
- windows版本:下载到当前脚本所在目录的config_and_log目录下,并删除/tmp/config_and_log目录和/tmp下的压缩包。
- linux版本:下载到当前脚本所在目录的config_and_log目录下,并删除/tmp/config_and_log目录和/tmp下的压缩包。
6. 服务自检报告需要补充配置文件与日志文件导出的结果,及所在目录。
## 需求规范
- 代码规范: `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
# _PRD_服务自检优化配置文件与日志导出_需求文档_计划执行
> 版本:V1.0
> 更新日期:2026-06-05
> 来源需求:`_PRD_服务自检优化配置文件与日志导出_需求文档.md`
> 状态:待执行
---
## 1. 执行概述
### 1.1 项目背景
在服务自检脚本的基础上,将旧的"日志导出"功能替换为更完整的"配置文件与日志导出"功能,支持将对应平台上所有服务的配置文件和日志文件进行分类收集、压缩打包并下载到本地。
### 1.2 执行目标
- **删除**旧的"是否导出服务日志到本地?"交互及 `Export-ServiceLogs` 功能
- **替换**为新的"是否导出配置文件与日志文件?"交互
- 支持新统一平台和传统平台两种部署模式
- 支持 Windows (PowerShell 远程) 和 Linux (Shell 本机) 两种执行环境
- 导出结果包含在服务自检报告中
### 1.3 涉及文件
| 文件 | 类型 | 说明 |
|------|------|------|
| `modules/LogExport.psm1` | 重构 | 删除旧的日志导出逻辑,替换为配置文件+日志导出逻辑 |
| `check_server_health.ps1` | 修改 | 更新交互提示、调用方式、报告参数 |
| `modules/Report.psm1` | 修改 | 更新报告中的日志导出章节为配置文件与日志导出 |
| `check_server_health.sh` | 修改 | 替换 `export_logs()` 函数,更新交互提示和报告 |
| `check_server_health_auto.sh` | 修改 | 同步更新自动化版本的导出逻辑 |
---
## 2. 任务分解与实施计划
### 2.1 任务总览
| 序号 | 任务 | 优先级 | 涉及文件 | 状态 |
|------|------|--------|----------|------|
| 2.1.1 | 重构 LogExport.psm1 模块 | P0 | `modules/LogExport.psm1` | [ ] |
| 2.1.2 | 更新 check_server_health.ps1 主流程 | P0 | `check_server_health.ps1` | [ ] |
| 2.1.3 | 更新 Report.psm1 报告模块 | P0 | `modules/Report.psm1` | [ ] |
| 2.1.4 | 重构 check_server_health.sh 的 export_logs() | P0 | `check_server_health.sh` | [ ] |
| 2.1.5 | 同步更新 check_server_health_auto.sh | P1 | `check_server_health_auto.sh` | [ ] |
| 2.1.6 | 同步到测试目录并测试 | P0 | - | [ ] |
---
### 2.2 重构 LogExport.psm1 模块(Windows 版)
**文件位置:** `AuxiliaryTool/ScriptTool/ServiceSelfInspection/modules/LogExport.psm1`
**修改说明:** 将旧的 `Export-ServiceLogs` 函数替换为 `Export-ConfigAndLogs` 函数
#### 2.2.1 服务目录配置定义
定义两个平台的完整服务目录映射(包含前端、后端、中间件),每条记录包含:
- 服务名称(用于分类存放)
- 服务根目录
- 需要导出的文件/目录列表(相对于服务根目录)
**新统一平台 - 前端服务(13项):**
| 服务名称 | 服务根目录 | 导出文件 |
|----------|-----------|---------|
| pc-vue2-ai | `/data/services/web/pc/pc-vue2-ai` | `static/config.json` |
| pc-vue2-backstage | `/data/services/web/pc/pc-vue2-backstage` | `static/config.json` |
| pc-vue2-main | `/data/services/web/pc/pc-vue2-main` | `static/config.json` |
| pc-vue2-meetingV2 | `/data/services/web/pc/pc-vue2-meetingV2` | `static/config.json` |
| pc-vue2-meetingV3 | `/data/services/web/pc/pc-vue2-meetingV3` | `static/config.json` |
| pc-vue2-meetingControl | `/data/services/web/pc/pc-vue2-meetingControl` | `static/config.json` |
| pc-vue2-moniter | `/data/services/web/pc/pc-vue2-moniter` | `static/config.json` |
| pc-vue2-platform | `/data/services/web/pc/pc-vue2-platform` | `static/config.json` |
| pc-vue2-voice | `/data/services/web/pc/pc-vue2-voice/pc-vue2-voice` | `static/config.json` |
| h5-uniapp-meeting | `/data/services/web/h5/h5-uniapp-meeting` | `static/config.json` |
| h5-uniapp-moniter | `/data/services/web/h5/h5-uniapp-moniter` | `static/config.json` |
| h5-platform-mobile | `/data/services/web/h5/h5-uniapp-platform/meeting-mobile` | `static/config.json` |
| h5-platform-platform-mobile | `/data/services/web/h5/h5-uniapp-platform/unified-platform-mobile` | `static/config.json` |
**新统一平台 - 后端服务(10项):**
| 服务名称 | 服务根目录 | 导出文件 |
|----------|-----------|---------|
| auth-sso-auth | `/data/services/api/auth/auth-sso-auth` | `log.out`, `config/` |
| auth-sso-gatway | `/data/services/api/auth/auth-sso-gatway` | `log.out`, `config/` |
| auth-sso-system | `/data/services/api/auth/auth-sso-system` | `log.out`, `config/` |
| java-meeting2.0 | `/data/services/api/java-meeting/java-meeting2.0` | `logs/ubains-INFO-AND-ERROR.log`, `logs/ubains-ERROR.log`, `config/` |
| java-meeting-extapi | `/data/services/api/java-meeting/java-meeting-extapi` | `logs/ubains-INFO-AND-ERROR.log`, `logs/ubains-ERROR.log`, `config/` |
| java-message-scheduling | `/data/services/api/java-meeting/java-message-scheduling` | `logs/ubains-INFO-AND-ERROR.log`, `logs/ubains-ERROR.log`, `config/` |
| java-mqtt | `/data/services/api/java-meeting/java-mqtt` | `logs/ubains-INFO-AND-ERROR.log`, `logs/ubains-ERROR.log`, `config/` |
| java-quartz | `/data/services/api/java-meeting/java-quartz` | `logs/ubains-INFO-AND-ERROR.log`, `logs/ubains-ERROR.log`, `config/` |
| python-cmdb | `/data/services/api/python-cmdb` | `log/uinfo.log`, `log/error.log`, `log/uwsgi.log`, `setting.conf`, `cmdb/bus/config/settingbus.conf` |
| python-voice | `/data/services/api/python-voice` | `log/uinfo.log`, `log/uwsgi.log`, `setting.conf`, `uvoice/bus/config/settingbus.conf` |
**新统一平台 - 中间件服务(4项):**
| 服务名称 | 服务根目录 | 导出文件 |
|----------|-----------|---------|
| nginx | `/data/middleware/nginx` | `log/`, `config/` |
| redis | `/data/middleware/redis` | `config/`, `log/` |
| emqx | `/data/middleware/emqx` | `config/`, `log/` |
| mysql | `/data/middleware/mysql` | `log/`, `conf/` |
| nacos | `/data/middleware/nacos` | `conf/`, `logs/alipay-jraft.log` 等25个日志文件 |
**新统一平台 - 第三方服务(2项):**
| 服务名称 | 服务根目录 | 导出文件 |
|----------|-----------|---------|
| paperless | `/data/third_party/paperless` | `application.yml`, `nohup.out` |
| wifi-local | `/data/third_party/wifi-local` | `nohup.out`, `config.ini` |
**传统平台 - 前端服务(6项):**
| 服务名称 | 服务根目录 | 导出文件 |
|----------|-----------|---------|
| ubains-web-2.0 | `/var/www/java/ubains-web-2.0` | `static/config.json` |
| ubains-web-admin | `/var/www/java/ubains-web-admin` | `static/config.json` |
| ubains-video-web-3.0 | `/var/www/java/ubains-video-web-3.0` | `static/config.json` |
| ubains-web-h5 | `/var/www/java/ubains-web-h5` | `static/h5/config.json` |
| web-vue-rms | `/var/www/html/web-vue-rms` | `static/config.json` |
| web-vue-h5 | `/var/www/html/web-vue-h5` | `static/config.json` |
**传统平台 - 后端服务(3项):**
| 服务名称 | 服务根目录 | 导出文件 |
|----------|-----------|---------|
| api-java-meeting2.0 | `/var/www/java/api-java-meeting2.0` | `logs/ubains-INFO-AND-ERROR.log`, `logs/ubains-ERROR.log`, `config/` |
| external-meeting-api | `/var/www/java/external-meeting-api` | `logs/ubains-INFO-AND-ERROR.log`, `logs/ubains-ERROR.log`, `config/` |
| html-ops | `/var/www/html` | `setting.conf`, `log/uinfo.log`, `log/error.log`, `log/uwsgi.log` |
**传统平台 - 中间件服务(5项):**
| 服务名称 | 服务根目录 | 导出文件 |
|----------|-----------|---------|
| nginx | `/var/www/java/nginx-conf.d` | `meeting443.conf`, `nginx_log/` |
| redis | `/var/www/redis` | `*.conf`, `data/` |
| emqx | `/var/www/emqx` | `config/`, `log/` |
| mysql | `/usr/local/docker/mysql` | `logs/` |
| fdfs | `/var/fdfs` | `storage/logs/`, `tracker/logs/` |
**传统平台 - 第三方服务(2项):**
| 服务名称 | 服务根目录 | 导出文件 |
|----------|-----------|---------|
| paperless | `/var/www/paperless` | `application.yml`, `nohup.out` |
| wifi-local | `/var/www/wifi-local` | `config.ini`, `nohup.out` |
#### 2.2.2 Export-ConfigAndLogs 函数设计
**参数:**
- `$Server` (hashtable) - `{IP, Port, User, Pass}`
- `$PlatformType` (string) - `"new"``"old"`
**返回值:** hashtable
```powershell
@{
Success = $true/$false
ExportDir = "本地导出目录路径"
ArchiveFile = "本地压缩包路径"
ExportedServices = @(@{ Name = ""; Files = @() }, ...) # 成功导出的服务
SkippedServices = @(@{ Name = ""; Reason = "" }, ...) # 跳过的服务
FailedServices = @(@{ Name = ""; Reason = "" }, ...) # 失败的服务
}
```
**执行流程:**
1. **验证 pscp 可用** - 检查 `$global:PSCP_PATH`
2. **远程创建临时目录** - 通过 SSH 在服务器上创建 `/tmp/config_and_log/`
3. **遍历服务目录配置** - 根据 `$PlatformType` 选择对应的服务列表
4. **逐服务导出:**
- 检查服务根目录是否存在于远程服务器(`test -d`
- 不存在 → 记录到 `SkippedServices`,继续下一个
- 存在 → 创建 `/tmp/config_and_log/<服务名称>/` 子目录
- 逐项导出文件/目录:
- 单文件:`cp <源文件> /tmp/config_and_log/<服务名称>/`
- 目录:`cp -r <源目录> /tmp/config_and_log/<服务名称>/`
- 通配符(如 `*.conf`):`cp <通配符> /tmp/config_and_log/<服务名称>/`
- 导出失败 → 记录到 `FailedServices`,继续
5. **远程压缩** - `tar -czf /tmp/<IP>_<时间>.tar.gz -C /tmp config_and_log`
6. **下载到本地** - 通过 pscp 下载到 `$SCRIPT_DIR/config_and_log/`
7. **远程清理** - 删除 `/tmp/config_and_log/``/tmp/<IP>_<时间>.tar.gz`
8. **返回结果**
#### 2.2.3 保留的旧函数
- `Export-NginxErrorLogFromContainer` 不再需要(nginx 日志已在新配置中覆盖)
- 删除 `$script:NewPlatformLogs``$script:OldPlatformUjavaLogs``$script:OldPlatformUpythonLogs` 配置变量
- 导出函数名由 `Export-ServiceLogs` 改为 `Export-ConfigAndLogs`
---
### 2.3 更新 check_server_health.ps1 主流程
**文件位置:** `AuxiliaryTool/ScriptTool/ServiceSelfInspection/check_server_health.ps1`
**修改范围:** 第2696-2707行(旧的日志导出交互)
**修改内容:**
1. **替换交互提示**(第2699行):
```
# 旧:
$exportChoice = Read-Host "是否导出服务日志到本地? (y/n) [默认: n]"
# 新:
$exportChoice = Read-Host "是否导出配置文件与日志文件? (y/n) [默认: n]"
```
2. **替换函数调用**(第2703行):
```powershell
# 旧:
$logExportResults = Export-ServiceLogs -Server $server -PlatformType $platformType -SystemInfo $systemInfo
# 新:
$logExportResults = Export-ConfigAndLogs -Server $server -PlatformType $platformType
```
3. **更新模块加载**(第313行):
- 模块文件名不变,仍为 `LogExport.psm1`,但需更新验证列表中的函数名(第361行)
- `Export-ServiceLogs` → `Export-ConfigAndLogs`
4. **更新报告调用**(第2737-2750行):
- `$logExportResults` 结构变更,需适配 Report.psm1 中的新字段
---
### 2.4 更新 Report.psm1 报告模块
**文件位置:** `AuxiliaryTool/ScriptTool/ServiceSelfInspection/modules/Report.psm1`
**修改内容:** 更新日志导出报告章节(当前在报告末尾的日志导出部分)
**新报告格式:**
```markdown
## 配置文件与日志导出
### 导出结果
- **状态**:✅ 成功 / ❌ 失败 / ⏭️ 跳过
- **压缩包**`<本地路径/IP_时间.tar.gz>`
- **本地目录**`<本地路径/config_and_log/>`
### 已导出服务 (<N>项)
| 服务名称 | 导出文件数 |
|----------|-----------|
| auth-sso-auth | 3 |
| java-meeting2.0 | 3 |
| ... | ... |
### 跳过的服务 (<N>项)
| 服务名称 | 原因 |
|----------|------|
| python-voice | 目录不存在 |
### 失败的服务 (<N>项)
| 服务名称 | 原因 |
|----------|------|
| ... | ... |
```
---
### 2.5 重构 check_server_health.sh 的 export_logs()
**文件位置:** `AuxiliaryTool/ScriptTool/ServiceSelfInspection/check_server_health.sh`
**修改范围:**
- `export_logs()` 函数(第2726-2844行)→ 替换为 `export_config_and_logs()`
- 交互提示(第3451-3465行)→ 更新提示文本和调用
- 报告部分(`write_report()` 中相关章节)→ 更新格式
#### 2.5.1 export_config_and_logs() 函数设计
**参数:**
- `$1` - `platform`("new" 或 "old")
**返回值:** 通过 `echo` 输出本地导出目录路径
**执行流程:**
1. 定义服务目录配置数组(与 PS1 版一致的结构)
2. 创建本地临时目录 `$SCRIPT_DIR/config_and_log/`
3. 遍历服务配置:
- `test -d` 检查服务目录是否存在
- 不存在 → 记录跳过,继续
- 存在 → 创建子目录,`cp` 文件/目录
4. 压缩:`tar -czf $SCRIPT_DIR/config_and_log/<IP>_<时间>.tar.gz -C $SCRIPT_DIR/config_and_log .`
5. 记录结果到 REPORT_KV
6. 输出导出目录路径
**与 PS1 版的差异:**
- Shell 版本直接在服务器本机操作,无需 SSH/pscp
- 使用 `cp` 代替远程拷贝+下载
- 压缩后文件直接在本机可用
#### 2.5.2 更新主流程 main()
**第3453行交互提示替换:**
```bash
# 旧:
read -r -p "是否导出服务日志到本地目录? (y/n) [默认:n]: " ex
# 新:
read -r -p "是否导出配置文件与日志文件? (y/n) [默认:n]: " ex
```
**第3456行函数调用替换:**
```bash
# 旧:
export_dir="$(export_logs "$platform" "$HAS_UJAVA" "$HAS_UPYTHON" "${UJAVA_CONTAINER:-}" "${UPYTHON_CONTAINER:-}" || true)"
# 新:
export_dir="$(export_config_and_logs "$platform" || true)"
```
#### 2.5.3 更新 write_report() 报告章节
更新日志导出章节为配置文件与日志导出,增加跳过服务和失败服务的展示。
---
### 2.6 同步更新 check_server_health_auto.sh
**文件位置:** `AuxiliaryTool/ScriptTool/ServiceSelfInspection/check_server_health_auto.sh`
**修改内容:** 与 check_server_health.sh 保持一致
- 将自动化版本中的 `export_logs()` 替换为 `export_config_and_logs()`
- 自动化版本默认执行导出(无需交互确认)
---
## 3. 验收标准
### 3.1 功能验收
| 序号 | 验收项 | 验收标准 | 状态 |
|------|--------|----------|------|
| 1 | 新统一平台导出 | 能正确导出所有前端config.json、后端日志和config目录、中间件配置和日志 | [ ] |
| 2 | 传统平台导出 | 能正确导出传统平台所有服务的配置文件和日志 | [ ] |
| 3 | 跳过不存在服务 | 不存在的服务目录自动跳过,在报告中列出 | [ ] |
| 4 | 压缩打包 | 生成 `IP_时间.tar.gz` 格式的压缩包 | [ ] |
| 5 | 下载到本地 | Windows 版通过 pscp 下载,Linux 版直接在本机 | [ ] |
| 6 | 远程清理 | 导出完成后清理服务器上的临时文件 | [ ] |
| 7 | 报告展示 | 报告包含导出结果、跳过服务、失败服务信息 | [ ] |
| 8 | 旧功能移除 | 旧的"导出服务日志"提示和功能已删除 | [ ] |
| 9 | 两个版本一致 | PS1 和 Shell 版本功能保持一致 | [ ] |
### 3.2 测试环境
| 项目 | 信息 |
|------|------|
| 测试目录 | `C:\Users\UBAINS\Desktop\Test-module` |
| 测试服务器 | 192.168.5.44 |
| SSH 凭据 | root / Ubains@123 |
| 测试方式 | 更新后同步到测试目录,连接测试服务器执行验证 |
---
## 4. 风险评估
| 风险 | 等级 | 应对措施 |
|------|------|----------|
| 部分服务目录不存在导致导出中断 | 低 | 逐服务检查,跳过不存在的目录 |
| 压缩包过大导致下载超时 | 中 | pscp 设置合理超时时间,不限制文件大小 |
| 权限不足无法读取某些配置文件 | 低 | 以 root 用户执行,记录失败原因 |
| PS1 和 Shell 版本不一致 | 低 | 共用相同的服务目录配置结构,同步实现 |
---
## 5. 实施记录
> 以下为实施过程中填写的记录
### 5.1 实施进度
| 日期 | 任务 | 结果 | 备注 |
|------|------|------|------|
| | 2.2 重构 LogExport.psm1 | | |
| | 2.3 更新 check_server_health.ps1 | | |
| | 2.4 更新 Report.psm1 | | |
| | 2.5 重构 check_server_health.sh | | |
| | 2.6 同步 check_server_health_auto.sh | | |
| | 测试验证 | | |
---
## 6. 后续工作
- [ ] 测试服务器 192.168.5.44 上验证新统一平台导出功能
- [ ] 传统平台环境验证(如有测试环境)
- [ ] 更新版本号
---
## 7. 附录
### 7.1 相关文档
- 需求文档:`Docs/PRD/服务自检/需求文档/_PRD_服务自检优化配置文件与日志导出_需求文档.md`
- 代码规范:`Docs/PRD/01规范文档/_PRD_规范文档_代码规范.md`
- 文档规范:`Docs/PRD/01规范文档/_PRD_规范文档_文档规范.md`
# 报告生成优化需求文档
## 代码路径
- 代码路径:[AuxiliaryTool/FunctionalTestReportGeneration]
## ERP对接文档
- 需求文档路径:[Docs/PRD/自动化生成功能测试报告/ERP对接PRD/PRD_测试列表_测试报告_例子说明.md]
## 功能需求
### 功能目标
**目标:** 对接获取人员列表接口以及调整上传文件接口的抄送人传参和创建人员传参。然后通过交互模式下输入的创建人姓名、抄送人姓名,最终通过接口传参实现。
### 需求描述
- 获取人员接口:
- 根据[Docs/PRD/自动化生成功能测试报告/ERP对接PRD/PRD_测试列表_测试报告_例子说明.md]第538–541实现获取人员列表。
- 上传文件接口调整:
- 根据[Docs/PRD/自动化生成功能测试报告/ERP对接PRD/PRD_测试列表_测试报告_例子说明.md]第182行。
- createuser_name通过交互模式,用户输入姓名。从获取人员列表中做提取ID。
- copyuserList通过交互模式,用户输入姓名,从获取人员列表中做提取ID,注意参数格式如下:
- "copyuserList": [1, 24, 31]
- 最终在上传文件接口中,将createuser_name、createuser_id、copyuserList作为参数传入。
## 规范文档
- 代码规范: `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
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论