Browse Source

修复终端设备上传

feature/x1-web-request
root 4 months ago
parent
commit
058c8149e8
  1. 4
      src/X1.Application/Features/DeviceRuntimes/Commands/StartDeviceRuntime/StartDeviceRuntimeCommandHandler.cs
  2. 4
      src/X1.Application/Features/DeviceRuntimes/Commands/StopDeviceRuntime/StopDeviceRuntimeCommandHandler.cs
  3. 4
      src/X1.Application/Features/Devices/Commands/CreateDevice/CreateDeviceCommandHandler.cs
  4. 2
      src/X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceCommand.cs
  5. 159
      src/X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceCommandHandler.cs
  6. 5
      src/X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceResponse.cs
  7. 20
      src/X1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/UpdateTerminalDeviceCommand.cs
  8. 41
      src/X1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/UpdateTerminalDeviceCommandHandler.cs
  9. 5
      src/X1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/UpdateTerminalDeviceResponse.cs
  10. 2
      src/X1.Application/Features/TerminalDevices/Queries/GetTerminalDeviceById/GetTerminalDeviceByIdQueryHandler.cs
  11. 10
      src/X1.Application/Features/TerminalDevices/Queries/GetTerminalDeviceById/GetTerminalDeviceByIdResponse.cs
  12. 2
      src/X1.Application/Features/TerminalDevices/Queries/GetTerminalDevices/GetTerminalDevicesQueryHandler.cs
  13. 10
      src/X1.Application/Features/TerminalDevices/Queries/GetTerminalDevices/GetTerminalDevicesResponse.cs
  14. 4
      src/X1.BackendServices/BackendServiceManager/DeviceManagementService.cs
  15. 19
      src/X1.Domain/DependencyInjection.cs
  16. 28
      src/X1.Domain/Entities/Terminal/TerminalDevice.cs
  17. 17
      src/X1.Domain/Entities/Terminal/TerminalDeviceType.cs
  18. 4
      src/X1.Domain/ThirdPartyDeviceHttpClient/IBaseInstrumentClient.cs
  19. 2
      src/X1.Domain/ThirdPartyDeviceHttpClient/IInstrumentHttpClient.cs
  20. 4
      src/X1.Domain/ThirdPartyDeviceHttpClient/IInstrumentProtocolClient.cs
  21. 4
      src/X1.Domain/ThirdPartyDeviceHttpClient/IServiceEndpointManager.cs
  22. 15
      src/X1.Domain/ThirdPartyDeviceHttpClient/ITerminal/IADBOperationTerminalClient.cs
  23. 13
      src/X1.Domain/ThirdPartyDeviceHttpClient/ITerminal/IATOperationTerminalClient.cs
  24. 26
      src/X1.Domain/ThirdPartyDeviceHttpClient/ITerminal/IBaseTerminalClient.cs
  25. 13
      src/X1.Domain/ThirdPartyDeviceHttpClient/ITerminal/ITerminalHttpClient.cs
  26. 2
      src/X1.Domain/ThirdPartyDeviceHttpClient/Models/ApiActionResult.cs
  27. 2
      src/X1.Domain/ThirdPartyDeviceHttpClient/Models/CellularNetworkConfiguration.cs
  28. 2
      src/X1.Domain/ThirdPartyDeviceHttpClient/Models/CellularNetworkRequest.cs
  29. 2
      src/X1.Domain/ThirdPartyDeviceHttpClient/Models/CircuitBreakerOptions.cs
  30. 2
      src/X1.Domain/ThirdPartyDeviceHttpClient/Models/DeviceInfoResponse.cs
  31. 2
      src/X1.Domain/ThirdPartyDeviceHttpClient/Models/DynamicHttpClientException.cs
  32. 2
      src/X1.Domain/ThirdPartyDeviceHttpClient/Models/HttpRequestOptions.cs
  33. 2
      src/X1.Domain/ThirdPartyDeviceHttpClient/Models/ServiceEndpoint.cs
  34. 55
      src/X1.Domain/ThirdPartyDeviceHttpClient/Models/TestTerminalResponse.cs
  35. 2
      src/X1.DynamicClientCore/Core/DynamicHttpClient.Core.cs
  36. 2
      src/X1.DynamicClientCore/Core/DynamicHttpClient.Sync.cs
  37. 2
      src/X1.DynamicClientCore/Core/DynamicHttpClient.SyncCore.cs
  38. 4
      src/X1.DynamicClientCore/Core/DynamicHttpClient.cs
  39. 10
      src/X1.DynamicClientCore/Extensions/ServiceCollectionExtensions.cs
  40. 2
      src/X1.DynamicClientCore/FileOperations/DynamicHttpClient.FileDownload.cs
  41. 2
      src/X1.DynamicClientCore/FileOperations/DynamicHttpClient.FileInfo.cs
  42. 2
      src/X1.DynamicClientCore/FileOperations/DynamicHttpClient.FileUpload.cs
  43. 2
      src/X1.DynamicClientCore/Infrastructure/CircuitBreakerManager.cs
  44. 4
      src/X1.DynamicClientCore/Infrastructure/ServiceEndpointManager.cs
  45. 2
      src/X1.DynamicClientCore/Interfaces/IAsyncHttpClient.cs
  46. 2
      src/X1.DynamicClientCore/Interfaces/ICircuitBreakerManager.cs
  47. 2
      src/X1.DynamicClientCore/Interfaces/IFileHttpClient.cs
  48. 2
      src/X1.DynamicClientCore/Interfaces/ISyncHttpClient.cs
  49. 32
      src/X1.DynamicClientCore/Service/InstrumentProtocolClient.cs
  50. 108
      src/X1.DynamicClientCore/Service/TestTerminalRequestClient.cs
  51. 91
      src/X1.Infrastructure/Configurations/Terminal/TerminalDeviceConfiguration.cs
  52. 6
      src/X1.Infrastructure/Context/AppDbContext.cs
  53. 2
      src/X1.WebSocket/Extensions/ServiceCollectionExtensions.cs
  54. 14
      src/X1.WebSocket/Handlers/ProtocolMessageHandler.cs
  55. 38
      src/X1.WebSocket/Handlers/TerminalMessageHandler.cs
  56. 627
      src/modify.md

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

@ -8,8 +8,8 @@ using CellularManagement.Domain.Services;
using CellularManagement.Domain.Repositories.NetworkProfile;
using X1.Domain.Models;
using System.Collections.Concurrent;
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ExternalCommunication;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
using X1.Domain.ThirdPartyDeviceHttpClient;
namespace CellularManagement.Application.Features.DeviceRuntimes.Commands.StartDeviceRuntime;

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

@ -5,8 +5,8 @@ using CellularManagement.Domain.Entities.Device;
using CellularManagement.Domain.Repositories.Device;
using CellularManagement.Domain.Repositories.Base;
using CellularManagement.Domain.Services;
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ExternalCommunication;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
using X1.Domain.ThirdPartyDeviceHttpClient;
namespace CellularManagement.Application.Features.DeviceRuntimes.Commands.StopDeviceRuntime;

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

@ -6,8 +6,8 @@ using CellularManagement.Domain.Repositories;
using CellularManagement.Domain.Repositories.Device;
using CellularManagement.Domain.Repositories.Base;
using CellularManagement.Domain.Services;
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ExternalCommunication;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
using X1.Domain.ThirdPartyDeviceHttpClient;
namespace CellularManagement.Application.Features.Devices.Commands.CreateDevice;

2
src/X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceCommand.cs

@ -40,4 +40,6 @@ public class CreateTerminalDeviceCommand : IRequest<OperationResult<CreateTermin
/// 是否启用
/// </summary>
public bool IsEnabled { get; set; } = true;
}

159
src/X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceCommandHandler.cs

@ -8,11 +8,28 @@ using CellularManagement.Domain.Repositories.Base;
using CellularManagement.Domain.Services;
using CellularManagement.Domain.Repositories.Terminal;
using CellularManagement.Domain.Entities.Terminal;
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ExternalCommunication;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
using X1.Domain.ThirdPartyDeviceHttpClient.ITerminal;
using X1.Domain.ThirdPartyDeviceHttpClient;
namespace CellularManagement.Application.Features.TerminalDevices.Commands.CreateTerminalDevice;
/// <summary>
/// 设备信息数据传输对象
/// </summary>
public class DeviceInfo
{
/// <summary>
/// 设备序列号
/// </summary>
public string SerialNumber { get; set; } = string.Empty;
/// <summary>
/// 设备类型
/// </summary>
public TerminalDeviceType DeviceType { get; set; }
}
/// <summary>
/// 创建终端设备命令处理器
/// </summary>
@ -22,7 +39,7 @@ public class CreateTerminalDeviceCommandHandler : IRequestHandler<CreateTerminal
private readonly ILogger<CreateTerminalDeviceCommandHandler> _logger;
private readonly IUnitOfWork _unitOfWork;
private readonly ICurrentUserService _currentUserService;
private readonly IBaseInstrumentClient _instrumentClient;
private readonly IBaseTerminalClient _terminalClient;
private readonly IServiceEndpointManager _endpointManager;
/// <summary>
@ -33,14 +50,14 @@ public class CreateTerminalDeviceCommandHandler : IRequestHandler<CreateTerminal
ILogger<CreateTerminalDeviceCommandHandler> logger,
IUnitOfWork unitOfWork,
ICurrentUserService currentUserService,
IBaseInstrumentClient instrumentClient,
IBaseTerminalClient terminalClient,
IServiceEndpointManager endpointManager)
{
_deviceRepository = deviceRepository;
_logger = logger;
_unitOfWork = unitOfWork;
_currentUserService = currentUserService;
_instrumentClient = instrumentClient;
_terminalClient = terminalClient;
_endpointManager = endpointManager;
}
@ -71,14 +88,15 @@ public class CreateTerminalDeviceCommandHandler : IRequestHandler<CreateTerminal
// 创建服务端点
var serviceEndpoint = CreateServiceEndpoint(request);
// 获取设备序列号
var serialNumberResult = await GetDeviceSerialNumberAsync(request, serviceEndpoint, cancellationToken);
if (!serialNumberResult.IsSuccess)
// 获取设备序列号和系统类型信息
var deviceInfoResult = await GetDeviceInfoAsync(request, serviceEndpoint, cancellationToken);
if (!deviceInfoResult.IsSuccess)
{
return OperationResult<CreateTerminalDeviceResponse>.CreateFailure(serialNumberResult.ErrorMessages);
return OperationResult<CreateTerminalDeviceResponse>.CreateFailure(deviceInfoResult.ErrorMessages ?? new List<string> { "获取设备信息失败" });
}
var serialNumber = serialNumberResult.Data;
var serialNumber = deviceInfoResult.Data!.SerialNumber;
var deviceType = deviceInfoResult.Data!.DeviceType;
// 验证序列号
if (string.IsNullOrWhiteSpace(serialNumber))
@ -95,14 +113,7 @@ public class CreateTerminalDeviceCommandHandler : IRequestHandler<CreateTerminal
}
// 生成设备编号
var deviceCode = await GenerateDeviceCodeAsync(serialNumber, cancellationToken);
// 检查设备编码是否已存在
if (await _deviceRepository.DeviceCodeExistsAsync(deviceCode, cancellationToken))
{
_logger.LogWarning("终端设备编码已存在: {DeviceCode}", deviceCode);
return OperationResult<CreateTerminalDeviceResponse>.CreateFailure($"终端设备编码 {deviceCode} 已存在");
}
var deviceCode = await GenerateUniqueDeviceCodeAsync(cancellationToken);
// 创建终端设备实体
var terminalDevice = TerminalDevice.Create(
@ -113,12 +124,18 @@ public class CreateTerminalDeviceCommandHandler : IRequestHandler<CreateTerminal
request.AgentPort,
request.IpAddress,
currentUserId.Data!,
deviceType,
request.IsEnabled);
// 保存到数据库
await _deviceRepository.AddDeviceAsync(terminalDevice, cancellationToken);
await _unitOfWork.SaveChangesAsync(cancellationToken);
// 设备保存成功后,修改端点名称并重新添加
serviceEndpoint.Name = deviceCode;
_endpointManager.AddOrUpdateEndpoint(serviceEndpoint);
_logger.LogInformation("终端设备创建成功,设备ID: {DeviceId}, 设备编码: {DeviceCode}",
terminalDevice.Id, terminalDevice.DeviceCode);
@ -131,6 +148,7 @@ public class CreateTerminalDeviceCommandHandler : IRequestHandler<CreateTerminal
Description = terminalDevice.Description,
AgentPort = terminalDevice.AgentPort,
IsEnabled = terminalDevice.IsEnabled,
DeviceType = terminalDevice.DeviceType.ToString(),
CreatedAt = terminalDevice.CreatedAt
};
@ -182,41 +200,27 @@ public class CreateTerminalDeviceCommandHandler : IRequestHandler<CreateTerminal
}
/// <summary>
/// 生成设备编码
/// 生成唯一的设备编码
/// </summary>
private async Task<string> GenerateDeviceCodeAsync(string serialNumber, CancellationToken cancellationToken)
private async Task<string> GenerateUniqueDeviceCodeAsync(CancellationToken cancellationToken)
{
// 获取当前设备总数
var deviceCount = await _deviceRepository.GetDeviceCountAsync(cancellationToken);
var nextNumber = deviceCount + 1;
// 计算需要的位数,确保至少3位数
var digitCount = CalculateRequiredDigits(nextNumber);
// 格式化序号,动态补0,确保从000开始
var formattedNumber = nextNumber.ToString($"D{digitCount}");
// 使用短UUID生成唯一编码
var shortId = Guid.NewGuid().ToString("N").Substring(0, 8);
var deviceCode = $"TERM-{shortId}";
// 生成设备编号格式:TERM-000-SN, TERM-001-SN, TERM-002-SN 等
var deviceCode = $"TERM-{formattedNumber}-{serialNumber}";
_logger.LogDebug("生成终端设备编号: {DeviceCode}, 设备总数: {DeviceCount}, 位数: {DigitCount}", deviceCode, deviceCount, digitCount);
// 检查设备编码是否已存在(理论上概率极低)
if (await _deviceRepository.DeviceCodeExistsAsync(deviceCode, cancellationToken))
{
// 如果冲突,使用时间戳后缀
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() % 1000000; // 取后6位
deviceCode = $"TERM-{shortId}-{timestamp}";
_logger.LogDebug("设备编码冲突,使用时间戳后缀: {DeviceCode}", deviceCode);
}
_logger.LogDebug("生成终端设备编号: {DeviceCode}", deviceCode);
return deviceCode;
}
/// <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位数
}
/// <summary>
/// 创建服务端点配置
/// </summary>
@ -235,9 +239,9 @@ public class CreateTerminalDeviceCommandHandler : IRequestHandler<CreateTerminal
}
/// <summary>
/// 获取设备序列号
/// 获取设备序列号和系统类型信息
/// </summary>
private async Task<OperationResult<string>> GetDeviceSerialNumberAsync(CreateTerminalDeviceCommand request, ServiceEndpoint serviceEndpoint, CancellationToken cancellationToken)
private async Task<OperationResult<DeviceInfo>> GetDeviceInfoAsync(CreateTerminalDeviceCommand request, ServiceEndpoint serviceEndpoint, CancellationToken cancellationToken)
{
try
{
@ -245,33 +249,74 @@ public class CreateTerminalDeviceCommandHandler : IRequestHandler<CreateTerminal
_endpointManager.AddOrUpdateEndpoint(serviceEndpoint);
_logger.LogDebug("已添加服务端点: {EndpointName}", serviceEndpoint.Name);
// 获取设备序列号
var serialNumber = await _instrumentClient.GetDeviceSerialNumberAsync(serviceEndpoint.Name, null, cancellationToken);
// 获取设备序列号和系统类型信息
var machineCodeData = await _terminalClient.GetDeviceSerialNumberAsync(serviceEndpoint.Name, null, cancellationToken);
if (string.IsNullOrEmpty(serialNumber))
if (machineCodeData == null || string.IsNullOrEmpty(machineCodeData.MachineCode) || string.IsNullOrEmpty(machineCodeData.SystemType))
{
_logger.LogWarning("无法通过IP地址获取终端设备序列号,IP地址: {IpAddress}", request.IpAddress);
_logger.LogWarning("无法通过IP地址获取终端设备序列号或系统类型,IP地址: {IpAddress}", request.IpAddress);
// 获取序列号失败时清理服务端点
_endpointManager.RemoveEndpoint(serviceEndpoint.Name);
_logger.LogDebug("已清理服务端点: {EndpointName}", serviceEndpoint.Name);
return OperationResult<string>.CreateFailure($"无法通过IP地址 {request.IpAddress} 获取终端设备序列号,请检查设备连接状态");
return OperationResult<DeviceInfo>.CreateFailure($"无法通过IP地址 {request.IpAddress} 获取终端设备序列号或系统类型,请检查设备连接状态");
}
_logger.LogInformation("成功获取终端设备序列号: {SerialNumber}, IP地址: {IpAddress}", serialNumber, request.IpAddress);
var serialNumber = machineCodeData.MachineCode;
var systemType = machineCodeData.SystemType.ToLowerInvariant();
var detectedType = DetectDeviceTypeFromSystemType(systemType);
_logger.LogInformation("成功获取终端设备序列号: {SerialNumber}, IP地址: {IpAddress}, 系统类型: {SystemType}",
serialNumber, request.IpAddress, systemType);
// 获取序列号成功后,移除临时服务端点
_endpointManager.RemoveEndpoint(serviceEndpoint.Name);
_logger.LogDebug("已移除临时服务端点: {EndpointName}", serviceEndpoint.Name);
return OperationResult<string>.CreateSuccess(serialNumber);
return OperationResult<DeviceInfo>.CreateSuccess(new DeviceInfo { SerialNumber = serialNumber, DeviceType = detectedType });
}
catch (Exception ex)
{
_logger.LogError(ex, "获取终端设备序列号时发生异常,IP地址: {IpAddress}", request.IpAddress);
_logger.LogError(ex, "获取终端设备序列号或系统类型时发生异常,IP地址: {IpAddress}", request.IpAddress);
// 发生异常时清理服务端点
_endpointManager.RemoveEndpoint(serviceEndpoint.Name);
_logger.LogDebug("已清理服务端点: {EndpointName}", serviceEndpoint.Name);
return OperationResult<string>.CreateFailure($"获取终端设备序列号时发生错误: {ex.Message}");
return OperationResult<DeviceInfo>.CreateFailure($"获取终端设备序列号或系统类型时发生错误: {ex.Message}");
}
}
/// <summary>
/// 根据系统类型字符串检测设备类型
/// </summary>
/// <param name="systemType">系统类型字符串</param>
/// <returns>检测到的设备类型</returns>
private TerminalDeviceType DetectDeviceTypeFromSystemType(string systemType)
{
// 转换为小写进行比较
var lowerSystemType = systemType.ToLowerInvariant();
// 检测Linux系统
if (lowerSystemType.Contains("linux") ||
lowerSystemType.Contains("ubuntu") ||
lowerSystemType.Contains("centos") ||
lowerSystemType.Contains("debian") ||
lowerSystemType.Contains("fedora") ||
lowerSystemType.Contains("redhat") ||
lowerSystemType.Contains("suse") ||
lowerSystemType.Contains("unix"))
{
return TerminalDeviceType.Linux;
}
// 检测Windows系统
if (lowerSystemType.Contains("windows") ||
lowerSystemType.Contains("win") ||
lowerSystemType.Contains("nt"))
{
return TerminalDeviceType.Windows;
}
// 默认返回Windows类型
_logger.LogDebug("未知系统类型: {SystemType},使用默认类型Windows", systemType);
return TerminalDeviceType.Windows;
}
}

5
src/X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceResponse.cs

@ -35,6 +35,11 @@ public class CreateTerminalDeviceResponse
/// </summary>
public bool IsEnabled { get; set; }
/// <summary>
/// 设备类型
/// </summary>
public string DeviceType { get; set; } = string.Empty;
/// <summary>
/// 创建时间
/// </summary>

20
src/X1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/UpdateTerminalDeviceCommand.cs

@ -15,32 +15,12 @@ public class UpdateTerminalDeviceCommand : IRequest<OperationResult<UpdateTermin
[Required(ErrorMessage = "设备ID不能为空")]
public string DeviceId { get; set; } = null!;
/// <summary>
/// 设备名称
/// </summary>
[Required(ErrorMessage = "设备名称不能为空")]
[MaxLength(100, ErrorMessage = "设备名称不能超过100个字符")]
public string DeviceName { get; set; } = null!;
/// <summary>
/// 设备描述
/// </summary>
[MaxLength(500, ErrorMessage = "设备描述不能超过500个字符")]
public string Description { get; set; } = string.Empty;
/// <summary>
/// IP地址
/// </summary>
[Required(ErrorMessage = "设备IP不能为空")]
[MaxLength(45, ErrorMessage = "IP地址不能超过45个字符")]
public string IpAddress { get; set; } = null!;
/// <summary>
/// Agent端口
/// </summary>
[Required(ErrorMessage = "Agent端口不能为空")]
public int AgentPort { get; set; }
/// <summary>
/// 是否启用
/// </summary>

41
src/X1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/UpdateTerminalDeviceCommandHandler.cs

@ -2,6 +2,7 @@ using MediatR;
using Microsoft.Extensions.Logging;
using CellularManagement.Domain.Common;
using CellularManagement.Domain.Entities.Device;
using CellularManagement.Domain.Entities.Terminal;
using CellularManagement.Domain.Repositories.Device;
using CellularManagement.Domain.Repositories.Base;
using CellularManagement.Domain.Services;
@ -46,8 +47,7 @@ public class UpdateTerminalDeviceCommandHandler : IRequestHandler<UpdateTerminal
return OperationResult<UpdateTerminalDeviceResponse>.CreateFailure(validationResult.ErrorMessages);
}
_logger.LogInformation("开始更新终端设备,设备ID: {DeviceId}, 设备名称: {DeviceName}",
request.DeviceId, request.DeviceName);
_logger.LogInformation("开始更新终端设备,设备ID: {DeviceId}", request.DeviceId);
// 验证用户认证
var currentUserId = ValidateUserAuthentication();
@ -66,16 +66,17 @@ public class UpdateTerminalDeviceCommandHandler : IRequestHandler<UpdateTerminal
return OperationResult<UpdateTerminalDeviceResponse>.CreateFailure("终端设备不存在");
}
// 更新设备信息
device.Update(
request.DeviceName,
device.SerialNumber, // 序列号不允许修改
device.DeviceCode, // 设备编码不允许修改
request.Description,
request.AgentPort,
request.IpAddress,
currentUserId.Data!,
request.IsEnabled);
// 只更新允许修改的字段
device.UpdateDescription(request.Description);
if (request.IsEnabled)
{
device.Enable();
}
else
{
device.Disable();
}
device.UpdateAuditInfo(currentUserId.Data!);
// 保存到数据库
_deviceRepository.UpdateDevice(device);
@ -92,6 +93,7 @@ public class UpdateTerminalDeviceCommandHandler : IRequestHandler<UpdateTerminal
Description = device.Description,
AgentPort = device.AgentPort,
IsEnabled = device.IsEnabled,
DeviceType = device.DeviceType.ToString(),
UpdatedAt = device.UpdatedAt
};
@ -114,21 +116,6 @@ public class UpdateTerminalDeviceCommandHandler : IRequestHandler<UpdateTerminal
return OperationResult<bool>.CreateFailure("设备ID不能为空");
}
if (string.IsNullOrWhiteSpace(request.DeviceName))
{
return OperationResult<bool>.CreateFailure("设备名称不能为空");
}
if (string.IsNullOrWhiteSpace(request.IpAddress))
{
return OperationResult<bool>.CreateFailure("IP地址不能为空");
}
if (request.AgentPort <= 0 || request.AgentPort > 65535)
{
return OperationResult<bool>.CreateFailure("Agent端口必须在1-65535之间");
}
return OperationResult<bool>.CreateSuccess(true);
}

5
src/X1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/UpdateTerminalDeviceResponse.cs

@ -35,6 +35,11 @@ public class UpdateTerminalDeviceResponse
/// </summary>
public bool IsEnabled { get; set; }
/// <summary>
/// 设备类型
/// </summary>
public string DeviceType { get; set; } = string.Empty;
/// <summary>
/// 更新时间
/// </summary>

2
src/X1.Application/Features/TerminalDevices/Queries/GetTerminalDeviceById/GetTerminalDeviceByIdQueryHandler.cs

@ -47,12 +47,12 @@ public class GetTerminalDeviceByIdQueryHandler : IRequestHandler<GetTerminalDevi
{
Id = device.Id,
Name = device.Name,
SerialNumber = device.SerialNumber,
DeviceCode = device.DeviceCode,
Description = device.Description,
AgentPort = device.AgentPort,
IpAddress = device.IpAddress,
IsEnabled = device.IsEnabled,
DeviceType = device.DeviceType.ToString(),
CreatedAt = device.CreatedAt,
UpdatedAt = device.UpdatedAt,
CreatedBy = device.CreatedBy,

10
src/X1.Application/Features/TerminalDevices/Queries/GetTerminalDeviceById/GetTerminalDeviceByIdResponse.cs

@ -15,10 +15,7 @@ public class GetTerminalDeviceByIdResponse
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// 设备序列号
/// </summary>
public string SerialNumber { get; set; } = string.Empty;
/// <summary>
/// 设备编码
@ -45,6 +42,11 @@ public class GetTerminalDeviceByIdResponse
/// </summary>
public bool IsEnabled { get; set; }
/// <summary>
/// 设备类型
/// </summary>
public string DeviceType { get; set; } = string.Empty;
/// <summary>
/// 创建时间
/// </summary>

2
src/X1.Application/Features/TerminalDevices/Queries/GetTerminalDevices/GetTerminalDevicesQueryHandler.cs

@ -49,12 +49,12 @@ public class GetTerminalDevicesQueryHandler : IRequestHandler<GetTerminalDevices
{
Id = d.Id,
Name = d.Name,
SerialNumber = d.SerialNumber,
DeviceCode = d.DeviceCode,
Description = d.Description,
AgentPort = d.AgentPort,
IpAddress = d.IpAddress,
IsEnabled = d.IsEnabled,
DeviceType = d.DeviceType.ToString(),
CreatedAt = d.CreatedAt,
UpdatedAt = d.UpdatedAt
}).ToList(),

10
src/X1.Application/Features/TerminalDevices/Queries/GetTerminalDevices/GetTerminalDevicesResponse.cs

@ -46,10 +46,7 @@ public class TerminalDeviceDto
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// 设备序列号
/// </summary>
public string SerialNumber { get; set; } = string.Empty;
/// <summary>
/// 设备编码
@ -76,6 +73,11 @@ public class TerminalDeviceDto
/// </summary>
public bool IsEnabled { get; set; }
/// <summary>
/// 设备类型
/// </summary>
public string DeviceType { get; set; } = string.Empty;
/// <summary>
/// 创建时间
/// </summary>

4
src/X1.BackendServices/BackendServiceManager/DeviceManagementService.cs

@ -13,8 +13,8 @@ using X1.Domain.Transmission;
using CellularManagement.Domain.Repositories.Base;
using System.Data;
using Microsoft.Extensions.DependencyInjection;
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ExternalCommunication;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
using X1.Domain.ThirdPartyDeviceHttpClient;
namespace X1.BackendServices.BackendServiceManager
{

19
src/X1.Domain/DependencyInjection.cs

@ -1,19 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
namespace CellularManagement.Domain;
/// <summary>
/// 领域层依赖注入扩展
/// </summary>
public static class DependencyInjection
{
/// <summary>
/// 添加领域层服务
/// </summary>
/// <param name="services">服务集合</param>
/// <returns>服务集合</returns>
public static IServiceCollection AddDomainServices(this IServiceCollection services)
{
return services;
}
}

28
src/X1.Domain/Entities/Terminal/TerminalDevice.cs

@ -56,6 +56,12 @@ public class TerminalDevice : AuditableEntity
/// </summary>
public bool IsEnabled { get; private set; } = true;
/// <summary>
/// 设备类型
/// </summary>
[Required]
public TerminalDeviceType DeviceType { get; private set; } = TerminalDeviceType.Windows;
/// <summary>
/// 创建设备
/// </summary>
@ -67,6 +73,7 @@ public class TerminalDevice : AuditableEntity
int agentPort,
string ipAddress,
string createdBy,
TerminalDeviceType deviceType = TerminalDeviceType.Windows,
bool isEnabled = true)
{
var device = new TerminalDevice
@ -78,6 +85,7 @@ public class TerminalDevice : AuditableEntity
Description = description,
AgentPort = agentPort,
IpAddress = ipAddress,
DeviceType = deviceType,
IsEnabled = isEnabled,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow,
@ -99,6 +107,7 @@ public class TerminalDevice : AuditableEntity
int agentPort,
string ipAddress,
string updatedBy,
TerminalDeviceType deviceType = TerminalDeviceType.Windows,
bool isEnabled = true)
{
Name = name;
@ -107,6 +116,7 @@ public class TerminalDevice : AuditableEntity
Description = description;
AgentPort = agentPort;
IpAddress = ipAddress;
DeviceType = deviceType;
IsEnabled = isEnabled;
UpdatedAt = DateTime.UtcNow;
UpdatedBy = updatedBy;
@ -129,4 +139,22 @@ public class TerminalDevice : AuditableEntity
IsEnabled = false;
UpdatedAt = DateTime.UtcNow;
}
/// <summary>
/// 更新描述
/// </summary>
public void UpdateDescription(string description)
{
Description = description;
UpdatedAt = DateTime.UtcNow;
}
/// <summary>
/// 更新审计信息
/// </summary>
public void UpdateAuditInfo(string updatedBy)
{
UpdatedAt = DateTime.UtcNow;
UpdatedBy = updatedBy;
}
}

17
src/X1.Domain/Entities/Terminal/TerminalDeviceType.cs

@ -0,0 +1,17 @@
namespace CellularManagement.Domain.Entities.Terminal;
/// <summary>
/// 终端设备类型枚举
/// </summary>
public enum TerminalDeviceType
{
/// <summary>
/// Windows系统
/// </summary>
Windows = 1,
/// <summary>
/// Linux系统
/// </summary>
Linux = 2
}

4
src/X1.Domain/ExternalCommunication/IBaseInstrumentClient.cs → src/X1.Domain/ThirdPartyDeviceHttpClient/IBaseInstrumentClient.cs

@ -3,9 +3,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
namespace X1.Domain.ExternalCommunication
namespace X1.Domain.ThirdPartyDeviceHttpClient
{
/// <summary>
/// 基础仪器客户端接口

2
src/X1.Domain/ExternalCommunication/IInstrumentHttpClient.cs → src/X1.Domain/ThirdPartyDeviceHttpClient/IInstrumentHttpClient.cs

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace X1.Domain.ExternalCommunication
namespace X1.Domain.ThirdPartyDeviceHttpClient
{
/// <summary>
/// 仪器HTTP客户端接口

4
src/X1.Domain/ExternalCommunication/IInstrumentProtocolClient.cs → src/X1.Domain/ThirdPartyDeviceHttpClient/IInstrumentProtocolClient.cs

@ -3,9 +3,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
namespace X1.Domain.ExternalCommunication
namespace X1.Domain.ThirdPartyDeviceHttpClient
{
/// <summary>
/// 仪器协议客户端接口

4
src/X1.Domain/ExternalCommunication/IServiceEndpointManager.cs → src/X1.Domain/ThirdPartyDeviceHttpClient/IServiceEndpointManager.cs

@ -1,6 +1,6 @@
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
namespace X1.Domain.ExternalCommunication
namespace X1.Domain.ThirdPartyDeviceHttpClient
{
/// <summary>
/// 服务端点管理器接口

15
src/X1.Domain/ThirdPartyDeviceHttpClient/ITerminal/IADBOperationTerminalClient.cs

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace X1.Domain.ThirdPartyDeviceHttpClient.ITerminal
{
public interface IADBOperationTerminalClient
{
}
}

13
src/X1.Domain/ThirdPartyDeviceHttpClient/ITerminal/IATOperationTerminalClient.cs

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace X1.Domain.ThirdPartyDeviceHttpClient.ITerminal
{
public interface IATOperationTerminalClient
{
}
}

26
src/X1.Domain/ThirdPartyDeviceHttpClient/ITerminal/IBaseTerminalClient.cs

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
namespace X1.Domain.ThirdPartyDeviceHttpClient.ITerminal
{
public interface IBaseTerminalClient
{
/// <summary>
/// 获取测试终端机器码数据
/// </summary>
/// <param name="terminalId">测试终端ID</param>
/// <param name="options">请求选项,包含超时、请求头等配置</param>
/// <param name="cancellationToken">取消令牌,用于取消异步操作</param>
/// <returns>测试终端机器码数据,如果获取失败则返回null</returns>
/// <exception cref="ArgumentNullException">当terminalId为null时抛出</exception>
/// <exception cref="ArgumentException">当terminalId配置无效时抛出</exception>
Task<MachineCodeData?> GetDeviceSerialNumberAsync(
string terminalId,
RequestOptions? options = null,
CancellationToken cancellationToken = default);
}
}

13
src/X1.Domain/ThirdPartyDeviceHttpClient/ITerminal/ITerminalHttpClient.cs

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace X1.Domain.ThirdPartyDeviceHttpClient.ITerminal
{
public interface ITerminalHttpClient: IADBOperationTerminalClient, IATOperationTerminalClient, IBaseTerminalClient
{
}
}

2
src/X1.Domain/ExternalCommunication/Models/ApiActionResult.cs → src/X1.Domain/ThirdPartyDeviceHttpClient/Models/ApiActionResult.cs

@ -5,7 +5,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Net;
namespace X1.Domain.ExternalCommunication.Models
namespace X1.Domain.ThirdPartyDeviceHttpClient.Models
{
/// <summary>
/// API操作结果

2
src/X1.Domain/ExternalCommunication/Models/CellularNetworkConfiguration.cs → src/X1.Domain/ThirdPartyDeviceHttpClient/Models/CellularNetworkConfiguration.cs

@ -5,7 +5,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace X1.Domain.ExternalCommunication.Models
namespace X1.Domain.ThirdPartyDeviceHttpClient.Models
{
/// <summary>
/// 蜂窝网络配置实体

2
src/X1.Domain/ExternalCommunication/Models/CellularNetworkRequest.cs → src/X1.Domain/ThirdPartyDeviceHttpClient/Models/CellularNetworkRequest.cs

@ -1,4 +1,4 @@
namespace X1.Domain.ExternalCommunication.Models
namespace X1.Domain.ThirdPartyDeviceHttpClient.Models
{
/// <summary>
/// 蜂窝网络启动请求包装类

2
src/X1.Domain/ExternalCommunication/Models/CircuitBreakerOptions.cs → src/X1.Domain/ThirdPartyDeviceHttpClient/Models/CircuitBreakerOptions.cs

@ -1,6 +1,6 @@
using System.Text.Json.Serialization;
namespace X1.Domain.ExternalCommunication.Models
namespace X1.Domain.ThirdPartyDeviceHttpClient.Models
{
/// <summary>
/// 熔断器配置选项

2
src/X1.Domain/ExternalCommunication/Models/DeviceInfoResponse.cs → src/X1.Domain/ThirdPartyDeviceHttpClient/Models/DeviceInfoResponse.cs

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace X1.Domain.ExternalCommunication.Models
namespace X1.Domain.ThirdPartyDeviceHttpClient.Models
{
/// <summary>
/// 设备信息响应模型(用于API响应)

2
src/X1.Domain/ExternalCommunication/Models/DynamicHttpClientException.cs → src/X1.Domain/ThirdPartyDeviceHttpClient/Models/DynamicHttpClientException.cs

@ -1,6 +1,6 @@
using System.Runtime.Serialization;
namespace X1.Domain.ExternalCommunication.Models
namespace X1.Domain.ThirdPartyDeviceHttpClient.Models
{
/// <summary>
/// 动态HTTP客户端异常

2
src/X1.Domain/ExternalCommunication/Models/HttpRequestOptions.cs → src/X1.Domain/ThirdPartyDeviceHttpClient/Models/HttpRequestOptions.cs

@ -1,6 +1,6 @@
using System.Text.Json.Serialization;
namespace X1.Domain.ExternalCommunication.Models
namespace X1.Domain.ThirdPartyDeviceHttpClient.Models
{
/// <summary>
/// HTTP请求选项 - 定义HTTP请求的配置参数

2
src/X1.Domain/ExternalCommunication/Models/ServiceEndpoint.cs → src/X1.Domain/ThirdPartyDeviceHttpClient/Models/ServiceEndpoint.cs

@ -1,6 +1,6 @@
using System.Text.Json.Serialization;
namespace X1.Domain.ExternalCommunication.Models
namespace X1.Domain.ThirdPartyDeviceHttpClient.Models
{
/// <summary>
/// 服务端点配置模型 - 定义单个服务的连接配置信息

55
src/X1.Domain/ThirdPartyDeviceHttpClient/Models/TestTerminalResponse.cs

@ -0,0 +1,55 @@
using System.Text.Json.Serialization;
namespace X1.Domain.ThirdPartyDeviceHttpClient.Models
{
/// <summary>
/// 测试终端响应模型(泛型版本)
/// </summary>
/// <typeparam name="T">响应数据类型</typeparam>
public class TestTerminalResponse<T>
{
/// <summary>
/// 是否成功
/// </summary>
[JsonPropertyName("success")]
public bool Success { get; set; }
/// <summary>
/// 响应消息
/// </summary>
[JsonPropertyName("message")]
public string Message { get; set; } = string.Empty;
/// <summary>
/// 响应数据
/// </summary>
[JsonPropertyName("data")]
public T? Data { get; set; }
}
/// <summary>
/// 机器码数据
/// </summary>
public class MachineCodeData
{
/// <summary>
/// 机器码
/// </summary>
[JsonPropertyName("machine_code")]
public string MachineCode { get; set; } = string.Empty;
/// <summary>
/// 系统类型
/// </summary>
[JsonPropertyName("system_type")]
public string SystemType { get; set; } = string.Empty;
}
/// <summary>
/// 测试终端响应模型(非泛型版本,使用MachineCodeData作为默认类型)
/// </summary>
public class TestTerminalResponse : TestTerminalResponse<MachineCodeData>
{
// 继承自泛型版本,使用MachineCodeData作为默认类型
}
}

2
src/X1.DynamicClientCore/Core/DynamicHttpClient.Core.cs

@ -1,6 +1,6 @@
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
namespace X1.DynamicClientCore.Core
{

2
src/X1.DynamicClientCore/Core/DynamicHttpClient.Sync.cs

@ -1,6 +1,6 @@
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
namespace X1.DynamicClientCore.Core
{

2
src/X1.DynamicClientCore/Core/DynamicHttpClient.SyncCore.cs

@ -2,7 +2,7 @@ using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Polly;
using Polly.CircuitBreaker;
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
namespace X1.DynamicClientCore.Core
{

4
src/X1.DynamicClientCore/Core/DynamicHttpClient.cs

@ -1,7 +1,7 @@
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using X1.Domain.ExternalCommunication;
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ThirdPartyDeviceHttpClient;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
using X1.DynamicClientCore.Interfaces;
namespace X1.DynamicClientCore.Core

10
src/X1.DynamicClientCore/Extensions/ServiceCollectionExtensions.cs

@ -3,9 +3,10 @@ using Microsoft.Extensions.Logging;
using X1.DynamicClientCore.Interfaces;
using X1.DynamicClientCore.Core;
using X1.DynamicClientCore.Infrastructure;
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
using X1.DynamicClientCore.Service;
using X1.Domain.ExternalCommunication;
using X1.Domain.ThirdPartyDeviceHttpClient;
using X1.Domain.ThirdPartyDeviceHttpClient.ITerminal;
namespace X1.DynamicClientCore.Extensions
{
@ -52,6 +53,11 @@ namespace X1.DynamicClientCore.Extensions
services.AddSingleton<IInstrumentHttpClient, InstrumentProtocolClient>();
services.AddSingleton<IInstrumentProtocolClient, InstrumentProtocolClient>();
// 注册仪器协议客户端(单例)
services.AddSingleton<IADBOperationTerminalClient, TestTerminalRequestClient>();
services.AddSingleton<IATOperationTerminalClient, TestTerminalRequestClient>();
services.AddSingleton<IBaseTerminalClient, TestTerminalRequestClient>();
services.AddSingleton<ITerminalHttpClient, TestTerminalRequestClient>();
return services;
}

2
src/X1.DynamicClientCore/FileOperations/DynamicHttpClient.FileDownload.cs

@ -1,5 +1,5 @@
using Microsoft.Extensions.Logging;
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
namespace X1.DynamicClientCore.Core
{

2
src/X1.DynamicClientCore/FileOperations/DynamicHttpClient.FileInfo.cs

@ -1,4 +1,4 @@
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
namespace X1.DynamicClientCore.Core
{

2
src/X1.DynamicClientCore/FileOperations/DynamicHttpClient.FileUpload.cs

@ -1,6 +1,6 @@
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
namespace X1.DynamicClientCore.Core
{

2
src/X1.DynamicClientCore/Infrastructure/CircuitBreakerManager.cs

@ -2,7 +2,7 @@ using Microsoft.Extensions.Logging;
using Polly;
using Polly.CircuitBreaker;
using Polly.Extensions.Http;
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
using X1.DynamicClientCore.Interfaces;
namespace X1.DynamicClientCore.Infrastructure

4
src/X1.DynamicClientCore/Infrastructure/ServiceEndpointManager.cs

@ -1,5 +1,5 @@
using X1.Domain.ExternalCommunication;
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ThirdPartyDeviceHttpClient;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
using X1.DynamicClientCore.Interfaces;
namespace X1.DynamicClientCore.Infrastructure

2
src/X1.DynamicClientCore/Interfaces/IAsyncHttpClient.cs

@ -1,4 +1,4 @@
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
namespace X1.DynamicClientCore.Interfaces
{

2
src/X1.DynamicClientCore/Interfaces/ICircuitBreakerManager.cs

@ -1,6 +1,6 @@
using Polly;
using Polly.CircuitBreaker;
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
namespace X1.DynamicClientCore.Interfaces
{

2
src/X1.DynamicClientCore/Interfaces/IFileHttpClient.cs

@ -1,4 +1,4 @@
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
namespace X1.DynamicClientCore.Interfaces

2
src/X1.DynamicClientCore/Interfaces/ISyncHttpClient.cs

@ -1,4 +1,4 @@
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
namespace X1.DynamicClientCore.Interfaces
{

32
src/X1.DynamicClientCore/Service/InstrumentProtocolClient.cs

@ -4,8 +4,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using X1.Domain.ExternalCommunication;
using X1.Domain.ExternalCommunication.Models;
using X1.Domain.ThirdPartyDeviceHttpClient;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
using X1.DynamicClientCore.Interfaces;
@ -33,8 +33,8 @@ namespace X1.DynamicClientCore.Service
/// <param name="logger">日志记录器</param>
/// <exception cref="ArgumentNullException">当任何必需参数为null时抛出</exception>
public InstrumentProtocolClient(
IDynamicHttpClient dynamicHttpClient,
IServiceEndpointManager serviceEndpointManager,
IDynamicHttpClient dynamicHttpClient,
IServiceEndpointManager serviceEndpointManager,
ILogger<InstrumentProtocolClient> logger)
{
_dynamicHttpClient = dynamicHttpClient ?? throw new ArgumentNullException(nameof(dynamicHttpClient));
@ -51,8 +51,8 @@ namespace X1.DynamicClientCore.Service
/// <returns>设备序列号,如果获取失败则返回空字符串</returns>
/// <exception cref="ArgumentNullException">当endpoint为null时抛出</exception>
public async Task<string> GetDeviceSerialNumberAsync(
string instrumentNumber,
RequestOptions? options = null,
string? instrumentNumber,
RequestOptions? options = null,
CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(instrumentNumber))
@ -61,13 +61,13 @@ namespace X1.DynamicClientCore.Service
try
{
_logger.LogDebug("开始获取设备序列号,端点:{EndpointName}", instrumentNumber);
// 发送HTTP请求获取设备信息
var response = await _dynamicHttpClient.GetAsync<ApiActionResult<DeviceInfoResponse>>(
instrumentNumber,
"System/serial-number",
options,
instrumentNumber,
"System/serial-number",
options,
cancellationToken);
if (response == null)
@ -78,15 +78,15 @@ namespace X1.DynamicClientCore.Service
if (!response.IsSuccess)
{
_logger.LogWarning("获取设备序列号失败:请求未成功,端点:{EndpointName},错误:{ErrorMessage}",
_logger.LogWarning("获取设备序列号失败:请求未成功,端点:{EndpointName},错误:{ErrorMessage}",
instrumentNumber, response.Message ?? "未知错误");
return string.Empty;
}
var serialNumber = response.Data?.SerialNumber ?? string.Empty;
_logger.LogInformation("成功获取设备序列号:{SerialNumber},端点:{EndpointName}",
_logger.LogInformation("成功获取设备序列号:{SerialNumber},端点:{EndpointName}",
serialNumber, instrumentNumber);
return serialNumber;
}
catch (Exception ex)
@ -107,7 +107,7 @@ namespace X1.DynamicClientCore.Service
/// <exception cref="ArgumentException">当request中的设备编号为空时抛出</exception>
public async Task<bool> StartNetworkAsync(
StartCellularNetworkRequest request,
RequestOptions? options = null,
RequestOptions? options = null,
CancellationToken cancellationToken = default)
{
if (request == null)
@ -170,7 +170,7 @@ namespace X1.DynamicClientCore.Service
public async Task<bool> StopNetworkAsync(
string instrumentNumber,
StopCellularNetworkRequest request,
RequestOptions? options = null,
RequestOptions? options = null,
CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(instrumentNumber))

108
src/X1.DynamicClientCore/Service/TestTerminalRequestClient.cs

@ -0,0 +1,108 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using X1.Domain.ThirdPartyDeviceHttpClient;
using X1.Domain.ThirdPartyDeviceHttpClient.ITerminal;
using X1.Domain.ThirdPartyDeviceHttpClient.Models;
using X1.DynamicClientCore.Interfaces;
namespace X1.DynamicClientCore.Service
{
/// <summary>
/// 测试终端HTTP客户端
/// 提供与测试终端设备进行HTTP通信的功能
/// </summary>
/// <remarks>
/// 实现ITerminalHttpClient接口,专门用于测试终端设备的通信。
/// 支持获取测试终端的机器码和系统信息等基础功能。
/// </remarks>
public class TestTerminalRequestClient : ITerminalHttpClient
{
private readonly ILogger<InstrumentProtocolClient> _logger;
private readonly IDynamicHttpClient _dynamicHttpClient;
private readonly IServiceEndpointManager _serviceEndpointManager;
/// <summary>
/// 初始化测试终端
/// </summary>
/// <param name="dynamicHttpClient">动态HTTP客户端</param>
/// <param name="serviceEndpointManager">服务端点管理器</param>
/// <param name="logger">日志记录器</param>
/// <exception cref="ArgumentNullException">当任何必需参数为null时抛出</exception>
public TestTerminalRequestClient(
IDynamicHttpClient dynamicHttpClient,
IServiceEndpointManager serviceEndpointManager,
ILogger<InstrumentProtocolClient> logger)
{
_dynamicHttpClient = dynamicHttpClient ?? throw new ArgumentNullException(nameof(dynamicHttpClient));
_serviceEndpointManager = serviceEndpointManager ?? throw new ArgumentNullException(nameof(serviceEndpointManager));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
/// 获取测试终端机器码数据
/// </summary>
/// <param name="terminalId">测试终端ID,用作服务名称</param>
/// <param name="options">请求选项,包含超时、请求头等配置</param>
/// <param name="cancellationToken">取消令牌,用于取消异步操作</param>
/// <returns>测试终端机器码数据,包含机器码和系统类型信息,如果获取失败则返回null</returns>
/// <exception cref="ArgumentNullException">当terminalId为null或空时抛出</exception>
/// <exception cref="ArgumentException">当terminalId配置无效时抛出</exception>
/// <remarks>
/// 该方法通过HTTP请求调用测试终端的机器码API,获取设备的机器码和系统类型信息。
/// 使用terminalId作为服务名称进行API调用,端点固定为"/api/v1/system/machine-code"。
/// 返回的MachineCodeData包含机器码和系统类型两个字段。
/// </remarks>
public async Task<MachineCodeData?> GetDeviceSerialNumberAsync(string terminalId, RequestOptions? options = null, CancellationToken cancellationToken = default)
{
try
{
_logger.LogInformation("开始获取测试终端机器码数据,终端ID: {TerminalId}", terminalId);
var endpoint = "/api/v1/system/machine-code";
// 调用机器码API
var response = await _dynamicHttpClient.GetAsync<TestTerminalResponse<MachineCodeData>>(
terminalId,
endpoint,
options,
cancellationToken);
if (response == null)
{
_logger.LogWarning("获取测试终端机器码响应为空,终端ID: {TerminalId}", terminalId);
return null;
}
if (!response.Success)
{
_logger.LogError("获取测试终端机器码失败,终端ID: {TerminalId}, 错误信息: {Message}",
terminalId, response.Message);
return null;
}
if (response.Data?.MachineCode == null)
{
_logger.LogWarning("测试终端机器码数据为空,终端ID: {TerminalId}", terminalId);
return null;
}
var machineCode = response.Data.MachineCode;
var systemType = response.Data.SystemType;
_logger.LogInformation("成功获取测试终端机器码数据,终端ID: {TerminalId}, 机器码: {MachineCode}, 系统类型: {SystemType}",
terminalId, machineCode, systemType);
return response.Data;
}
catch (Exception ex)
{
_logger.LogError(ex, "获取测试终端机器码数据时发生异常,终端ID: {TerminalId}", terminalId);
return null;
}
}
}
}

91
src/X1.Infrastructure/Configurations/Terminal/TerminalDeviceConfiguration.cs

@ -0,0 +1,91 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using CellularManagement.Domain.Entities.Terminal;
namespace CellularManagement.Infrastructure.Configurations.Terminal;
/// <summary>
/// 终端设备实体配置
/// </summary>
public class TerminalDeviceConfiguration : IEntityTypeConfiguration<TerminalDevice>
{
/// <summary>
/// 配置终端设备实体
/// </summary>
/// <param name="builder">实体类型构建器</param>
public void Configure(EntityTypeBuilder<TerminalDevice> builder)
{
// 表名
builder.ToTable("tb_terminal_devices");
// 主键
builder.HasKey(x => x.Id);
// 属性配置
builder.Property(x => x.Id)
.IsRequired()
.HasMaxLength(450);
builder.Property(x => x.Name)
.IsRequired()
.HasMaxLength(100);
builder.Property(x => x.SerialNumber)
.IsRequired()
.HasMaxLength(50);
builder.Property(x => x.DeviceCode)
.IsRequired()
.HasMaxLength(50);
builder.Property(x => x.Description)
.HasMaxLength(500);
builder.Property(x => x.AgentPort)
.IsRequired();
builder.Property(x => x.IpAddress)
.IsRequired()
.HasMaxLength(45);
builder.Property(x => x.IsEnabled)
.IsRequired()
.HasDefaultValue(true);
builder.Property(x => x.DeviceType)
.IsRequired()
.HasConversion<int>()
.HasDefaultValue(TerminalDeviceType.Windows);
// 审计字段
builder.Property(x => x.CreatedAt)
.IsRequired();
builder.Property(x => x.UpdatedAt);
builder.Property(x => x.CreatedBy)
.IsRequired()
.HasMaxLength(450);
builder.Property(x => x.UpdatedBy)
.HasMaxLength(450);
// 索引
builder.HasIndex(x => x.SerialNumber)
.IsUnique();
builder.HasIndex(x => x.DeviceCode)
.IsUnique();
builder.HasIndex(x => x.IpAddress);
builder.HasIndex(x => x.IsEnabled);
builder.HasIndex(x => x.DeviceType);
builder.HasIndex(x => x.CreatedAt);
// 软删除过滤器
builder.HasQueryFilter(x => !x.IsDeleted);
}
}

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

@ -6,6 +6,7 @@ using CellularManagement.Domain.Entities;
using CellularManagement.Domain.Entities.Logging;
using CellularManagement.Domain.Entities.Device;
using CellularManagement.Domain.Entities.NetworkProfile;
using CellularManagement.Domain.Entities.Terminal;
namespace CellularManagement.Infrastructure.Context;
@ -82,6 +83,11 @@ public class AppDbContext : IdentityDbContext<AppUser, AppRole, string>
/// </summary>
public DbSet<Stack_CoreIMS_Binding> Stack_CoreIMS_Bindings { get; set; } = null!;
/// <summary>
/// 终端设备集合
/// </summary>
public DbSet<TerminalDevice> TerminalDevices { get; set; } = null!;
/// <summary>
/// 初始化数据库上下文
/// </summary>

2
src/X1.WebSocket/Extensions/ServiceCollectionExtensions.cs

@ -37,7 +37,7 @@ public static class ServiceCollectionExtensions
services.AddSingleton<IWebSocketMessageHandler, HeartbeatHandlerManager>();
services.AddSingleton<IWebSocketMessageHandler, NotificationMessageHandler>();
services.AddSingleton<IWebSocketMessageHandler, ProtocolMessageHandler>();
services.AddSingleton<IWebSocketMessageHandler, TerminalMessageHandler>();
// 注册后台服务
services.AddHostedService<WebSocketMessageService>();
services.AddHostedService<ConnectionHealthCheckService>();

14
src/X1.WebSocket/Handlers/ProtocolMessageHandler.cs

@ -33,11 +33,11 @@ namespace CellularManagement.WebSocket.Handlers
public async Task<WebSocketMessage> HandleAsync(WebSocketMessage message)
{
var messageData = Encoding.UTF8.GetString(message.Data);
_logger.LogDebug("收到协议消息,连接ID:{ConnectionId},数据长度:{DataLength}字节",
_logger.LogDebug("收到协议消息,连接ID:{ConnectionId},数据长度:{DataLength}字节",
message.ConnectionId, message.Data.Length);
ProcessProtocolMessage(messageData);
ProcessProtocolMessage(messageData);
var response = new WebSocketMessage
{
ConnectionId = message.ConnectionId,
@ -64,7 +64,7 @@ namespace CellularManagement.WebSocket.Handlers
try
{
var protocolMessage = JsonSerializer.Deserialize<ProtocolMessage>(messageData, _jsonOptions);
if (protocolMessage?.Payload?.Message == null)
{
_logger.LogWarning("协议消息载荷为空或格式无效");
@ -81,7 +81,7 @@ namespace CellularManagement.WebSocket.Handlers
}
ProcessProtocolLogs(protocolMessage.Payload.Message);
_logger.LogInformation("协议消息处理完成,成功处理 {MessageCount} 条消息", messageCount);
}
catch (JsonException jsonEx)
@ -158,7 +158,7 @@ namespace CellularManagement.WebSocket.Handlers
Message = !string.IsNullOrEmpty(log.Message) ? log.Message : "N/A"
};
_logger.LogDebug("处理协议日志 - 【ID】{Id} 【时间】{Time} 【层】{Layer} 【UEID】{UEID} 【IMSI】{IMSI} 【PLMN】{PLMN} 【小区】{CellID} 【方向】{Direction} 【信息】{Info} 【消息】{Message}",
_logger.LogDebug("处理协议日志 - 【ID】{Id} 【时间】{Time} 【层】{Layer} 【UEID】{UEID} 【IMSI】{IMSI} 【PLMN】{PLMN} 【小区】{CellID} 【方向】{Direction} 【信息】{Info} 【消息】{Message}",
logInfo.Id, logInfo.Time, logInfo.Layer, logInfo.UEID, logInfo.IMSI, logInfo.PLMN, logInfo.CellID, logInfo.Direction, logInfo.Info, logInfo.Message);
// 这里可以添加具体的业务逻辑处理

38
src/X1.WebSocket/Handlers/TerminalMessageHandler.cs

@ -0,0 +1,38 @@
using CellularManagement.WebSocket.Models;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CellularManagement.WebSocket.Handlers
{
public class TerminalMessageHandler : IWebSocketMessageHandler
{
private readonly ILogger<TerminalMessageHandler> _logger;
public TerminalMessageHandler(ILogger<TerminalMessageHandler> logger)
{
_logger = logger;
}
public string MessageType => "terminal";
public async Task<WebSocketMessage> HandleAsync(WebSocketMessage message)
{
var messageData = Encoding.UTF8.GetString(message.Data);
_logger.LogDebug("收到终端数据,连接ID:{ConnectionId} Data:{messageData}", message.ConnectionId, messageData);
await Task.CompletedTask.ConfigureAwait(false);
var response = new WebSocketMessage
{
ConnectionId = message.ConnectionId,
MessageType = System.Net.WebSockets.WebSocketMessageType.Text,
NeedQueue = false
};
return response;
}
}
}

627
src/modify.md

@ -2,6 +2,75 @@
## 2024年修改记录
### 修复CreateTerminalDeviceCommandHandler中的空引用警告和并发问题
#### 修改文件:
`X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceCommandHandler.cs`
#### 修改内容:
1. **问题描述**
- 在第95行出现可能的空引用警告
- `deviceInfoResult.ErrorMessages` 可能为 null,但 `CreateFailure(List<string> errorMessages)` 方法期望非空参数
- 设备编码生成逻辑存在并发问题:多个请求可能生成相同的设备编码
2. **解决方案**
- 使用空合并操作符 `??` 提供默认值
- 使用空断言操作符 `!` 处理已知非空的Data属性
- 重构设备编码生成逻辑,添加重试机制和并发处理
- 将原来的 `GenerateDeviceCodeAsync` 和编码检查逻辑合并为 `GenerateUniqueDeviceCodeAsync`
3. **修改代码**
```csharp
// 修改前 - 空引用问题
return OperationResult<CreateTerminalDeviceResponse>.CreateFailure(deviceInfoResult.ErrorMessages);
// 修改后 - 空引用问题
return OperationResult<CreateTerminalDeviceResponse>.CreateFailure(deviceInfoResult.ErrorMessages ?? new List<string> { "获取设备信息失败" });
// 修改前 - Data空引用问题
var serialNumber = deviceInfoResult.Data.SerialNumber;
var deviceType = deviceInfoResult.Data.DeviceType;
// 修改后 - Data空引用问题
var serialNumber = deviceInfoResult.Data!.SerialNumber;
var deviceType = deviceInfoResult.Data!.DeviceType;
// 修改前 - 并发问题
var deviceCode = await GenerateDeviceCodeAsync(serialNumber, cancellationToken);
if (await _deviceRepository.DeviceCodeExistsAsync(deviceCode, cancellationToken))
{
return OperationResult<CreateTerminalDeviceResponse>.CreateFailure($"终端设备编码 {deviceCode} 已存在");
}
// 修改后 - 并发问题
var deviceCode = await GenerateUniqueDeviceCodeAsync(cancellationToken);
```
4. **新的GenerateUniqueDeviceCodeAsync方法特性**
- 使用短UUID(8位)生成简洁的设备编码
- 格式:`TERM-{8位短UUID}`,如 `TERM-a1b2c3d4`
- 极低的冲突概率,理论上几乎不可能重复
- 如果发生冲突,自动添加时间戳后缀(6位数字)
- 简洁高效的实现,无需复杂的重试逻辑
- 无需序列号参数,使用纯UUID生成方式
- 完整的日志记录,便于调试和监控
5. **技术细节**
- 使用 `??` 空合并操作符处理可能的 null 值
- 提供有意义的默认错误消息
- 使用 `Guid.NewGuid().ToString("N").Substring(0, 8)` 生成8位短UUID
- 使用 `DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() % 1000000` 生成6位时间戳后缀
- 保持代码的简洁性和高效性
#### 修改时间:
2024年
#### 修改原因:
解决编译警告,确保代码的健壮性,避免运行时出现空引用异常。同时解决高并发场景下设备编码重复的问题,提高系统的可靠性和稳定性。
---
### ADB操作和AT操作控制器实现
#### 修改文件:
@ -1154,4 +1223,560 @@
- ✅ 控制器已完善,参考ProtocolVersionsController模式
- ✅ 统一的错误处理和日志记录
- ✅ 符合DDD和CQRS架构模式
- ✅ 支持完整的CRUD操作
- ✅ 支持完整的CRUD操作
---
## 2025-01-13 TerminalDevice 添加设备类型枚举
### 修改文件
1. `X1.Domain/Entities/Terminal/TerminalDeviceType.cs` - 新增设备类型枚举
2. `X1.Domain/Entities/Terminal/TerminalDevice.cs` - 添加设备类型属性
3. `X1.Application/Features/TerminalDevices/Queries/GetTerminalDevices/GetTerminalDevicesResponse.cs` - 更新DTO
4. `X1.Application/Features/TerminalDevices/Queries/GetTerminalDevices/GetTerminalDevicesQueryHandler.cs` - 更新映射
5. `X1.Application/Features/TerminalDevices/Queries/GetTerminalDeviceById/GetTerminalDeviceByIdResponse.cs` - 更新响应
6. `X1.Application/Features/TerminalDevices/Queries/GetTerminalDeviceById/GetTerminalDeviceByIdQueryHandler.cs` - 更新映射
7. `X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceCommand.cs` - 添加设备类型参数
8. `X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceResponse.cs` - 更新响应
9. `X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceCommandHandler.cs` - 更新处理逻辑
10. `X1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/UpdateTerminalDeviceCommand.cs` - 添加设备类型参数
11. `X1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/UpdateTerminalDeviceResponse.cs` - 更新响应
12. `X1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/UpdateTerminalDeviceCommandHandler.cs` - 更新处理逻辑
13. `X1.Infrastructure/Context/AppDbContext.cs` - 添加TerminalDevice DbSet
14. `X1.Infrastructure/Configurations/Terminal/TerminalDeviceConfiguration.cs` - 新增数据库配置
### 修改内容
1. **设备类型枚举创建**
- 创建了 `TerminalDeviceType` 枚举
- 包含 `Windows = 1``Linux = 2` 两个选项
- 用于区分终端设备的操作系统类型
2. **TerminalDevice实体更新**
- 添加了 `DeviceType` 属性,类型为 `TerminalDeviceType`
- 默认值为 `TerminalDeviceType.Windows`
- 更新了 `Create``Update` 方法,添加设备类型参数
- 在数据库中以整数形式存储枚举值
3. **Application层更新**
- **DTO和响应类**:所有相关的DTO和响应类都添加了 `DeviceType` 字段
- **查询处理器**:更新了映射逻辑,将枚举值转换为字符串
- **命令类**:添加了设备类型参数,默认值为 "Windows"
- **命令处理器**:添加了枚举解析逻辑和验证
4. **数据库配置**
- 在 `AppDbContext` 中添加了 `TerminalDevices` DbSet
- 创建了 `TerminalDeviceConfiguration` 配置类
- 配置了设备类型字段的数据库映射(使用整数存储)
- 添加了相关的索引和约束
5. **技术特性**
- 使用枚举确保类型安全
- 在数据库中存储为整数,提高性能
- 在API中返回字符串,便于前端使用
- 完整的验证和错误处理
- 向后兼容,现有数据默认为Windows类型
6. **设计原则**
- 遵循DDD原则,使用枚举表示领域概念
- 保持与现有架构的一致性
- 提供完整的CRUD操作支持
- 确保数据完整性和类型安全
### 完成状态
- ✅ 设备类型枚举创建完成
- ✅ TerminalDevice实体更新完成
- ✅ Application层所有相关文件更新完成
- ✅ 数据库配置完成
- ✅ 类型安全和验证逻辑完整
- ✅ 向后兼容性确保完成
---
## 2025-01-13 TerminalDevice 数据库表命名规范修正
### 修改文件
`X1.Infrastructure/Configurations/Terminal/TerminalDeviceConfiguration.cs`
### 修改内容
1. **问题描述**
- 原表名 `"TerminalDevices"` 不符合项目的命名规范
- 项目要求使用 `tb_` 前缀并且都是小写
2. **修正方案**
- 将表名从 `"TerminalDevices"` 修改为 `"tb_terminal_devices"`
- 符合项目的命名规范:`tb_` 前缀 + 全小写 + 下划线分隔
3. **具体修改**
```csharp
// 修改前
builder.ToTable("TerminalDevices");
// 修改后
builder.ToTable("tb_terminal_devices");
```
4. **命名规范说明**
- `tb_`:表名前缀,表示这是一个数据库表
- `terminal_devices`:实体名称的小写形式,使用下划线分隔单词
- 符合项目的统一命名约定
### 完成状态
- ✅ 表名修正完成
- ✅ 符合项目命名规范
- ✅ 与现有数据库表命名保持一致
---
## 2025-01-13 TerminalDevice 命令字段限制修改
### 修改文件
1. `X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceCommand.cs`
2. `X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceCommandHandler.cs`
3. `X1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/UpdateTerminalDeviceCommand.cs`
4. `X1.Application/Features/TerminalDevices/Commands/UpdateTerminalDevice/UpdateTerminalDeviceCommandHandler.cs`
5. `X1.Domain/Entities/Terminal/TerminalDevice.cs`
### 修改内容
1. **CreateTerminalDeviceCommand 修改**
- **移除字段**:删除了 `DeviceType` 字段,不再由用户填写
- **自动检测**:设备类型由系统根据设备连接信息自动检测
- **业务逻辑**:系统会尝试获取设备系统信息来判断是Windows还是Linux
2. **CreateTerminalDeviceCommandHandler 修改**
- **自动检测逻辑**:添加了 `DetectDeviceTypeAsync` 方法
- **系统信息检测**:通过 `GetDeviceSystemInfoAsync` 获取设备系统信息
- **智能判断**:根据系统信息中的关键词判断设备类型
- **默认处理**:如果无法检测,默认使用Windows类型
- **错误处理**:完整的异常处理和日志记录
3. **UpdateTerminalDeviceCommand 修改**
- **字段限制**:只保留 `DeviceId`、`Description` 和 `IsEnabled` 字段
- **移除字段**:删除了 `DeviceName`、`IpAddress`、`AgentPort`、`DeviceType` 字段
- **业务逻辑**:只允许修改设备描述和启用状态,保护核心配置信息
4. **UpdateTerminalDeviceCommandHandler 修改**
- **更新逻辑**:只更新允许修改的字段(描述和启用状态)
- **验证简化**:移除了对已删除字段的验证
- **方法调用**:使用专门的 `UpdateDescription``Enable/Disable` 方法
- **审计信息**:正确更新审计信息
5. **TerminalDevice 实体修改**
- **新增方法**:添加了 `UpdateDescription` 方法用于更新描述
- **新增方法**:添加了 `UpdateAuditInfo` 方法用于更新审计信息
- **封装性**:保持实体的封装性,通过方法修改状态
6. **技术特性**
- **自动检测**:设备类型自动检测,无需用户干预
- **字段保护**:核心配置字段不允许修改,确保数据安全
- **业务逻辑**:符合实际业务需求,只允许修改非关键信息
- **错误处理**:完整的异常处理和日志记录
### 完成状态
- ✅ CreateTerminalDeviceCommand 设备类型字段移除完成
- ✅ 自动设备类型检测逻辑实现完成
- ✅ UpdateTerminalDeviceCommand 字段限制完成
- ✅ 命令处理器逻辑更新完成
- ✅ TerminalDevice 实体方法扩展完成
- ✅ 业务逻辑安全性确保完成
---
## 2025-01-13 TerminalDevice DTO 序列号字段移除
### 修改文件
1. `X1.Application/Features/TerminalDevices/Queries/GetTerminalDevices/GetTerminalDevicesResponse.cs`
2. `X1.Application/Features/TerminalDevices/Queries/GetTerminalDevices/GetTerminalDevicesQueryHandler.cs`
3. `X1.Application/Features/TerminalDevices/Queries/GetTerminalDeviceById/GetTerminalDeviceByIdResponse.cs`
4. `X1.Application/Features/TerminalDevices/Queries/GetTerminalDeviceById/GetTerminalDeviceByIdQueryHandler.cs`
### 修改内容
1. **GetTerminalDevicesResponse.cs 修改**
- **移除字段**:从 `TerminalDeviceDto` 中删除了 `SerialNumber` 字段
- **安全考虑**:序列号是敏感信息,不应该在列表中显示
- **数据保护**:保护设备序列号信息不被泄露
2. **GetTerminalDevicesQueryHandler.cs 修改**
- **映射移除**:移除了对 `SerialNumber` 字段的映射
- **数据过滤**:确保列表查询不返回序列号信息
- **性能优化**:减少不必要的数据传输
3. **GetTerminalDeviceByIdResponse.cs 修改**
- **移除字段**:从详情响应中删除了 `SerialNumber` 字段
- **一致性**:与列表查询保持一致,都不显示序列号
- **安全策略**:统一的安全策略,保护敏感信息
4. **GetTerminalDeviceByIdQueryHandler.cs 修改**
- **映射移除**:移除了对 `SerialNumber` 字段的映射
- **详情保护**:即使是在详情查询中也不显示序列号
- **数据安全**:确保序列号信息不被任何API端点泄露
5. **安全特性**
- **信息保护**:序列号作为设备唯一标识,属于敏感信息
- **访问控制**:通过API层面控制敏感信息的访问
- **数据最小化**:只返回必要的设备信息
- **隐私保护**:符合数据隐私保护原则
### 完成状态
- ✅ GetTerminalDevicesResponse 序列号字段移除完成
- ✅ GetTerminalDevicesQueryHandler 映射移除完成
- ✅ GetTerminalDeviceByIdResponse 序列号字段移除完成
- ✅ GetTerminalDeviceByIdQueryHandler 映射移除完成
- ✅ 数据安全保护策略实施完成
### TestTerminalRequestClient GetDeviceSerialNumberAsync 方法完善
#### 修改文件:
1. `X1.Domain/ThirdPartyDeviceHttpClient/Models/MachineCodeResponse.cs` - 新增机器码响应模型
2. `X1.DynamicClientCore/Service/TestTerminalRequestClient.cs` - 完善GetDeviceSerialNumberAsync方法
#### 修改内容:
1. **功能描述**
- 完善了 `TestTerminalRequestClient``GetDeviceSerialNumberAsync` 方法
- 实现了调用机器码API获取设备序列号的功能
- 创建了对应的响应模型来匹配API返回的数据结构
2. **新增响应模型**
- **MachineCodeResponse**:机器码响应模型
- `Success`:是否成功
- `Message`:响应消息
- `Data`:响应数据
- **MachineCodeData**:机器码数据
- `MachineCode`:机器码(设备序列号)
- `SystemType`:系统类型
3. **API调用实现**
- **端点**:`/api/v1/system/machine-code`
- **服务名称**:`test-terminal`
- **HTTP方法**:GET
- **响应处理**:完整的成功/失败状态检查
4. **技术特性**
- **异步操作**:使用 `async/await` 模式
- **错误处理**:完整的异常捕获和日志记录
- **参数验证**:检查响应数据的有效性
- **日志记录**:详细的操作日志,包括开始、成功、失败信息
- **资源清理**:确保异常情况下的资源正确释放
5. **业务逻辑**
- 调用机器码API获取设备序列号
- 验证API响应的成功状态
- 提取机器码作为设备序列号返回
- 记录系统类型信息用于调试
- 失败时返回空字符串
6. **响应格式匹配**
```json
{
"success": true,
"message": "机器码获取成功",
"data": {
"machine_code": "03000200-0400-0500-0006-000700080009",
"system_type": "windows"
}
}
```
7. **设计原则**
- 遵循现有架构模式
- 完整的错误处理和日志记录
- 使用依赖注入管理依赖关系
- 支持取消令牌和异步操作
- 统一的响应格式处理
#### 修改时间:
2025年1月13日
#### 修改原因:
需要完善 `TestTerminalRequestClient``GetDeviceSerialNumberAsync` 方法,实现从指定API端点获取设备机器码的功能,为终端设备管理提供真实的设备序列号获取能力。
### MachineCodeResponse 重构为 TestTerminalResponse
#### 修改文件:
1. `X1.Domain/ThirdPartyDeviceHttpClient/Models/MachineCodeResponse.cs` - 重构响应模型
2. `X1.DynamicClientCore/Service/TestTerminalRequestClient.cs` - 更新使用新的响应模型
#### 修改内容:
1. **功能描述**
- 将 `MachineCodeResponse` 重构为更通用的 `TestTerminalResponse`
- 使测试终端相关的所有API都可以使用这个响应实体
- 直接删除向后兼容的 `MachineCodeResponse` 类,简化代码结构
2. **响应模型重构**
- **TestTerminalResponse<T>**:泛型版本,支持任意数据类型
- `Success`:是否成功
- `Message`:响应消息
- `Data`:泛型响应数据
- **TestTerminalResponse**:非泛型版本,默认使用 `MachineCodeData`
- **MachineCodeResponse**:已删除,不再提供向后兼容
3. **数据模型**
- **MachineCodeData**:机器码数据模型
- `MachineCode`:机器码(设备序列号)
- `SystemType`:系统类型
4. **技术特性**
- **泛型设计**:支持任意数据类型的响应
- **简化结构**:删除不必要的向后兼容类
- **类型安全**:使用泛型确保编译时类型检查
- **可扩展性**:未来可以轻松添加新的数据类型
5. **使用示例**
```csharp
// 使用泛型版本(推荐)
var response = await _dynamicHttpClient.GetAsync<TestTerminalResponse<MachineCodeData>>(
serviceName, endpoint, options, cancellationToken);
// 使用非泛型版本(简化)
var response = await _dynamicHttpClient.GetAsync<TestTerminalResponse>(
serviceName, endpoint, options, cancellationToken);
```
6. **设计原则**
- **单一职责**:专注于测试终端响应格式
- **开闭原则**:对扩展开放,对修改封闭
- **简洁性**:删除不必要的向后兼容代码
- **类型安全**:编译时类型检查
7. **未来扩展**
- 可以轻松添加新的数据类型,如设备状态、网络配置等
- 所有测试终端API都可以使用统一的响应格式
- 支持不同的业务场景和数据需求
#### 修改时间:
2025年1月13日
#### 修改原因:
需要将机器码响应模型重构为更通用的测试终端响应模型,使所有测试终端相关的API都可以使用统一的响应格式,提高代码的可复用性和类型安全性。删除不必要的向后兼容代码,简化代码结构。
### TestTerminalRequestClient 添加 GetMachineCodeDataAsync 方法
#### 修改文件:
`X1.DynamicClientCore/Service/TestTerminalRequestClient.cs` - 添加获取完整机器码数据的方法
#### 修改内容:
1. **功能描述**
- 添加了 `GetMachineCodeDataAsync` 方法,返回完整的 `MachineCodeData` 对象
- 重构了 `GetDeviceSerialNumberAsync` 方法,使其调用新的方法
- 保持接口兼容性的同时,提供更丰富的数据返回
2. **新增方法**
- **GetMachineCodeDataAsync**:获取完整的机器码数据
- 返回类型:`Task<MachineCodeData?>`
- 包含机器码和系统类型信息
- 完整的错误处理和日志记录
3. **方法重构**
- **GetDeviceSerialNumberAsync**:重构为调用 `GetMachineCodeDataAsync`
- 保持原有接口兼容性
- 只返回机器码字符串
- 简化了实现逻辑
4. **技术特性**
- **代码复用**:避免重复的API调用逻辑
- **类型安全**:返回强类型的 `MachineCodeData` 对象
- **错误处理**:统一的异常处理和日志记录
- **向后兼容**:保持原有接口不变
5. **使用示例**
```csharp
// 获取完整机器码数据(推荐)
var machineCodeData = await client.GetMachineCodeDataAsync(instrumentNumber);
if (machineCodeData != null)
{
var machineCode = machineCodeData.MachineCode;
var systemType = machineCodeData.SystemType;
}
// 获取机器码字符串(兼容原有接口)
var serialNumber = await client.GetDeviceSerialNumberAsync(instrumentNumber);
```
6. **设计原则**
- **单一职责**:每个方法专注于特定功能
- **代码复用**:避免重复的API调用逻辑
- **向后兼容**:保持原有接口不变
- **类型安全**:提供强类型的数据返回
7. **业务价值**
- 提供更丰富的设备信息(机器码 + 系统类型)
- 支持更复杂的业务逻辑处理
- 保持与现有代码的兼容性
- 提高代码的可维护性
#### 修改时间:
2025年1月13日
#### 修改原因:
需要提供获取完整机器码数据的功能,包括机器码和系统类型信息,同时保持与现有接口的兼容性,为测试终端管理提供更丰富的数据支持。
### TestTerminalRequestClient 最终优化
#### 修改文件:
1. `X1.Domain/ThirdPartyDeviceHttpClient/ITerminal/IBaseTerminalClient.cs` - 更新接口定义
2. `X1.DynamicClientCore/Service/TestTerminalRequestClient.cs` - 优化实现和注释
#### 修改内容:
1. **接口定义优化**
- 将 `GetDeviceSerialNumberAsync` 方法返回类型从 `Task<string>` 改为 `Task<MachineCodeData?>`
- 参数名从 `instrumentNumber` 改为 `terminalId`
- 更新方法注释,明确返回测试终端机器码数据
2. **实现优化**
- 移除重复的 `GetMachineCodeDataAsync` 方法
- 直接使用 `GetDeviceSerialNumberAsync` 返回 `MachineCodeData` 对象
- 使用 `terminalId` 作为服务名称,而不是硬编码的 "test-terminal"
- 添加完整的XML注释,包含参数说明、返回值说明、异常说明和备注
3. **技术特性**
- **简化设计**:移除重复方法,统一使用一个方法
- **正确命名**:使用 `terminalId` 作为服务名称
- **完整注释**:提供详细的XML文档注释
- **类型安全**:返回强类型的 `MachineCodeData` 对象
4. **XML注释内容**
- 参数说明:明确 `terminalId` 用作服务名称
- 返回值说明:详细描述返回的数据结构
- 异常说明:列出可能抛出的异常类型
- 备注说明:解释方法的工作原理和API调用细节
5. **使用示例**
```csharp
// 获取测试终端机器码数据
var machineCodeData = await client.GetDeviceSerialNumberAsync("terminal-001");
if (machineCodeData != null)
{
var machineCode = machineCodeData.MachineCode;
var systemType = machineCodeData.SystemType;
}
```
6. **设计原则**
- **单一职责**:一个方法专注于获取机器码数据
- **正确命名**:参数名和方法名符合业务语义
- **完整文档**:提供详细的XML注释
- **类型安全**:使用强类型返回数据
#### 修改时间:
2025年1月13日
#### 修改原因:
优化TestTerminalRequestClient的设计,简化方法结构,修正命名规范,使用正确的服务名称,并提供完整的XML文档注释,提高代码的可读性和可维护性。
### CreateTerminalDeviceCommandHandler 设备类型检测优化
#### 修改文件:
`X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceCommandHandler.cs` - 优化设备类型检测逻辑
#### 修改内容:
1. **依赖注入修复**
- 将 `IBaseInstrumentClient` 替换为 `IBaseTerminalClient`
- 更新字段名从 `_instrumentClient``_terminalClient`
- 添加正确的using语句引用
2. **设备类型检测优化**
- 实现真实的设备类型检测逻辑
- 通过调用 `GetDeviceSerialNumberAsync` 获取系统类型信息
- 根据系统类型字符串智能判断设备类型
- 支持Linux和Windows系统的识别
3. **系统类型识别逻辑**
- **Linux系统识别**:linux、ubuntu、centos、debian、fedora、redhat、suse、unix
- **Windows系统识别**:windows、win、nt
- **默认处理**:未知系统类型默认使用Windows
4. **技术特性**
- **真实检测**:基于API返回的系统类型信息进行检测
- **智能识别**:支持多种Linux发行版和Windows版本
- **错误处理**:检测失败时使用默认类型
- **资源管理**:自动清理临时服务端点
5. **方法调用修复**
- 更新 `GetDeviceSerialNumberAsync` 方法调用
- 处理返回的 `MachineCodeData` 对象
- 提取 `MachineCode``SystemType` 信息
- 完整的null检查和异常处理
6. **日志记录优化**
- 记录设备类型检测过程
- 记录系统类型和检测结果
- 记录检测失败的原因
- 详细的调试信息
7. **使用示例**
```csharp
// 设备类型检测流程
var deviceType = await DetectDeviceTypeByPortsAsync(request, serviceEndpoint, cancellationToken);
// 根据API返回的系统类型自动判断是Windows还是Linux
```
#### 修改时间:
2025年1月13日
#### 修改原因:
需要实现真实的设备类型检测功能,通过API获取的系统类型信息智能判断设备是Windows还是Linux系统,提高设备管理的准确性和自动化程度。
### CreateTerminalDeviceCommandHandler API调用优化
#### 修改文件:
`X1.Application/Features/TerminalDevices/Commands/CreateTerminalDevice/CreateTerminalDeviceCommandHandler.cs` - 优化API调用逻辑
#### 修改内容:
1. **API调用优化**
- 合并序列号获取和设备类型检测为一次API调用
- 移除重复的 `DetectDeviceTypeByPortsAsync` 方法
- 创建 `DeviceInfo` 数据传输对象,包含序列号和设备类型
2. **新增DeviceInfo类**
- `SerialNumber`:设备序列号
- `DeviceType`:设备类型(Windows/Linux)
- 用于封装API返回的完整设备信息
3. **方法重构**
- `GetDeviceSerialNumberAsync``GetDeviceInfoAsync`
- 在一次API调用中同时获取序列号和系统类型
- 根据系统类型自动判断设备类型
- 返回包含完整信息的 `DeviceInfo` 对象
4. **技术特性**
- **性能优化**:减少API调用次数,从2次减少到1次
- **数据完整性**:确保序列号和系统类型信息的一致性
- **错误处理**:统一的错误处理和资源清理
- **类型安全**:使用强类型的 `DeviceInfo` 对象
5. **工作流程优化**
```
原流程:获取序列号 → 检测设备类型(重复API调用)
新流程:获取设备信息(一次API调用,包含序列号和系统类型)
```
6. **代码简化**
- 移除重复的API调用逻辑
- 简化主处理流程
- 统一的资源管理和错误处理
7. **使用示例**
```csharp
// 一次API调用获取完整设备信息
var deviceInfoResult = await GetDeviceInfoAsync(request, serviceEndpoint, cancellationToken);
if (deviceInfoResult.IsSuccess)
{
var serialNumber = deviceInfoResult.Data.SerialNumber;
var deviceType = deviceInfoResult.Data.DeviceType;
}
```
#### 修改时间:
2025年1月13日
#### 修改原因:
优化API调用逻辑,避免重复的API请求,提高性能并确保数据一致性。将序列号获取和设备类型检测合并为一次API调用,简化代码结构并提升用户体验。
Loading…
Cancel
Save