Browse Source

feat: 修复设备搜索和获取方法,添加DeviceCode搜索支持和运行时状态

- 修复SearchDevicesWithRuntimeAsync方法,添加DeviceCode搜索支持
- 为GetDeviceById添加运行时状态支持,新增GetDeviceByIdWithRuntimeAsync方法
- 统一所有设备搜索方法,确保支持按设备编码搜索
- 更新查询处理器,返回设备运行时状态信息
- 完善导航属性查询,正确处理CellularDevice和CellularDeviceRuntime的一对一关系
feature/x1-web-request
hyh 4 days ago
parent
commit
6a6de78986
  1. 46
      src/X1.Application/Features/DeviceRuntimes/Commands/StartDeviceRuntime/StartDeviceRuntimeCommand.cs
  2. 208
      src/X1.Application/Features/DeviceRuntimes/Commands/StartDeviceRuntime/StartDeviceRuntimeCommandHandler.cs
  3. 113
      src/X1.Application/Features/DeviceRuntimes/Commands/StartDeviceRuntime/StartDeviceRuntimeResponse.cs
  4. 18
      src/X1.Application/Features/DeviceRuntimes/Commands/StopDeviceRuntime/StopDeviceRuntimeCommand.cs
  5. 97
      src/X1.Application/Features/DeviceRuntimes/Commands/StopDeviceRuntime/StopDeviceRuntimeCommandHandler.cs
  6. 39
      src/X1.Application/Features/DeviceRuntimes/Commands/StopDeviceRuntime/StopDeviceRuntimeResponse.cs
  7. 26
      src/X1.Application/Features/DeviceRuntimes/Queries/GetDeviceRuntimeStatus/GetDeviceRuntimeStatusQuery.cs
  8. 66
      src/X1.Application/Features/DeviceRuntimes/Queries/GetDeviceRuntimeStatus/GetDeviceRuntimeStatusQueryHandler.cs
  9. 27
      src/X1.Application/Features/DeviceRuntimes/Queries/GetDeviceRuntimeStatus/GetDeviceRuntimeStatusResponse.cs
  10. 40
      src/X1.Application/Features/DeviceRuntimes/Queries/GetDeviceRuntimes/GetDeviceRuntimesQuery.cs
  11. 97
      src/X1.Application/Features/DeviceRuntimes/Queries/GetDeviceRuntimes/GetDeviceRuntimesQueryHandler.cs
  12. 126
      src/X1.Application/Features/DeviceRuntimes/Queries/GetDeviceRuntimes/GetDeviceRuntimesResponse.cs
  13. 7
      src/X1.Application/Features/Devices/Commands/CreateDevice/CreateDeviceCommand.cs
  14. 33
      src/X1.Application/Features/Devices/Commands/CreateDevice/CreateDeviceCommandHandler.cs
  15. 5
      src/X1.Application/Features/Devices/Commands/CreateDevice/CreateDeviceResponse.cs
  16. 7
      src/X1.Application/Features/Devices/Commands/DeleteDevice/DeleteDeviceCommandHandler.cs
  17. 5
      src/X1.Application/Features/Devices/Commands/UpdateDevice/UpdateDeviceCommand.cs
  18. 4
      src/X1.Application/Features/Devices/Commands/UpdateDevice/UpdateDeviceCommandHandler.cs
  19. 5
      src/X1.Application/Features/Devices/Commands/UpdateDevice/UpdateDeviceResponse.cs
  20. 6
      src/X1.Application/Features/Devices/Queries/GetDeviceById/GetDeviceByIdQueryHandler.cs
  21. 8
      src/X1.Application/Features/Devices/Queries/GetDeviceById/GetDeviceByIdResponse.cs
  22. 22
      src/X1.Application/Features/Devices/Queries/GetDevices/GetDevicesQueryHandler.cs
  23. 3
      src/X1.Domain/Entities/Common/BaseEntity.cs
  24. 28
      src/X1.Domain/Entities/Device/CellularDevice.cs
  25. 84
      src/X1.Domain/Entities/Device/CellularDeviceRuntime.cs
  26. 55
      src/X1.Domain/Entities/Device/CellularDeviceRuntimeDetail.cs
  27. 106
      src/X1.Domain/Entities/Device/CellularDeviceRuntimeHistory.cs
  28. 22
      src/X1.Domain/Entities/Device/DeviceConnectionStatus.cs
  29. 22
      src/X1.Domain/Entities/Device/DeviceRuntimeStatus.cs
  30. 14
      src/X1.Domain/Repositories/Device/ICellularDeviceRepository.cs
  31. 71
      src/X1.Domain/Repositories/Device/ICellularDeviceRuntimeDetailRepository.cs
  32. 107
      src/X1.Domain/Repositories/Device/ICellularDeviceRuntimeRepository.cs
  33. 1
      src/X1.Infrastructure/Configurations/Device/CellularDeviceConfiguration.cs
  34. 66
      src/X1.Infrastructure/Configurations/Device/CellularDeviceRuntimeConfiguration.cs
  35. 55
      src/X1.Infrastructure/Configurations/Device/CellularDeviceRuntimeDetailConfiguration.cs
  36. 5
      src/X1.Infrastructure/Context/AppDbContext.cs
  37. 1
      src/X1.Infrastructure/DependencyInjection.cs
  38. 47
      src/X1.Infrastructure/Repositories/Device/CellularDeviceRepository.cs
  39. 232
      src/X1.Infrastructure/Repositories/Device/CellularDeviceRuntimeRepository.cs
  40. 127
      src/X1.Presentation/Controllers/DeviceRuntimesController.cs
  41. 14
      src/X1.WebUI/src/pages/instruments/DevicesTable.tsx
  42. 5
      src/X1.WebUI/src/pages/instruments/DevicesView.tsx
  43. 123
      src/X1.WebUI/src/services/instrumentService.ts
  44. 992
      src/modify.md

46
src/X1.Application/Features/DeviceRuntimes/Commands/StartDeviceRuntime/StartDeviceRuntimeCommand.cs

@ -0,0 +1,46 @@
using CellularManagement.Domain.Common;
using MediatR;
using System.ComponentModel.DataAnnotations;
namespace CellularManagement.Application.Features.DeviceRuntimes.Commands.StartDeviceRuntime;
/// <summary>
/// 启动设备运行时状态命令
/// </summary>
public class StartDeviceRuntimeCommand : IRequest<OperationResult<StartDeviceRuntimeResponse>>
{
/// <summary>
/// 设备启动请求列表
/// </summary>
[Required(ErrorMessage = "设备启动请求列表不能为空")]
public List<DeviceStartRequest> DeviceRequests { get; set; } = null!;
/// <summary>
/// 验证命令参数
/// </summary>
public bool IsValid()
{
return DeviceRequests != null && DeviceRequests.Any() &&
DeviceRequests.All(d => !string.IsNullOrEmpty(d.DeviceCode) && !string.IsNullOrEmpty(d.NetworkStackCode));
}
}
/// <summary>
/// 设备启动请求
/// </summary>
public class DeviceStartRequest
{
/// <summary>
/// 设备编号
/// </summary>
[Required(ErrorMessage = "设备编号不能为空")]
[MaxLength(50, ErrorMessage = "设备编号不能超过50个字符")]
public string DeviceCode { get; set; } = null!;
/// <summary>
/// 网络栈配置编号
/// </summary>
[Required(ErrorMessage = "网络栈配置编号不能为空")]
[MaxLength(50, ErrorMessage = "网络栈配置编号不能超过50个字符")]
public string NetworkStackCode { get; set; } = null!;
}

208
src/X1.Application/Features/DeviceRuntimes/Commands/StartDeviceRuntime/StartDeviceRuntimeCommandHandler.cs

@ -0,0 +1,208 @@
using MediatR;
using Microsoft.Extensions.Logging;
using CellularManagement.Domain.Common;
using CellularManagement.Domain.Entities.Device;
using CellularManagement.Domain.Repositories.Device;
using CellularManagement.Domain.Repositories.Base;
using CellularManagement.Domain.Services;
using X1.DynamicClientCore.Features;
namespace CellularManagement.Application.Features.DeviceRuntimes.Commands.StartDeviceRuntime;
/// <summary>
/// 启动设备运行时状态命令处理器
/// </summary>
public class StartDeviceRuntimeCommandHandler : IRequestHandler<StartDeviceRuntimeCommand, OperationResult<StartDeviceRuntimeResponse>>
{
private readonly ICellularDeviceRuntimeRepository _deviceRuntimeRepository;
private readonly ICellularDeviceRepository _deviceRepository;
private readonly ILogger<StartDeviceRuntimeCommandHandler> _logger;
private readonly IUnitOfWork _unitOfWork;
private readonly ICurrentUserService _currentUserService;
private readonly IInstrumentProtocolClient _protocolClient;
/// <summary>
/// 初始化命令处理器
/// </summary>
public StartDeviceRuntimeCommandHandler(
ICellularDeviceRuntimeRepository deviceRuntimeRepository,
ICellularDeviceRepository deviceRepository,
ILogger<StartDeviceRuntimeCommandHandler> logger,
IUnitOfWork unitOfWork,
ICurrentUserService currentUserService,
IInstrumentProtocolClient protocolClient)
{
_deviceRuntimeRepository = deviceRuntimeRepository;
_deviceRepository = deviceRepository;
_logger = logger;
_unitOfWork = unitOfWork;
_currentUserService = currentUserService;
_protocolClient= protocolClient;
}
/// <summary>
/// 处理启动设备运行时状态命令
/// </summary>
public async Task<OperationResult<StartDeviceRuntimeResponse>> Handle(StartDeviceRuntimeCommand request, CancellationToken cancellationToken)
{
try
{
_logger.LogInformation("开始批量启动设备运行时状态,设备数量: {DeviceCount}", request.DeviceRequests.Count);
// 验证命令参数
if (!request.IsValid())
{
_logger.LogWarning("命令参数验证失败");
return OperationResult<StartDeviceRuntimeResponse>.CreateFailure("命令参数验证失败");
}
// 获取当前用户
var currentUser = _currentUserService.GetCurrentUserId();
if (string.IsNullOrEmpty(currentUser))
{
_logger.LogWarning("当前用户未登录");
return OperationResult<StartDeviceRuntimeResponse>.CreateFailure("用户未登录");
}
var deviceResults = new List<DeviceStartResult>();
var successCount = 0;
var failureCount = 0;
// 批量处理设备启动
foreach (var deviceRequest in request.DeviceRequests)
{
try
{
_logger.LogInformation("开始启动设备,设备编号: {DeviceCode}, 网络栈配置编号: {NetworkStackCode}",
deviceRequest.DeviceCode, deviceRequest.NetworkStackCode);
// 获取设备运行时状态
var deviceRuntime = await _deviceRuntimeRepository.GetRuntimeByDeviceCodeAsync(deviceRequest.DeviceCode, cancellationToken);
if (deviceRuntime == null)
{
_logger.LogWarning("设备运行时状态不存在,设备编号: {DeviceCode}", deviceRequest.DeviceCode);
deviceResults.Add(new DeviceStartResult
{
DeviceCode = deviceRequest.DeviceCode,
IsSuccess = false,
ErrorMessage = $"设备编号 {deviceRequest.DeviceCode} 的运行时状态不存在"
});
failureCount++;
continue;
}
// 检查设备是否已经在运行
if (deviceRuntime.RuntimeStatus == DeviceRuntimeStatus.Running)
{
_logger.LogWarning("设备已经在运行中,设备编号: {DeviceCode}", deviceRequest.DeviceCode);
deviceResults.Add(new DeviceStartResult
{
DeviceCode = deviceRequest.DeviceCode,
IsSuccess = false,
ErrorMessage = $"设备编号 {deviceRequest.DeviceCode} 已经在运行中"
});
failureCount++;
continue;
}
// 生成运行编码
var runtimeCode = await GenerateRuntimeCodeAsync(deviceRequest.DeviceCode, cancellationToken);
// 启动设备
deviceRuntime.Start(deviceRequest.NetworkStackCode, runtimeCode);
// 更新到数据库
_deviceRuntimeRepository.UpdateRuntime(deviceRuntime);
_logger.LogInformation("设备启动成功,设备编号: {DeviceCode}, 运行编码: {RuntimeCode}",
deviceRuntime.DeviceCode, deviceRuntime.RuntimeCode);
// 添加到结果列表
deviceResults.Add(new DeviceStartResult
{
DeviceCode = deviceRuntime.DeviceCode,
Id = deviceRuntime.Id,
RuntimeStatus = deviceRuntime.RuntimeStatus.ToString(),
NetworkStackCode = deviceRuntime.NetworkStackCode,
UpdatedAt = deviceRuntime.UpdatedAt ?? DateTime.UtcNow,
IsSuccess = true
});
successCount++;
}
catch (Exception ex)
{
_logger.LogError(ex, "启动设备失败,设备编号: {DeviceCode}", deviceRequest.DeviceCode);
deviceResults.Add(new DeviceStartResult
{
DeviceCode = deviceRequest.DeviceCode,
IsSuccess = false,
ErrorMessage = $"启动设备失败: {ex.Message}"
});
failureCount++;
}
}
// 保存所有更改
await _unitOfWork.SaveChangesAsync(cancellationToken);
// 构建响应
var response = new StartDeviceRuntimeResponse
{
DeviceResults = deviceResults,
Summary = new BatchOperationSummary
{
TotalCount = request.DeviceRequests.Count,
SuccessCount = successCount,
FailureCount = failureCount
}
};
_logger.LogInformation("批量启动设备完成,总数量: {TotalCount}, 成功: {SuccessCount}, 失败: {FailureCount}",
response.Summary.TotalCount, response.Summary.SuccessCount, response.Summary.FailureCount);
return OperationResult<StartDeviceRuntimeResponse>.CreateSuccess(response);
}
catch (Exception ex)
{
_logger.LogError(ex, "批量启动设备运行时状态失败");
return OperationResult<StartDeviceRuntimeResponse>.CreateFailure($"批量启动设备运行时状态失败: {ex.Message}");
}
}
/// <summary>
/// 生成运行编码
/// </summary>
private async Task<string> GenerateRuntimeCodeAsync(string deviceCode, CancellationToken cancellationToken)
{
// 获取当前设备运行时总数
var runtimeCount = await _deviceRuntimeRepository.GetRuntimeCountAsync(cancellationToken);
var nextNumber = runtimeCount + 1;
// 计算需要的位数,确保至少3位数
var digitCount = CalculateRequiredDigits(nextNumber);
// 格式化序号,动态补0,确保从000开始
var formattedNumber = nextNumber.ToString($"D{digitCount}");
// 生成运行编码格式:RT-000-DEVCODE, RT-001-DEVCODE 等
var runtimeCode = $"RT-{formattedNumber}-{deviceCode}";
_logger.LogDebug("生成运行编码: {RuntimeCode}, 运行时总数: {RuntimeCount}, 位数: {DigitCount}",
runtimeCode, runtimeCount, digitCount);
return runtimeCode;
}
/// <summary>
/// 计算需要的位数
/// </summary>
private int CalculateRequiredDigits(int number)
{
if (number <= 0) return 3; // 从000开始,至少3位数
// 计算位数:确保至少3位数,从000开始
// 1-999用3位,1000-9999用4位,10000-99999用5位,以此类推
var calculatedDigits = (int)Math.Floor(Math.Log10(number)) + 1;
return Math.Max(calculatedDigits, 3); // 确保至少3位数
}
}

113
src/X1.Application/Features/DeviceRuntimes/Commands/StartDeviceRuntime/StartDeviceRuntimeResponse.cs

@ -0,0 +1,113 @@
namespace CellularManagement.Application.Features.DeviceRuntimes.Commands.StartDeviceRuntime;
/// <summary>
/// 启动设备运行时状态响应
/// </summary>
public class StartDeviceRuntimeResponse
{
/// <summary>
/// 运行时状态ID(单个设备启动时使用)
/// </summary>
public string? Id { get; set; }
/// <summary>
/// 设备编号(单个设备启动时使用)
/// </summary>
public string? DeviceCode { get; set; }
/// <summary>
/// 运行时状态(单个设备启动时使用)
/// </summary>
public string? RuntimeStatus { get; set; }
/// <summary>
/// 网络栈配置编号
/// </summary>
public string? NetworkStackCode { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime UpdatedAt { get; set; }
/// <summary>
/// 设备启动结果列表(批量启动时使用)
/// </summary>
public List<DeviceStartResult>? DeviceResults { get; set; }
/// <summary>
/// 批量操作统计
/// </summary>
public BatchOperationSummary? Summary { get; set; }
}
/// <summary>
/// 设备启动结果
/// </summary>
public class DeviceStartResult
{
/// <summary>
/// 设备编号
/// </summary>
public string DeviceCode { get; set; } = null!;
/// <summary>
/// 运行时状态ID
/// </summary>
public string Id { get; set; } = null!;
/// <summary>
/// 运行时状态
/// </summary>
public string RuntimeStatus { get; set; } = null!;
/// <summary>
/// 网络栈配置编号
/// </summary>
public string? NetworkStackCode { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime UpdatedAt { get; set; }
/// <summary>
/// 是否成功
/// </summary>
public bool IsSuccess { get; set; }
/// <summary>
/// 错误信息
/// </summary>
public string? ErrorMessage { get; set; }
}
/// <summary>
/// 批量操作统计
/// </summary>
public class BatchOperationSummary
{
/// <summary>
/// 总设备数
/// </summary>
public int TotalCount { get; set; }
/// <summary>
/// 成功数量
/// </summary>
public int SuccessCount { get; set; }
/// <summary>
/// 失败数量
/// </summary>
public int FailureCount { get; set; }
/// <summary>
/// 成功率
/// </summary>
public double SuccessRate => TotalCount > 0 ? (double)SuccessCount / TotalCount * 100 : 0;
}

18
src/X1.Application/Features/DeviceRuntimes/Commands/StopDeviceRuntime/StopDeviceRuntimeCommand.cs

@ -0,0 +1,18 @@
using CellularManagement.Domain.Common;
using MediatR;
using System.ComponentModel.DataAnnotations;
namespace CellularManagement.Application.Features.DeviceRuntimes.Commands.StopDeviceRuntime;
/// <summary>
/// 停止设备运行时状态命令
/// </summary>
public class StopDeviceRuntimeCommand : IRequest<OperationResult<StopDeviceRuntimeResponse>>
{
/// <summary>
/// 设备编号
/// </summary>
[Required(ErrorMessage = "设备编号不能为空")]
[MaxLength(50, ErrorMessage = "设备编号不能超过50个字符")]
public string DeviceCode { get; set; } = null!;
}

97
src/X1.Application/Features/DeviceRuntimes/Commands/StopDeviceRuntime/StopDeviceRuntimeCommandHandler.cs

@ -0,0 +1,97 @@
using MediatR;
using Microsoft.Extensions.Logging;
using CellularManagement.Domain.Common;
using CellularManagement.Domain.Entities.Device;
using CellularManagement.Domain.Repositories.Device;
using CellularManagement.Domain.Repositories.Base;
using CellularManagement.Domain.Services;
namespace CellularManagement.Application.Features.DeviceRuntimes.Commands.StopDeviceRuntime;
/// <summary>
/// 停止设备运行时状态命令处理器
/// </summary>
public class StopDeviceRuntimeCommandHandler : IRequestHandler<StopDeviceRuntimeCommand, OperationResult<StopDeviceRuntimeResponse>>
{
private readonly ICellularDeviceRuntimeRepository _deviceRuntimeRepository;
private readonly ILogger<StopDeviceRuntimeCommandHandler> _logger;
private readonly IUnitOfWork _unitOfWork;
private readonly ICurrentUserService _currentUserService;
/// <summary>
/// 初始化命令处理器
/// </summary>
public StopDeviceRuntimeCommandHandler(
ICellularDeviceRuntimeRepository deviceRuntimeRepository,
ILogger<StopDeviceRuntimeCommandHandler> logger,
IUnitOfWork unitOfWork,
ICurrentUserService currentUserService)
{
_deviceRuntimeRepository = deviceRuntimeRepository;
_logger = logger;
_unitOfWork = unitOfWork;
_currentUserService = currentUserService;
}
/// <summary>
/// 处理停止设备运行时状态命令
/// </summary>
public async Task<OperationResult<StopDeviceRuntimeResponse>> Handle(StopDeviceRuntimeCommand request, CancellationToken cancellationToken)
{
try
{
_logger.LogInformation("开始停止设备运行时状态,设备编号: {DeviceCode}", request.DeviceCode);
// 获取设备运行时状态
var deviceRuntime = await _deviceRuntimeRepository.GetRuntimeByDeviceCodeAsync(request.DeviceCode, cancellationToken);
if (deviceRuntime == null)
{
_logger.LogWarning("设备运行时状态不存在,设备编号: {DeviceCode}", request.DeviceCode);
return OperationResult<StopDeviceRuntimeResponse>.CreateFailure($"设备编号 {request.DeviceCode} 的运行时状态不存在");
}
// 检查设备是否已经在停止或未运行
if (deviceRuntime.RuntimeStatus != DeviceRuntimeStatus.Running)
{
_logger.LogWarning("设备未在运行中,设备编号: {DeviceCode}, 当前状态: {RuntimeStatus}",
request.DeviceCode, deviceRuntime.RuntimeStatus);
return OperationResult<StopDeviceRuntimeResponse>.CreateFailure($"设备编号 {request.DeviceCode} 未在运行中,当前状态: {deviceRuntime.RuntimeStatus}");
}
// 获取当前用户
var currentUser = _currentUserService.GetCurrentUserId();
if (string.IsNullOrEmpty(currentUser))
{
_logger.LogWarning("当前用户未登录");
return OperationResult<StopDeviceRuntimeResponse>.CreateFailure("用户未登录");
}
// 停止设备
deviceRuntime.Stop();
// 更新到数据库
_deviceRuntimeRepository.UpdateRuntime(deviceRuntime);
await _unitOfWork.SaveChangesAsync(cancellationToken);
_logger.LogInformation("设备停止成功,设备编号: {DeviceCode}", deviceRuntime.DeviceCode);
// 构建响应
var response = new StopDeviceRuntimeResponse
{
Id = deviceRuntime.Id,
DeviceCode = deviceRuntime.DeviceCode,
RuntimeStatus = deviceRuntime.RuntimeStatus.ToString(),
RuntimeCode = deviceRuntime.RuntimeCode,
NetworkStackCode = deviceRuntime.NetworkStackCode,
UpdatedAt = deviceRuntime.UpdatedAt ?? DateTime.UtcNow
};
return OperationResult<StopDeviceRuntimeResponse>.CreateSuccess(response);
}
catch (Exception ex)
{
_logger.LogError(ex, "停止设备运行时状态失败,设备编号: {DeviceCode}", request.DeviceCode);
return OperationResult<StopDeviceRuntimeResponse>.CreateFailure($"停止设备运行时状态失败: {ex.Message}");
}
}
}

39
src/X1.Application/Features/DeviceRuntimes/Commands/StopDeviceRuntime/StopDeviceRuntimeResponse.cs

@ -0,0 +1,39 @@
namespace CellularManagement.Application.Features.DeviceRuntimes.Commands.StopDeviceRuntime;
/// <summary>
/// 停止设备运行时状态响应
/// </summary>
public class StopDeviceRuntimeResponse
{
/// <summary>
/// 运行时状态ID
/// </summary>
public string Id { get; set; } = null!;
/// <summary>
/// 设备编号
/// </summary>
public string DeviceCode { get; set; } = null!;
/// <summary>
/// 运行时状态
/// </summary>
public string RuntimeStatus { get; set; } = null!;
/// <summary>
/// 运行编码
/// </summary>
public string? RuntimeCode { get; set; }
/// <summary>
/// 网络栈配置编号
/// </summary>
public string? NetworkStackCode { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime UpdatedAt { get; set; }
}

26
src/X1.Application/Features/DeviceRuntimes/Queries/GetDeviceRuntimeStatus/GetDeviceRuntimeStatusQuery.cs

@ -0,0 +1,26 @@
using CellularManagement.Domain.Common;
using MediatR;
using System.ComponentModel.DataAnnotations;
namespace CellularManagement.Application.Features.DeviceRuntimes.Queries.GetDeviceRuntimeStatus;
/// <summary>
/// 获取设备运行时状态查询
/// </summary>
public class GetDeviceRuntimeStatusQuery : IRequest<OperationResult<GetDeviceRuntimeStatusResponse>>
{
/// <summary>
/// 设备编号
/// </summary>
[Required(ErrorMessage = "设备编号不能为空")]
[MaxLength(50, ErrorMessage = "设备编号不能超过50个字符")]
public string DeviceCode { get; }
/// <summary>
/// 初始化查询
/// </summary>
public GetDeviceRuntimeStatusQuery(string deviceCode)
{
DeviceCode = deviceCode;
}
}

66
src/X1.Application/Features/DeviceRuntimes/Queries/GetDeviceRuntimeStatus/GetDeviceRuntimeStatusQueryHandler.cs

@ -0,0 +1,66 @@
using MediatR;
using Microsoft.Extensions.Logging;
using CellularManagement.Domain.Common;
using CellularManagement.Domain.Entities.Device;
using CellularManagement.Domain.Repositories.Device;
namespace CellularManagement.Application.Features.DeviceRuntimes.Queries.GetDeviceRuntimeStatus;
/// <summary>
/// 获取设备运行时状态查询处理器
/// </summary>
public class GetDeviceRuntimeStatusQueryHandler : IRequestHandler<GetDeviceRuntimeStatusQuery, OperationResult<GetDeviceRuntimeStatusResponse>>
{
private readonly ICellularDeviceRuntimeRepository _deviceRuntimeRepository;
private readonly ILogger<GetDeviceRuntimeStatusQueryHandler> _logger;
/// <summary>
/// 初始化查询处理器
/// </summary>
public GetDeviceRuntimeStatusQueryHandler(
ICellularDeviceRuntimeRepository deviceRuntimeRepository,
ILogger<GetDeviceRuntimeStatusQueryHandler> logger)
{
_deviceRuntimeRepository = deviceRuntimeRepository;
_logger = logger;
}
/// <summary>
/// 处理查询请求
/// </summary>
public async Task<OperationResult<GetDeviceRuntimeStatusResponse>> Handle(GetDeviceRuntimeStatusQuery request, CancellationToken cancellationToken)
{
try
{
_logger.LogInformation("开始查询设备运行时状态,设备编号: {DeviceCode}", request.DeviceCode);
// 获取设备运行时状态
var deviceRuntime = await _deviceRuntimeRepository.GetRuntimeByDeviceCodeAsync(request.DeviceCode, cancellationToken);
if (deviceRuntime == null)
{
_logger.LogWarning("未找到设备运行时状态,设备编号: {DeviceCode}", request.DeviceCode);
return OperationResult<GetDeviceRuntimeStatusResponse>.CreateFailure($"未找到设备编号为 {request.DeviceCode} 的运行时状态");
}
// 构建响应
var response = new GetDeviceRuntimeStatusResponse
{
DeviceCode = deviceRuntime.DeviceCode,
RuntimeStatus = deviceRuntime.RuntimeStatus.ToString(),
RuntimeCode = deviceRuntime.RuntimeCode,
NetworkStackCode = deviceRuntime.NetworkStackCode
};
_logger.LogInformation("查询设备运行时状态成功,设备编号: {DeviceCode}, 运行时状态: {RuntimeStatus}",
deviceRuntime.DeviceCode, deviceRuntime.RuntimeStatus);
return OperationResult<GetDeviceRuntimeStatusResponse>.CreateSuccess(response);
}
catch (Exception ex)
{
_logger.LogError(ex, "查询设备运行时状态失败,设备编号: {DeviceCode}", request.DeviceCode);
return OperationResult<GetDeviceRuntimeStatusResponse>.CreateFailure($"查询设备运行时状态失败: {ex.Message}");
}
}
}

27
src/X1.Application/Features/DeviceRuntimes/Queries/GetDeviceRuntimeStatus/GetDeviceRuntimeStatusResponse.cs

@ -0,0 +1,27 @@
namespace CellularManagement.Application.Features.DeviceRuntimes.Queries.GetDeviceRuntimeStatus;
/// <summary>
/// 获取设备运行时状态响应
/// </summary>
public class GetDeviceRuntimeStatusResponse
{
/// <summary>
/// 设备编号
/// </summary>
public string DeviceCode { get; set; } = null!;
/// <summary>
/// 运行时状态
/// </summary>
public string RuntimeStatus { get; set; } = null!;
/// <summary>
/// 运行编码
/// </summary>
public string? RuntimeCode { get; set; }
/// <summary>
/// 网络栈配置编号
/// </summary>
public string? NetworkStackCode { get; set; }
}

40
src/X1.Application/Features/DeviceRuntimes/Queries/GetDeviceRuntimes/GetDeviceRuntimesQuery.cs

@ -0,0 +1,40 @@
using CellularManagement.Domain.Common;
using MediatR;
using System.ComponentModel.DataAnnotations;
namespace CellularManagement.Application.Features.DeviceRuntimes.Queries.GetDeviceRuntimes;
/// <summary>
/// 获取设备运行时状态列表查询
/// </summary>
public class GetDeviceRuntimesQuery : IRequest<OperationResult<GetDeviceRuntimesResponse>>
{
/// <summary>
/// 页码,从1开始
/// </summary>
[Range(1, int.MaxValue, ErrorMessage = "页码必须大于0")]
public int PageNumber { get; set; } = 1;
/// <summary>
/// 每页记录数
/// </summary>
[Range(1, 100, ErrorMessage = "每页记录数必须在1-100之间")]
public int PageSize { get; set; } = 10;
/// <summary>
/// 搜索关键词
/// </summary>
[MaxLength(100, ErrorMessage = "搜索关键词不能超过100个字符")]
public string? SearchTerm { get; set; }
/// <summary>
/// 运行时状态过滤
/// </summary>
public string? RuntimeStatus { get; set; }
/// <summary>
/// 网络栈配置编号过滤
/// </summary>
[MaxLength(50, ErrorMessage = "网络栈配置编号不能超过50个字符")]
public string? NetworkStackCode { get; set; }
}

97
src/X1.Application/Features/DeviceRuntimes/Queries/GetDeviceRuntimes/GetDeviceRuntimesQueryHandler.cs

@ -0,0 +1,97 @@
using MediatR;
using Microsoft.Extensions.Logging;
using CellularManagement.Domain.Common;
using CellularManagement.Domain.Entities.Device;
using CellularManagement.Domain.Repositories.Device;
namespace CellularManagement.Application.Features.DeviceRuntimes.Queries.GetDeviceRuntimes;
/// <summary>
/// 获取设备运行时状态列表查询处理器
/// </summary>
public class GetDeviceRuntimesQueryHandler : IRequestHandler<GetDeviceRuntimesQuery, OperationResult<GetDeviceRuntimesResponse>>
{
private readonly ICellularDeviceRuntimeRepository _deviceRuntimeRepository;
private readonly ILogger<GetDeviceRuntimesQueryHandler> _logger;
/// <summary>
/// 初始化查询处理器
/// </summary>
public GetDeviceRuntimesQueryHandler(
ICellularDeviceRuntimeRepository deviceRuntimeRepository,
ILogger<GetDeviceRuntimesQueryHandler> logger)
{
_deviceRuntimeRepository = deviceRuntimeRepository;
_logger = logger;
}
/// <summary>
/// 处理查询请求
/// </summary>
public async Task<OperationResult<GetDeviceRuntimesResponse>> Handle(GetDeviceRuntimesQuery request, CancellationToken cancellationToken)
{
try
{
_logger.LogInformation("开始查询设备运行时状态列表,页码: {PageNumber}, 每页数量: {PageSize}, 搜索关键词: {SearchTerm}, 运行时状态: {RuntimeStatus}, 网络栈配置编号: {NetworkStackCode}",
request.PageNumber, request.PageSize, request.SearchTerm, request.RuntimeStatus, request.NetworkStackCode);
// 执行分页查询
var (totalCount, items) = await _deviceRuntimeRepository.SearchRuntimesAsync(
request.SearchTerm,
request.PageNumber,
request.PageSize,
cancellationToken);
// 计算总页数
var totalPages = (int)Math.Ceiling((double)totalCount / request.PageSize);
// 构建响应
var response = new GetDeviceRuntimesResponse
{
TotalCount = totalCount,
PageNumber = request.PageNumber,
PageSize = request.PageSize,
TotalPages = totalPages,
Items = items.Select(MapToDto).ToList()
};
_logger.LogInformation("查询设备运行时状态列表成功,总记录数: {TotalCount}, 总页数: {TotalPages}", totalCount, totalPages);
return OperationResult<GetDeviceRuntimesResponse>.CreateSuccess(response);
}
catch (Exception ex)
{
_logger.LogError(ex, "查询设备运行时状态列表失败");
return OperationResult<GetDeviceRuntimesResponse>.CreateFailure($"查询设备运行时状态列表失败: {ex.Message}");
}
}
/// <summary>
/// 映射到数据传输对象
/// </summary>
private static DeviceRuntimeDto MapToDto(CellularDeviceRuntime runtime)
{
return new DeviceRuntimeDto
{
Id = runtime.Id,
DeviceCode = runtime.DeviceCode,
RuntimeStatus = runtime.RuntimeStatus.ToString(),
RuntimeCode = runtime.RuntimeCode,
NetworkStackCode = runtime.NetworkStackCode,
CreatedAt = runtime.CreatedAt,
UpdatedAt = runtime.UpdatedAt ?? DateTime.UtcNow,
Device = runtime.Device != null ? new DeviceInfoDto
{
Id = runtime.Device.Id,
Name = runtime.Device.Name,
SerialNumber = runtime.Device.SerialNumber,
DeviceCode = runtime.Device.DeviceCode,
Description = runtime.Device.Description,
IpAddress = runtime.Device.IpAddress,
AgentPort = runtime.Device.AgentPort,
IsEnabled = runtime.Device.IsEnabled
} : null
};
}
}

126
src/X1.Application/Features/DeviceRuntimes/Queries/GetDeviceRuntimes/GetDeviceRuntimesResponse.cs

@ -0,0 +1,126 @@
namespace CellularManagement.Application.Features.DeviceRuntimes.Queries.GetDeviceRuntimes;
/// <summary>
/// 获取设备运行时状态列表响应
/// </summary>
public class GetDeviceRuntimesResponse
{
/// <summary>
/// 总记录数
/// </summary>
public int TotalCount { get; set; }
/// <summary>
/// 当前页码
/// </summary>
public int PageNumber { get; set; }
/// <summary>
/// 每页记录数
/// </summary>
public int PageSize { get; set; }
/// <summary>
/// 总页数
/// </summary>
public int TotalPages { get; set; }
/// <summary>
/// 设备运行时状态列表
/// </summary>
public List<DeviceRuntimeDto> Items { get; set; } = new();
}
/// <summary>
/// 设备运行时状态数据传输对象
/// </summary>
public class DeviceRuntimeDto
{
/// <summary>
/// 运行时状态ID
/// </summary>
public string Id { get; set; } = null!;
/// <summary>
/// 设备编号
/// </summary>
public string DeviceCode { get; set; } = null!;
/// <summary>
/// 运行时状态
/// </summary>
public string RuntimeStatus { get; set; } = null!;
/// <summary>
/// 运行编码
/// </summary>
public string? RuntimeCode { get; set; }
/// <summary>
/// 网络栈配置编号
/// </summary>
public string? NetworkStackCode { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedAt { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime UpdatedAt { get; set; }
/// <summary>
/// 设备信息
/// </summary>
public DeviceInfoDto? Device { get; set; }
}
/// <summary>
/// 设备信息数据传输对象
/// </summary>
public class DeviceInfoDto
{
/// <summary>
/// 设备ID
/// </summary>
public string Id { get; set; } = null!;
/// <summary>
/// 设备名称
/// </summary>
public string Name { get; set; } = null!;
/// <summary>
/// 设备序列号
/// </summary>
public string SerialNumber { get; set; } = null!;
/// <summary>
/// 设备编码
/// </summary>
public string DeviceCode { get; set; } = null!;
/// <summary>
/// 设备描述
/// </summary>
public string Description { get; set; } = null!;
/// <summary>
/// IP地址
/// </summary>
public string IpAddress { get; set; } = null!;
/// <summary>
/// Agent端口
/// </summary>
public int AgentPort { get; set; }
/// <summary>
/// 是否启用
/// </summary>
public bool IsEnabled { get; set; }
}

7
src/X1.Application/Features/Devices/Commands/CreateDevice/CreateDeviceCommand.cs

@ -17,8 +17,6 @@ public class CreateDeviceCommand : IRequest<OperationResult<CreateDeviceResponse
[MaxLength(100, ErrorMessage = "设备名称不能超过100个字符")]
public string DeviceName { get; set; }
/// <summary>
/// 设备描述
/// </summary>
@ -42,9 +40,4 @@ public class CreateDeviceCommand : IRequest<OperationResult<CreateDeviceResponse
/// 是否启用
/// </summary>
public bool IsEnabled { get; set; } = true;
/// <summary>
/// 设备状态(启动/未启动)
/// </summary>
public bool IsRunning { get; set; } = false;
}

33
src/X1.Application/Features/Devices/Commands/CreateDevice/CreateDeviceCommandHandler.cs

@ -18,6 +18,7 @@ namespace CellularManagement.Application.Features.Devices.Commands.CreateDevice;
public class CreateDeviceCommandHandler : IRequestHandler<CreateDeviceCommand, OperationResult<CreateDeviceResponse>>
{
private readonly ICellularDeviceRepository _deviceRepository;
private readonly ICellularDeviceRuntimeRepository _deviceRuntimeRepository;
private readonly ILogger<CreateDeviceCommandHandler> _logger;
private readonly IUnitOfWork _unitOfWork;
private readonly ICurrentUserService _currentUserService;
@ -29,6 +30,7 @@ public class CreateDeviceCommandHandler : IRequestHandler<CreateDeviceCommand, O
/// </summary>
public CreateDeviceCommandHandler(
ICellularDeviceRepository deviceRepository,
ICellularDeviceRuntimeRepository deviceRuntimeRepository,
ILogger<CreateDeviceCommandHandler> logger,
IUnitOfWork unitOfWork,
ICurrentUserService currentUserService,
@ -36,6 +38,7 @@ public class CreateDeviceCommandHandler : IRequestHandler<CreateDeviceCommand, O
IServiceEndpointManager endpointManager)
{
_deviceRepository = deviceRepository;
_deviceRuntimeRepository = deviceRuntimeRepository;
_logger = logger;
_unitOfWork = unitOfWork;
_currentUserService = currentUserService;
@ -101,8 +104,8 @@ public class CreateDeviceCommandHandler : IRequestHandler<CreateDeviceCommand, O
return OperationResult<CreateDeviceResponse>.CreateFailure("生成设备编号失败");
}
// 创建设备并保存
var deviceResult = await CreateAndSaveDeviceAsync(request, serialNumber, deviceCode, currentUserId.Data, cancellationToken);
// 创建设备和运行时状态并保存(原子性操作)
var deviceResult = await CreateAndSaveDeviceWithRuntimeAsync(request, serialNumber, deviceCode, currentUserId.Data, cancellationToken);
if (!deviceResult.IsSuccess)
{
return OperationResult<CreateDeviceResponse>.CreateFailure(deviceResult.ErrorMessages);
@ -124,7 +127,6 @@ public class CreateDeviceCommandHandler : IRequestHandler<CreateDeviceCommand, O
Description = device.Description,
AgentPort = device.AgentPort,
IsEnabled = device.IsEnabled,
IsRunning = device.IsRunning,
CreatedAt = device.CreatedAt
};
@ -276,9 +278,9 @@ public class CreateDeviceCommandHandler : IRequestHandler<CreateDeviceCommand, O
}
/// <summary>
/// 创建设备并保存到数据库
/// 创建设备和运行时状态并保存到数据库(原子性操作)
/// </summary>
private async Task<OperationResult<CellularDevice>> CreateAndSaveDeviceAsync(
private async Task<OperationResult<CellularDevice>> CreateAndSaveDeviceWithRuntimeAsync(
CreateDeviceCommand request,
string serialNumber,
string deviceCode,
@ -296,21 +298,32 @@ public class CreateDeviceCommandHandler : IRequestHandler<CreateDeviceCommand, O
agentPort: request.AgentPort,
ipAddress: request.IpAddress,
createdBy: currentUserId,
isEnabled: request.IsEnabled,
isRunning: request.IsRunning);
isEnabled: request.IsEnabled);
// 创建设备运行时状态实体
var runtime = CellularDeviceRuntime.Create(
deviceCode: device.DeviceCode,
createdBy: currentUserId,
runtimeStatus: DeviceRuntimeStatus.Initializing,
networkStackCode: null);
// 保存设备
await _deviceRepository.AddDeviceAsync(device, cancellationToken);
// 保存更改到数据库
// 保存设备运行时状态
await _deviceRuntimeRepository.AddRuntimeAsync(runtime, cancellationToken);
// 原子性保存所有更改到数据库
await _unitOfWork.SaveChangesAsync(cancellationToken);
_logger.LogInformation("设备和运行时状态创建成功,设备编号: {DeviceCode}", device.DeviceCode);
return OperationResult<CellularDevice>.CreateSuccess(device);
}
catch (Exception ex)
{
_logger.LogError(ex, "创建设备实体时发生错误,设备名称: {DeviceName}", request.DeviceName);
return OperationResult<CellularDevice>.CreateFailure($"创建设备时发生错误: {ex.Message}");
_logger.LogError(ex, "创建设备和运行时状态时发生错误,设备名称: {DeviceName}", request.DeviceName);
return OperationResult<CellularDevice>.CreateFailure($"创建设备和运行时状态时发生错误: {ex.Message}");
}
}
}

5
src/X1.Application/Features/Devices/Commands/CreateDevice/CreateDeviceResponse.cs

@ -38,11 +38,6 @@ public class CreateDeviceResponse
/// </summary>
public bool IsEnabled { get; set; }
/// <summary>
/// 设备状态(启动/未启动)
/// </summary>
public bool IsRunning { get; set; }
/// <summary>
/// 创建时间
/// </summary>

7
src/X1.Application/Features/Devices/Commands/DeleteDevice/DeleteDeviceCommandHandler.cs

@ -13,6 +13,7 @@ namespace CellularManagement.Application.Features.Devices.Commands.DeleteDevice;
public class DeleteDeviceCommandHandler : IRequestHandler<DeleteDeviceCommand, OperationResult<bool>>
{
private readonly ICellularDeviceRepository _deviceRepository;
private readonly ICellularDeviceRuntimeRepository _runtimeRepository;
private readonly ILogger<DeleteDeviceCommandHandler> _logger;
private readonly IUnitOfWork _unitOfWork;
@ -21,10 +22,12 @@ public class DeleteDeviceCommandHandler : IRequestHandler<DeleteDeviceCommand, O
/// </summary>
public DeleteDeviceCommandHandler(
ICellularDeviceRepository deviceRepository,
ICellularDeviceRuntimeRepository runtimeRepository,
ILogger<DeleteDeviceCommandHandler> logger,
IUnitOfWork unitOfWork)
{
_deviceRepository = deviceRepository;
_runtimeRepository = runtimeRepository;
_logger = logger;
_unitOfWork = unitOfWork;
}
@ -46,6 +49,10 @@ public class DeleteDeviceCommandHandler : IRequestHandler<DeleteDeviceCommand, O
return OperationResult<bool>.CreateFailure($"未找到ID为 {request.DeviceId} 的设备");
}
// 软删除相关的设备运行时状态记录
await _runtimeRepository.DeleteRuntimeByDeviceCodeAsync(device.DeviceCode, cancellationToken);
_logger.LogInformation("已软删除设备运行时状态记录,设备编号: {DeviceCode}", device.DeviceCode);
// 删除设备
await _deviceRepository.DeleteDeviceAsync(request.DeviceId, cancellationToken);

5
src/X1.Application/Features/Devices/Commands/UpdateDevice/UpdateDeviceCommand.cs

@ -33,9 +33,4 @@ public class UpdateDeviceCommand : IRequest<OperationResult<UpdateDeviceResponse
/// 是否启用
/// </summary>
public bool IsEnabled { get; set; } = true;
/// <summary>
/// 设备状态(启动/未启动)
/// </summary>
public bool IsRunning { get; set; } = false;
}

4
src/X1.Application/Features/Devices/Commands/UpdateDevice/UpdateDeviceCommandHandler.cs

@ -71,8 +71,7 @@ public class UpdateDeviceCommandHandler : IRequestHandler<UpdateDeviceCommand, O
agentPort: existingDevice.AgentPort, // 保持原有端口不变
ipAddress: existingDevice.IpAddress, // 保持原有IP地址不变
updatedBy: currentUserId,
isEnabled: request.IsEnabled,
isRunning: request.IsRunning);
isEnabled: request.IsEnabled);
_deviceRepository.UpdateDevice(existingDevice);
@ -89,7 +88,6 @@ public class UpdateDeviceCommandHandler : IRequestHandler<UpdateDeviceCommand, O
Description = existingDevice.Description,
AgentPort = existingDevice.AgentPort,
IsEnabled = existingDevice.IsEnabled,
IsRunning = existingDevice.IsRunning,
UpdatedAt = existingDevice.UpdatedAt
};

5
src/X1.Application/Features/Devices/Commands/UpdateDevice/UpdateDeviceResponse.cs

@ -42,11 +42,6 @@ public class UpdateDeviceResponse
/// </summary>
public bool IsEnabled { get; set; }
/// <summary>
/// 设备状态(启动/未启动)
/// </summary>
public bool IsRunning { get; set; }
/// <summary>
/// 更新时间
/// </summary>

6
src/X1.Application/Features/Devices/Queries/GetDeviceById/GetDeviceByIdQueryHandler.cs

@ -35,7 +35,7 @@ public class GetDeviceByIdQueryHandler : IRequestHandler<GetDeviceByIdQuery, Ope
{
_logger.LogInformation("开始查询设备,设备ID: {DeviceId}", request.DeviceId);
var device = await _deviceRepository.GetDeviceByIdAsync(request.DeviceId, cancellationToken);
var device = await _deviceRepository.GetDeviceByIdWithRuntimeAsync(request.DeviceId, cancellationToken);
if (device == null)
{
@ -51,8 +51,8 @@ public class GetDeviceByIdQueryHandler : IRequestHandler<GetDeviceByIdQuery, Ope
Description = device.Description,
AgentPort = device.AgentPort,
IsEnabled = device.IsEnabled,
IsRunning = device.IsRunning,
CreatedAt = device.CreatedAt
CreatedAt = device.CreatedAt,
RuntimeStatus = device.Runtime?.RuntimeStatus != null ? (int)device.Runtime.RuntimeStatus : -1
};
_logger.LogInformation("成功查询到设备,设备ID: {DeviceId}", request.DeviceId);

8
src/X1.Application/Features/Devices/Queries/GetDeviceById/GetDeviceByIdResponse.cs

@ -38,12 +38,12 @@ public class GetDeviceByIdResponse
public bool IsEnabled { get; set; }
/// <summary>
/// 设备状态(启动/未启动)
/// 创建时间
/// </summary>
public bool IsRunning { get; set; }
public DateTime CreatedAt { get; set; }
/// <summary>
/// 创建时间
/// 运行时状态(-1表示未知状态)
/// </summary>
public DateTime CreatedAt { get; set; }
public int RuntimeStatus { get; set; }
}

22
src/X1.Application/Features/Devices/Queries/GetDevices/GetDevicesQueryHandler.cs

@ -49,8 +49,8 @@ public class GetDevicesQueryHandler : IRequestHandler<GetDevicesQuery, Operation
_logger.LogInformation("开始获取设备列表,页码: {PageNumber}, 每页数量: {PageSize}, 搜索关键词: {SearchTerm}",
request.PageNumber, request.PageSize, request.SearchTerm);
// 获取分页数据
var devices = await _deviceRepository.SearchDevicesAsync(
// 获取分页数据(包含运行时状态)
var devices = await _deviceRepository.SearchDevicesWithRuntimeAsync(
request.SearchTerm,
request.PageNumber,
request.PageSize,
@ -65,16 +65,16 @@ public class GetDevicesQueryHandler : IRequestHandler<GetDevicesQuery, Operation
TotalPages = (int)Math.Ceiling(devices.TotalCount / (double)request.PageSize),
HasPreviousPage = request.PageNumber > 1,
HasNextPage = request.PageNumber < (int)Math.Ceiling(devices.TotalCount / (double)request.PageSize),
Items = devices.Items.Select(device => new GetDeviceByIdResponse
Items = devices.Items.Select(item => new GetDeviceByIdResponse
{
DeviceId = device.Id,
DeviceName = device.Name,
DeviceCode = device.DeviceCode,
Description = device.Description,
AgentPort = device.AgentPort,
IsEnabled = device.IsEnabled,
IsRunning = device.IsRunning,
CreatedAt = device.CreatedAt
DeviceId = item.Device.Id,
DeviceName = item.Device.Name,
DeviceCode = item.Device.DeviceCode,
Description = item.Device.Description,
AgentPort = item.Device.AgentPort,
IsEnabled = item.Device.IsEnabled,
CreatedAt = item.Device.CreatedAt,
RuntimeStatus = item.Runtime?.RuntimeStatus != null ? (int)item.Runtime.RuntimeStatus : -1
}).ToList()
};

3
src/X1.Domain/Entities/Common/BaseEntity.cs

@ -1,11 +1,12 @@
using System.ComponentModel.DataAnnotations;
using CellularManagement.Domain.Abstractions;
namespace CellularManagement.Domain.Entities.Common;
/// <summary>
/// 基础实体类
/// </summary>
public abstract class BaseEntity
public abstract class BaseEntity : ISoftDelete
{
/// <summary>
/// 实体ID

28
src/X1.Domain/Entities/Device/CellularDevice.cs

@ -64,9 +64,9 @@ public class CellularDevice : AuditableEntity
public bool IsEnabled { get; private set; } = true;
/// <summary>
/// 设备状态(启动/未启动)
/// 设备运行时状态
/// </summary>
public bool IsRunning { get; private set; } = false;
public virtual CellularDeviceRuntime? Runtime { get; private set; }
/// <summary>
/// 创建设备
@ -79,8 +79,7 @@ public class CellularDevice : AuditableEntity
int agentPort,
string ipAddress,
string createdBy,
bool isEnabled = true,
bool isRunning = false)
bool isEnabled = true)
{
var device = new CellularDevice
{
@ -92,7 +91,6 @@ public class CellularDevice : AuditableEntity
AgentPort = agentPort,
IpAddress = ipAddress,
IsEnabled = isEnabled,
IsRunning = isRunning,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow,
CreatedBy = createdBy,
@ -113,8 +111,7 @@ public class CellularDevice : AuditableEntity
int agentPort,
string ipAddress,
string updatedBy,
bool isEnabled = true,
bool isRunning = false)
bool isEnabled = true)
{
Name = name;
SerialNumber = serialNumber;
@ -123,7 +120,6 @@ public class CellularDevice : AuditableEntity
AgentPort = agentPort;
IpAddress = ipAddress;
IsEnabled = isEnabled;
IsRunning = isRunning;
UpdatedAt = DateTime.UtcNow;
UpdatedBy = updatedBy;
}
@ -146,21 +142,5 @@ public class CellularDevice : AuditableEntity
UpdatedAt = DateTime.UtcNow;
}
/// <summary>
/// 启动设备
/// </summary>
public void Start()
{
IsRunning = true;
UpdatedAt = DateTime.UtcNow;
}
/// <summary>
/// 停止设备
/// </summary>
public void Stop()
{
IsRunning = false;
UpdatedAt = DateTime.UtcNow;
}
}

84
src/X1.Domain/Entities/Device/CellularDeviceRuntime.cs

@ -0,0 +1,84 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using CellularManagement.Domain.Entities.Common;
namespace CellularManagement.Domain.Entities.Device;
/// <summary>
/// 蜂窝设备运行时状态实体
/// </summary>
public class CellularDeviceRuntime : BaseEntity
{
private CellularDeviceRuntime() { }
/// <summary>
/// 设备编号
/// </summary>
[Required]
[MaxLength(50)]
public string DeviceCode { get; private set; } = null!;
/// <summary>
/// 运行时状态
/// </summary>
public DeviceRuntimeStatus RuntimeStatus { get; private set; } = DeviceRuntimeStatus.Initializing;
/// <summary>
/// 运行编码(仅在Running状态时生成)
/// </summary>
[MaxLength(50)]
public string? RuntimeCode { get; private set; }
/// <summary>
/// 网络栈配置编号
/// </summary>
[MaxLength(50)]
public string? NetworkStackCode { get; private set; }
/// <summary>
/// 设备实例
/// </summary>
public virtual CellularDevice Device { get; private set; } = null!;
/// <summary>
/// 创建设备运行时状态
/// </summary>
public static CellularDeviceRuntime Create(
string deviceCode,
string createdBy,
DeviceRuntimeStatus runtimeStatus = DeviceRuntimeStatus.Initializing,
string? networkStackCode = null)
{
var runtime = new CellularDeviceRuntime
{
Id = Guid.NewGuid().ToString(),
DeviceCode = deviceCode,
RuntimeStatus = runtimeStatus,
RuntimeCode = null, // 运行编码由业务层设置
NetworkStackCode = networkStackCode,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
};
return runtime;
}
/// <summary>
/// 启动设备
/// </summary>
public void Start(string networkStackCode, string runtimeCode)
{
RuntimeStatus = DeviceRuntimeStatus.Running;
NetworkStackCode = networkStackCode;
RuntimeCode = runtimeCode;
}
/// <summary>
/// 停止设备
/// </summary>
public void Stop()
{
RuntimeStatus = DeviceRuntimeStatus.Stopping;
RuntimeCode = null;
}
}

55
src/X1.Domain/Entities/Device/CellularDeviceRuntimeDetail.cs

@ -0,0 +1,55 @@
using System.ComponentModel.DataAnnotations;
using CellularManagement.Domain.Abstractions;
namespace CellularManagement.Domain.Entities.Device;
/// <summary>
/// 蜂窝设备运行时明细表实体
/// </summary>
public class CellularDeviceRuntimeDetail : Entity
{
private CellularDeviceRuntimeDetail() { }
/// <summary>
/// 运行编码
/// </summary>
[Required]
[MaxLength(50)]
public string RuntimeCode { get; private set; } = null!;
/// <summary>
/// 运行时状态(true=运行中,false=已停止)
/// </summary>
public bool RuntimeStatus { get; private set; } = false;
/// <summary>
/// 创建人ID
/// </summary>
[MaxLength(450)]
public string? CreatedBy { get; private set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedAt { get; private set; }
/// <summary>
/// 创建设备运行时明细记录
/// </summary>
public static CellularDeviceRuntimeDetail Create(
string runtimeCode,
string createdBy,
bool runtimeStatus = false)
{
var detail = new CellularDeviceRuntimeDetail
{
Id = Guid.NewGuid().ToString(),
RuntimeCode = runtimeCode,
RuntimeStatus = runtimeStatus,
CreatedBy = createdBy,
CreatedAt = DateTime.UtcNow
};
return detail;
}
}

106
src/X1.Domain/Entities/Device/CellularDeviceRuntimeHistory.cs

@ -0,0 +1,106 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using CellularManagement.Domain.Entities.Common;
namespace CellularManagement.Domain.Entities.Device;
/// <summary>
/// 蜂窝设备运行时历史记录实体
/// </summary>
public class CellularDeviceRuntimeHistory : BaseEntity
{
private CellularDeviceRuntimeHistory() { }
/// <summary>
/// 运行时ID(外键)
/// </summary>
[Required]
[MaxLength(450)]
public string RuntimeId { get; private set; } = null!;
/// <summary>
/// 设备编号
/// </summary>
[Required]
[MaxLength(50)]
public string DeviceCode { get; private set; } = null!;
/// <summary>
/// 操作前状态
/// </summary>
public DeviceRuntimeStatus PreviousStatus { get; private set; }
/// <summary>
/// 操作后状态
/// </summary>
public DeviceRuntimeStatus NewStatus { get; private set; }
/// <summary>
/// 运行编码
/// </summary>
[MaxLength(50)]
public string? RuntimeCode { get; private set; }
/// <summary>
/// 网络栈配置编号
/// </summary>
[MaxLength(50)]
public string? NetworkStackCode { get; private set; }
/// <summary>
/// 操作人ID
/// </summary>
[Required]
[MaxLength(450)]
public string OperatorId { get; private set; } = null!;
/// <summary>
/// 操作描述
/// </summary>
[Required]
[MaxLength(200)]
public string OperationDescription { get; private set; } = null!;
/// <summary>
/// 操作时间
/// </summary>
public DateTime OperationAt { get; private set; }
/// <summary>
/// 运行时实例
/// </summary>
public virtual CellularDeviceRuntime Runtime { get; private set; } = null!;
/// <summary>
/// 创建设备运行时历史记录
/// </summary>
public static CellularDeviceRuntimeHistory Create(
string runtimeId,
string deviceCode,
DeviceRuntimeStatus previousStatus,
DeviceRuntimeStatus newStatus,
string? runtimeCode,
string? networkStackCode,
string operatorId,
string operationDescription,
DateTime operationAt)
{
var history = new CellularDeviceRuntimeHistory
{
Id = Guid.NewGuid().ToString(),
RuntimeId = runtimeId,
DeviceCode = deviceCode,
PreviousStatus = previousStatus,
NewStatus = newStatus,
RuntimeCode = runtimeCode,
NetworkStackCode = networkStackCode,
OperatorId = operatorId,
OperationDescription = operationDescription,
OperationAt = operationAt,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
};
return history;
}
}

22
src/X1.Domain/Entities/Device/DeviceConnectionStatus.cs

@ -0,0 +1,22 @@
namespace CellularManagement.Domain.Entities.Device;
/// <summary>
/// 设备连接状态枚举
/// </summary>
public enum DeviceConnectionStatus
{
/// <summary>
/// 初始化
/// </summary>
Initializing = 0,
/// <summary>
/// 未连接
/// </summary>
Disconnected = 1,
/// <summary>
/// 已连接
/// </summary>
Connected = 2
}

22
src/X1.Domain/Entities/Device/DeviceRuntimeStatus.cs

@ -0,0 +1,22 @@
namespace CellularManagement.Domain.Entities.Device;
/// <summary>
/// 设备运行时状态枚举
/// </summary>
public enum DeviceRuntimeStatus
{
/// <summary>
/// 初始化
/// </summary>
Initializing = 0,
/// <summary>
/// 运行中
/// </summary>
Running = 1,
/// <summary>
/// 停止中
/// </summary>
Stopping = 2
}

14
src/X1.Domain/Repositories/Device/ICellularDeviceRepository.cs

@ -35,6 +35,11 @@ public interface ICellularDeviceRepository : IBaseRepository<CellularDevice>
/// </summary>
Task<CellularDevice?> GetDeviceByIdAsync(string id, CancellationToken cancellationToken = default);
/// <summary>
/// 根据ID获取蜂窝设备(包含运行时状态)
/// </summary>
Task<CellularDevice?> GetDeviceByIdWithRuntimeAsync(string id, CancellationToken cancellationToken = default);
/// <summary>
/// 根据序列号获取蜂窝设备
/// </summary>
@ -56,6 +61,15 @@ public interface ICellularDeviceRepository : IBaseRepository<CellularDevice>
int pageSize,
CancellationToken cancellationToken = default);
/// <summary>
/// 搜索蜂窝设备(包含运行时状态)
/// </summary>
Task<(int TotalCount, IList<(CellularDevice Device, CellularDeviceRuntime? Runtime)> Items)> SearchDevicesWithRuntimeAsync(
string? keyword,
int pageNumber,
int pageSize,
CancellationToken cancellationToken = default);
/// <summary>
/// 检查蜂窝设备是否存在
/// </summary>

71
src/X1.Domain/Repositories/Device/ICellularDeviceRuntimeDetailRepository.cs

@ -0,0 +1,71 @@
using CellularManagement.Domain.Entities.Device;
using CellularManagement.Domain.Repositories.Base;
namespace CellularManagement.Domain.Repositories.Device;
/// <summary>
/// 蜂窝设备运行时明细仓储接口
/// </summary>
public interface ICellularDeviceRuntimeDetailRepository : IBaseRepository<CellularDeviceRuntimeDetail>
{
/// <summary>
/// 根据运行编码获取运行时明细列表
/// </summary>
/// <param name="runtimeCode">运行编码</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>运行时明细列表</returns>
Task<List<CellularDeviceRuntimeDetail>> GetDetailsByRuntimeCodeAsync(string runtimeCode, CancellationToken cancellationToken = default);
/// <summary>
/// 根据创建人获取运行时明细列表
/// </summary>
/// <param name="createdBy">创建人ID</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>运行时明细列表</returns>
Task<List<CellularDeviceRuntimeDetail>> GetDetailsByCreatedByAsync(string createdBy, CancellationToken cancellationToken = default);
/// <summary>
/// 根据运行时状态获取运行时明细列表
/// </summary>
/// <param name="runtimeStatus">运行时状态(true=运行中,false=已停止)</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>运行时明细列表</returns>
Task<List<CellularDeviceRuntimeDetail>> GetDetailsByRuntimeStatusAsync(bool runtimeStatus, CancellationToken cancellationToken = default);
/// <summary>
/// 分页查询运行时明细
/// </summary>
/// <param name="runtimeCode">运行编码(可选)</param>
/// <param name="createdBy">创建人(可选)</param>
/// <param name="runtimeStatus">运行时状态(可选,true=运行中,false=已停止)</param>
/// <param name="startDate">开始日期(可选)</param>
/// <param name="endDate">结束日期(可选)</param>
/// <param name="pageNumber">页码</param>
/// <param name="pageSize">每页数量</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>分页结果</returns>
Task<(int TotalCount, List<CellularDeviceRuntimeDetail> Items)> SearchDetailsAsync(
string? runtimeCode = null,
string? createdBy = null,
bool? runtimeStatus = null,
DateTime? startDate = null,
DateTime? endDate = null,
int pageNumber = 1,
int pageSize = 10,
CancellationToken cancellationToken = default);
/// <summary>
/// 获取运行时明细总数
/// </summary>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>总数</returns>
Task<int> GetDetailCountAsync(CancellationToken cancellationToken = default);
/// <summary>
/// 批量添加运行时明细
/// </summary>
/// <param name="details">明细列表</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>添加后的明细列表</returns>
Task<IEnumerable<CellularDeviceRuntimeDetail>> AddDetailsRangeAsync(IEnumerable<CellularDeviceRuntimeDetail> details, CancellationToken cancellationToken = default);
}

107
src/X1.Domain/Repositories/Device/ICellularDeviceRuntimeRepository.cs

@ -0,0 +1,107 @@
using CellularManagement.Domain.Entities.Device;
using CellularManagement.Domain.Repositories.Base;
namespace CellularManagement.Domain.Repositories.Device;
/// <summary>
/// 蜂窝设备运行时状态仓储接口
/// 组合命令和查询仓储接口
/// </summary>
public interface ICellularDeviceRuntimeRepository : IBaseRepository<CellularDeviceRuntime>
{
/// <summary>
/// 添加设备运行时状态
/// </summary>
Task<CellularDeviceRuntime> AddRuntimeAsync(CellularDeviceRuntime runtime, CancellationToken cancellationToken = default);
/// <summary>
/// 更新设备运行时状态
/// </summary>
void UpdateRuntime(CellularDeviceRuntime runtime);
/// <summary>
/// 删除设备运行时状态
/// </summary>
Task DeleteRuntimeAsync(string id, CancellationToken cancellationToken = default);
/// <summary>
/// 获取所有设备运行时状态
/// </summary>
Task<IList<CellularDeviceRuntime>> GetAllRuntimesAsync(CancellationToken cancellationToken = default);
/// <summary>
/// 根据ID获取设备运行时状态
/// </summary>
Task<CellularDeviceRuntime?> GetRuntimeByIdAsync(string id, CancellationToken cancellationToken = default);
/// <summary>
/// 根据设备编号获取运行时状态
/// </summary>
Task<CellularDeviceRuntime?> GetRuntimeByDeviceCodeAsync(string deviceCode, CancellationToken cancellationToken = default);
/// <summary>
/// 根据设备编号获取运行时状态(包含设备信息)
/// </summary>
Task<CellularDeviceRuntime?> GetRuntimeByDeviceCodeWithDeviceAsync(string deviceCode, CancellationToken cancellationToken = default);
/// <summary>
/// 根据运行编码获取运行时状态
/// </summary>
Task<CellularDeviceRuntime?> GetRuntimeByRuntimeCodeAsync(string runtimeCode, CancellationToken cancellationToken = default);
/// <summary>
/// 获取正在运行的设备运行时状态
/// </summary>
Task<IList<CellularDeviceRuntime>> GetRunningRuntimesAsync(CancellationToken cancellationToken = default);
/// <summary>
/// 根据运行时状态获取设备运行时状态
/// </summary>
Task<IList<CellularDeviceRuntime>> GetRuntimesByStatusAsync(DeviceRuntimeStatus runtimeStatus, CancellationToken cancellationToken = default);
/// <summary>
/// 根据网络栈配置编号获取运行时状态
/// </summary>
Task<IList<CellularDeviceRuntime>> GetRuntimesByNetworkStackCodeAsync(string networkStackCode, CancellationToken cancellationToken = default);
/// <summary>
/// 搜索设备运行时状态
/// </summary>
Task<IList<CellularDeviceRuntime>> SearchRuntimesAsync(
string? keyword,
CancellationToken cancellationToken = default);
/// <summary>
/// 搜索设备运行时状态(分页)
/// </summary>
Task<(int TotalCount, IList<CellularDeviceRuntime> Items)> SearchRuntimesAsync(
string? keyword,
int pageNumber,
int pageSize,
CancellationToken cancellationToken = default);
/// <summary>
/// 检查设备运行时状态是否存在
/// </summary>
Task<bool> ExistsAsync(string id, CancellationToken cancellationToken = default);
/// <summary>
/// 检查设备编号是否有运行时状态
/// </summary>
Task<bool> DeviceCodeExistsAsync(string deviceCode, CancellationToken cancellationToken = default);
/// <summary>
/// 检查运行编码是否存在
/// </summary>
Task<bool> RuntimeCodeExistsAsync(string runtimeCode, CancellationToken cancellationToken = default);
/// <summary>
/// 获取运行时状态总数
/// </summary>
Task<int> GetRuntimeCountAsync(CancellationToken cancellationToken = default);
/// <summary>
/// 根据设备编号删除运行时状态
/// </summary>
Task DeleteRuntimeByDeviceCodeAsync(string deviceCode, CancellationToken cancellationToken = default);
}

1
src/X1.Infrastructure/Configurations/Device/CellularDeviceConfiguration.cs

@ -24,7 +24,6 @@ public class CellularDeviceConfiguration : IEntityTypeConfiguration<CellularDevi
builder.Property(d => d.AgentPort).IsRequired().HasComment("Agent端口");
builder.Property(d => d.IpAddress).IsRequired().HasMaxLength(45).HasComment("IP地址");
builder.Property(d => d.IsEnabled).IsRequired().HasComment("是否启用");
builder.Property(d => d.IsRunning).IsRequired().HasComment("设备状态(启动/未启动)");
builder.Property(d => d.CreatedAt).IsRequired().HasColumnType("timestamp with time zone").HasComment("创建时间");
builder.Property(d => d.UpdatedAt).IsRequired().HasColumnType("timestamp with time zone").HasComment("更新时间");

66
src/X1.Infrastructure/Configurations/Device/CellularDeviceRuntimeConfiguration.cs

@ -0,0 +1,66 @@
using CellularManagement.Domain.Entities.Device;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CellularManagement.Infrastructure.Configurations.Device;
/// <summary>
/// 蜂窝设备运行时状态配置
/// </summary>
public class CellularDeviceRuntimeConfiguration : IEntityTypeConfiguration<CellularDeviceRuntime>
{
public void Configure(EntityTypeBuilder<CellularDeviceRuntime> builder)
{
builder.ToTable("CellularDeviceRuntimes");
builder.HasKey(x => x.Id);
// 基础属性配置
builder.Property(x => x.DeviceCode)
.IsRequired()
.HasMaxLength(50);
builder.Property(x => x.RuntimeStatus)
.IsRequired()
.HasConversion<int>()
.HasDefaultValue(DeviceRuntimeStatus.Initializing);
builder.Property(x => x.RuntimeCode)
.HasMaxLength(50);
builder.Property(x => x.NetworkStackCode)
.HasMaxLength(50);
// BaseEntity 属性配置
builder.Property(x => x.CreatedAt)
.IsRequired();
builder.Property(x => x.UpdatedAt);
builder.Property(x => x.IsDeleted)
.HasDefaultValue(false);
// 配置与 CellularDevice 的关系
builder.HasOne(x => x.Device)
.WithOne(x => x.Runtime)
.HasForeignKey<CellularDeviceRuntime>(x => x.DeviceCode)
.HasPrincipalKey<CellularDevice>(x => x.DeviceCode)
.OnDelete(DeleteBehavior.Cascade);
// 创建索引
builder.HasIndex(x => x.DeviceCode)
.IsUnique();
// 复合索引:优化按设备编号和创建时间排序的查询
builder.HasIndex(x => new { x.DeviceCode, x.CreatedAt })
.HasDatabaseName("IX_CellularDeviceRuntimes_DeviceCode_CreatedAt");
builder.HasIndex(x => x.RuntimeStatus);
builder.HasIndex(x => x.RuntimeCode);
builder.HasIndex(x => x.NetworkStackCode);
builder.HasIndex(x => x.IsDeleted);
}
}

55
src/X1.Infrastructure/Configurations/Device/CellularDeviceRuntimeDetailConfiguration.cs

@ -0,0 +1,55 @@
using CellularManagement.Domain.Entities.Device;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CellularManagement.Infrastructure.Configurations.Device;
/// <summary>
/// 蜂窝设备运行时明细配置
/// </summary>
public class CellularDeviceRuntimeDetailConfiguration : IEntityTypeConfiguration<CellularDeviceRuntimeDetail>
{
public void Configure(EntityTypeBuilder<CellularDeviceRuntimeDetail> builder)
{
builder.ToTable("CellularDeviceRuntimeDetails");
builder.HasKey(x => x.Id);
// 基础属性配置
builder.Property(x => x.RuntimeCode)
.IsRequired()
.HasMaxLength(50);
builder.Property(x => x.RuntimeStatus)
.IsRequired()
.HasDefaultValue(false);
builder.Property(x => x.CreatedBy)
.HasMaxLength(450);
// Entity 属性配置
builder.Property(x => x.Id)
.IsRequired()
.HasMaxLength(450);
// 创建时间配置
builder.Property(x => x.CreatedAt)
.IsRequired();
// 创建索引
builder.HasIndex(x => x.RuntimeCode)
.HasDatabaseName("IX_CellularDeviceRuntimeDetails_RuntimeCode");
builder.HasIndex(x => x.CreatedBy)
.HasDatabaseName("IX_CellularDeviceRuntimeDetails_CreatedBy");
builder.HasIndex(x => x.RuntimeStatus)
.HasDatabaseName("IX_CellularDeviceRuntimeDetails_RuntimeStatus");
// 复合索引:优化按创建时间排序的查询
builder.HasIndex(x => x.CreatedAt)
.HasDatabaseName("IX_CellularDeviceRuntimeDetails_CreatedAt");
}
}

5
src/X1.Infrastructure/Context/AppDbContext.cs

@ -47,7 +47,10 @@ public class AppDbContext : IdentityDbContext<AppUser, AppRole, string>
/// </summary>
public DbSet<ProtocolVersion> ProtocolVersions { get; set; } = null!;
/// <summary>
/// 蜂窝设备运行时状态集合
/// </summary>
public DbSet<CellularDeviceRuntime> CellularDeviceRuntimes { get; set; } = null!;
/// <summary>
/// RAN配置集合

1
src/X1.Infrastructure/DependencyInjection.cs

@ -176,6 +176,7 @@ public static class DependencyInjection
// 注册设备相关仓储
services.AddScoped<ICellularDeviceRepository, CellularDeviceRepository>();
services.AddScoped<IProtocolVersionRepository, ProtocolVersionRepository>();
services.AddScoped<ICellularDeviceRuntimeRepository, CellularDeviceRuntimeRepository>();
services.AddScoped<IRAN_ConfigurationRepository, RAN_ConfigurationRepository>();
services.AddScoped<ICoreNetworkConfigRepository, CoreNetworkConfigRepository>();
services.AddScoped<IIMS_ConfigurationRepository, IMS_ConfigurationRepository>();

47
src/X1.Infrastructure/Repositories/Device/CellularDeviceRepository.cs

@ -75,6 +75,17 @@ public class CellularDeviceRepository : BaseRepository<CellularDevice>, ICellula
return await QueryRepository.GetByIdAsync(id, cancellationToken: cancellationToken);
}
/// <summary>
/// 根据ID获取蜂窝设备(包含运行时状态)
/// </summary>
public async Task<CellularDevice?> GetDeviceByIdWithRuntimeAsync(string id, CancellationToken cancellationToken = default)
{
return await QueryRepository.FirstOrDefaultAsync(
d => d.Id == id,
include: q => q.Include(d => d.Runtime),
cancellationToken: cancellationToken);
}
/// <summary>
/// 根据序列号获取蜂窝设备
/// </summary>
@ -97,6 +108,7 @@ public class CellularDeviceRepository : BaseRepository<CellularDevice>, ICellula
query = query.Where(d =>
d.Name.Contains(keyword) ||
d.SerialNumber.Contains(keyword) ||
d.DeviceCode.Contains(keyword) ||
d.Description.Contains(keyword));
}
@ -120,6 +132,7 @@ public class CellularDeviceRepository : BaseRepository<CellularDevice>, ICellula
{
predicate = d => d.Name.Contains(keyword) ||
d.SerialNumber.Contains(keyword) ||
d.DeviceCode.Contains(keyword) ||
d.Description.Contains(keyword);
}
@ -129,6 +142,40 @@ public class CellularDeviceRepository : BaseRepository<CellularDevice>, ICellula
return (result.TotalCount, result.Items.ToList());
}
/// <summary>
/// 搜索蜂窝设备(包含运行时状态)
/// </summary>
public async Task<(int TotalCount, IList<(CellularDevice Device, CellularDeviceRuntime? Runtime)> Items)> SearchDevicesWithRuntimeAsync(
string? keyword,
int pageNumber,
int pageSize,
CancellationToken cancellationToken = default)
{
// 构建查询条件
Expression<Func<CellularDevice, bool>> predicate = d => true;
if (!string.IsNullOrWhiteSpace(keyword))
{
predicate = d => d.Name.Contains(keyword) ||
d.SerialNumber.Contains(keyword) ||
d.DeviceCode.Contains(keyword) ||
d.Description.Contains(keyword);
}
// 执行分页查询,包含运行时状态
var result = await QueryRepository.GetPagedAsync(
predicate,
pageNumber,
pageSize,
include: q => q.Include(d => d.Runtime),
cancellationToken: cancellationToken);
// 构建结果
var items = result.Items.Select(d => (Device: d, Runtime: d.Runtime)).ToList();
return (result.TotalCount, items);
}
/// <summary>
/// 检查蜂窝设备是否存在
/// </summary>

232
src/X1.Infrastructure/Repositories/Device/CellularDeviceRuntimeRepository.cs

@ -0,0 +1,232 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System.Text.RegularExpressions;
using CellularManagement.Domain.Entities.Device;
using CellularManagement.Domain.Repositories.Device;
using CellularManagement.Domain.Repositories.Base;
using CellularManagement.Infrastructure.Repositories.Base;
namespace CellularManagement.Infrastructure.Repositories.Device;
/// <summary>
/// 蜂窝设备运行时状态仓储实现类
/// </summary>
public class CellularDeviceRuntimeRepository : BaseRepository<CellularDeviceRuntime>, ICellularDeviceRuntimeRepository
{
private readonly ILogger<CellularDeviceRuntimeRepository> _logger;
/// <summary>
/// 初始化仓储
/// </summary>
public CellularDeviceRuntimeRepository(
ICommandRepository<CellularDeviceRuntime> commandRepository,
IQueryRepository<CellularDeviceRuntime> queryRepository,
ILogger<CellularDeviceRuntimeRepository> logger)
: base(commandRepository, queryRepository, logger)
{
_logger = logger;
}
/// <summary>
/// 添加设备运行时状态
/// </summary>
public async Task<CellularDeviceRuntime> AddRuntimeAsync(CellularDeviceRuntime runtime, CancellationToken cancellationToken = default)
{
return await CommandRepository.AddAsync(runtime, cancellationToken);
}
/// <summary>
/// 更新设备运行时状态
/// </summary>
public void UpdateRuntime(CellularDeviceRuntime runtime)
{
CommandRepository.Update(runtime);
}
/// <summary>
/// 删除设备运行时状态
/// </summary>
public async Task DeleteRuntimeAsync(string id, CancellationToken cancellationToken = default)
{
await CommandRepository.DeleteByIdAsync(id, cancellationToken);
}
/// <summary>
/// 获取所有设备运行时状态
/// </summary>
public async Task<IList<CellularDeviceRuntime>> GetAllRuntimesAsync(CancellationToken cancellationToken = default)
{
var runtimes = await QueryRepository.GetAllAsync(cancellationToken: cancellationToken);
return runtimes.ToList();
}
/// <summary>
/// 根据ID获取设备运行时状态
/// </summary>
public async Task<CellularDeviceRuntime?> GetRuntimeByIdAsync(string id, CancellationToken cancellationToken = default)
{
return await QueryRepository.GetByIdAsync(id, cancellationToken: cancellationToken);
}
/// <summary>
/// 根据设备编号获取运行时状态(最新一条按创建时间排序)
/// </summary>
public async Task<CellularDeviceRuntime?> GetRuntimeByDeviceCodeAsync(string deviceCode, CancellationToken cancellationToken = default)
{
// 使用queryRepository获取所有匹配的记录,然后在内存中排序获取最新一条
// 注意:由于queryRepository的限制,我们无法直接在数据库层面进行排序
// 但通过添加的复合索引(DeviceCode, CreatedAt)可以提升查询性能
var runtimes = await QueryRepository.FindAsync(r => r.DeviceCode == deviceCode, cancellationToken: cancellationToken);
return runtimes.OrderByDescending(r => r.CreatedAt).FirstOrDefault();
}
/// <summary>
/// 根据设备编号获取运行时状态(包含设备信息)
/// </summary>
public async Task<CellularDeviceRuntime?> GetRuntimeByDeviceCodeWithDeviceAsync(string deviceCode, CancellationToken cancellationToken = default)
{
return await QueryRepository.FirstOrDefaultAsync(
r => r.DeviceCode == deviceCode,
include: q => q.Include(r => r.Device),
cancellationToken: cancellationToken);
}
/// <summary>
/// 根据运行编码获取运行时状态
/// </summary>
public async Task<CellularDeviceRuntime?> GetRuntimeByRuntimeCodeAsync(string runtimeCode, CancellationToken cancellationToken = default)
{
return await QueryRepository.FirstOrDefaultAsync(
r => r.RuntimeCode == runtimeCode,
include: q => q.Include(r => r.Device),
cancellationToken: cancellationToken);
}
/// <summary>
/// 获取正在运行的设备运行时状态
/// </summary>
public async Task<IList<CellularDeviceRuntime>> GetRunningRuntimesAsync(CancellationToken cancellationToken = default)
{
var runtimes = await QueryRepository.FindAsync(
r => r.RuntimeStatus == DeviceRuntimeStatus.Running,
include: q => q.Include(r => r.Device),
cancellationToken: cancellationToken);
return runtimes.ToList();
}
/// <summary>
/// 根据运行时状态获取设备运行时状态
/// </summary>
public async Task<IList<CellularDeviceRuntime>> GetRuntimesByStatusAsync(DeviceRuntimeStatus runtimeStatus, CancellationToken cancellationToken = default)
{
var runtimes = await QueryRepository.FindAsync(
r => r.RuntimeStatus == runtimeStatus,
include: q => q.Include(r => r.Device),
cancellationToken: cancellationToken);
return runtimes.ToList();
}
/// <summary>
/// 根据网络栈配置编号获取运行时状态
/// </summary>
public async Task<IList<CellularDeviceRuntime>> GetRuntimesByNetworkStackCodeAsync(string networkStackCode, CancellationToken cancellationToken = default)
{
var runtimes = await QueryRepository.FindAsync(
r => r.NetworkStackCode == networkStackCode,
include: q => q.Include(r => r.Device),
cancellationToken: cancellationToken);
return runtimes.ToList();
}
/// <summary>
/// 搜索设备运行时状态
/// </summary>
public async Task<IList<CellularDeviceRuntime>> SearchRuntimesAsync(
string? keyword,
CancellationToken cancellationToken = default)
{
var query = await QueryRepository.FindAsync(r => true, cancellationToken: cancellationToken);
if (!string.IsNullOrWhiteSpace(keyword))
{
query = query.Where(r =>
r.DeviceCode.Contains(keyword) ||
(r.RuntimeCode != null && r.RuntimeCode.Contains(keyword)) ||
(r.NetworkStackCode != null && r.NetworkStackCode.Contains(keyword)));
}
var runtimes = query;
return runtimes.ToList();
}
/// <summary>
/// 搜索设备运行时状态(分页)
/// </summary>
public async Task<(int TotalCount, IList<CellularDeviceRuntime> Items)> SearchRuntimesAsync(
string? keyword,
int pageNumber,
int pageSize,
CancellationToken cancellationToken = default)
{
// 构建查询条件
Expression<Func<CellularDeviceRuntime, bool>> predicate = r => true;
if (!string.IsNullOrWhiteSpace(keyword))
{
predicate = r => r.DeviceCode.Contains(keyword) ||
(r.RuntimeCode != null && r.RuntimeCode.Contains(keyword)) ||
(r.NetworkStackCode != null && r.NetworkStackCode.Contains(keyword));
}
// 执行分页查询
var result = await QueryRepository.GetPagedAsync(predicate, pageNumber, pageSize, null, cancellationToken);
return (result.TotalCount, result.Items.ToList());
}
/// <summary>
/// 检查设备运行时状态是否存在
/// </summary>
public async Task<bool> ExistsAsync(string id, CancellationToken cancellationToken = default)
{
return await QueryRepository.AnyAsync(r => r.Id == id, cancellationToken: cancellationToken);
}
/// <summary>
/// 检查设备编号是否有运行时状态
/// </summary>
public async Task<bool> DeviceCodeExistsAsync(string deviceCode, CancellationToken cancellationToken = default)
{
return await QueryRepository.AnyAsync(r => r.DeviceCode == deviceCode, cancellationToken: cancellationToken);
}
/// <summary>
/// 检查运行编码是否存在
/// </summary>
public async Task<bool> RuntimeCodeExistsAsync(string runtimeCode, CancellationToken cancellationToken = default)
{
return await QueryRepository.AnyAsync(r => r.RuntimeCode == runtimeCode, cancellationToken: cancellationToken);
}
/// <summary>
/// 获取运行时状态总数
/// </summary>
public async Task<int> GetRuntimeCountAsync(CancellationToken cancellationToken = default)
{
return await QueryRepository.CountAsync(r => true, cancellationToken: cancellationToken);
}
/// <summary>
/// 根据设备编号删除运行时状态
/// </summary>
public async Task DeleteRuntimeByDeviceCodeAsync(string deviceCode, CancellationToken cancellationToken = default)
{
await CommandRepository.DeleteWhereAsync(r => r.DeviceCode == deviceCode, cancellationToken);
}
}

127
src/X1.Presentation/Controllers/DeviceRuntimesController.cs

@ -0,0 +1,127 @@
using CellularManagement.Application.Features.DeviceRuntimes.Commands.StartDeviceRuntime;
using CellularManagement.Application.Features.DeviceRuntimes.Commands.StopDeviceRuntime;
using CellularManagement.Application.Features.DeviceRuntimes.Queries.GetDeviceRuntimes;
using CellularManagement.Application.Features.DeviceRuntimes.Queries.GetDeviceRuntimeStatus;
using CellularManagement.Domain.Common;
using CellularManagement.Presentation.Abstractions;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace CellularManagement.Presentation.Controllers;
/// <summary>
/// 设备运行时状态管理控制器
/// </summary>
[Route("api/device-runtimes")]
[ApiController]
[Authorize]
public class DeviceRuntimesController : ApiController
{
private readonly ILogger<DeviceRuntimesController> _logger;
/// <summary>
/// 初始化设备运行时状态控制器
/// </summary>
public DeviceRuntimesController(IMediator mediator, ILogger<DeviceRuntimesController> logger)
: base(mediator)
{
_logger = logger;
}
/// <summary>
/// 获取设备运行时状态列表
/// </summary>
[HttpGet]
public async Task<OperationResult<GetDeviceRuntimesResponse>> GetAll([FromQuery] GetDeviceRuntimesQuery query)
{
_logger.LogInformation("开始获取设备运行时状态列表,页码: {PageNumber}, 每页数量: {PageSize}, 搜索关键词: {SearchTerm}",
query.PageNumber, query.PageSize, query.SearchTerm);
var result = await mediator.Send(query);
if (result.IsSuccess)
{
_logger.LogInformation("获取设备运行时状态列表成功,总记录数: {TotalCount}", result.Data?.TotalCount);
}
else
{
_logger.LogWarning("获取设备运行时状态列表失败: {Error}", result.ErrorMessages?.FirstOrDefault());
}
return result;
}
/// <summary>
/// 根据设备编号获取设备运行时状态
/// </summary>
[HttpGet("{deviceCode}")]
public async Task<OperationResult<GetDeviceRuntimeStatusResponse>> GetByDeviceCode(string deviceCode)
{
_logger.LogInformation("开始获取设备运行时状态,设备编号: {DeviceCode}", deviceCode);
var query = new GetDeviceRuntimeStatusQuery(deviceCode);
var result = await mediator.Send(query);
if (result.IsSuccess)
{
_logger.LogInformation("获取设备运行时状态成功,设备编号: {DeviceCode}, 运行时状态: {RuntimeStatus}",
deviceCode, result.Data?.RuntimeStatus);
}
else
{
_logger.LogWarning("获取设备运行时状态失败,设备编号: {DeviceCode}, 错误: {Error}",
deviceCode, result.ErrorMessages?.FirstOrDefault());
}
return result;
}
/// <summary>
/// 启动设备
/// </summary>
[HttpPost("start")]
public async Task<OperationResult<StartDeviceRuntimeResponse>> StartDevices([FromBody] StartDeviceRuntimeCommand command)
{
_logger.LogInformation("开始批量启动设备,设备数量: {DeviceCount}", command.DeviceRequests.Count);
var result = await mediator.Send(command);
if (result.IsSuccess)
{
_logger.LogInformation("批量启动设备成功,总数量: {TotalCount}, 成功: {SuccessCount}, 失败: {FailureCount}",
result.Data?.Summary?.TotalCount, result.Data?.Summary?.SuccessCount, result.Data?.Summary?.FailureCount);
}
else
{
_logger.LogWarning("批量启动设备失败: {Error}", result.ErrorMessages?.FirstOrDefault());
}
return result;
}
/// <summary>
/// 停止设备
/// </summary>
[HttpPost("{deviceCode}/stop")]
public async Task<OperationResult<StopDeviceRuntimeResponse>> StopDevice(string deviceCode)
{
_logger.LogInformation("开始停止设备,设备编号: {DeviceCode}", deviceCode);
var command = new StopDeviceRuntimeCommand { DeviceCode = deviceCode };
var result = await mediator.Send(command);
if (result.IsSuccess)
{
_logger.LogInformation("设备停止成功,设备编号: {DeviceCode}", deviceCode);
}
else
{
_logger.LogWarning("设备停止失败,设备编号: {DeviceCode}, 错误: {Error}",
deviceCode, result.ErrorMessages?.FirstOrDefault());
}
return result;
}
}

14
src/X1.WebUI/src/pages/instruments/DevicesTable.tsx

@ -1,7 +1,7 @@
import React from 'react';
import { Button } from '@/components/ui/button';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
import { Device, DeviceStatus, DeviceRunningStatus } from '@/services/instrumentService';
import { Device } from '@/services/instrumentService';
import { Badge } from '@/components/ui/badge';
import { Pencil1Icon, TrashIcon } from '@radix-ui/react-icons';
import { DensityType } from '@/components/ui/TableToolbar';
@ -16,7 +16,6 @@ import { DensityType } from '@/components/ui/TableToolbar';
* - description: 设备描述
* - agentPort: Agent端口
* - isEnabled: 是否启用
* - isRunning: 设备状态/
* - createdAt: 创建时间
*
* IpAddress字段在创建/使
@ -45,15 +44,6 @@ function DeviceStatusBadge({ isEnabled }: { isEnabled: boolean }) {
);
}
// 设备运行状态徽章组件
function DeviceRunningStatusBadge({ isRunning }: { isRunning: boolean }) {
return (
<Badge variant={isRunning ? 'default' : 'secondary'}>
{isRunning ? '运行中' : '已停止'}
</Badge>
);
}
// 日期显示组件
function DateDisplay({ date }: { date: string }) {
return (
@ -110,8 +100,6 @@ export default function DevicesTable({
return <span className="font-mono text-sm">{device.agentPort}</span>;
case 'isEnabled':
return <DeviceStatusBadge isEnabled={device.isEnabled} />;
case 'isRunning':
return <DeviceRunningStatusBadge isRunning={device.isRunning} />;
case 'createdAt':
return <DateDisplay date={device.createdAt} />;
case 'actions':

5
src/X1.WebUI/src/pages/instruments/DevicesView.tsx

@ -17,7 +17,6 @@ const defaultColumns = [
{ key: 'description', title: '描述', visible: true },
{ key: 'agentPort', title: 'Agent端口', visible: true },
{ key: 'isEnabled', title: '状态', visible: true },
{ key: 'isRunning', title: '运行状态', visible: true },
{ key: 'createdAt', title: '创建时间', visible: true },
{ key: 'actions', title: '操作', visible: true },
];
@ -57,7 +56,6 @@ const advancedFields: SearchField[] = [
* - description: 设备描述
* - agentPort: Agent端口
* - isEnabled: 是否启用
* - isRunning: 设备状态/
* - createdAt: 创建时间
*
* IpAddress字段在创建/使
@ -391,8 +389,7 @@ export default function DevicesView() {
deviceId: selectedDevice.deviceId,
deviceName: selectedDevice.deviceName,
description: selectedDevice.description,
isEnabled: selectedDevice.isEnabled,
isRunning: selectedDevice.isRunning
isEnabled: selectedDevice.isEnabled
} : undefined}
isEdit={true}
isSubmitting={isSubmitting}

123
src/X1.WebUI/src/services/instrumentService.ts

@ -2,12 +2,6 @@ import { httpClient } from '@/lib/http-client';
import { OperationResult } from '@/types/auth';
import { API_PATHS } from '@/constants/api';
// 设备状态类型 - 根据后端实体定义
export type DeviceStatus = 'enabled' | 'disabled';
// 设备运行状态
export type DeviceRunningStatus = 'running' | 'stopped';
// 设备接口定义 - 匹配后端GetDeviceByIdResponse
export interface Device {
deviceId: string;
@ -105,119 +99,4 @@ export function updateDevice(id: string, data: UpdateDeviceRequest): Promise<Ope
// 删除设备
export function deleteDevice(id: string): Promise<OperationResult<boolean>> {
return httpClient.delete(`${API_PATHS.DEVICES}/${id}`);
}
// 为了向后兼容,保留原有的类型和服务
export type LegacyDeviceStatus = 'online' | 'offline' | 'maintenance' | 'error';
export type DeviceType = 'sensor' | 'controller' | 'monitor' | 'actuator' | 'gateway';
// 旧版设备接口定义
export interface LegacyDevice {
id: string;
deviceId: string;
deviceName: string;
deviceType: DeviceType;
status: LegacyDeviceStatus;
protocolId: string;
protocolName: string;
ipAddress: string;
port: number;
location: string;
description: string;
manufacturer: string;
model: string;
serialNumber: string;
firmwareVersion: string;
lastHeartbeat: string;
lastDataUpdate: string;
configId: string;
configName: string;
tags: string[];
properties: {
[key: string]: any;
};
createdAt: string;
updatedAt: string;
createdBy: string;
updatedBy: string;
}
// 旧版获取设备列表请求接口
export interface GetAllDevicesRequest {
deviceId?: string;
deviceName?: string;
deviceType?: DeviceType;
status?: LegacyDeviceStatus;
protocolId?: string;
location?: string;
manufacturer?: string;
createdBy?: string;
page?: number;
pageSize?: number;
}
// 旧版获取设备列表响应接口
export interface GetAllDevicesResponse {
devices: LegacyDevice[];
totalCount: number;
}
class DeviceService {
private readonly baseUrl = '/api/instruments/devices';
// 获取所有设备
async getAllDevices(params: GetAllDevicesRequest = {}): Promise<OperationResult<GetAllDevicesResponse>> {
const queryParams = new URLSearchParams();
if (params.deviceId) queryParams.append('deviceId', params.deviceId);
if (params.deviceName) queryParams.append('deviceName', params.deviceName);
if (params.deviceType) queryParams.append('deviceType', params.deviceType);
if (params.status) queryParams.append('status', params.status);
if (params.protocolId) queryParams.append('protocolId', params.protocolId);
if (params.location) queryParams.append('location', params.location);
if (params.manufacturer) queryParams.append('manufacturer', params.manufacturer);
if (params.createdBy) queryParams.append('createdBy', params.createdBy);
if (params.page) queryParams.append('page', params.page.toString());
if (params.pageSize) queryParams.append('pageSize', params.pageSize.toString());
const url = `${this.baseUrl}?${queryParams.toString()}`;
return httpClient.get<GetAllDevicesResponse>(url);
}
// 根据ID获取设备
async getDeviceById(id: string): Promise<OperationResult<LegacyDevice>> {
return httpClient.get<LegacyDevice>(`${this.baseUrl}/${id}`);
}
// 获取设备状态
async getDeviceStatus(deviceId: string): Promise<OperationResult<{
status: LegacyDeviceStatus;
lastHeartbeat: string;
lastDataUpdate: string;
connectionInfo: any;
}>> {
return httpClient.get(`${this.baseUrl}/${deviceId}/status`);
}
// 测试设备连接
async testDeviceConnection(deviceId: string): Promise<OperationResult<{
connected: boolean;
responseTime: number;
error?: string;
}>> {
return httpClient.post(`${this.baseUrl}/${deviceId}/test-connection`);
}
// 导出设备列表
async exportDevices(format: 'pdf' | 'excel' | 'csv', filters?: any): Promise<OperationResult<{ downloadUrl: string }>> {
return httpClient.post(`${this.baseUrl}/export`, { format, filters });
}
}
export const deviceService = new DeviceService();
// 重新导出其他服务,保持向后兼容
export * from './protocolService';
// 为了向后兼容,保留原有的 instrumentService 导出
export { deviceService as instrumentService };
}

992
src/modify.md

File diff suppressed because it is too large
Loading…
Cancel
Save