From b3e89431693da53f3db2952f9afd5ebd30509bb4 Mon Sep 17 00:00:00 2001
From: root <295172551@qq.com>
Date: Wed, 4 Jun 2025 23:41:50 +0800
Subject: [PATCH] Device List curd
---
.../CreateDevice/CreateDeviceCommand.cs | 61 +++
.../CreateDeviceCommandHandler.cs | 72 ++++
.../DeleteDevice/DeleteDeviceCommand.cs | 25 ++
.../DeleteDeviceCommandHandler.cs | 55 +++
.../UpdateDevice/UpdateDeviceCommand.cs | 67 ++++
.../UpdateDeviceCommandHandler.cs | 83 ++++
.../GetDeviceById/GetDeviceByIdQuery.cs | 26 ++
.../GetDeviceByIdQueryHandler.cs | 54 +++
.../Devices/Queries/GetDevices/DeviceDto.cs | 64 +++
.../Queries/GetDevices/GetDevicesQuery.cs | 34 ++
.../GetDevices/GetDevicesQueryHandler.cs | 92 +++++
.../Queries/GetDevices/GetDevicesResponse.cs | 44 +++
.../Entities/Device.cs | 19 +
.../Repositories/IDeviceRepository.cs | 145 +++++++
.../Context/AppDbContext.cs | 22 ++
.../Repositories/DeviceRepository.cs | 370 ++++++++++++++++++
.../Controllers/DevicesController.cs | 75 ++++
17 files changed, 1308 insertions(+)
create mode 100644 src/CellularManagement.Application/Features/Devices/Commands/CreateDevice/CreateDeviceCommand.cs
create mode 100644 src/CellularManagement.Application/Features/Devices/Commands/CreateDevice/CreateDeviceCommandHandler.cs
create mode 100644 src/CellularManagement.Application/Features/Devices/Commands/DeleteDevice/DeleteDeviceCommand.cs
create mode 100644 src/CellularManagement.Application/Features/Devices/Commands/DeleteDevice/DeleteDeviceCommandHandler.cs
create mode 100644 src/CellularManagement.Application/Features/Devices/Commands/UpdateDevice/UpdateDeviceCommand.cs
create mode 100644 src/CellularManagement.Application/Features/Devices/Commands/UpdateDevice/UpdateDeviceCommandHandler.cs
create mode 100644 src/CellularManagement.Application/Features/Devices/Queries/GetDeviceById/GetDeviceByIdQuery.cs
create mode 100644 src/CellularManagement.Application/Features/Devices/Queries/GetDeviceById/GetDeviceByIdQueryHandler.cs
create mode 100644 src/CellularManagement.Application/Features/Devices/Queries/GetDevices/DeviceDto.cs
create mode 100644 src/CellularManagement.Application/Features/Devices/Queries/GetDevices/GetDevicesQuery.cs
create mode 100644 src/CellularManagement.Application/Features/Devices/Queries/GetDevices/GetDevicesQueryHandler.cs
create mode 100644 src/CellularManagement.Application/Features/Devices/Queries/GetDevices/GetDevicesResponse.cs
create mode 100644 src/CellularManagement.Domain/Entities/Device.cs
create mode 100644 src/CellularManagement.Domain/Repositories/IDeviceRepository.cs
create mode 100644 src/CellularManagement.Infrastructure/Repositories/DeviceRepository.cs
create mode 100644 src/CellularManagement.Presentation/Controllers/DevicesController.cs
diff --git a/src/CellularManagement.Application/Features/Devices/Commands/CreateDevice/CreateDeviceCommand.cs b/src/CellularManagement.Application/Features/Devices/Commands/CreateDevice/CreateDeviceCommand.cs
new file mode 100644
index 0000000..c5fe6b5
--- /dev/null
+++ b/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;
+
+///
+/// 创建设备命令
+///
+public class CreateDeviceCommand : IRequest>
+{
+ ///
+ /// 设备名称
+ ///
+ [Required(ErrorMessage = "设备名称不能为空")]
+ [MaxLength(100, ErrorMessage = "设备名称不能超过100个字符")]
+ public string DeviceName { get; set; }
+
+ ///
+ /// 协议版本
+ ///
+ [MaxLength(50, ErrorMessage = "协议版本不能超过50个字符")]
+ public string ProtocolVersion { get; set; }
+
+ ///
+ /// 支持的频段
+ ///
+ [MaxLength(100, ErrorMessage = "支持的频段不能超过100个字符")]
+ public string SupportBands { get; set; }
+
+ ///
+ /// 硬件版本
+ ///
+ [MaxLength(50, ErrorMessage = "硬件版本不能超过50个字符")]
+ public string HardwareVersion { get; set; }
+
+ ///
+ /// 序列号
+ ///
+ [Required(ErrorMessage = "序列号不能为空")]
+ [MaxLength(100, ErrorMessage = "序列号不能超过100个字符")]
+ public string SerialNumber { get; set; }
+
+ ///
+ /// 备注
+ ///
+ [MaxLength(500, ErrorMessage = "备注不能超过500个字符")]
+ public string Comment { get; set; }
+
+ ///
+ /// IP地址
+ ///
+ [MaxLength(50, ErrorMessage = "IP地址不能超过50个字符")]
+ public string IPAddress { get; set; }
+
+ ///
+ /// 是否禁用
+ ///
+ public bool IsDisabled { get; set; }
+}
\ No newline at end of file
diff --git a/src/CellularManagement.Application/Features/Devices/Commands/CreateDevice/CreateDeviceCommandHandler.cs b/src/CellularManagement.Application/Features/Devices/Commands/CreateDevice/CreateDeviceCommandHandler.cs
new file mode 100644
index 0000000..c1579df
--- /dev/null
+++ b/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;
+
+///
+/// 创建设备命令处理器
+///
+public class CreateDeviceCommandHandler : IRequestHandler>
+{
+ private readonly IDeviceRepository _deviceRepository;
+ private readonly ILogger _logger;
+
+ ///
+ /// 初始化命令处理器
+ ///
+ public CreateDeviceCommandHandler(
+ IDeviceRepository deviceRepository,
+ ILogger logger)
+ {
+ _deviceRepository = deviceRepository;
+ _logger = logger;
+ }
+
+ ///
+ /// 处理创建设备命令
+ ///
+ public async Task> 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.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.CreateSuccess("设备创建成功", device);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error creating device with serial number: {SerialNumber}", request.SerialNumber);
+ return OperationResult.CreateFailure($"创建设备时发生错误: {ex.Message}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/CellularManagement.Application/Features/Devices/Commands/DeleteDevice/DeleteDeviceCommand.cs b/src/CellularManagement.Application/Features/Devices/Commands/DeleteDevice/DeleteDeviceCommand.cs
new file mode 100644
index 0000000..b799ac4
--- /dev/null
+++ b/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;
+
+///
+/// 删除设备命令
+///
+public class DeleteDeviceCommand : IRequest>
+{
+ ///
+ /// 设备ID
+ ///
+ [Required(ErrorMessage = "设备ID不能为空")]
+ public string DeviceId { get; set; }
+
+ ///
+ /// 初始化删除设备命令
+ ///
+ public DeleteDeviceCommand(string deviceId)
+ {
+ DeviceId = deviceId;
+ }
+}
\ No newline at end of file
diff --git a/src/CellularManagement.Application/Features/Devices/Commands/DeleteDevice/DeleteDeviceCommandHandler.cs b/src/CellularManagement.Application/Features/Devices/Commands/DeleteDevice/DeleteDeviceCommandHandler.cs
new file mode 100644
index 0000000..cd99de3
--- /dev/null
+++ b/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;
+
+///
+/// 删除设备命令处理器
+///
+public class DeleteDeviceCommandHandler : IRequestHandler>
+{
+ private readonly IDeviceRepository _deviceRepository;
+ private readonly ILogger _logger;
+
+ ///
+ /// 初始化删除设备命令处理器
+ ///
+ public DeleteDeviceCommandHandler(
+ IDeviceRepository deviceRepository,
+ ILogger logger)
+ {
+ _deviceRepository = deviceRepository;
+ _logger = logger;
+ }
+
+ ///
+ /// 处理删除设备命令
+ ///
+ public async Task> 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.CreateSuccess(true);
+ }
+ else
+ {
+ _logger.LogWarning("设备删除失败,设备ID: {DeviceId}", request.DeviceId);
+ return OperationResult.CreateFailure("设备删除失败");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "删除设备时发生错误,设备ID: {DeviceId}", request.DeviceId);
+ return OperationResult.CreateFailure($"删除设备时发生错误: {ex.Message}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/CellularManagement.Application/Features/Devices/Commands/UpdateDevice/UpdateDeviceCommand.cs b/src/CellularManagement.Application/Features/Devices/Commands/UpdateDevice/UpdateDeviceCommand.cs
new file mode 100644
index 0000000..a3b37ba
--- /dev/null
+++ b/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;
+
+///
+/// 更新设备命令
+///
+public class UpdateDeviceCommand : IRequest>
+{
+ ///
+ /// 设备ID
+ ///
+ [Required(ErrorMessage = "设备ID不能为空")]
+ public string DeviceID { get; set; }
+
+ ///
+ /// 设备名称
+ ///
+ [Required(ErrorMessage = "设备名称不能为空")]
+ [MaxLength(100, ErrorMessage = "设备名称不能超过100个字符")]
+ public string DeviceName { get; set; }
+
+ ///
+ /// 协议版本
+ ///
+ [MaxLength(50, ErrorMessage = "协议版本不能超过50个字符")]
+ public string ProtocolVersion { get; set; }
+
+ ///
+ /// 支持的频段
+ ///
+ [MaxLength(100, ErrorMessage = "支持的频段不能超过100个字符")]
+ public string SupportBands { get; set; }
+
+ ///
+ /// 硬件版本
+ ///
+ [MaxLength(50, ErrorMessage = "硬件版本不能超过50个字符")]
+ public string HardwareVersion { get; set; }
+
+ ///
+ /// 序列号
+ ///
+ [Required(ErrorMessage = "序列号不能为空")]
+ [MaxLength(100, ErrorMessage = "序列号不能超过100个字符")]
+ public string SerialNumber { get; set; }
+
+ ///
+ /// 备注
+ ///
+ [MaxLength(500, ErrorMessage = "备注不能超过500个字符")]
+ public string Comment { get; set; }
+
+ ///
+ /// IP地址
+ ///
+ [MaxLength(50, ErrorMessage = "IP地址不能超过50个字符")]
+ public string IPAddress { get; set; }
+
+ ///
+ /// 是否禁用
+ ///
+ public bool IsDisabled { get; set; }
+}
\ No newline at end of file
diff --git a/src/CellularManagement.Application/Features/Devices/Commands/UpdateDevice/UpdateDeviceCommandHandler.cs b/src/CellularManagement.Application/Features/Devices/Commands/UpdateDevice/UpdateDeviceCommandHandler.cs
new file mode 100644
index 0000000..18853f6
--- /dev/null
+++ b/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;
+
+///
+/// 更新设备命令处理器
+///
+public class UpdateDeviceCommandHandler : IRequestHandler>
+{
+ private readonly IDeviceRepository _deviceRepository;
+ private readonly ILogger _logger;
+
+ ///
+ /// 初始化命令处理器
+ ///
+ public UpdateDeviceCommandHandler(
+ IDeviceRepository deviceRepository,
+ ILogger logger)
+ {
+ _deviceRepository = deviceRepository;
+ _logger = logger;
+ }
+
+ ///
+ /// 处理更新设备命令
+ ///
+ public async Task> 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.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.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.CreateFailure("更新设备失败");
+ }
+
+ _logger.LogInformation("Successfully updated device with ID: {DeviceId}", request.DeviceID);
+ return OperationResult.CreateSuccess("设备更新成功", existingDevice);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error updating device with ID: {DeviceId}", request.DeviceID);
+ return OperationResult.CreateFailure($"更新设备时发生错误: {ex.Message}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/CellularManagement.Application/Features/Devices/Queries/GetDeviceById/GetDeviceByIdQuery.cs b/src/CellularManagement.Application/Features/Devices/Queries/GetDeviceById/GetDeviceByIdQuery.cs
new file mode 100644
index 0000000..22ebf53
--- /dev/null
+++ b/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;
+
+///
+/// 根据ID获取设备查询
+///
+public class GetDeviceByIdQuery : IRequest>
+{
+ ///
+ /// 设备ID
+ ///
+ [Required(ErrorMessage = "设备ID不能为空")]
+ public string DeviceId { get; set; }
+
+ ///
+ /// 初始化查询
+ ///
+ public GetDeviceByIdQuery(string deviceId)
+ {
+ DeviceId = deviceId;
+ }
+}
\ No newline at end of file
diff --git a/src/CellularManagement.Application/Features/Devices/Queries/GetDeviceById/GetDeviceByIdQueryHandler.cs b/src/CellularManagement.Application/Features/Devices/Queries/GetDeviceById/GetDeviceByIdQueryHandler.cs
new file mode 100644
index 0000000..51afe53
--- /dev/null
+++ b/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;
+
+///
+/// 根据ID获取设备查询处理器
+///
+public class GetDeviceByIdQueryHandler : IRequestHandler>
+{
+ private readonly IDeviceRepository _deviceRepository;
+ private readonly ILogger _logger;
+
+ ///
+ /// 初始化查询处理器
+ ///
+ public GetDeviceByIdQueryHandler(
+ IDeviceRepository deviceRepository,
+ ILogger logger)
+ {
+ _deviceRepository = deviceRepository;
+ _logger = logger;
+ }
+
+ ///
+ /// 处理查询请求
+ ///
+ public async Task> 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.CreateFailure($"未找到ID为 {request.DeviceId} 的设备");
+ }
+
+ _logger.LogInformation("成功查询到设备,设备ID: {DeviceId}", request.DeviceId);
+ return OperationResult.CreateSuccess(device);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "查询设备时发生错误,设备ID: {DeviceId}", request.DeviceId);
+ return OperationResult.CreateFailure($"查询设备时发生错误: {ex.Message}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/CellularManagement.Application/Features/Devices/Queries/GetDevices/DeviceDto.cs b/src/CellularManagement.Application/Features/Devices/Queries/GetDevices/DeviceDto.cs
new file mode 100644
index 0000000..0369a51
--- /dev/null
+++ b/src/CellularManagement.Application/Features/Devices/Queries/GetDevices/DeviceDto.cs
@@ -0,0 +1,64 @@
+using System;
+
+namespace CellularManagement.Application.Features.Devices.Queries.GetDevices;
+
+///
+/// 设备数据传输对象
+///
+public class DeviceDto
+{
+ ///
+ /// 设备ID
+ ///
+ 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; }
+
+ ///
+ /// IP地址
+ ///
+ public string IPAddress { get; set; }
+
+ ///
+ /// 是否禁用
+ ///
+ public bool IsDisabled { get; set; }
+
+ ///
+ /// 创建时间
+ ///
+ public DateTime CreatedAt { get; set; }
+
+ ///
+ /// 更新时间
+ ///
+ public DateTime UpdatedAt { get; set; }
+}
\ No newline at end of file
diff --git a/src/CellularManagement.Application/Features/Devices/Queries/GetDevices/GetDevicesQuery.cs b/src/CellularManagement.Application/Features/Devices/Queries/GetDevices/GetDevicesQuery.cs
new file mode 100644
index 0000000..5ce1c5a
--- /dev/null
+++ b/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;
+
+///
+/// 获取设备列表查询
+///
+public class GetDevicesQuery : IRequest>
+{
+ ///
+ /// 页码,从1开始
+ ///
+ [Range(1, int.MaxValue, ErrorMessage = "页码必须大于0")]
+ public int PageNumber { get; set; } = 1;
+
+ ///
+ /// 每页记录数
+ ///
+ [Range(1, 100, ErrorMessage = "每页记录数必须在1-100之间")]
+ public int PageSize { get; set; } = 10;
+
+ ///
+ /// 搜索关键词
+ ///
+ [MaxLength(100, ErrorMessage = "搜索关键词不能超过100个字符")]
+ public string? SearchTerm { get; set; }
+
+ ///
+ /// 是否禁用
+ ///
+ public bool? IsDisabled { get; set; }
+}
\ No newline at end of file
diff --git a/src/CellularManagement.Application/Features/Devices/Queries/GetDevices/GetDevicesQueryHandler.cs b/src/CellularManagement.Application/Features/Devices/Queries/GetDevices/GetDevicesQueryHandler.cs
new file mode 100644
index 0000000..97d0092
--- /dev/null
+++ b/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;
+
+///
+/// 获取设备列表查询处理器
+///
+public class GetDevicesQueryHandler : IRequestHandler>
+{
+ private readonly IDeviceRepository _deviceRepository;
+ private readonly ILogger _logger;
+
+ ///
+ /// 初始化查询处理器
+ ///
+ public GetDevicesQueryHandler(
+ IDeviceRepository deviceRepository,
+ ILogger logger)
+ {
+ _deviceRepository = deviceRepository;
+ _logger = logger;
+ }
+
+ ///
+ /// 处理获取设备列表查询
+ ///
+ public async Task> Handle(GetDevicesQuery request, CancellationToken cancellationToken)
+ {
+ try
+ {
+ // 验证请求参数
+ var validationContext = new ValidationContext(request);
+ var validationResults = new List();
+ 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.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.CreateSuccess(response);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error getting devices");
+ return OperationResult.CreateFailure($"获取设备列表时发生错误: {ex.Message}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/CellularManagement.Application/Features/Devices/Queries/GetDevices/GetDevicesResponse.cs b/src/CellularManagement.Application/Features/Devices/Queries/GetDevices/GetDevicesResponse.cs
new file mode 100644
index 0000000..a8aef0e
--- /dev/null
+++ b/src/CellularManagement.Application/Features/Devices/Queries/GetDevices/GetDevicesResponse.cs
@@ -0,0 +1,44 @@
+using System.Collections.Generic;
+
+namespace CellularManagement.Application.Features.Devices.Queries.GetDevices;
+
+///
+/// 设备列表响应
+///
+public class GetDevicesResponse
+{
+ ///
+ /// 总记录数
+ ///
+ public int TotalCount { get; set; }
+
+ ///
+ /// 当前页码
+ ///
+ public int PageNumber { get; set; }
+
+ ///
+ /// 每页记录数
+ ///
+ public int PageSize { get; set; }
+
+ ///
+ /// 总页数
+ ///
+ public int TotalPages { get; set; }
+
+ ///
+ /// 是否有上一页
+ ///
+ public bool HasPreviousPage { get; set; }
+
+ ///
+ /// 是否有下一页
+ ///
+ public bool HasNextPage { get; set; }
+
+ ///
+ /// 设备列表
+ ///
+ public List Items { get; set; } = new();
+}
\ No newline at end of file
diff --git a/src/CellularManagement.Domain/Entities/Device.cs b/src/CellularManagement.Domain/Entities/Device.cs
new file mode 100644
index 0000000..9130b1e
--- /dev/null
+++ b/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; }
+ }
+}
\ No newline at end of file
diff --git a/src/CellularManagement.Domain/Repositories/IDeviceRepository.cs b/src/CellularManagement.Domain/Repositories/IDeviceRepository.cs
new file mode 100644
index 0000000..c01687f
--- /dev/null
+++ b/src/CellularManagement.Domain/Repositories/IDeviceRepository.cs
@@ -0,0 +1,145 @@
+using CellularManagement.Domain.Entities;
+
+namespace CellularManagement.Domain.Repositories;
+
+///
+/// 设备命令仓储接口
+/// 负责设备实体的写入操作
+///
+public interface IDeviceCommandRepository : ICommandRepository
+{
+ ///
+ /// 创建设备
+ ///
+ /// 设备实体
+ /// 取消令牌
+ /// 创建后的设备实体
+ Task CreateAsync(Device device, CancellationToken cancellationToken = default);
+
+ ///
+ /// 批量创建设备
+ ///
+ /// 设备实体集合
+ /// 取消令牌
+ /// 创建后的设备实体集合
+ Task> CreateRangeAsync(IEnumerable devices, CancellationToken cancellationToken = default);
+
+ ///
+ /// 更新设备
+ ///
+ /// 设备实体
+ /// 取消令牌
+ /// 是否更新成功
+ Task UpdateAsync(Device device, CancellationToken cancellationToken = default);
+
+ ///
+ /// 批量更新设备
+ ///
+ /// 设备实体集合
+ /// 取消令牌
+ /// 是否更新成功
+ Task UpdateRangeAsync(IEnumerable devices, CancellationToken cancellationToken = default);
+
+ ///
+ /// 删除设备
+ ///
+ /// 设备ID
+ /// 取消令牌
+ /// 是否删除成功
+ Task DeleteAsync(string deviceId, CancellationToken cancellationToken = default);
+
+ ///
+ /// 批量删除设备
+ ///
+ /// 设备ID集合
+ /// 取消令牌
+ /// 删除的设备数量
+ Task DeleteRangeAsync(IEnumerable deviceIds, CancellationToken cancellationToken = default);
+
+ ///
+ /// 启用设备
+ ///
+ /// 设备ID
+ /// 取消令牌
+ /// 是否启用成功
+ Task EnableAsync(string deviceId, CancellationToken cancellationToken = default);
+
+ ///
+ /// 禁用设备
+ ///
+ /// 设备ID
+ /// 取消令牌
+ /// 是否禁用成功
+ Task DisableAsync(string deviceId, CancellationToken cancellationToken = default);
+
+ ///
+ /// 批量启用设备
+ ///
+ /// 设备ID集合
+ /// 取消令牌
+ /// 启用的设备数量
+ Task EnableRangeAsync(IEnumerable deviceIds, CancellationToken cancellationToken = default);
+
+ ///
+ /// 批量禁用设备
+ ///
+ /// 设备ID集合
+ /// 取消令牌
+ /// 禁用的设备数量
+ Task DisableRangeAsync(IEnumerable deviceIds, CancellationToken cancellationToken = default);
+}
+
+///
+/// 设备查询仓储接口
+/// 负责设备实体的读取操作
+///
+public interface IDeviceQueryRepository
+{
+ ///
+ /// 根据序列号获取设备
+ ///
+ Task GetBySerialNumberAsync(string serialNumber, CancellationToken cancellationToken = default);
+
+ ///
+ /// 检查序列号是否存在
+ ///
+ Task ExistsBySerialNumberAsync(string serialNumber, string? excludeDeviceId = null, CancellationToken cancellationToken = default);
+
+ ///
+ /// 分页查询设备列表
+ ///
+ Task<(int TotalCount, IEnumerable Items)> GetPagedAsync(
+ int pageNumber,
+ int pageSize,
+ string? searchTerm = null,
+ bool? isDisabled = null,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// 获取所有启用的设备
+ ///
+ Task> GetEnabledDevicesAsync(CancellationToken cancellationToken = default);
+
+ ///
+ /// 获取所有禁用的设备
+ ///
+ Task> GetDisabledDevicesAsync(CancellationToken cancellationToken = default);
+
+ ///
+ /// 根据IP地址获取设备
+ ///
+ Task GetByIPAddressAsync(string ipAddress, CancellationToken cancellationToken = default);
+
+ ///
+ /// 根据IP地址获取设备
+ ///
+ Task GetByIdAsync(string id, CancellationToken cancellationToken = default);
+}
+
+///
+/// 设备仓储接口
+/// 组合命令和查询仓储接口
+///
+public interface IDeviceRepository : IDeviceCommandRepository, IDeviceQueryRepository
+{
+}
\ No newline at end of file
diff --git a/src/CellularManagement.Infrastructure/Context/AppDbContext.cs b/src/CellularManagement.Infrastructure/Context/AppDbContext.cs
index 230166f..2e9da22 100644
--- a/src/CellularManagement.Infrastructure/Context/AppDbContext.cs
+++ b/src/CellularManagement.Infrastructure/Context/AppDbContext.cs
@@ -34,6 +34,11 @@ public class AppDbContext : IdentityDbContext
///
public DbSet LoginLogs { get; set; }
+ ///
+ /// 设备集合
+ ///
+ public DbSet Devices { get; set; }
+
///
/// 初始化数据库上下文
///
@@ -125,5 +130,22 @@ public class AppDbContext : IdentityDbContext
.HasForeignKey(l => l.UserId)
.OnDelete(DeleteBehavior.Restrict);
});
+
+ // 配置 Device 实体
+ modelBuilder.Entity(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();
+ });
}
}
\ No newline at end of file
diff --git a/src/CellularManagement.Infrastructure/Repositories/DeviceRepository.cs b/src/CellularManagement.Infrastructure/Repositories/DeviceRepository.cs
new file mode 100644
index 0000000..d47cf7b
--- /dev/null
+++ b/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
+{
+ ///
+ /// 设备仓储实现类
+ ///
+ public class DeviceRepository : CommandRepository, IDeviceRepository
+ {
+ private readonly AppDbContext _context;
+ private readonly ILogger _logger;
+
+ ///
+ /// 初始化仓储
+ ///
+ public DeviceRepository(
+ AppDbContext context,
+ IUnitOfWork unitOfWork,
+ ILogger logger)
+ : base(context, unitOfWork, logger)
+ {
+ _context = context;
+ _logger = logger;
+ }
+
+ #region IDeviceQueryRepository 实现
+
+ ///
+ /// 根据ID获取设备
+ ///
+ public async Task 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);
+ }
+
+ ///
+ /// 根据序列号获取设备
+ ///
+ public async Task 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);
+ }
+
+ ///
+ /// 检查序列号是否存在
+ ///
+ public async Task 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);
+ }
+
+ ///
+ /// 分页查询设备列表
+ ///
+ public async Task<(int TotalCount, IEnumerable 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);
+ }
+
+ ///
+ /// 获取所有启用的设备
+ ///
+ public async Task> GetEnabledDevicesAsync(CancellationToken cancellationToken = default)
+ {
+ _logger.LogInformation("Getting all enabled devices");
+ return await _context.Devices
+ .AsNoTracking()
+ .Where(d => !d.IsDisabled)
+ .ToListAsync(cancellationToken);
+ }
+
+ ///
+ /// 获取所有禁用的设备
+ ///
+ public async Task> GetDisabledDevicesAsync(CancellationToken cancellationToken = default)
+ {
+ _logger.LogInformation("Getting all disabled devices");
+ return await _context.Devices
+ .AsNoTracking()
+ .Where(d => d.IsDisabled)
+ .ToListAsync(cancellationToken);
+ }
+
+ ///
+ /// 根据IP地址获取设备
+ ///
+ public async Task 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 实现
+
+ ///
+ /// 创建设备
+ ///
+ public async Task 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);
+ }
+
+ ///
+ /// 批量创建设备
+ ///
+ public async Task> CreateRangeAsync(IEnumerable 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);
+ }
+
+ ///
+ /// 更新设备
+ ///
+ public async Task 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;
+ }
+
+ ///
+ /// 批量更新设备
+ ///
+ public async Task UpdateRangeAsync(IEnumerable 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;
+ }
+
+ ///
+ /// 删除设备
+ ///
+ public async Task DeleteAsync(string deviceId, CancellationToken cancellationToken = default)
+ {
+ _logger.LogInformation("Deleting device with ID: {DeviceId}", deviceId);
+ return await DeleteByIdAsync(deviceId, cancellationToken);
+ }
+
+ ///
+ /// 批量删除设备
+ ///
+ public async Task DeleteRangeAsync(IEnumerable deviceIds, CancellationToken cancellationToken = default)
+ {
+ _logger.LogInformation("Deleting {Count} devices in batch", deviceIds.Count());
+ return await DeleteWhereAsync(d => deviceIds.Contains(d.DeviceID), cancellationToken);
+ }
+
+ ///
+ /// 启用设备
+ ///
+ public async Task 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;
+ }
+
+ ///
+ /// 禁用设备
+ ///
+ public async Task 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;
+ }
+
+ ///
+ /// 批量启用设备
+ ///
+ public async Task EnableRangeAsync(IEnumerable 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;
+ }
+
+ ///
+ /// 批量禁用设备
+ ///
+ public async Task DisableRangeAsync(IEnumerable 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
+ }
+}
\ No newline at end of file
diff --git a/src/CellularManagement.Presentation/Controllers/DevicesController.cs b/src/CellularManagement.Presentation/Controllers/DevicesController.cs
new file mode 100644
index 0000000..67251b0
--- /dev/null
+++ b/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 Items)>>> GetAll([FromQuery] GetDevicesQuery query)
+ {
+ var result = await mediator.Send(query);
+ return Ok(result);
+ }
+
+ [HttpGet("{id}")]
+ public async Task>> GetById(string id)
+ {
+ var device = await mediator.Send(new GetDeviceByIdQuery(id));
+ if (device == null)
+ return NotFound(OperationResult.CreateFailure($"Device with ID {id} not found"));
+
+ return Ok(device);
+ }
+
+ [HttpPost]
+ public async Task>> Create([FromBody] CreateDeviceCommand command)
+ {
+ var deviceId = await mediator.Send(command);
+ return CreatedAtAction(
+ nameof(GetById),
+ new { id = deviceId },
+ deviceId);
+ }
+
+ [HttpPut("{id}")]
+ public async Task>> Update(string id, [FromBody] UpdateDeviceCommand command)
+ {
+ if (id != command.DeviceID)
+ return BadRequest(OperationResult.CreateFailure("ID mismatch"));
+
+ var result = await mediator.Send(command);
+ if (!result.IsSuccess)
+ return NotFound(OperationResult.CreateFailure($"Device with ID {id} not found"));
+
+ return Ok(OperationResult.CreateSuccess("Device updated successfully", true));
+ }
+
+ [HttpDelete("{id}")]
+ public async Task>> Delete(string id)
+ {
+ var result = await mediator.Send(new DeleteDeviceCommand(id));
+ if (!result.IsSuccess)
+ return NotFound(OperationResult.CreateFailure($"Device with ID {id} not found"));
+
+ return Ok(OperationResult.CreateSuccess("Device deleted successfully", true));
+ }
+ }
+}
\ No newline at end of file