# kfzgs-launcher.ps1 - 核价器启动脚本 (便携版) # 用法: # 协议模式: powershell -File kfzgs-launcher.ps1 -url "kfzgs://launch?dir=..." # 服务模式: powershell -File kfzgs-launcher.ps1 param([string]$url = "") $SERVICE_ID = "kfz-goods-pricing" $HTTP_PORT = 5000 $SCRIPT_DIR = Split-Path $MyInvocation.MyCommand.Path -Parent # 从同目录下的 config.json 读取 exe 路径,没有则用同目录下默认 exe $CONFIG_FILE = Join-Path $SCRIPT_DIR "launcher-config.json" $DEFAULT_EXE = Join-Path $SCRIPT_DIR "kfz-goods-pricing.exe" function Load-Config { if (Test-Path $CONFIG_FILE) { try { $config = Get-Content $CONFIG_FILE -Raw | ConvertFrom-Json if ($config.exe_path) { return $config.exe_path } } catch {} } return $DEFAULT_EXE } function Save-Config($exePath) { @{ exe_path = $exePath; id = $SERVICE_ID; name = "核价器" } | ConvertTo-Json | Out-File $CONFIG_FILE -Encoding UTF8 } $EXE_PATH = Load-Config function Parse-KfzgsUrl($rawUrl) { $decoded = [System.Uri]::UnescapeDataString($rawUrl) $stripped = $decoded -replace "^kfzgs://", "" $action = "" $params = @{} if ($stripped -match "^([^?]+)\?(.*)$") { $action = $Matches[1] $query = $Matches[2] $query -split "&" | ForEach-Object { $parts = $_ -split "=", 2 if ($parts.Count -eq 2) { $params[$parts[0]] = $parts[1] } } } elseif ($stripped -notmatch "[?]") { $action = $stripped } return @{ action = $action; params = $params } } function Invoke-ProtocolHandler($rawUrl) { $parsed = Parse-KfzgsUrl $rawUrl $p = $parsed.params if ($parsed.action -eq "launch") { $dir = $p["dir"] -replace "/", "\" $exeName = $p["exe"] if ($exeName) { if ($dir) { if ($dir.EndsWith("\")) { $fullPath = "$dir$exeName" } else { $fullPath = "$dir\$exeName" } } else { $fullPath = Join-Path $SCRIPT_DIR $exeName } } elseif ($dir -and $dir -match "\.exe$") { if (Test-Path $dir) { $fullPath = $dir } else { $fullPath = Join-Path $SCRIPT_DIR $dir } } else { $fullPath = $EXE_PATH } if ($fullPath -ne $EXE_PATH) { Save-Config $fullPath $script:EXE_PATH = $fullPath } if (Test-Path $fullPath) { $parentDir = Split-Path $fullPath -Parent Start-Process -FilePath $fullPath -WorkingDirectory $parentDir -WindowStyle Normal } } } function Start-ApiServer { $listener = New-Object System.Net.HttpListener $listener.Prefixes.Add("http://127.0.0.1:$HTTP_PORT/") try { $listener.Start() } catch { exit 1 } $services = @{} $services[$SERVICE_ID] = @{ id = $SERVICE_ID name = "核价器" exe_path = $EXE_PATH running = $false } while ($listener.IsListening) { $context = $listener.GetContext() $request = $context.Request $response = $context.Response if ($request.HttpMethod -eq "OPTIONS") { $response.AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS") $response.AddHeader("Access-Control-Allow-Headers", "Content-Type") $response.StatusCode = 204 $response.Close() continue } $path = $request.Url.AbsolutePath $result = @{ code = 0; msg = "ok" } try { if ($path -eq "/api/services") { if ($request.HttpMethod -eq "GET") { $services[$SERVICE_ID]["exe_path"] = $EXE_PATH $result["data"] = @($services.Values) } elseif ($request.HttpMethod -eq "POST") { $reader = New-Object System.IO.StreamReader($request.InputStream) $body = $reader.ReadToEnd() | ConvertFrom-Json if ($body.id -and $body.exe_path) { $services[$body.id] = @{ id = $body.id name = if ($body.name) { $body.name } else { $body.id } exe_path = $body.exe_path running = $false } Save-Config $body.exe_path $script:EXE_PATH = $body.exe_path $result["msg"] = "已注册: $($body.id)" $result["data"] = $services[$body.id] } else { $result["code"] = 1 $result["msg"] = "缺少 id 或 exe_path" } } } elseif ($path -match "^/api/services/([^/]+)$" -and $request.HttpMethod -eq "GET") { $sid = $Matches[1] if ($services.ContainsKey($sid)) { $result["data"] = $services[$sid] } else { $result["code"] = 1; $result["msg"] = "未找到" } } elseif ($path -match "^/api/services/([^/]+)/start$" -and $request.HttpMethod -eq "POST") { $sid = $Matches[1] if ($services.ContainsKey($sid)) { $svc = $services[$sid] $exePath = if ($svc["exe_path"] -and (Test-Path $svc["exe_path"])) { $svc["exe_path"] } else { $EXE_PATH } if (Test-Path $exePath) { $parentDir = Split-Path $exePath -Parent $proc = Start-Process -FilePath $exePath -WorkingDirectory $parentDir -WindowStyle Normal -PassThru $svc["running"] = $true $result["msg"] = "已启动" $result["pid"] = $proc.Id } else { $result["code"] = 1; $result["msg"] = "文件不存在: $exePath" } } else { $result["code"] = 1; $result["msg"] = "未找到" } } elseif ($path -match "^/api/services/([^/]+)/stop$" -and $request.HttpMethod -eq "POST") { $sid = $Matches[1] if ($services.ContainsKey($sid)) { $killed = $false Get-CimInstance Win32_Process | Where-Object { $_.ExecutablePath -eq $services[$sid]["exe_path"] } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue $killed = $true } $services[$sid]["running"] = $false $result["msg"] = if ($killed) { "已停止" } else { "未在运行" } $result["killed"] = $killed } else { $result["code"] = 1; $result["msg"] = "未找到" } } else { $result["code"] = 404; $result["msg"] = "未知接口" } } catch { $result["code"] = 1; $result["msg"] = $_.Exception.Message } $json = $result | ConvertTo-Json -Compress -Depth 3 $buffer = [System.Text.Encoding]::UTF8.GetBytes($json) $response.AddHeader("Access-Control-Allow-Origin", "*") $response.ContentType = "application/json; charset=utf-8" $response.ContentLength64 = $buffer.Length $response.OutputStream.Write($buffer, 0, $buffer.Length) $response.Close() } } if ($url -and $url -match "^kfzgs://") { Invoke-ProtocolHandler $url } else { Start-ApiServer }