diff --git a/src/X1.Application/DependencyInjection.cs b/src/X1.Application/DependencyInjection.cs index 6ab03bb..90ff2f2 100644 --- a/src/X1.Application/DependencyInjection.cs +++ b/src/X1.Application/DependencyInjection.cs @@ -42,6 +42,9 @@ public static class DependencyInjection // 注册验证器 services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviour<,>)); + // 注册 FluentValidation 验证器 + services.AddValidatorsFromAssembly(assembly); + // 注册测试用例流程构建器 services.AddScoped(); diff --git a/src/X1.Application/Features/Common/Dtos/ScenarioTestCaseDto.cs b/src/X1.Application/Features/Common/Dtos/ScenarioTestCaseDto.cs new file mode 100644 index 0000000..35fffcd --- /dev/null +++ b/src/X1.Application/Features/Common/Dtos/ScenarioTestCaseDto.cs @@ -0,0 +1,62 @@ +namespace X1.Application.Features.Common.Dtos; + +/// +/// 场景测试用例数据传输对象 +/// +public class ScenarioTestCaseDto +{ + /// + /// 场景测试用例ID + /// + public string ScenarioTestCaseId { get; set; } = null!; + + /// + /// 场景ID + /// + public string ScenarioId { get; set; } = null!; + + /// + /// 测试用例流程ID + /// + public string TestCaseFlowId { get; set; } = null!; + + /// + /// 测试用例流程名称 + /// + public string? TestCaseFlowName { get; set; } + + /// + /// 执行顺序 + /// + public int ExecutionOrder { get; set; } + + /// + /// 循环次数 + /// + public int LoopCount { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnabled { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreatedAt { get; set; } + + /// + /// 创建人 + /// + public string CreatedBy { get; set; } = null!; + + /// + /// 更新时间 + /// + public DateTime UpdatedAt { get; set; } + + /// + /// 更新人 + /// + public string UpdatedBy { get; set; } = null!; +} diff --git a/src/X1.Application/Features/ScenarioTestCases/Commands/CreateScenarioTestCase/CreateScenarioTestCaseCommandValidator.cs b/src/X1.Application/Features/ScenarioTestCases/Commands/CreateScenarioTestCase/CreateScenarioTestCaseCommandValidator.cs new file mode 100644 index 0000000..aaff5bf --- /dev/null +++ b/src/X1.Application/Features/ScenarioTestCases/Commands/CreateScenarioTestCase/CreateScenarioTestCaseCommandValidator.cs @@ -0,0 +1,78 @@ +using FluentValidation; + +namespace X1.Application.Features.ScenarioTestCases.Commands.CreateScenarioTestCase; + +/// +/// 创建场景测试用例命令验证器 +/// +public sealed class CreateScenarioTestCaseCommandValidator : AbstractValidator +{ + public CreateScenarioTestCaseCommandValidator() + { + // 验证场景ID + RuleFor(x => x.ScenarioId) + .NotEmpty().WithMessage("场景ID不能为空") + .MaximumLength(50).WithMessage("场景ID长度不能超过50个字符"); + + // 验证测试用例列表 + RuleFor(x => x.TestCases) + .NotEmpty().WithMessage("测试用例列表不能为空") + .Must(testCases => testCases != null && testCases.Count > 0) + .WithMessage("至少需要包含一个测试用例"); + + // 验证每个测试用例项 + RuleForEach(x => x.TestCases) + .SetValidator(new ScenarioTestCaseItemValidator()); + } +} + +/// +/// 创建场景测试用例请求验证器 +/// +public sealed class CreateScenarioTestCaseRequestValidator : AbstractValidator +{ + public CreateScenarioTestCaseRequestValidator() + { + // 验证场景ID + RuleFor(x => x.ScenarioId) + .NotEmpty().WithMessage("场景ID不能为空") + .MaximumLength(50).WithMessage("场景ID长度不能超过50个字符"); + + // 验证测试用例列表 + RuleFor(x => x.TestCases) + .NotEmpty().WithMessage("测试用例列表不能为空") + .Must(testCases => testCases != null && testCases.Count > 0) + .WithMessage("至少需要包含一个测试用例"); + + // 验证每个测试用例项 + RuleForEach(x => x.TestCases) + .SetValidator(new ScenarioTestCaseItemValidator()); + } +} + +/// +/// 场景测试用例项验证器 +/// +public sealed class ScenarioTestCaseItemValidator : AbstractValidator +{ + public ScenarioTestCaseItemValidator() + { + // 验证测试用例流程ID + RuleFor(x => x.TestCaseFlowId) + .NotEmpty().WithMessage("测试用例流程ID不能为空") + .MaximumLength(50).WithMessage("测试用例流程ID长度不能超过50个字符"); + + // 验证执行顺序 + RuleFor(x => x.ExecutionOrder) + .GreaterThanOrEqualTo(0).WithMessage("执行顺序必须大于等于0"); + + // 验证循环次数 + RuleFor(x => x.LoopCount) + .GreaterThan(0).WithMessage("循环次数必须大于0") + .LessThanOrEqualTo(1000).WithMessage("循环次数不能超过1000"); + + // 验证是否启用(布尔值,通常不需要特殊验证) + RuleFor(x => x.IsEnabled) + .NotNull().WithMessage("启用状态不能为空"); + } +} diff --git a/src/X1.Application/Features/ScenarioTestCases/Commands/CreateScenarioTestCase/CreateScenarioTestCaseRequest.cs b/src/X1.Application/Features/ScenarioTestCases/Commands/CreateScenarioTestCase/CreateScenarioTestCaseRequest.cs new file mode 100644 index 0000000..7a3e818 --- /dev/null +++ b/src/X1.Application/Features/ScenarioTestCases/Commands/CreateScenarioTestCase/CreateScenarioTestCaseRequest.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; + +namespace X1.Application.Features.ScenarioTestCases.Commands.CreateScenarioTestCase; + +/// +/// 创建场景测试用例请求模型 +/// +public class CreateScenarioTestCaseRequest +{ + /// + /// 场景ID + /// + [Required(ErrorMessage = "场景ID不能为空")] + public string ScenarioId { get; set; } = null!; + + /// + /// 测试用例列表 + /// + [Required(ErrorMessage = "测试用例列表不能为空")] + [MinLength(1, ErrorMessage = "至少需要包含一个测试用例")] + public List TestCases { get; set; } = new(); +} diff --git a/src/X1.Application/Features/ScenarioTestCases/Queries/GetScenarioTestCases/GetScenarioTestCasesQueryHandler.cs b/src/X1.Application/Features/ScenarioTestCases/Queries/GetScenarioTestCases/GetScenarioTestCasesQueryHandler.cs index 1ca3970..84f4795 100644 --- a/src/X1.Application/Features/ScenarioTestCases/Queries/GetScenarioTestCases/GetScenarioTestCasesQueryHandler.cs +++ b/src/X1.Application/Features/ScenarioTestCases/Queries/GetScenarioTestCases/GetScenarioTestCasesQueryHandler.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging; using X1.Domain.Common; using X1.Domain.Entities.TestCase; using X1.Domain.Repositories.TestCase; +using X1.Application.Features.Common.Dtos; namespace X1.Application.Features.ScenarioTestCases.Queries.GetScenarioTestCases; diff --git a/src/X1.Application/Features/ScenarioTestCases/Queries/GetScenarioTestCases/GetScenarioTestCasesResponse.cs b/src/X1.Application/Features/ScenarioTestCases/Queries/GetScenarioTestCases/GetScenarioTestCasesResponse.cs index 12e7640..c16f437 100644 --- a/src/X1.Application/Features/ScenarioTestCases/Queries/GetScenarioTestCases/GetScenarioTestCasesResponse.cs +++ b/src/X1.Application/Features/ScenarioTestCases/Queries/GetScenarioTestCases/GetScenarioTestCasesResponse.cs @@ -1,3 +1,5 @@ +using X1.Application.Features.Common.Dtos; + namespace X1.Application.Features.ScenarioTestCases.Queries.GetScenarioTestCases; /// @@ -20,64 +22,3 @@ public class GetScenarioTestCasesResponse /// public int TotalCount { get; set; } } - -/// -/// 场景测试用例数据传输对象 -/// -public class ScenarioTestCaseDto -{ - /// - /// 场景测试用例ID - /// - public string ScenarioTestCaseId { get; set; } = null!; - - /// - /// 场景ID - /// - public string ScenarioId { get; set; } = null!; - - /// - /// 测试用例流程ID - /// - public string TestCaseFlowId { get; set; } = null!; - - /// - /// 测试用例流程名称 - /// - public string? TestCaseFlowName { get; set; } - - /// - /// 执行顺序 - /// - public int ExecutionOrder { get; set; } - - /// - /// 循环次数 - /// - public int LoopCount { get; set; } - - /// - /// 是否启用 - /// - public bool IsEnabled { get; set; } - - /// - /// 创建时间 - /// - public DateTime CreatedAt { get; set; } - - /// - /// 创建人 - /// - public string CreatedBy { get; set; } = null!; - - /// - /// 更新时间 - /// - public DateTime UpdatedAt { get; set; } - - /// - /// 更新人 - /// - public string UpdatedBy { get; set; } = null!; -} diff --git a/src/X1.Application/Features/TestCaseFlow/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeMappingQuery.cs b/src/X1.Application/Features/TestCaseFlow/Queries/GetTestFlowTypesQueryMapping/GetTestFlowTypesQuery.cs similarity index 70% rename from src/X1.Application/Features/TestCaseFlow/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeMappingQuery.cs rename to src/X1.Application/Features/TestCaseFlow/Queries/GetTestFlowTypesQueryMapping/GetTestFlowTypesQuery.cs index 078e3dd..8972049 100644 --- a/src/X1.Application/Features/TestCaseFlow/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeMappingQuery.cs +++ b/src/X1.Application/Features/TestCaseFlow/Queries/GetTestFlowTypesQueryMapping/GetTestFlowTypesQuery.cs @@ -6,7 +6,7 @@ namespace X1.Application.Features.TestCaseFlow.Queries.GetFormTypeStepTypeMappin /// /// 获取表单类型到步骤类型映射查询 /// -public class GetFormTypeStepTypeMappingQuery : IRequest> +public class GetTestFlowTypesQuery : IRequest> { // 无需额外参数,返回所有映射关系 } diff --git a/src/X1.Application/Features/TestCaseFlow/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeMappingQueryHandler.cs b/src/X1.Application/Features/TestCaseFlow/Queries/GetTestFlowTypesQueryMapping/GetTestFlowTypesQueryHandler.cs similarity index 57% rename from src/X1.Application/Features/TestCaseFlow/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeMappingQueryHandler.cs rename to src/X1.Application/Features/TestCaseFlow/Queries/GetTestFlowTypesQueryMapping/GetTestFlowTypesQueryHandler.cs index ab15a63..b08f54f 100644 --- a/src/X1.Application/Features/TestCaseFlow/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeMappingQueryHandler.cs +++ b/src/X1.Application/Features/TestCaseFlow/Queries/GetTestFlowTypesQueryMapping/GetTestFlowTypesQueryHandler.cs @@ -6,7 +6,7 @@ namespace X1.Application.Features.TestCaseFlow.Queries.GetFormTypeStepTypeMappin /// /// 获取表单类型到步骤类型映射查询处理器 /// -public class GetFormTypeStepTypeMappingQueryHandler : IRequestHandler> +public class GetTestFlowTypesQueryHandler : IRequestHandler> { /// /// 处理查询 @@ -14,12 +14,12 @@ public class GetFormTypeStepTypeMappingQueryHandler : IRequestHandler查询请求 /// 取消令牌 /// 操作结果 - public async Task> Handle(GetFormTypeStepTypeMappingQuery request, CancellationToken cancellationToken) + public async Task> Handle(GetTestFlowTypesQuery request, CancellationToken cancellationToken) { try { // 构建响应对象 - var response = new GetFormTypeStepTypeMappingResponse(); + var response = new GetTestFlowTypesResponse(); // 获取测试流程类型列表 response.TestFlowTypes = TestFlowTypeConverter.GetTestFlowTypes().Select(tft => new TestFlowTypeDto @@ -29,11 +29,11 @@ public class GetFormTypeStepTypeMappingQueryHandler : IRequestHandler.CreateSuccess(response)); + return await Task.FromResult(OperationResult.CreateSuccess(response)); } catch (Exception ex) { - return await Task.FromResult(OperationResult.CreateFailure($"获取测试流程类型失败: {ex.Message}")); + return await Task.FromResult(OperationResult.CreateFailure($"获取测试流程类型失败: {ex.Message}")); } } } diff --git a/src/X1.Application/Features/TestCaseFlow/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeMappingResponse.cs b/src/X1.Application/Features/TestCaseFlow/Queries/GetTestFlowTypesQueryMapping/GetTestFlowTypesResponse.cs similarity index 93% rename from src/X1.Application/Features/TestCaseFlow/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeMappingResponse.cs rename to src/X1.Application/Features/TestCaseFlow/Queries/GetTestFlowTypesQueryMapping/GetTestFlowTypesResponse.cs index c177925..66a395a 100644 --- a/src/X1.Application/Features/TestCaseFlow/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeMappingResponse.cs +++ b/src/X1.Application/Features/TestCaseFlow/Queries/GetTestFlowTypesQueryMapping/GetTestFlowTypesResponse.cs @@ -3,7 +3,7 @@ namespace X1.Application.Features.TestCaseFlow.Queries.GetFormTypeStepTypeMappin /// /// 获取测试流程类型响应 /// -public class GetFormTypeStepTypeMappingResponse +public class GetTestFlowTypesResponse { /// /// 测试流程类型列表 diff --git a/src/X1.Application/Features/TestScenarios/Commands/CreateTestScenario/CreateTestScenarioCommandHandler.cs b/src/X1.Application/Features/TestScenarios/Commands/CreateTestScenario/CreateTestScenarioCommandHandler.cs index e655a8e..77a9bb2 100644 --- a/src/X1.Application/Features/TestScenarios/Commands/CreateTestScenario/CreateTestScenarioCommandHandler.cs +++ b/src/X1.Application/Features/TestScenarios/Commands/CreateTestScenario/CreateTestScenarioCommandHandler.cs @@ -101,9 +101,9 @@ public class CreateTestScenarioCommandHandler : IRequestHandler "FUNC", ScenarioType.Performance => "PERF", - ScenarioType.Security => "SEC", - ScenarioType.Integration => "INT", - ScenarioType.Regression => "REG", + //ScenarioType.Security => "SEC", + //ScenarioType.Integration => "INT", + //ScenarioType.Regression => "REG", _ => "GEN" }; diff --git a/src/X1.Application/Features/TestScenarios/Queries/GetScenarioTypes/GetScenarioTypesQuery.cs b/src/X1.Application/Features/TestScenarios/Queries/GetScenarioTypes/GetScenarioTypesQuery.cs new file mode 100644 index 0000000..ced704e --- /dev/null +++ b/src/X1.Application/Features/TestScenarios/Queries/GetScenarioTypes/GetScenarioTypesQuery.cs @@ -0,0 +1,12 @@ +using X1.Domain.Common; +using MediatR; + +namespace X1.Application.Features.TestScenarios.Queries.GetScenarioTypes; + +/// +/// 获取测试场景类型查询 +/// +public class GetScenarioTypesQuery : IRequest> +{ + // 无需额外参数,返回所有测试场景类型 +} diff --git a/src/X1.Application/Features/TestScenarios/Queries/GetScenarioTypes/GetScenarioTypesQueryHandler.cs b/src/X1.Application/Features/TestScenarios/Queries/GetScenarioTypes/GetScenarioTypesQueryHandler.cs new file mode 100644 index 0000000..cf2901f --- /dev/null +++ b/src/X1.Application/Features/TestScenarios/Queries/GetScenarioTypes/GetScenarioTypesQueryHandler.cs @@ -0,0 +1,41 @@ +using MediatR; +using X1.Domain.Common; +using X1.Domain.Entities.TestCase; + +namespace X1.Application.Features.TestScenarios.Queries.GetScenarioTypes; + +/// +/// 获取测试场景类型查询处理器 +/// +public class GetScenarioTypesQueryHandler : IRequestHandler> +{ + /// + /// 处理查询 + /// + /// 查询请求 + /// 取消令牌 + /// 操作结果 + public async Task> Handle(GetScenarioTypesQuery request, CancellationToken cancellationToken) + { + try + { + // 构建响应对象 + var response = new GetScenarioTypesResponse(); + + // 获取测试场景类型列表 + response.ScenarioTypes = ScenarioTypeConverter.GetScenarioTypes().Select(st => new ScenarioTypeDto + { + Value = st.Value, + EnumValue = st.Name, // 枚举的字符串值,如 "Functional" + Name = ((ScenarioType)st.Value).GetDisplayName(), // 显示名称,如 "功能测试" + Description = st.Description + }).ToList(); + + return await Task.FromResult(OperationResult.CreateSuccess(response)); + } + catch (Exception ex) + { + return await Task.FromResult(OperationResult.CreateFailure($"获取测试场景类型失败: {ex.Message}")); + } + } +} diff --git a/src/X1.Application/Features/TestScenarios/Queries/GetScenarioTypes/GetScenarioTypesResponse.cs b/src/X1.Application/Features/TestScenarios/Queries/GetScenarioTypes/GetScenarioTypesResponse.cs new file mode 100644 index 0000000..dc4f6c2 --- /dev/null +++ b/src/X1.Application/Features/TestScenarios/Queries/GetScenarioTypes/GetScenarioTypesResponse.cs @@ -0,0 +1,38 @@ +namespace X1.Application.Features.TestScenarios.Queries.GetScenarioTypes; + +/// +/// 获取测试场景类型响应 +/// +public class GetScenarioTypesResponse +{ + /// + /// 测试场景类型列表 + /// + public List ScenarioTypes { get; set; } = new(); +} + +/// +/// 测试场景类型DTO +/// +public class ScenarioTypeDto +{ + /// + /// 测试场景类型值 + /// + public int Value { get; set; } + + /// + /// 测试场景类型枚举字符串值 + /// + public string EnumValue { get; set; } = null!; + + /// + /// 测试场景类型名称 + /// + public string Name { get; set; } = null!; + + /// + /// 测试场景类型描述 + /// + public string Description { get; set; } = null!; +} diff --git a/src/X1.Application/Features/TestScenarios/Queries/GetTestScenarioById/GetTestScenarioByIdQueryHandler.cs b/src/X1.Application/Features/TestScenarios/Queries/GetTestScenarioById/GetTestScenarioByIdQueryHandler.cs index 186685d..c1acf2c 100644 --- a/src/X1.Application/Features/TestScenarios/Queries/GetTestScenarioById/GetTestScenarioByIdQueryHandler.cs +++ b/src/X1.Application/Features/TestScenarios/Queries/GetTestScenarioById/GetTestScenarioByIdQueryHandler.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging; using X1.Domain.Common; using X1.Domain.Entities.TestCase; using X1.Domain.Repositories.TestCase; +using X1.Application.Features.Common.Dtos; namespace X1.Application.Features.TestScenarios.Queries.GetTestScenarioById; @@ -75,13 +76,16 @@ public class GetTestScenarioByIdQueryHandler : IRequestHandler new ScenarioTestCaseDto { ScenarioTestCaseId = x.Id, + ScenarioId = x.ScenarioId, TestCaseFlowId = x.TestCaseFlowId, TestCaseFlowName = x.TestCaseFlow?.Name ?? "未知", ExecutionOrder = x.ExecutionOrder, LoopCount = x.LoopCount, IsEnabled = x.IsEnabled, CreatedAt = x.CreatedAt, - CreatedBy = x.CreatedBy + CreatedBy = x.CreatedBy, + UpdatedAt = x.UpdatedAt, + UpdatedBy = x.UpdatedBy }) .ToList(); } diff --git a/src/X1.Application/Features/TestScenarios/Queries/GetTestScenarioById/GetTestScenarioByIdResponse.cs b/src/X1.Application/Features/TestScenarios/Queries/GetTestScenarioById/GetTestScenarioByIdResponse.cs index b4e9d11..f9597c3 100644 --- a/src/X1.Application/Features/TestScenarios/Queries/GetTestScenarioById/GetTestScenarioByIdResponse.cs +++ b/src/X1.Application/Features/TestScenarios/Queries/GetTestScenarioById/GetTestScenarioByIdResponse.cs @@ -1,3 +1,5 @@ +using X1.Application.Features.Common.Dtos; + namespace X1.Application.Features.TestScenarios.Queries.GetTestScenarioById; /// @@ -60,49 +62,3 @@ public class GetTestScenarioByIdResponse /// public List? TestCases { get; set; } } - -/// -/// 场景测试用例数据传输对象 -/// -public class ScenarioTestCaseDto -{ - /// - /// 场景测试用例ID - /// - public string ScenarioTestCaseId { get; set; } = null!; - - /// - /// 测试用例流程ID - /// - public string TestCaseFlowId { get; set; } = null!; - - /// - /// 测试用例流程名称 - /// - public string TestCaseFlowName { get; set; } = null!; - - /// - /// 执行顺序 - /// - public int ExecutionOrder { get; set; } - - /// - /// 循环次数 - /// - public int LoopCount { get; set; } - - /// - /// 是否启用 - /// - public bool IsEnabled { get; set; } - - /// - /// 创建时间 - /// - public DateTime CreatedAt { get; set; } - - /// - /// 创建人 - /// - public string CreatedBy { get; set; } = null!; -} diff --git a/src/X1.Domain/Common/ScenarioTypeConverter.cs b/src/X1.Domain/Common/ScenarioTypeConverter.cs new file mode 100644 index 0000000..6808c61 --- /dev/null +++ b/src/X1.Domain/Common/ScenarioTypeConverter.cs @@ -0,0 +1,94 @@ +using X1.Domain.Entities.TestCase; +using System.ComponentModel; +using System.Reflection; +using System.ComponentModel.DataAnnotations; + +namespace X1.Domain.Common; + +/// +/// 测试场景类型转换器 +/// +public static class ScenarioTypeConverter +{ + /// + /// 获取所有测试场景类型 + /// + /// 测试场景类型列表 + public static List GetScenarioTypes() + { + var scenarioTypes = new List(); + + foreach (ScenarioType scenarioType in Enum.GetValues(typeof(ScenarioType))) + { + var fieldInfo = typeof(ScenarioType).GetField(scenarioType.ToString()); + if (fieldInfo != null) + { + var displayAttribute = fieldInfo.GetCustomAttribute(); + var descriptionAttribute = fieldInfo.GetCustomAttribute(); + + var name = displayAttribute?.Name ?? scenarioType.ToString(); + var description = descriptionAttribute?.Description ?? scenarioType.ToString(); + + scenarioTypes.Add(new EnumValueObject + { + Value = (int)scenarioType, + Name = scenarioType.ToString(), // 使用枚举的字符串值 + Description = description + }); + } + } + + return scenarioTypes; + } + + /// + /// 获取所有测试场景类型及其描述 + /// + /// 测试场景类型描述字典 + public static Dictionary GetScenarioTypeDescriptions() + { + return new Dictionary + { + { ScenarioType.Functional, ScenarioType.Functional.GetDisplayName() }, + { ScenarioType.Performance, ScenarioType.Performance.GetDisplayName() }, + { ScenarioType.Stress, ScenarioType.Stress.GetDisplayName() }, + { ScenarioType.Compatibility, ScenarioType.Compatibility.GetDisplayName() }, + //{ ScenarioType.Regression, ScenarioType.Regression.GetDisplayName() }, + //{ ScenarioType.Integration, ScenarioType.Integration.GetDisplayName() }, + //{ ScenarioType.Security, ScenarioType.Security.GetDisplayName() }, + //{ ScenarioType.UserExperience, ScenarioType.UserExperience.GetDisplayName() } + }; + } + + /// + /// 根据测试场景类型获取显示名称 + /// + /// 测试场景类型 + /// 显示名称 + public static string GetDisplayName(this ScenarioType scenarioType) + { + var fieldInfo = typeof(ScenarioType).GetField(scenarioType.ToString()); + if (fieldInfo != null) + { + var displayAttribute = fieldInfo.GetCustomAttribute(); + return displayAttribute?.Name ?? scenarioType.ToString(); + } + return scenarioType.ToString(); + } + + /// + /// 根据测试场景类型获取描述 + /// + /// 测试场景类型 + /// 描述 + public static string GetDescription(this ScenarioType scenarioType) + { + var fieldInfo = typeof(ScenarioType).GetField(scenarioType.ToString()); + if (fieldInfo != null) + { + var descriptionAttribute = fieldInfo.GetCustomAttribute(); + return descriptionAttribute?.Description ?? scenarioType.ToString(); + } + return scenarioType.ToString(); + } +} diff --git a/src/X1.Domain/Entities/TestCase/ScenarioType.cs b/src/X1.Domain/Entities/TestCase/ScenarioType.cs index e402994..a9ee8dc 100644 --- a/src/X1.Domain/Entities/TestCase/ScenarioType.cs +++ b/src/X1.Domain/Entities/TestCase/ScenarioType.cs @@ -36,31 +36,31 @@ public enum ScenarioType [Description("验证系统与不同设备和环境的兼容性的测试场景")] Compatibility = 4, - /// - /// 回归测试 - /// - [Display(Name = "回归测试")] - [Description("验证系统修改后原有功能是否正常的测试场景")] - Regression = 5, +// /// +// /// 回归测试 +// /// +// [Display(Name = "回归测试")] +// [Description("验证系统修改后原有功能是否正常的测试场景")] +// Regression = 5, - /// - /// 集成测试 - /// - [Display(Name = "集成测试")] - [Description("验证系统各模块间集成是否正常的测试场景")] - Integration = 6, +// /// +// /// 集成测试 +// /// +// [Display(Name = "集成测试")] +// [Description("验证系统各模块间集成是否正常的测试场景")] +// Integration = 6, - /// - /// 安全测试 - /// - [Display(Name = "安全测试")] - [Description("验证系统安全性和防护能力的测试场景")] - Security = 7, +// /// +// /// 安全测试 +// /// +// [Display(Name = "安全测试")] +// [Description("验证系统安全性和防护能力的测试场景")] +// Security = 7, - /// - /// 用户体验测试 - /// - [Display(Name = "用户体验测试")] - [Description("验证系统用户体验和界面友好性的测试场景")] - UserExperience = 8 +// /// +// /// 用户体验测试 +// /// +// [Display(Name = "用户体验测试")] +// [Description("验证系统用户体验和界面友好性的测试场景")] +// UserExperience = 8 } diff --git a/src/X1.Infrastructure/Migrations/20250824112436_AddTestScenarioTables.Designer.cs b/src/X1.Infrastructure/Migrations/20250824112436_AddTestScenarioTables.Designer.cs new file mode 100644 index 0000000..146ffae --- /dev/null +++ b/src/X1.Infrastructure/Migrations/20250824112436_AddTestScenarioTables.Designer.cs @@ -0,0 +1,2122 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using X1.Infrastructure.Context; + +#nullable disable + +namespace X1.Infrastructure.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20250824112436_AddTestScenarioTables")] + partial class AddTestScenarioTables + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("X1.Domain.Entities.AppRole", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("角色ID,主键"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text") + .HasComment("并发控制戳"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("角色描述"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("角色名称"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("标准化角色名称(大写)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("更新时间"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("IX_Roles_Name"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("tb_roles", null, t => + { + t.HasComment("角色表"); + }); + }); + + modelBuilder.Entity("X1.Domain.Entities.AppUser", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("用户ID,主键"); + + b.Property("AccessFailedCount") + .HasColumnType("integer") + .HasComment("登录失败次数"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text") + .HasComment("并发控制戳"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("电子邮箱"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean") + .HasComment("邮箱是否已验证"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasComment("用户状态(true: 启用, false: 禁用)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasComment("是否已删除"); + + b.Property("LastLoginTime") + .HasColumnType("timestamp with time zone") + .HasComment("最后登录时间"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean") + .HasComment("是否启用账户锁定"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone") + .HasComment("账户锁定结束时间"); + + b.Property("ModifiedTime") + .HasColumnType("timestamp with time zone") + .HasComment("修改时间"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("标准化电子邮箱(大写)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("标准化账号(大写)"); + + b.Property("PasswordHash") + .HasColumnType("text") + .HasComment("密码哈希值"); + + b.Property("PhoneNumber") + .IsRequired() + .HasColumnType("text") + .HasComment("电话号码"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean") + .HasComment("电话号码是否已验证"); + + b.Property("RealName") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("用户名"); + + b.Property("SecurityStamp") + .HasColumnType("text") + .HasComment("安全戳,用于并发控制"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean") + .HasComment("是否启用双因素认证"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasComment("账号"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasDatabaseName("IX_user_Email"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique() + .HasDatabaseName("IX_user_PhoneNumber"); + + b.HasIndex("UserName") + .IsUnique() + .HasDatabaseName("IX_user_UserName"); + + b.ToTable("tb_users", null, t => + { + t.HasComment("用户表"); + }); + }); + + modelBuilder.Entity("X1.Domain.Entities.Device.CellularDevice", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("设备ID"); + + b.Property("AgentPort") + .HasColumnType("integer") + .HasComment("Agent端口"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("设备描述"); + + b.Property("DeviceCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("设备编码"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(45) + .HasColumnType("character varying(45)") + .HasComment("IP地址"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsEnabled") + .HasColumnType("boolean") + .HasComment("是否启用"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("设备名称"); + + b.Property("SerialNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("序列号"); + + b.Property("UpdatedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasComment("更新时间"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DeviceCode") + .IsUnique() + .HasDatabaseName("IX_cellular_device_DeviceCode"); + + b.HasIndex("SerialNumber") + .IsUnique() + .HasDatabaseName("IX_cellular_device_SerialNumber"); + + b.ToTable("tb_cellular_device", null, t => + { + t.HasComment("蜂窝设备表"); + }); + }); + + modelBuilder.Entity("X1.Domain.Entities.Device.CellularDeviceRuntime", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeviceCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("NetworkStackCode") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RuntimeCode") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RuntimeStatus") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("DeviceCode") + .IsUnique(); + + b.HasIndex("IsDeleted"); + + b.HasIndex("NetworkStackCode"); + + b.HasIndex("RuntimeCode"); + + b.HasIndex("RuntimeStatus"); + + b.HasIndex("DeviceCode", "CreatedAt") + .HasDatabaseName("IX_CellularDeviceRuntimes_DeviceCode_CreatedAt"); + + b.ToTable("tb_cellular_device_runtimes", (string)null); + }); + + modelBuilder.Entity("X1.Domain.Entities.Device.CellularDeviceRuntimeDetail", b => + { + b.Property("Id") + .HasMaxLength(450) + .HasColumnType("character varying(450)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .HasMaxLength(450) + .HasColumnType("character varying(450)"); + + b.Property("DeviceCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("NetworkStackCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RuntimeCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RuntimeStatus") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_CellularDeviceRuntimeDetails_CreatedAt"); + + b.HasIndex("CreatedBy") + .HasDatabaseName("IX_CellularDeviceRuntimeDetails_CreatedBy"); + + b.HasIndex("RuntimeCode") + .HasDatabaseName("IX_CellularDeviceRuntimeDetails_RuntimeCode"); + + b.HasIndex("RuntimeStatus") + .HasDatabaseName("IX_CellularDeviceRuntimeDetails_RuntimeStatus"); + + b.ToTable("tb_cellular_device_runtime_details", (string)null); + }); + + modelBuilder.Entity("X1.Domain.Entities.Device.ProtocolVersion", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("版本ID"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("版本描述"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsEnabled") + .HasColumnType("boolean") + .HasComment("是否启用"); + + b.Property("MinimumSupportedVersion") + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasComment("最低支持版本"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("版本名称"); + + b.Property("ReleaseDate") + .HasColumnType("timestamp with time zone") + .HasComment("发布日期"); + + b.Property("SerialNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("设备序列号"); + + b.Property("UpdatedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasComment("更新时间"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.Property("Version") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasComment("版本号"); + + b.HasKey("Id"); + + b.HasIndex("SerialNumber") + .HasDatabaseName("IX_ProtocolVersions_SerialNumber"); + + b.HasIndex("Version") + .HasDatabaseName("IX_ProtocolVersions_Version"); + + b.ToTable("tb_protocol_versions", null, t => + { + t.HasComment("协议版本表"); + }); + }); + + modelBuilder.Entity("X1.Domain.Entities.Logging.LoginLog", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("日志ID"); + + b.Property("Browser") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("浏览器信息"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FailureReason") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasComment("失败原因"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("登录IP"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsSuccess") + .HasColumnType("boolean") + .HasComment("登录状态(成功/失败)"); + + b.Property("Location") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasComment("登录位置"); + + b.Property("LoginSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("LoginTime") + .HasColumnType("timestamp with time zone") + .HasComment("登录时间"); + + b.Property("LoginType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OperatingSystem") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("操作系统信息"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserAgent") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("设备信息"); + + b.Property("UserId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasComment("用户ID"); + + b.HasKey("Id"); + + b.HasIndex("IpAddress") + .HasDatabaseName("IX_LoginLogs_IpAddress"); + + b.HasIndex("LoginTime") + .HasDatabaseName("IX_LoginLogs_LoginTime"); + + b.HasIndex("UserId") + .HasDatabaseName("IX_LoginLogs_UserId"); + + b.HasIndex("UserId", "LoginTime") + .HasDatabaseName("IX_LoginLogs_UserId_LoginTime"); + + b.ToTable("tb_login_logs", null, t => + { + t.HasComment("用户登录日志表"); + }); + }); + + modelBuilder.Entity("X1.Domain.Entities.Logging.ProtocolLog", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("主键ID"); + + b.Property("CellID") + .HasColumnType("integer") + .HasComment("小区ID"); + + b.Property("DeviceCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("设备代码"); + + b.Property("Direction") + .HasColumnType("integer") + .HasComment("日志方向类型"); + + b.Property("IMSI") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("国际移动用户识别码"); + + b.Property("Info") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("信息字段"); + + b.Property("LayerType") + .HasMaxLength(50) + .HasColumnType("integer") + .HasComment("协议层类型"); + + b.Property("Message") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasComment("消息字段"); + + b.Property("MessageDetailJson") + .HasColumnType("text") + .HasComment("消息详情集合(JSON格式存储)"); + + b.Property("MessageId") + .HasColumnType("bigint") + .HasComment("消息ID"); + + b.Property("PLMN") + .HasMaxLength(20) + .HasColumnType("character varying(20)") + .HasComment("公共陆地移动网络标识"); + + b.Property("RuntimeCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("运行时代码"); + + b.Property("TimeMs") + .HasColumnType("bigint") + .HasComment("时间间隔(毫秒)"); + + b.Property("Timestamp") + .HasColumnType("bigint") + .HasComment("时间戳"); + + b.Property("UEID") + .HasColumnType("integer") + .HasComment("用户设备ID"); + + b.HasKey("Id"); + + b.HasIndex("DeviceCode") + .HasDatabaseName("IX_ProtocolLog_DeviceCode"); + + b.HasIndex("LayerType") + .HasDatabaseName("IX_ProtocolLog_LayerType"); + + b.HasIndex("MessageId") + .HasDatabaseName("IX_ProtocolLog_MessageId"); + + b.HasIndex("RuntimeCode") + .HasDatabaseName("IX_ProtocolLog_RuntimeCode"); + + b.HasIndex("Timestamp") + .HasDatabaseName("IX_ProtocolLog_Timestamp"); + + b.HasIndex("DeviceCode", "RuntimeCode") + .HasDatabaseName("IX_ProtocolLog_DeviceCode_RuntimeCode"); + + b.HasIndex("DeviceCode", "Timestamp") + .HasDatabaseName("IX_ProtocolLog_DeviceCode_Timestamp"); + + b.ToTable("tb_protocol_logs", null, t => + { + t.HasComment("协议日志表"); + }); + }); + + modelBuilder.Entity("X1.Domain.Entities.NetworkProfile.CoreNetworkConfig", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("配置ID"); + + b.Property("ConfigContent") + .IsRequired() + .HasColumnType("text") + .HasComment("配置内容(JSON格式)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("配置描述"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsDisabled") + .HasColumnType("boolean") + .HasComment("是否禁用"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("配置名称"); + + b.Property("UpdatedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasComment("更新时间"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_CoreNetworkConfigs_Name"); + + b.ToTable("tb_core_network_configs", null, t => + { + t.HasComment("核心网配置表"); + }); + }); + + modelBuilder.Entity("X1.Domain.Entities.NetworkProfile.IMS_Configuration", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("配置ID"); + + b.Property("ConfigContent") + .IsRequired() + .HasColumnType("text") + .HasComment("配置内容(JSON格式)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("配置描述"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsDisabled") + .HasColumnType("boolean") + .HasComment("是否禁用"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("配置名称"); + + b.Property("UpdatedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasComment("更新时间"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_IMS_Configurations_Name"); + + b.ToTable("tb_ims_configurations", null, t => + { + t.HasComment("IMS配置表"); + }); + }); + + modelBuilder.Entity("X1.Domain.Entities.NetworkProfile.NetworkStackConfig", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("配置ID"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("描述"); + + b.Property("IsActive") + .HasColumnType("boolean") + .HasComment("是否激活"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("NetworkStackCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("网络栈编码"); + + b.Property("NetworkStackName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("网络栈名称"); + + b.Property("RanId") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("RAN配置ID"); + + b.Property("UpdatedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasComment("更新时间"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("IsActive") + .HasDatabaseName("IX_NetworkStackConfigs_IsActive"); + + b.HasIndex("NetworkStackCode") + .IsUnique() + .HasDatabaseName("IX_NetworkStackConfigs_NetworkStackCode"); + + b.HasIndex("NetworkStackName") + .IsUnique() + .HasDatabaseName("IX_NetworkStackConfigs_NetworkStackName"); + + b.HasIndex("RanId") + .HasDatabaseName("IX_NetworkStackConfigs_RanId"); + + b.ToTable("tb_network_stack_configs", null, t => + { + t.HasComment("网络栈配置表"); + }); + }); + + modelBuilder.Entity("X1.Domain.Entities.NetworkProfile.RAN_Configuration", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("配置ID"); + + b.Property("ConfigContent") + .IsRequired() + .HasColumnType("text") + .HasComment("配置内容(JSON格式)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("text"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("配置描述"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsDisabled") + .HasColumnType("boolean") + .HasComment("是否禁用"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("配置名称"); + + b.Property("UpdatedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasComment("更新时间"); + + b.Property("UpdatedBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_RAN_Configurations_Name"); + + b.ToTable("tb_ran_configurations", null, t => + { + t.HasComment("RAN配置表"); + }); + }); + + modelBuilder.Entity("X1.Domain.Entities.NetworkProfile.Stack_CoreIMS_Binding", b => + { + b.Property("Id") + .HasColumnType("text") + .HasComment("绑定关系ID"); + + b.Property("CnId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("核心网配置ID"); + + b.Property("ImsId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("IMS配置ID"); + + b.Property("Index") + .HasColumnType("integer") + .HasComment("索引"); + + b.Property("NetworkStackConfigId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("网络栈配置ID"); + + b.HasKey("Id"); + + b.HasIndex("CnId") + .HasDatabaseName("IX_Stack_CoreIMS_Bindings_CnId"); + + b.HasIndex("ImsId") + .HasDatabaseName("IX_Stack_CoreIMS_Bindings_ImsId"); + + b.HasIndex("NetworkStackConfigId") + .HasDatabaseName("IX_Stack_CoreIMS_Bindings_NetworkStackConfigId"); + + b.HasIndex("NetworkStackConfigId", "Index") + .IsUnique() + .HasDatabaseName("IX_Stack_CoreIMS_Bindings_NetworkStackConfigId_Index"); + + b.ToTable("tb_stack_core_ims_bindings", null, t => + { + t.HasComment("栈与核心网/IMS绑定关系表"); + }); + }); + + modelBuilder.Entity("X1.Domain.Entities.Permission", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.ToTable("tb_permissions", (string)null); + }); + + modelBuilder.Entity("X1.Domain.Entities.RolePermission", b => + { + b.Property("RoleId") + .HasColumnType("text"); + + b.Property("PermissionId") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("RoleId", "PermissionId"); + + b.HasIndex("PermissionId"); + + b.ToTable("tb_role_permissions", (string)null); + }); + + modelBuilder.Entity("X1.Domain.Entities.Terminal.AdbOperation", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Command") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasComment("执行的ADB命令"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("CreatedBy") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("创建人"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("ADB操作的描述"); + + b.Property("DeviceId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("设备ID"); + + b.Property("IsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasComment("是否启用"); + + b.Property("Path") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("命令执行时所依赖的路径(当启用绝对路径时必填)"); + + b.Property("ScreenshotData") + .HasColumnType("BYTEA") + .HasComment("操作截图数据(字节数组)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("更新时间"); + + b.Property("UpdatedBy") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("更新人"); + + b.Property("UseAbsolutePath") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasComment("是否启用绝对路径"); + + b.Property("WaitTimeMs") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasComment("执行命令完需要等待的时间(毫秒)"); + + b.HasKey("Id"); + + b.HasIndex("Command") + .HasDatabaseName("IX_AdbOperations_Command"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_AdbOperations_CreatedAt"); + + b.HasIndex("DeviceId") + .HasDatabaseName("IX_AdbOperations_DeviceId"); + + b.HasIndex("IsEnabled") + .HasDatabaseName("IX_AdbOperations_IsEnabled"); + + b.ToTable("tb_adboperations", (string)null); + }); + + modelBuilder.Entity("X1.Domain.Entities.Terminal.AtOperation", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BaudRate") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(115200) + .HasComment("波特率"); + + b.Property("Command") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasComment("AT命令内容"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("创建时间"); + + b.Property("CreatedBy") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("创建人"); + + b.Property("DataBits") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(8) + .HasComment("数据位"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasComment("操作描述"); + + b.Property("DeviceId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("设备ID"); + + b.Property("IsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasComment("是否启用"); + + b.Property("Parameters") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasDefaultValue("") + .HasComment("命令参数(JSON格式)"); + + b.Property("Parity") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(10) + .HasColumnType("character varying(10)") + .HasDefaultValue("NONE") + .HasComment("校验位"); + + b.Property("Port") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("串口端口"); + + b.Property("ScreenshotData") + .HasColumnType("BYTEA") + .HasComment("操作截图数据(字节数组)"); + + b.Property("StopBits") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(10) + .HasColumnType("character varying(10)") + .HasDefaultValue("1") + .HasComment("停止位"); + + b.Property("Timeout") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(30) + .HasComment("超时时间(秒)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasComment("更新时间"); + + b.Property("UpdatedBy") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasComment("更新人"); + + b.HasKey("Id"); + + b.HasIndex("Command") + .HasDatabaseName("IX_AtOperations_Command"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_AtOperations_CreatedAt"); + + b.HasIndex("DeviceId") + .HasDatabaseName("IX_AtOperations_DeviceId"); + + b.HasIndex("IsEnabled") + .HasDatabaseName("IX_AtOperations_IsEnabled"); + + b.ToTable("tb_atoperations", (string)null); + }); + + modelBuilder.Entity("X1.Domain.Entities.Terminal.TerminalDevice", b => + { + b.Property("Id") + .HasMaxLength(450) + .HasColumnType("character varying(450)"); + + b.Property("Alias") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("AndroidVersion") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("BootSerial") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Brand") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BuildId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("BuildType") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Device") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Hardware") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("HardwarePlatform") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("IsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("LastConnectedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("LastDisconnectedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Locale") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Model") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SdkVersion") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Serial") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ServiceSerial") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("AndroidVersion"); + + b.HasIndex("Brand"); + + b.HasIndex("Hardware"); + + b.HasIndex("IsEnabled"); + + b.HasIndex("LastConnectedAt"); + + b.HasIndex("Model"); + + b.HasIndex("Serial") + .IsUnique(); + + b.HasIndex("ServiceSerial"); + + b.HasIndex("Status"); + + b.ToTable("tb_terminal_devices", (string)null); + }); + + modelBuilder.Entity("X1.Domain.Entities.Terminal.TerminalService", b => + { + b.Property("Id") + .HasMaxLength(450) + .HasColumnType("character varying(450)"); + + b.Property("AgentPort") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(45) + .HasColumnType("character varying(45)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("IsServiceStarted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SerialNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ServiceCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ServiceType") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasMaxLength(450) + .HasColumnType("character varying(450)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IpAddress"); + + b.HasIndex("IsEnabled"); + + b.HasIndex("IsServiceStarted"); + + b.HasIndex("SerialNumber") + .IsUnique(); + + b.HasIndex("ServiceCode") + .IsUnique(); + + b.HasIndex("ServiceType"); + + b.ToTable("tb_terminal_services", (string)null); + }); + + modelBuilder.Entity("X1.Domain.Entities.TestCase.CaseStepConfig", b => + { + b.Property("Id") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("createdat"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("createdby"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("description"); + + b.Property("FormType") + .HasColumnType("integer") + .HasColumnName("formtype"); + + b.Property("Icon") + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("icon"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsEnabled") + .HasColumnType("boolean") + .HasColumnName("isenabled"); + + b.Property("Mapping") + .HasColumnType("integer") + .HasColumnName("mapping"); + + b.Property("StepName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("stepname"); + + b.Property("StepType") + .HasColumnType("integer") + .HasColumnName("steptype"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updatedat"); + + b.Property("UpdatedBy") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("updatedby"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("FormType"); + + b.HasIndex("IsEnabled"); + + b.HasIndex("Mapping"); + + b.HasIndex("StepType"); + + b.ToTable("tb_casestepconfig", (string)null); + }); + + modelBuilder.Entity("X1.Domain.Entities.TestCase.ImsiRegistrationRecord", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("IsDualSim") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("NodeId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Sim1CellId") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Sim1Plmn") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Sim1RegistrationWaitTime") + .HasColumnType("integer"); + + b.Property("Sim2CellId") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Sim2Plmn") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Sim2RegistrationWaitTime") + .HasColumnType("integer"); + + b.Property("TestCaseId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_tb_imsi_registration_records_createdat"); + + b.HasIndex("NodeId") + .HasDatabaseName("IX_tb_imsi_registration_records_nodeid"); + + b.HasIndex("TestCaseId") + .HasDatabaseName("IX_tb_imsi_registration_records_testcaseid"); + + b.HasIndex("TestCaseId", "NodeId") + .IsUnique() + .HasDatabaseName("IX_tb_imsi_registration_records_testcaseid_nodeid"); + + b.ToTable("tb_imsi_registration_records", null, t => + { + t.HasComment("IMSI注册记录表"); + }); + }); + + modelBuilder.Entity("X1.Domain.Entities.TestCase.ScenarioTestCase", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ExecutionOrder") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("IsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("LoopCount") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("ScenarioId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("TestCaseFlowId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ScenarioId"); + + b.HasIndex("TestCaseFlowId"); + + b.HasIndex("ScenarioId", "ExecutionOrder"); + + b.HasIndex("ScenarioId", "TestCaseFlowId") + .IsUnique(); + + b.ToTable("tb_scenariotestcases", (string)null); + }); + + modelBuilder.Entity("X1.Domain.Entities.TestCase.TestCaseEdge", b => + { + b.Property("Id") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("id"); + + b.Property("Condition") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("condition"); + + b.Property("EdgeId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("edgeid"); + + b.Property("EdgeType") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("edgetype"); + + b.Property("IsAnimated") + .HasColumnType("boolean") + .HasColumnName("isanimated"); + + b.Property("SourceHandle") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("SourceNodeId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("sourcenodeid"); + + b.Property("Style") + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("style"); + + b.Property("TargetHandle") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("TargetNodeId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("targetnodeid"); + + b.Property("TestCaseId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("testcaseid"); + + b.HasKey("Id"); + + b.HasIndex("EdgeId") + .HasDatabaseName("IX_testcaseedge_edgeid"); + + b.HasIndex("SourceNodeId") + .HasDatabaseName("IX_testcaseedge_sourcenodeid"); + + b.HasIndex("TargetNodeId") + .HasDatabaseName("IX_testcaseedge_targetnodeid"); + + b.HasIndex("TestCaseId") + .HasDatabaseName("IX_testcaseedge_testcaseid"); + + b.ToTable("tb_testcaseedge", (string)null); + }); + + modelBuilder.Entity("X1.Domain.Entities.TestCase.TestCaseFlow", b => + { + b.Property("Id") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("id"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("createdat"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("createdby"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasColumnName("description"); + + b.Property("IsEnabled") + .HasColumnType("boolean") + .HasColumnName("isenabled"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("name"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updatedat"); + + b.Property("UpdatedBy") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("updatedby"); + + b.Property("ViewportX") + .HasColumnType("double precision") + .HasColumnName("viewport_x"); + + b.Property("ViewportY") + .HasColumnType("double precision") + .HasColumnName("viewport_y"); + + b.Property("ViewportZoom") + .HasColumnType("double precision") + .HasColumnName("viewport_zoom"); + + b.HasKey("Id"); + + b.HasIndex("IsEnabled") + .HasDatabaseName("IX_testcaseflow_isenabled"); + + b.HasIndex("Name") + .HasDatabaseName("IX_testcaseflow_name"); + + b.HasIndex("Type") + .HasDatabaseName("IX_testcaseflow_type"); + + b.ToTable("tb_testcaseflow", (string)null); + }); + + modelBuilder.Entity("X1.Domain.Entities.TestCase.TestCaseNode", b => + { + b.Property("Id") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("id"); + + b.Property("Height") + .HasColumnType("double precision") + .HasColumnName("height"); + + b.Property("IsDragging") + .HasColumnType("boolean") + .HasColumnName("isdragging"); + + b.Property("IsSelected") + .HasColumnType("boolean") + .HasColumnName("isselected"); + + b.Property("NodeId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("nodeid"); + + b.Property("PositionAbsoluteX") + .HasColumnType("double precision") + .HasColumnName("positionabsolutex"); + + b.Property("PositionAbsoluteY") + .HasColumnType("double precision") + .HasColumnName("positionabsolutey"); + + b.Property("PositionX") + .HasColumnType("double precision") + .HasColumnName("positionx"); + + b.Property("PositionY") + .HasColumnType("double precision") + .HasColumnName("positiony"); + + b.Property("SequenceNumber") + .HasColumnType("integer") + .HasColumnName("sequencenumber"); + + b.Property("StepId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("stepid"); + + b.Property("TestCaseId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("testcaseid"); + + b.Property("Width") + .HasColumnType("double precision") + .HasColumnName("width"); + + b.HasKey("Id"); + + b.HasIndex("NodeId") + .HasDatabaseName("IX_testcasenode_nodeid"); + + b.HasIndex("SequenceNumber") + .HasDatabaseName("IX_testcasenode_sequencenumber"); + + b.HasIndex("StepId"); + + b.HasIndex("TestCaseId") + .HasDatabaseName("IX_testcasenode_testcaseid"); + + b.ToTable("tb_testcasenode", (string)null); + }); + + modelBuilder.Entity("X1.Domain.Entities.TestCase.TestScenario", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("IsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("ScenarioCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ScenarioName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("IsEnabled"); + + b.HasIndex("ScenarioCode") + .IsUnique(); + + b.HasIndex("Type"); + + b.ToTable("tb_testscenarios", (string)null); + }); + + modelBuilder.Entity("X1.Domain.Entities.UserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("tb_user_roles", null, t => + { + t.HasComment("用户角色关系表"); + }); + }); + + modelBuilder.Entity("X1.Domain.Entities.Device.CellularDeviceRuntime", b => + { + b.HasOne("X1.Domain.Entities.Device.CellularDevice", "Device") + .WithOne("Runtime") + .HasForeignKey("X1.Domain.Entities.Device.CellularDeviceRuntime", "DeviceCode") + .HasPrincipalKey("X1.Domain.Entities.Device.CellularDevice", "DeviceCode") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Device"); + }); + + modelBuilder.Entity("X1.Domain.Entities.Device.ProtocolVersion", b => + { + b.HasOne("X1.Domain.Entities.Device.CellularDevice", null) + .WithMany("ProtocolVersions") + .HasForeignKey("SerialNumber") + .HasPrincipalKey("SerialNumber") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("X1.Domain.Entities.Logging.LoginLog", b => + { + b.HasOne("X1.Domain.Entities.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("X1.Domain.Entities.NetworkProfile.NetworkStackConfig", b => + { + b.HasOne("X1.Domain.Entities.NetworkProfile.RAN_Configuration", null) + .WithMany() + .HasForeignKey("RanId") + .OnDelete(DeleteBehavior.SetNull); + }); + + modelBuilder.Entity("X1.Domain.Entities.NetworkProfile.Stack_CoreIMS_Binding", b => + { + b.HasOne("X1.Domain.Entities.NetworkProfile.CoreNetworkConfig", "CoreNetworkConfig") + .WithMany() + .HasForeignKey("CnId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("X1.Domain.Entities.NetworkProfile.IMS_Configuration", "IMSConfiguration") + .WithMany() + .HasForeignKey("ImsId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("X1.Domain.Entities.NetworkProfile.NetworkStackConfig", "NetworkStackConfig") + .WithMany("StackCoreIMSBindings") + .HasForeignKey("NetworkStackConfigId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CoreNetworkConfig"); + + b.Navigation("IMSConfiguration"); + + b.Navigation("NetworkStackConfig"); + }); + + modelBuilder.Entity("X1.Domain.Entities.RolePermission", b => + { + b.HasOne("X1.Domain.Entities.Permission", "Permission") + .WithMany("RolePermissions") + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("X1.Domain.Entities.AppRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permission"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("X1.Domain.Entities.TestCase.ImsiRegistrationRecord", b => + { + b.HasOne("X1.Domain.Entities.TestCase.TestCaseNode", "TestCaseNode") + .WithMany() + .HasForeignKey("NodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("X1.Domain.Entities.TestCase.TestCaseFlow", "TestCase") + .WithMany() + .HasForeignKey("TestCaseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TestCase"); + + b.Navigation("TestCaseNode"); + }); + + modelBuilder.Entity("X1.Domain.Entities.TestCase.ScenarioTestCase", b => + { + b.HasOne("X1.Domain.Entities.TestCase.TestScenario", "Scenario") + .WithMany("ScenarioTestCases") + .HasForeignKey("ScenarioId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("X1.Domain.Entities.TestCase.TestCaseFlow", "TestCaseFlow") + .WithMany("TestScenarioTestCases") + .HasForeignKey("TestCaseFlowId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Scenario"); + + b.Navigation("TestCaseFlow"); + }); + + modelBuilder.Entity("X1.Domain.Entities.TestCase.TestCaseEdge", b => + { + b.HasOne("X1.Domain.Entities.TestCase.TestCaseFlow", "TestCase") + .WithMany("Edges") + .HasForeignKey("TestCaseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TestCase"); + }); + + modelBuilder.Entity("X1.Domain.Entities.TestCase.TestCaseNode", b => + { + b.HasOne("X1.Domain.Entities.TestCase.CaseStepConfig", "StepConfig") + .WithMany() + .HasForeignKey("StepId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("X1.Domain.Entities.TestCase.TestCaseFlow", "TestCase") + .WithMany("Nodes") + .HasForeignKey("TestCaseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("StepConfig"); + + b.Navigation("TestCase"); + }); + + modelBuilder.Entity("X1.Domain.Entities.UserRole", b => + { + b.HasOne("X1.Domain.Entities.AppRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("X1.Domain.Entities.AppUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("X1.Domain.Entities.Device.CellularDevice", b => + { + b.Navigation("ProtocolVersions"); + + b.Navigation("Runtime"); + }); + + modelBuilder.Entity("X1.Domain.Entities.NetworkProfile.NetworkStackConfig", b => + { + b.Navigation("StackCoreIMSBindings"); + }); + + modelBuilder.Entity("X1.Domain.Entities.Permission", b => + { + b.Navigation("RolePermissions"); + }); + + modelBuilder.Entity("X1.Domain.Entities.TestCase.TestCaseFlow", b => + { + b.Navigation("Edges"); + + b.Navigation("Nodes"); + + b.Navigation("TestScenarioTestCases"); + }); + + modelBuilder.Entity("X1.Domain.Entities.TestCase.TestScenario", b => + { + b.Navigation("ScenarioTestCases"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/X1.Infrastructure/Migrations/20250824112436_AddTestScenarioTables.cs b/src/X1.Infrastructure/Migrations/20250824112436_AddTestScenarioTables.cs new file mode 100644 index 0000000..48282b5 --- /dev/null +++ b/src/X1.Infrastructure/Migrations/20250824112436_AddTestScenarioTables.cs @@ -0,0 +1,114 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace X1.Infrastructure.Migrations +{ + /// + public partial class AddTestScenarioTables : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "tb_testscenarios", + columns: table => new + { + Id = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + ScenarioCode = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + ScenarioName = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + Type = table.Column(type: "text", nullable: false), + Description = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true), + IsEnabled = table.Column(type: "boolean", nullable: false, defaultValue: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false), + CreatedBy = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + UpdatedBy = table.Column(type: "character varying(50)", maxLength: 50, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_tb_testscenarios", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "tb_scenariotestcases", + columns: table => new + { + Id = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + ScenarioId = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + TestCaseFlowId = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + IsEnabled = table.Column(type: "boolean", nullable: false, defaultValue: true), + ExecutionOrder = table.Column(type: "integer", nullable: false, defaultValue: 0), + LoopCount = table.Column(type: "integer", nullable: false, defaultValue: 1), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false), + CreatedBy = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + UpdatedBy = table.Column(type: "character varying(50)", maxLength: 50, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_tb_scenariotestcases", x => x.Id); + table.ForeignKey( + name: "FK_tb_scenariotestcases_tb_testcaseflow_TestCaseFlowId", + column: x => x.TestCaseFlowId, + principalTable: "tb_testcaseflow", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_tb_scenariotestcases_tb_testscenarios_ScenarioId", + column: x => x.ScenarioId, + principalTable: "tb_testscenarios", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_tb_scenariotestcases_ScenarioId", + table: "tb_scenariotestcases", + column: "ScenarioId"); + + migrationBuilder.CreateIndex( + name: "IX_tb_scenariotestcases_ScenarioId_ExecutionOrder", + table: "tb_scenariotestcases", + columns: new[] { "ScenarioId", "ExecutionOrder" }); + + migrationBuilder.CreateIndex( + name: "IX_tb_scenariotestcases_ScenarioId_TestCaseFlowId", + table: "tb_scenariotestcases", + columns: new[] { "ScenarioId", "TestCaseFlowId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_tb_scenariotestcases_TestCaseFlowId", + table: "tb_scenariotestcases", + column: "TestCaseFlowId"); + + migrationBuilder.CreateIndex( + name: "IX_tb_testscenarios_IsEnabled", + table: "tb_testscenarios", + column: "IsEnabled"); + + migrationBuilder.CreateIndex( + name: "IX_tb_testscenarios_ScenarioCode", + table: "tb_testscenarios", + column: "ScenarioCode", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_tb_testscenarios_Type", + table: "tb_testscenarios", + column: "Type"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "tb_scenariotestcases"); + + migrationBuilder.DropTable( + name: "tb_testscenarios"); + } + } +} diff --git a/src/X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs b/src/X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs index 4407ecf..eb68b0f 100644 --- a/src/X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs +++ b/src/X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs @@ -1552,6 +1552,67 @@ namespace X1.Infrastructure.Migrations }); }); + modelBuilder.Entity("X1.Domain.Entities.TestCase.ScenarioTestCase", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ExecutionOrder") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("IsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("LoopCount") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("ScenarioId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("TestCaseFlowId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ScenarioId"); + + b.HasIndex("TestCaseFlowId"); + + b.HasIndex("ScenarioId", "ExecutionOrder"); + + b.HasIndex("ScenarioId", "TestCaseFlowId") + .IsUnique(); + + b.ToTable("tb_scenariotestcases", (string)null); + }); + modelBuilder.Entity("X1.Domain.Entities.TestCase.TestCaseEdge", b => { b.Property("Id") @@ -1775,6 +1836,63 @@ namespace X1.Infrastructure.Migrations b.ToTable("tb_testcasenode", (string)null); }); + modelBuilder.Entity("X1.Domain.Entities.TestCase.TestScenario", b => + { + b.Property("Id") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("IsEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("ScenarioCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ScenarioName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("IsEnabled"); + + b.HasIndex("ScenarioCode") + .IsUnique(); + + b.HasIndex("Type"); + + b.ToTable("tb_testscenarios", (string)null); + }); + modelBuilder.Entity("X1.Domain.Entities.UserRole", b => { b.Property("UserId") @@ -1897,6 +2015,25 @@ namespace X1.Infrastructure.Migrations b.Navigation("TestCaseNode"); }); + modelBuilder.Entity("X1.Domain.Entities.TestCase.ScenarioTestCase", b => + { + b.HasOne("X1.Domain.Entities.TestCase.TestScenario", "Scenario") + .WithMany("ScenarioTestCases") + .HasForeignKey("ScenarioId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("X1.Domain.Entities.TestCase.TestCaseFlow", "TestCaseFlow") + .WithMany("TestScenarioTestCases") + .HasForeignKey("TestCaseFlowId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Scenario"); + + b.Navigation("TestCaseFlow"); + }); + modelBuilder.Entity("X1.Domain.Entities.TestCase.TestCaseEdge", b => { b.HasOne("X1.Domain.Entities.TestCase.TestCaseFlow", "TestCase") @@ -1968,6 +2105,13 @@ namespace X1.Infrastructure.Migrations b.Navigation("Edges"); b.Navigation("Nodes"); + + b.Navigation("TestScenarioTestCases"); + }); + + modelBuilder.Entity("X1.Domain.Entities.TestCase.TestScenario", b => + { + b.Navigation("ScenarioTestCases"); }); #pragma warning restore 612, 618 } diff --git a/src/X1.Presentation/Controllers/TestCaseFlowController.cs b/src/X1.Presentation/Controllers/TestCaseFlowController.cs index 23f6c93..3755d45 100644 --- a/src/X1.Presentation/Controllers/TestCaseFlowController.cs +++ b/src/X1.Presentation/Controllers/TestCaseFlowController.cs @@ -147,12 +147,12 @@ public class TestCaseFlowController : ApiController /// 获取测试流程类型列表 /// /// 测试流程类型数据 - [HttpGet("form-type-step")] - public async Task> GetFormTypeStepTypeMapping() + [HttpGet("test-flow-types")] + public async Task> GetTestFlowTypes() { _logger.LogInformation("获取测试流程类型列表"); - var query = new GetFormTypeStepTypeMappingQuery(); + var query = new GetTestFlowTypesQuery(); var result = await mediator.Send(query); if (!result.IsSuccess) { diff --git a/src/X1.Presentation/Controllers/TestScenariosController.cs b/src/X1.Presentation/Controllers/TestScenariosController.cs index 0dc8e46..9068f50 100644 --- a/src/X1.Presentation/Controllers/TestScenariosController.cs +++ b/src/X1.Presentation/Controllers/TestScenariosController.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using X1.Application.Features.TestScenarios.Queries.GetTestScenarios; using X1.Application.Features.TestScenarios.Queries.GetTestScenarioById; +using X1.Application.Features.TestScenarios.Queries.GetScenarioTypes; using X1.Application.Features.TestScenarios.Commands.CreateTestScenario; using X1.Application.Features.TestScenarios.Commands.UpdateTestScenario; using X1.Application.Features.TestScenarios.Commands.DeleteTestScenario; @@ -33,6 +34,27 @@ public class TestScenariosController : ApiController _logger = logger; } + /// + /// 获取测试场景类型列表 + /// + /// 测试场景类型列表 + [HttpGet("types")] + public async Task> GetScenarioTypes() + { + _logger.LogInformation("获取测试场景类型列表"); + + var query = new GetScenarioTypesQuery(); + var result = await mediator.Send(query); + if (!result.IsSuccess) + { + _logger.LogWarning("获取测试场景类型列表失败: {ErrorMessages}", string.Join(", ", result.ErrorMessages ?? new List())); + return result; + } + + _logger.LogInformation("成功获取测试场景类型列表,总数: {Count}", result.Data?.ScenarioTypes?.Count ?? 0); + return result; + } + /// /// 获取测试场景列表 /// @@ -100,7 +122,7 @@ public class TestScenariosController : ApiController } _logger.LogInformation("成功获取测试场景详情,ID: {Id}, 名称: {Name}", - id, result.Data?.TestScenario?.ScenarioName); + id, result.Data?.ScenarioName); return result; } @@ -124,7 +146,7 @@ public class TestScenariosController : ApiController } _logger.LogInformation("成功创建测试场景,ID: {Id}, 名称: {ScenarioName}", - result.Data?.Id, result.Data?.ScenarioName); + result.Data?.TestScenarioId, result.Data?.ScenarioName); return result; } @@ -209,39 +231,38 @@ public class TestScenariosController : ApiController scenarioId, string.Join(", ", result.ErrorMessages ?? new List())); return result; } - - _logger.LogInformation("成功获取场景测试用例列表,场景ID: {ScenarioId}, 测试用例数量: {Count}", - scenarioId, result.Data?.ScenarioTestCases?.Count ?? 0); return result; } /// /// 创建场景测试用例 /// - /// 场景ID - /// 创建场景测试用例命令 + /// 创建场景测试用例请求 /// 创建结果 - [HttpPost("{scenarioId}/testcases")] + [HttpPost("testcases")] public async Task> CreateScenarioTestCase( - string scenarioId, - [FromBody] CreateScenarioTestCaseCommand command) + [FromBody] CreateScenarioTestCaseRequest request) { _logger.LogInformation("开始创建场景测试用例,场景ID: {ScenarioId}, 测试用例数量: {TestCaseCount}", - scenarioId, command.TestCases?.Count ?? 0); + request.ScenarioId, request.TestCases?.Count ?? 0); - // 确保命令中的场景ID与路由参数一致 - command.ScenarioId = scenarioId; + // 创建命令 + var command = new CreateScenarioTestCaseCommand + { + ScenarioId = request.ScenarioId, + TestCases = request.TestCases + }; var result = await mediator.Send(command); if (!result.IsSuccess) { _logger.LogWarning("创建场景测试用例失败,场景ID: {ScenarioId}, 错误: {ErrorMessages}", - scenarioId, string.Join(", ", result.ErrorMessages ?? new List())); + request.ScenarioId, string.Join(", ", result.ErrorMessages ?? new List())); return result; } _logger.LogInformation("成功创建场景测试用例,场景ID: {ScenarioId}, 成功数量: {SuccessCount}, 失败数量: {FailureCount}", - scenarioId, result.Data?.SuccessCount ?? 0, result.Data?.FailureCount ?? 0); + request.ScenarioId, result.Data?.SuccessCount ?? 0, result.Data?.FailureCount ?? 0); return result; } } diff --git a/src/X1.WebUI/src/components/layout/Content.tsx b/src/X1.WebUI/src/components/layout/Content.tsx index c4ac8ef..df325be 100644 --- a/src/X1.WebUI/src/components/layout/Content.tsx +++ b/src/X1.WebUI/src/components/layout/Content.tsx @@ -6,10 +6,8 @@ interface ContentProps { export function Content({ children }: ContentProps) { return ( -
-
- {children} -
+
+ {children}
); } \ No newline at end of file diff --git a/src/X1.WebUI/src/components/testcases/SaveTestCaseForm.tsx b/src/X1.WebUI/src/components/testcases/SaveTestCaseForm.tsx index 2ec75e1..36a1019 100644 --- a/src/X1.WebUI/src/components/testcases/SaveTestCaseForm.tsx +++ b/src/X1.WebUI/src/components/testcases/SaveTestCaseForm.tsx @@ -5,7 +5,7 @@ import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Label } from '@/components/ui/label'; -import { TestFlowType } from '@/services/testcaseService'; +import { TestFlowType, TestFlowTypeDto } from '@/services/testcaseService'; interface SaveTestCaseFormProps { open: boolean; @@ -13,6 +13,7 @@ interface SaveTestCaseFormProps { onSave: (data: { name: string; description: string; type: TestFlowType }) => void; loading?: boolean; nodeCount?: number; + testFlowTypes?: TestFlowTypeDto[]; } export default function SaveTestCaseForm({ @@ -20,7 +21,8 @@ export default function SaveTestCaseForm({ onOpenChange, onSave, loading = false, - nodeCount = 0 + nodeCount = 0, + testFlowTypes = [] }: SaveTestCaseFormProps) { const [formData, setFormData] = useState({ name: '', @@ -85,11 +87,11 @@ export default function SaveTestCaseForm({ - 功能测试 - 性能测试 - 安全测试 - UI测试 - API测试 + {testFlowTypes.map((flowType) => ( + + {flowType.name} + + ))} diff --git a/src/X1.WebUI/src/components/testcases/TestCaseDetailDrawer.tsx b/src/X1.WebUI/src/components/testcases/TestCaseDetailDrawer.tsx index 74a00bf..89639b2 100644 --- a/src/X1.WebUI/src/components/testcases/TestCaseDetailDrawer.tsx +++ b/src/X1.WebUI/src/components/testcases/TestCaseDetailDrawer.tsx @@ -406,7 +406,7 @@ function TestCaseDetailDrawerInner({ testCaseId, open, onOpenChange }: TestCaseD target: edge.target, sourceHandle: edge.sourceHandle, targetHandle: edge.targetHandle, - type: 'smoothstep', + type: edge.type, style: { stroke: '#3b82f6', strokeWidth: 2 }, markerEnd: { type: 'arrowclosed' as const, diff --git a/src/X1.WebUI/src/constants/api.ts b/src/X1.WebUI/src/constants/api.ts index 13c8967..203f764 100644 --- a/src/X1.WebUI/src/constants/api.ts +++ b/src/X1.WebUI/src/constants/api.ts @@ -31,8 +31,8 @@ export const API_PATHS = { // 用户相关 USERS: '/users', ROLES: '/roles', - PERMISSIONS: '/api/permissions', - ROLE_PERMISSIONS: '/api/role-permissions', + PERMISSIONS: '/permissions', + ROLE_PERMISSIONS: '/role-permissions', // 认证相关 AUTH: { @@ -49,6 +49,7 @@ export const API_PATHS = { // 场景相关 SCENARIOS: '/scenarios', + TEST_SCENARIOS: '/testscenarios', // 测试用例流程相关 TEST_CASE_FLOW: '/testcaseflow', diff --git a/src/X1.WebUI/src/constants/menuConfig.ts b/src/X1.WebUI/src/constants/menuConfig.ts index f6a97bb..635f53f 100644 --- a/src/X1.WebUI/src/constants/menuConfig.ts +++ b/src/X1.WebUI/src/constants/menuConfig.ts @@ -14,7 +14,6 @@ export type Permission = // 场景管理权限 | 'scenarios.view' | 'scenarios.manage' - | 'scenarios.create' // 用例管理权限 | 'testcases.view' | 'testcases.manage' @@ -105,9 +104,14 @@ export const menuItems: MenuItem[] = [ permission: 'scenarios.view', }, { - title: '创建场景', - href: '/dashboard/scenarios/create', - permission: 'scenarios.create', + title: '场景配置', + href: '/dashboard/scenarios/config', + permission: 'scenarios.manage', + }, + { + title: '场景绑定', + href: '/dashboard/scenarios/binding', + permission: 'scenarios.manage', }, ], }, diff --git a/src/X1.WebUI/src/contexts/AuthContext.tsx b/src/X1.WebUI/src/contexts/AuthContext.tsx index 05712bf..1422bbd 100644 --- a/src/X1.WebUI/src/contexts/AuthContext.tsx +++ b/src/X1.WebUI/src/contexts/AuthContext.tsx @@ -38,9 +38,9 @@ const getDefaultPermissions = (userPermissions: Record = {}) => "roles.view", "permissions.view", 'settings.view', + // 场景管理权限 'scenarios.view', 'scenarios.manage', - 'scenarios.create', 'testcases.view', 'testcases.manage', 'testcases.create', diff --git a/src/X1.WebUI/src/pages/scenarios/ScenarioForm.tsx b/src/X1.WebUI/src/pages/scenarios/ScenarioForm.tsx deleted file mode 100644 index 22e59df..0000000 --- a/src/X1.WebUI/src/pages/scenarios/ScenarioForm.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import React, { useState } from 'react'; -import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; -import { CreateScenarioRequest, UpdateScenarioRequest } from '@/services/scenarioService'; - -interface ScenarioFormProps { - onSubmit: (data: CreateScenarioRequest | UpdateScenarioRequest) => void; - initialData?: Partial; -} - -export default function ScenarioForm({ onSubmit, initialData }: ScenarioFormProps) { - const [formData, setFormData] = useState({ - name: initialData?.name || '', - description: initialData?.description || '', - status: initialData?.status || 'draft', - priority: initialData?.priority || 'medium', - tags: initialData?.tags || [], - requirements: initialData?.requirements || '' - }); - - const [newTag, setNewTag] = useState(''); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - onSubmit(formData); - }; - - const addTag = () => { - if (newTag.trim() && !formData.tags.includes(newTag.trim())) { - setFormData(prev => ({ - ...prev, - tags: [...prev.tags, newTag.trim()] - })); - setNewTag(''); - } - }; - - const removeTag = (tagToRemove: string) => { - setFormData(prev => ({ - ...prev, - tags: prev.tags.filter(tag => tag !== tagToRemove) - })); - }; - - const handleKeyPress = (e: React.KeyboardEvent) => { - if (e.key === 'Enter') { - e.preventDefault(); - addTag(); - } - }; - - return ( -
-
- - setFormData({ ...formData, name: e.target.value })} - required - placeholder="请输入场景名称" - /> -
- -
- -