From 8dd708633e03cc26530bba486b19937413f684f6 Mon Sep 17 00:00:00 2001 From: JusterZhu Date: Sun, 21 Jun 2026 01:13:48 +0800 Subject: [PATCH 1/2] fix: update server-side DTO and scripts to use PackageType instead of IsCrossVersion/FromVersion - Replace VerificationResultDTO.IsCrossVersion/FromVersion with PackageType - Replace Server internal VersionEntry record fields with PackageType - Update all 4 PowerShell scripts (generate, finalize, create_versions, gen_packages) - Update versions.json sample data - Update Program.cs verification/download logic - Keep PatchGenerator backward-compat parsing for old fields --- src/Server/DTOs/VerificationResultDTO.cs | 9 ++---- src/Server/Program.cs | 15 +++++----- src/Server/create_versions_json.ps1 | 6 ++-- src/Server/finalize_packages.ps1 | 6 ++-- src/Server/generate_packages.ps1 | 26 +++++++---------- src/Server/wwwroot/packages/versions.json | 35 +++++++++-------------- src/gen_packages.ps1 | 4 +-- 7 files changed, 39 insertions(+), 62 deletions(-) diff --git a/src/Server/DTOs/VerificationResultDTO.cs b/src/Server/DTOs/VerificationResultDTO.cs index 112896b..1894ca0 100644 --- a/src/Server/DTOs/VerificationResultDTO.cs +++ b/src/Server/DTOs/VerificationResultDTO.cs @@ -73,12 +73,7 @@ public record VerificationResultDTO public bool? IsFreeze { get; set; } /// - /// 是否为跨版本升级包 + /// 包类型: 0=Unspecified, 1=Chain(差分), 2=Full(完整包), 3=Driver /// - public bool? IsCrossVersion { get; set; } - - /// - /// 跨版本升级的源版本号 - /// - public string? FromVersion { get; set; } + public int PackageType { get; set; } } \ No newline at end of file diff --git a/src/Server/Program.cs b/src/Server/Program.cs index cd1269e..8293cdb 100644 --- a/src/Server/Program.cs +++ b/src/Server/Program.cs @@ -60,7 +60,7 @@ return true; }) .OrderByDescending(v => new Version(v.Version!)) - .ThenBy(v => v.IsCrossVersion == true ? 1 : 0) + .ThenBy(v => v.PackageType == 1 ? 1 : 0) .ToList(); if (available.Count == 0) @@ -80,14 +80,13 @@ Version = v.Version, AppType = v.AppType, Platform = v.Platform, ProductId = v.ProductId, IsForcibly = v.IsForcibly, Format = v.Format ?? ".zip", Size = v.Size, IsFreeze = v.IsFreeze, - IsCrossVersion = v.IsCrossVersion ?? false, - FromVersion = v.FromVersion + PackageType = v.PackageType, }); } Console.WriteLine($"[Verification] Returning {results.Count} packages"); foreach (var r in results) - Console.WriteLine($" {r.Version} — {r.Name} ({(r.IsCrossVersion == true ? $"Cross {r.FromVersion} → {r.Version}" : "Full")})"); + Console.WriteLine($" {r.Version} — {r.Name} ({(r.PackageType == 1 ? "Chain" : "Full")})"); return Results.Ok(HttpResponseDTO>.Success(results, $"Found {results.Count} update(s).")); @@ -162,7 +161,7 @@ if (versionStore.Count > 0) { foreach (var v in versionStore.OrderBy(v => new Version(v.Version!))) - Console.WriteLine($" {v.Version,-12} AppType={v.AppType} {(v.IsCrossVersion == true ? $"Cross {v.FromVersion} → {v.Version}" : "Full")} {v.PacketName}"); + Console.WriteLine($" {v.Version,-12} AppType={v.AppType} {(v.PackageType == 1 ? "Chain" : "Full")} {v.PacketName}"); } else { @@ -232,6 +231,8 @@ record VersionEntry public string? Format { get; set; } public long? Size { get; set; } public bool? IsFreeze { get; set; } - public bool? IsCrossVersion { get; set; } - public string? FromVersion { get; set; } + /// + /// 包类型: 0=Unspecified, 1=Chain(差分), 2=Full(完整包), 3=Driver + /// + public int PackageType { get; set; } } diff --git a/src/Server/create_versions_json.ps1 b/src/Server/create_versions_json.ps1 index f1755b2..ec13ba8 100644 --- a/src/Server/create_versions_json.ps1 +++ b/src/Server/create_versions_json.ps1 @@ -48,12 +48,10 @@ foreach ($p in $packages) { Format = ".zip" Size = $size IsFreeze = $false - IsCrossVersion = $p.IsCross - FromVersion = $fromVer - ToVersion = $toVer + PackageType = $(if ($p.IsCross) { 1 } else { 2 }) } $entries += $entry - $crossLabel = if ($p.IsCross) { "Cross $fromVer -> $toVer" } else { "Full" } + $crossLabel = if ($p.IsCross) { "Chain" } else { "Full" } Write-Host " AppType=$($p.AppType) v$($p.Ver) [$crossLabel] $($file.Name) ($size bytes)" } } diff --git a/src/Server/finalize_packages.ps1 b/src/Server/finalize_packages.ps1 index d54d32b..ceb5ad1 100644 --- a/src/Server/finalize_packages.ps1 +++ b/src/Server/finalize_packages.ps1 @@ -83,12 +83,10 @@ foreach ($f in $zipFiles) { Format = ".zip" Size = $size IsFreeze = $false - IsCrossVersion = $isCross - FromVersion = $fromVer - ToVersion = $toVer + PackageType = $(if ($isCross) { 1 } else { 2 }) } $entries += $entry - $crossLabel = if ($isCross) { "Cross $fromVer -> $toVer" } else { "Full" } + $crossLabel = if ($isCross) { "Chain" } else { "Full" } Write-Host " AppType=$appType v$version [$crossLabel] $($f.Name) ($size bytes)" } diff --git a/src/Server/generate_packages.ps1 b/src/Server/generate_packages.ps1 index 037854e..6ee2662 100644 --- a/src/Server/generate_packages.ps1 +++ b/src/Server/generate_packages.ps1 @@ -49,8 +49,7 @@ function New-VersionEntry { [string]$PacketName, [string]$Version, [int]$AppType, [int]$Platform = 1, [string]$ProductId, [bool]$IsForcibly = $false, [string]$Format = ".zip", - [bool]$IsCrossVersion = $false, - [string]$FromVersion = $null, [string]$ToVersion = $null, + [int]$PackageType = 2, # 1=Chain(差分), 2=Full(完整包) [bool]$IsFreeze = $false ) $zipPath = Join-Path $packagesDir "$PacketName.zip" @@ -70,9 +69,7 @@ function New-VersionEntry { Format = $Format Size = $size IsFreeze = $IsFreeze - IsCrossVersion = $(if ($IsCrossVersion) { $true } else { $false }) - FromVersion = $FromVersion - ToVersion = $ToVersion + PackageType = $PackageType } } @@ -80,8 +77,7 @@ function Add-ZipPackage { param( [string]$SourceDir, [string]$PacketName, [string]$Version, [int]$AppType, - [bool]$IsCrossVersion = $false, - [string]$FromVersion = $null, [string]$ToVersion = $null + [int]$PackageType = 2 # 1=Chain(差分), 2=Full(完整包) ) $zipPath = Join-Path $packagesDir "$PacketName.zip" Write-Host " Creating: $PacketName.zip" -ForegroundColor Yellow @@ -101,7 +97,7 @@ function Add-ZipPackage { $entry = New-VersionEntry -PacketName $PacketName -Version $Version ` -AppType $AppType -ProductId $productId ` - -IsCrossVersion $IsCrossVersion -FromVersion $FromVersion -ToVersion $ToVersion + -PackageType $PackageType $script:allVersions += $entry } @@ -119,7 +115,7 @@ if ((Test-Path $clientV2) -and (Test-Path $clientV1)) { # Full (VersionChain) package $pkgName = "packet_${timestamp}_full_client_2.0.0.0" Add-ZipPackage -SourceDir $clientV2 -PacketName $pkgName ` - -Version "2.0.0.0" -AppType 1 -IsCrossVersion $false + -Version "2.0.0.0" -AppType 1 -PackageType 2 if (-not $FullOnly) { # Differential (CrossVersion) package via PatchGenerator @@ -156,9 +152,7 @@ if ((Test-Path $clientV2) -and (Test-Path $clientV1)) { Format = $pe.Format Size = $pe.Size IsFreeze = $pe.IsFreeze - IsCrossVersion = $(if ($pe.IsCrossVersion) { $true } else { $false }) - FromVersion = $pe.FromVersion - ToVersion = $pe.ToVersion + PackageType = $(if ($pe.PackageType) { $pe.PackageType } elseif ($pe.IsCrossVersion) { 1 } else { 2 }) } } } @@ -186,7 +180,7 @@ if ((Test-Path $upgradeV2) -and (Test-Path $upgradeV1)) { # Full (VersionChain) package $pkgName = "packet_${timestamp}_full_upgrade_2.0.0.0" Add-ZipPackage -SourceDir $upgradeV2 -PacketName $pkgName ` - -Version "2.0.0.0" -AppType 2 -IsCrossVersion $false + -Version "2.0.0.0" -AppType 2 -PackageType 2 } else { Write-Host " [Skip] content_upgrade directories not found: $upgradeV1 or $upgradeV2" -ForegroundColor Yellow @@ -207,12 +201,12 @@ foreach ($ver in @("1.0.0.1", "1.0.0.2")) { # Client full package $pkgName = "packet_${timestamp}_full_client_$ver" Add-ZipPackage -SourceDir $srcPath -PacketName $pkgName ` - -Version $ver -AppType 1 -IsCrossVersion $false + -Version $ver -AppType 1 -PackageType 2 # Upgrade full package $pkgName = "packet_${timestamp}_full_upgrade_$ver" Add-ZipPackage -SourceDir $srcPath -PacketName $pkgName ` - -Version $ver -AppType 2 -IsCrossVersion $false + -Version $ver -AppType 2 -PackageType 2 } } @@ -233,6 +227,6 @@ Write-Host " Metadata: $versionsJsonPath" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan foreach ($v in $uniqueVersions) { - $cross = if ($v.IsCrossVersion) { "Cross $($v.FromVersion) → $($v.ToVersion)" } else { "Full" } + $cross = if ($v.PackageType -eq 1) { "Chain" } else { "Full" } Write-Host " AppType=$($v.AppType) v$($v.Version) [$cross] $($v.PacketName)" -ForegroundColor Gray } diff --git a/src/Server/wwwroot/packages/versions.json b/src/Server/wwwroot/packages/versions.json index 5d39a51..60c0028 100644 --- a/src/Server/wwwroot/packages/versions.json +++ b/src/Server/wwwroot/packages/versions.json @@ -8,12 +8,11 @@ "PacketName": "packet_20260602210052838_full_client_2.0.0.0", "Url": "http://localhost:5000/File/Download/a1f180bfc0a58429a42be59d15421b810c993274af65b23a0b049880bb415d47", "IsFreeze": false, - "IsCrossVersion": false, - "FromVersion": null, "Platform": 1, "IsForcibly": false, "Size": 1068, - "Hash": "a1f180bfc0a58429a42be59d15421b810c993274af65b23a0b049880bb415d47" + "Hash": "a1f180bfc0a58429a42be59d15421b810c993274af65b23a0b049880bb415d47", + "packageType": 2 }, { "PubTime": "2026-06-02T21:00:52.9527877+08:00", @@ -24,12 +23,11 @@ "PacketName": "packet_20260602210052838_full_upgrade_2.0.0.0", "Url": "http://localhost:5000/File/Download/0d7727be7ea8520fe60ce37ce30b1a956ee6e33b252489045b4d852134b4abc6", "IsFreeze": false, - "IsCrossVersion": false, - "FromVersion": null, "Platform": 1, "IsForcibly": false, "Size": 227, - "Hash": "0d7727be7ea8520fe60ce37ce30b1a956ee6e33b252489045b4d852134b4abc6" + "Hash": "0d7727be7ea8520fe60ce37ce30b1a956ee6e33b252489045b4d852134b4abc6", + "packageType": 2 }, { "PubTime": "2026-06-02T21:00:52.9705223+08:00", @@ -40,12 +38,11 @@ "PacketName": "packet_20260602210052838_full_client_1.0.0.1", "Url": "http://localhost:5000/File/Download/47e693ac91230db216cf4ece7d6775e7bc73b79028eb76f847f74ad01d125ef6", "IsFreeze": false, - "IsCrossVersion": false, - "FromVersion": null, "Platform": 1, "IsForcibly": false, "Size": 140, - "Hash": "47e693ac91230db216cf4ece7d6775e7bc73b79028eb76f847f74ad01d125ef6" + "Hash": "47e693ac91230db216cf4ece7d6775e7bc73b79028eb76f847f74ad01d125ef6", + "packageType": 2 }, { "PubTime": "2026-06-02T21:00:52.9735836+08:00", @@ -56,12 +53,11 @@ "PacketName": "packet_20260602210052838_full_upgrade_1.0.0.1", "Url": "http://localhost:5000/File/Download/47e693ac91230db216cf4ece7d6775e7bc73b79028eb76f847f74ad01d125ef6", "IsFreeze": false, - "IsCrossVersion": false, - "FromVersion": null, "Platform": 1, "IsForcibly": false, "Size": 140, - "Hash": "47e693ac91230db216cf4ece7d6775e7bc73b79028eb76f847f74ad01d125ef6" + "Hash": "47e693ac91230db216cf4ece7d6775e7bc73b79028eb76f847f74ad01d125ef6", + "packageType": 2 }, { "PubTime": "2026-06-02T21:00:52.9849748+08:00", @@ -72,12 +68,11 @@ "PacketName": "packet_20260602210052838_full_client_1.0.0.2", "Url": "http://localhost:5000/File/Download/d0e74a16a230eb53cfd6d3d955582f3b1c22f838822e61a3148afcd2336ef94c", "IsFreeze": false, - "IsCrossVersion": false, - "FromVersion": null, "Platform": 1, "IsForcibly": false, "Size": 140, - "Hash": "d0e74a16a230eb53cfd6d3d955582f3b1c22f838822e61a3148afcd2336ef94c" + "Hash": "d0e74a16a230eb53cfd6d3d955582f3b1c22f838822e61a3148afcd2336ef94c", + "packageType": 2 }, { "PubTime": "2026-06-02T21:00:52.9889760+08:00", @@ -88,12 +83,11 @@ "PacketName": "packet_20260602210052838_full_upgrade_1.0.0.2", "Url": "http://localhost:5000/File/Download/d0e74a16a230eb53cfd6d3d955582f3b1c22f838822e61a3148afcd2336ef94c", "IsFreeze": false, - "IsCrossVersion": false, - "FromVersion": null, "Platform": 1, "IsForcibly": false, "Size": 140, - "Hash": "d0e74a16a230eb53cfd6d3d955582f3b1c22f838822e61a3148afcd2336ef94c" + "Hash": "d0e74a16a230eb53cfd6d3d955582f3b1c22f838822e61a3148afcd2336ef94c", + "packageType": 2 }, { "PacketName": "appupdate_2.0.0.0", @@ -107,7 +101,6 @@ "Format": ".apk", "Size": 43346849, "IsFreeze": false, - "IsCrossVersion": false, - "FromVersion": null + "packageType": 2 } -] +] \ No newline at end of file diff --git a/src/gen_packages.ps1 b/src/gen_packages.ps1 index 787ad3c..3841700 100644 --- a/src/gen_packages.ps1 +++ b/src/gen_packages.ps1 @@ -31,9 +31,7 @@ function New-Package($SourceDir, $PacketName, $Version, $AppType) { Format = ".zip" Size = $size IsFreeze = $false - IsCrossVersion = $false - FromVersion = $null - ToVersion = $null + PackageType = 2 # Full } $script:allVersions += $entry Write-Host " $PacketName ($size bytes)" -ForegroundColor Gray From b8791ab84d8cf6c53c2e325432e82f0320af844b Mon Sep 17 00:00:00 2001 From: JusterZhu Date: Sun, 21 Jun 2026 01:33:02 +0800 Subject: [PATCH 2/2] feat: add StandardUpdateSample as the minimal production-grade bootstrap example - New StandardUpdateSample (Index=1): clean, minimal GeneralUpdateBootstrap config with all essential listeners, no mock files - Shift all existing sample indices by +1 to accommodate - StandardUpdateSample is the go-to reference for first-time integrators --- src/Hub/Samples/BowlSample.cs | 2 +- src/Hub/Samples/CompleteUpdateSample.cs | 2 +- src/Hub/Samples/CompressSample.cs | 2 +- src/Hub/Samples/DifferentialSample.cs | 2 +- src/Hub/Samples/ExtensionSample.cs | 2 +- src/Hub/Samples/ImDiskQuickInstallSample.cs | 2 +- src/Hub/Samples/OssSample.cs | 2 +- src/Hub/Samples/PushSample.cs | 2 +- src/Hub/Samples/SilentUpdateSample.cs | 2 +- src/Hub/Samples/StandardUpdateSample.cs | 118 ++++++++++++++++++++ 10 files changed, 127 insertions(+), 9 deletions(-) create mode 100644 src/Hub/Samples/StandardUpdateSample.cs diff --git a/src/Hub/Samples/BowlSample.cs b/src/Hub/Samples/BowlSample.cs index d4370ea..08e4550 100644 --- a/src/Hub/Samples/BowlSample.cs +++ b/src/Hub/Samples/BowlSample.cs @@ -7,7 +7,7 @@ namespace Hub.Samples; public class BowlSample : ISample { - public int Index => 6; + public int Index => 7; public string Name => "进程守护 — 崩溃监控·Dump导出"; public bool RequiresServer => false; public string[] CleanPaths => Array.Empty(); diff --git a/src/Hub/Samples/CompleteUpdateSample.cs b/src/Hub/Samples/CompleteUpdateSample.cs index f0374ae..ae4c23c 100644 --- a/src/Hub/Samples/CompleteUpdateSample.cs +++ b/src/Hub/Samples/CompleteUpdateSample.cs @@ -7,7 +7,7 @@ namespace Hub.Samples; public class CompleteUpdateSample : ISample { - public int Index => 1; + public int Index => 2; public string Name => "完整更新 — 版本发现→下载→应用"; public bool RequiresServer => true; public string[] CleanPaths => new[] { diff --git a/src/Hub/Samples/CompressSample.cs b/src/Hub/Samples/CompressSample.cs index a76819f..cdca7a0 100644 --- a/src/Hub/Samples/CompressSample.cs +++ b/src/Hub/Samples/CompressSample.cs @@ -6,7 +6,7 @@ namespace Hub.Samples; public class CompressSample : ISample { - public int Index => 9; + public int Index => 10; public string Name => "压缩工具 — 压缩·解压·校验"; public bool RequiresServer => false; public string[] CleanPaths => new[] { Path.Combine(Path.GetTempPath(), "compress_demo") }; diff --git a/src/Hub/Samples/DifferentialSample.cs b/src/Hub/Samples/DifferentialSample.cs index 9d9fb41..7148bb4 100644 --- a/src/Hub/Samples/DifferentialSample.cs +++ b/src/Hub/Samples/DifferentialSample.cs @@ -7,7 +7,7 @@ namespace Hub.Samples; public class DifferentialSample : ISample { - public int Index => 4; + public int Index => 5; public string Name => "二进制差分 — 生成·应用·校验"; public bool RequiresServer => false; public string[] CleanPaths => new[] { diff --git a/src/Hub/Samples/ExtensionSample.cs b/src/Hub/Samples/ExtensionSample.cs index d43c729..05e5713 100644 --- a/src/Hub/Samples/ExtensionSample.cs +++ b/src/Hub/Samples/ExtensionSample.cs @@ -9,7 +9,7 @@ namespace Hub.Samples; public class ExtensionSample : ISample { - public int Index => 7; + public int Index => 8; public string Name => "插件系统 — 安装·管理·兼容性"; public bool RequiresServer => false; public string[] CleanPaths => new[] { "{BaseDir}/extensions" }; diff --git a/src/Hub/Samples/ImDiskQuickInstallSample.cs b/src/Hub/Samples/ImDiskQuickInstallSample.cs index 78edc1d..de4196c 100644 --- a/src/Hub/Samples/ImDiskQuickInstallSample.cs +++ b/src/Hub/Samples/ImDiskQuickInstallSample.cs @@ -14,7 +14,7 @@ namespace Hub.Samples; /// public class ImDiskQuickInstallSample : ISample { - public int Index => 8; + public int Index => 9; public string Name => "Driver Update"; public bool RequiresServer => false; public string[] CleanPaths => Array.Empty(); diff --git a/src/Hub/Samples/OssSample.cs b/src/Hub/Samples/OssSample.cs index 20653fd..a540327 100644 --- a/src/Hub/Samples/OssSample.cs +++ b/src/Hub/Samples/OssSample.cs @@ -7,7 +7,7 @@ namespace Hub.Samples; public class OssSample : ISample { - public int Index => 3; + public int Index => 4; public string Name => "OSS 模式 — 对象存储更新"; public bool RequiresServer => true; public string[] CleanPaths => new[] { diff --git a/src/Hub/Samples/PushSample.cs b/src/Hub/Samples/PushSample.cs index e9dbcc3..e3c4b33 100644 --- a/src/Hub/Samples/PushSample.cs +++ b/src/Hub/Samples/PushSample.cs @@ -8,7 +8,7 @@ namespace Hub.Samples; public class PushSample : ISample { - public int Index => 5; + public int Index => 6; public string Name => "SignalR 推送 — 实时消息接收"; public bool RequiresServer => false; public string[] CleanPaths => Array.Empty(); diff --git a/src/Hub/Samples/SilentUpdateSample.cs b/src/Hub/Samples/SilentUpdateSample.cs index 3f575bc..be9152d 100644 --- a/src/Hub/Samples/SilentUpdateSample.cs +++ b/src/Hub/Samples/SilentUpdateSample.cs @@ -7,7 +7,7 @@ namespace Hub.Samples; public class SilentUpdateSample : ISample { - public int Index => 2; + public int Index => 3; public string Name => "静默更新 — 后台轮询·退出前准备"; public bool RequiresServer => true; public string[] CleanPaths => new[] { diff --git a/src/Hub/Samples/StandardUpdateSample.cs b/src/Hub/Samples/StandardUpdateSample.cs new file mode 100644 index 0000000..2c57259 --- /dev/null +++ b/src/Hub/Samples/StandardUpdateSample.cs @@ -0,0 +1,118 @@ +using GeneralUpdate.Core; +using GeneralUpdate.Core.Configuration; +using GeneralUpdate.Core.Download; +using GeneralUpdate.Core.Event; + +namespace Hub.Samples; + +/// +/// 标准更新流程 — 最简化的生产级集成方式。 +/// 展示从配置到执行的完整链路,不包含模拟数据或文件操作。 +/// +/// +/// 相比 ,本示例省略了模拟应用的创建和 +/// 更新后的文件遍历比对,聚焦于 GeneralUpdateBootstrap 的核心配置模式。 +/// +public class StandardUpdateSample : ISample +{ + public int Index => 1; + public string Name => "标准更新流程 — Bootstrap·配置·执行"; + public bool RequiresServer => true; + public string[] CleanPaths => Array.Empty(); + + public async Task RunAsync(AppConfig config, CancellationToken ct) + { + Console.WriteLine(); + Console.WriteLine("══ 标准更新流程 (v10.5.0-beta.6) ══"); + + // ════════════════════════════════════════════════════════════════ + // 1. 组装请求参数 + // ════════════════════════════════════════════════════════════════ + var request = new UpdateRequest + { + UpdateUrl = $"{config.ServerUrl}/Upgrade/Verification", + ReportUrl = $"{config.ServerUrl}/Upgrade/Report", + AppSecretKey = config.AppSecretKey, + InstallPath = AppDomain.CurrentDomain.BaseDirectory, + UpdatePath = AppDomain.CurrentDomain.BaseDirectory, + ClientVersion = config.ClientVersion, + MainAppName = config.MainAppName, + UpdateAppName = config.UpgradeAppName, + ProductId = config.ProductId + }; + + Console.WriteLine(); + Console.WriteLine("══ 配置 ══"); + Console.WriteLine($" Server: {request.UpdateUrl}"); + Console.WriteLine($" Version: {request.ClientVersion}"); + Console.WriteLine($" AppKey: {request.AppSecretKey?[..Math.Min(8, request.AppSecretKey?.Length ?? 0)]}..."); + + // ════════════════════════════════════════════════════════════════ + // 2. 构建 Bootstrap + // ════════════════════════════════════════════════════════════════ + Console.WriteLine(); + Console.WriteLine("══ 初始化 GeneralUpdateBootstrap ══"); + + var bootstrap = new GeneralUpdateBootstrap() + + // — 必要:请求配置 — + .SetConfig(request) + + // — 必要:指定应用类型(Client=主应用, Upgrade=升级器) — + .SetOption(Option.AppType, AppType.Client) + + // — 必要:版本发现事件 — + .AddListenerUpdateInfo((_, e) => + { + var count = e.Info?.Body?.Count ?? 0; + // 服务端可能在多个版本中返回 full + chain 包供客户端选择 + // 客户端会按 PackageType 自动排序:full 优先 + Console.WriteLine($" [版本发现] 找到 {count} 个可用更新"); + }) + + // — 可选:下载统计(多文件并行) — + .AddListenerMultiDownloadStatistics((_, e) => + { + Console.Write($"\r [下载] {e.BytesReceived}/{e.TotalBytesToReceive} bytes ({e.ProgressPercentage:F0}%) {e.Speed}"); + }) + + // — 可选:下载完成 — + .AddListenerMultiDownloadCompleted((_, e) => + { + Console.WriteLine(); + Console.WriteLine($" [下载完成] {(e.IsCompleted ? "✓" : "✗ 存在失败")}"); + }) + + // — 可选:单文件进度 — + .AddListenerProgress((_, e) => + { + if (e.Progress is { } p) + Console.WriteLine($" {p.AssetName}: {p.Percentage:F0}% ({p.Status})"); + }) + + // — 可选:异常通知 — + .AddListenerException((_, e) => + { + Console.WriteLine($" [异常] {e.Message}"); + }); + + // ════════════════════════════════════════════════════════════════ + // 3. 执行更新 + // ════════════════════════════════════════════════════════════════ + Console.WriteLine(); + Console.WriteLine("══ 启动更新 (LaunchAsync) ══"); + Console.WriteLine(" 流程: 版本发现 → 下载 → 解压 → 备份旧版本 → 应用新版本"); + + try + { + await bootstrap.LaunchAsync(); + Console.WriteLine(); + Console.WriteLine("══ 更新完成 ✓ ══"); + } + catch (Exception ex) + { + Console.WriteLine(); + Console.WriteLine($"══ 更新失败: {ex.Message} ══"); + } + } +}