diff --git a/CoreAgent.API/Controllers/BaseApiController.cs b/CoreAgent.API/Controllers/BaseApiController.cs index d520a51..d5a5bbd 100644 --- a/CoreAgent.API/Controllers/BaseApiController.cs +++ b/CoreAgent.API/Controllers/BaseApiController.cs @@ -1,4 +1,4 @@ -using CoreAgent.Domain.Models; +using CoreAgent.Domain.Models.Common; using MediatR; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; @@ -26,15 +26,11 @@ namespace CoreAgent.API.Controllers { _logger.LogInformation("Handling request of type {RequestType}", typeof(TRequest).Name); var response = await _mediator.Send(request); - return new ApiActionResult(ApiResponse.CreateSuccess(response)); + return ApiActionResult.Ok(response); } catch (Exception ex) { - _logger.LogError(ex, "Error handling request of type {RequestType}", typeof(TRequest).Name); - return new ApiActionResult( - ApiResponse.CreateError(ex.Message), - HttpStatusCode.InternalServerError - ); + return HandleException(ex); } } @@ -45,36 +41,36 @@ namespace CoreAgent.API.Controllers { _logger.LogInformation("Handling request of type {RequestType}", typeof(TRequest).Name); await _mediator.Send(request); - return new ApiActionResult(ApiResponse.CreateSuccess()); + return ApiActionResult.Ok(); } catch (Exception ex) { - _logger.LogError(ex, "Error handling request of type {RequestType}", typeof(TRequest).Name); - return new ApiActionResult( - ApiResponse.CreateError(ex.Message), - HttpStatusCode.InternalServerError - ); + return HandleException(ex); } } - protected IActionResult Success(T data, string message = null) + private IActionResult HandleException(Exception ex) { - return new ApiActionResult(ApiResponse.CreateSuccess(data, message)); + _logger.LogError(ex, "Error handling request"); + return ApiActionResult.Error(ex.Message, "INTERNAL_ERROR", HttpStatusCode.InternalServerError); } - protected IActionResult Success(string message = null) + private IActionResult HandleException(Exception ex) { - return new ApiActionResult(ApiResponse.CreateSuccess(message)); + _logger.LogError(ex, "Error handling request"); + return ApiActionResult.Error(ex.Message, "INTERNAL_ERROR", HttpStatusCode.InternalServerError); } - protected IActionResult Error(string message, List errors = null, HttpStatusCode statusCode = HttpStatusCode.BadRequest) - { - return new ApiActionResult(ApiResponse.CreateError(message, errors), statusCode); - } + protected IActionResult Success(T data, string message = null) => + ApiActionResult.Ok(data, message); - protected IActionResult Error(string message, List errors = null, HttpStatusCode statusCode = HttpStatusCode.BadRequest) - { - return new ApiActionResult(ApiResponse.CreateError(message, errors), statusCode); - } + protected IActionResult Success(string message = null) => + ApiActionResult.Ok(message); + + protected IActionResult Error(string message, string errorCode = "ERROR", HttpStatusCode statusCode = HttpStatusCode.BadRequest) => + ApiActionResult.Error(message, errorCode, statusCode); + + protected IActionResult Error(string message, string errorCode = "ERROR", HttpStatusCode statusCode = HttpStatusCode.BadRequest) => + ApiActionResult.Error(message, errorCode, statusCode); } } \ No newline at end of file diff --git a/CoreAgent.API/Startup.cs b/CoreAgent.API/Startup.cs index 6d71de7..e86895c 100644 --- a/CoreAgent.API/Startup.cs +++ b/CoreAgent.API/Startup.cs @@ -1,7 +1,9 @@ -using CoreAgent.Infrastructure.Extensions; using System.Reflection; using Serilog; +using CoreAgent.Infrastructure.Extensions.Logging; +using CoreAgent.Infrastructure.Extensions.ServiceCollection; +using CoreAgent.Infrastructure.Middleware.GlobalException; namespace CoreAgent.API { @@ -87,8 +89,8 @@ namespace CoreAgent.API public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration config) { return services - .AddLoggingServices(config) - .AddApiVersioningExtension(config) + .AddLoggingService(config) + .AddApiVersioningService(config) .AddRequestLogging(config) .AddAuthorization() .AddCommandCustomService() @@ -99,7 +101,7 @@ namespace CoreAgent.API { return app .UseHttpsRedirection() - .UseExceptionMiddleware() + .UseGlobalExceptionMiddleware() .UseAuthorization() .UseRequestLogging(config) .UseApiVersioningExtension(); diff --git a/CoreAgent.API/logs/log-20250611.txt b/CoreAgent.API/logs/log-20250611.txt index b4d0da3..588bffa 100644 --- a/CoreAgent.API/logs/log-20250611.txt +++ b/CoreAgent.API/logs/log-20250611.txt @@ -306,3 +306,52 @@ System.Exception: 命令执行失败: 2025-06-11 01:01:15.049 +08:00 [WRN] 蜂窝网络接口 string 启动失败 2025-06-11 01:01:15.073 +08:00 [INF] Response for POST /api/v1/CellularNetwork/start: 200 - {"success":true,"message":null,"data":false,"errors":[]} 2025-06-11 01:01:15.088 +08:00 [INF] Request completed: POST /api/v1/CellularNetwork/start in 1436ms with status code 200 +2025-06-11 10:05:49.558 +08:00 [INF] Logger initialized successfully +2025-06-11 10:05:49.605 +08:00 [INF] Application starting... +2025-06-11 10:05:50.814 +08:00 [INF] Application startup completed +2025-06-11 10:06:02.084 +08:00 [INF] Request started: GET /api/v1/WeatherForecast +2025-06-11 10:06:02.133 +08:00 [INF] Weather forecast requested +2025-06-11 10:06:02.152 +08:00 [INF] Response for GET /api/v1/WeatherForecast: 200 - [{"date":"2025-06-12","temperatureC":35,"summary":"Balmy","temperatureF":94},{"date":"2025-06-13","temperatureC":18,"summary":"Hot","temperatureF":64},{"date":"2025-06-14","temperatureC":11,"summary":"Cool","temperatureF":51},{"date":"2025-06-15","temperatureC":47,"summary":"Cool","temperatureF":116},{"date":"2025-06-16","temperatureC":-1,"summary":"Chilly","temperatureF":31}] +2025-06-11 10:06:02.158 +08:00 [INF] Request completed: GET /api/v1/WeatherForecast in 80ms with status code 200 +2025-06-11 10:06:14.992 +08:00 [INF] Request started: POST /api/v1/CellularNetwork/start +2025-06-11 10:06:15.055 +08:00 [INF] 收到启动蜂窝网络请求: string +2025-06-11 10:06:15.059 +08:00 [INF] Handling request of type StartCellularNetworkCommand +2025-06-11 10:06:15.063 +08:00 [INF] 正在启动蜂窝网络接口: string +2025-06-11 10:06:15.064 +08:00 [INF] 蜂窝网络接口 string 启动成功 +2025-06-11 10:06:15.073 +08:00 [INF] Response for POST /api/v1/CellularNetwork/start: 200 - {"data":true,"isSuccess":true,"message":"操作成功","errorCode":"","statusCode":200} +2025-06-11 10:06:15.074 +08:00 [INF] Request completed: POST /api/v1/CellularNetwork/start in 82ms with status code 200 +2025-06-11 10:34:57.416 +08:00 [INF] Logger initialized successfully +2025-06-11 10:34:57.453 +08:00 [INF] Application starting... +2025-06-11 10:34:58.566 +08:00 [INF] Application startup completed +2025-06-11 10:35:03.081 +08:00 [INF] Request started: GET /api/v1/WeatherForecast +2025-06-11 10:35:03.162 +08:00 [INF] Weather forecast requested +2025-06-11 10:35:03.186 +08:00 [INF] Response for GET /api/v1/WeatherForecast: 200 - [{"date":"2025-06-12","temperatureC":37,"summary":"Bracing","temperatureF":98},{"date":"2025-06-13","temperatureC":-15,"summary":"Warm","temperatureF":6},{"date":"2025-06-14","temperatureC":-17,"summary":"Chilly","temperatureF":2},{"date":"2025-06-15","temperatureC":12,"summary":"Chilly","temperatureF":53},{"date":"2025-06-16","temperatureC":49,"summary":"Chilly","temperatureF":120}] +2025-06-11 10:35:03.194 +08:00 [INF] Request completed: GET /api/v1/WeatherForecast in 119ms with status code 200 +2025-06-11 10:35:07.873 +08:00 [INF] Request started: POST /api/v1/CellularNetwork/start +2025-06-11 10:35:07.926 +08:00 [INF] 收到启动蜂窝网络请求: string +2025-06-11 10:35:07.928 +08:00 [INF] Handling request of type StartCellularNetworkCommand +2025-06-11 10:35:07.932 +08:00 [INF] 正在启动蜂窝网络接口: string +2025-06-11 10:35:07.933 +08:00 [INF] 蜂窝网络接口 string 启动成功 +2025-06-11 10:35:07.945 +08:00 [INF] Response for POST /api/v1/CellularNetwork/start: 200 - {"data":true,"isSuccess":true,"message":"操作成功","errorCode":"","statusCode":200} +2025-06-11 10:35:07.947 +08:00 [INF] Request completed: POST /api/v1/CellularNetwork/start in 74ms with status code 200 +2025-06-11 10:55:01.399 +08:00 [INF] Logger initialized successfully +2025-06-11 10:55:01.437 +08:00 [INF] Application starting... +2025-06-11 10:55:02.444 +08:00 [INF] Application startup completed +2025-06-11 10:55:09.051 +08:00 [INF] Request started: GET /api/v1/WeatherForecast +2025-06-11 10:55:09.100 +08:00 [INF] Weather forecast requested +2025-06-11 10:55:09.118 +08:00 [INF] Response for GET /api/v1/WeatherForecast: 200 - [{"date":"2025-06-12","temperatureC":5,"summary":"Hot","temperatureF":40},{"date":"2025-06-13","temperatureC":34,"summary":"Sweltering","temperatureF":93},{"date":"2025-06-14","temperatureC":-8,"summary":"Scorching","temperatureF":18},{"date":"2025-06-15","temperatureC":38,"summary":"Hot","temperatureF":100},{"date":"2025-06-16","temperatureC":41,"summary":"Hot","temperatureF":105}] +2025-06-11 10:55:09.126 +08:00 [INF] Request completed: GET /api/v1/WeatherForecast in 82ms with status code 200 +2025-06-11 10:55:14.506 +08:00 [INF] Request started: POST /api/v1/CellularNetwork/stop +2025-06-11 10:55:14.548 +08:00 [INF] 收到停止蜂窝网络请求: string +2025-06-11 10:55:14.551 +08:00 [INF] Handling request of type StopCellularNetworkCommand +2025-06-11 10:55:14.555 +08:00 [INF] 正在停止蜂窝网络接口: string +2025-06-11 10:55:14.556 +08:00 [INF] 蜂窝网络接口 string 停止成功 +2025-06-11 10:55:14.567 +08:00 [INF] Response for POST /api/v1/CellularNetwork/stop: 200 - {"data":true,"isSuccess":true,"message":"操作成功","errorCode":"","statusCode":200} +2025-06-11 10:55:14.571 +08:00 [INF] Request completed: POST /api/v1/CellularNetwork/stop in 65ms with status code 200 +2025-06-11 10:55:18.635 +08:00 [INF] Request started: POST /api/v1/CellularNetwork/start +2025-06-11 10:55:18.648 +08:00 [INF] 收到启动蜂窝网络请求: string +2025-06-11 10:55:18.648 +08:00 [INF] Handling request of type StartCellularNetworkCommand +2025-06-11 10:55:18.650 +08:00 [INF] 正在启动蜂窝网络接口: string +2025-06-11 10:55:18.651 +08:00 [INF] 蜂窝网络接口 string 启动成功 +2025-06-11 10:55:18.652 +08:00 [INF] Response for POST /api/v1/CellularNetwork/start: 200 - {"data":true,"isSuccess":true,"message":"操作成功","errorCode":"","statusCode":200} +2025-06-11 10:55:18.655 +08:00 [INF] Request completed: POST /api/v1/CellularNetwork/start in 19ms with status code 200 diff --git a/CoreAgent.Application/Commands/CellularNetwork/StartCellularNetworkCommand.cs b/CoreAgent.Application/Commands/CellularNetwork/StartCellularNetworkCommand.cs index 5c20bfc..6aea228 100644 --- a/CoreAgent.Application/Commands/CellularNetwork/StartCellularNetworkCommand.cs +++ b/CoreAgent.Application/Commands/CellularNetwork/StartCellularNetworkCommand.cs @@ -1,4 +1,4 @@ -using CoreAgent.Domain.Models; +using CoreAgent.Domain.Models.Network; using MediatR; namespace CoreAgent.Application.Commands.CellularNetwork; diff --git a/CoreAgent.Application/Commands/CellularNetwork/StartCellularNetworkCommandHandler.cs b/CoreAgent.Application/Commands/CellularNetwork/StartCellularNetworkCommandHandler.cs deleted file mode 100644 index 1552b18..0000000 --- a/CoreAgent.Application/Commands/CellularNetwork/StartCellularNetworkCommandHandler.cs +++ /dev/null @@ -1,33 +0,0 @@ -using CoreAgent.Domain.Interfaces; -using MediatR; -using Microsoft.Extensions.Logging; - -namespace CoreAgent.Application.Commands.CellularNetwork; - -public class StartCellularNetworkCommandHandler : IRequestHandler -{ - private readonly ICellularNetworkService _cellularNetworkService; - private readonly ILogger _logger; - - public StartCellularNetworkCommandHandler( - ICellularNetworkService cellularNetworkService, - ILogger logger) - { - _cellularNetworkService = cellularNetworkService; - _logger = logger; - } - - public async Task Handle(StartCellularNetworkCommand request, CancellationToken cancellationToken) - { - try - { - _logger.LogInformation("Starting cellular network for interface: {InterfaceName}", request.InterfaceName); - return await _cellularNetworkService.StartAsync(request.InterfaceName, request.Config); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error starting cellular network for interface: {InterfaceName}", request.InterfaceName); - throw; - } - } -} \ No newline at end of file diff --git a/CoreAgent.Application/Handlers/CellularNetwork/GetCellularNetworkStatusCommandHandler.cs b/CoreAgent.Application/Handlers/CellularNetwork/GetCellularNetworkStatusCommandHandler.cs index c6cc216..a261bbf 100644 --- a/CoreAgent.Application/Handlers/CellularNetwork/GetCellularNetworkStatusCommandHandler.cs +++ b/CoreAgent.Application/Handlers/CellularNetwork/GetCellularNetworkStatusCommandHandler.cs @@ -1,5 +1,6 @@ using CoreAgent.Application.Commands.CellularNetwork; using CoreAgent.Domain.Interfaces; +using CoreAgent.Domain.Interfaces.Network; using MediatR; using Microsoft.Extensions.Logging; diff --git a/CoreAgent.Application/Handlers/CellularNetwork/StartCellularNetworkCommandHandler.cs b/CoreAgent.Application/Handlers/CellularNetwork/StartCellularNetworkCommandHandler.cs index f178a94..7c96de3 100644 --- a/CoreAgent.Application/Handlers/CellularNetwork/StartCellularNetworkCommandHandler.cs +++ b/CoreAgent.Application/Handlers/CellularNetwork/StartCellularNetworkCommandHandler.cs @@ -1,5 +1,5 @@ using CoreAgent.Application.Commands.CellularNetwork; -using CoreAgent.Domain.Interfaces; +using CoreAgent.Domain.Interfaces.Network; using MediatR; using Microsoft.Extensions.Logging; @@ -26,11 +26,22 @@ public class StartCellularNetworkCommandHandler : IRequestHandler -/// 蜂窝网络服务接口 -/// -public interface ICellularNetworkService -{ - /// - /// 启动蜂窝网络 - /// - /// 网络接口名称 - /// 网络配置 - /// 操作是否成功 - Task StartAsync(string interfaceName, CellularNetworkConfig config); - - /// - /// 停止蜂窝网络 - /// - /// 网络接口名称 - /// 操作是否成功 - Task StopAsync(string interfaceName); - - /// - /// 获取蜂窝网络状态 - /// - /// 网络接口名称 - /// 网络状态信息 - Task GetStatusAsync(string interfaceName); -} \ No newline at end of file diff --git a/CoreAgent.Domain/Interfaces/ICommandStrategyFactory.cs b/CoreAgent.Domain/Interfaces/ICommandStrategyFactory.cs deleted file mode 100644 index 35a859f..0000000 --- a/CoreAgent.Domain/Interfaces/ICommandStrategyFactory.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace CoreAgent.Domain.Interfaces; - -/// -/// 命令策略工厂接口 -/// -public interface ICommandStrategyFactory -{ - /// - /// 创建命令策略 - /// - /// 命令策略实例 - ICommandStrategy CreateStrategy(); -} - -/// -/// 命令策略接口 -/// -public interface ICommandStrategy -{ - /// - /// 执行命令并返回缓冲结果 - /// - /// 要执行的命令 - /// 取消令牌源 - /// 日志记录器 - /// 命令执行结果 - Task ExecuteBufferedAsync(string command, CancellationTokenSource source); -} \ No newline at end of file diff --git a/CoreAgent.Domain/Interfaces/Network/ICellularNetworkService.cs b/CoreAgent.Domain/Interfaces/Network/ICellularNetworkService.cs new file mode 100644 index 0000000..512c639 --- /dev/null +++ b/CoreAgent.Domain/Interfaces/Network/ICellularNetworkService.cs @@ -0,0 +1,178 @@ +using CoreAgent.Domain.Models.Network; + +namespace CoreAgent.Domain.Interfaces.Network; + +/// +/// 蜂窝网络服务接口 +/// +public interface ICellularNetworkService +{ + /// + /// 启动蜂窝网络 + /// + /// 网络接口名称 + /// 网络配置 + /// 启动结果 + Task StartAsync(string interfaceName, CellularNetworkConfig config); + + /// + /// 停止蜂窝网络 + /// + /// 网络接口名称 + /// 停止结果 + Task StopAsync(string interfaceName); + + /// + /// 获取网络状态 + /// + /// 网络接口名称 + /// 网络状态信息 + Task GetNetworkStatusAsync(string interfaceName); + + /// + /// 获取信号强度 + /// + /// 网络接口名称 + /// 信号强度信息 + Task GetSignalStrengthAsync(string interfaceName); + + /// + /// 获取网络类型 + /// + /// 网络接口名称 + /// 网络类型信息 + Task GetNetworkTypeAsync(string interfaceName); + + /// + /// 设置发射功率 + /// + /// 网络接口名称 + /// 功率等级(0-100) + /// 设置结果 + Task SetTransmitPowerAsync(string interfaceName, int powerLevel); + + /// + /// 获取全局状态 + /// + /// 全局状态信息 + Task GetGlobalStatusAsync(); +} + +/// +/// 网络状态 +/// +public enum NetworkStatus +{ + /// + /// 未知 + /// + Unknown, + + /// + /// 已连接 + /// + Connected, + + /// + /// 未连接 + /// + Disconnected +} + +/// +/// 信号强度 +/// +public enum SignalStrength +{ + /// + /// 无信号 + /// + NoSignal, + + /// + /// 弱信号 + /// + Weak, + + /// + /// 中等信号 + /// + Medium, + + /// + /// 强信号 + /// + Strong +} + +/// +/// 网络类型 +/// +public enum NetworkType +{ + /// + /// 未知 + /// + Unknown, + + /// + /// 2G网络 + /// + G2, + + /// + /// 3G网络 + /// + G3, + + /// + /// 4G网络 + /// + G4, + + /// + /// 5G网络 + /// + G5 +} + +/// +/// 蜂窝网络全局状态 +/// +public class CellularNetworkGlobalStatus +{ + /// + /// 是否已初始化 + /// + public bool IsInitialized { get; set; } + + /// + /// 最后启动时间 + /// + public DateTime? LastStartTime { get; set; } + + /// + /// 最后停止时间 + /// + public DateTime? LastStopTime { get; set; } + + /// + /// 当前运行状态 + /// + public NetworkStatus CurrentStatus { get; set; } + + /// + /// 当前信号强度 + /// + public SignalStrength CurrentSignalStrength { get; set; } + + /// + /// 当前网络类型 + /// + public NetworkType CurrentNetworkType { get; set; } + + /// + /// 当前发射功率 + /// + public int CurrentTransmitPower { get; set; } +} \ No newline at end of file diff --git a/CoreAgent.Domain/Interfaces/Network/INetworkManager.cs b/CoreAgent.Domain/Interfaces/Network/INetworkManager.cs new file mode 100644 index 0000000..59aefbf --- /dev/null +++ b/CoreAgent.Domain/Interfaces/Network/INetworkManager.cs @@ -0,0 +1,127 @@ +namespace CoreAgent.Domain.Interfaces.Network; + +/// +/// 网络管理器接口 +/// +public interface INetworkManager +{ + /// + /// 获取所有网络接口信息 + /// + /// 网络接口信息列表 + Task> GetNetworkInterfacesAsync(); + + /// + /// 获取指定网络接口的详细信息 + /// + /// 网络接口ID + /// 网络接口详细信息 + Task GetNetworkInterfaceDetailAsync(string interfaceId); + + /// + /// 启用网络接口 + /// + /// 网络接口ID + /// 操作是否成功 + Task EnableNetworkInterfaceAsync(string interfaceId); + + /// + /// 禁用网络接口 + /// + /// 网络接口ID + /// 操作是否成功 + Task DisableNetworkInterfaceAsync(string interfaceId); +} + +/// +/// 网络接口信息 +/// +public class NetworkInterfaceInfo +{ + /// + /// 接口ID + /// + public string Id { get; set; } + + /// + /// 接口名称 + /// + public string Name { get; set; } + + /// + /// 接口类型 + /// + public NetworkInterfaceType Type { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnabled { get; set; } + + /// + /// MAC地址 + /// + public string MacAddress { get; set; } +} + +/// +/// 网络接口详细信息 +/// +public class NetworkInterfaceDetail : NetworkInterfaceInfo +{ + /// + /// IP地址列表 + /// + public IEnumerable IpAddresses { get; set; } + + /// + /// 子网掩码 + /// + public string SubnetMask { get; set; } + + /// + /// 默认网关 + /// + public string DefaultGateway { get; set; } + + /// + /// DNS服务器列表 + /// + public IEnumerable DnsServers { get; set; } + + /// + /// 连接速度(Mbps) + /// + public int Speed { get; set; } +} + +/// +/// 网络接口类型 +/// +public enum NetworkInterfaceType +{ + /// + /// 未知 + /// + Unknown, + + /// + /// 以太网 + /// + Ethernet, + + /// + /// 无线网络 + /// + Wireless, + + /// + /// 蜂窝网络 + /// + Cellular, + + /// + /// 虚拟网络 + /// + Virtual +} \ No newline at end of file diff --git a/CoreAgent.Domain/Interfaces/System/Command/ISystemCommandExecutor.cs b/CoreAgent.Domain/Interfaces/System/Command/ISystemCommandExecutor.cs new file mode 100644 index 0000000..13a3e5a --- /dev/null +++ b/CoreAgent.Domain/Interfaces/System/Command/ISystemCommandExecutor.cs @@ -0,0 +1,26 @@ +using CoreAgent.Domain.Models.System; + +namespace CoreAgent.Domain.Interfaces.System.Command; + +/// +/// 系统命令执行器接口 +/// +public interface ISystemCommandExecutor +{ + /// + /// 执行命令并实时输出结果 + /// + /// 要执行的命令 + /// 输出处理器 + /// 取消令牌源 + /// 执行任务 + Task ExecuteCommandWithOutputAsync(string command, Action outputHandler, CancellationTokenSource cancellationTokenSource); + + /// + /// 执行命令并返回结果 + /// + /// 要执行的命令 + /// 取消令牌源 + /// 命令执行结果 + Task ExecuteCommandAsync(string command, CancellationTokenSource cancellationTokenSource); +} \ No newline at end of file diff --git a/CoreAgent.Domain/Interfaces/System/Command/ISystemCommandExecutorFactory.cs b/CoreAgent.Domain/Interfaces/System/Command/ISystemCommandExecutorFactory.cs new file mode 100644 index 0000000..6bc0558 --- /dev/null +++ b/CoreAgent.Domain/Interfaces/System/Command/ISystemCommandExecutorFactory.cs @@ -0,0 +1,15 @@ +using CoreAgent.Domain.Models; + +namespace CoreAgent.Domain.Interfaces.System.Command; + +/// +/// 系统命令执行器工厂接口 +/// +public interface ISystemCommandExecutorFactory +{ + /// + /// 创建适合当前操作系统的命令执行器 + /// + /// 命令执行器实例 + ISystemCommandExecutor CreateExecutor(); +} \ No newline at end of file diff --git a/CoreAgent.Domain/Models/ApiActionResult.cs b/CoreAgent.Domain/Models/ApiActionResult.cs deleted file mode 100644 index 281f5ec..0000000 --- a/CoreAgent.Domain/Models/ApiActionResult.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using System.Net; - -namespace CoreAgent.Domain.Models -{ - public class ApiActionResult : IActionResult - { - private readonly ApiResponse _response; - private readonly HttpStatusCode _statusCode; - - public ApiActionResult(ApiResponse response, HttpStatusCode statusCode = HttpStatusCode.OK) - { - _response = response; - _statusCode = statusCode; - } - - public async Task ExecuteResultAsync(ActionContext context) - { - var objectResult = new ObjectResult(_response) - { - StatusCode = (int)_statusCode - }; - - await objectResult.ExecuteResultAsync(context); - } - } - - public class ApiActionResult : IActionResult - { - private readonly ApiResponse _response; - private readonly HttpStatusCode _statusCode; - - public ApiActionResult(ApiResponse response, HttpStatusCode statusCode = HttpStatusCode.OK) - { - _response = response; - _statusCode = statusCode; - } - - public async Task ExecuteResultAsync(ActionContext context) - { - var objectResult = new ObjectResult(_response) - { - StatusCode = (int)_statusCode - }; - - await objectResult.ExecuteResultAsync(context); - } - } -} \ No newline at end of file diff --git a/CoreAgent.Domain/Models/ApiResponse.cs b/CoreAgent.Domain/Models/ApiResponse.cs deleted file mode 100644 index 20a7a09..0000000 --- a/CoreAgent.Domain/Models/ApiResponse.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace CoreAgent.Domain.Models -{ - public class ApiResponse - { - public bool Success { get; set; } - public string? Message { get; set; } - public T? Data { get; set; } - public List Errors { get; set; } - - public ApiResponse() - { - Success = true; - Errors = new List(); - } - - public static ApiResponse CreateSuccess(T data, string? message = null) - { - return new ApiResponse - { - Success = true, - Data = data, - Message = message - }; - } - - public static ApiResponse CreateError(string message, List? errors = null) - { - return new ApiResponse - { - Success = false, - Message = message, - Errors = errors ?? new List() - }; - } - } - - public class ApiResponse : ApiResponse - { - public new static ApiResponse CreateSuccess(string? message = null) - { - return new ApiResponse - { - Success = true, - Message = message - }; - } - - public new static ApiResponse CreateError(string message, List? errors = null) - { - return new ApiResponse - { - Success = false, - Message = message, - Errors = errors ?? new List() - }; - } - } -} \ No newline at end of file diff --git a/CoreAgent.Domain/Models/Common/ApiActionResult.cs b/CoreAgent.Domain/Models/Common/ApiActionResult.cs new file mode 100644 index 0000000..4a5ff79 --- /dev/null +++ b/CoreAgent.Domain/Models/Common/ApiActionResult.cs @@ -0,0 +1,119 @@ +using Microsoft.AspNetCore.Mvc; +using System.Net; + +namespace CoreAgent.Domain.Models.Common; + +/// +/// API操作结果 +/// +public class ApiActionResult : IActionResult +{ + /// + /// 是否成功 + /// + public bool IsSuccess { get; set; } + + /// + /// 消息 + /// + public string Message { get; set; } + + /// + /// 错误代码 + /// + public string ErrorCode { get; set; } + + /// + /// HTTP状态码 + /// + public HttpStatusCode StatusCode { get; set; } = HttpStatusCode.OK; + + /// + /// 创建成功结果 + /// + /// 消息 + /// API操作结果 + public static ApiActionResult Ok(string message = "操作成功") + { + return new ApiActionResult + { + IsSuccess = true, + Message = message, + ErrorCode = string.Empty, + StatusCode = HttpStatusCode.OK + }; + } + + /// + /// 创建失败结果 + /// + /// 错误消息 + /// 错误代码 + /// HTTP状态码 + /// API操作结果 + public static ApiActionResult Error(string message, string errorCode = "ERROR", HttpStatusCode statusCode = HttpStatusCode.BadRequest) + { + return new ApiActionResult + { + IsSuccess = false, + Message = message, + ErrorCode = errorCode, + StatusCode = statusCode + }; + } + + public Task ExecuteResultAsync(ActionContext context) + { + var result = new ObjectResult(this) { StatusCode = (int)StatusCode }; + return result.ExecuteResultAsync(context); + } +} + +/// +/// 带数据的API操作结果 +/// +/// 数据类型 +public class ApiActionResult : ApiActionResult +{ + /// + /// 数据 + /// + public T Data { get; set; } + + /// + /// 创建成功结果 + /// + /// 数据 + /// 消息 + /// API操作结果 + public static ApiActionResult Ok(T data, string message = "操作成功") + { + return new ApiActionResult + { + IsSuccess = true, + Message = message, + ErrorCode = string.Empty, + StatusCode = HttpStatusCode.OK, + Data = data + }; + } + + /// + /// 创建失败结果 + /// + /// 错误消息 + /// 错误代码 + /// HTTP状态码 + /// API操作结果 + public static ApiActionResult Error(string message, string errorCode = "ERROR", HttpStatusCode statusCode = HttpStatusCode.BadRequest) + { + return new ApiActionResult + { + IsSuccess = false, + Message = message, + ErrorCode = errorCode, + StatusCode = statusCode, + Data = default + }; + } +} \ No newline at end of file diff --git a/CoreAgent.Domain/Models/Common/ErrorResponse.cs b/CoreAgent.Domain/Models/Common/ErrorResponse.cs new file mode 100644 index 0000000..ef5bb2d --- /dev/null +++ b/CoreAgent.Domain/Models/Common/ErrorResponse.cs @@ -0,0 +1,23 @@ +namespace CoreAgent.Domain.Models.Common; + +/// +/// 错误响应 +/// +public class ErrorResponse +{ + + /// + /// 请求的唯一跟踪标识符 + /// + public string TraceId { get; set; } + + /// + /// 错误消息 + /// + public string Message { get; set; } + + /// + /// 验证错误集合,键为字段名,值为错误消息数组 + /// + public IDictionary Errors { get; set; } +} \ No newline at end of file diff --git a/CoreAgent.Domain/Models/ErrorResponse.cs b/CoreAgent.Domain/Models/ErrorResponse.cs deleted file mode 100644 index 6700242..0000000 --- a/CoreAgent.Domain/Models/ErrorResponse.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace CoreAgent.Domain.Models -{ - /// - /// ʾAPIӦģ - /// - public class ErrorResponse - { - /// - /// Ψһٱʶ - /// - public string TraceId { get; set; } - - /// - /// Ϣ - /// - public string Message { get; set; } - - /// - /// ֤󼯺ϣΪֵֶΪϢ - /// - public IDictionary Errors { get; set; } - } -} \ No newline at end of file diff --git a/CoreAgent.Domain/Models/CellularNetworkConfig.cs b/CoreAgent.Domain/Models/Network/CellularNetworkConfig.cs similarity index 89% rename from CoreAgent.Domain/Models/CellularNetworkConfig.cs rename to CoreAgent.Domain/Models/Network/CellularNetworkConfig.cs index be7c115..0bce1de 100644 --- a/CoreAgent.Domain/Models/CellularNetworkConfig.cs +++ b/CoreAgent.Domain/Models/Network/CellularNetworkConfig.cs @@ -1,4 +1,4 @@ -namespace CoreAgent.Domain.Models; +namespace CoreAgent.Domain.Models.Network; /// /// 蜂窝网络配置 diff --git a/CoreAgent.Domain/Models/CellularNetworkStatus.cs b/CoreAgent.Domain/Models/Network/CellularNetworkStatus.cs similarity index 91% rename from CoreAgent.Domain/Models/CellularNetworkStatus.cs rename to CoreAgent.Domain/Models/Network/CellularNetworkStatus.cs index b8c164c..5b61a5d 100644 --- a/CoreAgent.Domain/Models/CellularNetworkStatus.cs +++ b/CoreAgent.Domain/Models/Network/CellularNetworkStatus.cs @@ -1,4 +1,4 @@ -namespace CoreAgent.Domain.Models; +namespace CoreAgent.Domain.Models.Network; /// /// 蜂窝网络状态 diff --git a/CoreAgent.Domain/Models/System/CommandExecutionResult.cs b/CoreAgent.Domain/Models/System/CommandExecutionResult.cs new file mode 100644 index 0000000..58e804d --- /dev/null +++ b/CoreAgent.Domain/Models/System/CommandExecutionResult.cs @@ -0,0 +1,61 @@ +namespace CoreAgent.Domain.Models.System; + +/// +/// 命令执行结果 +/// +public class CommandExecutionResult +{ + /// + /// 是否成功 + /// + public bool IsSuccess { get; set; } + + /// + /// 输出信息 + /// + public string Output { get; set; } + + /// + /// 错误信息 + /// + public string Error { get; set; } + + /// + /// 执行时间(毫秒) + /// + public long ExecutionTime { get; set; } + + /// + /// 创建成功结果 + /// + /// 输出信息 + /// 执行时间 + /// 命令执行结果 + public static CommandExecutionResult Success(string output, long executionTime) + { + return new CommandExecutionResult + { + IsSuccess = true, + Output = output, + Error = string.Empty, + ExecutionTime = executionTime + }; + } + + /// + /// 创建失败结果 + /// + /// 错误信息 + /// 执行时间 + /// 命令执行结果 + public static CommandExecutionResult Failure(string error, long executionTime) + { + return new CommandExecutionResult + { + IsSuccess = false, + Output = string.Empty, + Error = error, + ExecutionTime = executionTime + }; + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Command/Base/BaseCommandExecutor.cs b/CoreAgent.Infrastructure/Command/Base/BaseCommandExecutor.cs new file mode 100644 index 0000000..f96b9e0 --- /dev/null +++ b/CoreAgent.Infrastructure/Command/Base/BaseCommandExecutor.cs @@ -0,0 +1,57 @@ +using CliWrap; +using CliWrap.Buffered; +using CoreAgent.Domain.Interfaces.System.Command; +using CoreAgent.Domain.Models; +using CoreAgent.Domain.Models.System; +using Microsoft.Extensions.Logging; + +namespace CoreAgent.Infrastructure.Command.Base; + +/// +/// 命令执行器基类 +/// +public abstract class BaseCommandExecutor : ISystemCommandExecutor +{ + protected readonly ILogger _logger; + protected abstract string DefaultShell { get; } + + protected BaseCommandExecutor(ILogger logger) + { + _logger = logger; + } + + public abstract Task ExecuteCommandWithOutputAsync(string command, Action outputHandler, CancellationTokenSource cancellationTokenSource); + + public abstract Task ExecuteCommandAsync(string command, CancellationTokenSource cancellationTokenSource); + + protected async Task ExecuteCommandInternalAsync(string command, string arguments, CancellationTokenSource cancellationTokenSource) + { + var startTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + try + { + _logger.LogDebug("开始执行命令: {Command}", command); + var cmd = Cli.Wrap(DefaultShell) + .WithArguments(arguments) + .WithValidation(CommandResultValidation.None); + var result = await cmd.ExecuteBufferedAsync(cancellationTokenSource.Token); + var executionTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - startTime; + + if (result.IsSuccess) + { + _logger.LogDebug("命令执行成功 - 输出: {Output}", result.StandardOutput); + return CommandExecutionResult.Success(result.StandardOutput, executionTime); + } + else + { + _logger.LogDebug("命令执行失败 - 错误: {Error}", result.StandardError); + return CommandExecutionResult.Failure(result.StandardError, executionTime); + } + } + catch (Exception ex) + { + var executionTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - startTime; + _logger.LogError(ex, "命令执行失败: {Command}", command); + return CommandExecutionResult.Failure(ex.Message, executionTime); + } + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Command/CommandStrategyFactory.cs b/CoreAgent.Infrastructure/Command/CommandStrategyFactory.cs deleted file mode 100644 index 8cf36a2..0000000 --- a/CoreAgent.Infrastructure/Command/CommandStrategyFactory.cs +++ /dev/null @@ -1,44 +0,0 @@ -using CoreAgent.Domain.Interfaces; -using Microsoft.Extensions.Logging; -using System.Runtime.InteropServices; - -namespace CoreAgent.Infrastructure.Command; - -/// -/// 命令策略工厂实现 -/// -public class CommandStrategyFactory : ICommandStrategyFactory -{ - private readonly ILogger _logger; - private readonly ILoggerFactory _loggerFactory; - - public CommandStrategyFactory(ILogger logger, ILoggerFactory loggerFactory) - { - _logger = logger; - _loggerFactory = loggerFactory; - } - - /// - /// 创建适合当前操作系统的命令策略 - /// - /// 命令策略实例 - /// 当操作系统不是Windows或Linux时抛出 - public ICommandStrategy CreateStrategy() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - _logger.LogInformation("Creating Windows command strategy"); - return new WindowsCommandStrategy(_loggerFactory.CreateLogger()); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - _logger.LogInformation("Creating Linux command strategy"); - return new LinuxCommandStrategy(_loggerFactory.CreateLogger()); - } - else - { - _logger.LogError("Unsupported operating system"); - throw new PlatformNotSupportedException("Only Windows and Linux are supported."); - } - } -} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Command/Executors/LinuxCommandExecutor.cs b/CoreAgent.Infrastructure/Command/Executors/LinuxCommandExecutor.cs new file mode 100644 index 0000000..bac0078 --- /dev/null +++ b/CoreAgent.Infrastructure/Command/Executors/LinuxCommandExecutor.cs @@ -0,0 +1,46 @@ +using CliWrap; +using CliWrap.Buffered; +using CoreAgent.Domain.Interfaces.System.Command; +using CoreAgent.Domain.Models.System; +using CoreAgent.Infrastructure.Command.Base; +using Microsoft.Extensions.Logging; + +namespace CoreAgent.Infrastructure.Command.Executors; + +/// +/// Linux系统命令执行器实现 +/// +public class LinuxCommandExecutor : BaseCommandExecutor +{ + private const string ShellPath = "/bin/bash"; + + public LinuxCommandExecutor(ILogger logger) : base(logger) + { + } + + protected override string DefaultShell => ShellPath; + + public override async Task ExecuteCommandWithOutputAsync(string command, Action outputHandler, CancellationTokenSource cancellationTokenSource) + { + try + { + _logger.LogDebug("开始执行命令: {Command}", command); + var cmd = Cli.Wrap(DefaultShell) + .WithArguments($"-c \"{command}\"") + .WithStandardOutputPipe(PipeTarget.ToDelegate(outputHandler)) + .WithStandardErrorPipe(PipeTarget.ToDelegate(outputHandler)) + .WithValidation(CommandResultValidation.None); + var result = await cmd.ExecuteBufferedAsync(cancellationTokenSource.Token); + _logger.LogInformation("命令执行结果: {IsSuccess}", result.IsSuccess); + } + catch (Exception ex) + { + _logger.LogError(ex, "命令执行失败: {Command}", command); + } + } + + public override async Task ExecuteCommandAsync(string command, CancellationTokenSource cancellationTokenSource) + { + return await ExecuteCommandInternalAsync(command, $"-c \"{command}\"", cancellationTokenSource); + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Command/Executors/WindowsCommandExecutor.cs b/CoreAgent.Infrastructure/Command/Executors/WindowsCommandExecutor.cs new file mode 100644 index 0000000..35c5687 --- /dev/null +++ b/CoreAgent.Infrastructure/Command/Executors/WindowsCommandExecutor.cs @@ -0,0 +1,47 @@ +using CliWrap; +using CliWrap.Buffered; +using CoreAgent.Domain.Interfaces.System.Command; +using CoreAgent.Domain.Models.System; +using CoreAgent.Infrastructure.Command.Base; +using Microsoft.Extensions.Logging; + +namespace CoreAgent.Infrastructure.Command.Executors; + +/// +/// Windows系统命令执行器实现 +/// +public class WindowsCommandExecutor : BaseCommandExecutor +{ + private const string ShellPath = "cmd.exe"; + private const string PowerShellPath = "powershell.exe"; + + public WindowsCommandExecutor(ILogger logger) : base(logger) + { + } + + protected override string DefaultShell => ShellPath; + + public override async Task ExecuteCommandWithOutputAsync(string command, Action outputHandler, CancellationTokenSource cancellationTokenSource) + { + try + { + _logger.LogDebug("开始执行命令: {Command}", command); + var cmd = Cli.Wrap(DefaultShell) + .WithArguments($"/c {command}") + .WithStandardOutputPipe(PipeTarget.ToDelegate(outputHandler)) + .WithStandardErrorPipe(PipeTarget.ToDelegate(outputHandler)) + .WithValidation(CommandResultValidation.None); + var result = await cmd.ExecuteBufferedAsync(cancellationTokenSource.Token); + _logger.LogInformation("命令执行结果: {IsSuccess}", result.IsSuccess); + } + catch (Exception ex) + { + _logger.LogError(ex, "命令执行失败: {Command}", command); + } + } + + public override async Task ExecuteCommandAsync(string command, CancellationTokenSource cancellationTokenSource) + { + return await ExecuteCommandInternalAsync(command, $"/c {command}", cancellationTokenSource); + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Command/Factories/SystemCommandExecutorFactory.cs b/CoreAgent.Infrastructure/Command/Factories/SystemCommandExecutorFactory.cs new file mode 100644 index 0000000..d5a7609 --- /dev/null +++ b/CoreAgent.Infrastructure/Command/Factories/SystemCommandExecutorFactory.cs @@ -0,0 +1,38 @@ +using CoreAgent.Domain.Interfaces.System.Command; +using CoreAgent.Infrastructure.Command.Executors; +using Microsoft.Extensions.Logging; + +namespace CoreAgent.Infrastructure.Command.Factories; + +/// +/// 系统命令执行器工厂 +/// +public class SystemCommandExecutorFactory : ISystemCommandExecutorFactory +{ + private readonly ILogger _windowsLogger; + private readonly ILogger _linuxLogger; + + public SystemCommandExecutorFactory( + ILogger windowsLogger, + ILogger linuxLogger) + { + _windowsLogger = windowsLogger; + _linuxLogger = linuxLogger; + } + + public ISystemCommandExecutor CreateExecutor() + { + if (OperatingSystem.IsWindows()) + { + return new WindowsCommandExecutor(_windowsLogger); + } + else if (OperatingSystem.IsLinux()) + { + return new LinuxCommandExecutor(_linuxLogger); + } + else + { + throw new PlatformNotSupportedException("当前操作系统不支持命令执行"); + } + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Command/LinuxCommandStrategy.cs b/CoreAgent.Infrastructure/Command/LinuxCommandStrategy.cs deleted file mode 100644 index fdd5329..0000000 --- a/CoreAgent.Infrastructure/Command/LinuxCommandStrategy.cs +++ /dev/null @@ -1,123 +0,0 @@ -using CliWrap; -using CliWrap.Buffered; -using CoreAgent.Domain.Interfaces; -using Microsoft.Extensions.Logging; - -namespace CoreAgent.Infrastructure.Command; - -/// -/// Linux命令策略实现 -/// -public class LinuxCommandStrategy : ICommandStrategy -{ - private readonly ILogger _logger; - - public LinuxCommandStrategy(ILogger logger) - { - _logger = logger; - } - - public async Task ExecuteBufferedAsync(string arguments, Action onOutputReceived, CancellationTokenSource source) - { - try - { - _logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); - var cmd = Cli.Wrap("/bin/bash") - .WithArguments($"-c \"{arguments}\"") - .WithStandardOutputPipe(PipeTarget.ToDelegate(onOutputReceived)) - .WithStandardErrorPipe(PipeTarget.ToDelegate(onOutputReceived)) - .WithValidation(CommandResultValidation.None); - var result = await cmd.ExecuteBufferedAsync(source.Token); - _logger.LogInformation("{Arguments} result:{IsSuccess}", arguments, result.IsSuccess); - } - catch (Exception ex) - { - _logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); - } - } - - public async Task ExecuteBufferedAsync(string arguments, CancellationTokenSource source) - { - try - { - _logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); - var cmd = Cli.Wrap("/bin/bash") - .WithArguments($"-c \"{arguments}\"") - .WithValidation(CommandResultValidation.None); - var cmdResult = await cmd.ExecuteBufferedAsync(source.Token); - string result = string.Empty; - if (cmdResult.IsSuccess) - result = cmdResult.StandardOutput; - else - result = cmdResult.StandardError; - _logger.LogDebug("ExecuteAsync cmd :{Arguments} StandardOutput:{Output} :StandardError {Error}", - arguments, cmdResult.StandardOutput, cmdResult.StandardError); - return result; - } - catch (Exception ex) - { - _logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); - return string.Empty; - } - } - - public async Task ExecuteAsync(string arguments, CancellationTokenSource source) - { - try - { - _logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); - var cmd = await Cli.Wrap("/bin/bash") - .WithArguments($"-c \"{arguments}\"") - .ExecuteAsync(source.Token); - _logger.LogDebug("ExecuteAsync cmd :{Arguments} :result {IsSuccess}", arguments, cmd.IsSuccess); - return cmd.IsSuccess; - } - catch (Exception ex) - { - _logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); - return false; - } - } - - public async Task ExecuteBufferedAsync(string[] arguments, CancellationTokenSource source, string command = "/root/enb/doc/ws.js") - { - try - { - _logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); - var cmd = Cli.Wrap(command) - .WithArguments(arguments) - .WithValidation(CommandResultValidation.None); - var cmdResult = await cmd.ExecuteBufferedAsync(source.Token); - string result = string.Empty; - if (cmdResult.IsSuccess) - result = cmdResult.StandardOutput; - else - result = cmdResult.StandardError; - _logger.LogDebug("ExecuteAsync cmd :{Arguments} :result {Result}", arguments, result); - return result; - } - catch (Exception ex) - { - _logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); - return string.Empty; - } - } - - public async Task ExecuteAsync(string[] arguments, CancellationTokenSource source, string command = "/root/enb/doc/ws.js") - { - try - { - _logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); - var cmd = await Cli.Wrap(command) - .WithArguments(arguments) - .WithValidation(CommandResultValidation.None) - .ExecuteAsync(source.Token); - return cmd.IsSuccess; - } - catch (Exception ex) - { - _logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); - return false; - } - } -} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Command/WindowsCommandStrategy.cs b/CoreAgent.Infrastructure/Command/WindowsCommandStrategy.cs deleted file mode 100644 index b3789bf..0000000 --- a/CoreAgent.Infrastructure/Command/WindowsCommandStrategy.cs +++ /dev/null @@ -1,123 +0,0 @@ -using CliWrap; -using CliWrap.Buffered; -using CoreAgent.Domain.Interfaces; -using Microsoft.Extensions.Logging; - -namespace CoreAgent.Infrastructure.Command; - -/// -/// Windows命令策略实现 -/// -public class WindowsCommandStrategy : ICommandStrategy -{ - private readonly ILogger _logger; - - public WindowsCommandStrategy(ILogger logger) - { - _logger = logger; - } - - public async Task ExecuteBufferedAsync(string arguments, Action onOutputReceived, CancellationTokenSource source) - { - try - { - _logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); - var cmd = Cli.Wrap("cmd.exe") - .WithArguments($"/c {arguments}") - .WithStandardOutputPipe(PipeTarget.ToDelegate(onOutputReceived)) - .WithStandardErrorPipe(PipeTarget.ToDelegate(onOutputReceived)) - .WithValidation(CommandResultValidation.None); - var result = await cmd.ExecuteBufferedAsync(source.Token); - _logger.LogInformation("{Arguments} result:{IsSuccess}", arguments, result.IsSuccess); - } - catch (Exception ex) - { - _logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); - } - } - - public async Task ExecuteBufferedAsync(string arguments, CancellationTokenSource source) - { - try - { - _logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); - var cmd = Cli.Wrap("cmd.exe") - .WithArguments($"/c {arguments}") - .WithValidation(CommandResultValidation.None); - var cmdResult = await cmd.ExecuteBufferedAsync(source.Token); - string result = string.Empty; - if (cmdResult.IsSuccess) - result = cmdResult.StandardOutput; - else - result = cmdResult.StandardError; - _logger.LogDebug("ExecuteAsync cmd :{Arguments} StandardOutput:{Output} :StandardError {Error}", - arguments, cmdResult.StandardOutput, cmdResult.StandardError); - return result; - } - catch (Exception ex) - { - _logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); - return string.Empty; - } - } - - public async Task ExecuteAsync(string arguments, CancellationTokenSource source) - { - try - { - _logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); - var cmd = await Cli.Wrap("cmd.exe") - .WithArguments($"/c {arguments}") - .ExecuteAsync(source.Token); - _logger.LogDebug("ExecuteAsync cmd :{Arguments} :result {IsSuccess}", arguments, cmd.IsSuccess); - return cmd.IsSuccess; - } - catch (Exception ex) - { - _logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); - return false; - } - } - - public async Task ExecuteBufferedAsync(string[] arguments, CancellationTokenSource source, string command = "powershell.exe") - { - try - { - _logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); - var cmd = Cli.Wrap(command) - .WithArguments(arguments) - .WithValidation(CommandResultValidation.None); - var cmdResult = await cmd.ExecuteBufferedAsync(source.Token); - string result = string.Empty; - if (cmdResult.IsSuccess) - result = cmdResult.StandardOutput; - else - result = cmdResult.StandardError; - _logger.LogDebug("ExecuteAsync cmd :{Arguments} :result {Result}", arguments, result); - return result; - } - catch (Exception ex) - { - _logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); - return string.Empty; - } - } - - public async Task ExecuteAsync(string[] arguments, CancellationTokenSource source, string command = "powershell.exe") - { - try - { - _logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); - var cmd = await Cli.Wrap(command) - .WithArguments(arguments) - .WithValidation(CommandResultValidation.None) - .ExecuteAsync(source.Token); - return cmd.IsSuccess; - } - catch (Exception ex) - { - _logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); - return false; - } - } -} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/CoreAgent.Infrastructure.csproj.Backup.tmp b/CoreAgent.Infrastructure/CoreAgent.Infrastructure.csproj.Backup.tmp deleted file mode 100644 index 5a8ebfc..0000000 --- a/CoreAgent.Infrastructure/CoreAgent.Infrastructure.csproj.Backup.tmp +++ /dev/null @@ -1,13 +0,0 @@ - - - - net8.0 - enable - enable - - - - - - - diff --git a/CoreAgent.Infrastructure/Extensions/ApiVersioningExtensions.cs b/CoreAgent.Infrastructure/Extensions/ApiVersioningExtensions.cs deleted file mode 100644 index 1d93824..0000000 --- a/CoreAgent.Infrastructure/Extensions/ApiVersioningExtensions.cs +++ /dev/null @@ -1,44 +0,0 @@ -using CoreAgent.Infrastructure.Options; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Versioning; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace CoreAgent.Infrastructure.Extensions -{ - public static class ApiVersioningExtensions - { - public static IServiceCollection AddApiVersioningExtension(this IServiceCollection services, IConfiguration config) - { - var settings = config.GetSection("ApiVersioning").Get() ?? new ApiVersioningSettings(); - - services.AddApiVersioning(options => - { - options.DefaultApiVersion = ApiVersion.Parse(settings.DefaultVersion); - options.AssumeDefaultVersionWhenUnspecified = settings.AssumeDefaultVersionWhenUnspecified; - options.ReportApiVersions = settings.ReportApiVersions; - options.ApiVersionReader = ApiVersionReader.Combine( - settings.ShowVersionInUrl ? new UrlSegmentApiVersionReader() : null, - settings.ShowVersionInQueryString ? new QueryStringApiVersionReader("api-version") : null, - settings.ShowVersionInHeader ? new HeaderApiVersionReader("api-version") : null, - settings.ShowVersionInMediaType ? new MediaTypeApiVersionReader("version") : null - ); - }); - - services.AddVersionedApiExplorer(options => - { - options.GroupNameFormat = "'v'VVV"; - options.SubstituteApiVersionInUrl = true; - }); - - return services; - } - - public static IApplicationBuilder UseApiVersioningExtension(this IApplicationBuilder app) - { - // 可以在这里添加版本控制相关的中间件 - return app; - } - } -} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Extensions/ExceptionMiddlewareExtensions.cs b/CoreAgent.Infrastructure/Extensions/ExceptionMiddlewareExtensions.cs deleted file mode 100644 index 0e8ff5e..0000000 --- a/CoreAgent.Infrastructure/Extensions/ExceptionMiddlewareExtensions.cs +++ /dev/null @@ -1,13 +0,0 @@ -using CoreAgent.Infrastructure.Middleware; -using Microsoft.AspNetCore.Builder; - -namespace CoreAgent.Infrastructure.Extensions -{ - public static class ExceptionMiddlewareExtensions - { - public static IApplicationBuilder UseExceptionMiddleware(this IApplicationBuilder builder) - { - return builder.UseMiddleware(); - } - } -} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Extensions/Logging/LoggingExtensions.cs b/CoreAgent.Infrastructure/Extensions/Logging/LoggingExtensions.cs new file mode 100644 index 0000000..2fc4e8f --- /dev/null +++ b/CoreAgent.Infrastructure/Extensions/Logging/LoggingExtensions.cs @@ -0,0 +1,35 @@ +using CoreAgent.Infrastructure.Logging; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Serilog; +using Serilog.Events; + +namespace CoreAgent.Infrastructure.Extensions.Logging; + +/// +/// 日志扩展方法 +/// +public static class LoggingExtensions +{ + /// + /// 添加日志服务 + /// + /// 服务集合 + /// 服务集合 + public static IServiceCollection AddLoggingService(this IServiceCollection services, IConfiguration configuration) + { + // 初始化应用程序日志记录器 + ApplicationLogger.Initialize(configuration); + Log.Information("Application starting..."); + + // 添加 Serilog 到依赖注入容器 + services.AddLogging(loggingBuilder => + { + loggingBuilder.ClearProviders(); + loggingBuilder.AddSerilog(dispose: true); + }); + + return services; + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Extensions/Logging/RequestLoggingExtensions.cs b/CoreAgent.Infrastructure/Extensions/Logging/RequestLoggingExtensions.cs new file mode 100644 index 0000000..43940d7 --- /dev/null +++ b/CoreAgent.Infrastructure/Extensions/Logging/RequestLoggingExtensions.cs @@ -0,0 +1,33 @@ + +using CoreAgent.Infrastructure.Options; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Serilog; + +namespace CoreAgent.Infrastructure.Extensions.Logging; + +/// +/// 请求日志扩展方法 +/// +public static class RequestLoggingExtensions +{ + /// + /// 添加请求日志中间件 + /// + /// 应用程序构建器 + /// 应用程序构建器 + + public static IServiceCollection AddRequestLogging(this IServiceCollection services, IConfiguration config) + { + services.Configure(config.GetSection("RequestLogging")); + return services; + } + + public static IApplicationBuilder UseRequestLogging(this IApplicationBuilder app, IConfiguration config) + { + app.UseMiddleware(); + app.UseMiddleware(); + return app; + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Extensions/Logging/RequestLoggingMiddleware.cs b/CoreAgent.Infrastructure/Extensions/Logging/RequestLoggingMiddleware.cs new file mode 100644 index 0000000..87057f2 --- /dev/null +++ b/CoreAgent.Infrastructure/Extensions/Logging/RequestLoggingMiddleware.cs @@ -0,0 +1,112 @@ +using System.Diagnostics; +using CoreAgent.Infrastructure.Options; +using System.Text; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace CoreAgent.Infrastructure.Extensions.Logging; + +/// +/// 请求日志中间件 +/// +public class RequestLoggingMiddleware +{ + private readonly RequestDelegate _next; + private readonly ILogger _logger; + private readonly RequestLoggingOptions _options; + + public RequestLoggingMiddleware( + RequestDelegate next, + ILogger logger, + IOptions options) + { + _next = next; + _logger = logger; + _options = options.Value; + } + + public async Task InvokeAsync(HttpContext context) + { + if (ShouldSkipLogging(context)) + { + await _next(context); + return; + } + + var sw = Stopwatch.StartNew(); + var requestBody = string.Empty; + + try + { + if (_options.LogRequestBody && context.Request.Body != null) + { + requestBody = await ReadRequestBodyAsync(context.Request); + } + + _logger.LogInformation( + "Request started: {Method} {Path} {QueryString} {RequestBody}", + context.Request.Method, + context.Request.Path, + context.Request.QueryString, + requestBody); + + await _next(context); + } + finally + { + sw.Stop(); + _logger.LogInformation( + "Request completed: {Method} {Path} in {ElapsedMilliseconds}ms with status code {StatusCode}", + context.Request.Method, + context.Request.Path, + sw.ElapsedMilliseconds, + context.Response.StatusCode); + } + } + + private bool ShouldSkipLogging(HttpContext context) + { + if (_options.ExcludePaths.Any(p => context.Request.Path.StartsWithSegments(p))) + { + return true; + } + + if (_options.ExcludeMethods.Contains(context.Request.Method)) + { + return true; + } + + return false; + } + + private async Task ReadRequestBodyAsync(HttpRequest request) + { + if (!request.Body.CanRead || !request.Body.CanSeek) + { + return string.Empty; + } + + var originalPosition = request.Body.Position; + request.Body.Position = 0; + + try + { + using var reader = new StreamReader( + request.Body, + encoding: Encoding.UTF8, + detectEncodingFromByteOrderMarks: false, + bufferSize: 1024 * 45, + leaveOpen: true); + + var body = await reader.ReadToEndAsync(); + return body.Length > _options.MaxRequestBodySize + ? body.Substring(0, _options.MaxRequestBodySize) + "..." + : body; + } + finally + { + request.Body.Position = originalPosition; + } + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Extensions/Logging/ResponseLoggingMiddleware.cs b/CoreAgent.Infrastructure/Extensions/Logging/ResponseLoggingMiddleware.cs new file mode 100644 index 0000000..e572990 --- /dev/null +++ b/CoreAgent.Infrastructure/Extensions/Logging/ResponseLoggingMiddleware.cs @@ -0,0 +1,106 @@ +using System.Diagnostics; +using CoreAgent.Infrastructure.Options; +using System.Text; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace CoreAgent.Infrastructure.Extensions.Logging; + +/// +/// 响应日志中间件 +/// +public class ResponseLoggingMiddleware +{ + private readonly RequestDelegate _next; + private readonly ILogger _logger; + private readonly RequestLoggingOptions _options; + + public ResponseLoggingMiddleware( + RequestDelegate next, + ILogger logger, + IOptions options) + { + _next = next; + _logger = logger; + _options = options.Value; + } + + public async Task InvokeAsync(HttpContext context) + { + if (ShouldSkipLogging(context)) + { + await _next(context); + return; + } + + // 保存原始响应流 + var originalBodyStream = context.Response.Body; + + try + { + // 创建一个新的内存流来捕获响应 + using var responseBody = new MemoryStream(); + context.Response.Body = responseBody; + + // 继续处理请求 + await _next(context); + + // 读取响应内容 + responseBody.Seek(0, SeekOrigin.Begin); + var response = await ReadResponseBodyAsync(responseBody); + + // 记录响应 + _logger.LogInformation( + "Response for {Method} {Path}: {StatusCode} - {Response}", + context.Request.Method, + context.Request.Path, + context.Response.StatusCode, + response); + + // 将响应写回原始流 + responseBody.Seek(0, SeekOrigin.Begin); + await responseBody.CopyToAsync(originalBodyStream); + } + finally + { + // 确保响应体被恢复 + context.Response.Body = originalBodyStream; + } + } + + private bool ShouldSkipLogging(HttpContext context) + { + if (!_options.LogResponseBody) + { + return true; + } + + if (_options.ExcludePaths.Any(p => context.Request.Path.StartsWithSegments(p))) + { + return true; + } + + if (_options.ExcludeMethods.Contains(context.Request.Method)) + { + return true; + } + + return false; + } + + private async Task ReadResponseBodyAsync(Stream responseBody) + { + using var reader = new StreamReader( + responseBody, + encoding: Encoding.UTF8, + detectEncodingFromByteOrderMarks: false, + bufferSize: 1024 * 45, + leaveOpen: true); + + var body = await reader.ReadToEndAsync(); + return body.Length > _options.MaxResponseBodySize + ? body.Substring(0, _options.MaxResponseBodySize) + "..." + : body; + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Extensions/LoggingExtensions.cs b/CoreAgent.Infrastructure/Extensions/LoggingExtensions.cs deleted file mode 100644 index bd3dd64..0000000 --- a/CoreAgent.Infrastructure/Extensions/LoggingExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using CoreAgent.Infrastructure.Logging; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Serilog; - -namespace CoreAgent.Infrastructure.Extensions -{ - public static class LoggingExtensions - { - public static IServiceCollection AddLoggingServices(this IServiceCollection services, IConfiguration configuration) - { - // 初始化应用程序日志记录器 - ApplicationLogger.Initialize(configuration); - Log.Information("Application starting..."); - - // 添加 Serilog 到依赖注入容器 - services.AddLogging(loggingBuilder => - { - loggingBuilder.ClearProviders(); - loggingBuilder.AddSerilog(dispose: true); - }); - - return services; - } - } -} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Extensions/RequestLoggingExtensions.cs b/CoreAgent.Infrastructure/Extensions/RequestLoggingExtensions.cs deleted file mode 100644 index 335d4c4..0000000 --- a/CoreAgent.Infrastructure/Extensions/RequestLoggingExtensions.cs +++ /dev/null @@ -1,24 +0,0 @@ -using CoreAgent.Infrastructure.Middleware; -using CoreAgent.Infrastructure.Options; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace CoreAgent.Infrastructure.Extensions -{ - public static class RequestLoggingExtensions - { - public static IServiceCollection AddRequestLogging(this IServiceCollection services, IConfiguration config) - { - services.Configure(config.GetSection("RequestLogging")); - return services; - } - - public static IApplicationBuilder UseRequestLogging(this IApplicationBuilder app, IConfiguration config) - { - app.UseMiddleware(); - app.UseMiddleware(); - return app; - } - } -} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Extensions/ServiceCollection/ApiVersioningServiceExtensions.cs b/CoreAgent.Infrastructure/Extensions/ServiceCollection/ApiVersioningServiceExtensions.cs new file mode 100644 index 0000000..2788db8 --- /dev/null +++ b/CoreAgent.Infrastructure/Extensions/ServiceCollection/ApiVersioningServiceExtensions.cs @@ -0,0 +1,51 @@ +using CoreAgent.Infrastructure.Options; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Versioning; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace CoreAgent.Infrastructure.Extensions.ServiceCollection; + +/// +/// API版本服务扩展方法 +/// +public static class ApiVersioningServiceExtensions +{ + /// + /// 添加API版本服务 + /// + /// 服务集合 + /// 服务集合 + public static IServiceCollection AddApiVersioningService(this IServiceCollection services, IConfiguration config) + { + var settings = config.GetSection("ApiVersioning").Get() ?? new ApiVersioningSettings(); + + services.AddApiVersioning(options => + { + options.DefaultApiVersion = ApiVersion.Parse(settings.DefaultVersion); + options.AssumeDefaultVersionWhenUnspecified = settings.AssumeDefaultVersionWhenUnspecified; + options.ReportApiVersions = settings.ReportApiVersions; + options.ApiVersionReader = ApiVersionReader.Combine( + settings.ShowVersionInUrl ? new UrlSegmentApiVersionReader() : null, + settings.ShowVersionInQueryString ? new QueryStringApiVersionReader("api-version") : null, + settings.ShowVersionInHeader ? new HeaderApiVersionReader("api-version") : null, + settings.ShowVersionInMediaType ? new MediaTypeApiVersionReader("version") : null + ); + }); + + services.AddVersionedApiExplorer(options => + { + options.GroupNameFormat = "'v'VVV"; + options.SubstituteApiVersionInUrl = true; + }); + + return services; + } + + public static IApplicationBuilder UseApiVersioningExtension(this IApplicationBuilder app) + { + // 可以在这里添加版本控制相关的中间件 + return app; + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Extensions/ServiceCollection/CommandServiceExtensions.cs b/CoreAgent.Infrastructure/Extensions/ServiceCollection/CommandServiceExtensions.cs new file mode 100644 index 0000000..a6bfb6a --- /dev/null +++ b/CoreAgent.Infrastructure/Extensions/ServiceCollection/CommandServiceExtensions.cs @@ -0,0 +1,35 @@ +using CoreAgent.Domain.Interfaces.Network; +using CoreAgent.Domain.Interfaces.System.Command; +using CoreAgent.Infrastructure.Command.Factories; +using CoreAgent.Infrastructure.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace CoreAgent.Infrastructure.Extensions.ServiceCollection; + +/// +/// 命令服务扩展方法 +/// +public static class CommandServiceExtensions +{ + /// + /// 添加命令策略服务 + /// + /// 服务集合 + /// 服务集合 + public static IServiceCollection AddCommandCustomService(this IServiceCollection services) + { + // 注册命令执行器工厂 + services.AddSingleton(); + + // 注册命令执行器 + services.AddTransient(sp => + { + var factory = sp.GetRequiredService(); + return factory.CreateExecutor(); + }); + + // 注册 ICellularNetworkService + services.AddScoped(); + return services; + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Extensions/ServiceCollectionExtensions.cs b/CoreAgent.Infrastructure/Extensions/ServiceCollectionExtensions.cs deleted file mode 100644 index 27b6771..0000000 --- a/CoreAgent.Infrastructure/Extensions/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using CoreAgent.Domain.Interfaces; -using CoreAgent.Infrastructure.Command; -using CoreAgent.Infrastructure.Services; -using Microsoft.Extensions.DependencyInjection; - -namespace CoreAgent.Infrastructure.Extensions; - -/// -/// 服务集合扩展方法 -/// -public static class ServiceCollectionExtensions -{ - /// - /// 添加命令策略服务 - /// - /// 服务集合 - /// 服务集合 - public static IServiceCollection AddCommandCustomService(this IServiceCollection services) - { - // 注册命令策略工厂 - services.AddSingleton(); - - // 注册 ICellularNetworkService - services.AddScoped(); - return services; - } -} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Middleware/ExceptionMiddleware.cs b/CoreAgent.Infrastructure/Middleware/GlobalException/ExceptionMiddleware.cs similarity index 85% rename from CoreAgent.Infrastructure/Middleware/ExceptionMiddleware.cs rename to CoreAgent.Infrastructure/Middleware/GlobalException/ExceptionMiddleware.cs index c858abf..358e2f1 100644 --- a/CoreAgent.Infrastructure/Middleware/ExceptionMiddleware.cs +++ b/CoreAgent.Infrastructure/Middleware/GlobalException/ExceptionMiddleware.cs @@ -1,18 +1,19 @@ using CoreAgent.Domain.Exceptions; using CoreAgent.Domain.Models; +using CoreAgent.Domain.Models.Common; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using System.Net; using System.Text.Json; -namespace CoreAgent.Infrastructure.Middleware +namespace CoreAgent.Infrastructure.Middleware.GlobalException { - public class ExceptionMiddleware + public class GlobalExceptionMiddleware { private readonly RequestDelegate _next; - private readonly ILogger _logger; + private readonly ILogger _logger; - public ExceptionMiddleware(RequestDelegate next, ILogger logger) + public GlobalExceptionMiddleware(RequestDelegate next, ILogger logger) { _next = next; _logger = logger; @@ -24,13 +25,14 @@ namespace CoreAgent.Infrastructure.Middleware { await _next(context); } - catch (Exception ex) + catch (System.Exception ex) { await HandleExceptionAsync(context, ex); } + } - private async Task HandleExceptionAsync(HttpContext context, Exception exception) + private async Task HandleExceptionAsync(HttpContext context, System.Exception exception) { var response = context.Response; response.ContentType = "application/json"; @@ -72,4 +74,4 @@ namespace CoreAgent.Infrastructure.Middleware await response.WriteAsync(result); } } -} \ No newline at end of file +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Middleware/GlobalException/ExceptionMiddlewareExtensions.cs b/CoreAgent.Infrastructure/Middleware/GlobalException/ExceptionMiddlewareExtensions.cs new file mode 100644 index 0000000..58ef32e --- /dev/null +++ b/CoreAgent.Infrastructure/Middleware/GlobalException/ExceptionMiddlewareExtensions.cs @@ -0,0 +1,20 @@ +using CoreAgent.Infrastructure.Middleware.GlobalException; +using Microsoft.AspNetCore.Builder; + +namespace CoreAgent.Infrastructure.Middleware.GlobalException; + +/// +/// 全局异常中间件扩展方法 +/// +public static class GlobalExceptionMiddlewareExtensions +{ + /// + /// 使用全局异常处理中间件 + /// + /// 应用程序构建器 + /// 应用程序构建器 + public static IApplicationBuilder UseGlobalExceptionMiddleware(this IApplicationBuilder app) + { + return app.UseMiddleware(); + } +} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Middleware/RequestLoggingMiddleware.cs b/CoreAgent.Infrastructure/Middleware/RequestLoggingMiddleware.cs deleted file mode 100644 index 54f8328..0000000 --- a/CoreAgent.Infrastructure/Middleware/RequestLoggingMiddleware.cs +++ /dev/null @@ -1,110 +0,0 @@ -using CoreAgent.Infrastructure.Options; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System.Diagnostics; -using System.Text; - -namespace CoreAgent.Infrastructure.Middleware -{ - public class RequestLoggingMiddleware - { - private readonly RequestDelegate _next; - private readonly ILogger _logger; - private readonly RequestLoggingOptions _options; - - public RequestLoggingMiddleware( - RequestDelegate next, - ILogger logger, - IOptions options) - { - _next = next; - _logger = logger; - _options = options.Value; - } - - public async Task InvokeAsync(HttpContext context) - { - if (ShouldSkipLogging(context)) - { - await _next(context); - return; - } - - var sw = Stopwatch.StartNew(); - var requestBody = string.Empty; - - try - { - if (_options.LogRequestBody && context.Request.Body != null) - { - requestBody = await ReadRequestBodyAsync(context.Request); - } - - _logger.LogInformation( - "Request started: {Method} {Path} {QueryString} {RequestBody}", - context.Request.Method, - context.Request.Path, - context.Request.QueryString, - requestBody); - - await _next(context); - } - finally - { - sw.Stop(); - _logger.LogInformation( - "Request completed: {Method} {Path} in {ElapsedMilliseconds}ms with status code {StatusCode}", - context.Request.Method, - context.Request.Path, - sw.ElapsedMilliseconds, - context.Response.StatusCode); - } - } - - private bool ShouldSkipLogging(HttpContext context) - { - if (_options.ExcludePaths.Any(p => context.Request.Path.StartsWithSegments(p))) - { - return true; - } - - if (_options.ExcludeMethods.Contains(context.Request.Method)) - { - return true; - } - - return false; - } - - private async Task ReadRequestBodyAsync(HttpRequest request) - { - if (!request.Body.CanRead || !request.Body.CanSeek) - { - return string.Empty; - } - - var originalPosition = request.Body.Position; - request.Body.Position = 0; - - try - { - using var reader = new StreamReader( - request.Body, - encoding: Encoding.UTF8, - detectEncodingFromByteOrderMarks: false, - bufferSize: 1024 * 45, - leaveOpen: true); - - var body = await reader.ReadToEndAsync(); - return body.Length > _options.MaxRequestBodySize - ? body.Substring(0, _options.MaxRequestBodySize) + "..." - : body; - } - finally - { - request.Body.Position = originalPosition; - } - } - } -} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Middleware/ResponseLoggingMiddleware.cs b/CoreAgent.Infrastructure/Middleware/ResponseLoggingMiddleware.cs deleted file mode 100644 index 196c2e0..0000000 --- a/CoreAgent.Infrastructure/Middleware/ResponseLoggingMiddleware.cs +++ /dev/null @@ -1,103 +0,0 @@ -using CoreAgent.Infrastructure.Options; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System.Text; - -namespace CoreAgent.Infrastructure.Middleware -{ - public class ResponseLoggingMiddleware - { - private readonly RequestDelegate _next; - private readonly ILogger _logger; - private readonly RequestLoggingOptions _options; - - public ResponseLoggingMiddleware( - RequestDelegate next, - ILogger logger, - IOptions options) - { - _next = next; - _logger = logger; - _options = options.Value; - } - - public async Task InvokeAsync(HttpContext context) - { - if (ShouldSkipLogging(context)) - { - await _next(context); - return; - } - - // 保存原始响应流 - var originalBodyStream = context.Response.Body; - - try - { - // 创建一个新的内存流来捕获响应 - using var responseBody = new MemoryStream(); - context.Response.Body = responseBody; - - // 继续处理请求 - await _next(context); - - // 读取响应内容 - responseBody.Seek(0, SeekOrigin.Begin); - var response = await ReadResponseBodyAsync(responseBody); - - // 记录响应 - _logger.LogInformation( - "Response for {Method} {Path}: {StatusCode} - {Response}", - context.Request.Method, - context.Request.Path, - context.Response.StatusCode, - response); - - // 将响应写回原始流 - responseBody.Seek(0, SeekOrigin.Begin); - await responseBody.CopyToAsync(originalBodyStream); - } - finally - { - // 确保响应体被恢复 - context.Response.Body = originalBodyStream; - } - } - - private bool ShouldSkipLogging(HttpContext context) - { - if (!_options.LogResponseBody) - { - return true; - } - - if (_options.ExcludePaths.Any(p => context.Request.Path.StartsWithSegments(p))) - { - return true; - } - - if (_options.ExcludeMethods.Contains(context.Request.Method)) - { - return true; - } - - return false; - } - - private async Task ReadResponseBodyAsync(Stream responseBody) - { - using var reader = new StreamReader( - responseBody, - encoding: Encoding.UTF8, - detectEncodingFromByteOrderMarks: false, - bufferSize: 1024 * 45, - leaveOpen: true); - - var body = await reader.ReadToEndAsync(); - return body.Length > _options.MaxResponseBodySize - ? body.Substring(0, _options.MaxResponseBodySize) + "..." - : body; - } - } -} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Network/IPAddressUtils.cs b/CoreAgent.Infrastructure/Network/IPAddressUtils.cs deleted file mode 100644 index cb0b2d4..0000000 --- a/CoreAgent.Infrastructure/Network/IPAddressUtils.cs +++ /dev/null @@ -1,168 +0,0 @@ -using System.Numerics; -using CoreAgent.Domain.Interfaces; - -namespace CoreAgent.Infrastructure.Network; - -/// -/// 提供IP地址相关的工具方法 -/// -public static class IPAddressUtils -{ - /// - /// 检查两个IPv4地址是否在同一个子网内 - /// - /// 第一个IP地址 - /// 第二个IP地址 - /// 子网掩码 - /// 如果两个IP地址在同一个子网内返回true,否则返回false - /// 当IP地址或子网掩码不是IPv4格式时抛出 - public static bool IsSameIPv4Subnet(System.Net.IPAddress ip1, System.Net.IPAddress ip2, System.Net.IPAddress subnetMask) - { - if (ip1.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork || - ip2.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork || - subnetMask.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) - { - throw new ArgumentException("IP addresses and subnet mask must be IPv4."); - } - - byte[] ip1Bytes = ip1.GetAddressBytes(); - byte[] ip2Bytes = ip2.GetAddressBytes(); - byte[] subnetBytes = subnetMask.GetAddressBytes(); - - byte[] network1 = new byte[4]; - byte[] network2 = new byte[4]; - - for (int i = 0; i < 4; i++) - { - network1[i] = (byte)(ip1Bytes[i] & subnetBytes[i]); - network2[i] = (byte)(ip2Bytes[i] & subnetBytes[i]); - } - - return new System.Net.IPAddress(network1).Equals(new System.Net.IPAddress(network2)); - } - - /// - /// 检查两个IPv6地址是否在同一个子网内 - /// - /// 第一个IP地址 - /// 第二个IP地址 - /// 前缀长度 - /// 如果两个IP地址在同一个子网内返回true,否则返回false - /// 当IP地址不是IPv6格式时抛出 - public static bool IsSameIPv6Subnet(System.Net.IPAddress ip1, System.Net.IPAddress ip2, int prefixLength) - { - if (ip1.AddressFamily != System.Net.Sockets.AddressFamily.InterNetworkV6 || - ip2.AddressFamily != System.Net.Sockets.AddressFamily.InterNetworkV6) - { - throw new ArgumentException("IP addresses must be IPv6."); - } - - BigInteger ip1Value = new BigInteger(ip1.GetAddressBytes()); - BigInteger ip2Value = new BigInteger(ip2.GetAddressBytes()); - - BigInteger mask = BigInteger.One << (128 - prefixLength); - mask -= BigInteger.One; - mask = ~mask; - - return (ip1Value & mask) == (ip2Value & mask); - } - - /// - /// 获取系统的IPv4地址列表 - /// - /// 命令策略工厂 - /// 取消令牌源 - /// 日志记录器 - /// IPv4地址列表 - public static async Task> CheckAddressIPv4(ICommandStrategyFactory commandStrategyFactory, CancellationTokenSource source) - { - List addrs = new List(); - var inet4result = await commandStrategyFactory.CreateStrategy().ExecuteBufferedAsync("ifconfig | grep 'inet ' | awk '{print $2}'", source); - if (!string.IsNullOrWhiteSpace(inet4result)) - addrs.AddRange(RemoveNewLines(inet4result)); - return addrs; - } - - /// - /// 获取系统的IPv6地址列表 - /// - /// 命令策略工厂 - /// 取消令牌源 - /// 日志记录器 - /// IPv6地址列表 - public static async Task> CheckAddressIPv6(ICommandStrategyFactory commandStrategyFactory, CancellationTokenSource source) - { - List addrs = new List(); - var inet6result = await commandStrategyFactory.CreateStrategy().ExecuteBufferedAsync("ifconfig | grep 'inet6 ' | awk '{print $2}'", source); - if (!string.IsNullOrWhiteSpace(inet6result)) - addrs.AddRange(RemoveNewLines(inet6result)); - return addrs; - } - - /// - /// 获取系统的IPv4地址和子网掩码列表 - /// - /// 命令策略工厂 - /// 取消令牌源 - /// 日志记录器 - /// IPv4地址和子网掩码的元组列表 - public static async Task> CheckAddressIPv4Mask(ICommandStrategyFactory commandStrategyFactory, CancellationTokenSource source) - { - List<(string ipv4, string mask)> addrs = new List<(string ipv4, string mask)>(); - var inet4result = await commandStrategyFactory.CreateStrategy().ExecuteBufferedAsync("ifconfig | grep 'inet ' | awk '{print $2\\\"|\\\"$4}'", source); - if (!string.IsNullOrWhiteSpace(inet4result)) - addrs.AddRange(RemoveNewLines(inet4result).Select(s => (s.Split("|")[0], s.Split("|")[1]))); - return addrs; - } - - /// - /// 获取系统的IPv6地址和前缀长度列表 - /// - /// 命令策略工厂 - /// 取消令牌源 - /// 日志记录器 - /// IPv6地址和前缀长度的元组列表 - public static async Task> CheckAddressIPv6Prefix(ICommandStrategyFactory commandStrategyFactory, CancellationTokenSource source) - { - List<(string ipv6, string Prefix)> addrs = new List<(string ipv6, string Prefix)>(); - var inet6result = await commandStrategyFactory.CreateStrategy().ExecuteBufferedAsync("ifconfig | grep 'inet6 ' | awk '{print $2\\\"|\\\"$4}'", source); - if (!string.IsNullOrWhiteSpace(inet6result)) - addrs.AddRange(RemoveNewLines(inet6result).Select(s => (s.Split("|")[0], s.Split("|")[1]))); - return addrs; - } - - /// - /// 计算两个IP地址的匹配位数 - /// - /// 第一个IP地址 - /// 第二个IP地址 - /// 匹配的位数 - public static int GetMatchingBits(string ip1, string ip2) - { - byte[] bytes1 = System.Net.IPAddress.Parse(ip1).GetAddressBytes(); - byte[] bytes2 = System.Net.IPAddress.Parse(ip2).GetAddressBytes(); - int matchingBits = 0; - for (int i = 0; i < bytes1.Length; i++) - { - int diff = bytes1[i] ^ bytes2[i]; // 异或计算不同位 - for (int bit = 7; bit >= 0; bit--) - { - if ((diff & (1 << bit)) == 0) - matchingBits++; - else - return matchingBits; - } - } - return matchingBits; - } - - /// - /// 移除字符串中的换行符并分割成数组 - /// - /// 输入字符串 - /// 分割后的字符串数组 - private static string[] RemoveNewLines(string input) - { - return input.Split("\n", StringSplitOptions.RemoveEmptyEntries); - } -} \ No newline at end of file diff --git a/CoreAgent.Infrastructure/Services/CellularNetworkService.cs b/CoreAgent.Infrastructure/Services/CellularNetworkService.cs index c7381b8..9019e34 100644 --- a/CoreAgent.Infrastructure/Services/CellularNetworkService.cs +++ b/CoreAgent.Infrastructure/Services/CellularNetworkService.cs @@ -1,5 +1,8 @@ -using CoreAgent.Domain.Interfaces; -using CoreAgent.Domain.Models; +using CoreAgent.Domain.Interfaces.Network; +using CoreAgent.Domain.Interfaces.System.Command; +using CoreAgent.Domain.Models.Network; +using CoreAgent.Domain.Models.System; +using CoreAgent.Infrastructure.Command.Factories; using Microsoft.Extensions.Logging; using System.Diagnostics; @@ -11,32 +14,89 @@ namespace CoreAgent.Infrastructure.Services; public class CellularNetworkService : ICellularNetworkService { private readonly ILogger _logger; + private readonly CellularNetworkGlobalStatus _globalStatus; + private readonly ISystemCommandExecutorFactory _commandExecutorFactory; + private static readonly SemaphoreSlim _startLock = new SemaphoreSlim(1, 1); - public CellularNetworkService(ILogger logger) + public CellularNetworkService( + ILogger logger, + ISystemCommandExecutorFactory commandExecutorFactory) { _logger = logger; + _commandExecutorFactory = commandExecutorFactory; + _globalStatus = new CellularNetworkGlobalStatus + { + IsInitialized = false, + CurrentStatus = NetworkStatus.Unknown, + CurrentSignalStrength = SignalStrength.NoSignal, + CurrentNetworkType = NetworkType.Unknown, + CurrentTransmitPower = 0 + }; } public async Task StartAsync(string interfaceName, CellularNetworkConfig config) { try { - _logger.LogInformation("正在启动蜂窝网络接口: {InterfaceName}", interfaceName); + // 使用信号量确保只能启动一次 + if (!await _startLock.WaitAsync(TimeSpan.FromSeconds(5))) + { + _logger.LogWarning("蜂窝网络启动操作被锁定,可能已有其他启动操作正在进行"); + return false; + } - // 启用网络接口 - var enableCommand = $"netsh interface set interface \"{interfaceName}\" admin=ENABLED"; - await ExecuteCommandAsync(enableCommand); + try + { + if (_globalStatus.IsInitialized) + { + _logger.LogWarning("蜂窝网络已经初始化,不能重复启动"); + return false; + } - // 配置网络接口 - var configCommand = $"netsh interface cellular set profile name=\"{config.Apn}\" interface=\"{interfaceName}\""; - await ExecuteCommandAsync(configCommand); + _logger.LogInformation("正在启动蜂窝网络接口: {InterfaceName}", interfaceName); - _logger.LogInformation("蜂窝网络接口启动成功: {InterfaceName}", interfaceName); - return true; + // 1. 配置网络参数 + var configResult = await ConfigureNetworkAsync(interfaceName, config); + if (!configResult) + { + _logger.LogError("配置蜂窝网络参数失败"); + return false; + } + + // 2. 启动网络接口 + var startResult = await ExecuteCommandAsync($"netsh interface cellular set interface \"{interfaceName}\" admin=enable"); + if (!startResult) + { + _logger.LogError("启动蜂窝网络接口失败"); + return false; + } + + // 3. 等待网络连接 + var connected = await WaitForConnectionAsync(interfaceName); + if (!connected) + { + _logger.LogError("蜂窝网络连接超时"); + return false; + } + + // 4. 更新全局状态 + _globalStatus.IsInitialized = true; + _globalStatus.LastStartTime = DateTime.Now; + _globalStatus.CurrentStatus = NetworkStatus.Connected; + _globalStatus.CurrentSignalStrength = await GetSignalStrengthAsync(interfaceName); + _globalStatus.CurrentNetworkType = await GetNetworkTypeAsync(interfaceName); + + _logger.LogInformation("蜂窝网络接口 {InterfaceName} 启动成功", interfaceName); + return true; + } + finally + { + _startLock.Release(); + } } catch (Exception ex) { - _logger.LogError(ex, "启动蜂窝网络接口失败: {InterfaceName}", interfaceName); + _logger.LogError(ex, "启动蜂窝网络接口 {InterfaceName} 失败", interfaceName); return false; } } @@ -45,110 +105,276 @@ public class CellularNetworkService : ICellularNetworkService { try { + if (!_globalStatus.IsInitialized) + { + _logger.LogWarning("蜂窝网络未初始化,无需停止"); + return true; + } + _logger.LogInformation("正在停止蜂窝网络接口: {InterfaceName}", interfaceName); - var command = $"netsh interface set interface \"{interfaceName}\" admin=DISABLED"; - await ExecuteCommandAsync(command); + // 1. 停止网络接口 + var stopResult = await ExecuteCommandAsync($"netsh interface cellular set interface \"{interfaceName}\" admin=disable"); + if (!stopResult) + { + _logger.LogError("停止蜂窝网络接口失败"); + return false; + } + + // 2. 更新全局状态 + _globalStatus.IsInitialized = false; + _globalStatus.LastStopTime = DateTime.Now; + _globalStatus.CurrentStatus = NetworkStatus.Disconnected; + _globalStatus.CurrentSignalStrength = SignalStrength.NoSignal; + _globalStatus.CurrentNetworkType = NetworkType.Unknown; + _globalStatus.CurrentTransmitPower = 0; - _logger.LogInformation("蜂窝网络接口停止成功: {InterfaceName}", interfaceName); + _logger.LogInformation("蜂窝网络接口 {InterfaceName} 停止成功", interfaceName); return true; } catch (Exception ex) { - _logger.LogError(ex, "停止蜂窝网络接口失败: {InterfaceName}", interfaceName); + _logger.LogError(ex, "停止蜂窝网络接口 {InterfaceName} 失败", interfaceName); return false; } } - public async Task GetStatusAsync(string interfaceName) + public async Task GetNetworkStatusAsync(string interfaceName) { try { - _logger.LogInformation("正在获取蜂窝网络接口状态: {InterfaceName}", interfaceName); + _logger.LogDebug("正在获取蜂窝网络状态: {InterfaceName}", interfaceName); + var result = await ExecuteCommandWithResultAsync($"netsh interface cellular show interfaces \"{interfaceName}\""); - // 获取接口状态 - var statusCommand = $"netsh interface show interface \"{interfaceName}\""; - var statusResult = await ExecuteCommandAsync(statusCommand); + if (!result.IsSuccess) + { + _logger.LogError("获取蜂窝网络状态失败: {Error}", result.Error); + return NetworkStatus.Unknown; + } - // 获取信号强度 - var signalCommand = $"netsh interface cellular show interfaces \"{interfaceName}\""; - var signalResult = await ExecuteCommandAsync(signalCommand); + var output = result.Output; + if (output.Contains("已连接", StringComparison.OrdinalIgnoreCase)) + { + return NetworkStatus.Connected; + } + else if (output.Contains("已断开", StringComparison.OrdinalIgnoreCase)) + { + return NetworkStatus.Disconnected; + } + + return NetworkStatus.Unknown; + } + catch (Exception ex) + { + _logger.LogError(ex, "获取蜂窝网络状态失败: {InterfaceName}", interfaceName); + return NetworkStatus.Unknown; + } + } - var status = new CellularNetworkStatus + public async Task GetSignalStrengthAsync(string interfaceName) + { + try + { + _logger.LogDebug("正在获取蜂窝网络信号强度: {InterfaceName}", interfaceName); + var result = await ExecuteCommandWithResultAsync($"netsh interface cellular show interfaces \"{interfaceName}\""); + + if (!result.IsSuccess) { - IsEnabled = statusResult.Contains("已启用", StringComparison.OrdinalIgnoreCase), - ConnectionState = ParseConnectionState(statusResult), - SignalStrength = ParseSignalStrength(signalResult), - CarrierName = ParseCarrierName(signalResult) - }; + _logger.LogError("获取蜂窝网络信号强度失败: {Error}", result.Error); + return SignalStrength.NoSignal; + } - _logger.LogInformation("获取蜂窝网络接口状态成功: {InterfaceName}", interfaceName); - return status; + var output = result.Output; + if (output.Contains("信号强度: 强", StringComparison.OrdinalIgnoreCase)) + { + return SignalStrength.Strong; + } + else if (output.Contains("信号强度: 中", StringComparison.OrdinalIgnoreCase)) + { + return SignalStrength.Medium; + } + else if (output.Contains("信号强度: 弱", StringComparison.OrdinalIgnoreCase)) + { + return SignalStrength.Weak; + } + + return SignalStrength.NoSignal; } catch (Exception ex) { - _logger.LogError(ex, "获取蜂窝网络接口状态失败: {InterfaceName}", interfaceName); - throw; + _logger.LogError(ex, "获取蜂窝网络信号强度失败: {InterfaceName}", interfaceName); + return SignalStrength.NoSignal; } } - private async Task ExecuteCommandAsync(string command) + public async Task GetNetworkTypeAsync(string interfaceName) { - var process = new Process + try { - StartInfo = new ProcessStartInfo + _logger.LogDebug("正在获取蜂窝网络类型: {InterfaceName}", interfaceName); + var result = await ExecuteCommandWithResultAsync($"netsh interface cellular show interfaces \"{interfaceName}\""); + + if (!result.IsSuccess) { - FileName = "cmd.exe", - Arguments = $"/c {command}", - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = true + _logger.LogError("获取蜂窝网络类型失败: {Error}", result.Error); + return NetworkType.Unknown; + } + + var output = result.Output; + if (output.Contains("5G", StringComparison.OrdinalIgnoreCase)) + { + return NetworkType.G5; + } + else if (output.Contains("4G", StringComparison.OrdinalIgnoreCase)) + { + return NetworkType.G4; + } + else if (output.Contains("3G", StringComparison.OrdinalIgnoreCase)) + { + return NetworkType.G3; + } + else if (output.Contains("2G", StringComparison.OrdinalIgnoreCase)) + { + return NetworkType.G2; + } + + return NetworkType.Unknown; + } + catch (Exception ex) + { + _logger.LogError(ex, "获取蜂窝网络类型失败: {InterfaceName}", interfaceName); + return NetworkType.Unknown; + } + } + + public async Task SetTransmitPowerAsync(string interfaceName, int powerLevel) + { + try + { + if (!_globalStatus.IsInitialized) + { + _logger.LogWarning("蜂窝网络未初始化,无法设置发射功率"); + return false; + } + + if (powerLevel < 0 || powerLevel > 100) + { + _logger.LogWarning("发射功率设置无效: {PowerLevel}", powerLevel); + return false; } - }; - process.Start(); - var output = await process.StandardOutput.ReadToEndAsync(); - var error = await process.StandardError.ReadToEndAsync(); - await process.WaitForExitAsync(); + _logger.LogInformation("正在设置蜂窝网络发射功率: {InterfaceName}, {PowerLevel}", interfaceName, powerLevel); - if (process.ExitCode != 0) + // 设置发射功率 + var result = await ExecuteCommandAsync($"netsh interface cellular set interface \"{interfaceName}\" power={powerLevel}"); + if (!result) + { + _logger.LogError("设置蜂窝网络发射功率失败"); + return false; + } + + _globalStatus.CurrentTransmitPower = powerLevel; + _logger.LogInformation("蜂窝网络发射功率设置成功: {PowerLevel}", powerLevel); + return true; + } + catch (Exception ex) { - throw new Exception($"命令执行失败: {error}"); + _logger.LogError(ex, "设置蜂窝网络发射功率失败: {InterfaceName}, {PowerLevel}", interfaceName, powerLevel); + return false; } + } - return output; + public Task GetGlobalStatusAsync() + { + return Task.FromResult(_globalStatus); } - private string ParseConnectionState(string statusResult) + private async Task ConfigureNetworkAsync(string interfaceName, CellularNetworkConfig config) { - if (statusResult.Contains("已连接", StringComparison.OrdinalIgnoreCase)) - return "已连接"; - if (statusResult.Contains("已断开", StringComparison.OrdinalIgnoreCase)) - return "已断开"; - return "未知"; + try + { + _logger.LogDebug("正在配置蜂窝网络参数: {InterfaceName}", interfaceName); + + // 配置APN + var apnResult = await ExecuteCommandAsync($"netsh interface cellular set interface \"{interfaceName}\" apn=\"{config.Apn}\""); + if (!apnResult) + { + return false; + } + + // 配置用户名和密码 + if (!string.IsNullOrEmpty(config.Username) && !string.IsNullOrEmpty(config.Password)) + { + var authResult = await ExecuteCommandAsync($"netsh interface cellular set interface \"{interfaceName}\" username=\"{config.Username}\" password=\"{config.Password}\""); + if (!authResult) + { + return false; + } + } + + return true; + } + catch (Exception ex) + { + _logger.LogError(ex, "配置蜂窝网络参数失败: {InterfaceName}", interfaceName); + return false; + } } - private int ParseSignalStrength(string signalResult) + private async Task WaitForConnectionAsync(string interfaceName) { - // 这里需要根据实际输出格式进行解析 - // 示例实现,实际使用时需要根据具体输出格式调整 - if (signalResult.Contains("信号强度: 强", StringComparison.OrdinalIgnoreCase)) - return 4; - if (signalResult.Contains("信号强度: 中", StringComparison.OrdinalIgnoreCase)) - return 3; - if (signalResult.Contains("信号强度: 弱", StringComparison.OrdinalIgnoreCase)) - return 2; - return 1; + const int maxAttempts = 30; + const int delayMs = 1000; + + for (int i = 0; i < maxAttempts; i++) + { + var status = await GetNetworkStatusAsync(interfaceName); + if (status == NetworkStatus.Connected) + { + return true; + } + + await Task.Delay(delayMs); + } + + return false; } - private string ParseCarrierName(string signalResult) + private async Task ExecuteCommandAsync(string command) { - // 这里需要根据实际输出格式进行解析 - // 示例实现,实际使用时需要根据具体输出格式调整 - var carrierLine = signalResult.Split('\n') - .FirstOrDefault(line => line.Contains("运营商:", StringComparison.OrdinalIgnoreCase)); - - return carrierLine?.Split(':').LastOrDefault()?.Trim() ?? "未知"; + try + { + var executor = _commandExecutorFactory.CreateExecutor(); + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + var result = await executor.ExecuteCommandAsync(command, cts); + + if (!result.IsSuccess) + { + _logger.LogError("命令执行失败: {Command}, 错误: {Error}", command, result.Error); + return false; + } + + return true; + } + catch (Exception ex) + { + _logger.LogError(ex, "执行命令失败: {Command}", command); + return false; + } + } + + private async Task ExecuteCommandWithResultAsync(string command) + { + try + { + var executor = _commandExecutorFactory.CreateExecutor(); + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + return await executor.ExecuteCommandAsync(command, cts); + } + catch (Exception ex) + { + _logger.LogError(ex, "执行命令失败: {Command}", command); + return CommandExecutionResult.Failure(ex.Message, 0); + } } } \ No newline at end of file