247 lines
10 KiB
C#
247 lines
10 KiB
C#
using MassTransit;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||
using TakeoutSaaS.Domain.Identity.Entities;
|
||
using TakeoutSaaS.Infrastructure.Common.Persistence;
|
||
using TakeoutSaaS.Shared.Abstractions.Ids;
|
||
using TakeoutSaaS.Shared.Abstractions.Security;
|
||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||
|
||
namespace TakeoutSaaS.Infrastructure.Identity.Persistence;
|
||
|
||
/// <summary>
|
||
/// 身份认证 DbContext,带多租户过滤与审计字段处理。
|
||
/// </summary>
|
||
public sealed class IdentityDbContext(
|
||
DbContextOptions<IdentityDbContext> options,
|
||
ITenantProvider tenantProvider,
|
||
ICurrentUserAccessor? currentUserAccessor = null,
|
||
IIdGenerator? idGenerator = null)
|
||
: TenantAwareDbContext(options, tenantProvider, currentUserAccessor, idGenerator)
|
||
{
|
||
/// <summary>
|
||
/// 管理后台用户集合。
|
||
/// </summary>
|
||
public DbSet<IdentityUser> IdentityUsers => Set<IdentityUser>();
|
||
|
||
/// <summary>
|
||
/// 小程序用户集合。
|
||
/// </summary>
|
||
public DbSet<MiniUser> MiniUsers => Set<MiniUser>();
|
||
|
||
/// <summary>
|
||
/// 角色集合。
|
||
/// </summary>
|
||
public DbSet<Role> Roles => Set<Role>();
|
||
|
||
/// <summary>
|
||
/// 角色模板集合(系统级)。
|
||
/// </summary>
|
||
public DbSet<RoleTemplate> RoleTemplates => Set<RoleTemplate>();
|
||
|
||
/// <summary>
|
||
/// 角色模板权限集合。
|
||
/// </summary>
|
||
public DbSet<RoleTemplatePermission> RoleTemplatePermissions => Set<RoleTemplatePermission>();
|
||
|
||
/// <summary>
|
||
/// 权限集合。
|
||
/// </summary>
|
||
public DbSet<Permission> Permissions => Set<Permission>();
|
||
|
||
/// <summary>
|
||
/// 用户-角色关系。
|
||
/// </summary>
|
||
public DbSet<UserRole> UserRoles => Set<UserRole>();
|
||
|
||
/// <summary>
|
||
/// 角色-权限关系。
|
||
/// </summary>
|
||
public DbSet<RolePermission> RolePermissions => Set<RolePermission>();
|
||
|
||
/// <summary>
|
||
/// 菜单定义集合。
|
||
/// </summary>
|
||
public DbSet<MenuDefinition> MenuDefinitions => Set<MenuDefinition>();
|
||
|
||
/// <summary>
|
||
/// 配置实体模型。
|
||
/// </summary>
|
||
/// <param name="modelBuilder">模型构建器。</param>
|
||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||
{
|
||
base.OnModelCreating(modelBuilder);
|
||
ConfigureIdentityUser(modelBuilder.Entity<IdentityUser>());
|
||
ConfigureMiniUser(modelBuilder.Entity<MiniUser>());
|
||
ConfigureRole(modelBuilder.Entity<Role>());
|
||
ConfigureRoleTemplate(modelBuilder.Entity<RoleTemplate>());
|
||
ConfigureRoleTemplatePermission(modelBuilder.Entity<RoleTemplatePermission>());
|
||
ConfigurePermission(modelBuilder.Entity<Permission>());
|
||
ConfigureUserRole(modelBuilder.Entity<UserRole>());
|
||
ConfigureRolePermission(modelBuilder.Entity<RolePermission>());
|
||
ConfigureMenuDefinition(modelBuilder.Entity<MenuDefinition>());
|
||
modelBuilder.AddOutboxMessageEntity();
|
||
modelBuilder.AddOutboxStateEntity();
|
||
ApplyTenantQueryFilters(modelBuilder);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 配置管理后台用户实体。
|
||
/// </summary>
|
||
/// <param name="builder">实体构建器。</param>
|
||
private static void ConfigureIdentityUser(EntityTypeBuilder<IdentityUser> builder)
|
||
{
|
||
builder.ToTable("identity_users");
|
||
builder.HasKey(x => x.Id);
|
||
builder.Property(x => x.Account).HasMaxLength(64).IsRequired();
|
||
builder.Property(x => x.DisplayName).HasMaxLength(64).IsRequired();
|
||
builder.Property(x => x.PasswordHash).HasMaxLength(256).IsRequired();
|
||
builder.Property(x => x.Phone).HasMaxLength(32);
|
||
builder.Property(x => x.Email).HasMaxLength(128);
|
||
builder.Property(x => x.Status).HasConversion<int>();
|
||
builder.Property(x => x.FailedLoginCount).IsRequired();
|
||
builder.Property(x => x.LockedUntil);
|
||
builder.Property(x => x.LastLoginAt);
|
||
builder.Property(x => x.MustChangePassword).IsRequired();
|
||
builder.Property(x => x.Avatar).HasColumnType("text");
|
||
builder.Property(x => x.RowVersion)
|
||
.IsRowVersion()
|
||
.IsConcurrencyToken()
|
||
.HasColumnType("bytea");
|
||
builder.Property(x => x.TenantId).IsRequired();
|
||
ConfigureAuditableEntity(builder);
|
||
ConfigureSoftDeleteEntity(builder);
|
||
|
||
builder.HasIndex(x => x.TenantId);
|
||
builder.HasIndex(x => new { x.TenantId, x.Account }).IsUnique();
|
||
builder.HasIndex(x => new { x.TenantId, x.Phone })
|
||
.IsUnique()
|
||
.HasFilter("\"Phone\" IS NOT NULL");
|
||
builder.HasIndex(x => new { x.TenantId, x.Email })
|
||
.IsUnique()
|
||
.HasFilter("\"Email\" IS NOT NULL");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 配置小程序用户实体。
|
||
/// </summary>
|
||
/// <param name="builder">实体构建器。</param>
|
||
private static void ConfigureMiniUser(EntityTypeBuilder<MiniUser> builder)
|
||
{
|
||
builder.ToTable("mini_users");
|
||
builder.HasKey(x => x.Id);
|
||
builder.Property(x => x.TenantId).IsRequired();
|
||
builder.Property(x => x.OpenId).HasMaxLength(128).IsRequired();
|
||
builder.Property(x => x.UnionId).HasMaxLength(128);
|
||
builder.Property(x => x.Nickname).HasMaxLength(64).IsRequired();
|
||
builder.Property(x => x.Avatar).HasColumnType("text");
|
||
ConfigureAuditableEntity(builder);
|
||
ConfigureSoftDeleteEntity(builder);
|
||
|
||
builder.HasIndex(x => x.TenantId);
|
||
builder.HasIndex(x => new { x.TenantId, x.OpenId }).IsUnique();
|
||
}
|
||
|
||
private static void ConfigureRole(EntityTypeBuilder<Role> builder)
|
||
{
|
||
builder.ToTable("roles");
|
||
builder.HasKey(x => x.Id);
|
||
builder.Property(x => x.TenantId).IsRequired();
|
||
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
|
||
builder.Property(x => x.Code).HasMaxLength(64).IsRequired();
|
||
builder.Property(x => x.Description).HasMaxLength(256);
|
||
ConfigureAuditableEntity(builder);
|
||
ConfigureSoftDeleteEntity(builder);
|
||
builder.HasIndex(x => x.TenantId);
|
||
builder.HasIndex(x => new { x.TenantId, x.Code }).IsUnique();
|
||
}
|
||
|
||
private static void ConfigurePermission(EntityTypeBuilder<Permission> builder)
|
||
{
|
||
builder.ToTable("permissions");
|
||
builder.HasKey(x => x.Id);
|
||
builder.Property(x => x.TenantId).IsRequired();
|
||
builder.Property(x => x.ParentId).IsRequired();
|
||
builder.Property(x => x.SortOrder).IsRequired();
|
||
builder.Property(x => x.Type).HasMaxLength(16).IsRequired();
|
||
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
|
||
builder.Property(x => x.Code).HasMaxLength(128).IsRequired();
|
||
builder.Property(x => x.Description).HasMaxLength(256);
|
||
ConfigureAuditableEntity(builder);
|
||
ConfigureSoftDeleteEntity(builder);
|
||
builder.HasIndex(x => x.TenantId);
|
||
builder.HasIndex(x => new { x.TenantId, x.ParentId, x.SortOrder });
|
||
builder.HasIndex(x => new { x.TenantId, x.Code }).IsUnique();
|
||
}
|
||
|
||
private static void ConfigureRoleTemplate(EntityTypeBuilder<RoleTemplate> builder)
|
||
{
|
||
builder.ToTable("role_templates");
|
||
builder.HasKey(x => x.Id);
|
||
builder.Property(x => x.TemplateCode).HasMaxLength(64).IsRequired();
|
||
builder.Property(x => x.Name).HasMaxLength(128).IsRequired();
|
||
builder.Property(x => x.Description).HasMaxLength(256);
|
||
builder.Property(x => x.IsActive).IsRequired();
|
||
ConfigureAuditableEntity(builder);
|
||
builder.HasIndex(x => x.TemplateCode).IsUnique();
|
||
}
|
||
|
||
private static void ConfigureRoleTemplatePermission(EntityTypeBuilder<RoleTemplatePermission> builder)
|
||
{
|
||
builder.ToTable("role_template_permissions");
|
||
builder.HasKey(x => x.Id);
|
||
builder.Property(x => x.RoleTemplateId).IsRequired();
|
||
builder.Property(x => x.PermissionCode).HasMaxLength(128).IsRequired();
|
||
ConfigureAuditableEntity(builder);
|
||
builder.HasIndex(x => new { x.RoleTemplateId, x.PermissionCode }).IsUnique();
|
||
}
|
||
|
||
private static void ConfigureUserRole(EntityTypeBuilder<UserRole> builder)
|
||
{
|
||
builder.ToTable("user_roles");
|
||
builder.HasKey(x => x.Id);
|
||
builder.Property(x => x.TenantId).IsRequired();
|
||
builder.Property(x => x.UserId).IsRequired();
|
||
builder.Property(x => x.RoleId).IsRequired();
|
||
ConfigureAuditableEntity(builder);
|
||
ConfigureSoftDeleteEntity(builder);
|
||
builder.HasIndex(x => x.TenantId);
|
||
builder.HasIndex(x => new { x.TenantId, x.UserId, x.RoleId }).IsUnique();
|
||
}
|
||
|
||
private static void ConfigureRolePermission(EntityTypeBuilder<RolePermission> builder)
|
||
{
|
||
builder.ToTable("role_permissions");
|
||
builder.HasKey(x => x.Id);
|
||
builder.Property(x => x.TenantId).IsRequired();
|
||
builder.Property(x => x.RoleId).IsRequired();
|
||
builder.Property(x => x.PermissionId).IsRequired();
|
||
ConfigureAuditableEntity(builder);
|
||
ConfigureSoftDeleteEntity(builder);
|
||
builder.HasIndex(x => x.TenantId);
|
||
builder.HasIndex(x => new { x.TenantId, x.RoleId, x.PermissionId }).IsUnique();
|
||
}
|
||
|
||
private static void ConfigureMenuDefinition(EntityTypeBuilder<MenuDefinition> builder)
|
||
{
|
||
builder.ToTable("menu_definitions");
|
||
builder.HasKey(x => x.Id);
|
||
builder.Property(x => x.TenantId).IsRequired();
|
||
builder.Property(x => x.ParentId).IsRequired();
|
||
builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
|
||
builder.Property(x => x.Path).HasMaxLength(256).IsRequired();
|
||
builder.Property(x => x.Component).HasMaxLength(256).IsRequired();
|
||
builder.Property(x => x.Title).HasMaxLength(128).IsRequired();
|
||
builder.Property(x => x.Icon).HasMaxLength(64);
|
||
builder.Property(x => x.Link).HasMaxLength(512);
|
||
builder.Property(x => x.SortOrder).IsRequired();
|
||
builder.Property(x => x.RequiredPermissions).HasMaxLength(1024);
|
||
builder.Property(x => x.MetaPermissions).HasMaxLength(1024);
|
||
builder.Property(x => x.MetaRoles).HasMaxLength(1024);
|
||
builder.Property(x => x.AuthListJson).HasColumnType("text");
|
||
ConfigureAuditableEntity(builder);
|
||
ConfigureSoftDeleteEntity(builder);
|
||
builder.HasIndex(x => new { x.TenantId, x.ParentId, x.SortOrder });
|
||
}
|
||
}
|