A:全局Common的公共定义
This commit is contained in:
1 parent
83b7ca5971
commit
eaaedd48cf
18 files changed
+366
-1
No files matched your search
@@ -0,0 +1,33 @@
|
||||
using Common.Configuration;
|
||||
|
||||
namespace Common;
|
||||
|
||||
public sealed class AppConfig
|
||||
{
|
||||
// 使用 Lazy<T> 实现线程安全的懒加载单例
|
||||
private static readonly Lazy<AppConfig> _instance = new Lazy<AppConfig>(() => new AppConfig());
|
||||
|
||||
// 提供全局访问点
|
||||
public static AppConfig Instance => _instance.Value;
|
||||
|
||||
// 私有构造函数,防止外部实例化
|
||||
private AppConfig()
|
||||
{
|
||||
var model = ConfigReader.LoadConfig();
|
||||
AppName = model.AppName ?? "MyApp";
|
||||
Version = model.Version ?? "1.0.0";
|
||||
MaxConnection = model.MaxConnection > 0 ? model.MaxConnection : 10;
|
||||
|
||||
// 初始化配置(可以从文件或环境变量读取)
|
||||
AppName = "MyApp";
|
||||
Version = "1.0.0";
|
||||
MaxConnection = 10;
|
||||
}
|
||||
|
||||
// 配置项:只读属性
|
||||
public string AppName { get; }
|
||||
public string Version { get; }
|
||||
public int MaxConnection { get; }
|
||||
|
||||
// 可扩展其他读取配置的方法
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Common;
|
||||
|
||||
public class Class1
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.0-preview.4.25258.110" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.0-preview.4.25258.110" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.1-dev-02317" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Logger\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,34 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Common.Configuration;
|
||||
|
||||
public static class ConfigLoader
|
||||
{
|
||||
private static readonly object _lock = new();
|
||||
private static AppConfig _config;
|
||||
|
||||
public static AppConfig Config
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_config == null)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_config == null)
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(AppContext.BaseDirectory)
|
||||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
|
||||
|
||||
IConfiguration configuration = builder.Build();
|
||||
|
||||
// _config = new AppConfig();
|
||||
configuration.GetSection("AppSettings").Bind(_config);
|
||||
}
|
||||
}
|
||||
}
|
||||
return _config;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Common.Configuration;
|
||||
|
||||
public static class ConfigReader
|
||||
{
|
||||
public static AppConfigModel LoadConfig()
|
||||
{
|
||||
var env = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Production";
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(AppContext.BaseDirectory)
|
||||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env}.json", optional: true, reloadOnChange: true);
|
||||
|
||||
IConfiguration config = builder.Build();
|
||||
|
||||
var model = new AppConfigModel();
|
||||
config.GetSection("AppSettings").Bind(model);
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
// 可单独定义模型类用于绑定
|
||||
public class AppConfigModel
|
||||
{
|
||||
public string AppName { get; set; }
|
||||
public string Version { get; set; }
|
||||
public int MaxConnection { get; set; }
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Infrastructure;
|
||||
namespace Common;
|
||||
|
||||
public class ExceptionExpand : Exception
|
||||
{
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Common.Monitors;
|
||||
|
||||
public class ApplicationHealthMonitor
|
||||
{
|
||||
private readonly Timer _healthCheckTimer;
|
||||
|
||||
public ApplicationHealthMonitor()
|
||||
{
|
||||
_healthCheckTimer = new Timer(CheckApplicationHealth, null,
|
||||
TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(5));
|
||||
}
|
||||
|
||||
private void CheckApplicationHealth(object state)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查内存使用
|
||||
var memoryUsage = GC.GetTotalMemory(false);
|
||||
if (memoryUsage > 500 * 1024 * 1024) // 500MB
|
||||
{
|
||||
// Logger.Warn($"内存使用过高: {memoryUsage / 1024 / 1024}MB");
|
||||
GC.Collect(); // 强制垃圾回收
|
||||
}
|
||||
|
||||
// 检查线程数量
|
||||
var threadCount = Process.GetCurrentProcess().Threads.Count;
|
||||
if (threadCount > 100)
|
||||
{
|
||||
// Logger.Warn($"线程数量过多: {threadCount}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// LogException(ex, "健康检查异常");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
| 类别 | 示例 |
|
||||
| -------- | --------------------------------------------- |
|
||||
| ✅ 配置封装 | `AppConfig`, `ConfigReader` |
|
||||
| ✅ 通用工具类 | 字符串处理、加密、哈希、路径处理 |
|
||||
| ✅ 基础模型类 | `Result<T>`, `PagedList<T>`, `ErrorInfo` |
|
||||
| ✅ 日志适配器 | 封装 `Serilog`、`NLog` 的通用调用接口 |
|
||||
| ✅ 通用扩展方法 | `string.ToSafeInt()`, `DateTime.ToUnixTime()` |
|
||||
| ✅ 接口定义 | 如 `INetworkMonitor`, `ILogger`(便于依赖注入) |
|
||||
| ✅ 网络协议模型 | 公共 API 的数据结构定义(DTO/VO) |
|
||||
| ✅ 常量/枚举 | 错误码、状态码、常量定义 |
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace Application.Utils;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static class SysHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取可用端口
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static int GetAvailablePort()
|
||||
{
|
||||
TcpListener listener = new TcpListener(IPAddress.Loopback, 0); // 端口号为0表示由系统自动分配
|
||||
listener.Start();
|
||||
int port = ((IPEndPoint)listener.LocalEndpoint).Port;
|
||||
listener.Stop();
|
||||
return port;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Common\Common.csproj" />
|
||||
<ProjectReference Include="..\WinDivertNet\WinDivertNet.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Common;
|
||||
|
||||
namespace Infrastructure.utils;
|
||||
|
||||
using Infrastructure.GlobalCaches;
|
||||
|
||||
@@ -10,6 +10,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Application", "Application\
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinDivertNet", "WinDivertNet\WinDivertNet.csproj", "{CE7B8A5A-BD55-4945-A844-571DEDB08942}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "Common\Common.csproj", "{F16BB136-23AD-43F6-8D95-4C0D88FD8FBA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -36,5 +38,9 @@ Global
|
||||
{CE7B8A5A-BD55-4945-A844-571DEDB08942}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CE7B8A5A-BD55-4945-A844-571DEDB08942}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CE7B8A5A-BD55-4945-A844-571DEDB08942}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F16BB136-23AD-43F6-8D95-4C0D88FD8FBA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F16BB136-23AD-43F6-8D95-4C0D88FD8FBA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F16BB136-23AD-43F6-8D95-4C0D88FD8FBA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F16BB136-23AD-43F6-8D95-4C0D88FD8FBA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,6 +1,7 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFileVersionInfo_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7fad91e527c1434bb313ddbc78144605b8a0_003F3f_003F911bc64d_003FFileVersionInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFile_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F211e6f3b24fa438a92f1815153647ce2c8f908_003F71_003F40854420_003FFile_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIWebSocketConnection_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa7d96aff8e344c1d9aa8042f229d493cae00_003Fc1_003Fb154f2eb_003FIWebSocketConnection_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AKernelTraceEventParser_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F25b12134b1124ffbab73641db2e54e5b310830_003F24_003Fd19d02f2_003FKernelTraceEventParser_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANetworkInterface_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F9c72a65889eb40ae9d8e0f130d1572e0268a0_003F34_003F49f5e6ff_003FNetworkInterface_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AProcess_002Ecs_002Fl_003AC_0021_003FUsers_003Fcharlatans_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F4a26a752ec6249c4a090905d31c0cdfa528a0_003F8f_003F7037e590_003FProcess_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Net.Http;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using Application.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Shell;
|
||||
|
||||
@@ -9,4 +13,135 @@ namespace Shell;
|
||||
/// </summary>
|
||||
public partial class App : System.Windows.Application
|
||||
{
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
// 注册全局异常处理
|
||||
this.DispatcherUnhandledException += App_DispatcherUnhandledException;
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
|
||||
|
||||
base.OnStartup(e);
|
||||
|
||||
// ✅ 启动你的服务(如 WebSocket、HTTP、端口监听等)
|
||||
StartMyServer();
|
||||
|
||||
// 启动主窗口
|
||||
MainWindow mainWindow = new MainWindow();
|
||||
mainWindow.Show();
|
||||
}
|
||||
|
||||
private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||||
{
|
||||
LogException(e.Exception, "UI线程异常");
|
||||
|
||||
// 根据异常类型决定是否继续运行
|
||||
if (CanRecover(e.Exception))
|
||||
{
|
||||
e.Handled = true; // 标记为已处理,防止应用崩溃
|
||||
// ShowUserFriendlyMessage("操作失败,请重试");
|
||||
}
|
||||
else
|
||||
{
|
||||
// ShowCriticalErrorDialog(e.Exception);
|
||||
// 让应用正常关闭而不是崩溃
|
||||
}
|
||||
}
|
||||
private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
|
||||
{
|
||||
foreach (var ex in e.Exception.InnerExceptions)
|
||||
{
|
||||
LogException(ex, "Task未观察异常");
|
||||
}
|
||||
|
||||
e.SetObserved(); // 标记异常已被观察,防止应用终止
|
||||
}
|
||||
|
||||
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
var exception = e.ExceptionObject as Exception;
|
||||
LogException(exception, "非UI线程异常");
|
||||
|
||||
if (e.IsTerminating)
|
||||
{
|
||||
// 应用即将终止,进行紧急清理
|
||||
// EmergencyCleanup();
|
||||
// LogCritical("应用程序即将因未处理异常而终止");
|
||||
}
|
||||
}
|
||||
private bool CanRecover(Exception exception)
|
||||
{
|
||||
return exception switch
|
||||
{
|
||||
ArgumentException => true,
|
||||
InvalidOperationException => true,
|
||||
HttpRequestException => true,
|
||||
TimeoutException => true,
|
||||
OutOfMemoryException => false,
|
||||
StackOverflowException => false,
|
||||
AccessViolationException => false,
|
||||
_ => true
|
||||
};
|
||||
}
|
||||
|
||||
private void LogException(Exception exception, string context)
|
||||
{
|
||||
// var logLevel = GetLogLevel(exception);
|
||||
// var logMessage = $"[{context}] {exception.GetType().Name}: {exception.Message}\n{exception.StackTrace}";
|
||||
//
|
||||
// // 使用日志框架记录
|
||||
// Logger<>.Log(logLevel, logMessage);
|
||||
//
|
||||
// // 严重异常时发送邮件或其他通知
|
||||
// if (logLevel == LogLevel.Critical)
|
||||
// {
|
||||
// NotifyDevelopers(exception, context);
|
||||
// }
|
||||
}
|
||||
// 对于async/await操作,确保正确处理异常
|
||||
public async Task<T> SafeExecuteAsync<T>(Func<Task<T>> operation, T defaultValue = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await operation();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogException(ex, "异步操作异常");
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
// 对于Fire-and-Forget的Task,确保异常被捕获
|
||||
public void SafeFireAndForget(Func<Task> operation)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await operation();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogException(ex, "后台任务异常");
|
||||
}
|
||||
});
|
||||
}
|
||||
private void StartMyServer()
|
||||
{
|
||||
int port = SysHelper.GetAvailablePort();
|
||||
Console.WriteLine($"服务启动在端口: {port}");
|
||||
|
||||
// 例如开启线程或任务监听 WS:
|
||||
Task.Run(() =>
|
||||
{
|
||||
StartWebSocketServer(port);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void StartWebSocketServer(int port)
|
||||
{
|
||||
// 示例服务逻辑,如上文 WebSocket 实现
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,4 +38,16 @@
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="appsettings.Development.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="appsettings.Production.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"AppSettings": {
|
||||
"MaxConnection": 99
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"AppSettings": {
|
||||
"MaxConnection": 10
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"AppSettings": {
|
||||
"AppName": "MyApp",
|
||||
"MaxConnection": 10
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user