From 39a89f6361a83fbd9fce14b3bc66d806745f0e75 Mon Sep 17 00:00:00 2001
From: uye <99072975+ABA2396@users.noreply.github.com>
Date: Wed, 12 Nov 2025 14:15:15 +0800
Subject: [PATCH] =?UTF-8?q?rft:=20=E6=97=A5=E5=BF=97=E6=B1=87=E6=8A=A5?=
=?UTF-8?q?=E6=89=93=E5=8C=85=20debug=20=E6=88=AA=E5=9B=BE=EF=BC=8C?=
=?UTF-8?q?=E6=94=AF=E6=8C=81=E5=88=86=E5=8D=B7=EF=BC=8C=E4=BF=AE=E6=94=B9?=
=?UTF-8?q?=E5=AD=98=E5=82=A8=E8=B7=AF=E5=BE=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/MaaWpfGui/Helper/PathsHelper.cs | 10 +
.../Settings/IssueReportUserControlModel.cs | 199 ++++++++++++------
2 files changed, 148 insertions(+), 61 deletions(-)
diff --git a/src/MaaWpfGui/Helper/PathsHelper.cs b/src/MaaWpfGui/Helper/PathsHelper.cs
index 6f700e80ac..68a98fc6cd 100644
--- a/src/MaaWpfGui/Helper/PathsHelper.cs
+++ b/src/MaaWpfGui/Helper/PathsHelper.cs
@@ -76,4 +76,14 @@ public static class PathsHelper
/// named "data". This property returns the resolved path, creating the directory if it does not exist is the
/// responsibility of the caller.
public static string DataDir => _data ??= Path.Combine(BaseDir, "data");
+
+ private static string? _reports;
+
+ ///
+ /// Gets the full path to the application's reports directory.
+ ///
+ /// The reports directory is located within the application's base directory under a folder
+ /// named "_reports". This property returns the resolved path, creating the directory if it does not exist is the
+ /// responsibility of the caller.
+ public static string ReportsDir => _reports ??= Path.Combine(BaseDir, "reports");
}
diff --git a/src/MaaWpfGui/ViewModels/UserControl/Settings/IssueReportUserControlModel.cs b/src/MaaWpfGui/ViewModels/UserControl/Settings/IssueReportUserControlModel.cs
index 9f9fc4f012..afd916d5b6 100644
--- a/src/MaaWpfGui/ViewModels/UserControl/Settings/IssueReportUserControlModel.cs
+++ b/src/MaaWpfGui/ViewModels/UserControl/Settings/IssueReportUserControlModel.cs
@@ -13,16 +13,16 @@
#nullable enable
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
+using System.Linq;
using HandyControl.Controls;
using HandyControl.Data;
using JetBrains.Annotations;
-using MaaWpfGui.Configuration.Factory;
using MaaWpfGui.Constants;
using MaaWpfGui.Helper;
-using MaaWpfGui.Main;
using Serilog;
using Stylet;
@@ -38,110 +38,187 @@ public class IssueReportUserControlModel : PropertyChangedBase
Instance = new();
}
- private static readonly string[] _payloadFileNames = [
- Bootstrapper.UiLogFile,
- Bootstrapper.UiLogBakFile,
- Bootstrapper.CoreLogFile,
- Bootstrapper.CoreLogBakFile,
- ConfigurationHelper.ConfigFile,
- ConfigFactory.ConfigFile];
-
public static IssueReportUserControlModel Instance { get; }
public void OpenDebugFolder()
{
try
{
- if (!Directory.Exists(PathsHelper.DebugDir))
+ if (!Directory.Exists(PathsHelper.ReportsDir))
{
- Directory.CreateDirectory(PathsHelper.DebugDir);
+ Directory.CreateDirectory(PathsHelper.ReportsDir);
}
- Process.Start("explorer.exe", PathsHelper.DebugDir);
+ Process.Start("explorer.exe", PathsHelper.ReportsDir);
}
catch (Exception ex)
{
- ToastNotification.ShowDirect($"Failed to open debug folder\n{ex.Message}");
- Log.Error(ex, "Failed to open debug folder");
+ ToastNotification.ShowDirect($"Failed to open reports folder\n{ex.Message}");
+ Log.Error(ex, "Failed to open reports folder");
}
}
+ ///
+ /// 生成日志压缩包
+ ///
public void GenerateSupportPayload()
{
try
{
- var reportFileName = $"report_{DateTimeOffset.Now:MM-dd_HH-mm-ss}.zip";
- string zipPath = Path.Combine(PathsHelper.DebugDir, reportFileName);
- string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
- string debugTempPath = Path.Combine(tempPath, "debug");
- string resourceTempPath = Path.Combine(tempPath, "resource");
- if (File.Exists(zipPath))
- {
- File.Delete(zipPath);
- }
-
+ const int PartSize = 10 * 1024 * 1024; // 10 MB
+ string reportNameBase = $"report_{DateTimeOffset.Now:MM-dd_HH-mm-ss}";
+ string tempPath = Path.Combine(Path.GetTempPath(), $"maa-report-{Guid.NewGuid()}");
Directory.CreateDirectory(tempPath);
- Directory.CreateDirectory(debugTempPath);
- Directory.CreateDirectory(resourceTempPath);
- // 复制 _payloadFileNames 中的文件到 tempPath/debug
- foreach (var file in _payloadFileNames)
+ if (!Directory.Exists(PathsHelper.ReportsDir))
{
- if (!File.Exists(file))
- {
- continue;
- }
-
- string relativePath = Path.GetRelativePath(PathsHelper.BaseDir, file);
- string dest = Path.Combine(tempPath, relativePath);
- Directory.CreateDirectory(Path.GetDirectoryName(dest)!);
- File.Copy(file, dest, overwrite: true);
+ Directory.CreateDirectory(PathsHelper.ReportsDir);
}
- // 遍历 resource 文件夹下带 _custom 后缀的文件,复制到 tempPath/resource
- const string ResourceDir = "resource";
- if (Directory.Exists(ResourceDir))
+ // 复制文件
+ CopyDirectoryIfExists(PathsHelper.DebugDir, Path.Combine(tempPath, "debug"),
+ f => { return !Path.GetFileName(f).StartsWith("report", StringComparison.OrdinalIgnoreCase); });
+ CopyDirectoryIfExists(PathsHelper.ResourceDir, Path.Combine(tempPath, "resource"),
+ f => { return Path.GetFileName(f).Contains("_custom.", StringComparison.OrdinalIgnoreCase); });
+ CopyDirectoryIfExists(PathsHelper.ConfigDir, Path.Combine(tempPath, "config"));
+ CopyDirectoryIfExists(PathsHelper.CacheDir, Path.Combine(tempPath, "cache"));
+
+ string partsFolder = Path.Combine(PathsHelper.ReportsDir, $"{reportNameBase}_parts");
+ if (!Directory.Exists(partsFolder))
{
- foreach (var file in Directory.EnumerateFiles(ResourceDir, "*_custom.*", SearchOption.AllDirectories))
+ Directory.CreateDirectory(partsFolder);
+ }
+
+ // ====== part01:config + resource + cache + debug 根目录文件 ======
+ List part01Files = [];
+
+ string[] categories = ["config", "resource", "cache"];
+ foreach (string category in categories)
+ {
+ string categoryPath = Path.Combine(tempPath, category);
+ if (Directory.Exists(categoryPath))
{
- string relativePath = Path.GetRelativePath(ResourceDir, file);
- string dest = Path.Combine(resourceTempPath, relativePath);
- Directory.CreateDirectory(Path.GetDirectoryName(dest)!);
- File.Copy(file, dest, overwrite: true);
+ part01Files.AddRange(Directory.EnumerateFiles(categoryPath, "*", SearchOption.AllDirectories));
}
}
- // 遍历 cache 文件夹下的文件,复制到 tempPath/cache
- string cacheResourceDir = PathsHelper.CacheDir;
- if (Directory.Exists(cacheResourceDir))
+ string debugPath = Path.Combine(tempPath, "debug");
+ if (Directory.Exists(debugPath))
{
- foreach (var file in Directory.EnumerateFiles(cacheResourceDir, "*", SearchOption.AllDirectories))
+ // 只取 debug 根目录文件
+ var debugRootFiles = Directory.EnumerateFiles(debugPath, "*", SearchOption.TopDirectoryOnly).ToList();
+ part01Files.AddRange(debugRootFiles);
+ }
+
+ if (part01Files.Count > 0)
+ {
+ string part01Path = Path.Combine(partsFolder, "part01.zip");
+ using var fs = new FileStream(part01Path, FileMode.Create);
+ using var archive = new ZipArchive(fs, ZipArchiveMode.Create);
+ foreach (var file in part01Files)
{
- string relativePath = Path.GetRelativePath(cacheResourceDir, file);
- string dest = Path.Combine(tempPath, "cache", relativePath);
- Directory.CreateDirectory(Path.GetDirectoryName(dest)!);
- File.Copy(file, dest, overwrite: true);
+ string entryName = Path.GetRelativePath(tempPath, file).Replace("\\", "/");
+ var entry = archive.CreateEntry(entryName, CompressionLevel.SmallestSize);
+
+ using var entryStream = entry.Open();
+ using var fileStream = File.OpenRead(file);
+ fileStream.CopyTo(entryStream);
}
}
- using (FileStream zipToOpen = new FileStream(zipPath, FileMode.Create))
+ // ====== part02+:debug 子目录文件按 PartSize 分卷 ======
+ var debugSubFiles = Directory.EnumerateFiles(debugPath, "*", SearchOption.AllDirectories)
+ .Where(f => Path.GetDirectoryName(f) != debugPath).ToList();
+
+ int partNumber = 2;
+ while (debugSubFiles.Count > 0)
{
- using var archive = new ZipArchive(zipToOpen, ZipArchiveMode.Create);
- foreach (var file in Directory.EnumerateFiles(tempPath, "*", SearchOption.AllDirectories))
+ string partFileName = $"part{partNumber:D2}.zip";
+ string partPath = Path.Combine(partsFolder, partFileName);
+
+ using (var fs = new FileStream(partPath, FileMode.Create))
{
- string entryName = Path.GetRelativePath(tempPath, file);
- archive.CreateEntryFromFile(file, entryName, CompressionLevel.SmallestSize);
+ using var archive = new ZipArchive(fs, ZipArchiveMode.Create);
+ long currentSize = 0;
+ List processedFiles = [];
+
+ foreach (var file in debugSubFiles.ToList())
+ {
+ var fileInfo = new FileInfo(file);
+
+ if (currentSize + fileInfo.Length > PartSize && currentSize > 0)
+ {
+ break;
+ }
+
+ string entryName = Path.GetRelativePath(tempPath, file).Replace("\\", "/");
+ var entry = archive.CreateEntry(entryName, CompressionLevel.SmallestSize);
+
+ using (var entryStream = entry.Open())
+ {
+ using var fileStream = File.OpenRead(file);
+ fileStream.CopyTo(entryStream);
+ }
+
+ currentSize += fileInfo.Length;
+ processedFiles.Add(file);
+ }
+
+ debugSubFiles.RemoveAll(f => processedFiles.Contains(f));
}
+
+ partNumber++;
}
+ // ====== 生成完整压缩包 ======
+ string fullZipPath = Path.Combine(PathsHelper.ReportsDir, $"{reportNameBase}.zip");
+ ZipFile.CreateFromDirectory(tempPath, fullZipPath, CompressionLevel.SmallestSize, includeBaseDirectory: false);
+
+ // 清理临时目录
Directory.Delete(tempPath, recursive: true);
- ShowGrowl($"{LocalizationHelper.GetString("GenerateSupportPayloadSuccessful")}\n{reportFileName}");
+
+ ShowGrowl($"{LocalizationHelper.GetString("GenerateSupportPayloadSuccessful")}\n{fullZipPath}");
OpenDebugFolder();
}
catch (Exception ex)
{
ShowGrowl($"{LocalizationHelper.GetString("GenerateSupportPayloadException")}\n{ex.Message}");
- Log.Error(ex, "Failed to create log zip");
+ Log.Error(ex, "Failed to create support payload");
+ }
+ }
+
+ ///
+ /// 从 sourceDir 复制文件到 targetDir,支持过滤。
+ ///
+ private static void CopyDirectoryIfExists(string? sourceDir, string targetDir, Func? filter = null)
+ {
+ if (string.IsNullOrWhiteSpace(sourceDir) || !Directory.Exists(sourceDir))
+ {
+ return;
+ }
+
+ foreach (var file in Directory.EnumerateFiles(sourceDir, "*", SearchOption.AllDirectories))
+ {
+ if (filter != null && !filter(file))
+ {
+ continue;
+ }
+
+ string relative = Path.GetRelativePath(sourceDir, file);
+ string dest = Path.Combine(targetDir, relative);
+ Directory.CreateDirectory(Path.GetDirectoryName(dest)!);
+ try
+ {
+ File.Copy(file, dest, true);
+ }
+ catch (IOException)
+ {
+ // 某些文件可能被占用,忽略复制失败
+ }
+ catch (UnauthorizedAccessException)
+ {
+ // 也忽略权限问题
+ }
}
}