Browse Source

Device List curd

norm
root 2 months ago
parent
commit
b3e8943169
  1. 61
      src/CellularManagement.Application/Features/Devices/Commands/CreateDevice/CreateDeviceCommand.cs
  2. 72
      src/CellularManagement.Application/Features/Devices/Commands/CreateDevice/CreateDeviceCommandHandler.cs
  3. 25
      src/CellularManagement.Application/Features/Devices/Commands/DeleteDevice/DeleteDeviceCommand.cs
  4. 55
      src/CellularManagement.Application/Features/Devices/Commands/DeleteDevice/DeleteDeviceCommandHandler.cs
  5. 67
      src/CellularManagement.Application/Features/Devices/Commands/UpdateDevice/UpdateDeviceCommand.cs
  6. 83
      src/CellularManagement.Application/Features/Devices/Commands/UpdateDevice/UpdateDeviceCommandHandler.cs
  7. 26
      src/CellularManagement.Application/Features/Devices/Queries/GetDeviceById/GetDeviceByIdQuery.cs
  8. 54
      src/CellularManagement.Application/Features/Devices/Queries/GetDeviceById/GetDeviceByIdQueryHandler.cs
  9. 64
      src/CellularManagement.Application/Features/Devices/Queries/GetDevices/DeviceDto.cs
  10. 34
      src/CellularManagement.Application/Features/Devices/Queries/GetDevices/GetDevicesQuery.cs
  11. 92
      src/CellularManagement.Application/Features/Devices/Queries/GetDevices/GetDevicesQueryHandler.cs
  12. 44
      src/CellularManagement.Application/Features/Devices/Queries/GetDevices/GetDevicesResponse.cs
  13. 19
      src/CellularManagement.Domain/Entities/Device.cs
  14. 145
      src/CellularManagement.Domain/Repositories/IDeviceRepository.cs
  15. 22
      src/CellularManagement.Infrastructure/Context/AppDbContext.cs
  16. 370
      src/CellularManagement.Infrastructure/Repositories/DeviceRepository.cs
  17. 75
      src/CellularManagement.Presentation/Controllers/DevicesController.cs

61
src/CellularManagement.Application/Features/Devices/Commands/CreateDevice/CreateDeviceCommand.cs

@ -0,0 +1,61 @@
using CellularManagement.Domain.Common;
using CellularManagement.Domain.Entities;
using MediatR;
using System.ComponentModel.DataAnnotations;
namespace CellularManagement.Application.Features.Devices.Commands.CreateDevice;
/// <summary>
/// 创建设备命令
/// </summary>
public class CreateDeviceCommand : IRequest<OperationResult<Device>>
{
/// <summary>
/// 设备名称
/// </summary>
[Required(ErrorMessage = "设备名称不能为空")]
[MaxLength(100, ErrorMessage = "设备名称不能超过100个字符")]
public string DeviceName { get; set; }
/// <summary>
/// 协议版本
/// </summary>
[MaxLength(50, ErrorMessage = "协议版本不能超过50个字符")]
public string ProtocolVersion { get; set; }
/// <summary>
/// 支持的频段
/// </summary>
[MaxLength(100, ErrorMessage = "支持的频段不能超过100个字符")]
public string SupportBands { get; set; }
/// <summary>
/// 硬件版本
/// </summary>
[MaxLength(50, ErrorMessage = "硬件版本不能超过50个字符")]
public string HardwareVersion { get; set; }
/// <summary>
/// 序列号
/// </summary>
[Required(ErrorMessage = "序列号不能为空")]
[MaxLength(100, ErrorMessage = "序列号不能超过100个字符")]
public string SerialNumber { get; set; }
/// <summary>
/// 备注
/// </summary>
[MaxLength(500, ErrorMessage = "备注不能超过500个字符")]
public string Comment { get; set; }
/// <summary>
/// IP地址
/// </summary>
[MaxLength(50, ErrorMessage = "IP地址不能超过50个字符")]
public string IPAddress { get; set; }
/// <summary>
/// 是否禁用
/// </summary>
public bool IsDisabled { get; set; }
}

72
src/CellularManagement.Application/Features/Devices/Commands/CreateDevice/CreateDeviceCommandHandler.cs

@ -0,0 +1,72 @@
using MediatR;
using Microsoft.Extensions.Logging;
using CellularManagement.Domain.Common;
using CellularManagement.Domain.Entities;
using CellularManagement.Domain.Repositories;
namespace CellularManagement.Application.Features.Devices.Commands.CreateDevice;
/// <summary>
/// 创建设备命令处理器
/// </summary>
public class CreateDeviceCommandHandler : IRequestHandler<CreateDeviceCommand, OperationResult<Device>>
{
private readonly IDeviceRepository _deviceRepository;
private readonly ILogger<CreateDeviceCommandHandler> _logger;
/// <summary>
/// 初始化命令处理器
/// </summary>
public CreateDeviceCommandHandler(
IDeviceRepository deviceRepository,
ILogger<CreateDeviceCommandHandler> logger)
{
_deviceRepository = deviceRepository;
_logger = logger;
}
/// <summary>
/// 处理创建设备命令
/// </summary>
public async Task<OperationResult<Device>> Handle(CreateDeviceCommand request, CancellationToken cancellationToken)
{
try
{
_logger.LogInformation("Creating new device with serial number: {SerialNumber}", request.SerialNumber);
// 检查序列号是否已存在
if (await _deviceRepository.ExistsBySerialNumberAsync(request.SerialNumber, cancellationToken: cancellationToken))
{
_logger.LogWarning("Device with serial number {SerialNumber} already exists", request.SerialNumber);
return OperationResult<Device>.CreateFailure($"设备序列号 {request.SerialNumber} 已存在");
}
// 创建设备实体
var device = new Device
{
DeviceID = Guid.NewGuid().ToString(),
DeviceName = request.DeviceName,
ProtocolVersion = request.ProtocolVersion,
SupportBands = request.SupportBands,
HardwareVersion = request.HardwareVersion,
SerialNumber = request.SerialNumber,
Comment = request.Comment,
IPAddress = request.IPAddress,
IsDisabled = false,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
};
// 保存设备
await _deviceRepository.CreateAsync(device, cancellationToken);
_logger.LogInformation("Successfully created device with ID: {DeviceId}", device.DeviceID);
return OperationResult<Device>.CreateSuccess("设备创建成功", device);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating device with serial number: {SerialNumber}", request.SerialNumber);
return OperationResult<Device>.CreateFailure($"创建设备时发生错误: {ex.Message}");
}
}
}

25
src/CellularManagement.Application/Features/Devices/Commands/DeleteDevice/DeleteDeviceCommand.cs

@ -0,0 +1,25 @@
using CellularManagement.Domain.Common;
using MediatR;
using System.ComponentModel.DataAnnotations;
namespace CellularManagement.Application.Features.Devices.Commands.DeleteDevice;
/// <summary>
/// 删除设备命令
/// </summary>
public class DeleteDeviceCommand : IRequest<OperationResult<bool>>
{
/// <summary>
/// 设备ID
/// </summary>
[Required(ErrorMessage = "设备ID不能为空")]
public string DeviceId { get; set; }
/// <summary>
/// 初始化删除设备命令
/// </summary>
public DeleteDeviceCommand(string deviceId)
{
DeviceId = deviceId;
}
}

55
src/CellularManagement.Application/Features/Devices/Commands/DeleteDevice/DeleteDeviceCommandHandler.cs

@ -0,0 +1,55 @@
using MediatR;
using Microsoft.Extensions.Logging;
using CellularManagement.Domain.Common;
using CellularManagement.Domain.Repositories;
namespace CellularManagement.Application.Features.Devices.Commands.DeleteDevice;
/// <summary>
/// 删除设备命令处理器
/// </summary>
public class DeleteDeviceCommandHandler : IRequestHandler<DeleteDeviceCommand, OperationResult<bool>>
{
private readonly IDeviceRepository _deviceRepository;
private readonly ILogger<DeleteDeviceCommandHandler> _logger;
/// <summary>
/// 初始化删除设备命令处理器
/// </summary>
public DeleteDeviceCommandHandler(
IDeviceRepository deviceRepository,
ILogger<DeleteDeviceCommandHandler> logger)
{
_deviceRepository = deviceRepository;
_logger = logger;
}
/// <summary>
/// 处理删除设备命令
/// </summary>
public async Task<OperationResult<bool>> Handle(DeleteDeviceCommand request, CancellationToken cancellationToken)
{
try
{
_logger.LogInformation("开始处理删除设备命令,设备ID: {DeviceId}", request.DeviceId);
var result = await _deviceRepository.DeleteByIdAsync(request.DeviceId, cancellationToken);
if (result)
{
_logger.LogInformation("设备删除成功,设备ID: {DeviceId}", request.DeviceId);
return OperationResult<bool>.CreateSuccess(true);
}
else
{
_logger.LogWarning("设备删除失败,设备ID: {DeviceId}", request.DeviceId);
return OperationResult<bool>.CreateFailure("设备删除失败");
}
}
catch (Exception ex)
{
_logger.LogError(ex, "删除设备时发生错误,设备ID: {DeviceId}", request.DeviceId);
return OperationResult<bool>.CreateFailure($"删除设备时发生错误: {ex.Message}");
}
}
}

67
src/CellularManagement.Application/Features/Devices/Commands/UpdateDevice/UpdateDeviceCommand.cs

@ -0,0 +1,67 @@
using CellularManagement.Domain.Common;
using CellularManagement.Domain.Entities;
using MediatR;
using System.ComponentModel.DataAnnotations;
namespace CellularManagement.Application.Features.Devices.Commands.UpdateDevice;
/// <summary>
/// 更新设备命令
/// </summary>
public class UpdateDeviceCommand : IRequest<OperationResult<Device>>
{
/// <summary>
/// 设备ID
/// </summary>
[Required(ErrorMessage = "设备ID不能为空")]
public string DeviceID { get; set; }
/// <summary>
/// 设备名称
/// </summary>
[Required(ErrorMessage = "设备名称不能为空")]
[MaxLength(100, ErrorMessage = "设备名称不能超过100个字符")]
public string DeviceName { get; set; }
/// <summary>
/// 协议版本
/// </summary>
[MaxLength(50, ErrorMessage = "协议版本不能超过50个字符")]
public string ProtocolVersion { get; set; }
/// <summary>
/// 支持的频段
/// </summary>
[MaxLength(100, ErrorMessage = "支持的频段不能超过100个字符")]
public string SupportBands { get; set; }
/// <summary>
/// 硬件版本
/// </summary>
[MaxLength(50, ErrorMessage = "硬件版本不能超过50个字符")]
public string HardwareVersion { get; set; }
/// <summary>
/// 序列号
/// </summary>
[Required(ErrorMessage = "序列号不能为空")]
[MaxLength(100, ErrorMessage = "序列号不能超过100个字符")]
public string SerialNumber { get; set; }
/// <summary>
/// 备注
/// </summary>
[MaxLength(500, ErrorMessage = "备注不能超过500个字符")]
public string Comment { get; set; }
/// <summary>
/// IP地址
/// </summary>
[MaxLength(50, ErrorMessage = "IP地址不能超过50个字符")]
public string IPAddress { get; set; }
/// <summary>
/// 是否禁用
/// </summary>
public bool IsDisabled { get; set; }
}

83
src/CellularManagement.Application/Features/Devices/Commands/UpdateDevice/UpdateDeviceCommandHandler.cs

@ -0,0 +1,83 @@
using MediatR;
using Microsoft.Extensions.Logging;
using CellularManagement.Domain.Common;
using CellularManagement.Domain.Entities;
using CellularManagement.Domain.Repositories;
namespace CellularManagement.Application.Features.Devices.Commands.UpdateDevice;
/// <summary>
/// 更新设备命令处理器
/// </summary>
public class UpdateDeviceCommandHandler : IRequestHandler<UpdateDeviceCommand, OperationResult<Device>>
{
private readonly IDeviceRepository _deviceRepository;
private readonly ILogger<UpdateDeviceCommandHandler> _logger;
/// <summary>
/// 初始化命令处理器
/// </summary>
public UpdateDeviceCommandHandler(
IDeviceRepository deviceRepository,
ILogger<UpdateDeviceCommandHandler> logger)
{
_deviceRepository = deviceRepository;
_logger = logger;
}
/// <summary>
/// 处理更新设备命令
/// </summary>
public async Task<OperationResult<Device>> Handle(UpdateDeviceCommand request, CancellationToken cancellationToken)
{
try
{
_logger.LogInformation("Updating device with ID: {DeviceId}", request.DeviceID);
// 检查设备是否存在
var existingDevice = await _deviceRepository.GetByIdAsync(request.DeviceID, cancellationToken);
if (existingDevice == null)
{
_logger.LogWarning("Device not found with ID: {DeviceId}", request.DeviceID);
return OperationResult<Device>.CreateFailure($"未找到ID为 {request.DeviceID} 的设备");
}
// 如果序列号发生变化,检查新序列号是否已存在
if (existingDevice.SerialNumber != request.SerialNumber)
{
if (await _deviceRepository.ExistsBySerialNumberAsync(request.SerialNumber, request.DeviceID, cancellationToken))
{
_logger.LogWarning("Device with serial number {SerialNumber} already exists", request.SerialNumber);
return OperationResult<Device>.CreateFailure($"设备序列号 {request.SerialNumber} 已存在");
}
}
// 更新设备属性
existingDevice.DeviceName = request.DeviceName;
existingDevice.ProtocolVersion = request.ProtocolVersion;
existingDevice.SupportBands = request.SupportBands;
existingDevice.HardwareVersion = request.HardwareVersion;
existingDevice.SerialNumber = request.SerialNumber;
existingDevice.Comment = request.Comment;
existingDevice.IPAddress = request.IPAddress;
existingDevice.IsDisabled = request.IsDisabled;
existingDevice.UpdatedAt = DateTime.UtcNow;
// 保存更新
var result = await _deviceRepository.UpdateAsync(existingDevice, cancellationToken);
if (!result)
{
_logger.LogWarning("Failed to update device with ID: {DeviceId}", request.DeviceID);
return OperationResult<Device>.CreateFailure("更新设备失败");
}
_logger.LogInformation("Successfully updated device with ID: {DeviceId}", request.DeviceID);
return OperationResult<Device>.CreateSuccess("设备更新成功", existingDevice);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error updating device with ID: {DeviceId}", request.DeviceID);
return OperationResult<Device>.CreateFailure($"更新设备时发生错误: {ex.Message}");
}
}
}

26
src/CellularManagement.Application/Features/Devices/Queries/GetDeviceById/GetDeviceByIdQuery.cs

@ -0,0 +1,26 @@
using CellularManagement.Domain.Common;
using CellularManagement.Domain.Entities;
using MediatR;
using System.ComponentModel.DataAnnotations;
namespace CellularManagement.Application.Features.Devices.Queries.GetDeviceById;
/// <summary>
/// 根据ID获取设备查询
/// </summary>
public class GetDeviceByIdQuery : IRequest<OperationResult<Device>>
{
/// <summary>
/// 设备ID
/// </summary>
[Required(ErrorMessage = "设备ID不能为空")]
public string DeviceId { get; set; }
/// <summary>
/// 初始化查询
/// </summary>
public GetDeviceByIdQuery(string deviceId)
{
DeviceId = deviceId;
}
}

54
src/CellularManagement.Application/Features/Devices/Queries/GetDeviceById/GetDeviceByIdQueryHandler.cs

@ -0,0 +1,54 @@
using CellularManagement.Domain.Common;
using CellularManagement.Domain.Entities;
using CellularManagement.Domain.Repositories;
using MediatR;
using Microsoft.Extensions.Logging;
namespace CellularManagement.Application.Features.Devices.Queries.GetDeviceById;
/// <summary>
/// 根据ID获取设备查询处理器
/// </summary>
public class GetDeviceByIdQueryHandler : IRequestHandler<GetDeviceByIdQuery, OperationResult<Device>>
{
private readonly IDeviceRepository _deviceRepository;
private readonly ILogger<GetDeviceByIdQueryHandler> _logger;
/// <summary>
/// 初始化查询处理器
/// </summary>
public GetDeviceByIdQueryHandler(
IDeviceRepository deviceRepository,
ILogger<GetDeviceByIdQueryHandler> logger)
{
_deviceRepository = deviceRepository;
_logger = logger;
}
/// <summary>
/// 处理查询请求
/// </summary>
public async Task<OperationResult<Device>> Handle(GetDeviceByIdQuery request, CancellationToken cancellationToken)
{
try
{
_logger.LogInformation("开始查询设备,设备ID: {DeviceId}", request.DeviceId);
var device = await _deviceRepository.GetByIdAsync(request.DeviceId, cancellationToken);
if (device == null)
{
_logger.LogWarning("未找到设备,设备ID: {DeviceId}", request.DeviceId);
return OperationResult<Device>.CreateFailure($"未找到ID为 {request.DeviceId} 的设备");
}
_logger.LogInformation("成功查询到设备,设备ID: {DeviceId}", request.DeviceId);
return OperationResult<Device>.CreateSuccess(device);
}
catch (Exception ex)
{
_logger.LogError(ex, "查询设备时发生错误,设备ID: {DeviceId}", request.DeviceId);
return OperationResult<Device>.CreateFailure($"查询设备时发生错误: {ex.Message}");
}
}
}

64
src/CellularManagement.Application/Features/Devices/Queries/GetDevices/DeviceDto.cs

@ -0,0 +1,64 @@
using System;
namespace CellularManagement.Application.Features.Devices.Queries.GetDevices;
/// <summary>
/// 设备数据传输对象
/// </summary>
public class DeviceDto
{
/// <summary>
/// 设备ID
/// </summary>
public string DeviceId { get; set; }
/// <summary>
/// 设备名称
/// </summary>
public string DeviceName { get; set; }
/// <summary>
/// 协议版本
/// </summary>
public string ProtocolVersion { get; set; }
/// <summary>
/// 支持的频段
/// </summary>
public string SupportBands { get; set; }
/// <summary>
/// 硬件版本
/// </summary>
public string HardwareVersion { get; set; }
/// <summary>
/// 序列号
/// </summary>
public string SerialNumber { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Comment { get; set; }
/// <summary>
/// IP地址
/// </summary>
public string IPAddress { get; set; }
/// <summary>
/// 是否禁用
/// </summary>
public bool IsDisabled { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedAt { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime UpdatedAt { get; set; }
}

34
src/CellularManagement.Application/Features/Devices/Queries/GetDevices/GetDevicesQuery.cs

@ -0,0 +1,34 @@
using CellularManagement.Domain.Common;
using MediatR;
using System.ComponentModel.DataAnnotations;
namespace CellularManagement.Application.Features.Devices.Queries.GetDevices;
/// <summary>
/// 获取设备列表查询
/// </summary>
public class GetDevicesQuery : IRequest<OperationResult<GetDevicesResponse>>
{
/// <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 bool? IsDisabled { get; set; }
}

92
src/CellularManagement.Application/Features/Devices/Queries/GetDevices/GetDevicesQueryHandler.cs

@ -0,0 +1,92 @@
using MediatR;
using Microsoft.Extensions.Logging;
using CellularManagement.Domain.Common;
using CellularManagement.Domain.Entities;
using CellularManagement.Domain.Repositories;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace CellularManagement.Application.Features.Devices.Queries.GetDevices;
/// <summary>
/// 获取设备列表查询处理器
/// </summary>
public class GetDevicesQueryHandler : IRequestHandler<GetDevicesQuery, OperationResult<GetDevicesResponse>>
{
private readonly IDeviceRepository _deviceRepository;
private readonly ILogger<GetDevicesQueryHandler> _logger;
/// <summary>
/// 初始化查询处理器
/// </summary>
public GetDevicesQueryHandler(
IDeviceRepository deviceRepository,
ILogger<GetDevicesQueryHandler> logger)
{
_deviceRepository = deviceRepository;
_logger = logger;
}
/// <summary>
/// 处理获取设备列表查询
/// </summary>
public async Task<OperationResult<GetDevicesResponse>> Handle(GetDevicesQuery request, CancellationToken cancellationToken)
{
try
{
// 验证请求参数
var validationContext = new ValidationContext(request);
var validationResults = new List<ValidationResult>();
if (!Validator.TryValidateObject(request, validationContext, validationResults, true))
{
var errorMessages = validationResults.Select(r => r.ErrorMessage).ToList();
_logger.LogWarning("Invalid request parameters: {Errors}", string.Join(", ", errorMessages));
return OperationResult<GetDevicesResponse>.CreateFailure(errorMessages);
}
_logger.LogInformation("Getting devices with page: {PageNumber}, size: {PageSize}, search: {SearchTerm}, isDisabled: {IsDisabled}",
request.PageNumber, request.PageSize, request.SearchTerm, request.IsDisabled);
// 获取分页数据
var (totalCount, items) = await _deviceRepository.GetPagedAsync(
request.PageNumber,
request.PageSize,
request.SearchTerm,
request.IsDisabled,
cancellationToken);
// 构建响应
var response = new GetDevicesResponse
{
TotalCount = totalCount,
PageNumber = request.PageNumber,
PageSize = request.PageSize,
TotalPages = (int)Math.Ceiling(totalCount / (double)request.PageSize),
HasPreviousPage = request.PageNumber > 1,
HasNextPage = request.PageNumber < (int)Math.Ceiling(totalCount / (double)request.PageSize),
Items = items.Select(d => new DeviceDto
{
DeviceId = d.DeviceID,
DeviceName = d.DeviceName,
ProtocolVersion = d.ProtocolVersion,
SupportBands = d.SupportBands,
HardwareVersion = d.HardwareVersion,
SerialNumber = d.SerialNumber,
Comment = d.Comment,
IPAddress = d.IPAddress,
IsDisabled = d.IsDisabled,
CreatedAt = d.CreatedAt,
UpdatedAt = d.UpdatedAt
}).ToList()
};
_logger.LogInformation("Successfully retrieved {Count} devices", items.Count());
return OperationResult<GetDevicesResponse>.CreateSuccess(response);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting devices");
return OperationResult<GetDevicesResponse>.CreateFailure($"获取设备列表时发生错误: {ex.Message}");
}
}
}

44
src/CellularManagement.Application/Features/Devices/Queries/GetDevices/GetDevicesResponse.cs

@ -0,0 +1,44 @@
using System.Collections.Generic;
namespace CellularManagement.Application.Features.Devices.Queries.GetDevices;
/// <summary>
/// 设备列表响应
/// </summary>
public class GetDevicesResponse
{
/// <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 bool HasPreviousPage { get; set; }
/// <summary>
/// 是否有下一页
/// </summary>
public bool HasNextPage { get; set; }
/// <summary>
/// 设备列表
/// </summary>
public List<DeviceDto> Items { get; set; } = new();
}

19
src/CellularManagement.Domain/Entities/Device.cs

@ -0,0 +1,19 @@
using System;
namespace CellularManagement.Domain.Entities
{
public class Device
{
public string DeviceID { get; set; }
public string DeviceName { get; set; }
public string ProtocolVersion { get; set; }
public string SupportBands { get; set; }
public string HardwareVersion { get; set; }
public string SerialNumber { get; set; }
public string Comment { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public bool IsDisabled { get; set; }
public string IPAddress { get; set; }
}
}

145
src/CellularManagement.Domain/Repositories/IDeviceRepository.cs

@ -0,0 +1,145 @@
using CellularManagement.Domain.Entities;
namespace CellularManagement.Domain.Repositories;
/// <summary>
/// 设备命令仓储接口
/// 负责设备实体的写入操作
/// </summary>
public interface IDeviceCommandRepository : ICommandRepository<Device>
{
/// <summary>
/// 创建设备
/// </summary>
/// <param name="device">设备实体</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>创建后的设备实体</returns>
Task<Device> CreateAsync(Device device, CancellationToken cancellationToken = default);
/// <summary>
/// 批量创建设备
/// </summary>
/// <param name="devices">设备实体集合</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>创建后的设备实体集合</returns>
Task<IEnumerable<Device>> CreateRangeAsync(IEnumerable<Device> devices, CancellationToken cancellationToken = default);
/// <summary>
/// 更新设备
/// </summary>
/// <param name="device">设备实体</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>是否更新成功</returns>
Task<bool> UpdateAsync(Device device, CancellationToken cancellationToken = default);
/// <summary>
/// 批量更新设备
/// </summary>
/// <param name="devices">设备实体集合</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>是否更新成功</returns>
Task<bool> UpdateRangeAsync(IEnumerable<Device> devices, CancellationToken cancellationToken = default);
/// <summary>
/// 删除设备
/// </summary>
/// <param name="deviceId">设备ID</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>是否删除成功</returns>
Task<bool> DeleteAsync(string deviceId, CancellationToken cancellationToken = default);
/// <summary>
/// 批量删除设备
/// </summary>
/// <param name="deviceIds">设备ID集合</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>删除的设备数量</returns>
Task<int> DeleteRangeAsync(IEnumerable<string> deviceIds, CancellationToken cancellationToken = default);
/// <summary>
/// 启用设备
/// </summary>
/// <param name="deviceId">设备ID</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>是否启用成功</returns>
Task<bool> EnableAsync(string deviceId, CancellationToken cancellationToken = default);
/// <summary>
/// 禁用设备
/// </summary>
/// <param name="deviceId">设备ID</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>是否禁用成功</returns>
Task<bool> DisableAsync(string deviceId, CancellationToken cancellationToken = default);
/// <summary>
/// 批量启用设备
/// </summary>
/// <param name="deviceIds">设备ID集合</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>启用的设备数量</returns>
Task<int> EnableRangeAsync(IEnumerable<string> deviceIds, CancellationToken cancellationToken = default);
/// <summary>
/// 批量禁用设备
/// </summary>
/// <param name="deviceIds">设备ID集合</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>禁用的设备数量</returns>
Task<int> DisableRangeAsync(IEnumerable<string> deviceIds, CancellationToken cancellationToken = default);
}
/// <summary>
/// 设备查询仓储接口
/// 负责设备实体的读取操作
/// </summary>
public interface IDeviceQueryRepository
{
/// <summary>
/// 根据序列号获取设备
/// </summary>
Task<Device?> GetBySerialNumberAsync(string serialNumber, CancellationToken cancellationToken = default);
/// <summary>
/// 检查序列号是否存在
/// </summary>
Task<bool> ExistsBySerialNumberAsync(string serialNumber, string? excludeDeviceId = null, CancellationToken cancellationToken = default);
/// <summary>
/// 分页查询设备列表
/// </summary>
Task<(int TotalCount, IEnumerable<Device> Items)> GetPagedAsync(
int pageNumber,
int pageSize,
string? searchTerm = null,
bool? isDisabled = null,
CancellationToken cancellationToken = default);
/// <summary>
/// 获取所有启用的设备
/// </summary>
Task<IEnumerable<Device>> GetEnabledDevicesAsync(CancellationToken cancellationToken = default);
/// <summary>
/// 获取所有禁用的设备
/// </summary>
Task<IEnumerable<Device>> GetDisabledDevicesAsync(CancellationToken cancellationToken = default);
/// <summary>
/// 根据IP地址获取设备
/// </summary>
Task<Device?> GetByIPAddressAsync(string ipAddress, CancellationToken cancellationToken = default);
/// <summary>
/// 根据IP地址获取设备
/// </summary>
Task<Device?> GetByIdAsync(string id, CancellationToken cancellationToken = default);
}
/// <summary>
/// 设备仓储接口
/// 组合命令和查询仓储接口
/// </summary>
public interface IDeviceRepository : IDeviceCommandRepository, IDeviceQueryRepository
{
}

22
src/CellularManagement.Infrastructure/Context/AppDbContext.cs

@ -34,6 +34,11 @@ public class AppDbContext : IdentityDbContext<AppUser, AppRole, string>
/// </summary> /// </summary>
public DbSet<LoginLog> LoginLogs { get; set; } public DbSet<LoginLog> LoginLogs { get; set; }
/// <summary>
/// 设备集合
/// </summary>
public DbSet<Device> Devices { get; set; }
/// <summary> /// <summary>
/// 初始化数据库上下文 /// 初始化数据库上下文
/// </summary> /// </summary>
@ -125,5 +130,22 @@ public class AppDbContext : IdentityDbContext<AppUser, AppRole, string>
.HasForeignKey(l => l.UserId) .HasForeignKey(l => l.UserId)
.OnDelete(DeleteBehavior.Restrict); .OnDelete(DeleteBehavior.Restrict);
}); });
// 配置 Device 实体
modelBuilder.Entity<Device>(entity =>
{
entity.ToTable("Devices");
entity.HasKey(d => d.DeviceID);
entity.Property(d => d.DeviceName).IsRequired().HasMaxLength(100);
entity.Property(d => d.ProtocolVersion).HasMaxLength(50);
entity.Property(d => d.SupportBands).HasMaxLength(100);
entity.Property(d => d.HardwareVersion).HasMaxLength(50);
entity.Property(d => d.SerialNumber).IsRequired().HasMaxLength(100);
entity.Property(d => d.Comment).HasMaxLength(500);
entity.Property(d => d.IPAddress).HasMaxLength(50);
entity.Property(d => d.CreatedAt).IsRequired();
entity.Property(d => d.UpdatedAt).IsRequired();
entity.Property(d => d.IsDisabled).IsRequired();
});
} }
} }

370
src/CellularManagement.Infrastructure/Repositories/DeviceRepository.cs

@ -0,0 +1,370 @@
using CellularManagement.Domain.Entities;
using CellularManagement.Domain.Repositories;
using CellularManagement.Infrastructure.Context;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace CellularManagement.Infrastructure.Repositories
{
/// <summary>
/// 设备仓储实现类
/// </summary>
public class DeviceRepository : CommandRepository<Device>, IDeviceRepository
{
private readonly AppDbContext _context;
private readonly ILogger<DeviceRepository> _logger;
/// <summary>
/// 初始化仓储
/// </summary>
public DeviceRepository(
AppDbContext context,
IUnitOfWork unitOfWork,
ILogger<DeviceRepository> logger)
: base(context, unitOfWork, logger)
{
_context = context;
_logger = logger;
}
#region IDeviceQueryRepository 实现
/// <summary>
/// 根据ID获取设备
/// </summary>
public async Task<Device?> GetByIdAsync(string id, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Getting device by ID: {DeviceId}", id);
return await _context.Devices
.AsNoTracking() // 使用 AsNoTracking 提高查询性能
.FirstOrDefaultAsync(d => d.DeviceID == id, cancellationToken);
}
/// <summary>
/// 根据序列号获取设备
/// </summary>
public async Task<Device?> GetBySerialNumberAsync(string serialNumber, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Getting device by serial number: {SerialNumber}", serialNumber);
return await _context.Devices
.AsNoTracking()
.FirstOrDefaultAsync(d => d.SerialNumber == serialNumber, cancellationToken);
}
/// <summary>
/// 检查序列号是否存在
/// </summary>
public async Task<bool> ExistsBySerialNumberAsync(string serialNumber, string? excludeDeviceId = null, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Checking if device exists with serial number: {SerialNumber}", serialNumber);
var query = _context.Devices.AsNoTracking().Where(d => d.SerialNumber == serialNumber);
if (!string.IsNullOrEmpty(excludeDeviceId))
{
query = query.Where(d => d.DeviceID != excludeDeviceId);
}
return await query.AnyAsync(cancellationToken);
}
/// <summary>
/// 分页查询设备列表
/// </summary>
public async Task<(int TotalCount, IEnumerable<Device> Items)> GetPagedAsync(
int pageNumber,
int pageSize,
string? searchTerm = null,
bool? isDisabled = null,
CancellationToken cancellationToken = default)
{
_logger.LogInformation("Getting paged devices. Page: {PageNumber}, Size: {PageSize}", pageNumber, pageSize);
var query = _context.Devices.AsNoTracking().AsQueryable();
if (!string.IsNullOrEmpty(searchTerm))
{
query = query.Where(d =>
d.DeviceName.Contains(searchTerm) ||
d.SerialNumber.Contains(searchTerm) ||
d.IPAddress.Contains(searchTerm));
}
if (isDisabled.HasValue)
{
query = query.Where(d => d.IsDisabled == isDisabled.Value);
}
var totalCount = await query.CountAsync(cancellationToken);
var items = await query
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToListAsync(cancellationToken);
return (totalCount, items);
}
/// <summary>
/// 获取所有启用的设备
/// </summary>
public async Task<IEnumerable<Device>> GetEnabledDevicesAsync(CancellationToken cancellationToken = default)
{
_logger.LogInformation("Getting all enabled devices");
return await _context.Devices
.AsNoTracking()
.Where(d => !d.IsDisabled)
.ToListAsync(cancellationToken);
}
/// <summary>
/// 获取所有禁用的设备
/// </summary>
public async Task<IEnumerable<Device>> GetDisabledDevicesAsync(CancellationToken cancellationToken = default)
{
_logger.LogInformation("Getting all disabled devices");
return await _context.Devices
.AsNoTracking()
.Where(d => d.IsDisabled)
.ToListAsync(cancellationToken);
}
/// <summary>
/// 根据IP地址获取设备
/// </summary>
public async Task<Device?> GetByIPAddressAsync(string ipAddress, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Getting device by IP address: {IPAddress}", ipAddress);
return await _context.Devices
.AsNoTracking()
.FirstOrDefaultAsync(d => d.IPAddress == ipAddress, cancellationToken);
}
#endregion
#region IDeviceCommandRepository 实现
/// <summary>
/// 创建设备
/// </summary>
public async Task<Device> CreateAsync(Device device, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Creating new device with serial number: {SerialNumber}", device.SerialNumber);
// 确保实体有ID和时间戳
if (string.IsNullOrEmpty(device.DeviceID))
{
device.DeviceID = Guid.NewGuid().ToString();
}
device.CreatedAt = DateTime.UtcNow;
device.UpdatedAt = DateTime.UtcNow;
return await AddAsync(device, cancellationToken);
}
/// <summary>
/// 批量创建设备
/// </summary>
public async Task<IEnumerable<Device>> CreateRangeAsync(IEnumerable<Device> devices, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Creating {Count} devices in batch", devices.Count());
// 确保所有实体都有ID和时间戳
foreach (var device in devices)
{
if (string.IsNullOrEmpty(device.DeviceID))
{
device.DeviceID = Guid.NewGuid().ToString();
}
device.CreatedAt = DateTime.UtcNow;
device.UpdatedAt = DateTime.UtcNow;
}
return await AddRangeAsync(devices, cancellationToken);
}
/// <summary>
/// 更新设备
/// </summary>
public async Task<bool> UpdateAsync(Device device, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Updating device with ID: {DeviceId}", device.DeviceID);
var existingDevice = await _context.Devices.FindAsync(new object[] { device.DeviceID }, cancellationToken);
if (existingDevice == null)
{
_logger.LogWarning("Device not found with ID: {DeviceId}", device.DeviceID);
return false;
}
// 更新实体属性
existingDevice.DeviceName = device.DeviceName;
existingDevice.ProtocolVersion = device.ProtocolVersion;
existingDevice.SupportBands = device.SupportBands;
existingDevice.HardwareVersion = device.HardwareVersion;
existingDevice.SerialNumber = device.SerialNumber;
existingDevice.Comment = device.Comment;
existingDevice.IsDisabled = device.IsDisabled;
existingDevice.IPAddress = device.IPAddress;
existingDevice.UpdatedAt = DateTime.UtcNow;
Update(existingDevice);
await _context.SaveChangesAsync(cancellationToken);
return true;
}
/// <summary>
/// 批量更新设备
/// </summary>
public async Task<bool> UpdateRangeAsync(IEnumerable<Device> devices, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Updating {Count} devices in batch", devices.Count());
var deviceIds = devices.Select(d => d.DeviceID).ToList();
var existingDevices = await _context.Devices
.Where(d => deviceIds.Contains(d.DeviceID))
.ToListAsync(cancellationToken);
if (!existingDevices.Any())
{
_logger.LogWarning("No devices found for batch update");
return false;
}
// 更新每个实体的属性
foreach (var existingDevice in existingDevices)
{
var updateInfo = devices.First(d => d.DeviceID == existingDevice.DeviceID);
existingDevice.DeviceName = updateInfo.DeviceName;
existingDevice.ProtocolVersion = updateInfo.ProtocolVersion;
existingDevice.SupportBands = updateInfo.SupportBands;
existingDevice.HardwareVersion = updateInfo.HardwareVersion;
existingDevice.SerialNumber = updateInfo.SerialNumber;
existingDevice.Comment = updateInfo.Comment;
existingDevice.IsDisabled = updateInfo.IsDisabled;
existingDevice.IPAddress = updateInfo.IPAddress;
existingDevice.UpdatedAt = DateTime.UtcNow;
}
UpdateRange(existingDevices);
await _context.SaveChangesAsync(cancellationToken);
return true;
}
/// <summary>
/// 删除设备
/// </summary>
public async Task<bool> DeleteAsync(string deviceId, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Deleting device with ID: {DeviceId}", deviceId);
return await DeleteByIdAsync(deviceId, cancellationToken);
}
/// <summary>
/// 批量删除设备
/// </summary>
public async Task<int> DeleteRangeAsync(IEnumerable<string> deviceIds, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Deleting {Count} devices in batch", deviceIds.Count());
return await DeleteWhereAsync(d => deviceIds.Contains(d.DeviceID), cancellationToken);
}
/// <summary>
/// 启用设备
/// </summary>
public async Task<bool> EnableAsync(string deviceId, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Enabling device with ID: {DeviceId}", deviceId);
var device = await _context.Devices.FindAsync(new object[] { deviceId }, cancellationToken);
if (device == null)
{
_logger.LogWarning("Device not found with ID: {DeviceId}", deviceId);
return false;
}
device.IsDisabled = false;
device.UpdatedAt = DateTime.UtcNow;
Update(device);
await _context.SaveChangesAsync(cancellationToken);
return true;
}
/// <summary>
/// 禁用设备
/// </summary>
public async Task<bool> DisableAsync(string deviceId, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Disabling device with ID: {DeviceId}", deviceId);
var device = await _context.Devices.FindAsync(new object[] { deviceId }, cancellationToken);
if (device == null)
{
_logger.LogWarning("Device not found with ID: {DeviceId}", deviceId);
return false;
}
device.IsDisabled = true;
device.UpdatedAt = DateTime.UtcNow;
Update(device);
await _context.SaveChangesAsync(cancellationToken);
return true;
}
/// <summary>
/// 批量启用设备
/// </summary>
public async Task<int> EnableRangeAsync(IEnumerable<string> deviceIds, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Enabling {Count} devices in batch", deviceIds.Count());
var devices = await _context.Devices
.Where(d => deviceIds.Contains(d.DeviceID))
.ToListAsync(cancellationToken);
if (!devices.Any())
{
_logger.LogWarning("No devices found for batch enable");
return 0;
}
foreach (var device in devices)
{
device.IsDisabled = false;
device.UpdatedAt = DateTime.UtcNow;
}
UpdateRange(devices);
await _context.SaveChangesAsync(cancellationToken);
return devices.Count;
}
/// <summary>
/// 批量禁用设备
/// </summary>
public async Task<int> DisableRangeAsync(IEnumerable<string> deviceIds, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Disabling {Count} devices in batch", deviceIds.Count());
var devices = await _context.Devices
.Where(d => deviceIds.Contains(d.DeviceID))
.ToListAsync(cancellationToken);
if (!devices.Any())
{
_logger.LogWarning("No devices found for batch disable");
return 0;
}
foreach (var device in devices)
{
device.IsDisabled = true;
device.UpdatedAt = DateTime.UtcNow;
}
UpdateRange(devices);
await _context.SaveChangesAsync(cancellationToken);
return devices.Count;
}
#endregion
}
}

75
src/CellularManagement.Presentation/Controllers/DevicesController.cs

@ -0,0 +1,75 @@
using CellularManagement.Application.Features.Devices.Commands;
using CellularManagement.Application.Features.Devices.Commands.CreateDevice;
using CellularManagement.Application.Features.Devices.Commands.DeleteDevice;
using CellularManagement.Application.Features.Devices.Commands.UpdateDevice;
using CellularManagement.Application.Features.Devices.Queries.GetDeviceById;
using CellularManagement.Application.Features.Devices.Queries.GetDevices;
using CellularManagement.Domain.Common;
using CellularManagement.Domain.Entities;
using CellularManagement.Presentation.Abstractions;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace CellularManagement.Presentation.Controllers
{
[Route("api/devices")]
[ApiController]
[Authorize]
public class DevicesController : ApiController
{
public DevicesController(IMediator mediator) : base(mediator)
{
}
[HttpGet]
public async Task<ActionResult<OperationResult<(int TotalCount, IEnumerable<Device> Items)>>> GetAll([FromQuery] GetDevicesQuery query)
{
var result = await mediator.Send(query);
return Ok(result);
}
[HttpGet("{id}")]
public async Task<ActionResult<OperationResult<Device>>> GetById(string id)
{
var device = await mediator.Send(new GetDeviceByIdQuery(id));
if (device == null)
return NotFound(OperationResult<Device>.CreateFailure($"Device with ID {id} not found"));
return Ok(device);
}
[HttpPost]
public async Task<ActionResult<OperationResult<string>>> Create([FromBody] CreateDeviceCommand command)
{
var deviceId = await mediator.Send(command);
return CreatedAtAction(
nameof(GetById),
new { id = deviceId },
deviceId);
}
[HttpPut("{id}")]
public async Task<ActionResult<OperationResult<bool>>> Update(string id, [FromBody] UpdateDeviceCommand command)
{
if (id != command.DeviceID)
return BadRequest(OperationResult<bool>.CreateFailure("ID mismatch"));
var result = await mediator.Send(command);
if (!result.IsSuccess)
return NotFound(OperationResult<bool>.CreateFailure($"Device with ID {id} not found"));
return Ok(OperationResult<bool>.CreateSuccess("Device updated successfully", true));
}
[HttpDelete("{id}")]
public async Task<ActionResult<OperationResult<bool>>> Delete(string id)
{
var result = await mediator.Send(new DeleteDeviceCommand(id));
if (!result.IsSuccess)
return NotFound(OperationResult<bool>.CreateFailure($"Device with ID {id} not found"));
return Ok(OperationResult<bool>.CreateSuccess("Device deleted successfully", true));
}
}
}
Loading…
Cancel
Save