diff --git a/src/X1.Application/Features/Scenarios/Commands/CreateScenario/CreateScenarioCommand.cs b/src/X1.Application/Features/Scenarios/Commands/CreateScenario/CreateScenarioCommand.cs
new file mode 100644
index 0000000..e42a4ca
--- /dev/null
+++ b/src/X1.Application/Features/Scenarios/Commands/CreateScenario/CreateScenarioCommand.cs
@@ -0,0 +1,43 @@
+using CellularManagement.Domain.Common;
+using MediatR;
+using System.ComponentModel.DataAnnotations;
+
+namespace CellularManagement.Application.Features.Scenarios.Commands.CreateScenario;
+
+///
+/// 创建场景命令
+///
+public class CreateScenarioCommand : IRequest>
+{
+ ///
+ /// 场景编码
+ ///
+ [Required(ErrorMessage = "场景编码不能为空")]
+ [MaxLength(50, ErrorMessage = "场景编码长度不能超过50个字符")]
+ public string Code { get; set; } = null!;
+
+ ///
+ /// 场景名称
+ ///
+ [Required(ErrorMessage = "场景名称不能为空")]
+ [MaxLength(100, ErrorMessage = "场景名称长度不能超过100个字符")]
+ public string Name { get; set; } = null!;
+
+ ///
+ /// 网络配置ID
+ ///
+ [Required(ErrorMessage = "网络配置ID不能为空")]
+ [MaxLength(50, ErrorMessage = "网络配置ID长度不能超过50个字符")]
+ public string NetworkConfigId { get; set; } = null!;
+
+ ///
+ /// 说明
+ ///
+ [MaxLength(1000, ErrorMessage = "说明长度不能超过1000个字符")]
+ public string? Description { get; set; }
+
+ ///
+ /// 是否禁用
+ ///
+ public bool IsDisabled { get; set; } = false;
+}
\ No newline at end of file
diff --git a/src/X1.Application/Features/Scenarios/Commands/CreateScenario/CreateScenarioCommandHandler.cs b/src/X1.Application/Features/Scenarios/Commands/CreateScenario/CreateScenarioCommandHandler.cs
new file mode 100644
index 0000000..a524545
--- /dev/null
+++ b/src/X1.Application/Features/Scenarios/Commands/CreateScenario/CreateScenarioCommandHandler.cs
@@ -0,0 +1,100 @@
+using MediatR;
+using Microsoft.Extensions.Logging;
+using CellularManagement.Domain.Common;
+using CellularManagement.Domain.Entities.Device;
+using CellularManagement.Domain.Repositories.Device;
+using CellularManagement.Domain.Repositories.Base;
+using CellularManagement.Domain.Services;
+
+namespace CellularManagement.Application.Features.Scenarios.Commands.CreateScenario;
+
+///
+/// 创建场景命令处理器
+///
+public class CreateScenarioCommandHandler : IRequestHandler>
+{
+ private readonly IScenarioRepository _scenarioRepository;
+ private readonly ILogger _logger;
+ private readonly IUnitOfWork _unitOfWork;
+ private readonly ICurrentUserService _currentUserService;
+
+ ///
+ /// 初始化命令处理器
+ ///
+ public CreateScenarioCommandHandler(
+ IScenarioRepository scenarioRepository,
+ ILogger logger,
+ IUnitOfWork unitOfWork,
+ ICurrentUserService currentUserService)
+ {
+ _scenarioRepository = scenarioRepository;
+ _logger = logger;
+ _unitOfWork = unitOfWork;
+ _currentUserService = currentUserService;
+ }
+
+ ///
+ /// 处理创建场景命令
+ ///
+ public async Task> Handle(CreateScenarioCommand request, CancellationToken cancellationToken)
+ {
+ try
+ {
+ _logger.LogInformation("开始创建场景,场景编码: {Code}, 场景名称: {Name}",
+ request.Code, request.Name);
+
+ // 检查场景编码是否已存在
+ if (await _scenarioRepository.CodeExistsAsync(request.Code, cancellationToken))
+ {
+ _logger.LogWarning("场景编码已存在: {Code}", request.Code);
+ return OperationResult.CreateFailure($"场景编码 {request.Code} 已存在");
+ }
+
+ // 获取当前用户ID
+ var currentUserId = _currentUserService.GetCurrentUserId();
+ if (string.IsNullOrEmpty(currentUserId))
+ {
+ _logger.LogError("无法获取当前用户ID,用户可能未认证");
+ return OperationResult.CreateFailure("用户未认证,无法创建场景");
+ }
+
+ // 创建场景实体
+ var scenario = Scenario.Create(
+ code: request.Code,
+ name: request.Name,
+ networkConfigId: request.NetworkConfigId,
+ createdBy: currentUserId,
+ description: request.Description,
+ isDisabled: request.IsDisabled);
+
+ // 保存场景
+ await _scenarioRepository.AddScenarioAsync(scenario, cancellationToken);
+
+ // 保存更改到数据库
+ await _unitOfWork.SaveChangesAsync(cancellationToken);
+
+ // 构建响应
+ var response = new CreateScenarioResponse
+ {
+ ScenarioId = scenario.Id,
+ Code = scenario.Code,
+ Name = scenario.Name,
+ NetworkConfigId = scenario.NetworkConfigId,
+ Description = scenario.Description,
+ IsDisabled = scenario.IsDisabled,
+ CreatedAt = scenario.CreatedAt,
+ CreatedBy = scenario.CreatedBy
+ };
+
+ _logger.LogInformation("场景创建成功,场景ID: {ScenarioId}, 场景编码: {Code}, 场景名称: {Name}",
+ scenario.Id, scenario.Code, scenario.Name);
+ return OperationResult.CreateSuccess(response);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "创建场景时发生错误,场景编码: {Code}, 场景名称: {Name}",
+ request.Code, request.Name);
+ return OperationResult.CreateFailure($"创建场景时发生错误: {ex.Message}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/X1.Application/Features/Scenarios/Commands/CreateScenario/CreateScenarioResponse.cs b/src/X1.Application/Features/Scenarios/Commands/CreateScenario/CreateScenarioResponse.cs
new file mode 100644
index 0000000..7e8e5f3
--- /dev/null
+++ b/src/X1.Application/Features/Scenarios/Commands/CreateScenario/CreateScenarioResponse.cs
@@ -0,0 +1,47 @@
+namespace CellularManagement.Application.Features.Scenarios.Commands.CreateScenario;
+
+///
+/// 创建场景响应
+///
+public class CreateScenarioResponse
+{
+ ///
+ /// 场景ID
+ ///
+ public string ScenarioId { get; set; } = null!;
+
+ ///
+ /// 场景编码
+ ///
+ public string Code { get; set; } = null!;
+
+ ///
+ /// 场景名称
+ ///
+ public string Name { get; set; } = null!;
+
+ ///
+ /// 网络配置ID
+ ///
+ public string NetworkConfigId { get; set; } = null!;
+
+ ///
+ /// 说明
+ ///
+ public string? Description { get; set; }
+
+ ///
+ /// 是否禁用
+ ///
+ public bool IsDisabled { get; set; }
+
+ ///
+ /// 创建时间
+ ///
+ public DateTime CreatedAt { get; set; }
+
+ ///
+ /// 创建人
+ ///
+ public string CreatedBy { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/src/X1.Application/Features/Scenarios/Commands/DeleteScenario/DeleteScenarioCommand.cs b/src/X1.Application/Features/Scenarios/Commands/DeleteScenario/DeleteScenarioCommand.cs
new file mode 100644
index 0000000..af71cb1
--- /dev/null
+++ b/src/X1.Application/Features/Scenarios/Commands/DeleteScenario/DeleteScenarioCommand.cs
@@ -0,0 +1,18 @@
+using CellularManagement.Domain.Common;
+using MediatR;
+using System.ComponentModel.DataAnnotations;
+
+namespace CellularManagement.Application.Features.Scenarios.Commands.DeleteScenario;
+
+///
+/// 删除场景命令
+///
+public class DeleteScenarioCommand : IRequest>
+{
+ ///
+ /// 场景ID
+ ///
+ [Required(ErrorMessage = "场景ID不能为空")]
+ [MaxLength(50, ErrorMessage = "场景ID长度不能超过50个字符")]
+ public string ScenarioId { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/src/X1.Application/Features/Scenarios/Commands/DeleteScenario/DeleteScenarioCommandHandler.cs b/src/X1.Application/Features/Scenarios/Commands/DeleteScenario/DeleteScenarioCommandHandler.cs
new file mode 100644
index 0000000..d75346f
--- /dev/null
+++ b/src/X1.Application/Features/Scenarios/Commands/DeleteScenario/DeleteScenarioCommandHandler.cs
@@ -0,0 +1,94 @@
+using MediatR;
+using Microsoft.Extensions.Logging;
+using CellularManagement.Domain.Common;
+using CellularManagement.Domain.Entities.Device;
+using CellularManagement.Domain.Repositories.Device;
+using CellularManagement.Domain.Repositories.Base;
+using CellularManagement.Domain.Services;
+
+namespace CellularManagement.Application.Features.Scenarios.Commands.DeleteScenario;
+
+///
+/// 删除场景命令处理器
+///
+public class DeleteScenarioCommandHandler : IRequestHandler>
+{
+ private readonly IScenarioRepository _scenarioRepository;
+ private readonly ILogger _logger;
+ private readonly IUnitOfWork _unitOfWork;
+ private readonly ICurrentUserService _currentUserService;
+
+ ///
+ /// 初始化命令处理器
+ ///
+ public DeleteScenarioCommandHandler(
+ IScenarioRepository scenarioRepository,
+ ILogger logger,
+ IUnitOfWork unitOfWork,
+ ICurrentUserService currentUserService)
+ {
+ _scenarioRepository = scenarioRepository;
+ _logger = logger;
+ _unitOfWork = unitOfWork;
+ _currentUserService = currentUserService;
+ }
+
+ ///
+ /// 处理删除场景命令
+ ///
+ public async Task> Handle(DeleteScenarioCommand request, CancellationToken cancellationToken)
+ {
+ try
+ {
+ _logger.LogInformation("开始删除场景,场景ID: {ScenarioId}", request.ScenarioId);
+
+ // 检查场景是否存在
+ var existingScenario = await _scenarioRepository.GetScenarioByIdAsync(request.ScenarioId, cancellationToken);
+ if (existingScenario == null)
+ {
+ _logger.LogWarning("场景不存在: {ScenarioId}", request.ScenarioId);
+ return OperationResult.CreateFailure($"场景ID {request.ScenarioId} 不存在");
+ }
+
+ // 检查场景是否已被软删除
+ if (existingScenario.IsDeleted)
+ {
+ _logger.LogWarning("场景已被删除: {ScenarioId}", request.ScenarioId);
+ return OperationResult.CreateFailure($"场景ID {request.ScenarioId} 已被删除");
+ }
+
+ // 获取当前用户ID
+ var currentUserId = _currentUserService.GetCurrentUserId();
+ if (string.IsNullOrEmpty(currentUserId))
+ {
+ _logger.LogError("无法获取当前用户ID,用户可能未认证");
+ return OperationResult.CreateFailure("用户未认证,无法删除场景");
+ }
+
+ // 软删除场景
+ existingScenario.SoftDelete();
+ existingScenario.Update(
+ code: existingScenario.Code,
+ name: existingScenario.Name,
+ networkConfigId: existingScenario.NetworkConfigId,
+ updatedBy: currentUserId,
+ description: existingScenario.Description,
+ isDisabled: existingScenario.IsDisabled);
+
+ // 保存更改
+ _scenarioRepository.UpdateScenario(existingScenario);
+
+ // 保存更改到数据库
+ await _unitOfWork.SaveChangesAsync(cancellationToken);
+
+ _logger.LogInformation("场景删除成功,场景ID: {ScenarioId}, 场景编码: {Code}, 场景名称: {Name}",
+ existingScenario.Id, existingScenario.Code, existingScenario.Name);
+ return OperationResult.CreateSuccess(true);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "删除场景时发生错误,场景ID: {ScenarioId}", request.ScenarioId);
+ return OperationResult.CreateFailure($"删除场景时发生错误: {ex.Message}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/X1.Application/Features/Scenarios/Commands/DisableScenario/DisableScenarioCommand.cs b/src/X1.Application/Features/Scenarios/Commands/DisableScenario/DisableScenarioCommand.cs
new file mode 100644
index 0000000..7511432
--- /dev/null
+++ b/src/X1.Application/Features/Scenarios/Commands/DisableScenario/DisableScenarioCommand.cs
@@ -0,0 +1,18 @@
+using CellularManagement.Domain.Common;
+using MediatR;
+using System.ComponentModel.DataAnnotations;
+
+namespace CellularManagement.Application.Features.Scenarios.Commands.DisableScenario;
+
+///
+/// 禁用场景命令
+///
+public class DisableScenarioCommand : IRequest>
+{
+ ///
+ /// 场景ID
+ ///
+ [Required(ErrorMessage = "场景ID不能为空")]
+ [MaxLength(50, ErrorMessage = "场景ID长度不能超过50个字符")]
+ public string ScenarioId { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/src/X1.Application/Features/Scenarios/Commands/DisableScenario/DisableScenarioCommandHandler.cs b/src/X1.Application/Features/Scenarios/Commands/DisableScenario/DisableScenarioCommandHandler.cs
new file mode 100644
index 0000000..5d9678d
--- /dev/null
+++ b/src/X1.Application/Features/Scenarios/Commands/DisableScenario/DisableScenarioCommandHandler.cs
@@ -0,0 +1,101 @@
+using MediatR;
+using Microsoft.Extensions.Logging;
+using CellularManagement.Domain.Common;
+using CellularManagement.Domain.Entities.Device;
+using CellularManagement.Domain.Repositories.Device;
+using CellularManagement.Domain.Repositories.Base;
+using CellularManagement.Domain.Services;
+
+namespace CellularManagement.Application.Features.Scenarios.Commands.DisableScenario;
+
+///
+/// 禁用场景命令处理器
+///
+public class DisableScenarioCommandHandler : IRequestHandler>
+{
+ private readonly IScenarioRepository _scenarioRepository;
+ private readonly ILogger _logger;
+ private readonly IUnitOfWork _unitOfWork;
+ private readonly ICurrentUserService _currentUserService;
+
+ ///
+ /// 初始化命令处理器
+ ///
+ public DisableScenarioCommandHandler(
+ IScenarioRepository scenarioRepository,
+ ILogger logger,
+ IUnitOfWork unitOfWork,
+ ICurrentUserService currentUserService)
+ {
+ _scenarioRepository = scenarioRepository;
+ _logger = logger;
+ _unitOfWork = unitOfWork;
+ _currentUserService = currentUserService;
+ }
+
+ ///
+ /// 处理禁用场景命令
+ ///
+ public async Task> Handle(DisableScenarioCommand request, CancellationToken cancellationToken)
+ {
+ try
+ {
+ _logger.LogInformation("开始禁用场景,场景ID: {ScenarioId}", request.ScenarioId);
+
+ // 检查场景是否存在
+ var existingScenario = await _scenarioRepository.GetScenarioByIdAsync(request.ScenarioId, cancellationToken);
+ if (existingScenario == null)
+ {
+ _logger.LogWarning("场景不存在: {ScenarioId}", request.ScenarioId);
+ return OperationResult.CreateFailure($"场景ID {request.ScenarioId} 不存在");
+ }
+
+ // 检查场景是否已被软删除
+ if (existingScenario.IsDeleted)
+ {
+ _logger.LogWarning("场景已被删除: {ScenarioId}", request.ScenarioId);
+ return OperationResult.CreateFailure($"场景ID {request.ScenarioId} 已被删除");
+ }
+
+ // 检查场景是否已经禁用
+ if (existingScenario.IsDisabled)
+ {
+ _logger.LogWarning("场景已经禁用: {ScenarioId}", request.ScenarioId);
+ return OperationResult.CreateFailure($"场景ID {request.ScenarioId} 已经禁用");
+ }
+
+ // 获取当前用户ID
+ var currentUserId = _currentUserService.GetCurrentUserId();
+ if (string.IsNullOrEmpty(currentUserId))
+ {
+ _logger.LogError("无法获取当前用户ID,用户可能未认证");
+ return OperationResult.CreateFailure("用户未认证,无法禁用场景");
+ }
+
+ // 禁用场景
+ existingScenario.Disable();
+ existingScenario.Update(
+ code: existingScenario.Code,
+ name: existingScenario.Name,
+ networkConfigId: existingScenario.NetworkConfigId,
+ updatedBy: currentUserId,
+ description: existingScenario.Description,
+ isDisabled: true);
+
+ // 保存更改
+ _scenarioRepository.UpdateScenario(existingScenario);
+
+ // 保存更改到数据库
+ await _unitOfWork.SaveChangesAsync(cancellationToken);
+
+ _logger.LogInformation("场景禁用成功,场景ID: {ScenarioId}, 场景编码: {Code}, 场景名称: {Name}",
+ existingScenario.Id, existingScenario.Code, existingScenario.Name);
+ return OperationResult.CreateSuccess(true);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "禁用场景时发生错误,场景ID: {ScenarioId}", request.ScenarioId);
+ return OperationResult.CreateFailure($"禁用场景时发生错误: {ex.Message}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/X1.Application/Features/Scenarios/Commands/EnableScenario/EnableScenarioCommand.cs b/src/X1.Application/Features/Scenarios/Commands/EnableScenario/EnableScenarioCommand.cs
new file mode 100644
index 0000000..1581500
--- /dev/null
+++ b/src/X1.Application/Features/Scenarios/Commands/EnableScenario/EnableScenarioCommand.cs
@@ -0,0 +1,18 @@
+using CellularManagement.Domain.Common;
+using MediatR;
+using System.ComponentModel.DataAnnotations;
+
+namespace CellularManagement.Application.Features.Scenarios.Commands.EnableScenario;
+
+///
+/// 启用场景命令
+///
+public class EnableScenarioCommand : IRequest>
+{
+ ///
+ /// 场景ID
+ ///
+ [Required(ErrorMessage = "场景ID不能为空")]
+ [MaxLength(50, ErrorMessage = "场景ID长度不能超过50个字符")]
+ public string ScenarioId { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/src/X1.Application/Features/Scenarios/Commands/EnableScenario/EnableScenarioCommandHandler.cs b/src/X1.Application/Features/Scenarios/Commands/EnableScenario/EnableScenarioCommandHandler.cs
new file mode 100644
index 0000000..bab4cd7
--- /dev/null
+++ b/src/X1.Application/Features/Scenarios/Commands/EnableScenario/EnableScenarioCommandHandler.cs
@@ -0,0 +1,101 @@
+using MediatR;
+using Microsoft.Extensions.Logging;
+using CellularManagement.Domain.Common;
+using CellularManagement.Domain.Entities.Device;
+using CellularManagement.Domain.Repositories.Device;
+using CellularManagement.Domain.Repositories.Base;
+using CellularManagement.Domain.Services;
+
+namespace CellularManagement.Application.Features.Scenarios.Commands.EnableScenario;
+
+///
+/// 启用场景命令处理器
+///
+public class EnableScenarioCommandHandler : IRequestHandler>
+{
+ private readonly IScenarioRepository _scenarioRepository;
+ private readonly ILogger _logger;
+ private readonly IUnitOfWork _unitOfWork;
+ private readonly ICurrentUserService _currentUserService;
+
+ ///
+ /// 初始化命令处理器
+ ///
+ public EnableScenarioCommandHandler(
+ IScenarioRepository scenarioRepository,
+ ILogger logger,
+ IUnitOfWork unitOfWork,
+ ICurrentUserService currentUserService)
+ {
+ _scenarioRepository = scenarioRepository;
+ _logger = logger;
+ _unitOfWork = unitOfWork;
+ _currentUserService = currentUserService;
+ }
+
+ ///
+ /// 处理启用场景命令
+ ///
+ public async Task> Handle(EnableScenarioCommand request, CancellationToken cancellationToken)
+ {
+ try
+ {
+ _logger.LogInformation("开始启用场景,场景ID: {ScenarioId}", request.ScenarioId);
+
+ // 检查场景是否存在
+ var existingScenario = await _scenarioRepository.GetScenarioByIdAsync(request.ScenarioId, cancellationToken);
+ if (existingScenario == null)
+ {
+ _logger.LogWarning("场景不存在: {ScenarioId}", request.ScenarioId);
+ return OperationResult.CreateFailure($"场景ID {request.ScenarioId} 不存在");
+ }
+
+ // 检查场景是否已被软删除
+ if (existingScenario.IsDeleted)
+ {
+ _logger.LogWarning("场景已被删除: {ScenarioId}", request.ScenarioId);
+ return OperationResult.CreateFailure($"场景ID {request.ScenarioId} 已被删除");
+ }
+
+ // 检查场景是否已经启用
+ if (!existingScenario.IsDisabled)
+ {
+ _logger.LogWarning("场景已经启用: {ScenarioId}", request.ScenarioId);
+ return OperationResult.CreateFailure($"场景ID {request.ScenarioId} 已经启用");
+ }
+
+ // 获取当前用户ID
+ var currentUserId = _currentUserService.GetCurrentUserId();
+ if (string.IsNullOrEmpty(currentUserId))
+ {
+ _logger.LogError("无法获取当前用户ID,用户可能未认证");
+ return OperationResult.CreateFailure("用户未认证,无法启用场景");
+ }
+
+ // 启用场景
+ existingScenario.Enable();
+ existingScenario.Update(
+ code: existingScenario.Code,
+ name: existingScenario.Name,
+ networkConfigId: existingScenario.NetworkConfigId,
+ updatedBy: currentUserId,
+ description: existingScenario.Description,
+ isDisabled: false);
+
+ // 保存更改
+ _scenarioRepository.UpdateScenario(existingScenario);
+
+ // 保存更改到数据库
+ await _unitOfWork.SaveChangesAsync(cancellationToken);
+
+ _logger.LogInformation("场景启用成功,场景ID: {ScenarioId}, 场景编码: {Code}, 场景名称: {Name}",
+ existingScenario.Id, existingScenario.Code, existingScenario.Name);
+ return OperationResult.CreateSuccess(true);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "启用场景时发生错误,场景ID: {ScenarioId}", request.ScenarioId);
+ return OperationResult.CreateFailure($"启用场景时发生错误: {ex.Message}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/X1.Application/Features/Scenarios/Commands/UpdateScenario/UpdateScenarioCommand.cs b/src/X1.Application/Features/Scenarios/Commands/UpdateScenario/UpdateScenarioCommand.cs
new file mode 100644
index 0000000..dc3f342
--- /dev/null
+++ b/src/X1.Application/Features/Scenarios/Commands/UpdateScenario/UpdateScenarioCommand.cs
@@ -0,0 +1,50 @@
+using CellularManagement.Domain.Common;
+using MediatR;
+using System.ComponentModel.DataAnnotations;
+
+namespace CellularManagement.Application.Features.Scenarios.Commands.UpdateScenario;
+
+///
+/// 更新场景命令
+///
+public class UpdateScenarioCommand : IRequest>
+{
+ ///
+ /// 场景ID
+ ///
+ [Required(ErrorMessage = "场景ID不能为空")]
+ [MaxLength(50, ErrorMessage = "场景ID长度不能超过50个字符")]
+ public string ScenarioId { get; set; } = null!;
+
+ ///
+ /// 场景编码
+ ///
+ [Required(ErrorMessage = "场景编码不能为空")]
+ [MaxLength(50, ErrorMessage = "场景编码长度不能超过50个字符")]
+ public string Code { get; set; } = null!;
+
+ ///
+ /// 场景名称
+ ///
+ [Required(ErrorMessage = "场景名称不能为空")]
+ [MaxLength(100, ErrorMessage = "场景名称长度不能超过100个字符")]
+ public string Name { get; set; } = null!;
+
+ ///
+ /// 网络配置ID
+ ///
+ [Required(ErrorMessage = "网络配置ID不能为空")]
+ [MaxLength(50, ErrorMessage = "网络配置ID长度不能超过50个字符")]
+ public string NetworkConfigId { get; set; } = null!;
+
+ ///
+ /// 说明
+ ///
+ [MaxLength(1000, ErrorMessage = "说明长度不能超过1000个字符")]
+ public string? Description { get; set; }
+
+ ///
+ /// 是否禁用
+ ///
+ public bool IsDisabled { get; set; } = false;
+}
\ No newline at end of file
diff --git a/src/X1.Application/Features/Scenarios/Commands/UpdateScenario/UpdateScenarioCommandHandler.cs b/src/X1.Application/Features/Scenarios/Commands/UpdateScenario/UpdateScenarioCommandHandler.cs
new file mode 100644
index 0000000..782bda6
--- /dev/null
+++ b/src/X1.Application/Features/Scenarios/Commands/UpdateScenario/UpdateScenarioCommandHandler.cs
@@ -0,0 +1,109 @@
+using MediatR;
+using Microsoft.Extensions.Logging;
+using CellularManagement.Domain.Common;
+using CellularManagement.Domain.Entities.Device;
+using CellularManagement.Domain.Repositories.Device;
+using CellularManagement.Domain.Repositories.Base;
+using CellularManagement.Domain.Services;
+
+namespace CellularManagement.Application.Features.Scenarios.Commands.UpdateScenario;
+
+///
+/// 更新场景命令处理器
+///
+public class UpdateScenarioCommandHandler : IRequestHandler>
+{
+ private readonly IScenarioRepository _scenarioRepository;
+ private readonly ILogger _logger;
+ private readonly IUnitOfWork _unitOfWork;
+ private readonly ICurrentUserService _currentUserService;
+
+ ///
+ /// 初始化命令处理器
+ ///
+ public UpdateScenarioCommandHandler(
+ IScenarioRepository scenarioRepository,
+ ILogger logger,
+ IUnitOfWork unitOfWork,
+ ICurrentUserService currentUserService)
+ {
+ _scenarioRepository = scenarioRepository;
+ _logger = logger;
+ _unitOfWork = unitOfWork;
+ _currentUserService = currentUserService;
+ }
+
+ ///
+ /// 处理更新场景命令
+ ///
+ public async Task> Handle(UpdateScenarioCommand request, CancellationToken cancellationToken)
+ {
+ try
+ {
+ _logger.LogInformation("开始更新场景,场景ID: {ScenarioId}, 场景编码: {Code}, 场景名称: {Name}",
+ request.ScenarioId, request.Code, request.Name);
+
+ // 检查场景是否存在
+ var existingScenario = await _scenarioRepository.GetScenarioByIdAsync(request.ScenarioId, cancellationToken);
+ if (existingScenario == null)
+ {
+ _logger.LogWarning("场景不存在: {ScenarioId}", request.ScenarioId);
+ return OperationResult.CreateFailure($"场景ID {request.ScenarioId} 不存在");
+ }
+
+ // 检查场景编码是否已被其他场景使用
+ var scenarioWithSameCode = await _scenarioRepository.GetScenarioByCodeAsync(request.Code, cancellationToken);
+ if (scenarioWithSameCode != null && scenarioWithSameCode.Id != request.ScenarioId)
+ {
+ _logger.LogWarning("场景编码已被其他场景使用: {Code}", request.Code);
+ return OperationResult.CreateFailure($"场景编码 {request.Code} 已被其他场景使用");
+ }
+
+ // 获取当前用户ID
+ var currentUserId = _currentUserService.GetCurrentUserId();
+ if (string.IsNullOrEmpty(currentUserId))
+ {
+ _logger.LogError("无法获取当前用户ID,用户可能未认证");
+ return OperationResult.CreateFailure("用户未认证,无法更新场景");
+ }
+
+ // 更新场景
+ existingScenario.Update(
+ code: request.Code,
+ name: request.Name,
+ networkConfigId: request.NetworkConfigId,
+ updatedBy: currentUserId,
+ description: request.Description,
+ isDisabled: request.IsDisabled);
+
+ // 保存更改
+ _scenarioRepository.UpdateScenario(existingScenario);
+
+ // 保存更改到数据库
+ await _unitOfWork.SaveChangesAsync(cancellationToken);
+
+ // 构建响应
+ var response = new UpdateScenarioResponse
+ {
+ ScenarioId = existingScenario.Id,
+ Code = existingScenario.Code,
+ Name = existingScenario.Name,
+ NetworkConfigId = existingScenario.NetworkConfigId,
+ Description = existingScenario.Description,
+ IsDisabled = existingScenario.IsDisabled,
+ UpdatedAt = existingScenario.UpdatedAt,
+ UpdatedBy = existingScenario.UpdatedBy
+ };
+
+ _logger.LogInformation("场景更新成功,场景ID: {ScenarioId}, 场景编码: {Code}, 场景名称: {Name}",
+ existingScenario.Id, existingScenario.Code, existingScenario.Name);
+ return OperationResult.CreateSuccess(response);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "更新场景时发生错误,场景ID: {ScenarioId}, 场景编码: {Code}, 场景名称: {Name}",
+ request.ScenarioId, request.Code, request.Name);
+ return OperationResult.CreateFailure($"更新场景时发生错误: {ex.Message}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/X1.Application/Features/Scenarios/Commands/UpdateScenario/UpdateScenarioResponse.cs b/src/X1.Application/Features/Scenarios/Commands/UpdateScenario/UpdateScenarioResponse.cs
new file mode 100644
index 0000000..4733942
--- /dev/null
+++ b/src/X1.Application/Features/Scenarios/Commands/UpdateScenario/UpdateScenarioResponse.cs
@@ -0,0 +1,47 @@
+namespace CellularManagement.Application.Features.Scenarios.Commands.UpdateScenario;
+
+///
+/// 更新场景响应
+///
+public class UpdateScenarioResponse
+{
+ ///
+ /// 场景ID
+ ///
+ public string ScenarioId { get; set; } = null!;
+
+ ///
+ /// 场景编码
+ ///
+ public string Code { get; set; } = null!;
+
+ ///
+ /// 场景名称
+ ///
+ public string Name { get; set; } = null!;
+
+ ///
+ /// 网络配置ID
+ ///
+ public string NetworkConfigId { get; set; } = null!;
+
+ ///
+ /// 说明
+ ///
+ public string? Description { get; set; }
+
+ ///
+ /// 是否禁用
+ ///
+ public bool IsDisabled { get; set; }
+
+ ///
+ /// 更新时间
+ ///
+ public DateTime? UpdatedAt { get; set; }
+
+ ///
+ /// 修改人
+ ///
+ public string? UpdatedBy { get; set; }
+}
\ No newline at end of file
diff --git a/src/X1.Application/Features/Scenarios/Queries/GetScenarioById/GetScenarioByIdQuery.cs b/src/X1.Application/Features/Scenarios/Queries/GetScenarioById/GetScenarioByIdQuery.cs
new file mode 100644
index 0000000..c119de8
--- /dev/null
+++ b/src/X1.Application/Features/Scenarios/Queries/GetScenarioById/GetScenarioByIdQuery.cs
@@ -0,0 +1,18 @@
+using CellularManagement.Domain.Common;
+using MediatR;
+using System.ComponentModel.DataAnnotations;
+
+namespace CellularManagement.Application.Features.Scenarios.Queries.GetScenarioById;
+
+///
+/// 根据ID获取场景查询
+///
+public class GetScenarioByIdQuery : IRequest>
+{
+ ///
+ /// 场景ID
+ ///
+ [Required(ErrorMessage = "场景ID不能为空")]
+ [MaxLength(50, ErrorMessage = "场景ID长度不能超过50个字符")]
+ public string Id { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/src/X1.Application/Features/Scenarios/Queries/GetScenarioById/GetScenarioByIdQueryHandler.cs b/src/X1.Application/Features/Scenarios/Queries/GetScenarioById/GetScenarioByIdQueryHandler.cs
new file mode 100644
index 0000000..2f13476
--- /dev/null
+++ b/src/X1.Application/Features/Scenarios/Queries/GetScenarioById/GetScenarioByIdQueryHandler.cs
@@ -0,0 +1,82 @@
+using MediatR;
+using Microsoft.Extensions.Logging;
+using CellularManagement.Domain.Common;
+using CellularManagement.Domain.Entities.Device;
+using CellularManagement.Domain.Repositories;
+using System.ComponentModel.DataAnnotations;
+using CellularManagement.Domain.Repositories.Device;
+
+namespace CellularManagement.Application.Features.Scenarios.Queries.GetScenarioById;
+
+///
+/// 根据ID获取场景查询处理器
+///
+public class GetScenarioByIdQueryHandler : IRequestHandler>
+{
+ private readonly IScenarioRepository _scenarioRepository;
+ private readonly ILogger _logger;
+
+ ///
+ /// 初始化查询处理器
+ ///
+ public GetScenarioByIdQueryHandler(
+ IScenarioRepository scenarioRepository,
+ ILogger logger)
+ {
+ _scenarioRepository = scenarioRepository;
+ _logger = logger;
+ }
+
+ ///
+ /// 处理根据ID获取场景查询
+ ///
+ public async Task> Handle(GetScenarioByIdQuery 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("请求参数无效: {Errors}", string.Join(", ", errorMessages));
+ return OperationResult.CreateFailure(errorMessages);
+ }
+
+ _logger.LogInformation("开始根据ID获取场景,ID: {Id}", request.Id);
+
+ // 获取场景数据
+ var scenario = await _scenarioRepository.GetScenarioByIdAsync(request.Id, cancellationToken);
+
+ if (scenario == null)
+ {
+ _logger.LogWarning("未找到指定的场景,ID: {Id}", request.Id);
+ return OperationResult.CreateFailure($"未找到ID为 {request.Id} 的场景");
+ }
+
+ // 构建响应
+ var response = new GetScenarioByIdResponse
+ {
+ ScenarioId = scenario.Id,
+ Code = scenario.Code,
+ Name = scenario.Name,
+ NetworkConfigId = scenario.NetworkConfigId,
+ Description = scenario.Description,
+ IsDisabled = scenario.IsDisabled,
+ CreatedAt = scenario.CreatedAt,
+ UpdatedAt = scenario.UpdatedAt,
+ CreatedBy = scenario.CreatedBy,
+ UpdatedBy = scenario.UpdatedBy
+ };
+
+ _logger.LogInformation("成功获取场景信息,ID: {Id}", request.Id);
+ return OperationResult.CreateSuccess(response);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "根据ID获取场景时发生错误,ID: {Id}", request.Id);
+ return OperationResult.CreateFailure($"根据ID获取场景时发生错误: {ex.Message}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/X1.Application/Features/Scenarios/Queries/GetScenarioById/GetScenarioByIdResponse.cs b/src/X1.Application/Features/Scenarios/Queries/GetScenarioById/GetScenarioByIdResponse.cs
new file mode 100644
index 0000000..86e18f8
--- /dev/null
+++ b/src/X1.Application/Features/Scenarios/Queries/GetScenarioById/GetScenarioByIdResponse.cs
@@ -0,0 +1,57 @@
+namespace CellularManagement.Application.Features.Scenarios.Queries.GetScenarioById;
+
+///
+/// 根据ID获取场景响应
+///
+public class GetScenarioByIdResponse
+{
+ ///
+ /// 场景ID
+ ///
+ public string ScenarioId { get; set; } = null!;
+
+ ///
+ /// 场景编码
+ ///
+ public string Code { get; set; } = null!;
+
+ ///
+ /// 场景名称
+ ///
+ public string Name { get; set; } = null!;
+
+ ///
+ /// 网络配置ID
+ ///
+ public string NetworkConfigId { get; set; } = null!;
+
+ ///
+ /// 说明
+ ///
+ public string? Description { get; set; }
+
+ ///
+ /// 是否禁用
+ ///
+ public bool IsDisabled { get; set; }
+
+ ///
+ /// 创建时间
+ ///
+ public DateTime CreatedAt { get; set; }
+
+ ///
+ /// 更新时间
+ ///
+ public DateTime? UpdatedAt { get; set; }
+
+ ///
+ /// 创建人
+ ///
+ public string CreatedBy { get; set; } = null!;
+
+ ///
+ /// 修改人
+ ///
+ public string? UpdatedBy { get; set; }
+}
\ No newline at end of file
diff --git a/src/X1.Application/Features/Scenarios/Queries/GetScenarios/GetScenariosQuery.cs b/src/X1.Application/Features/Scenarios/Queries/GetScenarios/GetScenariosQuery.cs
new file mode 100644
index 0000000..5833e9a
--- /dev/null
+++ b/src/X1.Application/Features/Scenarios/Queries/GetScenarios/GetScenariosQuery.cs
@@ -0,0 +1,40 @@
+using CellularManagement.Domain.Common;
+using MediatR;
+using System.ComponentModel.DataAnnotations;
+
+namespace CellularManagement.Application.Features.Scenarios.Queries.GetScenarios;
+
+///
+/// 获取场景列表查询
+///
+public class GetScenariosQuery : IRequest>
+{
+ ///
+ /// 页码
+ ///
+ [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)]
+ public string? SearchTerm { get; set; }
+
+ ///
+ /// 是否只获取启用的场景
+ ///
+ public bool? IsEnabled { get; set; }
+
+ ///
+ /// 网络配置ID过滤
+ ///
+ [MaxLength(50)]
+ public string? NetworkConfigId { get; set; }
+}
\ No newline at end of file
diff --git a/src/X1.Application/Features/Scenarios/Queries/GetScenarios/GetScenariosQueryHandler.cs b/src/X1.Application/Features/Scenarios/Queries/GetScenarios/GetScenariosQueryHandler.cs
new file mode 100644
index 0000000..1eeee18
--- /dev/null
+++ b/src/X1.Application/Features/Scenarios/Queries/GetScenarios/GetScenariosQueryHandler.cs
@@ -0,0 +1,109 @@
+using MediatR;
+using Microsoft.Extensions.Logging;
+using CellularManagement.Domain.Common;
+using CellularManagement.Domain.Entities.Device;
+using CellularManagement.Domain.Repositories;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using CellularManagement.Application.Features.Scenarios.Queries.GetScenarioById;
+using CellularManagement.Domain.Repositories.Device;
+
+namespace CellularManagement.Application.Features.Scenarios.Queries.GetScenarios;
+
+///
+/// 获取场景列表查询处理器
+///
+public class GetScenariosQueryHandler : IRequestHandler>
+{
+ private readonly IScenarioRepository _scenarioRepository;
+ private readonly ILogger _logger;
+
+ ///
+ /// 初始化查询处理器
+ ///
+ public GetScenariosQueryHandler(
+ IScenarioRepository scenarioRepository,
+ ILogger logger)
+ {
+ _scenarioRepository = scenarioRepository;
+ _logger = logger;
+ }
+
+ ///
+ /// 处理获取场景列表查询
+ ///
+ public async Task> Handle(GetScenariosQuery 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("请求参数无效: {Errors}", string.Join(", ", errorMessages));
+ return OperationResult.CreateFailure(errorMessages);
+ }
+
+ _logger.LogInformation("开始获取场景列表,页码: {PageNumber}, 每页数量: {PageSize}, 搜索关键词: {SearchTerm}, 是否启用: {IsEnabled}, 网络配置ID: {NetworkConfigId}",
+ request.PageNumber, request.PageSize, request.SearchTerm, request.IsEnabled, request.NetworkConfigId);
+
+ // 获取场景数据
+ var scenarios = await _scenarioRepository.SearchScenariosAsync(
+ request.SearchTerm,
+ cancellationToken);
+
+ // 如果指定了启用状态过滤
+ if (request.IsEnabled.HasValue)
+ {
+ scenarios = scenarios.Where(s => !s.IsDisabled == request.IsEnabled.Value).ToList();
+ }
+
+ // 如果指定了网络配置ID过滤
+ if (!string.IsNullOrEmpty(request.NetworkConfigId))
+ {
+ scenarios = scenarios.Where(s => s.NetworkConfigId == request.NetworkConfigId).ToList();
+ }
+
+ // 计算分页
+ int totalCount = scenarios.Count();
+ var items = scenarios
+ .Skip((request.PageNumber - 1) * request.PageSize)
+ .Take(request.PageSize)
+ .ToList();
+
+ // 构建响应
+ var response = new GetScenariosResponse
+ {
+ 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(s => new GetScenarioByIdResponse
+ {
+ ScenarioId = s.Id,
+ Code = s.Code,
+ Name = s.Name,
+ NetworkConfigId = s.NetworkConfigId,
+ Description = s.Description,
+ IsDisabled = s.IsDisabled,
+ CreatedAt = s.CreatedAt,
+ UpdatedAt = s.UpdatedAt,
+ CreatedBy = s.CreatedBy,
+ UpdatedBy = s.UpdatedBy
+ }).ToList()
+ };
+
+ _logger.LogInformation("成功获取场景列表,共 {Count} 条记录", items.Count);
+ return OperationResult.CreateSuccess(response);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "获取场景列表时发生错误");
+ return OperationResult.CreateFailure($"获取场景列表时发生错误: {ex.Message}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/X1.Application/Features/Scenarios/Queries/GetScenarios/GetScenariosResponse.cs b/src/X1.Application/Features/Scenarios/Queries/GetScenarios/GetScenariosResponse.cs
new file mode 100644
index 0000000..a46ec3d
--- /dev/null
+++ b/src/X1.Application/Features/Scenarios/Queries/GetScenarios/GetScenariosResponse.cs
@@ -0,0 +1,44 @@
+using CellularManagement.Application.Features.Scenarios.Queries.GetScenarioById;
+
+namespace CellularManagement.Application.Features.Scenarios.Queries.GetScenarios;
+
+///
+/// 获取场景列表响应
+///
+public class GetScenariosResponse
+{
+ ///
+ /// 总数量
+ ///
+ 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/X1.Domain/Entities/Device/Scenario.cs b/src/X1.Domain/Entities/Device/Scenario.cs
new file mode 100644
index 0000000..80ff2d8
--- /dev/null
+++ b/src/X1.Domain/Entities/Device/Scenario.cs
@@ -0,0 +1,128 @@
+using System.ComponentModel.DataAnnotations;
+using CellularManagement.Domain.Entities.Common;
+
+namespace CellularManagement.Domain.Entities.Device;
+
+///
+/// 场景实体
+///
+public class Scenario : AuditableEntity
+{
+ private Scenario() { }
+
+ ///
+ /// 场景编码
+ ///
+ [Required]
+ [MaxLength(50)]
+ public string Code { get; private set; } = null!;
+
+ ///
+ /// 场景名称
+ ///
+ [Required]
+ [MaxLength(100)]
+ public string Name { get; private set; } = null!;
+
+ ///
+ /// 网络配置ID
+ ///
+ [Required]
+ [MaxLength(50)]
+ public string NetworkConfigId { get; private set; } = null!;
+
+ ///
+ /// 说明
+ ///
+ [MaxLength(1000)]
+ public string? Description { get; private set; }
+
+ ///
+ /// 是否禁用
+ ///
+ public bool IsDisabled { get; private set; } = false;
+
+ ///
+ /// 创建场景
+ ///
+ public static Scenario Create(
+ string code,
+ string name,
+ string networkConfigId,
+ string createdBy,
+ string? description = null,
+ bool isDisabled = false)
+ {
+ var scenario = new Scenario
+ {
+ Id = Guid.NewGuid().ToString(),
+ Code = code,
+ Name = name,
+ NetworkConfigId = networkConfigId,
+ Description = description,
+ IsDisabled = isDisabled,
+ CreatedAt = DateTime.UtcNow,
+ UpdatedAt = DateTime.UtcNow,
+ CreatedBy = createdBy,
+ UpdatedBy = createdBy
+ };
+
+ return scenario;
+ }
+
+ ///
+ /// 更新场景
+ ///
+ public void Update(
+ string code,
+ string name,
+ string networkConfigId,
+ string updatedBy,
+ string? description = null,
+ bool isDisabled = false)
+ {
+ Code = code;
+ Name = name;
+ NetworkConfigId = networkConfigId;
+ Description = description;
+ IsDisabled = isDisabled;
+ UpdatedAt = DateTime.UtcNow;
+ UpdatedBy = updatedBy;
+ }
+
+ ///
+ /// 启用场景
+ ///
+ public void Enable()
+ {
+ IsDisabled = false;
+ UpdatedAt = DateTime.UtcNow;
+ }
+
+ ///
+ /// 禁用场景
+ ///
+ public void Disable()
+ {
+ IsDisabled = true;
+ UpdatedAt = DateTime.UtcNow;
+ }
+
+ ///
+ /// 软删除场景
+ ///
+ public void SoftDelete()
+ {
+ IsDeleted = true;
+ UpdatedAt = DateTime.UtcNow;
+ }
+
+ ///
+ /// 恢复场景
+ ///
+ public void Restore()
+ {
+ IsDeleted = false;
+ UpdatedAt = DateTime.UtcNow;
+ }
+}
\ No newline at end of file
diff --git a/src/X1.Domain/Repositories/Device/IScenarioRepository.cs b/src/X1.Domain/Repositories/Device/IScenarioRepository.cs
new file mode 100644
index 0000000..be5a0b7
--- /dev/null
+++ b/src/X1.Domain/Repositories/Device/IScenarioRepository.cs
@@ -0,0 +1,68 @@
+using CellularManagement.Domain.Entities;
+using CellularManagement.Domain.Entities.Device;
+using CellularManagement.Domain.Repositories.Base;
+
+namespace CellularManagement.Domain.Repositories.Device;
+
+///
+/// 场景仓储接口
+///
+public interface IScenarioRepository : IBaseRepository
+{
+ ///
+ /// 添加场景
+ ///
+ Task AddScenarioAsync(Scenario scenario, CancellationToken cancellationToken = default);
+
+ ///
+ /// 更新场景
+ ///
+ void UpdateScenario(Scenario scenario);
+
+ ///
+ /// 删除场景
+ ///
+ Task DeleteScenarioAsync(string id, CancellationToken cancellationToken = default);
+
+ ///
+ /// 获取所有场景
+ ///
+ Task> GetAllScenariosAsync(CancellationToken cancellationToken = default);
+
+ ///
+ /// 根据ID获取场景
+ ///
+ Task GetScenarioByIdAsync(string id, CancellationToken cancellationToken = default);
+
+ ///
+ /// 根据编码获取场景
+ ///
+ Task GetScenarioByCodeAsync(string code, CancellationToken cancellationToken = default);
+
+ ///
+ /// 搜索场景
+ ///
+ Task> SearchScenariosAsync(
+ string? keyword,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// 检查场景是否存在
+ ///
+ Task ExistsAsync(string id, CancellationToken cancellationToken = default);
+
+ ///
+ /// 检查编码是否存在
+ ///
+ Task CodeExistsAsync(string code, CancellationToken cancellationToken = default);
+
+ ///
+ /// 获取启用的场景
+ ///
+ Task> GetEnabledScenariosAsync(CancellationToken cancellationToken = default);
+
+ ///
+ /// 根据网络配置ID获取场景
+ ///
+ Task> GetScenariosByNetworkConfigIdAsync(string networkConfigId, CancellationToken cancellationToken = default);
+}
\ No newline at end of file
diff --git a/src/X1.Infrastructure/Configurations/Device/ScenarioConfiguration.cs b/src/X1.Infrastructure/Configurations/Device/ScenarioConfiguration.cs
new file mode 100644
index 0000000..0e025e9
--- /dev/null
+++ b/src/X1.Infrastructure/Configurations/Device/ScenarioConfiguration.cs
@@ -0,0 +1,30 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using CellularManagement.Domain.Entities.Device;
+
+namespace CellularManagement.Infrastructure.Configurations.Device;
+
+public class ScenarioConfiguration : IEntityTypeConfiguration
+{
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.ToTable("Scenarios", t => t.HasComment("场景表"));
+ builder.HasKey(s => s.Id);
+
+ // 配置索引
+ builder.HasIndex(s => s.Code).IsUnique().HasDatabaseName("IX_Scenarios_Code");
+ builder.HasIndex(s => s.NetworkConfigId).HasDatabaseName("IX_Scenarios_NetworkConfigId");
+
+ // 配置属性
+ builder.Property(s => s.Id).HasComment("场景ID");
+ builder.Property(s => s.Code).IsRequired().HasMaxLength(50).HasComment("场景编码");
+ builder.Property(s => s.Name).IsRequired().HasMaxLength(100).HasComment("场景名称");
+ builder.Property(s => s.NetworkConfigId).IsRequired().HasMaxLength(50).HasComment("网络配置ID");
+ builder.Property(s => s.Description).HasMaxLength(1000).HasComment("说明");
+ builder.Property(s => s.IsDisabled).IsRequired().HasComment("是否禁用");
+ builder.Property(s => s.CreatedAt).IsRequired().HasColumnType("timestamp with time zone").HasComment("创建时间");
+ builder.Property(s => s.UpdatedAt).HasColumnType("timestamp with time zone").HasComment("更新时间");
+ builder.Property(s => s.CreatedBy).IsRequired().HasMaxLength(50).HasComment("创建人");
+ builder.Property(s => s.UpdatedBy).HasMaxLength(50).HasComment("修改人");
+ }
+}
\ No newline at end of file
diff --git a/src/X1.Infrastructure/Context/AppDbContext.cs b/src/X1.Infrastructure/Context/AppDbContext.cs
index 264d3e4..0437a35 100644
--- a/src/X1.Infrastructure/Context/AppDbContext.cs
+++ b/src/X1.Infrastructure/Context/AppDbContext.cs
@@ -51,6 +51,11 @@ public class AppDbContext : IdentityDbContext
///
public DbSet NetworkConfigs { get; set; } = null!;
+ ///
+ /// 场景集合
+ ///
+ public DbSet Scenarios { get; set; } = null!;
+
///
/// 初始化数据库上下文
///
diff --git a/src/X1.Infrastructure/DependencyInjection.cs b/src/X1.Infrastructure/DependencyInjection.cs
index d4824dc..9cbfe4b 100644
--- a/src/X1.Infrastructure/DependencyInjection.cs
+++ b/src/X1.Infrastructure/DependencyInjection.cs
@@ -175,6 +175,7 @@ public static class DependencyInjection
services.AddScoped();
services.AddScoped();
services.AddScoped();
+ services.AddScoped();
return services;
}
diff --git a/src/X1.Infrastructure/Repositories/Device/ScenarioRepository.cs b/src/X1.Infrastructure/Repositories/Device/ScenarioRepository.cs
new file mode 100644
index 0000000..0fdcf7f
--- /dev/null
+++ b/src/X1.Infrastructure/Repositories/Device/ScenarioRepository.cs
@@ -0,0 +1,139 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Linq;
+using Microsoft.Extensions.Logging;
+using CellularManagement.Domain.Entities;
+using CellularManagement.Domain.Repositories;
+using CellularManagement.Infrastructure.Repositories.Base;
+using CellularManagement.Domain.Entities.Device;
+using CellularManagement.Domain.Repositories.Base;
+using CellularManagement.Domain.Repositories.Device;
+
+namespace CellularManagement.Infrastructure.Repositories.Device;
+
+///
+/// 场景仓储实现类
+///
+public class ScenarioRepository : BaseRepository, IScenarioRepository
+{
+ private readonly ILogger _logger;
+
+ ///
+ /// 初始化仓储
+ ///
+ public ScenarioRepository(
+ ICommandRepository commandRepository,
+ IQueryRepository queryRepository,
+ ILogger logger)
+ : base(commandRepository, queryRepository, logger)
+ {
+ _logger = logger;
+ }
+
+ ///
+ /// 添加场景
+ ///
+ public async Task AddScenarioAsync(Scenario scenario, CancellationToken cancellationToken = default)
+ {
+ var result = await CommandRepository.AddAsync(scenario, cancellationToken);
+ return result;
+ }
+
+ ///
+ /// 更新场景
+ ///
+ public void UpdateScenario(Scenario scenario)
+ {
+ CommandRepository.Update(scenario);
+ }
+
+ ///
+ /// 删除场景
+ ///
+ public async Task DeleteScenarioAsync(string id, CancellationToken cancellationToken = default)
+ {
+ await CommandRepository.DeleteByIdAsync(id, cancellationToken);
+ }
+
+ ///
+ /// 获取所有场景
+ ///
+ public async Task> GetAllScenariosAsync(CancellationToken cancellationToken = default)
+ {
+ var scenarios = await QueryRepository.GetAllAsync(cancellationToken: cancellationToken);
+ return scenarios.ToList();
+ }
+
+ ///
+ /// 根据ID获取场景
+ ///
+ public async Task GetScenarioByIdAsync(string id, CancellationToken cancellationToken = default)
+ {
+ return await QueryRepository.GetByIdAsync(id, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// 根据编码获取场景
+ ///
+ public async Task GetScenarioByCodeAsync(string code, CancellationToken cancellationToken = default)
+ {
+ return await QueryRepository.FirstOrDefaultAsync(s => s.Code == code, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// 搜索场景
+ ///
+ public async Task> SearchScenariosAsync(
+ string? keyword,
+ CancellationToken cancellationToken = default)
+ {
+ var query = await QueryRepository.FindAsync(s => true, cancellationToken: cancellationToken);
+
+ if (!string.IsNullOrWhiteSpace(keyword))
+ {
+ query = query.Where(s =>
+ s.Name.Contains(keyword) ||
+ s.Code.Contains(keyword) ||
+ (s.Description != null && s.Description.Contains(keyword)));
+ }
+
+ var scenarios = query;
+ return scenarios.ToList();
+ }
+
+ ///
+ /// 检查场景是否存在
+ ///
+ public async Task ExistsAsync(string id, CancellationToken cancellationToken = default)
+ {
+ return await QueryRepository.AnyAsync(s => s.Id == id, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// 检查编码是否存在
+ ///
+ public async Task CodeExistsAsync(string code, CancellationToken cancellationToken = default)
+ {
+ return await QueryRepository.AnyAsync(s => s.Code == code, cancellationToken: cancellationToken);
+ }
+
+ ///
+ /// 获取启用的场景
+ ///
+ public async Task> GetEnabledScenariosAsync(CancellationToken cancellationToken = default)
+ {
+ var scenarios = await QueryRepository.FindAsync(s => !s.IsDisabled, cancellationToken: cancellationToken);
+ return scenarios.ToList();
+ }
+
+ ///
+ /// 根据网络配置ID获取场景
+ ///
+ public async Task> GetScenariosByNetworkConfigIdAsync(string networkConfigId, CancellationToken cancellationToken = default)
+ {
+ var scenarios = await QueryRepository.FindAsync(s => s.NetworkConfigId == networkConfigId, cancellationToken: cancellationToken);
+ return scenarios.ToList();
+ }
+}
\ No newline at end of file
diff --git a/src/X1.Presentation/Controllers/ScenariosController.cs b/src/X1.Presentation/Controllers/ScenariosController.cs
new file mode 100644
index 0000000..ba90bcd
--- /dev/null
+++ b/src/X1.Presentation/Controllers/ScenariosController.cs
@@ -0,0 +1,175 @@
+using CellularManagement.Application.Features.Scenarios.Commands.CreateScenario;
+using CellularManagement.Application.Features.Scenarios.Commands.DeleteScenario;
+using CellularManagement.Application.Features.Scenarios.Commands.DisableScenario;
+using CellularManagement.Application.Features.Scenarios.Commands.EnableScenario;
+using CellularManagement.Application.Features.Scenarios.Commands.UpdateScenario;
+using CellularManagement.Application.Features.Scenarios.Queries.GetScenarioById;
+using CellularManagement.Application.Features.Scenarios.Queries.GetScenarios;
+using CellularManagement.Domain.Common;
+using CellularManagement.Presentation.Abstractions;
+using MediatR;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+
+namespace CellularManagement.Presentation.Controllers;
+
+///
+/// 场景管理控制器
+///
+[Route("api/scenarios")]
+[ApiController]
+[Authorize]
+public class ScenariosController : ApiController
+{
+ private readonly ILogger _logger;
+
+ ///
+ /// 初始化场景控制器
+ ///
+ public ScenariosController(IMediator mediator, ILogger logger)
+ : base(mediator)
+ {
+ _logger = logger;
+ }
+
+ ///
+ /// 获取场景列表
+ ///
+ [HttpGet]
+ public async Task> GetAll([FromQuery] GetScenariosQuery query)
+ {
+ _logger.LogInformation("开始获取场景列表,页码: {PageNumber}, 每页数量: {PageSize}, 搜索关键词: {SearchTerm}, 是否启用: {IsEnabled}, 网络配置ID: {NetworkConfigId}",
+ query.PageNumber, query.PageSize, query.SearchTerm, query.IsEnabled, query.NetworkConfigId);
+
+ var result = await mediator.Send(query);
+ if (!result.IsSuccess)
+ {
+ _logger.LogWarning("获取场景列表失败: {Message}", result.ErrorMessages);
+ return result;
+ }
+
+ _logger.LogInformation("成功获取场景列表,共 {Count} 条记录", result.Data?.TotalCount ?? 0);
+ return result;
+ }
+
+ ///
+ /// 获取场景详情
+ ///
+ [HttpGet("{id}")]
+ public async Task> GetById(string id)
+ {
+ _logger.LogInformation("开始获取场景详情,场景ID: {ScenarioId}", id);
+
+ var result = await mediator.Send(new GetScenarioByIdQuery { Id = id });
+ if (!result.IsSuccess)
+ {
+ _logger.LogWarning("获取场景详情失败: {Message}", result.ErrorMessages);
+ return result;
+ }
+
+ _logger.LogInformation("成功获取场景详情,场景ID: {ScenarioId}", id);
+ return result;
+ }
+
+ ///
+ /// 创建场景
+ ///
+ [HttpPost]
+ public async Task> Create([FromBody] CreateScenarioCommand command)
+ {
+ _logger.LogInformation("开始创建场景,场景编码: {Code}, 场景名称: {Name}", command.Code, command.Name);
+
+ var result = await mediator.Send(command);
+ if (!result.IsSuccess)
+ {
+ _logger.LogWarning("创建场景失败: {Message}", result.ErrorMessages);
+ return result;
+ }
+
+ _logger.LogInformation("成功创建场景,场景ID: {ScenarioId}", result.Data?.ScenarioId);
+ return result;
+ }
+
+ ///
+ /// 更新场景
+ ///
+ [HttpPut("{id}")]
+ public async Task> Update(string id, [FromBody] UpdateScenarioCommand command)
+ {
+ _logger.LogInformation("开始更新场景,场景ID: {ScenarioId}", id);
+
+ if (id != command.ScenarioId)
+ {
+ _logger.LogWarning("场景ID不匹配,路径ID: {PathId}, 命令ID: {CommandId}", id, command.ScenarioId);
+ return OperationResult.CreateFailure("场景ID不匹配");
+ }
+
+ var result = await mediator.Send(command);
+ if (!result.IsSuccess)
+ {
+ _logger.LogWarning("更新场景失败: {Message}", result.ErrorMessages);
+ return result;
+ }
+
+ _logger.LogInformation("成功更新场景,场景ID: {ScenarioId}", id);
+ return result;
+ }
+
+ ///
+ /// 删除场景
+ ///
+ [HttpDelete("{id}")]
+ public async Task> Delete(string id)
+ {
+ _logger.LogInformation("开始删除场景,场景ID: {ScenarioId}", id);
+
+ var result = await mediator.Send(new DeleteScenarioCommand { ScenarioId = id });
+ if (!result.IsSuccess)
+ {
+ _logger.LogWarning("删除场景失败: {Message}", result.ErrorMessages);
+ return result;
+ }
+
+ _logger.LogInformation("成功删除场景,场景ID: {ScenarioId}", id);
+ return result;
+ }
+
+ ///
+ /// 启用场景
+ ///
+ [HttpPost("{id}/enable")]
+ public async Task> Enable(string id)
+ {
+ _logger.LogInformation("开始启用场景,场景ID: {ScenarioId}", id);
+
+ var result = await mediator.Send(new EnableScenarioCommand { ScenarioId = id });
+ if (!result.IsSuccess)
+ {
+ _logger.LogWarning("启用场景失败: {Message}", result.ErrorMessages);
+ return result;
+ }
+
+ _logger.LogInformation("成功启用场景,场景ID: {ScenarioId}", id);
+ return result;
+ }
+
+ ///
+ /// 禁用场景
+ ///
+ [HttpPost("{id}/disable")]
+ public async Task> Disable(string id)
+ {
+ _logger.LogInformation("开始禁用场景,场景ID: {ScenarioId}", id);
+
+ var result = await mediator.Send(new DisableScenarioCommand { ScenarioId = id });
+ if (!result.IsSuccess)
+ {
+ _logger.LogWarning("禁用场景失败: {Message}", result.ErrorMessages);
+ return result;
+ }
+
+ _logger.LogInformation("成功禁用场景,场景ID: {ScenarioId}", id);
+ return result;
+ }
+}
\ No newline at end of file