A:全局Common的公共定义

This commit is contained in:
jimmy committed 2025-05-26 22:56:51 +08:00
1 parent 83b7ca5971
commit eaaedd48cf
18 files changed
+366 -1

No files matched your search

+33
View File
@@ -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; }
// 可扩展其他读取配置的方法
}
+5
View File
@@ -0,0 +1,5 @@
namespace Common;
public class Class1
{
}
+19
View File
@@ -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>
+34
View File
@@ -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;
}
}
}
+29
View File
@@ -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, "健康检查异常");
}
}
}
+10
View File
@@ -0,0 +1,10 @@
| 类别 | 示例 |
| -------- | --------------------------------------------- |
| ✅ 配置封装 | `AppConfig`, `ConfigReader` |
| ✅ 通用工具类 | 字符串处理、加密、哈希、路径处理 |
| ✅ 基础模型类 | `Result<T>`, `PagedList<T>`, `ErrorInfo` |
| ✅ 日志适配器 | 封装 `Serilog`、`NLog` 的通用调用接口 |
| ✅ 通用扩展方法 | `string.ToSafeInt()`, `DateTime.ToUnixTime()` |
| ✅ 接口定义 | 如 `INetworkMonitor`, `ILogger`(便于依赖注入) |
| ✅ 网络协议模型 | 公共 API 的数据结构定义(DTO/VO) |
| ✅ 常量/枚举 | 错误码、状态码、常量定义 |
+23
View File
@@ -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;
}
}
+1
View File
@@ -13,6 +13,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Common\Common.csproj" />
<ProjectReference Include="..\WinDivertNet\WinDivertNet.csproj" />
</ItemGroup>
+2
View File
@@ -1,3 +1,5 @@
using Common;
namespace Infrastructure.utils;
using Infrastructure.GlobalCaches;
+6
View File
@@ -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
View File
@@ -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>
+135
View File
@@ -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 实现
}
}
+12
View File
@@ -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>
+5
View File
@@ -0,0 +1,5 @@
{
"AppSettings": {
"MaxConnection": 99
}
}
+5
View File
@@ -0,0 +1,5 @@
{
"AppSettings": {
"MaxConnection": 10
}
}
+6
View File
@@ -0,0 +1,6 @@
{
"AppSettings": {
"AppName": "MyApp",
"MaxConnection": 10
}
}