diff --git a/src/X1.Application/Features/TestCases/Commands/CreateTestCase/CreateTestCaseCommand.cs b/src/X1.Application/Features/TestCases/Commands/CreateTestCase/CreateTestCaseCommand.cs new file mode 100644 index 0000000..858e3ab --- /dev/null +++ b/src/X1.Application/Features/TestCases/Commands/CreateTestCase/CreateTestCaseCommand.cs @@ -0,0 +1,91 @@ +using CellularManagement.Domain.Common; +using MediatR; +using System.ComponentModel.DataAnnotations; + +namespace CellularManagement.Application.Features.TestCases.Commands.CreateTestCase; + +/// +/// 创建用例命令 +/// +public class CreateTestCaseCommand : 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!; + + /// + /// 用户数 + /// + [Range(0, int.MaxValue, ErrorMessage = "用户数必须大于等于0")] + public int UserCount { get; set; } = 0; + + /// + /// 用户信息列表(JSON格式) + /// + [MaxLength(4000, ErrorMessage = "用户信息列表长度不能超过4000个字符")] + public string? UserInfoList { get; set; } + + /// + /// 时延阈值数 + /// + [Range(0, int.MaxValue, ErrorMessage = "时延阈值数必须大于等于0")] + public int LatencyThresholdCount { get; set; } = 0; + + /// + /// 时延阈值列表(JSON格式) + /// + [MaxLength(2000, ErrorMessage = "时延阈值列表长度不能超过2000个字符")] + public string? LatencyThresholdList { get; set; } + + /// + /// 吞吐量阈值数 + /// + [Range(0, int.MaxValue, ErrorMessage = "吞吐量阈值数必须大于等于0")] + public int ThroughputThresholdCount { get; set; } = 0; + + /// + /// 吞吐量阈值列表(JSON格式) + /// + [MaxLength(2000, ErrorMessage = "吞吐量阈值列表长度不能超过2000个字符")] + public string? ThroughputThresholdList { get; set; } + + /// + /// 用例说明 + /// + [MaxLength(1000, ErrorMessage = "用例说明长度不能超过1000个字符")] + public string? Description { get; set; } + + /// + /// 用例版本 + /// + [Required(ErrorMessage = "用例版本不能为空")] + [MaxLength(20, ErrorMessage = "用例版本长度不能超过20个字符")] + public string Version { get; set; } = null!; + + /// + /// 版本更新信息 + /// + [MaxLength(500, ErrorMessage = "版本更新信息长度不能超过500个字符")] + public string? VersionUpdateInfo { get; set; } + + /// + /// 是否禁用 + /// + public bool IsDisabled { get; set; } = false; + + /// + /// 备注 + /// + [MaxLength(1000, ErrorMessage = "备注长度不能超过1000个字符")] + public string? Remarks { get; set; } +} \ No newline at end of file diff --git a/src/X1.Application/Features/TestCases/Commands/CreateTestCase/CreateTestCaseCommandHandler.cs b/src/X1.Application/Features/TestCases/Commands/CreateTestCase/CreateTestCaseCommandHandler.cs new file mode 100644 index 0000000..4774956 --- /dev/null +++ b/src/X1.Application/Features/TestCases/Commands/CreateTestCase/CreateTestCaseCommandHandler.cs @@ -0,0 +1,123 @@ +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.TestCases.Commands.CreateTestCase; + +/// +/// 创建用例命令处理器 +/// +public class CreateTestCaseCommandHandler : IRequestHandler> +{ + private readonly ITestCaseRepository _testCaseRepository; + private readonly ILogger _logger; + private readonly IUnitOfWork _unitOfWork; + private readonly ICurrentUserService _currentUserService; + + /// + /// 初始化命令处理器 + /// + public CreateTestCaseCommandHandler( + ITestCaseRepository testCaseRepository, + ILogger logger, + IUnitOfWork unitOfWork, + ICurrentUserService currentUserService) + { + _testCaseRepository = testCaseRepository; + _logger = logger; + _unitOfWork = unitOfWork; + _currentUserService = currentUserService; + } + + /// + /// 处理创建用例命令 + /// + public async Task> Handle(CreateTestCaseCommand request, CancellationToken cancellationToken) + { + try + { + _logger.LogInformation("开始创建用例,用例编码: {Code}, 用例名称: {Name}, 版本: {Version}", + request.Code, request.Name, request.Version); + + // 检查用例编码是否已存在 + if (await _testCaseRepository.CodeExistsAsync(request.Code, cancellationToken)) + { + _logger.LogWarning("用例编码已存在: {Code}", request.Code); + return OperationResult.CreateFailure($"用例编码 {request.Code} 已存在"); + } + + // 检查用例版本是否已存在 + if (await _testCaseRepository.VersionExistsAsync(request.Version, cancellationToken)) + { + _logger.LogWarning("用例版本已存在: {Version}", request.Version); + return OperationResult.CreateFailure($"用例版本 {request.Version} 已存在"); + } + + // 获取当前用户ID + var currentUserId = _currentUserService.GetCurrentUserId(); + if (string.IsNullOrEmpty(currentUserId)) + { + _logger.LogError("无法获取当前用户ID,用户可能未认证"); + return OperationResult.CreateFailure("用户未认证,无法创建用例"); + } + + // 创建用例实体 + var testCase = TestCase.Create( + code: request.Code, + name: request.Name, + version: request.Version, + createdBy: currentUserId, + userCount: request.UserCount, + userInfoList: request.UserInfoList, + latencyThresholdCount: request.LatencyThresholdCount, + latencyThresholdList: request.LatencyThresholdList, + throughputThresholdCount: request.ThroughputThresholdCount, + throughputThresholdList: request.ThroughputThresholdList, + description: request.Description, + versionUpdateInfo: request.VersionUpdateInfo, + isDisabled: request.IsDisabled, + remarks: request.Remarks); + + // 保存用例 + await _testCaseRepository.AddTestCaseAsync(testCase, cancellationToken); + + // 保存更改到数据库 + await _unitOfWork.SaveChangesAsync(cancellationToken); + + // 构建响应 + var response = new CreateTestCaseResponse + { + TestCaseId = testCase.Id, + Code = testCase.Code, + Name = testCase.Name, + UserCount = testCase.UserCount, + UserInfoList = testCase.UserInfoList, + LatencyThresholdCount = testCase.LatencyThresholdCount, + LatencyThresholdList = testCase.LatencyThresholdList, + ThroughputThresholdCount = testCase.ThroughputThresholdCount, + ThroughputThresholdList = testCase.ThroughputThresholdList, + Description = testCase.Description, + Version = testCase.Version, + VersionUpdateInfo = testCase.VersionUpdateInfo, + IsDisabled = testCase.IsDisabled, + Remarks = testCase.Remarks, + CreatedAt = testCase.CreatedAt, + CreatedBy = testCase.CreatedBy + }; + + _logger.LogInformation("用例创建成功,用例ID: {TestCaseId}, 用例编码: {Code}, 用例名称: {Name}, 版本: {Version}", + testCase.Id, testCase.Code, testCase.Name, testCase.Version); + return OperationResult.CreateSuccess(response); + } + catch (Exception ex) + { + _logger.LogError(ex, "创建用例时发生错误,用例编码: {Code}, 用例名称: {Name}, 版本: {Version}", + request.Code, request.Name, request.Version); + return OperationResult.CreateFailure($"创建用例时发生错误: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/src/X1.Application/Features/TestCases/Commands/CreateTestCase/CreateTestCaseResponse.cs b/src/X1.Application/Features/TestCases/Commands/CreateTestCase/CreateTestCaseResponse.cs new file mode 100644 index 0000000..424cea4 --- /dev/null +++ b/src/X1.Application/Features/TestCases/Commands/CreateTestCase/CreateTestCaseResponse.cs @@ -0,0 +1,87 @@ +namespace CellularManagement.Application.Features.TestCases.Commands.CreateTestCase; + +/// +/// 创建用例响应 +/// +public class CreateTestCaseResponse +{ + /// + /// 用例ID + /// + public string TestCaseId { get; set; } = null!; + + /// + /// 用例编码 + /// + public string Code { get; set; } = null!; + + /// + /// 用例名称 + /// + public string Name { get; set; } = null!; + + /// + /// 用户数 + /// + public int UserCount { get; set; } + + /// + /// 用户信息列表 + /// + public string? UserInfoList { get; set; } + + /// + /// 时延阈值数 + /// + public int LatencyThresholdCount { get; set; } + + /// + /// 时延阈值列表 + /// + public string? LatencyThresholdList { get; set; } + + /// + /// 吞吐量阈值数 + /// + public int ThroughputThresholdCount { get; set; } + + /// + /// 吞吐量阈值列表 + /// + public string? ThroughputThresholdList { get; set; } + + /// + /// 用例说明 + /// + public string? Description { get; set; } + + /// + /// 用例版本 + /// + public string Version { get; set; } = null!; + + /// + /// 版本更新信息 + /// + public string? VersionUpdateInfo { get; set; } + + /// + /// 是否禁用 + /// + public bool IsDisabled { get; set; } + + /// + /// 备注 + /// + public string? Remarks { 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/TestCases/Commands/DeleteTestCase/DeleteTestCaseCommand.cs b/src/X1.Application/Features/TestCases/Commands/DeleteTestCase/DeleteTestCaseCommand.cs new file mode 100644 index 0000000..896b6f6 --- /dev/null +++ b/src/X1.Application/Features/TestCases/Commands/DeleteTestCase/DeleteTestCaseCommand.cs @@ -0,0 +1,18 @@ +using CellularManagement.Domain.Common; +using MediatR; +using System.ComponentModel.DataAnnotations; + +namespace CellularManagement.Application.Features.TestCases.Commands.DeleteTestCase; + +/// +/// 删除用例命令 +/// +public class DeleteTestCaseCommand : IRequest> +{ + /// + /// 用例ID + /// + [Required(ErrorMessage = "用例ID不能为空")] + [MaxLength(50, ErrorMessage = "用例ID长度不能超过50个字符")] + public string TestCaseId { get; set; } = null!; +} \ No newline at end of file diff --git a/src/X1.Application/Features/TestCases/Commands/DeleteTestCase/DeleteTestCaseCommandHandler.cs b/src/X1.Application/Features/TestCases/Commands/DeleteTestCase/DeleteTestCaseCommandHandler.cs new file mode 100644 index 0000000..97a3dd7 --- /dev/null +++ b/src/X1.Application/Features/TestCases/Commands/DeleteTestCase/DeleteTestCaseCommandHandler.cs @@ -0,0 +1,102 @@ +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.TestCases.Commands.DeleteTestCase; + +/// +/// 删除用例命令处理器 +/// +public class DeleteTestCaseCommandHandler : IRequestHandler> +{ + private readonly ITestCaseRepository _testCaseRepository; + private readonly ILogger _logger; + private readonly IUnitOfWork _unitOfWork; + private readonly ICurrentUserService _currentUserService; + + /// + /// 初始化命令处理器 + /// + public DeleteTestCaseCommandHandler( + ITestCaseRepository testCaseRepository, + ILogger logger, + IUnitOfWork unitOfWork, + ICurrentUserService currentUserService) + { + _testCaseRepository = testCaseRepository; + _logger = logger; + _unitOfWork = unitOfWork; + _currentUserService = currentUserService; + } + + /// + /// 处理删除用例命令 + /// + public async Task> Handle(DeleteTestCaseCommand request, CancellationToken cancellationToken) + { + try + { + _logger.LogInformation("开始删除用例,用例ID: {TestCaseId}", request.TestCaseId); + + // 检查用例是否存在 + var existingTestCase = await _testCaseRepository.GetTestCaseByIdAsync(request.TestCaseId, cancellationToken); + if (existingTestCase == null) + { + _logger.LogWarning("用例不存在: {TestCaseId}", request.TestCaseId); + return OperationResult.CreateFailure($"用例ID {request.TestCaseId} 不存在"); + } + + // 检查用例是否已被软删除 + if (existingTestCase.IsDeleted) + { + _logger.LogWarning("用例已被删除: {TestCaseId}", request.TestCaseId); + return OperationResult.CreateFailure($"用例ID {request.TestCaseId} 已被删除"); + } + + // 获取当前用户ID + var currentUserId = _currentUserService.GetCurrentUserId(); + if (string.IsNullOrEmpty(currentUserId)) + { + _logger.LogError("无法获取当前用户ID,用户可能未认证"); + return OperationResult.CreateFailure("用户未认证,无法删除用例"); + } + + // 软删除用例 + existingTestCase.SoftDelete(); + existingTestCase.Update( + code: existingTestCase.Code, + name: existingTestCase.Name, + version: existingTestCase.Version, + updatedBy: currentUserId, + userCount: existingTestCase.UserCount, + userInfoList: existingTestCase.UserInfoList, + latencyThresholdCount: existingTestCase.LatencyThresholdCount, + latencyThresholdList: existingTestCase.LatencyThresholdList, + throughputThresholdCount: existingTestCase.ThroughputThresholdCount, + throughputThresholdList: existingTestCase.ThroughputThresholdList, + description: existingTestCase.Description, + versionUpdateInfo: existingTestCase.VersionUpdateInfo, + isDisabled: existingTestCase.IsDisabled, + remarks: existingTestCase.Remarks); + + // 保存更改 + _testCaseRepository.UpdateTestCase(existingTestCase); + + // 保存更改到数据库 + await _unitOfWork.SaveChangesAsync(cancellationToken); + + _logger.LogInformation("用例删除成功,用例ID: {TestCaseId}, 用例编码: {Code}, 用例名称: {Name}, 版本: {Version}", + existingTestCase.Id, existingTestCase.Code, existingTestCase.Name, existingTestCase.Version); + return OperationResult.CreateSuccess(true); + } + catch (Exception ex) + { + _logger.LogError(ex, "删除用例时发生错误,用例ID: {TestCaseId}", request.TestCaseId); + return OperationResult.CreateFailure($"删除用例时发生错误: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/src/X1.Application/Features/TestCases/Commands/DisableTestCase/DisableTestCaseCommand.cs b/src/X1.Application/Features/TestCases/Commands/DisableTestCase/DisableTestCaseCommand.cs new file mode 100644 index 0000000..50f2300 --- /dev/null +++ b/src/X1.Application/Features/TestCases/Commands/DisableTestCase/DisableTestCaseCommand.cs @@ -0,0 +1,18 @@ +using CellularManagement.Domain.Common; +using MediatR; +using System.ComponentModel.DataAnnotations; + +namespace CellularManagement.Application.Features.TestCases.Commands.DisableTestCase; + +/// +/// 禁用用例命令 +/// +public class DisableTestCaseCommand : IRequest> +{ + /// + /// 用例ID + /// + [Required(ErrorMessage = "用例ID不能为空")] + [MaxLength(50, ErrorMessage = "用例ID长度不能超过50个字符")] + public string TestCaseId { get; set; } = null!; +} \ No newline at end of file diff --git a/src/X1.Application/Features/TestCases/Commands/DisableTestCase/DisableTestCaseCommandHandler.cs b/src/X1.Application/Features/TestCases/Commands/DisableTestCase/DisableTestCaseCommandHandler.cs new file mode 100644 index 0000000..7938d05 --- /dev/null +++ b/src/X1.Application/Features/TestCases/Commands/DisableTestCase/DisableTestCaseCommandHandler.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.TestCases.Commands.DisableTestCase; + +/// +/// 禁用用例命令处理器 +/// +public class DisableTestCaseCommandHandler : IRequestHandler> +{ + private readonly ITestCaseRepository _testCaseRepository; + private readonly ILogger _logger; + private readonly IUnitOfWork _unitOfWork; + private readonly ICurrentUserService _currentUserService; + + /// + /// 初始化命令处理器 + /// + public DisableTestCaseCommandHandler( + ITestCaseRepository testCaseRepository, + ILogger logger, + IUnitOfWork unitOfWork, + ICurrentUserService currentUserService) + { + _testCaseRepository = testCaseRepository; + _logger = logger; + _unitOfWork = unitOfWork; + _currentUserService = currentUserService; + } + + /// + /// 处理禁用用例命令 + /// + public async Task> Handle(DisableTestCaseCommand request, CancellationToken cancellationToken) + { + try + { + _logger.LogInformation("开始禁用用例,用例ID: {TestCaseId}", request.TestCaseId); + + // 检查用例是否存在 + var existingTestCase = await _testCaseRepository.GetTestCaseByIdAsync(request.TestCaseId, cancellationToken); + if (existingTestCase == null) + { + _logger.LogWarning("用例不存在: {TestCaseId}", request.TestCaseId); + return OperationResult.CreateFailure($"用例ID {request.TestCaseId} 不存在"); + } + + // 检查用例是否已被软删除 + if (existingTestCase.IsDeleted) + { + _logger.LogWarning("用例已被删除: {TestCaseId}", request.TestCaseId); + return OperationResult.CreateFailure($"用例ID {request.TestCaseId} 已被删除"); + } + + // 检查用例是否已经禁用 + if (existingTestCase.IsDisabled) + { + _logger.LogWarning("用例已经禁用: {TestCaseId}", request.TestCaseId); + return OperationResult.CreateFailure($"用例ID {request.TestCaseId} 已经禁用"); + } + + // 获取当前用户ID + var currentUserId = _currentUserService.GetCurrentUserId(); + if (string.IsNullOrEmpty(currentUserId)) + { + _logger.LogError("无法获取当前用户ID,用户可能未认证"); + return OperationResult.CreateFailure("用户未认证,无法禁用用例"); + } + + // 禁用用例 + existingTestCase.Disable(); + existingTestCase.Update( + code: existingTestCase.Code, + name: existingTestCase.Name, + version: existingTestCase.Version, + updatedBy: currentUserId, + userCount: existingTestCase.UserCount, + userInfoList: existingTestCase.UserInfoList, + latencyThresholdCount: existingTestCase.LatencyThresholdCount, + latencyThresholdList: existingTestCase.LatencyThresholdList, + throughputThresholdCount: existingTestCase.ThroughputThresholdCount, + throughputThresholdList: existingTestCase.ThroughputThresholdList, + description: existingTestCase.Description, + versionUpdateInfo: existingTestCase.VersionUpdateInfo, + isDisabled: true, + remarks: existingTestCase.Remarks); + + // 保存更改 + _testCaseRepository.UpdateTestCase(existingTestCase); + + // 保存更改到数据库 + await _unitOfWork.SaveChangesAsync(cancellationToken); + + _logger.LogInformation("用例禁用成功,用例ID: {TestCaseId}, 用例编码: {Code}, 用例名称: {Name}, 版本: {Version}", + existingTestCase.Id, existingTestCase.Code, existingTestCase.Name, existingTestCase.Version); + return OperationResult.CreateSuccess(true); + } + catch (Exception ex) + { + _logger.LogError(ex, "禁用用例时发生错误,用例ID: {TestCaseId}", request.TestCaseId); + return OperationResult.CreateFailure($"禁用用例时发生错误: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/src/X1.Application/Features/TestCases/Commands/EnableTestCase/EnableTestCaseCommand.cs b/src/X1.Application/Features/TestCases/Commands/EnableTestCase/EnableTestCaseCommand.cs new file mode 100644 index 0000000..4357532 --- /dev/null +++ b/src/X1.Application/Features/TestCases/Commands/EnableTestCase/EnableTestCaseCommand.cs @@ -0,0 +1,18 @@ +using CellularManagement.Domain.Common; +using MediatR; +using System.ComponentModel.DataAnnotations; + +namespace CellularManagement.Application.Features.TestCases.Commands.EnableTestCase; + +/// +/// 启用用例命令 +/// +public class EnableTestCaseCommand : IRequest> +{ + /// + /// 用例ID + /// + [Required(ErrorMessage = "用例ID不能为空")] + [MaxLength(50, ErrorMessage = "用例ID长度不能超过50个字符")] + public string TestCaseId { get; set; } = null!; +} \ No newline at end of file diff --git a/src/X1.Application/Features/TestCases/Commands/EnableTestCase/EnableTestCaseCommandHandler.cs b/src/X1.Application/Features/TestCases/Commands/EnableTestCase/EnableTestCaseCommandHandler.cs new file mode 100644 index 0000000..a5a7e6f --- /dev/null +++ b/src/X1.Application/Features/TestCases/Commands/EnableTestCase/EnableTestCaseCommandHandler.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.TestCases.Commands.EnableTestCase; + +/// +/// 启用用例命令处理器 +/// +public class EnableTestCaseCommandHandler : IRequestHandler> +{ + private readonly ITestCaseRepository _testCaseRepository; + private readonly ILogger _logger; + private readonly IUnitOfWork _unitOfWork; + private readonly ICurrentUserService _currentUserService; + + /// + /// 初始化命令处理器 + /// + public EnableTestCaseCommandHandler( + ITestCaseRepository testCaseRepository, + ILogger logger, + IUnitOfWork unitOfWork, + ICurrentUserService currentUserService) + { + _testCaseRepository = testCaseRepository; + _logger = logger; + _unitOfWork = unitOfWork; + _currentUserService = currentUserService; + } + + /// + /// 处理启用用例命令 + /// + public async Task> Handle(EnableTestCaseCommand request, CancellationToken cancellationToken) + { + try + { + _logger.LogInformation("开始启用用例,用例ID: {TestCaseId}", request.TestCaseId); + + // 检查用例是否存在 + var existingTestCase = await _testCaseRepository.GetTestCaseByIdAsync(request.TestCaseId, cancellationToken); + if (existingTestCase == null) + { + _logger.LogWarning("用例不存在: {TestCaseId}", request.TestCaseId); + return OperationResult.CreateFailure($"用例ID {request.TestCaseId} 不存在"); + } + + // 检查用例是否已被软删除 + if (existingTestCase.IsDeleted) + { + _logger.LogWarning("用例已被删除: {TestCaseId}", request.TestCaseId); + return OperationResult.CreateFailure($"用例ID {request.TestCaseId} 已被删除"); + } + + // 检查用例是否已经启用 + if (!existingTestCase.IsDisabled) + { + _logger.LogWarning("用例已经启用: {TestCaseId}", request.TestCaseId); + return OperationResult.CreateFailure($"用例ID {request.TestCaseId} 已经启用"); + } + + // 获取当前用户ID + var currentUserId = _currentUserService.GetCurrentUserId(); + if (string.IsNullOrEmpty(currentUserId)) + { + _logger.LogError("无法获取当前用户ID,用户可能未认证"); + return OperationResult.CreateFailure("用户未认证,无法启用用例"); + } + + // 启用用例 + existingTestCase.Enable(); + existingTestCase.Update( + code: existingTestCase.Code, + name: existingTestCase.Name, + version: existingTestCase.Version, + updatedBy: currentUserId, + userCount: existingTestCase.UserCount, + userInfoList: existingTestCase.UserInfoList, + latencyThresholdCount: existingTestCase.LatencyThresholdCount, + latencyThresholdList: existingTestCase.LatencyThresholdList, + throughputThresholdCount: existingTestCase.ThroughputThresholdCount, + throughputThresholdList: existingTestCase.ThroughputThresholdList, + description: existingTestCase.Description, + versionUpdateInfo: existingTestCase.VersionUpdateInfo, + isDisabled: false, + remarks: existingTestCase.Remarks); + + // 保存更改 + _testCaseRepository.UpdateTestCase(existingTestCase); + + // 保存更改到数据库 + await _unitOfWork.SaveChangesAsync(cancellationToken); + + _logger.LogInformation("用例启用成功,用例ID: {TestCaseId}, 用例编码: {Code}, 用例名称: {Name}, 版本: {Version}", + existingTestCase.Id, existingTestCase.Code, existingTestCase.Name, existingTestCase.Version); + return OperationResult.CreateSuccess(true); + } + catch (Exception ex) + { + _logger.LogError(ex, "启用用例时发生错误,用例ID: {TestCaseId}", request.TestCaseId); + return OperationResult.CreateFailure($"启用用例时发生错误: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/src/X1.Application/Features/TestCases/Commands/UpdateTestCase/UpdateTestCaseCommand.cs b/src/X1.Application/Features/TestCases/Commands/UpdateTestCase/UpdateTestCaseCommand.cs new file mode 100644 index 0000000..df0d27a --- /dev/null +++ b/src/X1.Application/Features/TestCases/Commands/UpdateTestCase/UpdateTestCaseCommand.cs @@ -0,0 +1,98 @@ +using CellularManagement.Domain.Common; +using MediatR; +using System.ComponentModel.DataAnnotations; + +namespace CellularManagement.Application.Features.TestCases.Commands.UpdateTestCase; + +/// +/// 更新用例命令 +/// +public class UpdateTestCaseCommand : IRequest> +{ + /// + /// 用例ID + /// + [Required(ErrorMessage = "用例ID不能为空")] + [MaxLength(50, ErrorMessage = "用例ID长度不能超过50个字符")] + public string TestCaseId { 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!; + + /// + /// 用户数 + /// + [Range(0, int.MaxValue, ErrorMessage = "用户数必须大于等于0")] + public int UserCount { get; set; } = 0; + + /// + /// 用户信息列表(JSON格式) + /// + [MaxLength(4000, ErrorMessage = "用户信息列表长度不能超过4000个字符")] + public string? UserInfoList { get; set; } + + /// + /// 时延阈值数 + /// + [Range(0, int.MaxValue, ErrorMessage = "时延阈值数必须大于等于0")] + public int LatencyThresholdCount { get; set; } = 0; + + /// + /// 时延阈值列表(JSON格式) + /// + [MaxLength(2000, ErrorMessage = "时延阈值列表长度不能超过2000个字符")] + public string? LatencyThresholdList { get; set; } + + /// + /// 吞吐量阈值数 + /// + [Range(0, int.MaxValue, ErrorMessage = "吞吐量阈值数必须大于等于0")] + public int ThroughputThresholdCount { get; set; } = 0; + + /// + /// 吞吐量阈值列表(JSON格式) + /// + [MaxLength(2000, ErrorMessage = "吞吐量阈值列表长度不能超过2000个字符")] + public string? ThroughputThresholdList { get; set; } + + /// + /// 用例说明 + /// + [MaxLength(1000, ErrorMessage = "用例说明长度不能超过1000个字符")] + public string? Description { get; set; } + + /// + /// 用例版本 + /// + [Required(ErrorMessage = "用例版本不能为空")] + [MaxLength(20, ErrorMessage = "用例版本长度不能超过20个字符")] + public string Version { get; set; } = null!; + + /// + /// 版本更新信息 + /// + [MaxLength(500, ErrorMessage = "版本更新信息长度不能超过500个字符")] + public string? VersionUpdateInfo { get; set; } + + /// + /// 是否禁用 + /// + public bool IsDisabled { get; set; } = false; + + /// + /// 备注 + /// + [MaxLength(1000, ErrorMessage = "备注长度不能超过1000个字符")] + public string? Remarks { get; set; } +} \ No newline at end of file diff --git a/src/X1.Application/Features/TestCases/Commands/UpdateTestCase/UpdateTestCaseCommandHandler.cs b/src/X1.Application/Features/TestCases/Commands/UpdateTestCase/UpdateTestCaseCommandHandler.cs new file mode 100644 index 0000000..707ebe1 --- /dev/null +++ b/src/X1.Application/Features/TestCases/Commands/UpdateTestCase/UpdateTestCaseCommandHandler.cs @@ -0,0 +1,133 @@ +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.TestCases.Commands.UpdateTestCase; + +/// +/// 更新用例命令处理器 +/// +public class UpdateTestCaseCommandHandler : IRequestHandler> +{ + private readonly ITestCaseRepository _testCaseRepository; + private readonly ILogger _logger; + private readonly IUnitOfWork _unitOfWork; + private readonly ICurrentUserService _currentUserService; + + /// + /// 初始化命令处理器 + /// + public UpdateTestCaseCommandHandler( + ITestCaseRepository testCaseRepository, + ILogger logger, + IUnitOfWork unitOfWork, + ICurrentUserService currentUserService) + { + _testCaseRepository = testCaseRepository; + _logger = logger; + _unitOfWork = unitOfWork; + _currentUserService = currentUserService; + } + + /// + /// 处理更新用例命令 + /// + public async Task> Handle(UpdateTestCaseCommand request, CancellationToken cancellationToken) + { + try + { + _logger.LogInformation("开始更新用例,用例ID: {TestCaseId}, 用例编码: {Code}, 用例名称: {Name}, 版本: {Version}", + request.TestCaseId, request.Code, request.Name, request.Version); + + // 检查用例是否存在 + var existingTestCase = await _testCaseRepository.GetTestCaseByIdAsync(request.TestCaseId, cancellationToken); + if (existingTestCase == null) + { + _logger.LogWarning("用例不存在: {TestCaseId}", request.TestCaseId); + return OperationResult.CreateFailure($"用例ID {request.TestCaseId} 不存在"); + } + + // 检查用例编码是否已被其他用例使用 + var testCaseWithSameCode = await _testCaseRepository.GetTestCaseByCodeAsync(request.Code, cancellationToken); + if (testCaseWithSameCode != null && testCaseWithSameCode.Id != request.TestCaseId) + { + _logger.LogWarning("用例编码已被其他用例使用: {Code}", request.Code); + return OperationResult.CreateFailure($"用例编码 {request.Code} 已被其他用例使用"); + } + + // 检查用例版本是否已被其他用例使用 + var testCaseWithSameVersion = await _testCaseRepository.GetTestCaseByVersionAsync(request.Version, cancellationToken); + if (testCaseWithSameVersion != null && testCaseWithSameVersion.Id != request.TestCaseId) + { + _logger.LogWarning("用例版本已被其他用例使用: {Version}", request.Version); + return OperationResult.CreateFailure($"用例版本 {request.Version} 已被其他用例使用"); + } + + // 获取当前用户ID + var currentUserId = _currentUserService.GetCurrentUserId(); + if (string.IsNullOrEmpty(currentUserId)) + { + _logger.LogError("无法获取当前用户ID,用户可能未认证"); + return OperationResult.CreateFailure("用户未认证,无法更新用例"); + } + + // 更新用例 + existingTestCase.Update( + code: request.Code, + name: request.Name, + version: request.Version, + updatedBy: currentUserId, + userCount: request.UserCount, + userInfoList: request.UserInfoList, + latencyThresholdCount: request.LatencyThresholdCount, + latencyThresholdList: request.LatencyThresholdList, + throughputThresholdCount: request.ThroughputThresholdCount, + throughputThresholdList: request.ThroughputThresholdList, + description: request.Description, + versionUpdateInfo: request.VersionUpdateInfo, + isDisabled: request.IsDisabled, + remarks: request.Remarks); + + // 保存更改 + _testCaseRepository.UpdateTestCase(existingTestCase); + + // 保存更改到数据库 + await _unitOfWork.SaveChangesAsync(cancellationToken); + + // 构建响应 + var response = new UpdateTestCaseResponse + { + TestCaseId = existingTestCase.Id, + Code = existingTestCase.Code, + Name = existingTestCase.Name, + UserCount = existingTestCase.UserCount, + UserInfoList = existingTestCase.UserInfoList, + LatencyThresholdCount = existingTestCase.LatencyThresholdCount, + LatencyThresholdList = existingTestCase.LatencyThresholdList, + ThroughputThresholdCount = existingTestCase.ThroughputThresholdCount, + ThroughputThresholdList = existingTestCase.ThroughputThresholdList, + Description = existingTestCase.Description, + Version = existingTestCase.Version, + VersionUpdateInfo = existingTestCase.VersionUpdateInfo, + IsDisabled = existingTestCase.IsDisabled, + Remarks = existingTestCase.Remarks, + UpdatedAt = existingTestCase.UpdatedAt, + UpdatedBy = existingTestCase.UpdatedBy + }; + + _logger.LogInformation("用例更新成功,用例ID: {TestCaseId}, 用例编码: {Code}, 用例名称: {Name}, 版本: {Version}", + existingTestCase.Id, existingTestCase.Code, existingTestCase.Name, existingTestCase.Version); + return OperationResult.CreateSuccess(response); + } + catch (Exception ex) + { + _logger.LogError(ex, "更新用例时发生错误,用例ID: {TestCaseId}, 用例编码: {Code}, 用例名称: {Name}, 版本: {Version}", + request.TestCaseId, request.Code, request.Name, request.Version); + return OperationResult.CreateFailure($"更新用例时发生错误: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/src/X1.Application/Features/TestCases/Commands/UpdateTestCase/UpdateTestCaseResponse.cs b/src/X1.Application/Features/TestCases/Commands/UpdateTestCase/UpdateTestCaseResponse.cs new file mode 100644 index 0000000..28273d8 --- /dev/null +++ b/src/X1.Application/Features/TestCases/Commands/UpdateTestCase/UpdateTestCaseResponse.cs @@ -0,0 +1,87 @@ +namespace CellularManagement.Application.Features.TestCases.Commands.UpdateTestCase; + +/// +/// 更新用例响应 +/// +public class UpdateTestCaseResponse +{ + /// + /// 用例ID + /// + public string TestCaseId { get; set; } = null!; + + /// + /// 用例编码 + /// + public string Code { get; set; } = null!; + + /// + /// 用例名称 + /// + public string Name { get; set; } = null!; + + /// + /// 用户数 + /// + public int UserCount { get; set; } + + /// + /// 用户信息列表 + /// + public string? UserInfoList { get; set; } + + /// + /// 时延阈值数 + /// + public int LatencyThresholdCount { get; set; } + + /// + /// 时延阈值列表 + /// + public string? LatencyThresholdList { get; set; } + + /// + /// 吞吐量阈值数 + /// + public int ThroughputThresholdCount { get; set; } + + /// + /// 吞吐量阈值列表 + /// + public string? ThroughputThresholdList { get; set; } + + /// + /// 用例说明 + /// + public string? Description { get; set; } + + /// + /// 用例版本 + /// + public string Version { get; set; } = null!; + + /// + /// 版本更新信息 + /// + public string? VersionUpdateInfo { get; set; } + + /// + /// 是否禁用 + /// + public bool IsDisabled { get; set; } + + /// + /// 备注 + /// + public string? Remarks { 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/TestCases/Queries/GetTestCaseById/GetTestCaseByIdQuery.cs b/src/X1.Application/Features/TestCases/Queries/GetTestCaseById/GetTestCaseByIdQuery.cs new file mode 100644 index 0000000..83f7b0f --- /dev/null +++ b/src/X1.Application/Features/TestCases/Queries/GetTestCaseById/GetTestCaseByIdQuery.cs @@ -0,0 +1,14 @@ +using MediatR; + +namespace CellularManagement.Application.Features.TestCases.Queries.GetTestCaseById; + +/// +/// 根据ID获取用例查询 +/// +public class GetTestCaseByIdQuery : IRequest +{ + /// + /// 用例ID + /// + public string Id { get; set; } = null!; +} \ No newline at end of file diff --git a/src/X1.Application/Features/TestCases/Queries/GetTestCaseById/GetTestCaseByIdQueryHandler.cs b/src/X1.Application/Features/TestCases/Queries/GetTestCaseById/GetTestCaseByIdQueryHandler.cs new file mode 100644 index 0000000..4be08ff --- /dev/null +++ b/src/X1.Application/Features/TestCases/Queries/GetTestCaseById/GetTestCaseByIdQueryHandler.cs @@ -0,0 +1,76 @@ +using MediatR; +using Microsoft.Extensions.Logging; +using CellularManagement.Domain.Repositories.Device; + +namespace CellularManagement.Application.Features.TestCases.Queries.GetTestCaseById; + +/// +/// 根据ID获取用例查询处理器 +/// +public class GetTestCaseByIdQueryHandler : IRequestHandler +{ + private readonly ITestCaseRepository _testCaseRepository; + private readonly ILogger _logger; + + /// + /// 初始化查询处理器 + /// + public GetTestCaseByIdQueryHandler( + ITestCaseRepository testCaseRepository, + ILogger logger) + { + _testCaseRepository = testCaseRepository; + _logger = logger; + } + + /// + /// 处理根据ID获取用例查询 + /// + public async Task Handle(GetTestCaseByIdQuery request, CancellationToken cancellationToken) + { + try + { + _logger.LogInformation("开始获取用例详情,用例ID: {TestCaseId}", request.Id); + + // 获取用例 + var testCase = await _testCaseRepository.GetTestCaseByIdAsync(request.Id, cancellationToken); + if (testCase == null) + { + _logger.LogWarning("用例不存在: {TestCaseId}", request.Id); + return null; + } + + // 转换为响应 + var response = new GetTestCaseByIdResponse + { + Id = testCase.Id, + Code = testCase.Code, + Name = testCase.Name, + UserCount = testCase.UserCount, + UserInfoList = testCase.UserInfoList, + LatencyThresholdCount = testCase.LatencyThresholdCount, + LatencyThresholdList = testCase.LatencyThresholdList, + ThroughputThresholdCount = testCase.ThroughputThresholdCount, + ThroughputThresholdList = testCase.ThroughputThresholdList, + Description = testCase.Description, + Version = testCase.Version, + VersionUpdateInfo = testCase.VersionUpdateInfo, + IsDisabled = testCase.IsDisabled, + Remarks = testCase.Remarks, + CreatedAt = testCase.CreatedAt, + CreatedBy = testCase.CreatedBy, + UpdatedAt = testCase.UpdatedAt, + UpdatedBy = testCase.UpdatedBy + }; + + _logger.LogInformation("成功获取用例详情,用例ID: {TestCaseId}, 用例编码: {Code}, 用例名称: {Name}", + testCase.Id, testCase.Code, testCase.Name); + return response; + } + catch (Exception ex) + { + _logger.LogError(ex, "获取用例详情时发生错误,用例ID: {TestCaseId}", request.Id); + throw; + } + } +} \ No newline at end of file diff --git a/src/X1.Application/Features/TestCases/Queries/GetTestCaseById/GetTestCaseByIdResponse.cs b/src/X1.Application/Features/TestCases/Queries/GetTestCaseById/GetTestCaseByIdResponse.cs new file mode 100644 index 0000000..e020e66 --- /dev/null +++ b/src/X1.Application/Features/TestCases/Queries/GetTestCaseById/GetTestCaseByIdResponse.cs @@ -0,0 +1,97 @@ +namespace CellularManagement.Application.Features.TestCases.Queries.GetTestCaseById; + +/// +/// 根据ID获取用例响应 +/// +public class GetTestCaseByIdResponse +{ + /// + /// 用例ID + /// + public string Id { get; set; } = null!; + + /// + /// 用例编码 + /// + public string Code { get; set; } = null!; + + /// + /// 用例名称 + /// + public string Name { get; set; } = null!; + + /// + /// 用户数 + /// + public int UserCount { get; set; } + + /// + /// 用户信息列表 + /// + public string? UserInfoList { get; set; } + + /// + /// 时延阈值数 + /// + public int LatencyThresholdCount { get; set; } + + /// + /// 时延阈值列表 + /// + public string? LatencyThresholdList { get; set; } + + /// + /// 吞吐量阈值数 + /// + public int ThroughputThresholdCount { get; set; } + + /// + /// 吞吐量阈值列表 + /// + public string? ThroughputThresholdList { get; set; } + + /// + /// 用例说明 + /// + public string? Description { get; set; } + + /// + /// 用例版本 + /// + public string Version { get; set; } = null!; + + /// + /// 版本更新信息 + /// + public string? VersionUpdateInfo { get; set; } + + /// + /// 是否禁用 + /// + public bool IsDisabled { get; set; } + + /// + /// 备注 + /// + public string? Remarks { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreatedAt { get; set; } + + /// + /// 创建人 + /// + public string CreatedBy { get; set; } = null!; + + /// + /// 更新时间 + /// + public DateTime? UpdatedAt { get; set; } + + /// + /// 修改人 + /// + public string? UpdatedBy { get; set; } +} \ No newline at end of file diff --git a/src/X1.Application/Features/TestCases/Queries/GetTestCases/GetTestCasesQuery.cs b/src/X1.Application/Features/TestCases/Queries/GetTestCases/GetTestCasesQuery.cs new file mode 100644 index 0000000..0199a61 --- /dev/null +++ b/src/X1.Application/Features/TestCases/Queries/GetTestCases/GetTestCasesQuery.cs @@ -0,0 +1,14 @@ +using MediatR; + +namespace CellularManagement.Application.Features.TestCases.Queries.GetTestCases; + +/// +/// 获取所有用例查询 +/// +public class GetTestCasesQuery : IRequest +{ + /// + /// 搜索关键词 + /// + public string? Keyword { get; set; } +} \ No newline at end of file diff --git a/src/X1.Application/Features/TestCases/Queries/GetTestCases/GetTestCasesQueryHandler.cs b/src/X1.Application/Features/TestCases/Queries/GetTestCases/GetTestCasesQueryHandler.cs new file mode 100644 index 0000000..62dd532 --- /dev/null +++ b/src/X1.Application/Features/TestCases/Queries/GetTestCases/GetTestCasesQueryHandler.cs @@ -0,0 +1,76 @@ +using MediatR; +using Microsoft.Extensions.Logging; +using CellularManagement.Domain.Repositories.Device; + +namespace CellularManagement.Application.Features.TestCases.Queries.GetTestCases; + +/// +/// 获取所有用例查询处理器 +/// +public class GetTestCasesQueryHandler : IRequestHandler +{ + private readonly ITestCaseRepository _testCaseRepository; + private readonly ILogger _logger; + + /// + /// 初始化查询处理器 + /// + public GetTestCasesQueryHandler( + ITestCaseRepository testCaseRepository, + ILogger logger) + { + _testCaseRepository = testCaseRepository; + _logger = logger; + } + + /// + /// 处理获取所有用例查询 + /// + public async Task Handle(GetTestCasesQuery request, CancellationToken cancellationToken) + { + try + { + _logger.LogInformation("开始获取用例列表,搜索关键词: {Keyword}", request.Keyword ?? "无"); + + // 获取用例列表 + var testCases = await _testCaseRepository.SearchTestCasesAsync(request.Keyword, cancellationToken); + + // 转换为DTO + var testCaseDtos = testCases.Select(tc => new TestCaseDto + { + Id = tc.Id, + Code = tc.Code, + Name = tc.Name, + UserCount = tc.UserCount, + UserInfoList = tc.UserInfoList, + LatencyThresholdCount = tc.LatencyThresholdCount, + LatencyThresholdList = tc.LatencyThresholdList, + ThroughputThresholdCount = tc.ThroughputThresholdCount, + ThroughputThresholdList = tc.ThroughputThresholdList, + Description = tc.Description, + Version = tc.Version, + VersionUpdateInfo = tc.VersionUpdateInfo, + IsDisabled = tc.IsDisabled, + Remarks = tc.Remarks, + CreatedAt = tc.CreatedAt, + CreatedBy = tc.CreatedBy, + UpdatedAt = tc.UpdatedAt, + UpdatedBy = tc.UpdatedBy + }).ToList(); + + var response = new GetTestCasesResponse + { + TestCases = testCaseDtos, + TotalCount = testCaseDtos.Count + }; + + _logger.LogInformation("成功获取用例列表,总数: {TotalCount}", response.TotalCount); + return response; + } + catch (Exception ex) + { + _logger.LogError(ex, "获取用例列表时发生错误,搜索关键词: {Keyword}", request.Keyword ?? "无"); + throw; + } + } +} \ No newline at end of file diff --git a/src/X1.Application/Features/TestCases/Queries/GetTestCases/GetTestCasesResponse.cs b/src/X1.Application/Features/TestCases/Queries/GetTestCases/GetTestCasesResponse.cs new file mode 100644 index 0000000..1d590b9 --- /dev/null +++ b/src/X1.Application/Features/TestCases/Queries/GetTestCases/GetTestCasesResponse.cs @@ -0,0 +1,113 @@ +namespace CellularManagement.Application.Features.TestCases.Queries.GetTestCases; + +/// +/// 获取所有用例响应 +/// +public class GetTestCasesResponse +{ + /// + /// 用例列表 + /// + public List TestCases { get; set; } = new(); + + /// + /// 总数 + /// + public int TotalCount { get; set; } +} + +/// +/// 用例数据传输对象 +/// +public class TestCaseDto +{ + /// + /// 用例ID + /// + public string Id { get; set; } = null!; + + /// + /// 用例编码 + /// + public string Code { get; set; } = null!; + + /// + /// 用例名称 + /// + public string Name { get; set; } = null!; + + /// + /// 用户数 + /// + public int UserCount { get; set; } + + /// + /// 用户信息列表 + /// + public string? UserInfoList { get; set; } + + /// + /// 时延阈值数 + /// + public int LatencyThresholdCount { get; set; } + + /// + /// 时延阈值列表 + /// + public string? LatencyThresholdList { get; set; } + + /// + /// 吞吐量阈值数 + /// + public int ThroughputThresholdCount { get; set; } + + /// + /// 吞吐量阈值列表 + /// + public string? ThroughputThresholdList { get; set; } + + /// + /// 用例说明 + /// + public string? Description { get; set; } + + /// + /// 用例版本 + /// + public string Version { get; set; } = null!; + + /// + /// 版本更新信息 + /// + public string? VersionUpdateInfo { get; set; } + + /// + /// 是否禁用 + /// + public bool IsDisabled { get; set; } + + /// + /// 备注 + /// + public string? Remarks { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreatedAt { get; set; } + + /// + /// 创建人 + /// + public string CreatedBy { get; set; } = null!; + + /// + /// 更新时间 + /// + public DateTime? UpdatedAt { get; set; } + + /// + /// 修改人 + /// + public string? UpdatedBy { get; set; } +} \ No newline at end of file diff --git a/src/X1.Domain/Entities/Device/TestCase.cs b/src/X1.Domain/Entities/Device/TestCase.cs new file mode 100644 index 0000000..6b7adc0 --- /dev/null +++ b/src/X1.Domain/Entities/Device/TestCase.cs @@ -0,0 +1,205 @@ +using System.ComponentModel.DataAnnotations; +using CellularManagement.Domain.Entities.Common; + +namespace CellularManagement.Domain.Entities.Device; + +/// +/// 用例实体 +/// +public class TestCase : AuditableEntity +{ + private TestCase() { } + + /// + /// 用例编码 + /// + [Required] + [MaxLength(50)] + public string Code { get; private set; } = null!; + + /// + /// 用例名称 + /// + [Required] + [MaxLength(100)] + public string Name { get; private set; } = null!; + + /// + /// 用户数 + /// + public int UserCount { get; private set; } = 0; + + /// + /// 用户信息列表(JSON格式存储) + /// + [MaxLength(4000)] + public string? UserInfoList { get; private set; } + + /// + /// 时延阈值数 + /// + public int LatencyThresholdCount { get; private set; } = 0; + + /// + /// 时延阈值列表(JSON格式存储) + /// + [MaxLength(2000)] + public string? LatencyThresholdList { get; private set; } + + /// + /// 吞吐量阈值数 + /// + public int ThroughputThresholdCount { get; private set; } = 0; + + /// + /// 吞吐量阈值列表(JSON格式存储) + /// + [MaxLength(2000)] + public string? ThroughputThresholdList { get; private set; } + + /// + /// 用例说明 + /// + [MaxLength(1000)] + public string? Description { get; private set; } + + /// + /// 用例版本 + /// + [Required] + [MaxLength(20)] + public string Version { get; private set; } = null!; + + /// + /// 版本更新信息 + /// + [MaxLength(500)] + public string? VersionUpdateInfo { get; private set; } + + /// + /// 是否禁用 + /// + public bool IsDisabled { get; private set; } = false; + + /// + /// 备注 + /// + [MaxLength(1000)] + public string? Remarks { get; private set; } + + /// + /// 创建用例 + /// + public static TestCase Create( + string code, + string name, + string version, + string createdBy, + int userCount = 0, + string? userInfoList = null, + int latencyThresholdCount = 0, + string? latencyThresholdList = null, + int throughputThresholdCount = 0, + string? throughputThresholdList = null, + string? description = null, + string? versionUpdateInfo = null, + bool isDisabled = false, + string? remarks = null) + { + var testCase = new TestCase + { + Id = Guid.NewGuid().ToString(), + Code = code, + Name = name, + Version = version, + UserCount = userCount, + UserInfoList = userInfoList, + LatencyThresholdCount = latencyThresholdCount, + LatencyThresholdList = latencyThresholdList, + ThroughputThresholdCount = throughputThresholdCount, + ThroughputThresholdList = throughputThresholdList, + Description = description, + VersionUpdateInfo = versionUpdateInfo, + IsDisabled = isDisabled, + Remarks = remarks, + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow, + CreatedBy = createdBy, + UpdatedBy = createdBy + }; + + return testCase; + } + + /// + /// 更新用例 + /// + public void Update( + string code, + string name, + string version, + string updatedBy, + int userCount = 0, + string? userInfoList = null, + int latencyThresholdCount = 0, + string? latencyThresholdList = null, + int throughputThresholdCount = 0, + string? throughputThresholdList = null, + string? description = null, + string? versionUpdateInfo = null, + bool isDisabled = false, + string? remarks = null) + { + Code = code; + Name = name; + Version = version; + UserCount = userCount; + UserInfoList = userInfoList; + LatencyThresholdCount = latencyThresholdCount; + LatencyThresholdList = latencyThresholdList; + ThroughputThresholdCount = throughputThresholdCount; + ThroughputThresholdList = throughputThresholdList; + Description = description; + VersionUpdateInfo = versionUpdateInfo; + IsDisabled = isDisabled; + Remarks = remarks; + 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/ITestCaseRepository.cs b/src/X1.Domain/Repositories/Device/ITestCaseRepository.cs new file mode 100644 index 0000000..e7e088b --- /dev/null +++ b/src/X1.Domain/Repositories/Device/ITestCaseRepository.cs @@ -0,0 +1,73 @@ +using CellularManagement.Domain.Entities; +using CellularManagement.Domain.Entities.Device; +using CellularManagement.Domain.Repositories.Base; + +namespace CellularManagement.Domain.Repositories.Device; + +/// +/// 用例仓储接口 +/// +public interface ITestCaseRepository : IBaseRepository +{ + /// + /// 添加用例 + /// + Task AddTestCaseAsync(TestCase testCase, CancellationToken cancellationToken = default); + + /// + /// 更新用例 + /// + void UpdateTestCase(TestCase testCase); + + /// + /// 删除用例 + /// + Task DeleteTestCaseAsync(string id, CancellationToken cancellationToken = default); + + /// + /// 获取所有用例 + /// + Task> GetAllTestCasesAsync(CancellationToken cancellationToken = default); + + /// + /// 根据ID获取用例 + /// + Task GetTestCaseByIdAsync(string id, CancellationToken cancellationToken = default); + + /// + /// 根据编码获取用例 + /// + Task GetTestCaseByCodeAsync(string code, CancellationToken cancellationToken = default); + + /// + /// 根据版本获取用例 + /// + Task GetTestCaseByVersionAsync(string version, CancellationToken cancellationToken = default); + + /// + /// 搜索用例 + /// + Task> SearchTestCasesAsync( + string? keyword, + CancellationToken cancellationToken = default); + + /// + /// 检查用例是否存在 + /// + Task ExistsAsync(string id, CancellationToken cancellationToken = default); + + /// + /// 检查编码是否存在 + /// + Task CodeExistsAsync(string code, CancellationToken cancellationToken = default); + + /// + /// 检查版本是否存在 + /// + Task VersionExistsAsync(string version, CancellationToken cancellationToken = default); + + /// + /// 获取启用的用例 + /// + Task> GetEnabledTestCasesAsync(CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/X1.Infrastructure/Configurations/Device/TestCaseConfiguration.cs b/src/X1.Infrastructure/Configurations/Device/TestCaseConfiguration.cs new file mode 100644 index 0000000..221803b --- /dev/null +++ b/src/X1.Infrastructure/Configurations/Device/TestCaseConfiguration.cs @@ -0,0 +1,94 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using CellularManagement.Domain.Entities.Device; + +namespace CellularManagement.Infrastructure.Configurations.Device; + +/// +/// 用例数据库配置 +/// +public class TestCaseConfiguration : IEntityTypeConfiguration +{ + /// + /// 配置用例实体 + /// + public void Configure(EntityTypeBuilder builder) + { + // 表名 + builder.ToTable("TestCases"); + + // 主键 + builder.HasKey(x => x.Id); + + // 属性配置 + builder.Property(x => x.Id) + .HasMaxLength(50) + .IsRequired(); + + builder.Property(x => x.Code) + .HasMaxLength(50) + .IsRequired(); + + builder.Property(x => x.Name) + .HasMaxLength(100) + .IsRequired(); + + builder.Property(x => x.UserCount) + .IsRequired() + .HasDefaultValue(0); + + builder.Property(x => x.UserInfoList) + .HasMaxLength(4000); + + builder.Property(x => x.LatencyThresholdCount) + .IsRequired() + .HasDefaultValue(0); + + builder.Property(x => x.LatencyThresholdList) + .HasMaxLength(2000); + + builder.Property(x => x.ThroughputThresholdCount) + .IsRequired() + .HasDefaultValue(0); + + builder.Property(x => x.ThroughputThresholdList) + .HasMaxLength(2000); + + builder.Property(x => x.Description) + .HasMaxLength(1000); + + builder.Property(x => x.Version) + .HasMaxLength(20) + .IsRequired(); + + builder.Property(x => x.VersionUpdateInfo) + .HasMaxLength(500); + + builder.Property(x => x.IsDisabled) + .IsRequired() + .HasDefaultValue(false); + + builder.Property(x => x.Remarks) + .HasMaxLength(1000); + + // 索引 + builder.HasIndex(x => x.Code) + .IsUnique() + .HasFilter("\"IsDeleted\" = false"); + + builder.HasIndex(x => x.Version) + .HasFilter("\"IsDeleted\" = false"); + + builder.HasIndex(x => x.Name) + .HasFilter("\"IsDeleted\" = false"); + + builder.HasIndex(x => x.IsDisabled) + .HasFilter("\"IsDeleted\" = false"); + + builder.HasIndex(x => x.CreatedAt) + .HasFilter("\"IsDeleted\" = false"); + + // 软删除过滤器 + builder.HasQueryFilter(x => !x.IsDeleted); + } +} \ No newline at end of file diff --git a/src/X1.Infrastructure/Context/AppDbContext.cs b/src/X1.Infrastructure/Context/AppDbContext.cs index 5481590..7235fab 100644 --- a/src/X1.Infrastructure/Context/AppDbContext.cs +++ b/src/X1.Infrastructure/Context/AppDbContext.cs @@ -61,6 +61,11 @@ public class AppDbContext : IdentityDbContext /// public DbSet TestCaseSets { get; set; } = null!; + /// + /// 用例集合 + /// + public DbSet TestCases { get; set; } = null!; + /// /// 初始化数据库上下文 /// diff --git a/src/X1.Infrastructure/DependencyInjection.cs b/src/X1.Infrastructure/DependencyInjection.cs index 0dccb9e..369dd5e 100644 --- a/src/X1.Infrastructure/DependencyInjection.cs +++ b/src/X1.Infrastructure/DependencyInjection.cs @@ -177,6 +177,7 @@ public static class DependencyInjection services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); return services; } diff --git a/src/X1.Infrastructure/Repositories/Device/TestCaseRepository.cs b/src/X1.Infrastructure/Repositories/Device/TestCaseRepository.cs new file mode 100644 index 0000000..aa58f5e --- /dev/null +++ b/src/X1.Infrastructure/Repositories/Device/TestCaseRepository.cs @@ -0,0 +1,149 @@ +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 TestCaseRepository : BaseRepository, ITestCaseRepository +{ + private readonly ILogger _logger; + + /// + /// 初始化仓储 + /// + public TestCaseRepository( + ICommandRepository commandRepository, + IQueryRepository queryRepository, + ILogger logger) + : base(commandRepository, queryRepository, logger) + { + _logger = logger; + } + + /// + /// 添加用例 + /// + public async Task AddTestCaseAsync(TestCase testCase, CancellationToken cancellationToken = default) + { + var result = await CommandRepository.AddAsync(testCase, cancellationToken); + return result; + } + + /// + /// 更新用例 + /// + public void UpdateTestCase(TestCase testCase) + { + CommandRepository.Update(testCase); + } + + /// + /// 删除用例 + /// + public async Task DeleteTestCaseAsync(string id, CancellationToken cancellationToken = default) + { + await CommandRepository.DeleteByIdAsync(id, cancellationToken); + } + + /// + /// 获取所有用例 + /// + public async Task> GetAllTestCasesAsync(CancellationToken cancellationToken = default) + { + var testCases = await QueryRepository.GetAllAsync(cancellationToken: cancellationToken); + return testCases.ToList(); + } + + /// + /// 根据ID获取用例 + /// + public async Task GetTestCaseByIdAsync(string id, CancellationToken cancellationToken = default) + { + return await QueryRepository.GetByIdAsync(id, cancellationToken: cancellationToken); + } + + /// + /// 根据编码获取用例 + /// + public async Task GetTestCaseByCodeAsync(string code, CancellationToken cancellationToken = default) + { + return await QueryRepository.FirstOrDefaultAsync(tc => tc.Code == code, cancellationToken: cancellationToken); + } + + /// + /// 根据版本获取用例 + /// + public async Task GetTestCaseByVersionAsync(string version, CancellationToken cancellationToken = default) + { + return await QueryRepository.FirstOrDefaultAsync(tc => tc.Version == version, cancellationToken: cancellationToken); + } + + /// + /// 搜索用例 + /// + public async Task> SearchTestCasesAsync( + string? keyword, + CancellationToken cancellationToken = default) + { + var query = await QueryRepository.FindAsync(tc => true, cancellationToken: cancellationToken); + + if (!string.IsNullOrWhiteSpace(keyword)) + { + query = query.Where(tc => + tc.Name.Contains(keyword) || + tc.Code.Contains(keyword) || + tc.Version.Contains(keyword) || + (tc.Description != null && tc.Description.Contains(keyword)) || + (tc.VersionUpdateInfo != null && tc.VersionUpdateInfo.Contains(keyword)) || + (tc.Remarks != null && tc.Remarks.Contains(keyword))); + } + + var testCases = query; + return testCases.ToList(); + } + + /// + /// 检查用例是否存在 + /// + public async Task ExistsAsync(string id, CancellationToken cancellationToken = default) + { + return await QueryRepository.AnyAsync(tc => tc.Id == id, cancellationToken: cancellationToken); + } + + /// + /// 检查编码是否存在 + /// + public async Task CodeExistsAsync(string code, CancellationToken cancellationToken = default) + { + return await QueryRepository.AnyAsync(tc => tc.Code == code, cancellationToken: cancellationToken); + } + + /// + /// 检查版本是否存在 + /// + public async Task VersionExistsAsync(string version, CancellationToken cancellationToken = default) + { + return await QueryRepository.AnyAsync(tc => tc.Version == version, cancellationToken: cancellationToken); + } + + /// + /// 获取启用的用例 + /// + public async Task> GetEnabledTestCasesAsync(CancellationToken cancellationToken = default) + { + var testCases = await QueryRepository.FindAsync(tc => !tc.IsDisabled, cancellationToken: cancellationToken); + return testCases.ToList(); + } +} \ No newline at end of file diff --git a/src/X1.Presentation/Controllers/TestCasesController.cs b/src/X1.Presentation/Controllers/TestCasesController.cs new file mode 100644 index 0000000..4a79ed3 --- /dev/null +++ b/src/X1.Presentation/Controllers/TestCasesController.cs @@ -0,0 +1,169 @@ +using CellularManagement.Application.Features.TestCases.Commands.CreateTestCase; +using CellularManagement.Application.Features.TestCases.Commands.DeleteTestCase; +using CellularManagement.Application.Features.TestCases.Commands.DisableTestCase; +using CellularManagement.Application.Features.TestCases.Commands.EnableTestCase; +using CellularManagement.Application.Features.TestCases.Commands.UpdateTestCase; +using CellularManagement.Application.Features.TestCases.Queries.GetTestCaseById; +using CellularManagement.Application.Features.TestCases.Queries.GetTestCases; +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; + +/// +/// 用例控制器 +/// +[ApiController] +[Route("api/[controller]")] +[Authorize] +public class TestCasesController : ApiController +{ + private readonly ILogger _logger; + + /// + /// 初始化控制器 + /// + public TestCasesController(IMediator mediator, ILogger logger) + : base(mediator) + { + _logger = logger; + } + + /// + /// 获取用例列表 + /// + [HttpGet] + public async Task GetList([FromQuery] string? keyword) + { + _logger.LogInformation("开始获取用例列表,搜索关键词: {Keyword}", keyword ?? "无"); + + var result = await mediator.Send(new GetTestCasesQuery { Keyword = keyword }); + + _logger.LogInformation("成功获取用例列表,总数: {TotalCount}", result.TotalCount); + return result; + } + + /// + /// 获取用例详情 + /// + [HttpGet("{id}")] + public async Task GetById(string id) + { + _logger.LogInformation("开始获取用例详情,用例ID: {TestCaseId}", id); + + var result = await mediator.Send(new GetTestCaseByIdQuery { Id = id }); + if (result == null) + { + _logger.LogWarning("用例不存在: {TestCaseId}", id); + return null; + } + + _logger.LogInformation("成功获取用例详情,用例ID: {TestCaseId}", id); + return result; + } + + /// + /// 创建用例 + /// + [HttpPost] + public async Task> Create([FromBody] CreateTestCaseCommand command) + { + _logger.LogInformation("开始创建用例,用例编码: {Code}, 用例名称: {Name}, 版本: {Version}", command.Code, command.Name, command.Version); + + var result = await mediator.Send(command); + if (!result.IsSuccess) + { + _logger.LogWarning("创建用例失败: {Message}", result.ErrorMessages); + return result; + } + + _logger.LogInformation("成功创建用例,用例ID: {TestCaseId}", result.Data?.TestCaseId); + return result; + } + + /// + /// 更新用例 + /// + [HttpPut("{id}")] + public async Task> Update(string id, [FromBody] UpdateTestCaseCommand command) + { + _logger.LogInformation("开始更新用例,用例ID: {TestCaseId}", id); + + if (id != command.TestCaseId) + { + _logger.LogWarning("用例ID不匹配,路径ID: {PathId}, 命令ID: {CommandId}", id, command.TestCaseId); + return OperationResult.CreateFailure("用例ID不匹配"); + } + + var result = await mediator.Send(command); + if (!result.IsSuccess) + { + _logger.LogWarning("更新用例失败: {Message}", result.ErrorMessages); + return result; + } + + _logger.LogInformation("成功更新用例,用例ID: {TestCaseId}", id); + return result; + } + + /// + /// 删除用例 + /// + [HttpDelete("{id}")] + public async Task> Delete(string id) + { + _logger.LogInformation("开始删除用例,用例ID: {TestCaseId}", id); + + var result = await mediator.Send(new DeleteTestCaseCommand { TestCaseId = id }); + if (!result.IsSuccess) + { + _logger.LogWarning("删除用例失败: {Message}", result.ErrorMessages); + return result; + } + + _logger.LogInformation("成功删除用例,用例ID: {TestCaseId}", id); + return result; + } + + /// + /// 启用用例 + /// + [HttpPost("{id}/enable")] + public async Task> Enable(string id) + { + _logger.LogInformation("开始启用用例,用例ID: {TestCaseId}", id); + + var result = await mediator.Send(new EnableTestCaseCommand { TestCaseId = id }); + if (!result.IsSuccess) + { + _logger.LogWarning("启用用例失败: {Message}", result.ErrorMessages); + return result; + } + + _logger.LogInformation("成功启用用例,用例ID: {TestCaseId}", id); + return result; + } + + /// + /// 禁用用例 + /// + [HttpPost("{id}/disable")] + public async Task> Disable(string id) + { + _logger.LogInformation("开始禁用用例,用例ID: {TestCaseId}", id); + + var result = await mediator.Send(new DisableTestCaseCommand { TestCaseId = id }); + if (!result.IsSuccess) + { + _logger.LogWarning("禁用用例失败: {Message}", result.ErrorMessages); + return result; + } + + _logger.LogInformation("成功禁用用例,用例ID: {TestCaseId}", id); + return result; + } +} \ No newline at end of file