diff --git a/src/X1.Application/Features/NetworkStackConfigs/Commands/CreateNetworkStackConfig/CreateNetworkStackConfigCommandHandler.cs b/src/X1.Application/Features/NetworkStackConfigs/Commands/CreateNetworkStackConfig/CreateNetworkStackConfigCommandHandler.cs index 907d481..5d5404b 100644 --- a/src/X1.Application/Features/NetworkStackConfigs/Commands/CreateNetworkStackConfig/CreateNetworkStackConfigCommandHandler.cs +++ b/src/X1.Application/Features/NetworkStackConfigs/Commands/CreateNetworkStackConfig/CreateNetworkStackConfigCommandHandler.cs @@ -60,9 +60,32 @@ public class CreateNetworkStackConfigCommandHandler : IRequestHandler.CreateFailure("用户未认证,无法创建网络栈配置"); } + // 生成网络栈编码 + var networkStackCode = await GenerateNetworkStackCodeAsync(cancellationToken); + + // 检查编码是否重复,如果重复则重新生成(最多重试3次) + var maxRetries = 3; + var retryCount = 0; + while (await _networkStackConfigRepository.CodeExistsAsync(networkStackCode, cancellationToken) && retryCount < maxRetries) + { + retryCount++; + _logger.LogWarning("网络栈编码重复,重新生成,重试次数: {RetryCount}, 编码: {NetworkStackCode}", retryCount, networkStackCode); + + // 等待一小段时间后重新生成 + await Task.Delay(10, cancellationToken); + networkStackCode = await GenerateNetworkStackCodeAsync(cancellationToken); + } + + if (retryCount >= maxRetries) + { + _logger.LogError("网络栈编码生成失败,重试次数已达上限: {MaxRetries}", maxRetries); + return OperationResult.CreateFailure("网络栈编码生成失败,请稍后重试"); + } + // 创建网络栈配置实体 var networkStackConfig = NetworkStackConfig.Create( networkStackName: request.NetworkStackName, + networkStackCode: networkStackCode, createdBy: currentUserId, ranId: request.RanId, description: request.Description, @@ -123,6 +146,7 @@ public class CreateNetworkStackConfigCommandHandler : IRequestHandler.CreateSuccess(response); } catch (Exception ex) @@ -140,4 +164,45 @@ public class CreateNetworkStackConfigCommandHandler : IRequestHandler.CreateFailure($"创建网络栈配置时发生错误: {ex.Message}"); } } + + /// + /// 生成网络栈编码 + /// + private async Task GenerateNetworkStackCodeAsync(CancellationToken cancellationToken) + { + // 获取当前网络栈配置总数 + var networkStackCount = await _networkStackConfigRepository.GetNetworkStackConfigCountAsync(cancellationToken); + var nextNumber = networkStackCount + 1; + + // 计算需要的位数,确保至少3位数 + var digitCount = CalculateRequiredDigits(nextNumber); + + // 格式化序号,动态补0,确保从000开始 + var formattedNumber = nextNumber.ToString($"D{digitCount}"); + + // 获取当前时间(毫秒级精度) + var currentTime = DateTime.UtcNow; + var timeStamp = currentTime.ToString("yyyyMMdd-HHmmss-fff"); + + // 生成网络栈编码格式:NSC-YYYYMMDD-HHMMSS-FFF-000, NSC-YYYYMMDD-HHMMSS-FFF-001 等 + var networkStackCode = $"NSC-{timeStamp}-{formattedNumber}"; + + _logger.LogDebug("生成网络栈编码: {NetworkStackCode}, 网络栈配置总数: {NetworkStackCount}, 位数: {DigitCount}, 时间戳: {TimeStamp}", + networkStackCode, networkStackCount, digitCount, timeStamp); + + return networkStackCode; + } + + /// + /// 计算需要的位数 + /// + private int CalculateRequiredDigits(int number) + { + if (number <= 0) return 3; // 从000开始,至少3位数 + + // 计算位数:确保至少3位数,从000开始 + // 1-999用3位,1000-9999用4位,10000-99999用5位,以此类推 + var calculatedDigits = (int)Math.Floor(Math.Log10(number)) + 1; + return Math.Max(calculatedDigits, 3); // 确保至少3位数 + } } \ No newline at end of file diff --git a/src/X1.Application/Features/NetworkStackConfigs/Commands/CreateNetworkStackConfig/CreateNetworkStackConfigResponse.cs b/src/X1.Application/Features/NetworkStackConfigs/Commands/CreateNetworkStackConfig/CreateNetworkStackConfigResponse.cs index 39d205a..13ba50b 100644 --- a/src/X1.Application/Features/NetworkStackConfigs/Commands/CreateNetworkStackConfig/CreateNetworkStackConfigResponse.cs +++ b/src/X1.Application/Features/NetworkStackConfigs/Commands/CreateNetworkStackConfig/CreateNetworkStackConfigResponse.cs @@ -15,6 +15,11 @@ public class CreateNetworkStackConfigResponse /// public string NetworkStackName { get; set; } = null!; + /// + /// 网络栈编码 + /// + public string NetworkStackCode { get; set; } = null!; + /// /// RAN配置ID(外键,可为空) /// diff --git a/src/X1.Domain/Common/OperationResult.cs b/src/X1.Domain/Common/OperationResult.cs index 74304be..a3777c9 100644 --- a/src/X1.Domain/Common/OperationResult.cs +++ b/src/X1.Domain/Common/OperationResult.cs @@ -48,7 +48,7 @@ public sealed record OperationResult( /// /// 成功消息 /// 成功结果 - public static OperationResult CreateSuccess(string successMessage) + public static OperationResult CreateSuccessWithMessage(string successMessage) { return new OperationResult(successMessage, null, default); } diff --git a/src/X1.Domain/Entities/NetworkProfile/NetworkStackConfig.cs b/src/X1.Domain/Entities/NetworkProfile/NetworkStackConfig.cs index 9c2829b..2870b9f 100644 --- a/src/X1.Domain/Entities/NetworkProfile/NetworkStackConfig.cs +++ b/src/X1.Domain/Entities/NetworkProfile/NetworkStackConfig.cs @@ -17,6 +17,13 @@ public class NetworkStackConfig : AuditableEntity [MaxLength(100)] public string NetworkStackName { get; private set; } = null!; + /// + /// 网络栈编码 + /// + [Required] + [MaxLength(50)] + public string NetworkStackCode { get; private set; } = null!; + /// /// RAN配置ID(外键,可为空) /// @@ -44,6 +51,7 @@ public class NetworkStackConfig : AuditableEntity /// public static NetworkStackConfig Create( string networkStackName, + string networkStackCode, string createdBy, string? ranId = null, string? description = null, @@ -53,6 +61,7 @@ public class NetworkStackConfig : AuditableEntity { Id = Guid.NewGuid().ToString(), NetworkStackName = networkStackName, + NetworkStackCode = networkStackCode, RanId = string.IsNullOrWhiteSpace(ranId)?null:ranId, Description = description, IsActive = isActive, diff --git a/src/X1.Domain/Repositories/NetworkProfile/INetworkStackConfigRepository.cs b/src/X1.Domain/Repositories/NetworkProfile/INetworkStackConfigRepository.cs index 855b14c..802dfdd 100644 --- a/src/X1.Domain/Repositories/NetworkProfile/INetworkStackConfigRepository.cs +++ b/src/X1.Domain/Repositories/NetworkProfile/INetworkStackConfigRepository.cs @@ -48,6 +48,16 @@ public interface INetworkStackConfigRepository : IBaseRepository Task NameExistsAsync(string networkStackName, CancellationToken cancellationToken = default); + /// + /// 检查网络栈编码是否存在 + /// + Task CodeExistsAsync(string networkStackCode, CancellationToken cancellationToken = default); + + /// + /// 获取网络栈配置总数 + /// + Task GetNetworkStackConfigCountAsync(CancellationToken cancellationToken = default); + /// /// 搜索网络栈配置 /// diff --git a/src/X1.Infrastructure/Configurations/NetworkProfile/NetworkStackConfigConfiguration.cs b/src/X1.Infrastructure/Configurations/NetworkProfile/NetworkStackConfigConfiguration.cs index 8369ac1..830750b 100644 --- a/src/X1.Infrastructure/Configurations/NetworkProfile/NetworkStackConfigConfiguration.cs +++ b/src/X1.Infrastructure/Configurations/NetworkProfile/NetworkStackConfigConfiguration.cs @@ -13,12 +13,14 @@ public class NetworkStackConfigConfiguration : IEntityTypeConfiguration nsc.NetworkStackName).IsUnique().HasDatabaseName("IX_NetworkStackConfigs_NetworkStackName"); + builder.HasIndex(nsc => nsc.NetworkStackCode).IsUnique().HasDatabaseName("IX_NetworkStackConfigs_NetworkStackCode"); builder.HasIndex(nsc => nsc.RanId).HasDatabaseName("IX_NetworkStackConfigs_RanId"); builder.HasIndex(nsc => nsc.IsActive).HasDatabaseName("IX_NetworkStackConfigs_IsActive"); // 配置属性 builder.Property(nsc => nsc.Id).HasComment("配置ID"); builder.Property(nsc => nsc.NetworkStackName).IsRequired().HasMaxLength(100).HasComment("网络栈名称"); + builder.Property(nsc => nsc.NetworkStackCode).IsRequired().HasMaxLength(50).HasComment("网络栈编码"); builder.Property(nsc => nsc.RanId).HasMaxLength(50).HasComment("RAN配置ID"); builder.Property(nsc => nsc.Description).HasMaxLength(500).HasComment("描述"); builder.Property(nsc => nsc.IsActive).IsRequired().HasComment("是否激活"); diff --git a/src/X1.Infrastructure/Migrations/20250729142128_AddNetworkCodeToNetworkStackConfig.Designer.cs b/src/X1.Infrastructure/Migrations/20250729142128_AddNetworkCodeToNetworkStackConfig.Designer.cs new file mode 100644 index 0000000..7276751 --- /dev/null +++ b/src/X1.Infrastructure/Migrations/20250729142128_AddNetworkCodeToNetworkStackConfig.Designer.cs @@ -0,0 +1,923 @@ +// +using System; +using CellularManagement.Infrastructure.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace X1.Infrastructure.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20250729142128_AddNetworkCodeToNetworkStackConfig")] + partial class AddNetworkCodeToNetworkStackConfig + { + /// + 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("CellularManagement.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("Roles", null, t => + { + t.HasComment("角色表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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_Users_Email"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique() + .HasDatabaseName("IX_Users_PhoneNumber"); + + b.HasIndex("UserName") + .IsUnique() + .HasDatabaseName("IX_Users_UserName"); + + b.ToTable("Users", null, t => + { + t.HasComment("用户表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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("IsRunning") + .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_CellularDevices_DeviceCode"); + + b.HasIndex("SerialNumber") + .IsUnique() + .HasDatabaseName("IX_CellularDevices_SerialNumber"); + + b.ToTable("CellularDevices", null, t => + { + t.HasComment("蜂窝设备表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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("ProtocolVersions", null, t => + { + t.HasComment("协议版本表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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("LoginLogs", null, t => + { + t.HasComment("用户登录日志表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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("CoreNetworkConfigs", null, t => + { + t.HasComment("核心网配置表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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("IMS_Configurations", null, t => + { + t.HasComment("IMS配置表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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("NetworkCode") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + 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("NetworkStackName") + .IsUnique() + .HasDatabaseName("IX_NetworkStackConfigs_NetworkStackName"); + + b.HasIndex("RanId") + .HasDatabaseName("IX_NetworkStackConfigs_RanId"); + + b.ToTable("NetworkStackConfigs", null, t => + { + t.HasComment("网络栈配置表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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("RAN_Configurations", null, t => + { + t.HasComment("RAN配置表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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("Stack_CoreIMS_Bindings", null, t => + { + t.HasComment("栈与核心网/IMS绑定关系表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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("Permissions", (string)null); + }); + + modelBuilder.Entity("CellularManagement.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("RolePermissions", (string)null); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.UserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("UserRoles", null, t => + { + t.HasComment("用户角色关系表"); + }); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Device.ProtocolVersion", b => + { + b.HasOne("CellularManagement.Domain.Entities.Device.CellularDevice", null) + .WithMany("ProtocolVersions") + .HasForeignKey("SerialNumber") + .HasPrincipalKey("SerialNumber") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Logging.LoginLog", b => + { + b.HasOne("CellularManagement.Domain.Entities.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.NetworkProfile.NetworkStackConfig", b => + { + b.HasOne("CellularManagement.Domain.Entities.NetworkProfile.RAN_Configuration", null) + .WithMany() + .HasForeignKey("RanId") + .OnDelete(DeleteBehavior.SetNull); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.NetworkProfile.Stack_CoreIMS_Binding", b => + { + b.HasOne("CellularManagement.Domain.Entities.NetworkProfile.CoreNetworkConfig", "CoreNetworkConfig") + .WithMany() + .HasForeignKey("CnId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CellularManagement.Domain.Entities.NetworkProfile.IMS_Configuration", "IMSConfiguration") + .WithMany() + .HasForeignKey("ImsId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CellularManagement.Domain.Entities.NetworkProfile.NetworkStackConfig", "NetworkStackConfig") + .WithMany("StackCoreIMSBindings") + .HasForeignKey("NetworkStackConfigId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CoreNetworkConfig"); + + b.Navigation("IMSConfiguration"); + + b.Navigation("NetworkStackConfig"); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.RolePermission", b => + { + b.HasOne("CellularManagement.Domain.Entities.Permission", "Permission") + .WithMany("RolePermissions") + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CellularManagement.Domain.Entities.AppRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permission"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.UserRole", b => + { + b.HasOne("CellularManagement.Domain.Entities.AppRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CellularManagement.Domain.Entities.AppUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Device.CellularDevice", b => + { + b.Navigation("ProtocolVersions"); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.NetworkProfile.NetworkStackConfig", b => + { + b.Navigation("StackCoreIMSBindings"); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Permission", b => + { + b.Navigation("RolePermissions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/X1.Infrastructure/Migrations/20250729142128_AddNetworkCodeToNetworkStackConfig.cs b/src/X1.Infrastructure/Migrations/20250729142128_AddNetworkCodeToNetworkStackConfig.cs new file mode 100644 index 0000000..a980d65 --- /dev/null +++ b/src/X1.Infrastructure/Migrations/20250729142128_AddNetworkCodeToNetworkStackConfig.cs @@ -0,0 +1,52 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace X1.Infrastructure.Migrations +{ + /// + public partial class AddNetworkCodeToNetworkStackConfig : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "NetworkCode", + table: "NetworkStackConfigs", + type: "character varying(50)", + maxLength: 50, + nullable: true); + + migrationBuilder.AddColumn( + name: "DeviceCode", + table: "CellularDevices", + type: "character varying(50)", + maxLength: 50, + nullable: false, + defaultValue: "", + comment: "设备编码"); + + migrationBuilder.CreateIndex( + name: "IX_CellularDevices_DeviceCode", + table: "CellularDevices", + column: "DeviceCode", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_CellularDevices_DeviceCode", + table: "CellularDevices"); + + migrationBuilder.DropColumn( + name: "NetworkCode", + table: "NetworkStackConfigs"); + + migrationBuilder.DropColumn( + name: "DeviceCode", + table: "CellularDevices"); + } + } +} diff --git a/src/X1.Infrastructure/Migrations/20250729144236_AddNetworkStackCode.Designer.cs b/src/X1.Infrastructure/Migrations/20250729144236_AddNetworkStackCode.Designer.cs new file mode 100644 index 0000000..f43f6bb --- /dev/null +++ b/src/X1.Infrastructure/Migrations/20250729144236_AddNetworkStackCode.Designer.cs @@ -0,0 +1,929 @@ +// +using System; +using CellularManagement.Infrastructure.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace X1.Infrastructure.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20250729144236_AddNetworkStackCode")] + partial class AddNetworkStackCode + { + /// + 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("CellularManagement.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("Roles", null, t => + { + t.HasComment("角色表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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_Users_Email"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique() + .HasDatabaseName("IX_Users_PhoneNumber"); + + b.HasIndex("UserName") + .IsUnique() + .HasDatabaseName("IX_Users_UserName"); + + b.ToTable("Users", null, t => + { + t.HasComment("用户表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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("IsRunning") + .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_CellularDevices_DeviceCode"); + + b.HasIndex("SerialNumber") + .IsUnique() + .HasDatabaseName("IX_CellularDevices_SerialNumber"); + + b.ToTable("CellularDevices", null, t => + { + t.HasComment("蜂窝设备表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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("ProtocolVersions", null, t => + { + t.HasComment("协议版本表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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("LoginLogs", null, t => + { + t.HasComment("用户登录日志表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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("CoreNetworkConfigs", null, t => + { + t.HasComment("核心网配置表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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("IMS_Configurations", null, t => + { + t.HasComment("IMS配置表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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("NetworkStackConfigs", null, t => + { + t.HasComment("网络栈配置表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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("RAN_Configurations", null, t => + { + t.HasComment("RAN配置表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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("Stack_CoreIMS_Bindings", null, t => + { + t.HasComment("栈与核心网/IMS绑定关系表"); + }); + }); + + modelBuilder.Entity("CellularManagement.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("Permissions", (string)null); + }); + + modelBuilder.Entity("CellularManagement.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("RolePermissions", (string)null); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.UserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("UserRoles", null, t => + { + t.HasComment("用户角色关系表"); + }); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Device.ProtocolVersion", b => + { + b.HasOne("CellularManagement.Domain.Entities.Device.CellularDevice", null) + .WithMany("ProtocolVersions") + .HasForeignKey("SerialNumber") + .HasPrincipalKey("SerialNumber") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Logging.LoginLog", b => + { + b.HasOne("CellularManagement.Domain.Entities.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.NetworkProfile.NetworkStackConfig", b => + { + b.HasOne("CellularManagement.Domain.Entities.NetworkProfile.RAN_Configuration", null) + .WithMany() + .HasForeignKey("RanId") + .OnDelete(DeleteBehavior.SetNull); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.NetworkProfile.Stack_CoreIMS_Binding", b => + { + b.HasOne("CellularManagement.Domain.Entities.NetworkProfile.CoreNetworkConfig", "CoreNetworkConfig") + .WithMany() + .HasForeignKey("CnId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CellularManagement.Domain.Entities.NetworkProfile.IMS_Configuration", "IMSConfiguration") + .WithMany() + .HasForeignKey("ImsId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CellularManagement.Domain.Entities.NetworkProfile.NetworkStackConfig", "NetworkStackConfig") + .WithMany("StackCoreIMSBindings") + .HasForeignKey("NetworkStackConfigId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CoreNetworkConfig"); + + b.Navigation("IMSConfiguration"); + + b.Navigation("NetworkStackConfig"); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.RolePermission", b => + { + b.HasOne("CellularManagement.Domain.Entities.Permission", "Permission") + .WithMany("RolePermissions") + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CellularManagement.Domain.Entities.AppRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permission"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.UserRole", b => + { + b.HasOne("CellularManagement.Domain.Entities.AppRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CellularManagement.Domain.Entities.AppUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Device.CellularDevice", b => + { + b.Navigation("ProtocolVersions"); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.NetworkProfile.NetworkStackConfig", b => + { + b.Navigation("StackCoreIMSBindings"); + }); + + modelBuilder.Entity("CellularManagement.Domain.Entities.Permission", b => + { + b.Navigation("RolePermissions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/X1.Infrastructure/Migrations/20250729144236_AddNetworkStackCode.cs b/src/X1.Infrastructure/Migrations/20250729144236_AddNetworkStackCode.cs new file mode 100644 index 0000000..89ac020 --- /dev/null +++ b/src/X1.Infrastructure/Migrations/20250729144236_AddNetworkStackCode.cs @@ -0,0 +1,76 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace X1.Infrastructure.Migrations +{ + /// + public partial class AddNetworkStackCode : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "NetworkCode", + table: "NetworkStackConfigs"); + + migrationBuilder.AddColumn( + name: "NetworkStackCode", + table: "NetworkStackConfigs", + type: "character varying(50)", + maxLength: 50, + nullable: true, + comment: "网络栈编码"); + + // 为现有数据生成编码 + migrationBuilder.Sql(@" + WITH numbered_configs AS ( + SELECT + ""Id"", + ROW_NUMBER() OVER (ORDER BY ""CreatedAt"") as row_num, + to_char(""CreatedAt"", 'YYYYMMDD-HH24MISS-MS') as time_stamp + FROM ""NetworkStackConfigs"" + WHERE ""NetworkStackCode"" IS NULL + ) + UPDATE ""NetworkStackConfigs"" + SET ""NetworkStackCode"" = 'NSC-' || nc.time_stamp || '-' || LPAD(nc.row_num::text, 3, '0') + FROM numbered_configs nc + WHERE ""NetworkStackConfigs"".""Id"" = nc.""Id""; + "); + + // 设置字段为非空 + migrationBuilder.AlterColumn( + name: "NetworkStackCode", + table: "NetworkStackConfigs", + type: "character varying(50)", + maxLength: 50, + nullable: false, + comment: "网络栈编码"); + + migrationBuilder.CreateIndex( + name: "IX_NetworkStackConfigs_NetworkStackCode", + table: "NetworkStackConfigs", + column: "NetworkStackCode", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_NetworkStackConfigs_NetworkStackCode", + table: "NetworkStackConfigs"); + + migrationBuilder.DropColumn( + name: "NetworkStackCode", + table: "NetworkStackConfigs"); + + migrationBuilder.AddColumn( + name: "NetworkCode", + table: "NetworkStackConfigs", + type: "character varying(50)", + maxLength: 50, + nullable: true); + } + } +} diff --git a/src/X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs b/src/X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs index 4283455..0416bf5 100644 --- a/src/X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs +++ b/src/X1.Infrastructure/Migrations/AppDbContextModelSnapshot.cs @@ -224,6 +224,12 @@ namespace X1.Infrastructure.Migrations .HasColumnType("character varying(500)") .HasComment("设备描述"); + b.Property("DeviceCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("设备编码"); + b.Property("IpAddress") .IsRequired() .HasMaxLength(45) @@ -263,6 +269,10 @@ namespace X1.Infrastructure.Migrations b.HasKey("Id"); + b.HasIndex("DeviceCode") + .IsUnique() + .HasDatabaseName("IX_CellularDevices_DeviceCode"); + b.HasIndex("SerialNumber") .IsUnique() .HasDatabaseName("IX_CellularDevices_SerialNumber"); @@ -583,6 +593,12 @@ namespace X1.Infrastructure.Migrations b.Property("IsDeleted") .HasColumnType("boolean"); + b.Property("NetworkStackCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasComment("网络栈编码"); + b.Property("NetworkStackName") .IsRequired() .HasMaxLength(100) @@ -607,6 +623,10 @@ namespace X1.Infrastructure.Migrations b.HasIndex("IsActive") .HasDatabaseName("IX_NetworkStackConfigs_IsActive"); + b.HasIndex("NetworkStackCode") + .IsUnique() + .HasDatabaseName("IX_NetworkStackConfigs_NetworkStackCode"); + b.HasIndex("NetworkStackName") .IsUnique() .HasDatabaseName("IX_NetworkStackConfigs_NetworkStackName"); diff --git a/src/X1.Infrastructure/Repositories/NetworkProfile/NetworkStackConfigRepository.cs b/src/X1.Infrastructure/Repositories/NetworkProfile/NetworkStackConfigRepository.cs index 8cc0b94..7e4370c 100644 --- a/src/X1.Infrastructure/Repositories/NetworkProfile/NetworkStackConfigRepository.cs +++ b/src/X1.Infrastructure/Repositories/NetworkProfile/NetworkStackConfigRepository.cs @@ -154,6 +154,22 @@ public class NetworkStackConfigRepository : BaseRepository, return await QueryRepository.AnyAsync(nsc => nsc.NetworkStackName == networkStackName, cancellationToken: cancellationToken); } + /// + /// 检查网络栈编码是否存在 + /// + public async Task CodeExistsAsync(string networkStackCode, CancellationToken cancellationToken = default) + { + return await QueryRepository.AnyAsync(nsc => nsc.NetworkStackCode == networkStackCode, cancellationToken: cancellationToken); + } + + /// + /// 获取网络栈配置总数 + /// + public async Task GetNetworkStackConfigCountAsync(CancellationToken cancellationToken = default) + { + return await QueryRepository.CountAsync(nsc => true, cancellationToken: cancellationToken); + } + /// /// 根据ID获取网络栈配置(包含绑定关系) /// diff --git a/src/X1.WebAPI/Properties/launchSettings.json b/src/X1.WebAPI/Properties/launchSettings.json index cf56244..7d2ed8d 100644 --- a/src/X1.WebAPI/Properties/launchSettings.json +++ b/src/X1.WebAPI/Properties/launchSettings.json @@ -24,8 +24,8 @@ "dotnetRunMessages": true, "launchBrowser": true, "launchUrl": "swagger", - "applicationUrl": "https://localhost:7268;http://localhost:5000;https://192.168.2.142:7268;http://192.168.2.142:5000", - //"applicationUrl": "https://localhost:7268;http://localhost:5000", + //"applicationUrl": "https://localhost:7268;http://localhost:5000;https://192.168.2.142:7268;http://192.168.2.142:5000", + "applicationUrl": "https://localhost:7268;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/src/X1.WebUI/src/services/networkStackConfigService.ts b/src/X1.WebUI/src/services/networkStackConfigService.ts index 66ef7b0..e4b0351 100644 --- a/src/X1.WebUI/src/services/networkStackConfigService.ts +++ b/src/X1.WebUI/src/services/networkStackConfigService.ts @@ -6,6 +6,7 @@ import { API_PATHS } from '@/constants/api'; export interface NetworkStackConfig { networkStackConfigId: string; networkStackName: string; + networkStackCode: string; ranId?: string; description?: string; isActive: boolean; @@ -66,6 +67,7 @@ export interface CreateStackCoreIMSBindingItem { export interface CreateNetworkStackConfigResponse { networkStackConfigId: string; networkStackName: string; + networkStackCode: string; ranId?: string; description?: string; isActive: boolean; @@ -94,6 +96,7 @@ export interface UpdateStackCoreIMSBindingItem { export interface UpdateNetworkStackConfigResponse { networkStackConfigId: string; networkStackName: string; + networkStackCode: string; ranId?: string; description?: string; isActive: boolean;