Browse Source

重构:优化项目结构和代码组织

master
hyh 6 days ago
parent
commit
b1266616d7
  1. 46
      CoreAgent.API/Controllers/BaseApiController.cs
  2. 10
      CoreAgent.API/Startup.cs
  3. 49
      CoreAgent.API/logs/log-20250611.txt
  4. 2
      CoreAgent.Application/Commands/CellularNetwork/StartCellularNetworkCommand.cs
  5. 33
      CoreAgent.Application/Commands/CellularNetwork/StartCellularNetworkCommandHandler.cs
  6. 1
      CoreAgent.Application/Handlers/CellularNetwork/GetCellularNetworkStatusCommandHandler.cs
  7. 13
      CoreAgent.Application/Handlers/CellularNetwork/StartCellularNetworkCommandHandler.cs
  8. 13
      CoreAgent.Application/Handlers/CellularNetwork/StopCellularNetworkCommandHandler.cs
  9. 31
      CoreAgent.Domain/Interfaces/ICellularNetworkService.cs
  10. 28
      CoreAgent.Domain/Interfaces/ICommandStrategyFactory.cs
  11. 178
      CoreAgent.Domain/Interfaces/Network/ICellularNetworkService.cs
  12. 127
      CoreAgent.Domain/Interfaces/Network/INetworkManager.cs
  13. 26
      CoreAgent.Domain/Interfaces/System/Command/ISystemCommandExecutor.cs
  14. 15
      CoreAgent.Domain/Interfaces/System/Command/ISystemCommandExecutorFactory.cs
  15. 49
      CoreAgent.Domain/Models/ApiActionResult.cs
  16. 58
      CoreAgent.Domain/Models/ApiResponse.cs
  17. 119
      CoreAgent.Domain/Models/Common/ApiActionResult.cs
  18. 23
      CoreAgent.Domain/Models/Common/ErrorResponse.cs
  19. 23
      CoreAgent.Domain/Models/ErrorResponse.cs
  20. 2
      CoreAgent.Domain/Models/Network/CellularNetworkConfig.cs
  21. 2
      CoreAgent.Domain/Models/Network/CellularNetworkStatus.cs
  22. 61
      CoreAgent.Domain/Models/System/CommandExecutionResult.cs
  23. 57
      CoreAgent.Infrastructure/Command/Base/BaseCommandExecutor.cs
  24. 44
      CoreAgent.Infrastructure/Command/CommandStrategyFactory.cs
  25. 46
      CoreAgent.Infrastructure/Command/Executors/LinuxCommandExecutor.cs
  26. 47
      CoreAgent.Infrastructure/Command/Executors/WindowsCommandExecutor.cs
  27. 38
      CoreAgent.Infrastructure/Command/Factories/SystemCommandExecutorFactory.cs
  28. 123
      CoreAgent.Infrastructure/Command/LinuxCommandStrategy.cs
  29. 123
      CoreAgent.Infrastructure/Command/WindowsCommandStrategy.cs
  30. 13
      CoreAgent.Infrastructure/CoreAgent.Infrastructure.csproj.Backup.tmp
  31. 44
      CoreAgent.Infrastructure/Extensions/ApiVersioningExtensions.cs
  32. 13
      CoreAgent.Infrastructure/Extensions/ExceptionMiddlewareExtensions.cs
  33. 35
      CoreAgent.Infrastructure/Extensions/Logging/LoggingExtensions.cs
  34. 33
      CoreAgent.Infrastructure/Extensions/Logging/RequestLoggingExtensions.cs
  35. 112
      CoreAgent.Infrastructure/Extensions/Logging/RequestLoggingMiddleware.cs
  36. 106
      CoreAgent.Infrastructure/Extensions/Logging/ResponseLoggingMiddleware.cs
  37. 27
      CoreAgent.Infrastructure/Extensions/LoggingExtensions.cs
  38. 24
      CoreAgent.Infrastructure/Extensions/RequestLoggingExtensions.cs
  39. 51
      CoreAgent.Infrastructure/Extensions/ServiceCollection/ApiVersioningServiceExtensions.cs
  40. 35
      CoreAgent.Infrastructure/Extensions/ServiceCollection/CommandServiceExtensions.cs
  41. 27
      CoreAgent.Infrastructure/Extensions/ServiceCollectionExtensions.cs
  42. 16
      CoreAgent.Infrastructure/Middleware/GlobalException/ExceptionMiddleware.cs
  43. 20
      CoreAgent.Infrastructure/Middleware/GlobalException/ExceptionMiddlewareExtensions.cs
  44. 110
      CoreAgent.Infrastructure/Middleware/RequestLoggingMiddleware.cs
  45. 103
      CoreAgent.Infrastructure/Middleware/ResponseLoggingMiddleware.cs
  46. 168
      CoreAgent.Infrastructure/Network/IPAddressUtils.cs
  47. 376
      CoreAgent.Infrastructure/Services/CellularNetworkService.cs

46
CoreAgent.API/Controllers/BaseApiController.cs

@ -1,4 +1,4 @@
using CoreAgent.Domain.Models; using CoreAgent.Domain.Models.Common;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -26,15 +26,11 @@ namespace CoreAgent.API.Controllers
{ {
_logger.LogInformation("Handling request of type {RequestType}", typeof(TRequest).Name); _logger.LogInformation("Handling request of type {RequestType}", typeof(TRequest).Name);
var response = await _mediator.Send(request); var response = await _mediator.Send(request);
return new ApiActionResult<TResponse>(ApiResponse<TResponse>.CreateSuccess(response)); return ApiActionResult<TResponse>.Ok(response);
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Error handling request of type {RequestType}", typeof(TRequest).Name); return HandleException<TResponse>(ex);
return new ApiActionResult<TResponse>(
ApiResponse<TResponse>.CreateError(ex.Message),
HttpStatusCode.InternalServerError
);
} }
} }
@ -45,36 +41,36 @@ namespace CoreAgent.API.Controllers
{ {
_logger.LogInformation("Handling request of type {RequestType}", typeof(TRequest).Name); _logger.LogInformation("Handling request of type {RequestType}", typeof(TRequest).Name);
await _mediator.Send(request); await _mediator.Send(request);
return new ApiActionResult(ApiResponse.CreateSuccess()); return ApiActionResult.Ok();
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Error handling request of type {RequestType}", typeof(TRequest).Name); return HandleException(ex);
return new ApiActionResult(
ApiResponse.CreateError(ex.Message),
HttpStatusCode.InternalServerError
);
} }
} }
protected IActionResult Success<T>(T data, string message = null) private IActionResult HandleException<T>(Exception ex)
{ {
return new ApiActionResult<T>(ApiResponse<T>.CreateSuccess(data, message)); _logger.LogError(ex, "Error handling request");
return ApiActionResult<T>.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<string> errors = null, HttpStatusCode statusCode = HttpStatusCode.BadRequest) protected IActionResult Success<T>(T data, string message = null) =>
{ ApiActionResult<T>.Ok(data, message);
return new ApiActionResult(ApiResponse.CreateError(message, errors), statusCode);
}
protected IActionResult Error<T>(string message, List<string> errors = null, HttpStatusCode statusCode = HttpStatusCode.BadRequest) protected IActionResult Success(string message = null) =>
{ ApiActionResult.Ok(message);
return new ApiActionResult<T>(ApiResponse<T>.CreateError(message, errors), statusCode);
} protected IActionResult Error(string message, string errorCode = "ERROR", HttpStatusCode statusCode = HttpStatusCode.BadRequest) =>
ApiActionResult.Error(message, errorCode, statusCode);
protected IActionResult Error<T>(string message, string errorCode = "ERROR", HttpStatusCode statusCode = HttpStatusCode.BadRequest) =>
ApiActionResult<T>.Error(message, errorCode, statusCode);
} }
} }

10
CoreAgent.API/Startup.cs

@ -1,7 +1,9 @@
using CoreAgent.Infrastructure.Extensions;
using System.Reflection; using System.Reflection;
using Serilog; using Serilog;
using CoreAgent.Infrastructure.Extensions.Logging;
using CoreAgent.Infrastructure.Extensions.ServiceCollection;
using CoreAgent.Infrastructure.Middleware.GlobalException;
namespace CoreAgent.API namespace CoreAgent.API
{ {
@ -87,8 +89,8 @@ namespace CoreAgent.API
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration config) public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration config)
{ {
return services return services
.AddLoggingServices(config) .AddLoggingService(config)
.AddApiVersioningExtension(config) .AddApiVersioningService(config)
.AddRequestLogging(config) .AddRequestLogging(config)
.AddAuthorization() .AddAuthorization()
.AddCommandCustomService() .AddCommandCustomService()
@ -99,7 +101,7 @@ namespace CoreAgent.API
{ {
return app return app
.UseHttpsRedirection() .UseHttpsRedirection()
.UseExceptionMiddleware() .UseGlobalExceptionMiddleware()
.UseAuthorization() .UseAuthorization()
.UseRequestLogging(config) .UseRequestLogging(config)
.UseApiVersioningExtension(); .UseApiVersioningExtension();

49
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.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.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 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

2
CoreAgent.Application/Commands/CellularNetwork/StartCellularNetworkCommand.cs

@ -1,4 +1,4 @@
using CoreAgent.Domain.Models; using CoreAgent.Domain.Models.Network;
using MediatR; using MediatR;
namespace CoreAgent.Application.Commands.CellularNetwork; namespace CoreAgent.Application.Commands.CellularNetwork;

33
CoreAgent.Application/Commands/CellularNetwork/StartCellularNetworkCommandHandler.cs

@ -1,33 +0,0 @@
using CoreAgent.Domain.Interfaces;
using MediatR;
using Microsoft.Extensions.Logging;
namespace CoreAgent.Application.Commands.CellularNetwork;
public class StartCellularNetworkCommandHandler : IRequestHandler<StartCellularNetworkCommand, bool>
{
private readonly ICellularNetworkService _cellularNetworkService;
private readonly ILogger<StartCellularNetworkCommandHandler> _logger;
public StartCellularNetworkCommandHandler(
ICellularNetworkService cellularNetworkService,
ILogger<StartCellularNetworkCommandHandler> logger)
{
_cellularNetworkService = cellularNetworkService;
_logger = logger;
}
public async Task<bool> 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;
}
}
}

1
CoreAgent.Application/Handlers/CellularNetwork/GetCellularNetworkStatusCommandHandler.cs

@ -1,5 +1,6 @@
using CoreAgent.Application.Commands.CellularNetwork; using CoreAgent.Application.Commands.CellularNetwork;
using CoreAgent.Domain.Interfaces; using CoreAgent.Domain.Interfaces;
using CoreAgent.Domain.Interfaces.Network;
using MediatR; using MediatR;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

13
CoreAgent.Application/Handlers/CellularNetwork/StartCellularNetworkCommandHandler.cs

@ -1,5 +1,5 @@
using CoreAgent.Application.Commands.CellularNetwork; using CoreAgent.Application.Commands.CellularNetwork;
using CoreAgent.Domain.Interfaces; using CoreAgent.Domain.Interfaces.Network;
using MediatR; using MediatR;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -26,11 +26,22 @@ public class StartCellularNetworkCommandHandler : IRequestHandler<StartCellularN
try try
{ {
_logger.LogInformation("正在启动蜂窝网络接口: {InterfaceName}", request.InterfaceName); _logger.LogInformation("正在启动蜂窝网络接口: {InterfaceName}", request.InterfaceName);
// 启动网络
var result = await _cellularNetworkService.StartAsync(request.InterfaceName, request.Config); var result = await _cellularNetworkService.StartAsync(request.InterfaceName, request.Config);
if (result) if (result)
{ {
_logger.LogInformation("蜂窝网络接口 {InterfaceName} 启动成功", request.InterfaceName); _logger.LogInformation("蜂窝网络接口 {InterfaceName} 启动成功", request.InterfaceName);
// 获取全局状态
var status = await _cellularNetworkService.GetGlobalStatusAsync();
_logger.LogInformation(
"蜂窝网络状态: 已连接={IsConnected}, 信号强度={SignalStrength}, 网络类型={NetworkType}, 发射功率={TransmitPower}",
status.CurrentStatus == NetworkStatus.Connected,
status.CurrentSignalStrength,
status.CurrentNetworkType,
status.CurrentTransmitPower);
} }
else else
{ {

13
CoreAgent.Application/Handlers/CellularNetwork/StopCellularNetworkCommandHandler.cs

@ -1,5 +1,5 @@
using CoreAgent.Application.Commands.CellularNetwork; using CoreAgent.Application.Commands.CellularNetwork;
using CoreAgent.Domain.Interfaces; using CoreAgent.Domain.Interfaces.Network;
using MediatR; using MediatR;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -26,11 +26,22 @@ public class StopCellularNetworkCommandHandler : IRequestHandler<StopCellularNet
try try
{ {
_logger.LogInformation("正在停止蜂窝网络接口: {InterfaceName}", request.InterfaceName); _logger.LogInformation("正在停止蜂窝网络接口: {InterfaceName}", request.InterfaceName);
// 停止网络
var result = await _cellularNetworkService.StopAsync(request.InterfaceName); var result = await _cellularNetworkService.StopAsync(request.InterfaceName);
if (result) if (result)
{ {
_logger.LogInformation("蜂窝网络接口 {InterfaceName} 停止成功", request.InterfaceName); _logger.LogInformation("蜂窝网络接口 {InterfaceName} 停止成功", request.InterfaceName);
// 获取全局状态
var status = await _cellularNetworkService.GetGlobalStatusAsync();
_logger.LogInformation(
"蜂窝网络状态: 已连接={IsConnected}, 信号强度={SignalStrength}, 网络类型={NetworkType}, 发射功率={TransmitPower}",
status.CurrentStatus == NetworkStatus.Connected,
status.CurrentSignalStrength,
status.CurrentNetworkType,
status.CurrentTransmitPower);
} }
else else
{ {

31
CoreAgent.Domain/Interfaces/ICellularNetworkService.cs

@ -1,31 +0,0 @@
using CoreAgent.Domain.Models;
namespace CoreAgent.Domain.Interfaces;
/// <summary>
/// 蜂窝网络服务接口
/// </summary>
public interface ICellularNetworkService
{
/// <summary>
/// 启动蜂窝网络
/// </summary>
/// <param name="interfaceName">网络接口名称</param>
/// <param name="config">网络配置</param>
/// <returns>操作是否成功</returns>
Task<bool> StartAsync(string interfaceName, CellularNetworkConfig config);
/// <summary>
/// 停止蜂窝网络
/// </summary>
/// <param name="interfaceName">网络接口名称</param>
/// <returns>操作是否成功</returns>
Task<bool> StopAsync(string interfaceName);
/// <summary>
/// 获取蜂窝网络状态
/// </summary>
/// <param name="interfaceName">网络接口名称</param>
/// <returns>网络状态信息</returns>
Task<CellularNetworkStatus> GetStatusAsync(string interfaceName);
}

28
CoreAgent.Domain/Interfaces/ICommandStrategyFactory.cs

@ -1,28 +0,0 @@
namespace CoreAgent.Domain.Interfaces;
/// <summary>
/// 命令策略工厂接口
/// </summary>
public interface ICommandStrategyFactory
{
/// <summary>
/// 创建命令策略
/// </summary>
/// <returns>命令策略实例</returns>
ICommandStrategy CreateStrategy();
}
/// <summary>
/// 命令策略接口
/// </summary>
public interface ICommandStrategy
{
/// <summary>
/// 执行命令并返回缓冲结果
/// </summary>
/// <param name="command">要执行的命令</param>
/// <param name="source">取消令牌源</param>
/// <param name="logger">日志记录器</param>
/// <returns>命令执行结果</returns>
Task<string> ExecuteBufferedAsync(string command, CancellationTokenSource source);
}

178
CoreAgent.Domain/Interfaces/Network/ICellularNetworkService.cs

@ -0,0 +1,178 @@
using CoreAgent.Domain.Models.Network;
namespace CoreAgent.Domain.Interfaces.Network;
/// <summary>
/// 蜂窝网络服务接口
/// </summary>
public interface ICellularNetworkService
{
/// <summary>
/// 启动蜂窝网络
/// </summary>
/// <param name="interfaceName">网络接口名称</param>
/// <param name="config">网络配置</param>
/// <returns>启动结果</returns>
Task<bool> StartAsync(string interfaceName, CellularNetworkConfig config);
/// <summary>
/// 停止蜂窝网络
/// </summary>
/// <param name="interfaceName">网络接口名称</param>
/// <returns>停止结果</returns>
Task<bool> StopAsync(string interfaceName);
/// <summary>
/// 获取网络状态
/// </summary>
/// <param name="interfaceName">网络接口名称</param>
/// <returns>网络状态信息</returns>
Task<NetworkStatus> GetNetworkStatusAsync(string interfaceName);
/// <summary>
/// 获取信号强度
/// </summary>
/// <param name="interfaceName">网络接口名称</param>
/// <returns>信号强度信息</returns>
Task<SignalStrength> GetSignalStrengthAsync(string interfaceName);
/// <summary>
/// 获取网络类型
/// </summary>
/// <param name="interfaceName">网络接口名称</param>
/// <returns>网络类型信息</returns>
Task<NetworkType> GetNetworkTypeAsync(string interfaceName);
/// <summary>
/// 设置发射功率
/// </summary>
/// <param name="interfaceName">网络接口名称</param>
/// <param name="powerLevel">功率等级(0-100)</param>
/// <returns>设置结果</returns>
Task<bool> SetTransmitPowerAsync(string interfaceName, int powerLevel);
/// <summary>
/// 获取全局状态
/// </summary>
/// <returns>全局状态信息</returns>
Task<CellularNetworkGlobalStatus> GetGlobalStatusAsync();
}
/// <summary>
/// 网络状态
/// </summary>
public enum NetworkStatus
{
/// <summary>
/// 未知
/// </summary>
Unknown,
/// <summary>
/// 已连接
/// </summary>
Connected,
/// <summary>
/// 未连接
/// </summary>
Disconnected
}
/// <summary>
/// 信号强度
/// </summary>
public enum SignalStrength
{
/// <summary>
/// 无信号
/// </summary>
NoSignal,
/// <summary>
/// 弱信号
/// </summary>
Weak,
/// <summary>
/// 中等信号
/// </summary>
Medium,
/// <summary>
/// 强信号
/// </summary>
Strong
}
/// <summary>
/// 网络类型
/// </summary>
public enum NetworkType
{
/// <summary>
/// 未知
/// </summary>
Unknown,
/// <summary>
/// 2G网络
/// </summary>
G2,
/// <summary>
/// 3G网络
/// </summary>
G3,
/// <summary>
/// 4G网络
/// </summary>
G4,
/// <summary>
/// 5G网络
/// </summary>
G5
}
/// <summary>
/// 蜂窝网络全局状态
/// </summary>
public class CellularNetworkGlobalStatus
{
/// <summary>
/// 是否已初始化
/// </summary>
public bool IsInitialized { get; set; }
/// <summary>
/// 最后启动时间
/// </summary>
public DateTime? LastStartTime { get; set; }
/// <summary>
/// 最后停止时间
/// </summary>
public DateTime? LastStopTime { get; set; }
/// <summary>
/// 当前运行状态
/// </summary>
public NetworkStatus CurrentStatus { get; set; }
/// <summary>
/// 当前信号强度
/// </summary>
public SignalStrength CurrentSignalStrength { get; set; }
/// <summary>
/// 当前网络类型
/// </summary>
public NetworkType CurrentNetworkType { get; set; }
/// <summary>
/// 当前发射功率
/// </summary>
public int CurrentTransmitPower { get; set; }
}

127
CoreAgent.Domain/Interfaces/Network/INetworkManager.cs

@ -0,0 +1,127 @@
namespace CoreAgent.Domain.Interfaces.Network;
/// <summary>
/// 网络管理器接口
/// </summary>
public interface INetworkManager
{
/// <summary>
/// 获取所有网络接口信息
/// </summary>
/// <returns>网络接口信息列表</returns>
Task<IEnumerable<NetworkInterfaceInfo>> GetNetworkInterfacesAsync();
/// <summary>
/// 获取指定网络接口的详细信息
/// </summary>
/// <param name="interfaceId">网络接口ID</param>
/// <returns>网络接口详细信息</returns>
Task<NetworkInterfaceDetail> GetNetworkInterfaceDetailAsync(string interfaceId);
/// <summary>
/// 启用网络接口
/// </summary>
/// <param name="interfaceId">网络接口ID</param>
/// <returns>操作是否成功</returns>
Task<bool> EnableNetworkInterfaceAsync(string interfaceId);
/// <summary>
/// 禁用网络接口
/// </summary>
/// <param name="interfaceId">网络接口ID</param>
/// <returns>操作是否成功</returns>
Task<bool> DisableNetworkInterfaceAsync(string interfaceId);
}
/// <summary>
/// 网络接口信息
/// </summary>
public class NetworkInterfaceInfo
{
/// <summary>
/// 接口ID
/// </summary>
public string Id { get; set; }
/// <summary>
/// 接口名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 接口类型
/// </summary>
public NetworkInterfaceType Type { get; set; }
/// <summary>
/// 是否启用
/// </summary>
public bool IsEnabled { get; set; }
/// <summary>
/// MAC地址
/// </summary>
public string MacAddress { get; set; }
}
/// <summary>
/// 网络接口详细信息
/// </summary>
public class NetworkInterfaceDetail : NetworkInterfaceInfo
{
/// <summary>
/// IP地址列表
/// </summary>
public IEnumerable<string> IpAddresses { get; set; }
/// <summary>
/// 子网掩码
/// </summary>
public string SubnetMask { get; set; }
/// <summary>
/// 默认网关
/// </summary>
public string DefaultGateway { get; set; }
/// <summary>
/// DNS服务器列表
/// </summary>
public IEnumerable<string> DnsServers { get; set; }
/// <summary>
/// 连接速度(Mbps)
/// </summary>
public int Speed { get; set; }
}
/// <summary>
/// 网络接口类型
/// </summary>
public enum NetworkInterfaceType
{
/// <summary>
/// 未知
/// </summary>
Unknown,
/// <summary>
/// 以太网
/// </summary>
Ethernet,
/// <summary>
/// 无线网络
/// </summary>
Wireless,
/// <summary>
/// 蜂窝网络
/// </summary>
Cellular,
/// <summary>
/// 虚拟网络
/// </summary>
Virtual
}

26
CoreAgent.Domain/Interfaces/System/Command/ISystemCommandExecutor.cs

@ -0,0 +1,26 @@
using CoreAgent.Domain.Models.System;
namespace CoreAgent.Domain.Interfaces.System.Command;
/// <summary>
/// 系统命令执行器接口
/// </summary>
public interface ISystemCommandExecutor
{
/// <summary>
/// 执行命令并实时输出结果
/// </summary>
/// <param name="command">要执行的命令</param>
/// <param name="outputHandler">输出处理器</param>
/// <param name="cancellationTokenSource">取消令牌源</param>
/// <returns>执行任务</returns>
Task ExecuteCommandWithOutputAsync(string command, Action<string> outputHandler, CancellationTokenSource cancellationTokenSource);
/// <summary>
/// 执行命令并返回结果
/// </summary>
/// <param name="command">要执行的命令</param>
/// <param name="cancellationTokenSource">取消令牌源</param>
/// <returns>命令执行结果</returns>
Task<CommandExecutionResult> ExecuteCommandAsync(string command, CancellationTokenSource cancellationTokenSource);
}

15
CoreAgent.Domain/Interfaces/System/Command/ISystemCommandExecutorFactory.cs

@ -0,0 +1,15 @@
using CoreAgent.Domain.Models;
namespace CoreAgent.Domain.Interfaces.System.Command;
/// <summary>
/// 系统命令执行器工厂接口
/// </summary>
public interface ISystemCommandExecutorFactory
{
/// <summary>
/// 创建适合当前操作系统的命令执行器
/// </summary>
/// <returns>命令执行器实例</returns>
ISystemCommandExecutor CreateExecutor();
}

49
CoreAgent.Domain/Models/ApiActionResult.cs

@ -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<T> : IActionResult
{
private readonly ApiResponse<T> _response;
private readonly HttpStatusCode _statusCode;
public ApiActionResult(ApiResponse<T> 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);
}
}
}

58
CoreAgent.Domain/Models/ApiResponse.cs

@ -1,58 +0,0 @@
namespace CoreAgent.Domain.Models
{
public class ApiResponse<T>
{
public bool Success { get; set; }
public string? Message { get; set; }
public T? Data { get; set; }
public List<string> Errors { get; set; }
public ApiResponse()
{
Success = true;
Errors = new List<string>();
}
public static ApiResponse<T> CreateSuccess(T data, string? message = null)
{
return new ApiResponse<T>
{
Success = true,
Data = data,
Message = message
};
}
public static ApiResponse<T> CreateError(string message, List<string>? errors = null)
{
return new ApiResponse<T>
{
Success = false,
Message = message,
Errors = errors ?? new List<string>()
};
}
}
public class ApiResponse : ApiResponse<object>
{
public new static ApiResponse CreateSuccess(string? message = null)
{
return new ApiResponse
{
Success = true,
Message = message
};
}
public new static ApiResponse CreateError(string message, List<string>? errors = null)
{
return new ApiResponse
{
Success = false,
Message = message,
Errors = errors ?? new List<string>()
};
}
}
}

119
CoreAgent.Domain/Models/Common/ApiActionResult.cs

@ -0,0 +1,119 @@
using Microsoft.AspNetCore.Mvc;
using System.Net;
namespace CoreAgent.Domain.Models.Common;
/// <summary>
/// API操作结果
/// </summary>
public class ApiActionResult : IActionResult
{
/// <summary>
/// 是否成功
/// </summary>
public bool IsSuccess { get; set; }
/// <summary>
/// 消息
/// </summary>
public string Message { get; set; }
/// <summary>
/// 错误代码
/// </summary>
public string ErrorCode { get; set; }
/// <summary>
/// HTTP状态码
/// </summary>
public HttpStatusCode StatusCode { get; set; } = HttpStatusCode.OK;
/// <summary>
/// 创建成功结果
/// </summary>
/// <param name="message">消息</param>
/// <returns>API操作结果</returns>
public static ApiActionResult Ok(string message = "操作成功")
{
return new ApiActionResult
{
IsSuccess = true,
Message = message,
ErrorCode = string.Empty,
StatusCode = HttpStatusCode.OK
};
}
/// <summary>
/// 创建失败结果
/// </summary>
/// <param name="message">错误消息</param>
/// <param name="errorCode">错误代码</param>
/// <param name="statusCode">HTTP状态码</param>
/// <returns>API操作结果</returns>
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);
}
}
/// <summary>
/// 带数据的API操作结果
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
public class ApiActionResult<T> : ApiActionResult
{
/// <summary>
/// 数据
/// </summary>
public T Data { get; set; }
/// <summary>
/// 创建成功结果
/// </summary>
/// <param name="data">数据</param>
/// <param name="message">消息</param>
/// <returns>API操作结果</returns>
public static ApiActionResult<T> Ok(T data, string message = "操作成功")
{
return new ApiActionResult<T>
{
IsSuccess = true,
Message = message,
ErrorCode = string.Empty,
StatusCode = HttpStatusCode.OK,
Data = data
};
}
/// <summary>
/// 创建失败结果
/// </summary>
/// <param name="message">错误消息</param>
/// <param name="errorCode">错误代码</param>
/// <param name="statusCode">HTTP状态码</param>
/// <returns>API操作结果</returns>
public static ApiActionResult<T> Error(string message, string errorCode = "ERROR", HttpStatusCode statusCode = HttpStatusCode.BadRequest)
{
return new ApiActionResult<T>
{
IsSuccess = false,
Message = message,
ErrorCode = errorCode,
StatusCode = statusCode,
Data = default
};
}
}

23
CoreAgent.Domain/Models/Common/ErrorResponse.cs

@ -0,0 +1,23 @@
namespace CoreAgent.Domain.Models.Common;
/// <summary>
/// 错误响应
/// </summary>
public class ErrorResponse
{
/// <summary>
/// 请求的唯一跟踪标识符
/// </summary>
public string TraceId { get; set; }
/// <summary>
/// 错误消息
/// </summary>
public string Message { get; set; }
/// <summary>
/// 验证错误集合,键为字段名,值为错误消息数组
/// </summary>
public IDictionary<string, string[]> Errors { get; set; }
}

23
CoreAgent.Domain/Models/ErrorResponse.cs

@ -1,23 +0,0 @@
namespace CoreAgent.Domain.Models
{
/// <summary>
/// 表示API错误响应的领域模型
/// </summary>
public class ErrorResponse
{
/// <summary>
/// 请求的唯一跟踪标识符
/// </summary>
public string TraceId { get; set; }
/// <summary>
/// 错误消息
/// </summary>
public string Message { get; set; }
/// <summary>
/// 验证错误集合,键为字段名,值为错误消息数组
/// </summary>
public IDictionary<string, string[]> Errors { get; set; }
}
}

2
CoreAgent.Domain/Models/CellularNetworkConfig.cs → CoreAgent.Domain/Models/Network/CellularNetworkConfig.cs

@ -1,4 +1,4 @@
namespace CoreAgent.Domain.Models; namespace CoreAgent.Domain.Models.Network;
/// <summary> /// <summary>
/// 蜂窝网络配置 /// 蜂窝网络配置

2
CoreAgent.Domain/Models/CellularNetworkStatus.cs → CoreAgent.Domain/Models/Network/CellularNetworkStatus.cs

@ -1,4 +1,4 @@
namespace CoreAgent.Domain.Models; namespace CoreAgent.Domain.Models.Network;
/// <summary> /// <summary>
/// 蜂窝网络状态 /// 蜂窝网络状态

61
CoreAgent.Domain/Models/System/CommandExecutionResult.cs

@ -0,0 +1,61 @@
namespace CoreAgent.Domain.Models.System;
/// <summary>
/// 命令执行结果
/// </summary>
public class CommandExecutionResult
{
/// <summary>
/// 是否成功
/// </summary>
public bool IsSuccess { get; set; }
/// <summary>
/// 输出信息
/// </summary>
public string Output { get; set; }
/// <summary>
/// 错误信息
/// </summary>
public string Error { get; set; }
/// <summary>
/// 执行时间(毫秒)
/// </summary>
public long ExecutionTime { get; set; }
/// <summary>
/// 创建成功结果
/// </summary>
/// <param name="output">输出信息</param>
/// <param name="executionTime">执行时间</param>
/// <returns>命令执行结果</returns>
public static CommandExecutionResult Success(string output, long executionTime)
{
return new CommandExecutionResult
{
IsSuccess = true,
Output = output,
Error = string.Empty,
ExecutionTime = executionTime
};
}
/// <summary>
/// 创建失败结果
/// </summary>
/// <param name="error">错误信息</param>
/// <param name="executionTime">执行时间</param>
/// <returns>命令执行结果</returns>
public static CommandExecutionResult Failure(string error, long executionTime)
{
return new CommandExecutionResult
{
IsSuccess = false,
Output = string.Empty,
Error = error,
ExecutionTime = executionTime
};
}
}

57
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;
/// <summary>
/// 命令执行器基类
/// </summary>
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<string> outputHandler, CancellationTokenSource cancellationTokenSource);
public abstract Task<CommandExecutionResult> ExecuteCommandAsync(string command, CancellationTokenSource cancellationTokenSource);
protected async Task<CommandExecutionResult> 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);
}
}
}

44
CoreAgent.Infrastructure/Command/CommandStrategyFactory.cs

@ -1,44 +0,0 @@
using CoreAgent.Domain.Interfaces;
using Microsoft.Extensions.Logging;
using System.Runtime.InteropServices;
namespace CoreAgent.Infrastructure.Command;
/// <summary>
/// 命令策略工厂实现
/// </summary>
public class CommandStrategyFactory : ICommandStrategyFactory
{
private readonly ILogger<CommandStrategyFactory> _logger;
private readonly ILoggerFactory _loggerFactory;
public CommandStrategyFactory(ILogger<CommandStrategyFactory> logger, ILoggerFactory loggerFactory)
{
_logger = logger;
_loggerFactory = loggerFactory;
}
/// <summary>
/// 创建适合当前操作系统的命令策略
/// </summary>
/// <returns>命令策略实例</returns>
/// <exception cref="PlatformNotSupportedException">当操作系统不是Windows或Linux时抛出</exception>
public ICommandStrategy CreateStrategy()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
_logger.LogInformation("Creating Windows command strategy");
return new WindowsCommandStrategy(_loggerFactory.CreateLogger<WindowsCommandStrategy>());
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
_logger.LogInformation("Creating Linux command strategy");
return new LinuxCommandStrategy(_loggerFactory.CreateLogger<LinuxCommandStrategy>());
}
else
{
_logger.LogError("Unsupported operating system");
throw new PlatformNotSupportedException("Only Windows and Linux are supported.");
}
}
}

46
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;
/// <summary>
/// Linux系统命令执行器实现
/// </summary>
public class LinuxCommandExecutor : BaseCommandExecutor
{
private const string ShellPath = "/bin/bash";
public LinuxCommandExecutor(ILogger<LinuxCommandExecutor> logger) : base(logger)
{
}
protected override string DefaultShell => ShellPath;
public override async Task ExecuteCommandWithOutputAsync(string command, Action<string> 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<CommandExecutionResult> ExecuteCommandAsync(string command, CancellationTokenSource cancellationTokenSource)
{
return await ExecuteCommandInternalAsync(command, $"-c \"{command}\"", cancellationTokenSource);
}
}

47
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;
/// <summary>
/// Windows系统命令执行器实现
/// </summary>
public class WindowsCommandExecutor : BaseCommandExecutor
{
private const string ShellPath = "cmd.exe";
private const string PowerShellPath = "powershell.exe";
public WindowsCommandExecutor(ILogger<WindowsCommandExecutor> logger) : base(logger)
{
}
protected override string DefaultShell => ShellPath;
public override async Task ExecuteCommandWithOutputAsync(string command, Action<string> 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<CommandExecutionResult> ExecuteCommandAsync(string command, CancellationTokenSource cancellationTokenSource)
{
return await ExecuteCommandInternalAsync(command, $"/c {command}", cancellationTokenSource);
}
}

38
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;
/// <summary>
/// 系统命令执行器工厂
/// </summary>
public class SystemCommandExecutorFactory : ISystemCommandExecutorFactory
{
private readonly ILogger<WindowsCommandExecutor> _windowsLogger;
private readonly ILogger<LinuxCommandExecutor> _linuxLogger;
public SystemCommandExecutorFactory(
ILogger<WindowsCommandExecutor> windowsLogger,
ILogger<LinuxCommandExecutor> 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("当前操作系统不支持命令执行");
}
}
}

123
CoreAgent.Infrastructure/Command/LinuxCommandStrategy.cs

@ -1,123 +0,0 @@
using CliWrap;
using CliWrap.Buffered;
using CoreAgent.Domain.Interfaces;
using Microsoft.Extensions.Logging;
namespace CoreAgent.Infrastructure.Command;
/// <summary>
/// Linux命令策略实现
/// </summary>
public class LinuxCommandStrategy : ICommandStrategy
{
private readonly ILogger<LinuxCommandStrategy> _logger;
public LinuxCommandStrategy(ILogger<LinuxCommandStrategy> logger)
{
_logger = logger;
}
public async Task ExecuteBufferedAsync(string arguments, Action<string> 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<string> 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<bool> 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<string> 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<bool> 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;
}
}
}

123
CoreAgent.Infrastructure/Command/WindowsCommandStrategy.cs

@ -1,123 +0,0 @@
using CliWrap;
using CliWrap.Buffered;
using CoreAgent.Domain.Interfaces;
using Microsoft.Extensions.Logging;
namespace CoreAgent.Infrastructure.Command;
/// <summary>
/// Windows命令策略实现
/// </summary>
public class WindowsCommandStrategy : ICommandStrategy
{
private readonly ILogger<WindowsCommandStrategy> _logger;
public WindowsCommandStrategy(ILogger<WindowsCommandStrategy> logger)
{
_logger = logger;
}
public async Task ExecuteBufferedAsync(string arguments, Action<string> 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<string> 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<bool> 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<string> 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<bool> 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;
}
}
}

13
CoreAgent.Infrastructure/CoreAgent.Infrastructure.csproj.Backup.tmp

@ -1,13 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Serilog" Version="4.3.0" />
</ItemGroup>
</Project>

44
CoreAgent.Infrastructure/Extensions/ApiVersioningExtensions.cs

@ -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<ApiVersioningSettings>() ?? 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;
}
}
}

13
CoreAgent.Infrastructure/Extensions/ExceptionMiddlewareExtensions.cs

@ -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<ExceptionMiddleware>();
}
}
}

35
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;
/// <summary>
/// 日志扩展方法
/// </summary>
public static class LoggingExtensions
{
/// <summary>
/// 添加日志服务
/// </summary>
/// <param name="services">服务集合</param>
/// <returns>服务集合</returns>
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;
}
}

33
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;
/// <summary>
/// 请求日志扩展方法
/// </summary>
public static class RequestLoggingExtensions
{
/// <summary>
/// 添加请求日志中间件
/// </summary>
/// <param name="app">应用程序构建器</param>
/// <returns>应用程序构建器</returns>
public static IServiceCollection AddRequestLogging(this IServiceCollection services, IConfiguration config)
{
services.Configure<RequestLoggingOptions>(config.GetSection("RequestLogging"));
return services;
}
public static IApplicationBuilder UseRequestLogging(this IApplicationBuilder app, IConfiguration config)
{
app.UseMiddleware<RequestLoggingMiddleware>();
app.UseMiddleware<ResponseLoggingMiddleware>();
return app;
}
}

112
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;
/// <summary>
/// 请求日志中间件
/// </summary>
public class RequestLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RequestLoggingMiddleware> _logger;
private readonly RequestLoggingOptions _options;
public RequestLoggingMiddleware(
RequestDelegate next,
ILogger<RequestLoggingMiddleware> logger,
IOptions<RequestLoggingOptions> 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<string> 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;
}
}
}

106
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;
/// <summary>
/// 响应日志中间件
/// </summary>
public class ResponseLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ResponseLoggingMiddleware> _logger;
private readonly RequestLoggingOptions _options;
public ResponseLoggingMiddleware(
RequestDelegate next,
ILogger<ResponseLoggingMiddleware> logger,
IOptions<RequestLoggingOptions> 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<string> 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;
}
}

27
CoreAgent.Infrastructure/Extensions/LoggingExtensions.cs

@ -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;
}
}
}

24
CoreAgent.Infrastructure/Extensions/RequestLoggingExtensions.cs

@ -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<RequestLoggingOptions>(config.GetSection("RequestLogging"));
return services;
}
public static IApplicationBuilder UseRequestLogging(this IApplicationBuilder app, IConfiguration config)
{
app.UseMiddleware<RequestLoggingMiddleware>();
app.UseMiddleware<ResponseLoggingMiddleware>();
return app;
}
}
}

51
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;
/// <summary>
/// API版本服务扩展方法
/// </summary>
public static class ApiVersioningServiceExtensions
{
/// <summary>
/// 添加API版本服务
/// </summary>
/// <param name="services">服务集合</param>
/// <returns>服务集合</returns>
public static IServiceCollection AddApiVersioningService(this IServiceCollection services, IConfiguration config)
{
var settings = config.GetSection("ApiVersioning").Get<ApiVersioningSettings>() ?? 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;
}
}

35
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;
/// <summary>
/// 命令服务扩展方法
/// </summary>
public static class CommandServiceExtensions
{
/// <summary>
/// 添加命令策略服务
/// </summary>
/// <param name="services">服务集合</param>
/// <returns>服务集合</returns>
public static IServiceCollection AddCommandCustomService(this IServiceCollection services)
{
// 注册命令执行器工厂
services.AddSingleton<ISystemCommandExecutorFactory, SystemCommandExecutorFactory>();
// 注册命令执行器
services.AddTransient<ISystemCommandExecutor>(sp =>
{
var factory = sp.GetRequiredService<ISystemCommandExecutorFactory>();
return factory.CreateExecutor();
});
// 注册 ICellularNetworkService
services.AddScoped<ICellularNetworkService, CellularNetworkService>();
return services;
}
}

27
CoreAgent.Infrastructure/Extensions/ServiceCollectionExtensions.cs

@ -1,27 +0,0 @@
using CoreAgent.Domain.Interfaces;
using CoreAgent.Infrastructure.Command;
using CoreAgent.Infrastructure.Services;
using Microsoft.Extensions.DependencyInjection;
namespace CoreAgent.Infrastructure.Extensions;
/// <summary>
/// 服务集合扩展方法
/// </summary>
public static class ServiceCollectionExtensions
{
/// <summary>
/// 添加命令策略服务
/// </summary>
/// <param name="services">服务集合</param>
/// <returns>服务集合</returns>
public static IServiceCollection AddCommandCustomService(this IServiceCollection services)
{
// 注册命令策略工厂
services.AddSingleton<ICommandStrategyFactory, CommandStrategyFactory>();
// 注册 ICellularNetworkService
services.AddScoped<ICellularNetworkService, CellularNetworkService>();
return services;
}
}

16
CoreAgent.Infrastructure/Middleware/ExceptionMiddleware.cs → CoreAgent.Infrastructure/Middleware/GlobalException/ExceptionMiddleware.cs

@ -1,18 +1,19 @@
using CoreAgent.Domain.Exceptions; using CoreAgent.Domain.Exceptions;
using CoreAgent.Domain.Models; using CoreAgent.Domain.Models;
using CoreAgent.Domain.Models.Common;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Net; using System.Net;
using System.Text.Json; 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 RequestDelegate _next;
private readonly ILogger<ExceptionMiddleware> _logger; private readonly ILogger<GlobalExceptionMiddleware> _logger;
public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger) public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> logger)
{ {
_next = next; _next = next;
_logger = logger; _logger = logger;
@ -24,13 +25,14 @@ namespace CoreAgent.Infrastructure.Middleware
{ {
await _next(context); await _next(context);
} }
catch (Exception ex) catch (System.Exception ex)
{ {
await HandleExceptionAsync(context, 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; var response = context.Response;
response.ContentType = "application/json"; response.ContentType = "application/json";
@ -72,4 +74,4 @@ namespace CoreAgent.Infrastructure.Middleware
await response.WriteAsync(result); await response.WriteAsync(result);
} }
} }
} }

20
CoreAgent.Infrastructure/Middleware/GlobalException/ExceptionMiddlewareExtensions.cs

@ -0,0 +1,20 @@
using CoreAgent.Infrastructure.Middleware.GlobalException;
using Microsoft.AspNetCore.Builder;
namespace CoreAgent.Infrastructure.Middleware.GlobalException;
/// <summary>
/// 全局异常中间件扩展方法
/// </summary>
public static class GlobalExceptionMiddlewareExtensions
{
/// <summary>
/// 使用全局异常处理中间件
/// </summary>
/// <param name="app">应用程序构建器</param>
/// <returns>应用程序构建器</returns>
public static IApplicationBuilder UseGlobalExceptionMiddleware(this IApplicationBuilder app)
{
return app.UseMiddleware<GlobalExceptionMiddleware>();
}
}

110
CoreAgent.Infrastructure/Middleware/RequestLoggingMiddleware.cs

@ -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<RequestLoggingMiddleware> _logger;
private readonly RequestLoggingOptions _options;
public RequestLoggingMiddleware(
RequestDelegate next,
ILogger<RequestLoggingMiddleware> logger,
IOptions<RequestLoggingOptions> 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<string> 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;
}
}
}
}

103
CoreAgent.Infrastructure/Middleware/ResponseLoggingMiddleware.cs

@ -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<ResponseLoggingMiddleware> _logger;
private readonly RequestLoggingOptions _options;
public ResponseLoggingMiddleware(
RequestDelegate next,
ILogger<ResponseLoggingMiddleware> logger,
IOptions<RequestLoggingOptions> 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<string> 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;
}
}
}

168
CoreAgent.Infrastructure/Network/IPAddressUtils.cs

@ -1,168 +0,0 @@
using System.Numerics;
using CoreAgent.Domain.Interfaces;
namespace CoreAgent.Infrastructure.Network;
/// <summary>
/// 提供IP地址相关的工具方法
/// </summary>
public static class IPAddressUtils
{
/// <summary>
/// 检查两个IPv4地址是否在同一个子网内
/// </summary>
/// <param name="ip1">第一个IP地址</param>
/// <param name="ip2">第二个IP地址</param>
/// <param name="subnetMask">子网掩码</param>
/// <returns>如果两个IP地址在同一个子网内返回true,否则返回false</returns>
/// <exception cref="ArgumentException">当IP地址或子网掩码不是IPv4格式时抛出</exception>
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));
}
/// <summary>
/// 检查两个IPv6地址是否在同一个子网内
/// </summary>
/// <param name="ip1">第一个IP地址</param>
/// <param name="ip2">第二个IP地址</param>
/// <param name="prefixLength">前缀长度</param>
/// <returns>如果两个IP地址在同一个子网内返回true,否则返回false</returns>
/// <exception cref="ArgumentException">当IP地址不是IPv6格式时抛出</exception>
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);
}
/// <summary>
/// 获取系统的IPv4地址列表
/// </summary>
/// <param name="commandStrategyFactory">命令策略工厂</param>
/// <param name="source">取消令牌源</param>
/// <param name="logger">日志记录器</param>
/// <returns>IPv4地址列表</returns>
public static async Task<IEnumerable<string>> CheckAddressIPv4(ICommandStrategyFactory commandStrategyFactory, CancellationTokenSource source)
{
List<string> addrs = new List<string>();
var inet4result = await commandStrategyFactory.CreateStrategy().ExecuteBufferedAsync("ifconfig | grep 'inet ' | awk '{print $2}'", source);
if (!string.IsNullOrWhiteSpace(inet4result))
addrs.AddRange(RemoveNewLines(inet4result));
return addrs;
}
/// <summary>
/// 获取系统的IPv6地址列表
/// </summary>
/// <param name="commandStrategyFactory">命令策略工厂</param>
/// <param name="source">取消令牌源</param>
/// <param name="logger">日志记录器</param>
/// <returns>IPv6地址列表</returns>
public static async Task<IEnumerable<string>> CheckAddressIPv6(ICommandStrategyFactory commandStrategyFactory, CancellationTokenSource source)
{
List<string> addrs = new List<string>();
var inet6result = await commandStrategyFactory.CreateStrategy().ExecuteBufferedAsync("ifconfig | grep 'inet6 ' | awk '{print $2}'", source);
if (!string.IsNullOrWhiteSpace(inet6result))
addrs.AddRange(RemoveNewLines(inet6result));
return addrs;
}
/// <summary>
/// 获取系统的IPv4地址和子网掩码列表
/// </summary>
/// <param name="commandStrategyFactory">命令策略工厂</param>
/// <param name="source">取消令牌源</param>
/// <param name="logger">日志记录器</param>
/// <returns>IPv4地址和子网掩码的元组列表</returns>
public static async Task<List<(string ipv4, string mask)>> 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;
}
/// <summary>
/// 获取系统的IPv6地址和前缀长度列表
/// </summary>
/// <param name="commandStrategyFactory">命令策略工厂</param>
/// <param name="source">取消令牌源</param>
/// <param name="logger">日志记录器</param>
/// <returns>IPv6地址和前缀长度的元组列表</returns>
public static async Task<List<(string ipv6, string Prefix)>> 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;
}
/// <summary>
/// 计算两个IP地址的匹配位数
/// </summary>
/// <param name="ip1">第一个IP地址</param>
/// <param name="ip2">第二个IP地址</param>
/// <returns>匹配的位数</returns>
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;
}
/// <summary>
/// 移除字符串中的换行符并分割成数组
/// </summary>
/// <param name="input">输入字符串</param>
/// <returns>分割后的字符串数组</returns>
private static string[] RemoveNewLines(string input)
{
return input.Split("\n", StringSplitOptions.RemoveEmptyEntries);
}
}

376
CoreAgent.Infrastructure/Services/CellularNetworkService.cs

@ -1,5 +1,8 @@
using CoreAgent.Domain.Interfaces; using CoreAgent.Domain.Interfaces.Network;
using CoreAgent.Domain.Models; 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 Microsoft.Extensions.Logging;
using System.Diagnostics; using System.Diagnostics;
@ -11,32 +14,89 @@ namespace CoreAgent.Infrastructure.Services;
public class CellularNetworkService : ICellularNetworkService public class CellularNetworkService : ICellularNetworkService
{ {
private readonly ILogger<CellularNetworkService> _logger; private readonly ILogger<CellularNetworkService> _logger;
private readonly CellularNetworkGlobalStatus _globalStatus;
private readonly ISystemCommandExecutorFactory _commandExecutorFactory;
private static readonly SemaphoreSlim _startLock = new SemaphoreSlim(1, 1);
public CellularNetworkService(ILogger<CellularNetworkService> logger) public CellularNetworkService(
ILogger<CellularNetworkService> logger,
ISystemCommandExecutorFactory commandExecutorFactory)
{ {
_logger = logger; _logger = logger;
_commandExecutorFactory = commandExecutorFactory;
_globalStatus = new CellularNetworkGlobalStatus
{
IsInitialized = false,
CurrentStatus = NetworkStatus.Unknown,
CurrentSignalStrength = SignalStrength.NoSignal,
CurrentNetworkType = NetworkType.Unknown,
CurrentTransmitPower = 0
};
} }
public async Task<bool> StartAsync(string interfaceName, CellularNetworkConfig config) public async Task<bool> StartAsync(string interfaceName, CellularNetworkConfig config)
{ {
try try
{ {
_logger.LogInformation("正在启动蜂窝网络接口: {InterfaceName}", interfaceName); // 使用信号量确保只能启动一次
if (!await _startLock.WaitAsync(TimeSpan.FromSeconds(5)))
{
_logger.LogWarning("蜂窝网络启动操作被锁定,可能已有其他启动操作正在进行");
return false;
}
// 启用网络接口 try
var enableCommand = $"netsh interface set interface \"{interfaceName}\" admin=ENABLED"; {
await ExecuteCommandAsync(enableCommand); if (_globalStatus.IsInitialized)
{
_logger.LogWarning("蜂窝网络已经初始化,不能重复启动");
return false;
}
// 配置网络接口 _logger.LogInformation("正在启动蜂窝网络接口: {InterfaceName}", interfaceName);
var configCommand = $"netsh interface cellular set profile name=\"{config.Apn}\" interface=\"{interfaceName}\"";
await ExecuteCommandAsync(configCommand);
_logger.LogInformation("蜂窝网络接口启动成功: {InterfaceName}", interfaceName); // 1. 配置网络参数
return true; 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) catch (Exception ex)
{ {
_logger.LogError(ex, "启动蜂窝网络接口失败: {InterfaceName}", interfaceName); _logger.LogError(ex, "启动蜂窝网络接口 {InterfaceName} 失败", interfaceName);
return false; return false;
} }
} }
@ -45,110 +105,276 @@ public class CellularNetworkService : ICellularNetworkService
{ {
try try
{ {
if (!_globalStatus.IsInitialized)
{
_logger.LogWarning("蜂窝网络未初始化,无需停止");
return true;
}
_logger.LogInformation("正在停止蜂窝网络接口: {InterfaceName}", interfaceName); _logger.LogInformation("正在停止蜂窝网络接口: {InterfaceName}", interfaceName);
var command = $"netsh interface set interface \"{interfaceName}\" admin=DISABLED"; // 1. 停止网络接口
await ExecuteCommandAsync(command); 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; return true;
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "停止蜂窝网络接口失败: {InterfaceName}", interfaceName); _logger.LogError(ex, "停止蜂窝网络接口 {InterfaceName} 失败", interfaceName);
return false; return false;
} }
} }
public async Task<CellularNetworkStatus> GetStatusAsync(string interfaceName) public async Task<NetworkStatus> GetNetworkStatusAsync(string interfaceName)
{ {
try try
{ {
_logger.LogInformation("正在获取蜂窝网络接口状态: {InterfaceName}", interfaceName); _logger.LogDebug("正在获取蜂窝网络状态: {InterfaceName}", interfaceName);
var result = await ExecuteCommandWithResultAsync($"netsh interface cellular show interfaces \"{interfaceName}\"");
// 获取接口状态 if (!result.IsSuccess)
var statusCommand = $"netsh interface show interface \"{interfaceName}\""; {
var statusResult = await ExecuteCommandAsync(statusCommand); _logger.LogError("获取蜂窝网络状态失败: {Error}", result.Error);
return NetworkStatus.Unknown;
}
// 获取信号强度 var output = result.Output;
var signalCommand = $"netsh interface cellular show interfaces \"{interfaceName}\""; if (output.Contains("已连接", StringComparison.OrdinalIgnoreCase))
var signalResult = await ExecuteCommandAsync(signalCommand); {
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<SignalStrength> 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), _logger.LogError("获取蜂窝网络信号强度失败: {Error}", result.Error);
ConnectionState = ParseConnectionState(statusResult), return SignalStrength.NoSignal;
SignalStrength = ParseSignalStrength(signalResult), }
CarrierName = ParseCarrierName(signalResult)
};
_logger.LogInformation("获取蜂窝网络接口状态成功: {InterfaceName}", interfaceName); var output = result.Output;
return status; 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) catch (Exception ex)
{ {
_logger.LogError(ex, "获取蜂窝网络接口状态失败: {InterfaceName}", interfaceName); _logger.LogError(ex, "获取蜂窝网络信号强度失败: {InterfaceName}", interfaceName);
throw; return SignalStrength.NoSignal;
} }
} }
private async Task<string> ExecuteCommandAsync(string command) public async Task<NetworkType> 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", _logger.LogError("获取蜂窝网络类型失败: {Error}", result.Error);
Arguments = $"/c {command}", return NetworkType.Unknown;
RedirectStandardOutput = true, }
RedirectStandardError = true,
UseShellExecute = false, var output = result.Output;
CreateNoWindow = true 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<bool> 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(); _logger.LogInformation("正在设置蜂窝网络发射功率: {InterfaceName}, {PowerLevel}", interfaceName, powerLevel);
var output = await process.StandardOutput.ReadToEndAsync();
var error = await process.StandardError.ReadToEndAsync();
await process.WaitForExitAsync();
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<CellularNetworkGlobalStatus> GetGlobalStatusAsync()
{
return Task.FromResult(_globalStatus);
} }
private string ParseConnectionState(string statusResult) private async Task<bool> ConfigureNetworkAsync(string interfaceName, CellularNetworkConfig config)
{ {
if (statusResult.Contains("已连接", StringComparison.OrdinalIgnoreCase)) try
return "已连接"; {
if (statusResult.Contains("已断开", StringComparison.OrdinalIgnoreCase)) _logger.LogDebug("正在配置蜂窝网络参数: {InterfaceName}", interfaceName);
return "已断开";
return "未知"; // 配置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<bool> WaitForConnectionAsync(string interfaceName)
{ {
// 这里需要根据实际输出格式进行解析 const int maxAttempts = 30;
// 示例实现,实际使用时需要根据具体输出格式调整 const int delayMs = 1000;
if (signalResult.Contains("信号强度: 强", StringComparison.OrdinalIgnoreCase))
return 4; for (int i = 0; i < maxAttempts; i++)
if (signalResult.Contains("信号强度: 中", StringComparison.OrdinalIgnoreCase)) {
return 3; var status = await GetNetworkStatusAsync(interfaceName);
if (signalResult.Contains("信号强度: 弱", StringComparison.OrdinalIgnoreCase)) if (status == NetworkStatus.Connected)
return 2; {
return 1; return true;
}
await Task.Delay(delayMs);
}
return false;
} }
private string ParseCarrierName(string signalResult) private async Task<bool> ExecuteCommandAsync(string command)
{ {
// 这里需要根据实际输出格式进行解析 try
// 示例实现,实际使用时需要根据具体输出格式调整 {
var carrierLine = signalResult.Split('\n') var executor = _commandExecutorFactory.CreateExecutor();
.FirstOrDefault(line => line.Contains("运营商:", StringComparison.OrdinalIgnoreCase)); using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
var result = await executor.ExecuteCommandAsync(command, cts);
return carrierLine?.Split(':').LastOrDefault()?.Trim() ?? "未知";
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<CommandExecutionResult> 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);
}
} }
} }
Loading…
Cancel
Save