From a83d7bfd95e8394e52a16da5c7cd3cf6b0a1e47e Mon Sep 17 00:00:00 2001
From: xyc <jc_xiong@hotmail.com>
Date: 星期六, 03 十月 2026 12:37:19 +0800
Subject: [PATCH] feat(部署): 增加双击安装服务入口(install-service.cmd)并自动验证启动结果

---
 deploy-templates/backend/install-service.ps1 |   77 ++++++++++++++++++++++++++++++++++----
 1 files changed, 69 insertions(+), 8 deletions(-)

diff --git a/deploy-templates/backend/install-service.ps1 b/deploy-templates/backend/install-service.ps1
index 16193e8..2b8f094 100644
--- a/deploy-templates/backend/install-service.ps1
+++ b/deploy-templates/backend/install-service.ps1
@@ -129,6 +129,23 @@
   Get-Service -Name $serviceId -ErrorAction SilentlyContinue
 }
 
+function Test-LocalPort {
+  param([int]$Port, [string]$TargetHost = '127.0.0.1')
+
+  # Probe TCP instead of HTTP: Invoke-WebRequest does not exist on Windows
+  # PowerShell 2 (Windows 7), and this works on every PowerShell version.
+  $client = New-Object System.Net.Sockets.TcpClient
+  try {
+    $async = $client.BeginConnect($TargetHost, $Port, $null, $null)
+    if ($async.AsyncWaitHandle.WaitOne(1000, $false) -and $client.Connected) { return $true }
+    return $false
+  } catch {
+    return $false
+  } finally {
+    $client.Close()
+  }
+}
+
 # ---------- read-only status: no elevation needed ----------
 if ($Status) {
   $service = Get-ServiceState
@@ -180,6 +197,7 @@
 if (-not (Test-Path $wrapper)) {
   throw "Missing wrapper executable: $wrapper`nRebuild the deploy package with pack-deploy.ps1 (it provides WinSW), or copy WinSW-x64.exe here renamed to trafficAudit.exe."
 }
+$passwordOk = $false
 if (-not (Test-Path $appConfig)) {
   Write-Host "WARN: application.yml is missing next to the jar."
   Write-Host "      The built-in password placeholder resolves to an empty string and startup will fail with"
@@ -195,6 +213,8 @@
     } elseif ($value -match '[^\x00-\x7F]') {
       Write-Host "WARN: the datasource password contains non-ASCII characters."
       Write-Host "      If it is still the Chinese placeholder text, replace it with the real MySQL password first."
+    } else {
+      $passwordOk = $true
     }
   }
 }
@@ -218,18 +238,59 @@
 if ($LASTEXITCODE -ne 0) { throw "WinSW install failed with exit code $LASTEXITCODE" }
 
 if (-not $NoStart) {
-  Write-Host "Starting service '$serviceId' (delayed auto-start is used at boot)..."
+  Write-Host "Starting service '$serviceId' (auto-start at boot is enabled)..."
   & $wrapper start
-  Start-Sleep -Seconds 8
+  Start-Sleep -Seconds 5
   $service = Get-ServiceState
   $service | Select-Object Name, DisplayName, Status, StartType | Format-List
-  $serviceDir = Join-Path $root 'logs'
+
+  # Read the port from application.yml so the check matches the real config.
+  $port = 8090
+  if (Test-Path $appConfig) {
+    $portLine = Select-String -LiteralPath $appConfig -Pattern '^\s*port\s*:' | Select-Object -First 1
+    if ($portLine -and $portLine.Line -match '(\d{2,5})') { $port = [int]$matches[1] }
+  }
+
+  $ready = $false
+  for ($i = 1; $i -le 30; $i++) {
+    if (Test-LocalPort -Port $port) { $ready = $true; break }
+    Start-Sleep -Seconds 2
+    $state = Get-ServiceState
+    if ($state -and $state.Status -ne 'Running') { break }
+  }
+
+  $logDir = Join-Path $root 'logs'
   Write-Host ''
-  Write-Host "Logs        : $serviceDir\trafficAudit.out.log / trafficAudit.err.log"
-  Write-Host "Health check: (Invoke-WebRequest http://127.0.0.1:8090 -UseBasicParsing).StatusCode"
+  if ($ready) {
+    Write-Host "OK  : the backend is listening on port $port."
+    Write-Host "      On this server   : http://127.0.0.1:$port"
+    Write-Host "      For other people : http://<server-ip>:$port"
+  } else {
+    Write-Host "WARN: nothing is listening on port $port yet - startup is still running, or it failed."
+    Write-Host "      Fix application.yml / MySQL, then run: .\install-service.ps1 -Restart"
+    foreach ($name in @('trafficAudit.err.log', 'trafficAudit.out.log')) {
+      $logFile = Join-Path $logDir $name
+      if (Test-Path -LiteralPath $logFile) {
+        Write-Host ''
+        Write-Host "---- last 15 lines of $name ----"
+        Get-Content -LiteralPath $logFile -Tail 15 -ErrorAction SilentlyContinue | ForEach-Object { Write-Host "  $_" }
+      }
+    }
+  }
+
+  if (-not $passwordOk) {
+    Write-Host ''
+    Write-Host '=============================================================='
+    Write-Host ' IMPORTANT: backend\application.yml still looks unset.'
+    Write-Host ' Put the real MySQL url / username / password in it, then run'
+    Write-Host '   .\install-service.ps1 -Restart'
+    Write-Host '=============================================================='
+  }
+
   Write-Host ''
-  Write-Host "Manage it with: Stop-Service $serviceId | Start-Service $serviceId"
-  Write-Host "Or: .\install-service.ps1 -Status | -Restart | -Remove"
+  Write-Host "Logs  : $logDir\trafficAudit.out.log / trafficAudit.err.log"
+  Write-Host "Manage: .\install-service.ps1 -Status | -Restart | -Remove"
+  Write-Host "        Stop-Service $serviceId | Start-Service $serviceId"
   Write-Host ''
-  Write-Host 'The service auto-starts on every boot (Automatic, delayed start).'
+  Write-Host 'This service starts automatically on every boot (Automatic + delayed start).'
 }

--
Gitblit v1.9.1