From 7f645ddd13a3ba26cf57ef2db83e8bebddd7c45f Mon Sep 17 00:00:00 2001 From: movingsam Date: Sun, 1 Mar 2026 13:33:02 +0800 Subject: [PATCH] =?UTF-8?q?refactor(gateway):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=BD=91=E5=85=B3=E6=9C=8D=E5=8A=A1=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E6=94=B9Id=E7=B1=BB=E5=9E=8B=E5=B9=B6=E5=BC=95?= =?UTF-8?q?=E5=85=A5=E5=AD=98=E5=82=A8=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将网关服务相关实体Id类型由long改为string,统一使用Guid V7格式Id - 新增ConsoleDbContext,配置数据库表命名规范,适配PostgreSQL约定 - 引入IRouteStore和IInstanceStore接口,替代直接使用DbContext访问数据库 - 修改GatewayService实现,调用存储接口进行数据操作以支持解耦扩展 - 调整GatewayController中实例Id参数类型为string,保证一致性 - 更新GatewayDto中各实体的Id类型为string,确保与数据库模型匹配 - 在项目配置中添加EntityFrameworkCore.Design依赖及版本更新 - 新增DesignTimeDbContextFactory方便迁移和设计时上下文创建 - 删除appsettings.json中的GatewayConnection配置,简化连接字符串配置 --- Directory.Packages.props | 5 +- src/Controllers/GatewayController.cs | 4 +- src/Data/ConsoleDbContext.cs | 77 ++ src/Data/DesignTimeDbContextFactory.cs | 15 + src/Fengling.Console.csproj | 2 +- .../Migrations/sql/initial.sql | 254 ++++++ .../20260301040647_Initial.Designer.cs | 734 ++++++++++++++++++ src/Migrations/20260301040647_Initial.cs | 505 ++++++++++++ .../ConsoleDbContextModelSnapshot.cs | 731 +++++++++++++++++ src/Migrations/initial.sql | 254 ++++++ src/Models/Dtos/GatewayDto.cs | 6 +- src/Program.cs | 24 +- src/Services/GatewayService.cs | 205 ++--- src/appsettings.json | 3 +- 14 files changed, 2702 insertions(+), 117 deletions(-) create mode 100644 src/Data/ConsoleDbContext.cs create mode 100644 src/Data/DesignTimeDbContextFactory.cs create mode 100644 src/Fengling.Console/Migrations/sql/initial.sql create mode 100644 src/Migrations/20260301040647_Initial.Designer.cs create mode 100644 src/Migrations/20260301040647_Initial.cs create mode 100644 src/Migrations/ConsoleDbContextModelSnapshot.cs create mode 100644 src/Migrations/initial.sql diff --git a/Directory.Packages.props b/Directory.Packages.props index 745bc7b..b0c5c35 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,7 +4,7 @@ - + @@ -13,6 +13,7 @@ + @@ -26,4 +27,4 @@ - \ No newline at end of file + diff --git a/src/Controllers/GatewayController.cs b/src/Controllers/GatewayController.cs index 7c58dcb..a63adf9 100644 --- a/src/Controllers/GatewayController.cs +++ b/src/Controllers/GatewayController.cs @@ -285,7 +285,7 @@ public class GatewayController(IGatewayService gatewayService, ILogger RemoveInstance(long instanceId) + public async Task RemoveInstance(string instanceId) { try { @@ -317,7 +317,7 @@ public class GatewayController(IGatewayService gatewayService, ILogger UpdateWeight(long instanceId, [FromBody] GatewayUpdateWeightDto dto) + public async Task UpdateWeight(string instanceId, [FromBody] GatewayUpdateWeightDto dto) { try { diff --git a/src/Data/ConsoleDbContext.cs b/src/Data/ConsoleDbContext.cs new file mode 100644 index 0000000..2bf61df --- /dev/null +++ b/src/Data/ConsoleDbContext.cs @@ -0,0 +1,77 @@ +using Fengling.Platform.Domain.AggregatesModel.GatewayAggregate; +using Fengling.Platform.Domain.AggregatesModel.RoleAggregate; +using Fengling.Platform.Domain.AggregatesModel.TenantAggregate; +using Fengling.Platform.Domain.AggregatesModel.UserAggregate; +using Fengling.Platform.Infrastructure; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; + +namespace Fengling.Console.Data; + +/// +/// Console 项目的 DbContext,继承 PlatformDbContext 并配置表命名规范 +/// PostgreSQL 命名规范:小写字母 + 下划线,模块前缀 +/// +public class ConsoleDbContext : PlatformDbContext +{ + public ConsoleDbContext(DbContextOptions options) : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + // ========== Gateway 模块 ========== + modelBuilder.Entity(entity => + { + entity.ToTable("gw_tenants"); + }); + + modelBuilder.Entity(entity => + { + entity.ToTable("gw_tenant_routes"); + }); + + modelBuilder.Entity(entity => + { + entity.ToTable("gw_service_instances"); + }); + + // ========== Tenant 模块 ========== + modelBuilder.Entity(entity => + { + entity.ToTable("sys_tenants"); + }); + + // ========== Audit 模块 ========== + modelBuilder.Entity(entity => + { + entity.ToTable("sys_access_logs"); + }); + + modelBuilder.Entity(entity => + { + entity.ToTable("sys_audit_logs"); + }); + + // ========== Identity 模块 ========== + modelBuilder.Entity(entity => + { + entity.ToTable("idn_users"); + }); + + modelBuilder.Entity(entity => + { + entity.ToTable("idn_roles"); + }); + + // Identity tables - custom names + modelBuilder.Entity>(entity => entity.ToTable("idn_user_claims")); + modelBuilder.Entity>(entity => entity.ToTable("idn_role_claims")); + modelBuilder.Entity>(entity => entity.ToTable("idn_user_logins")); + modelBuilder.Entity>(entity => entity.ToTable("idn_user_tokens")); + modelBuilder.Entity>(entity => entity.ToTable("idn_user_roles")); + } +} diff --git a/src/Data/DesignTimeDbContextFactory.cs b/src/Data/DesignTimeDbContextFactory.cs new file mode 100644 index 0000000..c63631c --- /dev/null +++ b/src/Data/DesignTimeDbContextFactory.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Fengling.Console.Data; + +public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory +{ + public ConsoleDbContext CreateDbContext(string[] args) + { + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.UseNpgsql("Host=81.68.223.70;Port=15432;Database=fengling_auth;Username=movingsam;Password=sl52788542"); + return new ConsoleDbContext(optionsBuilder.Options); + } +} diff --git a/src/Fengling.Console.csproj b/src/Fengling.Console.csproj index b8e14d5..4220245 100644 --- a/src/Fengling.Console.csproj +++ b/src/Fengling.Console.csproj @@ -19,6 +19,7 @@ + @@ -33,5 +34,4 @@ - diff --git a/src/Fengling.Console/Migrations/sql/initial.sql b/src/Fengling.Console/Migrations/sql/initial.sql new file mode 100644 index 0000000..1552bb5 --- /dev/null +++ b/src/Fengling.Console/Migrations/sql/initial.sql @@ -0,0 +1,254 @@ +CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" ( + "MigrationId" character varying(150) NOT NULL, + "ProductVersion" character varying(32) NOT NULL, + CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY ("MigrationId") +); + +START TRANSACTION; +CREATE TABLE gw_service_instances ( + "Id" text NOT NULL, + "ClusterId" character varying(100) NOT NULL, + "DestinationId" character varying(100) NOT NULL, + "Address" character varying(200) NOT NULL, + "Health" integer NOT NULL, + "Weight" integer NOT NULL, + "Status" integer NOT NULL, + "CreatedBy" bigint, + "CreatedTime" timestamp with time zone NOT NULL, + "UpdatedBy" bigint, + "UpdatedTime" timestamp with time zone, + "IsDeleted" boolean NOT NULL, + "Version" integer NOT NULL, + CONSTRAINT "PK_gw_service_instances" PRIMARY KEY ("Id") +); + +CREATE TABLE gw_tenant_routes ( + "Id" text NOT NULL, + "TenantCode" character varying(50) NOT NULL, + "ServiceName" character varying(100) NOT NULL, + "ClusterId" character varying(100) NOT NULL, + "PathPattern" character varying(200) NOT NULL, + "Priority" integer NOT NULL, + "Status" integer NOT NULL, + "IsGlobal" boolean NOT NULL, + "CreatedBy" bigint, + "CreatedTime" timestamp with time zone NOT NULL, + "UpdatedBy" bigint, + "UpdatedTime" timestamp with time zone, + "IsDeleted" boolean NOT NULL, + "Version" integer NOT NULL, + CONSTRAINT "PK_gw_tenant_routes" PRIMARY KEY ("Id") +); + +CREATE TABLE gw_tenants ( + "Id" bigint GENERATED BY DEFAULT AS IDENTITY, + "TenantCode" character varying(50) NOT NULL, + "TenantName" character varying(100) NOT NULL, + "Status" integer NOT NULL, + "CreatedBy" bigint, + "CreatedTime" timestamp with time zone NOT NULL, + "UpdatedBy" bigint, + "UpdatedTime" timestamp with time zone, + "IsDeleted" boolean NOT NULL, + "Version" integer NOT NULL, + CONSTRAINT "PK_gw_tenants" PRIMARY KEY ("Id") +); + +CREATE TABLE idn_roles ( + "Id" bigint GENERATED BY DEFAULT AS IDENTITY, + "Description" character varying(200), + "CreatedTime" timestamp with time zone NOT NULL, + "TenantId" bigint, + "IsSystem" boolean NOT NULL, + "DisplayName" text, + "Permissions" text[], + "Name" character varying(256), + "NormalizedName" character varying(256), + "ConcurrencyStamp" text, + CONSTRAINT "PK_idn_roles" PRIMARY KEY ("Id") +); + +CREATE TABLE idn_users ( + "Id" bigint GENERATED BY DEFAULT AS IDENTITY, + "RealName" text NOT NULL, + "TenantId" bigint, + "TenantCode" text, + "TenantName" text, + "CreatedTime" timestamp with time zone NOT NULL, + "UpdatedTime" timestamp with time zone, + "IsDeleted" boolean NOT NULL, + "UserName" character varying(256), + "NormalizedUserName" character varying(256), + "Email" character varying(256), + "NormalizedEmail" character varying(256), + "EmailConfirmed" boolean NOT NULL, + "PasswordHash" text, + "SecurityStamp" text, + "ConcurrencyStamp" text, + "PhoneNumber" character varying(20), + "PhoneNumberConfirmed" boolean NOT NULL, + "TwoFactorEnabled" boolean NOT NULL, + "LockoutEnd" timestamp with time zone, + "LockoutEnabled" boolean NOT NULL, + "AccessFailedCount" integer NOT NULL, + CONSTRAINT "PK_idn_users" PRIMARY KEY ("Id") +); + +CREATE TABLE sys_access_logs ( + "Id" bigint GENERATED BY DEFAULT AS IDENTITY, + "UserName" character varying(50), + "TenantId" character varying(50), + "Action" character varying(20) NOT NULL, + "Resource" character varying(200), + "Method" character varying(10), + "IpAddress" character varying(50), + "UserAgent" character varying(500), + "Status" character varying(20) NOT NULL, + "Duration" integer NOT NULL, + "RequestData" text, + "ResponseData" text, + "ErrorMessage" text, + "CreatedAt" timestamp with time zone NOT NULL, + CONSTRAINT "PK_sys_access_logs" PRIMARY KEY ("Id") +); + +CREATE TABLE sys_audit_logs ( + "Id" bigint GENERATED BY DEFAULT AS IDENTITY, + "Operator" character varying(50) NOT NULL, + "TenantId" character varying(50), + "Operation" character varying(20) NOT NULL, + "Action" character varying(20) NOT NULL, + "TargetType" character varying(50), + "TargetId" bigint, + "TargetName" character varying(100), + "IpAddress" character varying(50) NOT NULL, + "Description" character varying(500), + "OldValue" text, + "NewValue" text, + "ErrorMessage" text, + "Status" character varying(20) NOT NULL, + "CreatedAt" timestamp with time zone NOT NULL, + CONSTRAINT "PK_sys_audit_logs" PRIMARY KEY ("Id") +); + +CREATE TABLE sys_tenants ( + "Id" bigint GENERATED BY DEFAULT AS IDENTITY, + "TenantCode" character varying(50) NOT NULL, + "Name" character varying(100) NOT NULL, + "ContactName" character varying(50) NOT NULL, + "ContactEmail" character varying(100) NOT NULL, + "ContactPhone" character varying(20), + "MaxUsers" integer, + "CreatedAt" timestamp with time zone NOT NULL, + "UpdatedAt" timestamp with time zone, + "ExpiresAt" timestamp with time zone, + "Description" character varying(500), + "Status" integer NOT NULL, + "IsDeleted" boolean NOT NULL, + "RowVersion" bigint NOT NULL, + CONSTRAINT "PK_sys_tenants" PRIMARY KEY ("Id") +); + +CREATE TABLE idn_role_claims ( + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "RoleId" bigint NOT NULL, + "ClaimType" text, + "ClaimValue" text, + CONSTRAINT "PK_idn_role_claims" PRIMARY KEY ("Id"), + CONSTRAINT "FK_idn_role_claims_idn_roles_RoleId" FOREIGN KEY ("RoleId") REFERENCES idn_roles ("Id") ON DELETE CASCADE +); + +CREATE TABLE idn_user_claims ( + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "UserId" bigint NOT NULL, + "ClaimType" text, + "ClaimValue" text, + CONSTRAINT "PK_idn_user_claims" PRIMARY KEY ("Id"), + CONSTRAINT "FK_idn_user_claims_idn_users_UserId" FOREIGN KEY ("UserId") REFERENCES idn_users ("Id") ON DELETE CASCADE +); + +CREATE TABLE idn_user_logins ( + "LoginProvider" text NOT NULL, + "ProviderKey" text NOT NULL, + "ProviderDisplayName" text, + "UserId" bigint NOT NULL, + CONSTRAINT "PK_idn_user_logins" PRIMARY KEY ("LoginProvider", "ProviderKey"), + CONSTRAINT "FK_idn_user_logins_idn_users_UserId" FOREIGN KEY ("UserId") REFERENCES idn_users ("Id") ON DELETE CASCADE +); + +CREATE TABLE idn_user_roles ( + "UserId" bigint NOT NULL, + "RoleId" bigint NOT NULL, + CONSTRAINT "PK_idn_user_roles" PRIMARY KEY ("UserId", "RoleId"), + CONSTRAINT "FK_idn_user_roles_idn_roles_RoleId" FOREIGN KEY ("RoleId") REFERENCES idn_roles ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_idn_user_roles_idn_users_UserId" FOREIGN KEY ("UserId") REFERENCES idn_users ("Id") ON DELETE CASCADE +); + +CREATE TABLE idn_user_tokens ( + "UserId" bigint NOT NULL, + "LoginProvider" text NOT NULL, + "Name" text NOT NULL, + "Value" text, + CONSTRAINT "PK_idn_user_tokens" PRIMARY KEY ("UserId", "LoginProvider", "Name"), + CONSTRAINT "FK_idn_user_tokens_idn_users_UserId" FOREIGN KEY ("UserId") REFERENCES idn_users ("Id") ON DELETE CASCADE +); + +CREATE UNIQUE INDEX "IX_gw_service_instances_ClusterId_DestinationId" ON gw_service_instances ("ClusterId", "DestinationId"); + +CREATE INDEX "IX_gw_service_instances_Health" ON gw_service_instances ("Health"); + +CREATE INDEX "IX_gw_tenant_routes_ClusterId" ON gw_tenant_routes ("ClusterId"); + +CREATE INDEX "IX_gw_tenant_routes_ServiceName" ON gw_tenant_routes ("ServiceName"); + +CREATE INDEX "IX_gw_tenant_routes_ServiceName_IsGlobal_Status" ON gw_tenant_routes ("ServiceName", "IsGlobal", "Status"); + +CREATE INDEX "IX_gw_tenant_routes_TenantCode" ON gw_tenant_routes ("TenantCode"); + +CREATE UNIQUE INDEX "IX_gw_tenants_TenantCode" ON gw_tenants ("TenantCode"); + +CREATE INDEX "IX_idn_role_claims_RoleId" ON idn_role_claims ("RoleId"); + +CREATE UNIQUE INDEX "RoleNameIndex" ON idn_roles ("NormalizedName"); + +CREATE INDEX "IX_idn_user_claims_UserId" ON idn_user_claims ("UserId"); + +CREATE INDEX "IX_idn_user_logins_UserId" ON idn_user_logins ("UserId"); + +CREATE INDEX "IX_idn_user_roles_RoleId" ON idn_user_roles ("RoleId"); + +CREATE INDEX "EmailIndex" ON idn_users ("NormalizedEmail"); + +CREATE UNIQUE INDEX "IX_idn_users_PhoneNumber" ON idn_users ("PhoneNumber"); + +CREATE UNIQUE INDEX "UserNameIndex" ON idn_users ("NormalizedUserName"); + +CREATE INDEX "IX_sys_access_logs_Action" ON sys_access_logs ("Action"); + +CREATE INDEX "IX_sys_access_logs_CreatedAt" ON sys_access_logs ("CreatedAt"); + +CREATE INDEX "IX_sys_access_logs_Status" ON sys_access_logs ("Status"); + +CREATE INDEX "IX_sys_access_logs_TenantId" ON sys_access_logs ("TenantId"); + +CREATE INDEX "IX_sys_access_logs_UserName" ON sys_access_logs ("UserName"); + +CREATE INDEX "IX_sys_audit_logs_Action" ON sys_audit_logs ("Action"); + +CREATE INDEX "IX_sys_audit_logs_CreatedAt" ON sys_audit_logs ("CreatedAt"); + +CREATE INDEX "IX_sys_audit_logs_Operation" ON sys_audit_logs ("Operation"); + +CREATE INDEX "IX_sys_audit_logs_Operator" ON sys_audit_logs ("Operator"); + +CREATE INDEX "IX_sys_audit_logs_TenantId" ON sys_audit_logs ("TenantId"); + +CREATE INDEX "IX_sys_tenants_Status" ON sys_tenants ("Status"); + +CREATE UNIQUE INDEX "IX_sys_tenants_TenantCode" ON sys_tenants ("TenantCode"); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20260301040647_Initial', '10.0.3'); + +COMMIT; + diff --git a/src/Migrations/20260301040647_Initial.Designer.cs b/src/Migrations/20260301040647_Initial.Designer.cs new file mode 100644 index 0000000..c10cade --- /dev/null +++ b/src/Migrations/20260301040647_Initial.Designer.cs @@ -0,0 +1,734 @@ +// +using System; +using System.Collections.Generic; +using Fengling.Console.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Fengling.Console.Migrations +{ + [DbContext(typeof(ConsoleDbContext))] + [Migration("20260301040647_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.GatewayAggregate.GwServiceInstance", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClusterId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .HasColumnType("bigint"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("DestinationId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Health") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("bigint"); + + b.Property("UpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Version") + .HasColumnType("integer"); + + b.Property("Weight") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("Health"); + + b.HasIndex("ClusterId", "DestinationId") + .IsUnique(); + + b.ToTable("gw_service_instances", (string)null); + }); + + modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.GatewayAggregate.GwTenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedBy") + .HasColumnType("bigint"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TenantCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("TenantName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("UpdatedBy") + .HasColumnType("bigint"); + + b.Property("UpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("TenantCode") + .IsUnique(); + + b.ToTable("gw_tenants", (string)null); + }); + + modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.GatewayAggregate.GwTenantRoute", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ClusterId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .HasColumnType("bigint"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsGlobal") + .HasColumnType("boolean"); + + b.Property("PathPattern") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Priority") + .HasColumnType("integer"); + + b.Property("ServiceName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TenantCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedBy") + .HasColumnType("bigint"); + + b.Property("UpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ClusterId"); + + b.HasIndex("ServiceName"); + + b.HasIndex("TenantCode"); + + b.HasIndex("ServiceName", "IsGlobal", "Status"); + + b.ToTable("gw_tenant_routes", (string)null); + }); + + modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.RoleAggregate.ApplicationRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("IsSystem") + .HasColumnType("boolean"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.PrimitiveCollection>("Permissions") + .HasColumnType("text[]"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("idn_roles", (string)null); + }); + + modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.TenantAggregate.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ContactEmail") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ContactName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ContactPhone") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("MaxUsers") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("RowVersion") + .HasColumnType("bigint"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TenantCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Status"); + + b.HasIndex("TenantCode") + .IsUnique(); + + b.ToTable("sys_tenants", (string)null); + }); + + modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.AccessLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("ErrorMessage") + .HasColumnType("text"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Method") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("RequestData") + .HasColumnType("text"); + + b.Property("Resource") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ResponseData") + .HasColumnType("text"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("TenantId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UserAgent") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UserName") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("Status"); + + b.HasIndex("TenantId"); + + b.HasIndex("UserName"); + + b.ToTable("sys_access_logs", (string)null); + }); + + modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("RealName") + .IsRequired() + .HasColumnType("text"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("idn_users", (string)null); + }); + + modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ErrorMessage") + .HasColumnType("text"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("NewValue") + .HasColumnType("text"); + + b.Property("OldValue") + .HasColumnType("text"); + + b.Property("Operation") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Operator") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("TargetId") + .HasColumnType("bigint"); + + b.Property("TargetName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TargetType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("TenantId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("Operation"); + + b.HasIndex("Operator"); + + b.HasIndex("TenantId"); + + b.ToTable("sys_audit_logs", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("idn_role_claims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("idn_user_claims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("idn_user_logins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("RoleId") + .HasColumnType("bigint"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("idn_user_roles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("idn_user_tokens", (string)null); + }); + + modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", b => + { + b.OwnsOne("Fengling.Platform.Domain.AggregatesModel.TenantAggregate.TenantInfo", "TenantInfo", b1 => + { + b1.Property("ApplicationUserId") + .HasColumnType("bigint"); + + b1.Property("TenantCode") + .HasColumnType("text") + .HasColumnName("TenantCode"); + + b1.Property("TenantId") + .HasColumnType("bigint") + .HasColumnName("TenantId"); + + b1.Property("TenantName") + .HasColumnType("text") + .HasColumnName("TenantName"); + + b1.HasKey("ApplicationUserId"); + + b1.ToTable("idn_users"); + + b1.WithOwner() + .HasForeignKey("ApplicationUserId"); + }); + + b.Navigation("TenantInfo"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Fengling.Platform.Domain.AggregatesModel.RoleAggregate.ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Fengling.Platform.Domain.AggregatesModel.RoleAggregate.ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Migrations/20260301040647_Initial.cs b/src/Migrations/20260301040647_Initial.cs new file mode 100644 index 0000000..ac5332d --- /dev/null +++ b/src/Migrations/20260301040647_Initial.cs @@ -0,0 +1,505 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Fengling.Console.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "gw_service_instances", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + ClusterId = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + DestinationId = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + Address = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + Health = table.Column(type: "integer", nullable: false), + Weight = table.Column(type: "integer", nullable: false), + Status = table.Column(type: "integer", nullable: false), + CreatedBy = table.Column(type: "bigint", nullable: true), + CreatedTime = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedBy = table.Column(type: "bigint", nullable: true), + UpdatedTime = table.Column(type: "timestamp with time zone", nullable: true), + IsDeleted = table.Column(type: "boolean", nullable: false), + Version = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_gw_service_instances", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "gw_tenant_routes", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + TenantCode = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + ServiceName = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + ClusterId = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + PathPattern = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + Priority = table.Column(type: "integer", nullable: false), + Status = table.Column(type: "integer", nullable: false), + IsGlobal = table.Column(type: "boolean", nullable: false), + CreatedBy = table.Column(type: "bigint", nullable: true), + CreatedTime = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedBy = table.Column(type: "bigint", nullable: true), + UpdatedTime = table.Column(type: "timestamp with time zone", nullable: true), + IsDeleted = table.Column(type: "boolean", nullable: false), + Version = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_gw_tenant_routes", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "gw_tenants", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + TenantCode = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + TenantName = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + Status = table.Column(type: "integer", nullable: false), + CreatedBy = table.Column(type: "bigint", nullable: true), + CreatedTime = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedBy = table.Column(type: "bigint", nullable: true), + UpdatedTime = table.Column(type: "timestamp with time zone", nullable: true), + IsDeleted = table.Column(type: "boolean", nullable: false), + Version = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_gw_tenants", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "idn_roles", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Description = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), + CreatedTime = table.Column(type: "timestamp with time zone", nullable: false), + TenantId = table.Column(type: "bigint", nullable: true), + IsSystem = table.Column(type: "boolean", nullable: false), + DisplayName = table.Column(type: "text", nullable: true), + Permissions = table.Column>(type: "text[]", nullable: true), + Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_idn_roles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "idn_users", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + RealName = table.Column(type: "text", nullable: false), + TenantId = table.Column(type: "bigint", nullable: true), + TenantCode = table.Column(type: "text", nullable: true), + TenantName = table.Column(type: "text", nullable: true), + CreatedTime = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedTime = table.Column(type: "timestamp with time zone", nullable: true), + IsDeleted = table.Column(type: "boolean", nullable: false), + UserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "boolean", nullable: false), + PasswordHash = table.Column(type: "text", nullable: true), + SecurityStamp = table.Column(type: "text", nullable: true), + ConcurrencyStamp = table.Column(type: "text", nullable: true), + PhoneNumber = table.Column(type: "character varying(20)", maxLength: 20, nullable: true), + PhoneNumberConfirmed = table.Column(type: "boolean", nullable: false), + TwoFactorEnabled = table.Column(type: "boolean", nullable: false), + LockoutEnd = table.Column(type: "timestamp with time zone", nullable: true), + LockoutEnabled = table.Column(type: "boolean", nullable: false), + AccessFailedCount = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_idn_users", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "sys_access_logs", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + UserName = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + TenantId = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + Action = table.Column(type: "character varying(20)", maxLength: 20, nullable: false), + Resource = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), + Method = table.Column(type: "character varying(10)", maxLength: 10, nullable: true), + IpAddress = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + UserAgent = table.Column(type: "character varying(500)", maxLength: 500, nullable: true), + Status = table.Column(type: "character varying(20)", maxLength: 20, nullable: false), + Duration = table.Column(type: "integer", nullable: false), + RequestData = table.Column(type: "text", nullable: true), + ResponseData = table.Column(type: "text", nullable: true), + ErrorMessage = table.Column(type: "text", nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_sys_access_logs", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "sys_audit_logs", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Operator = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + TenantId = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + Operation = table.Column(type: "character varying(20)", maxLength: 20, nullable: false), + Action = table.Column(type: "character varying(20)", maxLength: 20, nullable: false), + TargetType = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + TargetId = table.Column(type: "bigint", nullable: true), + TargetName = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), + IpAddress = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + Description = table.Column(type: "character varying(500)", maxLength: 500, nullable: true), + OldValue = table.Column(type: "text", nullable: true), + NewValue = table.Column(type: "text", nullable: true), + ErrorMessage = table.Column(type: "text", nullable: true), + Status = table.Column(type: "character varying(20)", maxLength: 20, nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_sys_audit_logs", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "sys_tenants", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + TenantCode = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + Name = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + ContactName = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + ContactEmail = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + ContactPhone = table.Column(type: "character varying(20)", maxLength: 20, nullable: true), + MaxUsers = table.Column(type: "integer", nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), + ExpiresAt = table.Column(type: "timestamp with time zone", nullable: true), + Description = table.Column(type: "character varying(500)", maxLength: 500, nullable: true), + Status = table.Column(type: "integer", nullable: false), + IsDeleted = table.Column(type: "boolean", nullable: false), + RowVersion = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_sys_tenants", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "idn_role_claims", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + RoleId = table.Column(type: "bigint", nullable: false), + ClaimType = table.Column(type: "text", nullable: true), + ClaimValue = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_idn_role_claims", x => x.Id); + table.ForeignKey( + name: "FK_idn_role_claims_idn_roles_RoleId", + column: x => x.RoleId, + principalTable: "idn_roles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "idn_user_claims", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + UserId = table.Column(type: "bigint", nullable: false), + ClaimType = table.Column(type: "text", nullable: true), + ClaimValue = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_idn_user_claims", x => x.Id); + table.ForeignKey( + name: "FK_idn_user_claims_idn_users_UserId", + column: x => x.UserId, + principalTable: "idn_users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "idn_user_logins", + columns: table => new + { + LoginProvider = table.Column(type: "text", nullable: false), + ProviderKey = table.Column(type: "text", nullable: false), + ProviderDisplayName = table.Column(type: "text", nullable: true), + UserId = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_idn_user_logins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_idn_user_logins_idn_users_UserId", + column: x => x.UserId, + principalTable: "idn_users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "idn_user_roles", + columns: table => new + { + UserId = table.Column(type: "bigint", nullable: false), + RoleId = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_idn_user_roles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_idn_user_roles_idn_roles_RoleId", + column: x => x.RoleId, + principalTable: "idn_roles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_idn_user_roles_idn_users_UserId", + column: x => x.UserId, + principalTable: "idn_users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "idn_user_tokens", + columns: table => new + { + UserId = table.Column(type: "bigint", nullable: false), + LoginProvider = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: false), + Value = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_idn_user_tokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_idn_user_tokens_idn_users_UserId", + column: x => x.UserId, + principalTable: "idn_users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_gw_service_instances_ClusterId_DestinationId", + table: "gw_service_instances", + columns: new[] { "ClusterId", "DestinationId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_gw_service_instances_Health", + table: "gw_service_instances", + column: "Health"); + + migrationBuilder.CreateIndex( + name: "IX_gw_tenant_routes_ClusterId", + table: "gw_tenant_routes", + column: "ClusterId"); + + migrationBuilder.CreateIndex( + name: "IX_gw_tenant_routes_ServiceName", + table: "gw_tenant_routes", + column: "ServiceName"); + + migrationBuilder.CreateIndex( + name: "IX_gw_tenant_routes_ServiceName_IsGlobal_Status", + table: "gw_tenant_routes", + columns: new[] { "ServiceName", "IsGlobal", "Status" }); + + migrationBuilder.CreateIndex( + name: "IX_gw_tenant_routes_TenantCode", + table: "gw_tenant_routes", + column: "TenantCode"); + + migrationBuilder.CreateIndex( + name: "IX_gw_tenants_TenantCode", + table: "gw_tenants", + column: "TenantCode", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_idn_role_claims_RoleId", + table: "idn_role_claims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "idn_roles", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_idn_user_claims_UserId", + table: "idn_user_claims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_idn_user_logins_UserId", + table: "idn_user_logins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_idn_user_roles_RoleId", + table: "idn_user_roles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "idn_users", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX_idn_users_PhoneNumber", + table: "idn_users", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "idn_users", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_sys_access_logs_Action", + table: "sys_access_logs", + column: "Action"); + + migrationBuilder.CreateIndex( + name: "IX_sys_access_logs_CreatedAt", + table: "sys_access_logs", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_sys_access_logs_Status", + table: "sys_access_logs", + column: "Status"); + + migrationBuilder.CreateIndex( + name: "IX_sys_access_logs_TenantId", + table: "sys_access_logs", + column: "TenantId"); + + migrationBuilder.CreateIndex( + name: "IX_sys_access_logs_UserName", + table: "sys_access_logs", + column: "UserName"); + + migrationBuilder.CreateIndex( + name: "IX_sys_audit_logs_Action", + table: "sys_audit_logs", + column: "Action"); + + migrationBuilder.CreateIndex( + name: "IX_sys_audit_logs_CreatedAt", + table: "sys_audit_logs", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_sys_audit_logs_Operation", + table: "sys_audit_logs", + column: "Operation"); + + migrationBuilder.CreateIndex( + name: "IX_sys_audit_logs_Operator", + table: "sys_audit_logs", + column: "Operator"); + + migrationBuilder.CreateIndex( + name: "IX_sys_audit_logs_TenantId", + table: "sys_audit_logs", + column: "TenantId"); + + migrationBuilder.CreateIndex( + name: "IX_sys_tenants_Status", + table: "sys_tenants", + column: "Status"); + + migrationBuilder.CreateIndex( + name: "IX_sys_tenants_TenantCode", + table: "sys_tenants", + column: "TenantCode", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "gw_service_instances"); + + migrationBuilder.DropTable( + name: "gw_tenant_routes"); + + migrationBuilder.DropTable( + name: "gw_tenants"); + + migrationBuilder.DropTable( + name: "idn_role_claims"); + + migrationBuilder.DropTable( + name: "idn_user_claims"); + + migrationBuilder.DropTable( + name: "idn_user_logins"); + + migrationBuilder.DropTable( + name: "idn_user_roles"); + + migrationBuilder.DropTable( + name: "idn_user_tokens"); + + migrationBuilder.DropTable( + name: "sys_access_logs"); + + migrationBuilder.DropTable( + name: "sys_audit_logs"); + + migrationBuilder.DropTable( + name: "sys_tenants"); + + migrationBuilder.DropTable( + name: "idn_roles"); + + migrationBuilder.DropTable( + name: "idn_users"); + } + } +} diff --git a/src/Migrations/ConsoleDbContextModelSnapshot.cs b/src/Migrations/ConsoleDbContextModelSnapshot.cs new file mode 100644 index 0000000..9a36b70 --- /dev/null +++ b/src/Migrations/ConsoleDbContextModelSnapshot.cs @@ -0,0 +1,731 @@ +// +using System; +using System.Collections.Generic; +using Fengling.Console.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Fengling.Console.Migrations +{ + [DbContext(typeof(ConsoleDbContext))] + partial class ConsoleDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.GatewayAggregate.GwServiceInstance", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClusterId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .HasColumnType("bigint"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("DestinationId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Health") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UpdatedBy") + .HasColumnType("bigint"); + + b.Property("UpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Version") + .HasColumnType("integer"); + + b.Property("Weight") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("Health"); + + b.HasIndex("ClusterId", "DestinationId") + .IsUnique(); + + b.ToTable("gw_service_instances", (string)null); + }); + + modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.GatewayAggregate.GwTenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedBy") + .HasColumnType("bigint"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TenantCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("TenantName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("UpdatedBy") + .HasColumnType("bigint"); + + b.Property("UpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("TenantCode") + .IsUnique(); + + b.ToTable("gw_tenants", (string)null); + }); + + modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.GatewayAggregate.GwTenantRoute", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ClusterId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedBy") + .HasColumnType("bigint"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsGlobal") + .HasColumnType("boolean"); + + b.Property("PathPattern") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Priority") + .HasColumnType("integer"); + + b.Property("ServiceName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TenantCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedBy") + .HasColumnType("bigint"); + + b.Property("UpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ClusterId"); + + b.HasIndex("ServiceName"); + + b.HasIndex("TenantCode"); + + b.HasIndex("ServiceName", "IsGlobal", "Status"); + + b.ToTable("gw_tenant_routes", (string)null); + }); + + modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.RoleAggregate.ApplicationRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("IsSystem") + .HasColumnType("boolean"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.PrimitiveCollection>("Permissions") + .HasColumnType("text[]"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("idn_roles", (string)null); + }); + + modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.TenantAggregate.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ContactEmail") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ContactName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ContactPhone") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("MaxUsers") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("RowVersion") + .HasColumnType("bigint"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TenantCode") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Status"); + + b.HasIndex("TenantCode") + .IsUnique(); + + b.ToTable("sys_tenants", (string)null); + }); + + modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.AccessLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Duration") + .HasColumnType("integer"); + + b.Property("ErrorMessage") + .HasColumnType("text"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Method") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("RequestData") + .HasColumnType("text"); + + b.Property("Resource") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ResponseData") + .HasColumnType("text"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("TenantId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UserAgent") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("UserName") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("Status"); + + b.HasIndex("TenantId"); + + b.HasIndex("UserName"); + + b.ToTable("sys_access_logs", (string)null); + }); + + modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("RealName") + .IsRequired() + .HasColumnType("text"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("idn_users", (string)null); + }); + + modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ErrorMessage") + .HasColumnType("text"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("NewValue") + .HasColumnType("text"); + + b.Property("OldValue") + .HasColumnType("text"); + + b.Property("Operation") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Operator") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("TargetId") + .HasColumnType("bigint"); + + b.Property("TargetName") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TargetType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("TenantId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("Action"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("Operation"); + + b.HasIndex("Operator"); + + b.HasIndex("TenantId"); + + b.ToTable("sys_audit_logs", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("idn_role_claims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("idn_user_claims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("idn_user_logins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("RoleId") + .HasColumnType("bigint"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("idn_user_roles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("idn_user_tokens", (string)null); + }); + + modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", b => + { + b.OwnsOne("Fengling.Platform.Domain.AggregatesModel.TenantAggregate.TenantInfo", "TenantInfo", b1 => + { + b1.Property("ApplicationUserId") + .HasColumnType("bigint"); + + b1.Property("TenantCode") + .HasColumnType("text") + .HasColumnName("TenantCode"); + + b1.Property("TenantId") + .HasColumnType("bigint") + .HasColumnName("TenantId"); + + b1.Property("TenantName") + .HasColumnType("text") + .HasColumnName("TenantName"); + + b1.HasKey("ApplicationUserId"); + + b1.ToTable("idn_users"); + + b1.WithOwner() + .HasForeignKey("ApplicationUserId"); + }); + + b.Navigation("TenantInfo"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Fengling.Platform.Domain.AggregatesModel.RoleAggregate.ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Fengling.Platform.Domain.AggregatesModel.RoleAggregate.ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Migrations/initial.sql b/src/Migrations/initial.sql new file mode 100644 index 0000000..1552bb5 --- /dev/null +++ b/src/Migrations/initial.sql @@ -0,0 +1,254 @@ +CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" ( + "MigrationId" character varying(150) NOT NULL, + "ProductVersion" character varying(32) NOT NULL, + CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY ("MigrationId") +); + +START TRANSACTION; +CREATE TABLE gw_service_instances ( + "Id" text NOT NULL, + "ClusterId" character varying(100) NOT NULL, + "DestinationId" character varying(100) NOT NULL, + "Address" character varying(200) NOT NULL, + "Health" integer NOT NULL, + "Weight" integer NOT NULL, + "Status" integer NOT NULL, + "CreatedBy" bigint, + "CreatedTime" timestamp with time zone NOT NULL, + "UpdatedBy" bigint, + "UpdatedTime" timestamp with time zone, + "IsDeleted" boolean NOT NULL, + "Version" integer NOT NULL, + CONSTRAINT "PK_gw_service_instances" PRIMARY KEY ("Id") +); + +CREATE TABLE gw_tenant_routes ( + "Id" text NOT NULL, + "TenantCode" character varying(50) NOT NULL, + "ServiceName" character varying(100) NOT NULL, + "ClusterId" character varying(100) NOT NULL, + "PathPattern" character varying(200) NOT NULL, + "Priority" integer NOT NULL, + "Status" integer NOT NULL, + "IsGlobal" boolean NOT NULL, + "CreatedBy" bigint, + "CreatedTime" timestamp with time zone NOT NULL, + "UpdatedBy" bigint, + "UpdatedTime" timestamp with time zone, + "IsDeleted" boolean NOT NULL, + "Version" integer NOT NULL, + CONSTRAINT "PK_gw_tenant_routes" PRIMARY KEY ("Id") +); + +CREATE TABLE gw_tenants ( + "Id" bigint GENERATED BY DEFAULT AS IDENTITY, + "TenantCode" character varying(50) NOT NULL, + "TenantName" character varying(100) NOT NULL, + "Status" integer NOT NULL, + "CreatedBy" bigint, + "CreatedTime" timestamp with time zone NOT NULL, + "UpdatedBy" bigint, + "UpdatedTime" timestamp with time zone, + "IsDeleted" boolean NOT NULL, + "Version" integer NOT NULL, + CONSTRAINT "PK_gw_tenants" PRIMARY KEY ("Id") +); + +CREATE TABLE idn_roles ( + "Id" bigint GENERATED BY DEFAULT AS IDENTITY, + "Description" character varying(200), + "CreatedTime" timestamp with time zone NOT NULL, + "TenantId" bigint, + "IsSystem" boolean NOT NULL, + "DisplayName" text, + "Permissions" text[], + "Name" character varying(256), + "NormalizedName" character varying(256), + "ConcurrencyStamp" text, + CONSTRAINT "PK_idn_roles" PRIMARY KEY ("Id") +); + +CREATE TABLE idn_users ( + "Id" bigint GENERATED BY DEFAULT AS IDENTITY, + "RealName" text NOT NULL, + "TenantId" bigint, + "TenantCode" text, + "TenantName" text, + "CreatedTime" timestamp with time zone NOT NULL, + "UpdatedTime" timestamp with time zone, + "IsDeleted" boolean NOT NULL, + "UserName" character varying(256), + "NormalizedUserName" character varying(256), + "Email" character varying(256), + "NormalizedEmail" character varying(256), + "EmailConfirmed" boolean NOT NULL, + "PasswordHash" text, + "SecurityStamp" text, + "ConcurrencyStamp" text, + "PhoneNumber" character varying(20), + "PhoneNumberConfirmed" boolean NOT NULL, + "TwoFactorEnabled" boolean NOT NULL, + "LockoutEnd" timestamp with time zone, + "LockoutEnabled" boolean NOT NULL, + "AccessFailedCount" integer NOT NULL, + CONSTRAINT "PK_idn_users" PRIMARY KEY ("Id") +); + +CREATE TABLE sys_access_logs ( + "Id" bigint GENERATED BY DEFAULT AS IDENTITY, + "UserName" character varying(50), + "TenantId" character varying(50), + "Action" character varying(20) NOT NULL, + "Resource" character varying(200), + "Method" character varying(10), + "IpAddress" character varying(50), + "UserAgent" character varying(500), + "Status" character varying(20) NOT NULL, + "Duration" integer NOT NULL, + "RequestData" text, + "ResponseData" text, + "ErrorMessage" text, + "CreatedAt" timestamp with time zone NOT NULL, + CONSTRAINT "PK_sys_access_logs" PRIMARY KEY ("Id") +); + +CREATE TABLE sys_audit_logs ( + "Id" bigint GENERATED BY DEFAULT AS IDENTITY, + "Operator" character varying(50) NOT NULL, + "TenantId" character varying(50), + "Operation" character varying(20) NOT NULL, + "Action" character varying(20) NOT NULL, + "TargetType" character varying(50), + "TargetId" bigint, + "TargetName" character varying(100), + "IpAddress" character varying(50) NOT NULL, + "Description" character varying(500), + "OldValue" text, + "NewValue" text, + "ErrorMessage" text, + "Status" character varying(20) NOT NULL, + "CreatedAt" timestamp with time zone NOT NULL, + CONSTRAINT "PK_sys_audit_logs" PRIMARY KEY ("Id") +); + +CREATE TABLE sys_tenants ( + "Id" bigint GENERATED BY DEFAULT AS IDENTITY, + "TenantCode" character varying(50) NOT NULL, + "Name" character varying(100) NOT NULL, + "ContactName" character varying(50) NOT NULL, + "ContactEmail" character varying(100) NOT NULL, + "ContactPhone" character varying(20), + "MaxUsers" integer, + "CreatedAt" timestamp with time zone NOT NULL, + "UpdatedAt" timestamp with time zone, + "ExpiresAt" timestamp with time zone, + "Description" character varying(500), + "Status" integer NOT NULL, + "IsDeleted" boolean NOT NULL, + "RowVersion" bigint NOT NULL, + CONSTRAINT "PK_sys_tenants" PRIMARY KEY ("Id") +); + +CREATE TABLE idn_role_claims ( + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "RoleId" bigint NOT NULL, + "ClaimType" text, + "ClaimValue" text, + CONSTRAINT "PK_idn_role_claims" PRIMARY KEY ("Id"), + CONSTRAINT "FK_idn_role_claims_idn_roles_RoleId" FOREIGN KEY ("RoleId") REFERENCES idn_roles ("Id") ON DELETE CASCADE +); + +CREATE TABLE idn_user_claims ( + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "UserId" bigint NOT NULL, + "ClaimType" text, + "ClaimValue" text, + CONSTRAINT "PK_idn_user_claims" PRIMARY KEY ("Id"), + CONSTRAINT "FK_idn_user_claims_idn_users_UserId" FOREIGN KEY ("UserId") REFERENCES idn_users ("Id") ON DELETE CASCADE +); + +CREATE TABLE idn_user_logins ( + "LoginProvider" text NOT NULL, + "ProviderKey" text NOT NULL, + "ProviderDisplayName" text, + "UserId" bigint NOT NULL, + CONSTRAINT "PK_idn_user_logins" PRIMARY KEY ("LoginProvider", "ProviderKey"), + CONSTRAINT "FK_idn_user_logins_idn_users_UserId" FOREIGN KEY ("UserId") REFERENCES idn_users ("Id") ON DELETE CASCADE +); + +CREATE TABLE idn_user_roles ( + "UserId" bigint NOT NULL, + "RoleId" bigint NOT NULL, + CONSTRAINT "PK_idn_user_roles" PRIMARY KEY ("UserId", "RoleId"), + CONSTRAINT "FK_idn_user_roles_idn_roles_RoleId" FOREIGN KEY ("RoleId") REFERENCES idn_roles ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_idn_user_roles_idn_users_UserId" FOREIGN KEY ("UserId") REFERENCES idn_users ("Id") ON DELETE CASCADE +); + +CREATE TABLE idn_user_tokens ( + "UserId" bigint NOT NULL, + "LoginProvider" text NOT NULL, + "Name" text NOT NULL, + "Value" text, + CONSTRAINT "PK_idn_user_tokens" PRIMARY KEY ("UserId", "LoginProvider", "Name"), + CONSTRAINT "FK_idn_user_tokens_idn_users_UserId" FOREIGN KEY ("UserId") REFERENCES idn_users ("Id") ON DELETE CASCADE +); + +CREATE UNIQUE INDEX "IX_gw_service_instances_ClusterId_DestinationId" ON gw_service_instances ("ClusterId", "DestinationId"); + +CREATE INDEX "IX_gw_service_instances_Health" ON gw_service_instances ("Health"); + +CREATE INDEX "IX_gw_tenant_routes_ClusterId" ON gw_tenant_routes ("ClusterId"); + +CREATE INDEX "IX_gw_tenant_routes_ServiceName" ON gw_tenant_routes ("ServiceName"); + +CREATE INDEX "IX_gw_tenant_routes_ServiceName_IsGlobal_Status" ON gw_tenant_routes ("ServiceName", "IsGlobal", "Status"); + +CREATE INDEX "IX_gw_tenant_routes_TenantCode" ON gw_tenant_routes ("TenantCode"); + +CREATE UNIQUE INDEX "IX_gw_tenants_TenantCode" ON gw_tenants ("TenantCode"); + +CREATE INDEX "IX_idn_role_claims_RoleId" ON idn_role_claims ("RoleId"); + +CREATE UNIQUE INDEX "RoleNameIndex" ON idn_roles ("NormalizedName"); + +CREATE INDEX "IX_idn_user_claims_UserId" ON idn_user_claims ("UserId"); + +CREATE INDEX "IX_idn_user_logins_UserId" ON idn_user_logins ("UserId"); + +CREATE INDEX "IX_idn_user_roles_RoleId" ON idn_user_roles ("RoleId"); + +CREATE INDEX "EmailIndex" ON idn_users ("NormalizedEmail"); + +CREATE UNIQUE INDEX "IX_idn_users_PhoneNumber" ON idn_users ("PhoneNumber"); + +CREATE UNIQUE INDEX "UserNameIndex" ON idn_users ("NormalizedUserName"); + +CREATE INDEX "IX_sys_access_logs_Action" ON sys_access_logs ("Action"); + +CREATE INDEX "IX_sys_access_logs_CreatedAt" ON sys_access_logs ("CreatedAt"); + +CREATE INDEX "IX_sys_access_logs_Status" ON sys_access_logs ("Status"); + +CREATE INDEX "IX_sys_access_logs_TenantId" ON sys_access_logs ("TenantId"); + +CREATE INDEX "IX_sys_access_logs_UserName" ON sys_access_logs ("UserName"); + +CREATE INDEX "IX_sys_audit_logs_Action" ON sys_audit_logs ("Action"); + +CREATE INDEX "IX_sys_audit_logs_CreatedAt" ON sys_audit_logs ("CreatedAt"); + +CREATE INDEX "IX_sys_audit_logs_Operation" ON sys_audit_logs ("Operation"); + +CREATE INDEX "IX_sys_audit_logs_Operator" ON sys_audit_logs ("Operator"); + +CREATE INDEX "IX_sys_audit_logs_TenantId" ON sys_audit_logs ("TenantId"); + +CREATE INDEX "IX_sys_tenants_Status" ON sys_tenants ("Status"); + +CREATE UNIQUE INDEX "IX_sys_tenants_TenantCode" ON sys_tenants ("TenantCode"); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20260301040647_Initial', '10.0.3'); + +COMMIT; + diff --git a/src/Models/Dtos/GatewayDto.cs b/src/Models/Dtos/GatewayDto.cs index 8049c03..008ce1a 100644 --- a/src/Models/Dtos/GatewayDto.cs +++ b/src/Models/Dtos/GatewayDto.cs @@ -2,7 +2,7 @@ namespace Fengling.Console.Models.Dtos; public class GatewayServiceDto { - public long Id { get; set; } + public string Id { get; set; } = ""; public string ServicePrefix { get; set; } = ""; public string ServiceName { get; set; } = ""; public string Version { get; set; } = "v1"; @@ -32,7 +32,7 @@ public class CreateGatewayServiceDto public class GatewayRouteDto { - public long Id { get; set; } + public string Id { get; set; } = ""; public string ServiceName { get; set; } = ""; public string ClusterId { get; set; } = ""; public string PathPattern { get; set; } = ""; @@ -55,7 +55,7 @@ public class CreateGatewayRouteDto public class GatewayInstanceDto { - public long Id { get; set; } + public string Id { get; set; } = ""; public string ClusterId { get; set; } = ""; public string DestinationId { get; set; } = ""; public string Address { get; set; } = ""; diff --git a/src/Program.cs b/src/Program.cs index 292fc77..96cd9d1 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -1,4 +1,6 @@ using System.Reflection; +using Fengling.Console.Data; +using Fengling.Console.Services; using Fengling.Console.Services; using Fengling.Platform.Domain.AggregatesModel.UserAggregate; using Fengling.Platform.Domain.AggregatesModel.RoleAggregate; @@ -17,7 +19,16 @@ var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); -// Use PlatformDbContext for all identity +// Use ConsoleDbContext for all identity +builder.Services.AddDbContext(options => +{ + options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")); + if (builder.Environment.IsDevelopment()) + { + options.EnableSensitiveDataLogging(); + } + options.EnableDetailedErrors(); +}); builder.Services.AddDbContext(options => { options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")); @@ -31,6 +42,7 @@ builder.Services.AddDbContext(options => // Use Platform's identity builder.Services.AddIdentity() + .AddEntityFrameworkStores() .AddEntityFrameworkStores() .AddDefaultTokenProviders(); @@ -40,14 +52,15 @@ builder.Services.AddHttpClient(); builder.Services.AddScoped(); // Register Platform managers -builder.Services.AddScoped>(); +builder.Services.AddScoped>(); builder.Services.AddScoped(); // Register Gateway managers -builder.Services.AddScoped>(); +builder.Services.AddScoped>(); +builder.Services.AddScoped>(); builder.Services.AddScoped>(); builder.Services.AddScoped(); -builder.Services.AddScoped>(); +builder.Services.AddScoped>(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -57,6 +70,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddOpenIddict() + .AddCore(options => { options.UseEntityFrameworkCore().UseDbContext(); }) .AddCore(options => { options.UseEntityFrameworkCore().UseDbContext(); }) .AddValidation(options => { @@ -95,7 +109,7 @@ builder.Services.AddSwaggerGen(c => c.CustomSchemaIds(type => type.FullName); }); -builder.Services.AddRepositories(typeof(PlatformDbContext).Assembly); +builder.Services.AddRepositories(typeof(ConsoleDbContext).Assembly); var app = builder.Build(); diff --git a/src/Services/GatewayService.cs b/src/Services/GatewayService.cs index 35cb849..a3369c7 100644 --- a/src/Services/GatewayService.cs +++ b/src/Services/GatewayService.cs @@ -1,7 +1,5 @@ -using Fengling.Platform.Infrastructure; using Fengling.Platform.Domain.AggregatesModel.GatewayAggregate; - -using Microsoft.EntityFrameworkCore; +using Fengling.Platform.Infrastructure; using Fengling.Console.Models.Dtos; namespace Fengling.Console.Services; @@ -17,35 +15,43 @@ public interface IGatewayService Task CreateRouteAsync(CreateGatewayRouteDto dto); Task> GetInstancesAsync(string clusterId); Task AddInstanceAsync(CreateGatewayInstanceDto dto); - Task RemoveInstanceAsync(long instanceId); - Task UpdateInstanceWeightAsync(long instanceId, int weight); + Task RemoveInstanceAsync(string instanceId); + Task UpdateInstanceWeightAsync(string instanceId, int weight); Task ReloadGatewayAsync(); } public class GatewayService : IGatewayService { - private readonly PlatformDbContext _dbContext; + private readonly IRouteStore _routeStore; + private readonly IInstanceStore _instanceStore; private readonly ILogger _logger; - public GatewayService(PlatformDbContext dbContext, ILogger logger) + public GatewayService( + IRouteStore routeStore, + IInstanceStore instanceStore, + ILogger logger) { - _dbContext = dbContext; + _routeStore = routeStore; + _instanceStore = instanceStore; _logger = logger; - } +} public async Task GetStatisticsAsync() { - var routes = await _dbContext.GwTenantRoutes.Where(r => !r.IsDeleted).ToListAsync(); - var instances = await _dbContext.GwServiceInstances.Where(i => !i.IsDeleted).ToListAsync(); + var routes = await _routeStore.GetAllAsync(); + var instances = await _instanceStore.GetAllAsync(); + + var activeRoutes = routes.Where(r => !r.IsDeleted).ToList(); + var activeInstances = instances.Where(i => !i.IsDeleted).ToList(); return new GatewayStatisticsDto { - TotalServices = routes.Select(r => r.ServiceName).Distinct().Count(), - GlobalRoutes = routes.Count(r => r.IsGlobal), - TenantRoutes = routes.Count(r => !r.IsGlobal), - TotalInstances = instances.Count, - HealthyInstances = instances.Count(i => i.Health == 1), - RecentServices = routes + TotalServices = activeRoutes.Select(r => r.ServiceName).Distinct().Count(), + GlobalRoutes = activeRoutes.Count(r => r.IsGlobal), + TenantRoutes = activeRoutes.Count(r => !r.IsGlobal), + TotalInstances = activeInstances.Count, + HealthyInstances = activeInstances.Count(i => i.Health == (int)InstanceHealth.Healthy), + RecentServices = activeRoutes .OrderByDescending(r => r.CreatedTime) .Take(5) .Select(MapToServiceDto) @@ -55,38 +61,41 @@ public class GatewayService : IGatewayService public async Task> GetServicesAsync(bool globalOnly = false, string? tenantCode = null) { - var query = _dbContext.GwTenantRoutes.Where(r => !r.IsDeleted); + var routes = await _routeStore.GetAllAsync(); + var instances = await _instanceStore.GetAllAsync(); + + var query = routes.Where(r => !r.IsDeleted); if (globalOnly) query = query.Where(r => r.IsGlobal); else if (!string.IsNullOrEmpty(tenantCode)) query = query.Where(r => r.TenantCode == tenantCode); - var routes = await query.OrderByDescending(r => r.CreatedTime).ToListAsync(); - var clusters = routes.Select(r => r.ClusterId).Distinct().ToList(); + var routeList = query.OrderByDescending(r => r.CreatedTime).ToList(); + var clusters = routeList.Select(r => r.ClusterId).Distinct().ToList(); - var instances = await _dbContext.GwServiceInstances + var instancesDict = instances .Where(i => clusters.Contains(i.ClusterId) && !i.IsDeleted) .GroupBy(i => i.ClusterId) - .ToDictionaryAsync(g => g.Key, g => g.Count()); + .ToDictionary(g => g.Key, g => g.Count()); - return routes.Select(r => MapToServiceDto(r, instances.GetValueOrDefault(r.ClusterId, 0))).ToList(); + return routeList.Select(r => MapToServiceDto(r, instancesDict.GetValueOrDefault(r.ClusterId, 0))).ToList(); } public async Task GetServiceAsync(string serviceName, string? tenantCode = null) { - var route = await _dbContext.GwTenantRoutes - .FirstOrDefaultAsync(r => - r.ServiceName == serviceName && - r.IsDeleted == false && - (r.IsGlobal || r.TenantCode == tenantCode)); + var routes = await _routeStore.GetAllAsync(); + var route = routes.FirstOrDefault(r => + r.ServiceName == serviceName && + !r.IsDeleted && + (r.IsGlobal || r.TenantCode == tenantCode)); if (route == null) return null; - var instances = await _dbContext.GwServiceInstances - .CountAsync(i => i.ClusterId == route.ClusterId && !i.IsDeleted); + var instances = await _instanceStore.GetAllAsync(); + var instanceCount = instances.Count(i => i.ClusterId == route.ClusterId && !i.IsDeleted); - return MapToServiceDto(route, instances); + return MapToServiceDto(route, instanceCount); } public async Task RegisterServiceAsync(CreateGatewayServiceDto dto) @@ -98,11 +107,11 @@ public class GatewayService : IGatewayService : dto.DestinationId; // Check if route already exists - var existingRoute = await _dbContext.GwTenantRoutes - .FirstOrDefaultAsync(r => - r.ServiceName == dto.ServicePrefix && - r.IsGlobal == dto.IsGlobal && - (dto.IsGlobal || r.TenantCode == dto.TenantCode)); + var routes = await _routeStore.GetAllAsync(); + var existingRoute = routes.FirstOrDefault(r => + r.ServiceName == dto.ServicePrefix && + r.IsGlobal == dto.IsGlobal && + (dto.IsGlobal || r.TenantCode == dto.TenantCode)); if (existingRoute != null) { @@ -110,7 +119,7 @@ public class GatewayService : IGatewayService } // Add instance - var instanceId = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + var instanceId = Guid.CreateVersion7().ToString("N"); var instance = new GwServiceInstance { Id = instanceId, @@ -118,14 +127,14 @@ public class GatewayService : IGatewayService DestinationId = destinationId, Address = dto.ServiceAddress, Weight = dto.Weight, - Health = 1, - Status = 1, + Health = (int)InstanceHealth.Healthy, + Status = (int)InstanceStatus.Active, CreatedTime = DateTime.UtcNow }; - await _dbContext.GwServiceInstances.AddAsync(instance); + await _instanceStore.CreateAsync(instance); // Add route - var routeId = instanceId + 1; + var routeId = Guid.CreateVersion7().ToString("N"); var route = new GwTenantRoute { Id = routeId, @@ -134,13 +143,11 @@ public class GatewayService : IGatewayService ClusterId = clusterId, PathPattern = pathPattern, Priority = dto.IsGlobal ? 0 : 10, - Status = 1, + Status = (int)RouteStatus.Active, IsGlobal = dto.IsGlobal, CreatedTime = DateTime.UtcNow }; - await _dbContext.GwTenantRoutes.AddAsync(route); - - await _dbContext.SaveChangesAsync(); + await _routeStore.CreateAsync(route); _logger.LogInformation("Registered service {Service} at {Address}", dto.ServicePrefix, dto.ServiceAddress); @@ -149,31 +156,30 @@ public class GatewayService : IGatewayService public async Task UnregisterServiceAsync(string serviceName, string? tenantCode = null) { - var route = await _dbContext.GwTenantRoutes - .FirstOrDefaultAsync(r => - r.ServiceName == serviceName && - r.IsDeleted == false && - (r.IsGlobal || r.TenantCode == tenantCode)); + var routes = await _routeStore.GetAllAsync(); + var route = routes.FirstOrDefault(r => + r.ServiceName == serviceName && + !r.IsDeleted && + (r.IsGlobal || r.TenantCode == tenantCode)); if (route == null) return false; // Soft delete route route.IsDeleted = true; route.UpdatedTime = DateTime.UtcNow; + await _routeStore.UpdateAsync(route); // Soft delete instances - var instances = await _dbContext.GwServiceInstances - .Where(i => i.ClusterId == route.ClusterId && !i.IsDeleted) - .ToListAsync(); + var instances = await _instanceStore.GetAllAsync(); + var routeInstances = instances.Where(i => i.ClusterId == route.ClusterId && !i.IsDeleted).ToList(); - foreach (var instance in instances) + foreach (var instance in routeInstances) { instance.IsDeleted = true; instance.UpdatedTime = DateTime.UtcNow; + await _instanceStore.UpdateAsync(instance); } - await _dbContext.SaveChangesAsync(); - _logger.LogInformation("Unregistered service {Service}", serviceName); return true; @@ -181,20 +187,23 @@ public class GatewayService : IGatewayService public async Task> GetRoutesAsync(bool globalOnly = false) { - var query = _dbContext.GwTenantRoutes.Where(r => !r.IsDeleted); + var routes = await _routeStore.GetAllAsync(); + var instances = await _instanceStore.GetAllAsync(); + + var query = routes.Where(r => !r.IsDeleted); if (globalOnly) query = query.Where(r => r.IsGlobal); - var routes = await query.OrderByDescending(r => r.Priority).ToListAsync(); - var clusters = routes.Select(r => r.ClusterId).Distinct().ToList(); + var routeList = query.OrderByDescending(r => r.Priority).ToList(); + var clusters = routeList.Select(r => r.ClusterId).Distinct().ToList(); - var instances = await _dbContext.GwServiceInstances + var instancesDict = instances .Where(i => clusters.Contains(i.ClusterId) && !i.IsDeleted) .GroupBy(i => i.ClusterId) - .ToDictionaryAsync(g => g.Key, g => g.Count()); + .ToDictionary(g => g.Key, g => g.Count()); - return routes.Select(r => new GatewayRouteDto + return routeList.Select(r => new GatewayRouteDto { Id = r.Id, ServiceName = r.ServiceName, @@ -203,18 +212,18 @@ public class GatewayService : IGatewayService Priority = r.Priority, IsGlobal = r.IsGlobal, TenantCode = r.TenantCode, - Status = r.Status, - InstanceCount = instances.GetValueOrDefault(r.ClusterId, 0) + Status = (int)r.Status, + InstanceCount = instancesDict.GetValueOrDefault(r.ClusterId, 0) }).ToList(); } public async Task CreateRouteAsync(CreateGatewayRouteDto dto) { - var existing = await _dbContext.GwTenantRoutes - .FirstOrDefaultAsync(r => - r.ServiceName == dto.ServiceName && - r.IsGlobal == dto.IsGlobal && - (dto.IsGlobal || r.TenantCode == dto.TenantCode)); + var routes = await _routeStore.GetAllAsync(); + var existing = routes.FirstOrDefault(r => + r.ServiceName == dto.ServiceName && + r.IsGlobal == dto.IsGlobal && + (dto.IsGlobal || r.TenantCode == dto.TenantCode)); if (existing != null) { @@ -223,19 +232,18 @@ public class GatewayService : IGatewayService var route = new GwTenantRoute { - Id = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), + Id = Guid.CreateVersion7().ToString("N"), TenantCode = dto.IsGlobal ? "" : dto.TenantCode ?? "", ServiceName = dto.ServiceName, ClusterId = dto.ClusterId, PathPattern = dto.PathPattern, Priority = dto.Priority, - Status = 1, + Status = (int)RouteStatus.Active, IsGlobal = dto.IsGlobal, CreatedTime = DateTime.UtcNow }; - await _dbContext.GwTenantRoutes.AddAsync(route); - await _dbContext.SaveChangesAsync(); + await _routeStore.CreateAsync(route); return new GatewayRouteDto { @@ -246,58 +254,53 @@ public class GatewayService : IGatewayService Priority = route.Priority, IsGlobal = route.IsGlobal, TenantCode = route.TenantCode, - Status = route.Status, + Status = (int)route.Status, InstanceCount = 0 }; } public async Task> GetInstancesAsync(string clusterId) { - var instances = await _dbContext.GwServiceInstances + var instances = await _instanceStore.GetAllAsync(); + var clusterInstances = instances .Where(i => i.ClusterId == clusterId && !i.IsDeleted) .OrderByDescending(i => i.Weight) - .ToListAsync(); + .ToList(); - return instances.Select(i => new GatewayInstanceDto + return clusterInstances.Select(i => new GatewayInstanceDto { Id = i.Id, ClusterId = i.ClusterId, DestinationId = i.DestinationId, Address = i.Address, Weight = i.Weight, - Health = i.Health, - Status = i.Status, + Health = (int)i.Health, + Status = (int)i.Status, CreatedAt = i.CreatedTime }).ToList(); } public async Task AddInstanceAsync(CreateGatewayInstanceDto dto) { - var existing = await _dbContext.GwServiceInstances - .FirstOrDefaultAsync(i => - i.ClusterId == dto.ClusterId && - i.DestinationId == dto.DestinationId && - !i.IsDeleted); - - if (existing != null) + var existing = await _instanceStore.FindByDestinationAsync(dto.ClusterId, dto.DestinationId); + if (existing != null && !existing.IsDeleted) { throw new InvalidOperationException($"Instance {dto.DestinationId} already exists in cluster {dto.ClusterId}"); } var instance = new GwServiceInstance { - Id = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), + Id = Guid.CreateVersion7().ToString("N"), ClusterId = dto.ClusterId, DestinationId = dto.DestinationId, Address = dto.Address, Weight = dto.Weight, - Health = 1, - Status = 1, + Health = (int)InstanceHealth.Healthy, + Status = (int)InstanceStatus.Active, CreatedTime = DateTime.UtcNow }; - await _dbContext.GwServiceInstances.AddAsync(instance); - await _dbContext.SaveChangesAsync(); + await _instanceStore.CreateAsync(instance); return new GatewayInstanceDto { @@ -306,33 +309,31 @@ public class GatewayService : IGatewayService DestinationId = instance.DestinationId, Address = instance.Address, Weight = instance.Weight, - Health = instance.Health, - Status = instance.Status, + Health = (int)instance.Health, + Status = (int)instance.Status, CreatedAt = instance.CreatedTime }; } - public async Task RemoveInstanceAsync(long instanceId) + public async Task RemoveInstanceAsync(string instanceId) { - var instance = await _dbContext.GwServiceInstances.FindAsync(instanceId); + var instance = await _instanceStore.FindByIdAsync(instanceId); if (instance == null) return false; instance.IsDeleted = true; instance.UpdatedTime = DateTime.UtcNow; - - await _dbContext.SaveChangesAsync(); + await _instanceStore.UpdateAsync(instance); return true; } - public async Task UpdateInstanceWeightAsync(long instanceId, int weight) + public async Task UpdateInstanceWeightAsync(string instanceId, int weight) { - var instance = await _dbContext.GwServiceInstances.FindAsync(instanceId); + var instance = await _instanceStore.FindByIdAsync(instanceId); if (instance == null) return false; instance.Weight = weight; instance.UpdatedTime = DateTime.UtcNow; - - await _dbContext.SaveChangesAsync(); + await _instanceStore.UpdateAsync(instance); return true; } @@ -357,7 +358,7 @@ public class GatewayService : IGatewayService InstanceCount = instanceCount, IsGlobal = route.IsGlobal, TenantCode = route.TenantCode, - Status = route.Status, + Status = (int)route.Status, CreatedAt = route.CreatedTime }; } diff --git a/src/appsettings.json b/src/appsettings.json index d3721d7..5bb9137 100644 --- a/src/appsettings.json +++ b/src/appsettings.json @@ -7,7 +7,6 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "DefaultConnection": "Host=81.68.223.70;Port=15432;Database=fengling_auth;Username=movingsam;Password=sl52788542", - "GatewayConnection" : "Host=81.68.223.70;Port=15432;Database=fengling_gateway;Username=movingsam;Password=sl52788542" + "DefaultConnection": "Host=81.68.223.70;Port=15432;Database=fengling_auth;Username=movingsam;Password=sl52788542" } }