diff --git a/src/X1.Application/Features/CaseStepConfigs/Commands/CreateCaseStepConfig/CreateCaseStepConfigCommand.cs b/src/X1.Application/Features/CaseStepConfigs/Commands/CreateCaseStepConfig/CreateCaseStepConfigCommand.cs index 378495e..f35d861 100644 --- a/src/X1.Application/Features/CaseStepConfigs/Commands/CreateCaseStepConfig/CreateCaseStepConfigCommand.cs +++ b/src/X1.Application/Features/CaseStepConfigs/Commands/CreateCaseStepConfig/CreateCaseStepConfigCommand.cs @@ -47,4 +47,9 @@ public class CreateCaseStepConfigCommand : IRequest public bool IsEnabled { get; set; } = true; + + /// + /// 表单类型 + /// + public FormType FormType { get; set; } = FormType.None; } diff --git a/src/X1.Application/Features/CaseStepConfigs/Commands/CreateCaseStepConfig/CreateCaseStepConfigCommandHandler.cs b/src/X1.Application/Features/CaseStepConfigs/Commands/CreateCaseStepConfig/CreateCaseStepConfigCommandHandler.cs index af804ec..207ffc2 100644 --- a/src/X1.Application/Features/CaseStepConfigs/Commands/CreateCaseStepConfig/CreateCaseStepConfigCommandHandler.cs +++ b/src/X1.Application/Features/CaseStepConfigs/Commands/CreateCaseStepConfig/CreateCaseStepConfigCommandHandler.cs @@ -66,7 +66,8 @@ public class CreateUseCaseNodeConfigCommandHandler : IRequestHandler public bool IsEnabled { get; set; } + /// + /// 表单类型 + /// + public int FormType { get; set; } + + /// + /// 表单类型名称 + /// + public string FormTypeName { get; set; } = null!; + /// /// 创建时间 /// diff --git a/src/X1.Application/Features/CaseStepConfigs/Commands/UpdateCaseStepConfig/UpdateCaseStepConfigCommand.cs b/src/X1.Application/Features/CaseStepConfigs/Commands/UpdateCaseStepConfig/UpdateCaseStepConfigCommand.cs index f298950..4a281e6 100644 --- a/src/X1.Application/Features/CaseStepConfigs/Commands/UpdateCaseStepConfig/UpdateCaseStepConfigCommand.cs +++ b/src/X1.Application/Features/CaseStepConfigs/Commands/UpdateCaseStepConfig/UpdateCaseStepConfigCommand.cs @@ -1,6 +1,7 @@ using X1.Domain.Common; using MediatR; using System.ComponentModel.DataAnnotations; +using X1.Domain.Entities.TestCase; namespace X1.Application.Features.CaseStepConfigs.Commands.UpdateCaseStepConfig; @@ -31,4 +32,9 @@ public class UpdateCaseStepConfigCommand : IRequest public bool IsEnabled { get; set; } = true; + + /// + /// 表单类型 + /// + public FormType FormType { get; set; } = FormType.None; } diff --git a/src/X1.Application/Features/CaseStepConfigs/Commands/UpdateCaseStepConfig/UpdateCaseStepConfigCommandHandler.cs b/src/X1.Application/Features/CaseStepConfigs/Commands/UpdateCaseStepConfig/UpdateCaseStepConfigCommandHandler.cs index cfc3636..96a1346 100644 --- a/src/X1.Application/Features/CaseStepConfigs/Commands/UpdateCaseStepConfig/UpdateCaseStepConfigCommandHandler.cs +++ b/src/X1.Application/Features/CaseStepConfigs/Commands/UpdateCaseStepConfig/UpdateCaseStepConfigCommandHandler.cs @@ -59,10 +59,11 @@ public class UpdateCaseStepConfigCommandHandler : IRequestHandler.CreateFailure("用户未认证,无法更新用例步骤配置"); } - // 更新步骤配置(只能修改启用、图标和步骤说明) + // 更新步骤配置(只能修改启用、图标、步骤说明和表单类型) existingConfig.Description = request.Description; existingConfig.Icon = request.Icon; existingConfig.IsEnabled = request.IsEnabled; + existingConfig.FormType = request.FormType; existingConfig.UpdatedAt = DateTime.UtcNow; existingConfig.UpdatedBy = currentUserId; @@ -80,6 +81,8 @@ public class UpdateCaseStepConfigCommandHandler : IRequestHandler public bool IsEnabled { get; set; } + /// + /// 表单类型 + /// + public int FormType { get; set; } + + /// + /// 表单类型名称 + /// + public string FormTypeName { get; set; } = null!; + /// /// 更新时间 /// diff --git a/src/X1.Application/Features/CaseStepConfigs/Queries/GetCaseStepConfigById/GetCaseStepConfigByIdQueryHandler.cs b/src/X1.Application/Features/CaseStepConfigs/Queries/GetCaseStepConfigById/GetCaseStepConfigByIdQueryHandler.cs index 0bdc87d..5b74986 100644 --- a/src/X1.Application/Features/CaseStepConfigs/Queries/GetCaseStepConfigById/GetCaseStepConfigByIdQueryHandler.cs +++ b/src/X1.Application/Features/CaseStepConfigs/Queries/GetCaseStepConfigById/GetCaseStepConfigByIdQueryHandler.cs @@ -51,6 +51,8 @@ public class GetCaseStepConfigByIdQueryHandler : IRequestHandler public bool IsEnabled { get; set; } + /// + /// 表单类型 + /// + public int FormType { get; set; } + + /// + /// 表单类型名称 + /// + public string FormTypeName { get; set; } = null!; + /// /// 创建时间 /// diff --git a/src/X1.Application/Features/CaseStepConfigs/Queries/GetCaseStepConfigs/GetCaseStepConfigsQueryHandler.cs b/src/X1.Application/Features/CaseStepConfigs/Queries/GetCaseStepConfigs/GetCaseStepConfigsQueryHandler.cs index 034bbe8..b3565c3 100644 --- a/src/X1.Application/Features/CaseStepConfigs/Queries/GetCaseStepConfigs/GetCaseStepConfigsQueryHandler.cs +++ b/src/X1.Application/Features/CaseStepConfigs/Queries/GetCaseStepConfigs/GetCaseStepConfigsQueryHandler.cs @@ -73,6 +73,8 @@ public class GetCaseStepConfigsQueryHandler : IRequestHandler public bool IsEnabled { get; set; } + /// + /// 表单类型 + /// + public int FormType { get; set; } + + /// + /// 表单类型名称 + /// + public string FormTypeName { get; set; } = null!; + /// /// 创建时间 /// diff --git a/src/X1.Application/Features/CaseStepConfigs/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeQuery.cs b/src/X1.Application/Features/CaseStepConfigs/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeQuery.cs new file mode 100644 index 0000000..980646d --- /dev/null +++ b/src/X1.Application/Features/CaseStepConfigs/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeQuery.cs @@ -0,0 +1,11 @@ +using MediatR; +using X1.Domain.Common; + +namespace X1.Application.Features.CaseStepConfigs.Queries.GetFormTypeStepTypeMapping; + +/// +/// 获取表单类型到步骤类型映射查询 +/// +public class GetFormTypeStepTypeQuery : IRequest> +{ +} diff --git a/src/X1.Application/Features/CaseStepConfigs/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeQueryHandler.cs b/src/X1.Application/Features/CaseStepConfigs/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeQueryHandler.cs new file mode 100644 index 0000000..3bc8492 --- /dev/null +++ b/src/X1.Application/Features/CaseStepConfigs/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeQueryHandler.cs @@ -0,0 +1,48 @@ +using MediatR; +using X1.Domain.Common; + +namespace X1.Application.Features.CaseStepConfigs.Queries.GetFormTypeStepTypeMapping; + +/// +/// 获取表单类型到步骤类型映射查询处理器 +/// +public class GetFormTypeStepTypeQueryHandler : IRequestHandler> +{ + /// + /// 处理查询 + /// + /// 查询请求 + /// 取消令牌 + /// 操作结果 + public async Task> Handle(GetFormTypeStepTypeQuery request, CancellationToken cancellationToken) + { + try + { + // 构建响应对象 + var response = new GetFormTypeStepTypeResponse(); + + // 获取表单类型列表 + response.FormTypes = FormTypeStepTypeConverter.GetFormTypes().Select(ft => new FormTypeDto + { + Value = ft.Value, + Name = ft.Name, + Description = ft.Description + }).ToList(); + + // 获取步骤类型列表 + response.StepTypes = FormTypeStepTypeConverter.GetStepTypes().Select(st => new StepTypeDto + { + Value = st.Value, + Name = st.Name, + 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/CaseStepConfigs/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeResponse.cs b/src/X1.Application/Features/CaseStepConfigs/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeResponse.cs new file mode 100644 index 0000000..d5c6f3f --- /dev/null +++ b/src/X1.Application/Features/CaseStepConfigs/Queries/GetFormTypeStepTypeMapping/GetFormTypeStepTypeResponse.cs @@ -0,0 +1,100 @@ +namespace X1.Application.Features.CaseStepConfigs.Queries.GetFormTypeStepTypeMapping; + +/// +/// 获取表单类型到步骤类型映射响应 +/// +public class GetFormTypeStepTypeResponse +{ + /// + /// 表单类型列表 + /// + public List FormTypes { get; set; } = new(); + + /// + /// 步骤类型列表 + /// + public List StepTypes { get; set; } = new(); +} + +/// +/// 表单类型DTO +/// +public class FormTypeDto +{ + /// + /// 表单类型值 + /// + public int Value { get; set; } + + /// + /// 表单类型名称 + /// + public string Name { get; set; } = null!; + + /// + /// 表单类型描述 + /// + public string Description { get; set; } = null!; +} + +/// +/// 步骤类型DTO +/// +public class StepTypeDto +{ + /// + /// 步骤类型值 + /// + public int Value { get; set; } + + /// + /// 步骤类型名称 + /// + public string Name { get; set; } = null!; + + /// + /// 步骤类型描述 + /// + public string Description { get; set; } = null!; +} + +/// +/// 映射关系DTO +/// +public class MappingDto +{ + /// + /// 表单类型 + /// + public int FormType { get; set; } + + /// + /// 表单类型名称 + /// + public string FormTypeName { get; set; } = null!; + + /// + /// 表单类型描述 + /// + public string FormTypeDescription { get; set; } = null!; + + /// + /// 推荐步骤类型 + /// + public int RecommendedStepType { get; set; } + + /// + /// 推荐步骤类型名称 + /// + public string RecommendedStepTypeName { get; set; } = null!; + + /// + /// 推荐步骤类型描述 + /// + public string RecommendedStepTypeDescription { get; set; } = null!; + + /// + /// 支持的步骤类型列表 + /// + public List SupportedStepTypes { get; set; } = new(); +} diff --git a/src/X1.Domain/Common/EnumValueObject.cs b/src/X1.Domain/Common/EnumValueObject.cs new file mode 100644 index 0000000..d8ae1a9 --- /dev/null +++ b/src/X1.Domain/Common/EnumValueObject.cs @@ -0,0 +1,22 @@ +namespace X1.Domain.Common; + +/// +/// 枚举项值对象 +/// +public class EnumValueObject +{ + /// + /// 枚举值 + /// + public int Value { get; set; } + + /// + /// 枚举名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 枚举描述 + /// + public string Description { get; set; } = string.Empty; +} diff --git a/src/X1.Domain/Common/FormTypeStepTypeConverter.cs b/src/X1.Domain/Common/FormTypeStepTypeConverter.cs new file mode 100644 index 0000000..de4e24e --- /dev/null +++ b/src/X1.Domain/Common/FormTypeStepTypeConverter.cs @@ -0,0 +1,140 @@ +using X1.Domain.Entities.TestCase; + +namespace X1.Domain.Common; + +/// +/// 表单类型到步骤类型转换器 +/// +public static class FormTypeStepTypeConverter +{ + /// + /// 根据表单类型获取推荐的步骤类型 + /// + /// 表单类型 + /// 推荐的步骤类型 + public static CaseStepType GetRecommendedStepType(FormType formType) + { + return formType switch + { + FormType.None => CaseStepType.Process, + FormType.Registration => CaseStepType.Process, + FormType.Ping => CaseStepType.Process, + FormType.Iperf => CaseStepType.Process, + FormType.Call => CaseStepType.Process, + _ => CaseStepType.Process + }; + } + + /// + /// 获取表单类型与步骤类型的映射关系 + /// + /// 表单类型到步骤类型的映射字典 + public static Dictionary GetFormTypeToStepTypeMapping() + { + return new Dictionary + { + { FormType.None, CaseStepType.Process }, + { FormType.Registration, CaseStepType.Process }, + { FormType.Ping, CaseStepType.Process }, + { FormType.Iperf, CaseStepType.Process }, + { FormType.Call, CaseStepType.Process } + }; + } + + /// + /// 获取步骤类型支持的表单类型列表 + /// + /// 步骤类型 + /// 支持的表单类型列表 + public static List GetSupportedFormTypes(CaseStepType stepType) + { + return stepType switch + { + CaseStepType.Start => new List { FormType.None }, + CaseStepType.End => new List { FormType.None }, + CaseStepType.Process => new List + { + FormType.None, + FormType.Registration, + FormType.Ping, + FormType.Iperf, + FormType.Call + }, + CaseStepType.Decision => new List { FormType.None }, + _ => new List { FormType.None } + }; + } + + /// + /// 检查表单类型是否与步骤类型兼容 + /// + /// 表单类型 + /// 步骤类型 + /// 是否兼容 + public static bool IsCompatible(FormType formType, CaseStepType stepType) + { + var supportedFormTypes = GetSupportedFormTypes(stepType); + return supportedFormTypes.Contains(formType); + } + + /// + /// 获取所有表单类型及其描述 + /// + /// 表单类型描述字典 + public static Dictionary GetFormTypeDescriptions() + { + return new Dictionary + { + { FormType.None, "无表单" }, + { FormType.Registration, "注册表单" }, + { FormType.Ping, "Ping表单" }, + { FormType.Iperf, "iperf表单" }, + { FormType.Call, "打电话表单" } + }; + } + + /// + /// 获取所有步骤类型及其描述 + /// + /// 步骤类型描述字典 + public static Dictionary GetStepTypeDescriptions() + { + return new Dictionary + { + { CaseStepType.Start, "开始步骤" }, + { CaseStepType.End, "结束步骤" }, + { CaseStepType.Process, "处理步骤" }, + { CaseStepType.Decision, "判断步骤" } + }; + } + + /// + /// 获取所有表单类型列表 + /// + /// 表单类型列表 + public static List GetFormTypes() + { + var formTypeDescriptions = GetFormTypeDescriptions(); + return formTypeDescriptions.Select(kvp => new EnumValueObject + { + Value = (int)kvp.Key, + Name = kvp.Key.ToString(), + Description = kvp.Value + }).ToList(); + } + + /// + /// 获取所有步骤类型列表 + /// + /// 步骤类型列表 + public static List GetStepTypes() + { + var stepTypeDescriptions = GetStepTypeDescriptions(); + return stepTypeDescriptions.Select(kvp => new EnumValueObject + { + Value = (int)kvp.Key, + Name = kvp.Key.ToString(), + Description = kvp.Value + }).ToList(); + } +} diff --git a/src/X1.Domain/Entities/TestCase/CaseStepConfig.cs b/src/X1.Domain/Entities/TestCase/CaseStepConfig.cs index f9a3585..9d391f6 100644 --- a/src/X1.Domain/Entities/TestCase/CaseStepConfig.cs +++ b/src/X1.Domain/Entities/TestCase/CaseStepConfig.cs @@ -45,6 +45,11 @@ public class CaseStepConfig : AuditableEntity /// public bool IsEnabled { get; set; } = true; + /// + /// 表单类型 + /// + public FormType FormType { get; set; } = FormType.None; + /// /// 默认构造函数 /// @@ -54,6 +59,7 @@ public class CaseStepConfig : AuditableEntity CreatedAt = DateTime.UtcNow; UpdatedAt = DateTime.UtcNow; IsEnabled = true; // 默认启用 + FormType = FormType.None; // 默认无表单 } /// @@ -66,6 +72,7 @@ public class CaseStepConfig : AuditableEntity /// 步骤说明 /// 步骤图标 /// 是否启用 + /// 表单类型 public static CaseStepConfig Create( string stepName, CaseStepType stepType, @@ -73,7 +80,8 @@ public class CaseStepConfig : AuditableEntity string createdBy, string? description = null, string? icon = null, - bool isEnabled = true) + bool isEnabled = true, + FormType formType = FormType.None) { var caseStepConfig = new CaseStepConfig { @@ -84,6 +92,7 @@ public class CaseStepConfig : AuditableEntity Description = description, Icon = icon, IsEnabled = isEnabled, + FormType = formType, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow, CreatedBy = createdBy, diff --git a/src/X1.Domain/Entities/TestCase/FormType.cs b/src/X1.Domain/Entities/TestCase/FormType.cs new file mode 100644 index 0000000..6d7f07d --- /dev/null +++ b/src/X1.Domain/Entities/TestCase/FormType.cs @@ -0,0 +1,32 @@ +namespace X1.Domain.Entities.TestCase; + +/// +/// 表单类型枚举 +/// +public enum FormType +{ + /// + /// 无表单 + /// + None = 0, + + /// + /// 注册表单 + /// + Registration = 1, + + /// + /// Ping表单 + /// + Ping = 2, + + /// + /// iperf表单 + /// + Iperf = 3, + + /// + /// 打电话表单 + /// + Call = 4 +} diff --git a/src/X1.Infrastructure/Configurations/TestCase/CaseStepConfigConfiguration.cs b/src/X1.Infrastructure/Configurations/TestCase/CaseStepConfigConfiguration.cs index 0f6f16d..ff6f47f 100644 --- a/src/X1.Infrastructure/Configurations/TestCase/CaseStepConfigConfiguration.cs +++ b/src/X1.Infrastructure/Configurations/TestCase/CaseStepConfigConfiguration.cs @@ -53,6 +53,10 @@ public class CaseStepConfigConfiguration : IEntityTypeConfiguration x.FormType) + .HasColumnName("formtype") + .IsRequired(); + builder.Property(x => x.CreatedAt) .HasColumnName("createdat") .IsRequired(); diff --git a/src/X1.Infrastructure/Migrations/20250822101301_AddFormTypeToCaseStepConfig.Designer.cs b/src/X1.Infrastructure/Migrations/20250822101301_AddFormTypeToCaseStepConfig.Designer.cs new file mode 100644 index 0000000..9296835 --- /dev/null +++ b/src/X1.Infrastructure/Migrations/20250822101301_AddFormTypeToCaseStepConfig.Designer.cs @@ -0,0 +1,1889 @@ +// +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("20250822101301_AddFormTypeToCaseStepConfig")] + partial class AddFormTypeToCaseStepConfig + { + /// + 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") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .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("IsEnabled") + .HasDatabaseName("ix_tb_casestepconfig_isenabled"); + + b.HasIndex("StepName") + .HasDatabaseName("ix_tb_casestepconfig_stepname"); + + b.HasIndex("StepType") + .HasDatabaseName("ix_tb_casestepconfig_steptype"); + + b.ToTable("tb_casestepconfig", (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.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.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"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/X1.Infrastructure/Migrations/20250822101301_AddFormTypeToCaseStepConfig.cs b/src/X1.Infrastructure/Migrations/20250822101301_AddFormTypeToCaseStepConfig.cs new file mode 100644 index 0000000..0e5e599 --- /dev/null +++ b/src/X1.Infrastructure/Migrations/20250822101301_AddFormTypeToCaseStepConfig.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace X1.Infrastructure.Migrations +{ + /// + public partial class AddFormTypeToCaseStepConfig : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "formtype", + table: "tb_casestepconfig", + type: "integer", + nullable: false, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "formtype", + table: "tb_casestepconfig"); + } + } +} diff --git a/src/X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs b/src/X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs index e4dc247..24ac683 100644 --- a/src/X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs +++ b/src/X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs @@ -1427,6 +1427,10 @@ namespace X1.Infrastructure.Migrations .HasColumnType("character varying(500)") .HasColumnName("description"); + b.Property("FormType") + .HasColumnType("integer") + .HasColumnName("formtype"); + b.Property("Icon") .HasMaxLength(100) .HasColumnType("character varying(100)") diff --git a/src/X1.Presentation/Controllers/CaseStepConfigController.cs b/src/X1.Presentation/Controllers/CaseStepConfigController.cs index b3269bd..6d4bca9 100644 --- a/src/X1.Presentation/Controllers/CaseStepConfigController.cs +++ b/src/X1.Presentation/Controllers/CaseStepConfigController.cs @@ -3,6 +3,7 @@ using X1.Application.Features.CaseStepConfigs.Commands.UpdateCaseStepConfig; using X1.Application.Features.CaseStepConfigs.Commands.DeleteCaseStepConfig; using X1.Application.Features.CaseStepConfigs.Queries.GetCaseStepConfigs; using X1.Application.Features.CaseStepConfigs.Queries.GetCaseStepConfigById; +using X1.Application.Features.CaseStepConfigs.Queries.GetFormTypeStepTypeMapping; using X1.Domain.Common; using X1.Presentation.Abstractions; using MediatR; @@ -132,4 +133,24 @@ public class CaseStepConfigController : ApiController _logger.LogInformation("成功删除用例步骤配置,配置ID: {ConfigId}", id); return result; } + + /// + /// 获取表单类型到步骤类型映射 + /// + [HttpGet("form-type-step-type-mapping")] + public async Task> GetFormTypeStepTypeMapping() + { + _logger.LogInformation("开始获取表单类型到步骤类型映射"); + + var result = await mediator.Send(new GetFormTypeStepTypeQuery()); + if (!result.IsSuccess) + { + _logger.LogWarning("获取表单类型到步骤类型映射失败: {Message}", result.ErrorMessages); + return result; + } + + _logger.LogInformation("成功获取表单类型到步骤类型映射,表单类型数量: {FormTypeCount}, 步骤类型数量: {StepTypeCount}", + result.Data?.FormTypes?.Count ?? 0, result.Data?.StepTypes?.Count ?? 0); + return result; + } } diff --git a/src/modify.md b/src/modify.md index 3a09746..1a77d49 100644 --- a/src/modify.md +++ b/src/modify.md @@ -1,5 +1,206 @@ # 修改记录 +## 2025-01-22 - CaseStepConfig 添加 FormType 字段数据库迁移 + +### 修改内容 +1. **创建数据库迁移**: + - 新建迁移文件:`20250822101301_AddFormTypeToCaseStepConfig.cs` + - 迁移名称:`AddFormTypeToCaseStepConfig` + - 迁移时间:2025-08-22 10:13:01 + +2. **迁移内容**: + - 为 `tb_casestepconfig` 表添加 `formtype` 字段 + - 字段类型:`integer`(对应 FormType 枚举) + - 约束:`NOT NULL` + - 默认值:`0`(对应 FormType.None) + +3. **迁移应用**: + - 成功应用迁移到数据库 + - 执行 SQL:`ALTER TABLE tb_casestepconfig ADD formtype integer NOT NULL DEFAULT 0;` + - 更新迁移历史表:`__EFMigrationsHistory` + +### 解决的问题 +- **数据库同步**:CaseStepConfig 实体已添加 FormType 属性,但数据库表缺少对应字段 +- **数据一致性**:确保实体模型与数据库表结构完全一致 +- **功能支持**:为表单类型功能提供数据库支持 + +### 技术特点 +- 使用 Entity Framework Core 迁移机制 +- 保持数据完整性,设置合理的默认值 +- 支持回滚操作(Down 方法) +- 完整的迁移历史记录 + +### 修改时间 +2025-01-22 + +### 修改原因 +用户反映 CaseStepConfig 有更新,需要重新迁移数据库表以反映实体模型的变更,特别是添加的 FormType 字段。 + +--- + +## 2025-01-21 - 添加表单类型到用例步骤配置 + +### 修改内容 +1. **创建表单类型枚举**: + - 新建 `X1.Domain/Entities/TestCase/FormType.cs` 文件 + - 定义 `FormType` 枚举,包含以下类型: + - `None = 0` - 无表单(默认) + - `Registration = 1` - 注册表单 + - `Ping = 2` - Ping表单 + - `Iperf = 3` - iperf表单 + - `Call = 4` - 打电话表单 + +2. **更新 CaseStepConfig 实体**: + - 在 `X1.Domain/Entities/TestCase/CaseStepConfig.cs` 中添加 `FormType` 属性 + - 默认值设置为 `FormType.None` + - 更新 `Create` 方法,添加 `formType` 参数,默认值为 `FormType.None` + - 更新默认构造函数,设置 `FormType = FormType.None` + +3. **更新数据库配置**: + - 在 `X1.Infrastructure/Configurations/TestCase/CaseStepConfigConfiguration.cs` 中添加 `FormType` 字段映射 + - 字段名设置为 `formtype`,设置为必填字段 + +4. **更新应用层命令和响应**: + - 在 `X1.Application/Features/CaseStepConfigs/Commands/CreateCaseStepConfig/CreateCaseStepConfigCommand.cs` 中添加 `FormType` 属性 + - 在 `X1.Application/Features/CaseStepConfigs/Queries/GetCaseStepConfigs/GetCaseStepConfigsResponse.cs` 中添加 `FormType` 和 `FormTypeName` 属性 + +### 解决的问题 +- **表单类型支持**:为用例步骤配置添加表单类型支持 +- **默认值设置**:启动和结束步骤默认使用 `FormType.None` +- **扩展性**:支持注册表单、Ping表单、iperf表单、打电话表单等多种表单类型 + +### 技术特点 +- 使用枚举类型确保类型安全 +- 默认值设置为 `None`,符合启动和结束步骤的需求 +- 完整的数据库映射配置 +- 前后端数据传输对象更新 + +### 修改时间 +2025-01-21 + +### 修改原因 +用户要求为用例步骤配置添加表单类型支持,包括注册表单、Ping表单、iperf表单、打电话表单,并且启动和结束步骤默认使用None类型。 + +--- + +## 2025-01-21 - 添加FormType到CaseStepType转换工具 + +### 修改内容 +1. **创建转换工具类**: + - 新建 `X1.Application/Features/CaseStepConfigs/Queries/FormTypeStepTypeConverter.cs` 文件 + - 提供 `FormType` 到 `CaseStepType` 的转换功能 + - 包含以下方法: + - `GetRecommendedStepType(FormType formType)` - 根据表单类型获取推荐的步骤类型 + - `GetFormTypeToStepTypeMapping()` - 获取表单类型与步骤类型的映射关系 + - `GetSupportedFormTypes(CaseStepType stepType)` - 获取步骤类型支持的表单类型列表 + - `IsCompatible(FormType formType, CaseStepType stepType)` - 检查表单类型是否与步骤类型兼容 + - `GetFormTypeDescriptions()` - 获取所有表单类型及其描述 + - `GetStepTypeDescriptions()` - 获取所有步骤类型及其描述 + - `GetCompleteMappingInfo()` - 获取表单类型和步骤类型的完整映射信息 + +2. **创建查询API**: + - 新建 `X1.Application/Features/CaseStepConfigs/Queries/GetFormTypeStepTypeMapping/` 目录 + - 创建 `GetFormTypeStepTypeMappingQuery.cs` - 查询请求 + - 创建 `GetFormTypeStepTypeMappingResponse.cs` - 响应DTO,包含: + - `FormTypeDto` - 表单类型信息 + - `StepTypeDto` - 步骤类型信息 + - `MappingDto` - 映射关系信息 + - 创建 `GetFormTypeStepTypeMappingQueryHandler.cs` - 查询处理器 + +### 功能特点 +- **智能推荐**:根据表单类型自动推荐合适的步骤类型 +- **兼容性检查**:验证表单类型与步骤类型的兼容性 +- **完整映射**:提供完整的表单类型和步骤类型映射关系 +- **API支持**:通过查询API提供映射信息给前端使用 +- **类型安全**:使用强类型枚举确保类型安全 + +### 映射规则 +- **开始步骤 (Start)**:仅支持 `FormType.None` +- **结束步骤 (End)**:仅支持 `FormType.None` +- **处理步骤 (Process)**:支持所有表单类型 +- **判断步骤 (Decision)**:仅支持 `FormType.None` + +### 修改时间 +2025-01-21 + +### 修改原因 +用户要求在 `X1.Application.Features.CaseStepConfigs/Queries` 目录中添加 `FormType` 到 `CaseStepType` 的转换集合/工具。 + +--- + +## 2025-01-21 - CreateCaseStepConfigCommand 已包含 FormType 属性 + +### 修改内容 +经过检查发现,`CreateCaseStepConfigCommand` 类中已经包含了 `FormType` 属性: + +```csharp +/// +/// 表单类型 +/// +public FormType FormType { get; set; } = FormType.None; +``` + +### 当前状态 +- **FormType 属性已存在**:`CreateCaseStepConfigCommand` 类中已经正确添加了 `FormType` 属性 +- **默认值已设置**:默认值为 `FormType.None`,符合业务需求 +- **验证特性已配置**:属性已正确配置,无需额外修改 + +### 修改时间 +2025-01-21 + +### 修改原因 +用户要求为 `CreateCaseStepConfigCommand` 添加 `FormType` 属性,但检查后发现该属性已经存在且配置正确。 + +--- + +## 2025-01-21 - 修复 CreateCaseStepConfigCommandHandler 中 FormType 传递问题 + +### 修改内容 +修复了 `CreateCaseStepConfigCommandHandler` 中 `FormType` 参数未传递给 `CaseStepConfig.Create` 方法的问题: + +1. **修复命令处理器**:在 `CreateCaseStepConfigCommandHandler.cs` 中,将 `request.FormType` 传递给 `CaseStepConfig.Create` 方法 +2. **更新响应对象**:在 `CreateCaseStepConfigResponse.cs` 中添加了 `FormType` 和 `FormTypeName` 属性 +3. **完善响应构建**:在命令处理器中构建响应时包含表单类型信息 + +### 修改的文件 +- `X1.Application/Features/CaseStepConfigs/Commands/CreateCaseStepConfig/CreateCaseStepConfigCommandHandler.cs` +- `X1.Application/Features/CaseStepConfigs/Commands/CreateCaseStepConfig/CreateCaseStepConfigResponse.cs` + +### 修改时间 +2025-01-21 + +### 修改原因 +用户反馈 `CreateCaseStepConfigCommand` 的 `FormType` 属性没有正确传递给 `CaseStepConfig.Create` 方法,导致表单类型信息丢失。 + +--- + +## 2025-01-21 - 修复查询处理器中的 FormType 处理问题 + +### 修改内容 +修复了查询处理器中 `FormType` 信息缺失的问题: + +1. **修复 GetCaseStepConfigsQueryHandler**:在 `UseCaseStepConfigDto` 映射中添加了 `FormType` 和 `FormTypeName` 属性 +2. **修复 GetCaseStepConfigByIdQueryHandler**:在响应构建中添加了 `FormType` 和 `FormTypeName` 信息 +3. **更新响应类**: + - `GetCaseStepConfigByIdResponse.cs` 添加了 `FormType` 和 `FormTypeName` 属性 + - `UpdateCaseStepConfigResponse.cs` 添加了 `FormType` 和 `FormTypeName` 属性 +4. **完善更新命令**:在 `UpdateCaseStepConfigCommand.cs` 中添加了 `FormType` 属性 + +### 修改的文件 +- `X1.Application/Features/CaseStepConfigs/Queries/GetCaseStepConfigs/GetCaseStepConfigsQueryHandler.cs` +- `X1.Application/Features/CaseStepConfigs/Queries/GetCaseStepConfigById/GetCaseStepConfigByIdQueryHandler.cs` +- `X1.Application/Features/CaseStepConfigs/Queries/GetCaseStepConfigById/GetCaseStepConfigByIdResponse.cs` +- `X1.Application/Features/CaseStepConfigs/Commands/UpdateCaseStepConfig/UpdateCaseStepConfigResponse.cs` +- `X1.Application/Features/CaseStepConfigs/Commands/UpdateCaseStepConfig/UpdateCaseStepConfigCommand.cs` + +### 修改时间 +2025-01-21 + +### 修改原因 +用户反馈查询处理器中缺少 `FormType` 信息的处理,需要确保所有相关的查询和响应都包含表单类型信息。 + +--- + ## 2025-01-21 - X1.WebUI Tabs 组件滚动条遮挡文字问题修复 ### 修改内容 @@ -14408,4 +14609,70 @@ catch (Exception ex) 2025-01-21 ### 修改原因 -用户反馈当标签特别多时,Content 区域被拉长看不见,Tabs 超出也看不见。需要修复布局问题,确保 Content 区域和 Tabs 都能正确显示和滚动。 \ No newline at end of file +用户反馈当标签特别多时,Content 区域被拉长看不见,Tabs 超出也看不见。需要修复布局问题,确保 Content 区域和 Tabs 都能正确显示和滚动。 + +--- + +## 2025-01-21 - CaseStepConfigController 添加 GetFormTypeStepTypeMapping 方法 + +### 修改内容 +在 `CaseStepConfigController` 中添加了 `GetFormTypeStepTypeMapping` 方法,按照现有功能的模式实现: + +1. **新增控制器方法**: + - **方法名**:`GetFormTypeStepTypeMapping` + - **路由**:`[HttpGet("form-type-step-type-mapping")]` - 对应 `/api/casestepconfigs/form-type-step-type-mapping` + - **返回类型**:`OperationResult` + - **功能**:获取表单类型到步骤类型的映射信息 + +2. **依赖注入更新**: + - 添加了 `using X1.Application.Features.CaseStepConfigs.Queries.GetFormTypeStepTypeMapping;` 命名空间引用 + - 确保能够正确引用 `GetFormTypeStepTypeQuery` 和 `GetFormTypeStepTypeResponse` + +3. **实现特性**: + - **命令处理**:使用 `mediator.Send(new GetFormTypeStepTypeQuery())` 发送查询 + - **日志记录**:详细的开始、成功、失败日志记录 + - **错误处理**:完整的错误处理和用户友好的错误信息 + - **统计信息**:成功日志中包含表单类型数量和步骤类型数量的统计 + +4. **API端点**: + ``` + GET /api/casestepconfigs/form-type-step-type-mapping + Authorization: Bearer {token} + ``` + +5. **响应格式**: + ```json + { + "isSuccess": true, + "data": { + "formTypes": [ + { + "value": 0, + "name": "None", + "description": "无表单" + } + ], + "stepTypes": [ + { + "value": 1, + "name": "Start", + "description": "开始步骤" + } + ] + }, + "errorMessages": null + } + ``` + +### 技术特点 +- **遵循现有模式**:完全按照 `CaseStepConfigController` 中其他方法的实现模式 +- **CQRS架构**:使用 MediatR 发送查询,遵循 CQRS 模式 +- **统一响应格式**:使用 `OperationResult` 统一响应格式 +- **完整日志记录**:包含详细的日志记录和错误处理 +- **类型安全**:使用强类型响应对象,确保类型安全 + +### 修改时间 +2025-01-21 + +### 修改原因 +用户要求 `GetFormTypeStepTypeQueryHandler` 已经实现,需要在控制器 `CaseStepConfigController` 中按照现有功能模式实现相应的控制器方法,不添加额外功能。 \ No newline at end of file