Compare commits

...

10 Commits

Author SHA1 Message Date
Sam
405936890c docs: add hierarchical AGENTS.md for subdirectories 2026-02-23 00:55:03 +08:00
movingsam
e9b7a362e0 feat[platform]: add fengling-api OAuth client for introspection
- Add RegisterApiClientAsync to seed OpenIddict applications
- Register fengling-api client for Console backend token introspection
- Client secret: fengling-api-secret (Confidential, with Introspection permission)

This enables Console backend to validate tokens via introspection endpoint.
2026-02-21 16:43:26 +08:00
movingsam
95bd5847f7 调整配置 2026-02-21 16:31:47 +08:00
movingsam
bbdd88e6a1 配置调整 2026-02-21 16:29:56 +08:00
movingsam
d2adce6030 openid的模型也迁移一下 2026-02-21 15:13:44 +08:00
movingsam
b97c2038a6 调整项目 2026-02-21 15:05:37 +08:00
movingsam
c1f1e09796 refactor(console): remove Repository layer, use Manager pattern
- Remove Repositories folder (IUserRepository, UserRepository, IRoleRepository, RoleRepository)
- Remove Managers folder (old TenantManager)
- Remove Datas folder (old ApplicationDbContext)
- Remove Models/Entities folder (old domain entities)
- Remove EntityConfigurations folder
- Update Services to use UserManager/RoleManager/PlatformDbContext directly
- Update DTOs to use Platform's TenantStatus
2026-02-21 13:52:37 +08:00
movingsam
a17dc9c419 refactor(platform): migrate Tenant to anemia model, use Manager pattern
- Convert Tenant to anemia model with long Id (no strong-typed ID)
- Add ApplicationUser, ApplicationRole to Platform.Domain (inherit Identity)
- Add TenantInfo value object for user-tenant redundancy
- Implement TenantManager/TenantStore in Platform.Infrastructure
- Update PlatformDbContext to inherit IdentityDbContext
- Migrate AuthService and Console to use Platform entities
- Remove old TenantRepository (replaced by TenantManager)
- Update AGENTS.md documentation
2026-02-21 13:22:08 +08:00
movingsam
f9bd73a71d refactor(console): migrate tenant management to TenantManager pattern
- Replace TenantRepository with TenantManager (ASP.NET Identity style)
- Change TenantId from long to int (auto-increment)
- Add TenantStore with CRUD operations
- Update TenantService, UserService, RoleService to use TenantManager
- Add Tenant entity with TenantStatus enum
- Update DTOs and controllers for int tenant IDs
2026-02-19 21:43:24 +08:00
movingsam
0bbc97e4a1 refactor: align TenantRepository with CleanDDD/NetCorePal规范
- remove duplicate ITenantRepository/TenantRepository from Console
- extend Platform ITenantRepository with GetByIdAsync, GetPagedAsync, CountAsync
- update Console services to use Platform.Infrastructure.Repositories
- fix nullable warnings (UserDto, OAuthClientService)
- fix YarpGateway Directory.Build.props duplicate import
- fix DynamicProxyConfigProvider CS8618 warning
2026-02-19 19:20:06 +08:00
28 changed files with 3687 additions and 310 deletions

43
AGENTS.md Normal file
View File

@ -0,0 +1,43 @@
# AGENTS.md - Fengling.Platform
**Parent:** `/AGENTS.md`
## Overview
Cross-service platform infrastructure — multi-tenant, Identity, authentication.
## Structure
```
src/Fengling.Platform/
├── Fengling.Platform.Domain/
│ └── AggregatesModel/
│ ├── UserAggregate/ # ApplicationUser
│ ├── RoleAggregate/ # ApplicationRole
│ └── TenantAggregate/ # Tenant, TenantInfo
└── Fengling.Platform.Infrastructure/
├── PlatformDbContext.cs # IdentityDbContext
├── ITenantStore.cs
├── ITenantManager.cs
└── Configurations/ # EF Core configs
```
## Where to Look
| Task | Location |
|------|----------|
| Tenant mgmt | `Platform.Domain/AggregatesModel/TenantAggregate/` |
| User/Role | `Platform.Domain/AggregatesModel/*Aggregate/` |
| DB Context | `Platform.Infrastructure/PlatformDbContext.cs` |
| Auth config | `Platform.Infrastructure/` |
## Conventions
- **Pattern**: Manager + Store (ASP.NET Core Identity style)
- **Entities**: 贫血模型 for Tenant, 充血模型 for aggregates
- **ID type**: `long` for all identities
## Anti-Patterns
- **Migration in progress** — Console not yet migrated to Platform
- Don't add new entities to Console — use Platform instead

View File

@ -4,6 +4,7 @@
<NetCorePalVersion>3.2.1</NetCorePalVersion> <NetCorePalVersion>3.2.1</NetCorePalVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.0" /> <PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.0" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.0" />
<PackageVersion Include="NetCorePal.Extensions.Repository.EntityFrameworkCore.Snowflake" Version="3.2.1" /> <PackageVersion Include="NetCorePal.Extensions.Repository.EntityFrameworkCore.Snowflake" Version="3.2.1" />
@ -12,5 +13,6 @@
<PackageVersion Include="NetCorePal.Extensions.Domain.Abstractions" Version="$(NetCorePalVersion)" /> <PackageVersion Include="NetCorePal.Extensions.Domain.Abstractions" Version="$(NetCorePalVersion)" />
<PackageVersion Include="NetCorePal.Extensions.Primitives" Version="$(NetCorePalVersion)" /> <PackageVersion Include="NetCorePal.Extensions.Primitives" Version="$(NetCorePalVersion)" />
<PackageVersion Include="MediatR" Version="12.5.0" /> <PackageVersion Include="MediatR" Version="12.5.0" />
<PackageVersion Include="OpenIddict.EntityFrameworkCore" Version="7.2.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Identity;
namespace Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
public class ApplicationRole : IdentityRole<long>
{
public string? Description { get; set; }
public DateTimeOffset CreatedTime { get; set; } = DateTimeOffset.UtcNow;
public long? TenantId { get; set; }
public bool IsSystem { get; set; }
public string? DisplayName { get; set; }
public List<string>? Permissions { get; set; }
}

View File

@ -1,99 +1,21 @@
namespace Fengling.Platform.Domain.AggregatesModel.TenantAggregate; namespace Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
using System.Threading; public class Tenant
using Fengling.Platform.Domain.DomainEvents;
public partial record TenantId : IInt64StronglyTypedId
{ {
public static TenantId New() => new TenantId(Interlocked.Increment(ref _lastId)); public long Id { get; set; }
public string TenantCode { get; set; } = string.Empty;
private static long _lastId = 0; public string Name { get; set; } = string.Empty;
public string ContactName { get; set; } = string.Empty;
public long Value => this; public string ContactEmail { get; set; } = string.Empty;
public string? ContactPhone { get; set; }
public static implicit operator long(TenantId id) => id.Value; public int? MaxUsers { get; set; }
public static implicit operator TenantId(long value) => new TenantId(value); public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
} public DateTime? UpdatedAt { get; set; }
public DateTime? ExpiresAt { get; set; }
public class Tenant : Entity<TenantId>, IAggregateRoot public string? Description { get; set; }
{ public TenantStatus Status { get; set; } = TenantStatus.Active;
protected Tenant() { } public bool IsDeleted { get; set; }
public long RowVersion { get; set; }
public Tenant(string tenantCode, string name, string contactName, string contactEmail)
{
TenantCode = tenantCode;
Name = name;
ContactName = contactName;
ContactEmail = contactEmail;
Status = TenantStatus.Active;
CreatedAt = DateTime.UtcNow;
this.AddDomainEvent(new TenantCreatedDomainEvent(this));
}
public Tenant(string tenantCode, string name, string contactName, string contactEmail,
string? contactPhone, int? maxUsers, string? description, DateTime? expiresAt)
: this(tenantCode, name, contactName, contactEmail)
{
ContactPhone = contactPhone;
MaxUsers = maxUsers;
Description = description;
ExpiresAt = expiresAt;
}
public string TenantCode { get; private set; } = string.Empty;
public string Name { get; private set; } = string.Empty;
public string ContactName { get; private set; } = string.Empty;
public string ContactEmail { get; private set; } = string.Empty;
public string? ContactPhone { get; private set; }
public int? MaxUsers { get; private set; }
public DateTime CreatedAt { get; private set; }
public DateTime? UpdatedAt { get; private set; }
public DateTime? ExpiresAt { get; private set; }
public string? Description { get; private set; }
public TenantStatus Status { get; private set; }
public Deleted Deleted { get; private set; } = new();
public RowVersion RowVersion { get; private set; } = new(0);
public void UpdateInfo(string name, string contactName, string contactEmail, string? contactPhone = null)
{
Name = name;
ContactName = contactName;
ContactEmail = contactEmail;
ContactPhone = contactPhone;
UpdatedAt = DateTime.UtcNow;
}
public void Activate()
{
if (Status == TenantStatus.Active) return;
Status = TenantStatus.Active;
UpdatedAt = DateTime.UtcNow;
this.AddDomainEvent(new TenantStatusChangedDomainEvent(this, TenantStatus.Inactive, TenantStatus.Active));
}
public void Deactivate()
{
if (Status == TenantStatus.Inactive) return;
Status = TenantStatus.Inactive;
UpdatedAt = DateTime.UtcNow;
this.AddDomainEvent(new TenantStatusChangedDomainEvent(this, TenantStatus.Active, TenantStatus.Inactive));
}
public void Delete()
{
if (Status == TenantStatus.Inactive) return;
Status = TenantStatus.Inactive;
UpdatedAt = DateTime.UtcNow;
Deleted = new Deleted(true);
}
public void Freeze()
{
if (Status == TenantStatus.Frozen) return;
Status = TenantStatus.Frozen;
UpdatedAt = DateTime.UtcNow;
this.AddDomainEvent(new TenantStatusChangedDomainEvent(this, TenantStatus.Active, TenantStatus.Frozen));
}
} }
public enum TenantStatus public enum TenantStatus

View File

@ -1,19 +1,24 @@
namespace Fengling.Platform.Domain.AggregatesModel.TenantAggregate; namespace Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
/// <summary> /// <summary>
/// /// 租户信息
/// </summary> /// </summary>
/// <param name="TenantId"></param> /// <param name="TenantId"></param>
/// <param name="TenantCode"></param> /// <param name="TenantCode"></param>
/// <param name="TenantName"></param> /// <param name="TenantName"></param>
public record TenantInfo(long TenantId, string TenantCode, string TenantName) public record TenantInfo(long? TenantId, string? TenantCode, string? TenantName)
{ {
/// <summary> /// <summary>
/// /// 租户信息
/// </summary> /// </summary>
/// <param name="tenant"></param> /// <param name="tenant"></param>
public TenantInfo(Tenant tenant) public TenantInfo(Tenant? tenant)
: this(tenant.Id, tenant.TenantCode, tenant.Name) : this(tenant?.Id, tenant?.TenantCode, tenant?.Name)
{ {
} }
/// <summary>
/// 无租户,系统级
/// </summary>
public static TenantInfo Admin => new TenantInfo(null, null, null);
} }

View File

@ -0,0 +1,43 @@
namespace Fengling.Platform.Domain.AggregatesModel.UserAggregate;
using System.ComponentModel.DataAnnotations;
public class AccessLog
{
[Key]
public long Id { get; set; }
[MaxLength(50)]
public string? UserName { get; set; }
[MaxLength(50)]
public string? TenantId { get; set; }
[MaxLength(20)]
public string Action { get; set; } = string.Empty;
[MaxLength(200)]
public string? Resource { get; set; }
[MaxLength(10)]
public string? Method { get; set; }
[MaxLength(50)]
public string? IpAddress { get; set; }
[MaxLength(500)]
public string? UserAgent { get; set; }
[MaxLength(20)]
public string Status { get; set; } = "success";
public int Duration { get; set; }
public string? RequestData { get; set; }
public string? ResponseData { get; set; }
public string? ErrorMessage { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

View File

@ -0,0 +1,13 @@
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
using Microsoft.AspNetCore.Identity;
namespace Fengling.Platform.Domain.AggregatesModel.UserAggregate;
public class ApplicationUser : IdentityUser<long>
{
public string RealName { get; set; }
public TenantInfo? TenantInfo { get; set; }
public DateTimeOffset CreatedTime { get; set; } = DateTimeOffset.UtcNow;
public DateTimeOffset? UpdatedTime { get; set; }
public bool IsDeleted { get; set; }
}

View File

@ -0,0 +1,47 @@
namespace Fengling.Platform.Domain.AggregatesModel.UserAggregate;
using System.ComponentModel.DataAnnotations;
public class AuditLog
{
[Key]
public long Id { get; set; }
[MaxLength(50)]
[Required]
public string Operator { get; set; } = string.Empty;
[MaxLength(50)]
public string? TenantId { get; set; }
[MaxLength(20)]
public string Operation { get; set; } = string.Empty;
[MaxLength(20)]
public string Action { get; set; } = string.Empty;
[MaxLength(50)]
public string? TargetType { get; set; }
public long? TargetId { get; set; }
[MaxLength(100)]
public string? TargetName { get; set; }
[MaxLength(50)]
public string IpAddress { get; set; } = string.Empty;
[MaxLength(500)]
public string? Description { get; set; }
public string? OldValue { get; set; }
public string? NewValue { get; set; }
public string? ErrorMessage { get; set; }
[MaxLength(20)]
public string Status { get; set; } = "success";
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

View File

@ -1,6 +0,0 @@
namespace Fengling.Platform.Domain.DomainEvents;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
public record TenantCreatedDomainEvent(Tenant Tenant) : IDomainEvent;
public record TenantStatusChangedDomainEvent(Tenant Tenant, TenantStatus OldStatus, TenantStatus NewStatus) : IDomainEvent;

View File

@ -9,6 +9,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
<PackageReference Include="NetCorePal.Extensions.Domain.Abstractions" /> <PackageReference Include="NetCorePal.Extensions.Domain.Abstractions" />
<PackageReference Include="NetCorePal.Extensions.Primitives" /> <PackageReference Include="NetCorePal.Extensions.Primitives" />
</ItemGroup> </ItemGroup>

View File

@ -11,7 +11,7 @@ public class TenantConfiguration : IEntityTypeConfiguration<Tenant>
builder.ToTable("Platform_Tenants"); builder.ToTable("Platform_Tenants");
builder.HasKey(t => t.Id); builder.HasKey(t => t.Id);
builder.Property(t => t.Id).UseSnowFlakeValueGenerator(); builder.Property(t => t.Id).ValueGeneratedOnAdd();
builder.Property(t => t.TenantCode) builder.Property(t => t.TenantCode)
.IsRequired() .IsRequired()

View File

@ -18,6 +18,7 @@ public class DesignTimeApplicationDbContextFactory : IDesignTimeDbContextFactory
{ {
b.MigrationsAssembly(typeof(DesignTimeApplicationDbContextFactory).Assembly.FullName); b.MigrationsAssembly(typeof(DesignTimeApplicationDbContextFactory).Assembly.FullName);
}); });
options.UseOpenIddict();
}); });
var provider = services.BuildServiceProvider(); var provider = services.BuildServiceProvider();
var dbContext = provider.CreateScope().ServiceProvider.GetRequiredService<PlatformDbContext>(); var dbContext = provider.CreateScope().ServiceProvider.GetRequiredService<PlatformDbContext>();

View File

@ -5,15 +5,18 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
<PackageReference Include="Microsoft.EntityFrameworkCore" /> <PackageReference Include="Microsoft.EntityFrameworkCore" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" />
<PackageReference Include="NetCorePal.Extensions.Repository.EntityFrameworkCore.Snowflake" /> <PackageReference Include="NetCorePal.Extensions.Repository.EntityFrameworkCore.Snowflake" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
<PackageReference Include="NetCorePal.Extensions.Repository.EntityFrameworkCore" /> <PackageReference Include="NetCorePal.Extensions.Repository.EntityFrameworkCore" />
<PackageReference Include="MediatR" /> <PackageReference Include="MediatR" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -0,0 +1,38 @@
namespace Fengling.Platform.Infrastructure;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
public interface ITenantStore
{
Task<Tenant?> FindByIdAsync(long? tenantId, CancellationToken cancellationToken = default);
Task<Tenant?> FindByTenantCodeAsync(string tenantCode, CancellationToken cancellationToken = default);
Task<IList<Tenant>> GetAllAsync(CancellationToken cancellationToken = default);
Task<IList<Tenant>> GetPagedAsync(int page, int pageSize, string? name = null, string? tenantCode = null, TenantStatus? status = null, CancellationToken cancellationToken = default);
Task<int> GetCountAsync(string? name = null, string? tenantCode = null, TenantStatus? status = null, CancellationToken cancellationToken = default);
Task<int> GetUserCountAsync(long tenantId, CancellationToken cancellationToken = default);
Task<Microsoft.AspNetCore.Identity.IdentityResult> CreateAsync(Tenant tenant, CancellationToken cancellationToken = default);
Task<Microsoft.AspNetCore.Identity.IdentityResult> UpdateAsync(Tenant tenant, CancellationToken cancellationToken = default);
Task<Microsoft.AspNetCore.Identity.IdentityResult> DeleteAsync(Tenant tenant, CancellationToken cancellationToken = default);
Task<string> GetTenantCodeAsync(Tenant tenant, CancellationToken cancellationToken = default);
Task<string> GetNameAsync(Tenant tenant, CancellationToken cancellationToken = default);
Task<string> GetContactNameAsync(Tenant tenant, CancellationToken cancellationToken = default);
Task<string> GetContactEmailAsync(Tenant tenant, CancellationToken cancellationToken = default);
Task<string?> GetContactPhoneAsync(Tenant tenant, CancellationToken cancellationToken = default);
Task<int?> GetMaxUsersAsync(Tenant tenant, CancellationToken cancellationToken = default);
Task<string?> GetDescriptionAsync(Tenant tenant, CancellationToken cancellationToken = default);
Task<TenantStatus> GetStatusAsync(Tenant tenant, CancellationToken cancellationToken = default);
Task<DateTime?> GetExpiresAtAsync(Tenant tenant, CancellationToken cancellationToken = default);
Task<DateTime> GetCreatedAtAsync(Tenant tenant, CancellationToken cancellationToken = default);
Task<DateTime?> GetUpdatedAtAsync(Tenant tenant, CancellationToken cancellationToken = default);
Task SetTenantCodeAsync(Tenant tenant, string code, CancellationToken cancellationToken = default);
Task SetNameAsync(Tenant tenant, string name, CancellationToken cancellationToken = default);
Task SetContactNameAsync(Tenant tenant, string name, CancellationToken cancellationToken = default);
Task SetContactEmailAsync(Tenant tenant, string email, CancellationToken cancellationToken = default);
Task SetContactPhoneAsync(Tenant tenant, string? phone, CancellationToken cancellationToken = default);
Task SetMaxUsersAsync(Tenant tenant, int? maxUsers, CancellationToken cancellationToken = default);
Task SetDescriptionAsync(Tenant tenant, string? description, CancellationToken cancellationToken = default);
Task SetStatusAsync(Tenant tenant, TenantStatus status, CancellationToken cancellationToken = default);
Task SetExpiresAtAsync(Tenant tenant, DateTime? expiresAt, CancellationToken cancellationToken = default);
}

View File

@ -1,95 +0,0 @@
// <auto-generated />
using System;
using Fengling.Platform.Infrastructure;
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.Platform.Infrastructure.Migrations
{
[DbContext(typeof(PlatformDbContext))]
[Migration("20260218145512_Initial")]
partial class Initial
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.TenantAggregate.Tenant", b =>
{
b.Property<long>("Id")
.HasColumnType("bigint");
b.Property<string>("ContactEmail")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<string>("ContactName")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("ContactPhone")
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<bool>("Deleted")
.HasColumnType("boolean");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property<DateTime?>("ExpiresAt")
.HasColumnType("timestamp with time zone");
b.Property<int?>("MaxUsers")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<int>("RowVersion")
.IsConcurrencyToken()
.HasColumnType("integer");
b.Property<int>("Status")
.HasColumnType("integer");
b.Property<string>("TenantCode")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("Status");
b.HasIndex("TenantCode")
.IsUnique();
b.ToTable("Platform_Tenants", (string)null);
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,57 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Fengling.Platform.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class Initial : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Platform_Tenants",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false),
TenantCode = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
Name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
ContactName = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
ContactEmail = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
ContactPhone = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: true),
MaxUsers = table.Column<int>(type: "integer", nullable: true),
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
ExpiresAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
Description = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
Status = table.Column<int>(type: "integer", nullable: false),
Deleted = table.Column<bool>(type: "boolean", nullable: false),
RowVersion = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Platform_Tenants", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_Platform_Tenants_Status",
table: "Platform_Tenants",
column: "Status");
migrationBuilder.CreateIndex(
name: "IX_Platform_Tenants_TenantCode",
table: "Platform_Tenants",
column: "TenantCode",
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Platform_Tenants");
}
}
}

View File

@ -0,0 +1,565 @@
// <auto-generated />
using System;
using System.Collections.Generic;
using Fengling.Platform.Infrastructure;
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.Platform.Infrastructure.Migrations
{
[DbContext(typeof(PlatformDbContext))]
[Migration("20260221065049_Initial")]
partial class Initial
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.RoleAggregate.ApplicationRole", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<DateTimeOffset>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<bool>("IsSystem")
.HasColumnType("boolean");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.PrimitiveCollection<List<string>>("Permissions")
.HasColumnType("text[]");
b.Property<long?>("TenantId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.TenantAggregate.Tenant", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("ContactEmail")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<string>("ContactName")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("ContactPhone")
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property<DateTime?>("ExpiresAt")
.HasColumnType("timestamp with time zone");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<int?>("MaxUsers")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<long>("RowVersion")
.HasColumnType("bigint");
b.Property<int>("Status")
.HasColumnType("integer");
b.Property<string>("TenantCode")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("Status");
b.HasIndex("TenantCode")
.IsUnique();
b.ToTable("Platform_Tenants", (string)null);
});
modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.AccessLog", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Action")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<int>("Duration")
.HasColumnType("integer");
b.Property<string>("ErrorMessage")
.HasColumnType("text");
b.Property<string>("IpAddress")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Method")
.HasMaxLength(10)
.HasColumnType("character varying(10)");
b.Property<string>("RequestData")
.HasColumnType("text");
b.Property<string>("Resource")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("ResponseData")
.HasColumnType("text");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("TenantId")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("UserAgent")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property<string>("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("AccessLogs");
});
modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<DateTimeOffset>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<string>("RealName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("UpdatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("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("AspNetUsers", (string)null);
});
modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.AuditLog", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Action")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property<string>("ErrorMessage")
.HasColumnType("text");
b.Property<string>("IpAddress")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("NewValue")
.HasColumnType("text");
b.Property<string>("OldValue")
.HasColumnType("text");
b.Property<string>("Operation")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("Operator")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<long?>("TargetId")
.HasColumnType("bigint");
b.Property<string>("TargetName")
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<string>("TargetType")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("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("AuditLogs");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<long>("RoleId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<long>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<long>("UserId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<long>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("ProviderKey")
.HasColumnType("text");
b.Property<string>("ProviderDisplayName")
.HasColumnType("text");
b.Property<long>("UserId")
.HasColumnType("bigint");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<long>", b =>
{
b.Property<long>("UserId")
.HasColumnType("bigint");
b.Property<long>("RoleId")
.HasColumnType("bigint");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<long>", b =>
{
b.Property<long>("UserId")
.HasColumnType("bigint");
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Value")
.HasColumnType("text");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", b =>
{
b.OwnsOne("Fengling.Platform.Domain.AggregatesModel.TenantAggregate.TenantInfo", "TenantInfo", b1 =>
{
b1.Property<long>("ApplicationUserId")
.HasColumnType("bigint");
b1.Property<string>("TenantCode")
.HasColumnType("text")
.HasColumnName("TenantCode");
b1.Property<long?>("TenantId")
.HasColumnType("bigint")
.HasColumnName("TenantId");
b1.Property<string>("TenantName")
.HasColumnType("text")
.HasColumnName("TenantName");
b1.HasKey("ApplicationUserId");
b1.ToTable("AspNetUsers");
b1.WithOwner()
.HasForeignKey("ApplicationUserId");
});
b.Navigation("TenantInfo");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>
{
b.HasOne("Fengling.Platform.Domain.AggregatesModel.RoleAggregate.ApplicationRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<long>", b =>
{
b.HasOne("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<long>", b =>
{
b.HasOne("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<long>", 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<long>", b =>
{
b.HasOne("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,391 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Fengling.Platform.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class Initial : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AccessLogs",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
UserName = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
TenantId = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
Action = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
Resource = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
Method = table.Column<string>(type: "character varying(10)", maxLength: 10, nullable: true),
IpAddress = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
UserAgent = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
Duration = table.Column<int>(type: "integer", nullable: false),
RequestData = table.Column<string>(type: "text", nullable: true),
ResponseData = table.Column<string>(type: "text", nullable: true),
ErrorMessage = table.Column<string>(type: "text", nullable: true),
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AccessLogs", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Description = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
CreatedTime = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
TenantId = table.Column<long>(type: "bigint", nullable: true),
IsSystem = table.Column<bool>(type: "boolean", nullable: false),
DisplayName = table.Column<string>(type: "text", nullable: true),
Permissions = table.Column<List<string>>(type: "text[]", nullable: true),
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetUsers",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
RealName = table.Column<string>(type: "text", nullable: false),
TenantId = table.Column<long>(type: "bigint", nullable: true),
TenantCode = table.Column<string>(type: "text", nullable: true),
TenantName = table.Column<string>(type: "text", nullable: true),
CreatedTime = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
UpdatedTime = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
IsDeleted = table.Column<bool>(type: "boolean", nullable: false),
UserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
Email = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
NormalizedEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
EmailConfirmed = table.Column<bool>(type: "boolean", nullable: false),
PasswordHash = table.Column<string>(type: "text", nullable: true),
SecurityStamp = table.Column<string>(type: "text", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true),
PhoneNumber = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: true),
PhoneNumberConfirmed = table.Column<bool>(type: "boolean", nullable: false),
TwoFactorEnabled = table.Column<bool>(type: "boolean", nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
LockoutEnabled = table.Column<bool>(type: "boolean", nullable: false),
AccessFailedCount = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AuditLogs",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Operator = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
TenantId = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
Operation = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
Action = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
TargetType = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
TargetId = table.Column<long>(type: "bigint", nullable: true),
TargetName = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
IpAddress = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
Description = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
OldValue = table.Column<string>(type: "text", nullable: true),
NewValue = table.Column<string>(type: "text", nullable: true),
ErrorMessage = table.Column<string>(type: "text", nullable: true),
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AuditLogs", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Platform_Tenants",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
TenantCode = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
Name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
ContactName = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
ContactEmail = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
ContactPhone = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: true),
MaxUsers = table.Column<int>(type: "integer", nullable: true),
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
ExpiresAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
Description = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
Status = table.Column<int>(type: "integer", nullable: false),
IsDeleted = table.Column<bool>(type: "boolean", nullable: false),
RowVersion = table.Column<long>(type: "bigint", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Platform_Tenants", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetRoleClaims",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
RoleId = table.Column<long>(type: "bigint", nullable: false),
ClaimType = table.Column<string>(type: "text", nullable: true),
ClaimValue = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserClaims",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
UserId = table.Column<long>(type: "bigint", nullable: false),
ClaimType = table.Column<string>(type: "text", nullable: true),
ClaimValue = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserLogins",
columns: table => new
{
LoginProvider = table.Column<string>(type: "text", nullable: false),
ProviderKey = table.Column<string>(type: "text", nullable: false),
ProviderDisplayName = table.Column<string>(type: "text", nullable: true),
UserId = table.Column<long>(type: "bigint", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
table.ForeignKey(
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserRoles",
columns: table => new
{
UserId = table.Column<long>(type: "bigint", nullable: false),
RoleId = table.Column<long>(type: "bigint", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserTokens",
columns: table => new
{
UserId = table.Column<long>(type: "bigint", nullable: false),
LoginProvider = table.Column<string>(type: "text", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
Value = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
table.ForeignKey(
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AccessLogs_Action",
table: "AccessLogs",
column: "Action");
migrationBuilder.CreateIndex(
name: "IX_AccessLogs_CreatedAt",
table: "AccessLogs",
column: "CreatedAt");
migrationBuilder.CreateIndex(
name: "IX_AccessLogs_Status",
table: "AccessLogs",
column: "Status");
migrationBuilder.CreateIndex(
name: "IX_AccessLogs_TenantId",
table: "AccessLogs",
column: "TenantId");
migrationBuilder.CreateIndex(
name: "IX_AccessLogs_UserName",
table: "AccessLogs",
column: "UserName");
migrationBuilder.CreateIndex(
name: "IX_AspNetRoleClaims_RoleId",
table: "AspNetRoleClaims",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "RoleNameIndex",
table: "AspNetRoles",
column: "NormalizedName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AspNetUserClaims_UserId",
table: "AspNetUserClaims",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserLogins_UserId",
table: "AspNetUserLogins",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserRoles_RoleId",
table: "AspNetUserRoles",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "EmailIndex",
table: "AspNetUsers",
column: "NormalizedEmail");
migrationBuilder.CreateIndex(
name: "IX_AspNetUsers_PhoneNumber",
table: "AspNetUsers",
column: "PhoneNumber",
unique: true);
migrationBuilder.CreateIndex(
name: "UserNameIndex",
table: "AspNetUsers",
column: "NormalizedUserName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AuditLogs_Action",
table: "AuditLogs",
column: "Action");
migrationBuilder.CreateIndex(
name: "IX_AuditLogs_CreatedAt",
table: "AuditLogs",
column: "CreatedAt");
migrationBuilder.CreateIndex(
name: "IX_AuditLogs_Operation",
table: "AuditLogs",
column: "Operation");
migrationBuilder.CreateIndex(
name: "IX_AuditLogs_Operator",
table: "AuditLogs",
column: "Operator");
migrationBuilder.CreateIndex(
name: "IX_AuditLogs_TenantId",
table: "AuditLogs",
column: "TenantId");
migrationBuilder.CreateIndex(
name: "IX_Platform_Tenants_Status",
table: "Platform_Tenants",
column: "Status");
migrationBuilder.CreateIndex(
name: "IX_Platform_Tenants_TenantCode",
table: "Platform_Tenants",
column: "TenantCode",
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AccessLogs");
migrationBuilder.DropTable(
name: "AspNetRoleClaims");
migrationBuilder.DropTable(
name: "AspNetUserClaims");
migrationBuilder.DropTable(
name: "AspNetUserLogins");
migrationBuilder.DropTable(
name: "AspNetUserRoles");
migrationBuilder.DropTable(
name: "AspNetUserTokens");
migrationBuilder.DropTable(
name: "AuditLogs");
migrationBuilder.DropTable(
name: "Platform_Tenants");
migrationBuilder.DropTable(
name: "AspNetRoles");
migrationBuilder.DropTable(
name: "AspNetUsers");
}
}
}

View File

@ -0,0 +1,809 @@
// <auto-generated />
using System;
using System.Collections.Generic;
using Fengling.Platform.Infrastructure;
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.Platform.Infrastructure.Migrations
{
[DbContext(typeof(PlatformDbContext))]
[Migration("20260221071055_OpenIddict")]
partial class OpenIddict
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.RoleAggregate.ApplicationRole", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<DateTimeOffset>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<bool>("IsSystem")
.HasColumnType("boolean");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.PrimitiveCollection<List<string>>("Permissions")
.HasColumnType("text[]");
b.Property<long?>("TenantId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.TenantAggregate.Tenant", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("ContactEmail")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<string>("ContactName")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("ContactPhone")
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property<DateTime?>("ExpiresAt")
.HasColumnType("timestamp with time zone");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<int?>("MaxUsers")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<long>("RowVersion")
.HasColumnType("bigint");
b.Property<int>("Status")
.HasColumnType("integer");
b.Property<string>("TenantCode")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("Status");
b.HasIndex("TenantCode")
.IsUnique();
b.ToTable("Platform_Tenants", (string)null);
});
modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.AccessLog", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Action")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<int>("Duration")
.HasColumnType("integer");
b.Property<string>("ErrorMessage")
.HasColumnType("text");
b.Property<string>("IpAddress")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Method")
.HasMaxLength(10)
.HasColumnType("character varying(10)");
b.Property<string>("RequestData")
.HasColumnType("text");
b.Property<string>("Resource")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("ResponseData")
.HasColumnType("text");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("TenantId")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("UserAgent")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property<string>("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("AccessLogs");
});
modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<DateTimeOffset>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<string>("RealName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("UpdatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("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("AspNetUsers", (string)null);
});
modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.AuditLog", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Action")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property<string>("ErrorMessage")
.HasColumnType("text");
b.Property<string>("IpAddress")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("NewValue")
.HasColumnType("text");
b.Property<string>("OldValue")
.HasColumnType("text");
b.Property<string>("Operation")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("Operator")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<long?>("TargetId")
.HasColumnType("bigint");
b.Property<string>("TargetName")
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<string>("TargetType")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("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("AuditLogs");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<long>("RoleId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<long>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<long>("UserId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<long>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("ProviderKey")
.HasColumnType("text");
b.Property<string>("ProviderDisplayName")
.HasColumnType("text");
b.Property<long>("UserId")
.HasColumnType("bigint");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<long>", b =>
{
b.Property<long>("UserId")
.HasColumnType("bigint");
b.Property<long>("RoleId")
.HasColumnType("bigint");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<long>", b =>
{
b.Property<long>("UserId")
.HasColumnType("bigint");
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Value")
.HasColumnType("text");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("text");
b.Property<string>("ApplicationType")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("ClientId")
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<string>("ClientSecret")
.HasColumnType("text");
b.Property<string>("ClientType")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("ConsentType")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("DisplayNames")
.HasColumnType("text");
b.Property<string>("JsonWebKeySet")
.HasColumnType("text");
b.Property<string>("Permissions")
.HasColumnType("text");
b.Property<string>("PostLogoutRedirectUris")
.HasColumnType("text");
b.Property<string>("Properties")
.HasColumnType("text");
b.Property<string>("RedirectUris")
.HasColumnType("text");
b.Property<string>("Requirements")
.HasColumnType("text");
b.Property<string>("Settings")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("ClientId")
.IsUnique();
b.ToTable("OpenIddictApplications", (string)null);
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("text");
b.Property<string>("ApplicationId")
.HasColumnType("text");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<DateTime?>("CreationDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("Properties")
.HasColumnType("text");
b.Property<string>("Scopes")
.HasColumnType("text");
b.Property<string>("Status")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Subject")
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.Property<string>("Type")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.HasKey("Id");
b.HasIndex("ApplicationId", "Status", "Subject", "Type");
b.ToTable("OpenIddictAuthorizations", (string)null);
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("text");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<string>("Descriptions")
.HasColumnType("text");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("DisplayNames")
.HasColumnType("text");
b.Property<string>("Name")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("Properties")
.HasColumnType("text");
b.Property<string>("Resources")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("OpenIddictScopes", (string)null);
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("text");
b.Property<string>("ApplicationId")
.HasColumnType("text");
b.Property<string>("AuthorizationId")
.HasColumnType("text");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<DateTime?>("CreationDate")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("ExpirationDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("Payload")
.HasColumnType("text");
b.Property<string>("Properties")
.HasColumnType("text");
b.Property<DateTime?>("RedemptionDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("ReferenceId")
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<string>("Status")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Subject")
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.Property<string>("Type")
.HasMaxLength(150)
.HasColumnType("character varying(150)");
b.HasKey("Id");
b.HasIndex("AuthorizationId");
b.HasIndex("ReferenceId")
.IsUnique();
b.HasIndex("ApplicationId", "Status", "Subject", "Type");
b.ToTable("OpenIddictTokens", (string)null);
});
modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", b =>
{
b.OwnsOne("Fengling.Platform.Domain.AggregatesModel.TenantAggregate.TenantInfo", "TenantInfo", b1 =>
{
b1.Property<long>("ApplicationUserId")
.HasColumnType("bigint");
b1.Property<string>("TenantCode")
.HasColumnType("text")
.HasColumnName("TenantCode");
b1.Property<long?>("TenantId")
.HasColumnType("bigint")
.HasColumnName("TenantId");
b1.Property<string>("TenantName")
.HasColumnType("text")
.HasColumnName("TenantName");
b1.HasKey("ApplicationUserId");
b1.ToTable("AspNetUsers");
b1.WithOwner()
.HasForeignKey("ApplicationUserId");
});
b.Navigation("TenantInfo");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>
{
b.HasOne("Fengling.Platform.Domain.AggregatesModel.RoleAggregate.ApplicationRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<long>", b =>
{
b.HasOne("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<long>", b =>
{
b.HasOne("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<long>", 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<long>", b =>
{
b.HasOne("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b =>
{
b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application")
.WithMany("Authorizations")
.HasForeignKey("ApplicationId");
b.Navigation("Application");
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b =>
{
b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application")
.WithMany("Tokens")
.HasForeignKey("ApplicationId");
b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization")
.WithMany("Tokens")
.HasForeignKey("AuthorizationId");
b.Navigation("Application");
b.Navigation("Authorization");
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b =>
{
b.Navigation("Authorizations");
b.Navigation("Tokens");
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b =>
{
b.Navigation("Tokens");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,166 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Fengling.Platform.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class OpenIddict : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "OpenIddictApplications",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
ApplicationType = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
ClientId = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
ClientSecret = table.Column<string>(type: "text", nullable: true),
ClientType = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
ConcurrencyToken = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
ConsentType = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
DisplayName = table.Column<string>(type: "text", nullable: true),
DisplayNames = table.Column<string>(type: "text", nullable: true),
JsonWebKeySet = table.Column<string>(type: "text", nullable: true),
Permissions = table.Column<string>(type: "text", nullable: true),
PostLogoutRedirectUris = table.Column<string>(type: "text", nullable: true),
Properties = table.Column<string>(type: "text", nullable: true),
RedirectUris = table.Column<string>(type: "text", nullable: true),
Requirements = table.Column<string>(type: "text", nullable: true),
Settings = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_OpenIddictApplications", x => x.Id);
});
migrationBuilder.CreateTable(
name: "OpenIddictScopes",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
ConcurrencyToken = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
Description = table.Column<string>(type: "text", nullable: true),
Descriptions = table.Column<string>(type: "text", nullable: true),
DisplayName = table.Column<string>(type: "text", nullable: true),
DisplayNames = table.Column<string>(type: "text", nullable: true),
Name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
Properties = table.Column<string>(type: "text", nullable: true),
Resources = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_OpenIddictScopes", x => x.Id);
});
migrationBuilder.CreateTable(
name: "OpenIddictAuthorizations",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
ApplicationId = table.Column<string>(type: "text", nullable: true),
ConcurrencyToken = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
CreationDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
Properties = table.Column<string>(type: "text", nullable: true),
Scopes = table.Column<string>(type: "text", nullable: true),
Status = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
Subject = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: true),
Type = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_OpenIddictAuthorizations", x => x.Id);
table.ForeignKey(
name: "FK_OpenIddictAuthorizations_OpenIddictApplications_Application~",
column: x => x.ApplicationId,
principalTable: "OpenIddictApplications",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "OpenIddictTokens",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
ApplicationId = table.Column<string>(type: "text", nullable: true),
AuthorizationId = table.Column<string>(type: "text", nullable: true),
ConcurrencyToken = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
CreationDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
ExpirationDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
Payload = table.Column<string>(type: "text", nullable: true),
Properties = table.Column<string>(type: "text", nullable: true),
RedemptionDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
ReferenceId = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
Status = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
Subject = table.Column<string>(type: "character varying(400)", maxLength: 400, nullable: true),
Type = table.Column<string>(type: "character varying(150)", maxLength: 150, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_OpenIddictTokens", x => x.Id);
table.ForeignKey(
name: "FK_OpenIddictTokens_OpenIddictApplications_ApplicationId",
column: x => x.ApplicationId,
principalTable: "OpenIddictApplications",
principalColumn: "Id");
table.ForeignKey(
name: "FK_OpenIddictTokens_OpenIddictAuthorizations_AuthorizationId",
column: x => x.AuthorizationId,
principalTable: "OpenIddictAuthorizations",
principalColumn: "Id");
});
migrationBuilder.CreateIndex(
name: "IX_OpenIddictApplications_ClientId",
table: "OpenIddictApplications",
column: "ClientId",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_OpenIddictAuthorizations_ApplicationId_Status_Subject_Type",
table: "OpenIddictAuthorizations",
columns: new[] { "ApplicationId", "Status", "Subject", "Type" });
migrationBuilder.CreateIndex(
name: "IX_OpenIddictScopes_Name",
table: "OpenIddictScopes",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_OpenIddictTokens_ApplicationId_Status_Subject_Type",
table: "OpenIddictTokens",
columns: new[] { "ApplicationId", "Status", "Subject", "Type" });
migrationBuilder.CreateIndex(
name: "IX_OpenIddictTokens_AuthorizationId",
table: "OpenIddictTokens",
column: "AuthorizationId");
migrationBuilder.CreateIndex(
name: "IX_OpenIddictTokens_ReferenceId",
table: "OpenIddictTokens",
column: "ReferenceId",
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "OpenIddictScopes");
migrationBuilder.DropTable(
name: "OpenIddictTokens");
migrationBuilder.DropTable(
name: "OpenIddictAuthorizations");
migrationBuilder.DropTable(
name: "OpenIddictApplications");
}
}
}

View File

@ -1,5 +1,6 @@
// <auto-generated /> // <auto-generated />
using System; using System;
using System.Collections.Generic;
using Fengling.Platform.Infrastructure; using Fengling.Platform.Infrastructure;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
@ -22,11 +23,62 @@ namespace Fengling.Platform.Infrastructure.Migrations
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.RoleAggregate.ApplicationRole", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<DateTimeOffset>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<bool>("IsSystem")
.HasColumnType("boolean");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.PrimitiveCollection<List<string>>("Permissions")
.HasColumnType("text[]");
b.Property<long?>("TenantId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.TenantAggregate.Tenant", b => modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.TenantAggregate.Tenant", b =>
{ {
b.Property<long>("Id") b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint"); .HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("ContactEmail") b.Property<string>("ContactEmail")
.IsRequired() .IsRequired()
.HasMaxLength(100) .HasMaxLength(100)
@ -44,9 +96,6 @@ namespace Fengling.Platform.Infrastructure.Migrations
b.Property<DateTime>("CreatedAt") b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone"); .HasColumnType("timestamp with time zone");
b.Property<bool>("Deleted")
.HasColumnType("boolean");
b.Property<string>("Description") b.Property<string>("Description")
.HasMaxLength(500) .HasMaxLength(500)
.HasColumnType("character varying(500)"); .HasColumnType("character varying(500)");
@ -54,6 +103,9 @@ namespace Fengling.Platform.Infrastructure.Migrations
b.Property<DateTime?>("ExpiresAt") b.Property<DateTime?>("ExpiresAt")
.HasColumnType("timestamp with time zone"); .HasColumnType("timestamp with time zone");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<int?>("MaxUsers") b.Property<int?>("MaxUsers")
.HasColumnType("integer"); .HasColumnType("integer");
@ -62,9 +114,8 @@ namespace Fengling.Platform.Infrastructure.Migrations
.HasMaxLength(100) .HasMaxLength(100)
.HasColumnType("character varying(100)"); .HasColumnType("character varying(100)");
b.Property<int>("RowVersion") b.Property<long>("RowVersion")
.IsConcurrencyToken() .HasColumnType("bigint");
.HasColumnType("integer");
b.Property<int>("Status") b.Property<int>("Status")
.HasColumnType("integer"); .HasColumnType("integer");
@ -86,6 +137,669 @@ namespace Fengling.Platform.Infrastructure.Migrations
b.ToTable("Platform_Tenants", (string)null); b.ToTable("Platform_Tenants", (string)null);
}); });
modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.AccessLog", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Action")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<int>("Duration")
.HasColumnType("integer");
b.Property<string>("ErrorMessage")
.HasColumnType("text");
b.Property<string>("IpAddress")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Method")
.HasMaxLength(10)
.HasColumnType("character varying(10)");
b.Property<string>("RequestData")
.HasColumnType("text");
b.Property<string>("Resource")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("ResponseData")
.HasColumnType("text");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("TenantId")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("UserAgent")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property<string>("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("AccessLogs");
});
modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<DateTimeOffset>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<string>("RealName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("UpdatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("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("AspNetUsers", (string)null);
});
modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.AuditLog", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Action")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property<string>("ErrorMessage")
.HasColumnType("text");
b.Property<string>("IpAddress")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("NewValue")
.HasColumnType("text");
b.Property<string>("OldValue")
.HasColumnType("text");
b.Property<string>("Operation")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("Operator")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<long?>("TargetId")
.HasColumnType("bigint");
b.Property<string>("TargetName")
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<string>("TargetType")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("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("AuditLogs");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<long>("RoleId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<long>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<long>("UserId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<long>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("ProviderKey")
.HasColumnType("text");
b.Property<string>("ProviderDisplayName")
.HasColumnType("text");
b.Property<long>("UserId")
.HasColumnType("bigint");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<long>", b =>
{
b.Property<long>("UserId")
.HasColumnType("bigint");
b.Property<long>("RoleId")
.HasColumnType("bigint");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<long>", b =>
{
b.Property<long>("UserId")
.HasColumnType("bigint");
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Value")
.HasColumnType("text");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("text");
b.Property<string>("ApplicationType")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("ClientId")
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<string>("ClientSecret")
.HasColumnType("text");
b.Property<string>("ClientType")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("ConsentType")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("DisplayNames")
.HasColumnType("text");
b.Property<string>("JsonWebKeySet")
.HasColumnType("text");
b.Property<string>("Permissions")
.HasColumnType("text");
b.Property<string>("PostLogoutRedirectUris")
.HasColumnType("text");
b.Property<string>("Properties")
.HasColumnType("text");
b.Property<string>("RedirectUris")
.HasColumnType("text");
b.Property<string>("Requirements")
.HasColumnType("text");
b.Property<string>("Settings")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("ClientId")
.IsUnique();
b.ToTable("OpenIddictApplications", (string)null);
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("text");
b.Property<string>("ApplicationId")
.HasColumnType("text");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<DateTime?>("CreationDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("Properties")
.HasColumnType("text");
b.Property<string>("Scopes")
.HasColumnType("text");
b.Property<string>("Status")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Subject")
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.Property<string>("Type")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.HasKey("Id");
b.HasIndex("ApplicationId", "Status", "Subject", "Type");
b.ToTable("OpenIddictAuthorizations", (string)null);
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("text");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<string>("Descriptions")
.HasColumnType("text");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("DisplayNames")
.HasColumnType("text");
b.Property<string>("Name")
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<string>("Properties")
.HasColumnType("text");
b.Property<string>("Resources")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("OpenIddictScopes", (string)null);
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("text");
b.Property<string>("ApplicationId")
.HasColumnType("text");
b.Property<string>("AuthorizationId")
.HasColumnType("text");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<DateTime?>("CreationDate")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("ExpirationDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("Payload")
.HasColumnType("text");
b.Property<string>("Properties")
.HasColumnType("text");
b.Property<DateTime?>("RedemptionDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("ReferenceId")
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<string>("Status")
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<string>("Subject")
.HasMaxLength(400)
.HasColumnType("character varying(400)");
b.Property<string>("Type")
.HasMaxLength(150)
.HasColumnType("character varying(150)");
b.HasKey("Id");
b.HasIndex("AuthorizationId");
b.HasIndex("ReferenceId")
.IsUnique();
b.HasIndex("ApplicationId", "Status", "Subject", "Type");
b.ToTable("OpenIddictTokens", (string)null);
});
modelBuilder.Entity("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", b =>
{
b.OwnsOne("Fengling.Platform.Domain.AggregatesModel.TenantAggregate.TenantInfo", "TenantInfo", b1 =>
{
b1.Property<long>("ApplicationUserId")
.HasColumnType("bigint");
b1.Property<string>("TenantCode")
.HasColumnType("text")
.HasColumnName("TenantCode");
b1.Property<long?>("TenantId")
.HasColumnType("bigint")
.HasColumnName("TenantId");
b1.Property<string>("TenantName")
.HasColumnType("text")
.HasColumnName("TenantName");
b1.HasKey("ApplicationUserId");
b1.ToTable("AspNetUsers");
b1.WithOwner()
.HasForeignKey("ApplicationUserId");
});
b.Navigation("TenantInfo");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>
{
b.HasOne("Fengling.Platform.Domain.AggregatesModel.RoleAggregate.ApplicationRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<long>", b =>
{
b.HasOne("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<long>", b =>
{
b.HasOne("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<long>", 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<long>", b =>
{
b.HasOne("Fengling.Platform.Domain.AggregatesModel.UserAggregate.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b =>
{
b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application")
.WithMany("Authorizations")
.HasForeignKey("ApplicationId");
b.Navigation("Application");
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b =>
{
b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application")
.WithMany("Tokens")
.HasForeignKey("ApplicationId");
b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization")
.WithMany("Tokens")
.HasForeignKey("AuthorizationId");
b.Navigation("Application");
b.Navigation("Authorization");
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b =>
{
b.Navigation("Authorizations");
b.Navigation("Tokens");
});
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b =>
{
b.Navigation("Tokens");
});
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }
} }

View File

@ -0,0 +1,191 @@
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 "AccessLogs" (
"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_AccessLogs" PRIMARY KEY ("Id")
);
CREATE TABLE "AspNetRoles" (
"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_AspNetRoles" PRIMARY KEY ("Id")
);
CREATE TABLE "AspNetUsers" (
"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_AspNetUsers" PRIMARY KEY ("Id")
);
CREATE TABLE "AuditLogs" (
"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_AuditLogs" PRIMARY KEY ("Id")
);
CREATE TABLE "Platform_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_Platform_Tenants" PRIMARY KEY ("Id")
);
CREATE TABLE "AspNetRoleClaims" (
"Id" integer GENERATED BY DEFAULT AS IDENTITY,
"RoleId" bigint NOT NULL,
"ClaimType" text,
"ClaimValue" text,
CONSTRAINT "PK_AspNetRoleClaims" PRIMARY KEY ("Id"),
CONSTRAINT "FK_AspNetRoleClaims_AspNetRoles_RoleId" FOREIGN KEY ("RoleId") REFERENCES "AspNetRoles" ("Id") ON DELETE CASCADE
);
CREATE TABLE "AspNetUserClaims" (
"Id" integer GENERATED BY DEFAULT AS IDENTITY,
"UserId" bigint NOT NULL,
"ClaimType" text,
"ClaimValue" text,
CONSTRAINT "PK_AspNetUserClaims" PRIMARY KEY ("Id"),
CONSTRAINT "FK_AspNetUserClaims_AspNetUsers_UserId" FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id") ON DELETE CASCADE
);
CREATE TABLE "AspNetUserLogins" (
"LoginProvider" text NOT NULL,
"ProviderKey" text NOT NULL,
"ProviderDisplayName" text,
"UserId" bigint NOT NULL,
CONSTRAINT "PK_AspNetUserLogins" PRIMARY KEY ("LoginProvider", "ProviderKey"),
CONSTRAINT "FK_AspNetUserLogins_AspNetUsers_UserId" FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id") ON DELETE CASCADE
);
CREATE TABLE "AspNetUserRoles" (
"UserId" bigint NOT NULL,
"RoleId" bigint NOT NULL,
CONSTRAINT "PK_AspNetUserRoles" PRIMARY KEY ("UserId", "RoleId"),
CONSTRAINT "FK_AspNetUserRoles_AspNetRoles_RoleId" FOREIGN KEY ("RoleId") REFERENCES "AspNetRoles" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_AspNetUserRoles_AspNetUsers_UserId" FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id") ON DELETE CASCADE
);
CREATE TABLE "AspNetUserTokens" (
"UserId" bigint NOT NULL,
"LoginProvider" text NOT NULL,
"Name" text NOT NULL,
"Value" text,
CONSTRAINT "PK_AspNetUserTokens" PRIMARY KEY ("UserId", "LoginProvider", "Name"),
CONSTRAINT "FK_AspNetUserTokens_AspNetUsers_UserId" FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_AccessLogs_Action" ON "AccessLogs" ("Action");
CREATE INDEX "IX_AccessLogs_CreatedAt" ON "AccessLogs" ("CreatedAt");
CREATE INDEX "IX_AccessLogs_Status" ON "AccessLogs" ("Status");
CREATE INDEX "IX_AccessLogs_TenantId" ON "AccessLogs" ("TenantId");
CREATE INDEX "IX_AccessLogs_UserName" ON "AccessLogs" ("UserName");
CREATE INDEX "IX_AspNetRoleClaims_RoleId" ON "AspNetRoleClaims" ("RoleId");
CREATE UNIQUE INDEX "RoleNameIndex" ON "AspNetRoles" ("NormalizedName");
CREATE INDEX "IX_AspNetUserClaims_UserId" ON "AspNetUserClaims" ("UserId");
CREATE INDEX "IX_AspNetUserLogins_UserId" ON "AspNetUserLogins" ("UserId");
CREATE INDEX "IX_AspNetUserRoles_RoleId" ON "AspNetUserRoles" ("RoleId");
CREATE INDEX "EmailIndex" ON "AspNetUsers" ("NormalizedEmail");
CREATE UNIQUE INDEX "IX_AspNetUsers_PhoneNumber" ON "AspNetUsers" ("PhoneNumber");
CREATE UNIQUE INDEX "UserNameIndex" ON "AspNetUsers" ("NormalizedUserName");
CREATE INDEX "IX_AuditLogs_Action" ON "AuditLogs" ("Action");
CREATE INDEX "IX_AuditLogs_CreatedAt" ON "AuditLogs" ("CreatedAt");
CREATE INDEX "IX_AuditLogs_Operation" ON "AuditLogs" ("Operation");
CREATE INDEX "IX_AuditLogs_Operator" ON "AuditLogs" ("Operator");
CREATE INDEX "IX_AuditLogs_TenantId" ON "AuditLogs" ("TenantId");
CREATE INDEX "IX_Platform_Tenants_Status" ON "Platform_Tenants" ("Status");
CREATE UNIQUE INDEX "IX_Platform_Tenants_TenantCode" ON "Platform_Tenants" ("TenantCode");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20260221065049_Initial', '10.0.0');
COMMIT;

View File

@ -0,0 +1,84 @@
START TRANSACTION;
CREATE TABLE "OpenIddictApplications" (
"Id" text NOT NULL,
"ApplicationType" character varying(50),
"ClientId" character varying(100),
"ClientSecret" text,
"ClientType" character varying(50),
"ConcurrencyToken" character varying(50),
"ConsentType" character varying(50),
"DisplayName" text,
"DisplayNames" text,
"JsonWebKeySet" text,
"Permissions" text,
"PostLogoutRedirectUris" text,
"Properties" text,
"RedirectUris" text,
"Requirements" text,
"Settings" text,
CONSTRAINT "PK_OpenIddictApplications" PRIMARY KEY ("Id")
);
CREATE TABLE "OpenIddictScopes" (
"Id" text NOT NULL,
"ConcurrencyToken" character varying(50),
"Description" text,
"Descriptions" text,
"DisplayName" text,
"DisplayNames" text,
"Name" character varying(200),
"Properties" text,
"Resources" text,
CONSTRAINT "PK_OpenIddictScopes" PRIMARY KEY ("Id")
);
CREATE TABLE "OpenIddictAuthorizations" (
"Id" text NOT NULL,
"ApplicationId" text,
"ConcurrencyToken" character varying(50),
"CreationDate" timestamp with time zone,
"Properties" text,
"Scopes" text,
"Status" character varying(50),
"Subject" character varying(400),
"Type" character varying(50),
CONSTRAINT "PK_OpenIddictAuthorizations" PRIMARY KEY ("Id"),
CONSTRAINT "FK_OpenIddictAuthorizations_OpenIddictApplications_Application~" FOREIGN KEY ("ApplicationId") REFERENCES "OpenIddictApplications" ("Id")
);
CREATE TABLE "OpenIddictTokens" (
"Id" text NOT NULL,
"ApplicationId" text,
"AuthorizationId" text,
"ConcurrencyToken" character varying(50),
"CreationDate" timestamp with time zone,
"ExpirationDate" timestamp with time zone,
"Payload" text,
"Properties" text,
"RedemptionDate" timestamp with time zone,
"ReferenceId" character varying(100),
"Status" character varying(50),
"Subject" character varying(400),
"Type" character varying(150),
CONSTRAINT "PK_OpenIddictTokens" PRIMARY KEY ("Id"),
CONSTRAINT "FK_OpenIddictTokens_OpenIddictApplications_ApplicationId" FOREIGN KEY ("ApplicationId") REFERENCES "OpenIddictApplications" ("Id"),
CONSTRAINT "FK_OpenIddictTokens_OpenIddictAuthorizations_AuthorizationId" FOREIGN KEY ("AuthorizationId") REFERENCES "OpenIddictAuthorizations" ("Id")
);
CREATE UNIQUE INDEX "IX_OpenIddictApplications_ClientId" ON "OpenIddictApplications" ("ClientId");
CREATE INDEX "IX_OpenIddictAuthorizations_ApplicationId_Status_Subject_Type" ON "OpenIddictAuthorizations" ("ApplicationId", "Status", "Subject", "Type");
CREATE UNIQUE INDEX "IX_OpenIddictScopes_Name" ON "OpenIddictScopes" ("Name");
CREATE INDEX "IX_OpenIddictTokens_ApplicationId_Status_Subject_Type" ON "OpenIddictTokens" ("ApplicationId", "Status", "Subject", "Type");
CREATE INDEX "IX_OpenIddictTokens_AuthorizationId" ON "OpenIddictTokens" ("AuthorizationId");
CREATE UNIQUE INDEX "IX_OpenIddictTokens_ReferenceId" ON "OpenIddictTokens" ("ReferenceId");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20260221071055_OpenIddict', '10.0.0');
COMMIT;

View File

@ -1,14 +1,19 @@
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace Fengling.Platform.Infrastructure; namespace Fengling.Platform.Infrastructure;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
using MediatR;
using Microsoft.EntityFrameworkCore;
using NetCorePal.Extensions.Repository.EntityFrameworkCore;
public partial class PlatformDbContext(DbContextOptions<PlatformDbContext> options, IMediator mediator) public partial class PlatformDbContext(DbContextOptions<PlatformDbContext> options)
: AppDbContextBase(options, mediator) : IdentityDbContext<ApplicationUser, ApplicationRole, long>(options)
{ {
public DbSet<Tenant> Tenants => Set<Tenant>(); public DbSet<Tenant> Tenants => Set<Tenant>();
public DbSet<AccessLog> AccessLogs => Set<AccessLog>();
public DbSet<AuditLog> AuditLogs => Set<AuditLog>();
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {
@ -17,13 +22,63 @@ public partial class PlatformDbContext(DbContextOptions<PlatformDbContext> optio
throw new ArgumentNullException(nameof(modelBuilder)); throw new ArgumentNullException(nameof(modelBuilder));
} }
modelBuilder.Entity<ApplicationUser>(entity =>
{
entity.Property(e => e.PhoneNumber).HasMaxLength(20);
entity.HasIndex(e => e.PhoneNumber).IsUnique();
entity.OwnsOne(e => e.TenantInfo, navigationBuilder =>
{
navigationBuilder.Property(t => t.TenantCode).HasColumnName("TenantCode");
navigationBuilder.Property(t => t.TenantId).HasColumnName("TenantId");
navigationBuilder.Property(t => t.TenantName).HasColumnName("TenantName");
navigationBuilder.WithOwner();
});
});
modelBuilder.Entity<ApplicationRole>(entity =>
{
entity.Property(e => e.Description).HasMaxLength(200);
});
modelBuilder.Entity<AccessLog>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => e.CreatedAt);
entity.HasIndex(e => e.UserName);
entity.HasIndex(e => e.TenantId);
entity.HasIndex(e => e.Action);
entity.HasIndex(e => e.Status);
entity.Property(e => e.UserName).HasMaxLength(50);
entity.Property(e => e.TenantId).HasMaxLength(50);
entity.Property(e => e.Action).HasMaxLength(20);
entity.Property(e => e.Resource).HasMaxLength(200);
entity.Property(e => e.Method).HasMaxLength(10);
entity.Property(e => e.IpAddress).HasMaxLength(50);
entity.Property(e => e.UserAgent).HasMaxLength(500);
entity.Property(e => e.Status).HasMaxLength(20);
});
modelBuilder.Entity<AuditLog>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => e.CreatedAt);
entity.HasIndex(e => e.Operator);
entity.HasIndex(e => e.TenantId);
entity.HasIndex(e => e.Operation);
entity.HasIndex(e => e.Action);
entity.Property(e => e.Operator).HasMaxLength(50);
entity.Property(e => e.TenantId).HasMaxLength(50);
entity.Property(e => e.Operation).HasMaxLength(20);
entity.Property(e => e.Action).HasMaxLength(20);
entity.Property(e => e.TargetType).HasMaxLength(50);
entity.Property(e => e.TargetName).HasMaxLength(100);
entity.Property(e => e.IpAddress).HasMaxLength(50);
entity.Property(e => e.Description).HasMaxLength(500);
entity.Property(e => e.Status).HasMaxLength(20);
});
modelBuilder.ApplyConfigurationsFromAssembly(typeof(PlatformDbContext).Assembly); modelBuilder.ApplyConfigurationsFromAssembly(typeof(PlatformDbContext).Assembly);
base.OnModelCreating(modelBuilder); base.OnModelCreating(modelBuilder);
} }
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
ConfigureStronglyTypedIdValueConverter(configurationBuilder);
base.ConfigureConventions(configurationBuilder);
}
} }

View File

@ -1,27 +0,0 @@
using NetCorePal.Extensions.Repository.EntityFrameworkCore;
namespace Fengling.Platform.Infrastructure.Repositories;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
using NetCorePal.Extensions.Repository;
using Microsoft.EntityFrameworkCore;
public interface ITenantRepository : IRepository<Tenant, TenantId>
{
Task<Tenant?> GetByTenantIdAsync(string tenantId, CancellationToken cancellationToken = default);
Task<Tenant?> GetByIdIncludeH5Async(TenantId id, CancellationToken cancellationToken = default);
}
public class TenantRepository(PlatformDbContext context)
: RepositoryBase<Tenant, TenantId, PlatformDbContext>(context), ITenantRepository
{
public async Task<Tenant?> GetByTenantIdAsync(string tenantId, CancellationToken cancellationToken = default)
{
return await DbContext.Tenants.FirstOrDefaultAsync(t => t.TenantCode == tenantId, cancellationToken);
}
public async Task<Tenant?> GetByIdIncludeH5Async(TenantId id, CancellationToken cancellationToken = default)
{
return await DbContext.Tenants.FirstOrDefaultAsync(t => t.Id == id, cancellationToken);
}
}

View File

@ -1,24 +1,219 @@
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate; using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Abstractions;
namespace Fengling.Platform.Infrastructure; namespace Fengling.Platform.Infrastructure;
public static class SeedData public static class SeedData
{ {
public static async Task<Tenant> InitializeAsync(this PlatformDbContext context) public static async Task InitializeAsync(this IServiceScope scope)
{ {
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<ApplicationRole>>();
var context = scope.ServiceProvider.GetRequiredService<PlatformDbContext>();
await context.Database.EnsureCreatedAsync(); await context.Database.EnsureCreatedAsync();
var adminTenant = context.Tenants var adminTenant = context.Tenants
.FirstOrDefault(t => t.Name == "Administrator"); .FirstOrDefault(t => t.TenantCode == "Administrator");
if (adminTenant != null) if (adminTenant == null)
{ {
return adminTenant; adminTenant = new Tenant
{
TenantCode = "Administrator",
Name = "超级系统",
ContactName = "",
ContactEmail = "",
Status = TenantStatus.Active,
CreatedAt = DateTime.UtcNow
};
await context.Tenants.AddAsync(adminTenant);
} }
adminTenant = new Tenant("Administrator", "超级系统",
"", "");
await context.Tenants.AddAsync(adminTenant);
var role = await roleManager.Roles
.OfType<ApplicationRole>()
.AsQueryable()
.FirstOrDefaultAsync(x => x.Name == "admin" && x.TenantId == null);
if (role == null)
{
role = new ApplicationRole()
{
CreatedTime = DateTimeOffset.UtcNow,
TenantId = null,
Name = "admin", Description = "系统管理员",
DisplayName = "系统管理员",
IsSystem = true,
};
await roleManager.CreateAsync(role);
}
var user = await userManager.FindByNameAsync("admin");
if (user == null)
{
user = new ApplicationUser()
{
UserName = "admin",
RealName = "系统超级管理员",
Email = "samsu9194@163.com",
TenantInfo = new TenantInfo(adminTenant),
PhoneNumber = "15921072307",
SecurityStamp = Guid.NewGuid().ToString(),
};
await userManager.CreateAsync(user, "Admin@123");
await userManager.AddToRoleAsync(user, "admin");
}
await context.SaveChangesAsync(); await context.SaveChangesAsync();
return adminTenant;
await InitializeOpenIddictAsync(scope.ServiceProvider);
}
private static async Task InitializeOpenIddictAsync(IServiceProvider serviceProvider)
{
var applicationManager = serviceProvider.GetRequiredService<IOpenIddictApplicationManager>();
var scopeManager = serviceProvider.GetRequiredService<IOpenIddictScopeManager>();
await RegisterCustomScopesAsync(scopeManager);
await RegisterVbenConsoleClientAsync(applicationManager);
await RegisterSwaggerClientAsync(applicationManager);
await RegisterApiClientAsync(applicationManager);
}
private static async Task RegisterCustomScopesAsync(IOpenIddictScopeManager scopeManager)
{
var fenglingApiScope = await scopeManager.FindByNameAsync("fengling_api");
if (fenglingApiScope == null)
{
await scopeManager.CreateAsync(new OpenIddictScopeDescriptor
{
Name = "fengling_api",
DisplayName = "Fengling API Access",
Description = "Allow access to Fengling API resources"
});
}
var authServerAdminScope = await scopeManager.FindByNameAsync("auth_server_admin");
if (authServerAdminScope == null)
{
await scopeManager.CreateAsync(new OpenIddictScopeDescriptor
{
Name = "auth_server_admin",
DisplayName = "Auth Server Admin",
Description = "Allow access to auth server admin APIs"
});
}
}
private static async Task RegisterVbenConsoleClientAsync(IOpenIddictApplicationManager applicationManager)
{
var existingClient = await applicationManager.FindByClientIdAsync("fengling-console");
if (existingClient != null)
{
return;
}
await applicationManager.CreateAsync(new OpenIddictApplicationDescriptor
{
ClientId = "fengling-console",
DisplayName = "Fengling Console (Vben Admin)",
ApplicationType = OpenIddictConstants.ApplicationTypes.Web,
ClientType = OpenIddictConstants.ClientTypes.Public,
RedirectUris =
{
new Uri("http://localhost:5777/auth/callback"),
new Uri("http://localhost:5777")
},
PostLogoutRedirectUris =
{
new Uri("http://localhost:5777")
},
Permissions =
{
OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode,
"hybrid",
OpenIddictConstants.Permissions.GrantTypes.RefreshToken,
OpenIddictConstants.Permissions.GrantTypes.ClientCredentials,
OpenIddictConstants.Permissions.Endpoints.Authorization,
OpenIddictConstants.Permissions.Endpoints.Token,
"userinfo",
OpenIddictConstants.Permissions.Endpoints.EndSession,
"client_secret",
OpenIddictConstants.Permissions.Scopes.Email,
OpenIddictConstants.Permissions.Scopes.Profile,
"openid",
"offline_access",
OpenIddictConstants.Permissions.Scopes.Roles,
OpenIddictConstants.Permissions.ResponseTypes.Code,
OpenIddictConstants.Permissions.ResponseTypes.CodeIdTokenToken,
OpenIddictConstants.Permissions.Prefixes.Scope + "api",
},
Requirements =
{
OpenIddictConstants.Requirements.Features.ProofKeyForCodeExchange
}
});
}
private static async Task RegisterSwaggerClientAsync(IOpenIddictApplicationManager applicationManager)
{
var existingClient = await applicationManager.FindByClientIdAsync("swagger-ui");
if (existingClient != null)
{
return;
}
await applicationManager.CreateAsync(new OpenIddictApplicationDescriptor
{
ClientId = "swagger-ui",
DisplayName = "Swagger UI",
RedirectUris =
{
new Uri("http://localhost:5231/swagger/oauth2-redirect.html"),
new Uri("http://localhost:5511/swagger/oauth2-redirect.html"),
new Uri("http://localhost:5132/swagger/oauth2-redirect.html"),
},
Permissions =
{
OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode,
OpenIddictConstants.Permissions.Endpoints.Authorization,
OpenIddictConstants.Permissions.Endpoints.Token,
"client_secret",
"openid"
}
});
}
private static async Task RegisterApiClientAsync(IOpenIddictApplicationManager applicationManager)
{
var existingClient = await applicationManager.FindByClientIdAsync("fengling-api");
if (existingClient != null)
{
return;
}
await applicationManager.CreateAsync(new OpenIddictApplicationDescriptor
{
ClientId = "fengling-api",
ClientSecret = "fengling-api-secret",
DisplayName = "Fengling API (Resource Server)",
ApplicationType = OpenIddictConstants.ApplicationTypes.Web,
ClientType = OpenIddictConstants.ClientTypes.Confidential,
Permissions =
{
OpenIddictConstants.Permissions.Endpoints.Introspection
}
});
} }
} }

View File

@ -0,0 +1,74 @@
namespace Fengling.Platform.Infrastructure;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
using Microsoft.AspNetCore.Identity;
public interface ITenantManager
{
Task<Tenant?> FindByIdAsync(long? tenantId, CancellationToken cancellationToken = default);
Task<Tenant?> FindByTenantCodeAsync(string tenantCode, CancellationToken cancellationToken = default);
Task<IList<Tenant>> GetAllAsync(CancellationToken cancellationToken = default);
Task<IList<Tenant>> GetPagedAsync(int page, int pageSize, string? name = null, string? tenantCode = null, TenantStatus? status = null, CancellationToken cancellationToken = default);
Task<int> GetCountAsync(string? name = null, string? tenantCode = null, TenantStatus? status = null, CancellationToken cancellationToken = default);
Task<IdentityResult> CreateAsync(Tenant tenant, CancellationToken cancellationToken = default);
Task<IdentityResult> UpdateAsync(Tenant tenant, CancellationToken cancellationToken = default);
Task<IdentityResult> DeleteAsync(Tenant tenant, CancellationToken cancellationToken = default);
Task<int> GetUserCountAsync(long tenantId, CancellationToken cancellationToken = default);
Task<IdentityResult> SetTenantCodeAsync(Tenant tenant, string code, CancellationToken cancellationToken = default);
}
public sealed class TenantManager(ITenantStore store) : ITenantManager
{
public async Task<Tenant?> FindByIdAsync(long? tenantId, CancellationToken cancellationToken = default)
{
return await store.FindByIdAsync(tenantId, cancellationToken);
}
public async Task<Tenant?> FindByTenantCodeAsync(string tenantCode, CancellationToken cancellationToken = default)
{
return await store.FindByTenantCodeAsync(tenantCode, cancellationToken);
}
public async Task<IList<Tenant>> GetAllAsync(CancellationToken cancellationToken = default)
{
return await store.GetAllAsync(cancellationToken);
}
public async Task<IList<Tenant>> GetPagedAsync(int page, int pageSize, string? name = null,
string? tenantCode = null, TenantStatus? status = null, CancellationToken cancellationToken = default)
{
return await store.GetPagedAsync(page, pageSize, name, tenantCode, status, cancellationToken);
}
public async Task<int> GetCountAsync(string? name = null, string? tenantCode = null,
TenantStatus? status = null, CancellationToken cancellationToken = default)
{
return await store.GetCountAsync(name, tenantCode, status, cancellationToken);
}
public async Task<IdentityResult> CreateAsync(Tenant tenant, CancellationToken cancellationToken = default)
{
return await store.CreateAsync(tenant, cancellationToken);
}
public async Task<IdentityResult> UpdateAsync(Tenant tenant, CancellationToken cancellationToken = default)
{
return await store.UpdateAsync(tenant, cancellationToken);
}
public async Task<IdentityResult> DeleteAsync(Tenant tenant, CancellationToken cancellationToken = default)
{
return await store.DeleteAsync(tenant, cancellationToken);
}
public async Task<int> GetUserCountAsync(long tenantId, CancellationToken cancellationToken = default)
{
return await store.GetUserCountAsync(tenantId, cancellationToken);
}
public async Task<IdentityResult> SetTenantCodeAsync(Tenant tenant, string code, CancellationToken cancellationToken = default)
{
await store.SetTenantCodeAsync(tenant, code, cancellationToken);
return IdentityResult.Success;
}
}

View File

@ -0,0 +1,187 @@
namespace Fengling.Platform.Infrastructure;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
public class TenantStore : ITenantStore
{
private readonly PlatformDbContext _context;
private readonly DbSet<Tenant> _tenants;
public TenantStore(PlatformDbContext context)
{
_context = context;
_tenants = context.Tenants;
}
public void Dispose() { }
public virtual Task<Tenant?> FindByIdAsync(long? tenantId, CancellationToken cancellationToken = default)
{
if (tenantId == null) return Task.FromResult<Tenant?>(null);
return _tenants.FirstOrDefaultAsync(t => t.Id == tenantId, cancellationToken);
}
public virtual Task<Tenant?> FindByTenantCodeAsync(string tenantCode, CancellationToken cancellationToken = default)
{
return _tenants.FirstOrDefaultAsync(t => t.TenantCode == tenantCode, cancellationToken);
}
public virtual async Task<IList<Tenant>> GetAllAsync(CancellationToken cancellationToken = default)
{
return await _tenants.ToListAsync(cancellationToken);
}
public virtual async Task<IList<Tenant>> GetPagedAsync(int page, int pageSize, string? name = null,
string? tenantCode = null, TenantStatus? status = null, CancellationToken cancellationToken = default)
{
var query = _tenants.AsQueryable();
if (!string.IsNullOrEmpty(name))
query = query.Where(t => t.Name.Contains(name));
if (!string.IsNullOrEmpty(tenantCode))
query = query.Where(t => t.TenantCode.Contains(tenantCode));
if (status.HasValue)
query = query.Where(t => t.Status == status);
return await query
.OrderByDescending(t => t.CreatedAt)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync(cancellationToken);
}
public virtual async Task<int> GetCountAsync(string? name = null, string? tenantCode = null,
TenantStatus? status = null, CancellationToken cancellationToken = default)
{
var query = _tenants.AsQueryable();
if (!string.IsNullOrEmpty(name))
query = query.Where(t => t.Name.Contains(name));
if (!string.IsNullOrEmpty(tenantCode))
query = query.Where(t => t.TenantCode.Contains(tenantCode));
if (status.HasValue)
query = query.Where(t => t.Status == status);
return await query.CountAsync(cancellationToken);
}
public virtual async Task<IdentityResult> CreateAsync(Tenant tenant, CancellationToken cancellationToken = default)
{
_tenants.Add(tenant);
await _context.SaveChangesAsync(cancellationToken);
return IdentityResult.Success;
}
public virtual async Task<IdentityResult> UpdateAsync(Tenant tenant, CancellationToken cancellationToken = default)
{
tenant.UpdatedAt = DateTime.UtcNow;
_tenants.Update(tenant);
await _context.SaveChangesAsync(cancellationToken);
return IdentityResult.Success;
}
public virtual async Task<IdentityResult> DeleteAsync(Tenant tenant, CancellationToken cancellationToken = default)
{
_tenants.Remove(tenant);
await _context.SaveChangesAsync(cancellationToken);
return IdentityResult.Success;
}
public virtual async Task<int> GetUserCountAsync(long tenantId, CancellationToken cancellationToken = default)
{
return await _context.Users.CountAsync(u => u.TenantInfo.TenantId == tenantId && !u.IsDeleted, cancellationToken);
}
public virtual Task<string> GetTenantCodeAsync(Tenant tenant, CancellationToken cancellationToken = default)
=> Task.FromResult(tenant.TenantCode);
public virtual Task<string> GetNameAsync(Tenant tenant, CancellationToken cancellationToken = default)
=> Task.FromResult(tenant.Name);
public virtual Task<string> GetContactNameAsync(Tenant tenant, CancellationToken cancellationToken = default)
=> Task.FromResult(tenant.ContactName);
public virtual Task<string> GetContactEmailAsync(Tenant tenant, CancellationToken cancellationToken = default)
=> Task.FromResult(tenant.ContactEmail);
public virtual Task<string?> GetContactPhoneAsync(Tenant tenant, CancellationToken cancellationToken = default)
=> Task.FromResult(tenant.ContactPhone);
public virtual Task<int?> GetMaxUsersAsync(Tenant tenant, CancellationToken cancellationToken = default)
=> Task.FromResult(tenant.MaxUsers);
public virtual Task<string?> GetDescriptionAsync(Tenant tenant, CancellationToken cancellationToken = default)
=> Task.FromResult(tenant.Description);
public virtual Task<TenantStatus> GetStatusAsync(Tenant tenant, CancellationToken cancellationToken = default)
=> Task.FromResult(tenant.Status);
public virtual Task<DateTime?> GetExpiresAtAsync(Tenant tenant, CancellationToken cancellationToken = default)
=> Task.FromResult(tenant.ExpiresAt);
public virtual Task<DateTime> GetCreatedAtAsync(Tenant tenant, CancellationToken cancellationToken = default)
=> Task.FromResult(tenant.CreatedAt);
public virtual Task<DateTime?> GetUpdatedAtAsync(Tenant tenant, CancellationToken cancellationToken = default)
=> Task.FromResult(tenant.UpdatedAt);
public virtual Task SetTenantCodeAsync(Tenant tenant, string code, CancellationToken cancellationToken = default)
{
tenant.TenantCode = code;
return Task.CompletedTask;
}
public virtual Task SetNameAsync(Tenant tenant, string name, CancellationToken cancellationToken = default)
{
tenant.Name = name;
return Task.CompletedTask;
}
public virtual Task SetContactNameAsync(Tenant tenant, string name, CancellationToken cancellationToken = default)
{
tenant.ContactName = name;
return Task.CompletedTask;
}
public virtual Task SetContactEmailAsync(Tenant tenant, string email, CancellationToken cancellationToken = default)
{
tenant.ContactEmail = email;
return Task.CompletedTask;
}
public virtual Task SetContactPhoneAsync(Tenant tenant, string? phone, CancellationToken cancellationToken = default)
{
tenant.ContactPhone = phone;
return Task.CompletedTask;
}
public virtual Task SetMaxUsersAsync(Tenant tenant, int? maxUsers, CancellationToken cancellationToken = default)
{
tenant.MaxUsers = maxUsers;
return Task.CompletedTask;
}
public virtual Task SetDescriptionAsync(Tenant tenant, string? description, CancellationToken cancellationToken = default)
{
tenant.Description = description;
return Task.CompletedTask;
}
public virtual Task SetStatusAsync(Tenant tenant, TenantStatus status, CancellationToken cancellationToken = default)
{
tenant.Status = status;
return Task.CompletedTask;
}
public virtual Task SetExpiresAtAsync(Tenant tenant, DateTime? expiresAt, CancellationToken cancellationToken = default)
{
tenant.ExpiresAt = expiresAt;
return Task.CompletedTask;
}
}