fix: 修复公告过时警告并补齐用户权限

This commit is contained in:
2025-12-27 07:19:27 +08:00
parent caad541e2f
commit 04444c6554
16 changed files with 879 additions and 31 deletions

View File

@@ -58,8 +58,7 @@ public sealed class CreateTenantAnnouncementCommandHandler(
PublisherUserId = publisherUserId,
Status = AnnouncementStatus.Draft,
TargetType = request.TargetType.Trim(),
TargetParameters = request.TargetParameters,
IsActive = false
TargetParameters = request.TargetParameters
};
// 3. 持久化并返回 DTO

View File

@@ -42,7 +42,7 @@ public sealed class MarkAnnouncementAsReadCommandHandler(
// 2. 仅允许已发布且在有效期内的公告标记已读
var now = DateTime.UtcNow;
if (announcement.Status != AnnouncementStatus.Published || !announcement.IsActive)
if (announcement.Status != AnnouncementStatus.Published)
{
return null;
}

View File

@@ -56,7 +56,6 @@ public sealed class PublishAnnouncementCommandHandler(
announcement.Status = AnnouncementStatus.Published;
announcement.PublishedAt = DateTime.UtcNow;
announcement.RevokedAt = null;
announcement.IsActive = true;
announcement.RowVersion = request.RowVersion;
await announcementRepository.UpdateAsync(announcement, cancellationToken);

View File

@@ -50,7 +50,6 @@ public sealed class RevokeAnnouncementCommandHandler(
// 3. 撤销公告
announcement.Status = AnnouncementStatus.Revoked;
announcement.RevokedAt = DateTime.UtcNow;
announcement.IsActive = false;
announcement.RowVersion = request.RowVersion;
await announcementRepository.UpdateAsync(announcement, cancellationToken);

View File

@@ -49,7 +49,6 @@ public sealed class UpdateTenantAnnouncementCommandHandler(ITenantAnnouncementRe
announcement.Content = request.Content;
announcement.TargetType = string.IsNullOrWhiteSpace(request.TargetType) ? announcement.TargetType : request.TargetType.Trim();
announcement.TargetParameters = request.TargetParameters;
announcement.IsActive = false;
announcement.RowVersion = request.RowVersion;
// 4. 持久化

View File

@@ -1,5 +1,6 @@
using TakeoutSaaS.Application.App.Tenants.Dto;
using TakeoutSaaS.Domain.Tenants.Entities;
using TakeoutSaaS.Domain.Tenants.Enums;
namespace TakeoutSaaS.Application.App.Tenants;
@@ -193,7 +194,7 @@ internal static class TenantMapping
TargetType = announcement.TargetType,
TargetParameters = announcement.TargetParameters,
RowVersion = announcement.RowVersion,
IsActive = announcement.IsActive,
IsActive = announcement.Status == AnnouncementStatus.Published,
IsRead = isRead,
ReadAt = readAt
};

View File

@@ -774,14 +774,14 @@ public sealed class TakeoutAppDbContext(
builder.Property(x => x.TargetType).HasMaxLength(64).IsRequired();
builder.Property(x => x.TargetParameters).HasColumnType("text");
builder.Property(x => x.Priority).IsRequired();
builder.Property(x => x.IsActive).IsRequired();
builder.Property<bool>("IsActive").IsRequired();
builder.Property(x => x.RowVersion)
.IsRowVersion()
.IsConcurrencyToken()
.HasColumnType("bytea");
ConfigureAuditableEntity(builder);
ConfigureSoftDeleteEntity(builder);
builder.HasIndex(x => new { x.TenantId, x.AnnouncementType, x.IsActive });
builder.HasIndex("TenantId", "AnnouncementType", "IsActive");
builder.HasIndex(x => new { x.TenantId, x.EffectiveFrom, x.EffectiveTo });
builder.HasIndex(x => new { x.TenantId, x.Status, x.EffectiveFrom });
builder.HasIndex(x => new { x.Status, x.EffectiveFrom })

View File

@@ -50,7 +50,9 @@ public sealed class EfTenantAnnouncementRepository(TakeoutAppDbContext context)
if (isActive.HasValue)
{
query = query.Where(x => x.IsActive == isActive.Value);
query = isActive.Value
? query.Where(x => x.Status == AnnouncementStatus.Published)
: query.Where(x => x.Status != AnnouncementStatus.Published);
}
if (effectiveFrom.HasValue)
@@ -114,7 +116,9 @@ public sealed class EfTenantAnnouncementRepository(TakeoutAppDbContext context)
if (isActive.HasValue)
{
announcementQuery = announcementQuery.Where(x => x.IsActive == isActive.Value);
announcementQuery = isActive.Value
? announcementQuery.Where(x => x.Status == AnnouncementStatus.Published)
: announcementQuery.Where(x => x.Status != AnnouncementStatus.Published);
}
if (effectiveAt.HasValue)

View File

@@ -0,0 +1,726 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using TakeoutSaaS.Infrastructure.Identity.Persistence;
#nullable disable
namespace TakeoutSaaS.Infrastructure.Migrations.IdentityDb
{
[DbContext(typeof(IdentityDbContext))]
[Migration("20251226231440_GrantIdentityUserPermissionsToSuperAdmin")]
partial class GrantIdentityUserPermissionsToSuperAdmin
{
/// <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("TakeoutSaaS.Domain.Identity.Entities.IdentityUser", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasComment("实体唯一标识。");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Account")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasComment("登录账号。");
b.Property<string>("Avatar")
.HasColumnType("text")
.HasComment("头像地址。");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("创建时间UTC。");
b.Property<long?>("CreatedBy")
.HasColumnType("bigint")
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
b.Property<DateTime?>("DeletedAt")
.HasColumnType("timestamp with time zone")
.HasComment("软删除时间UTC未删除时为 null。");
b.Property<long?>("DeletedBy")
.HasColumnType("bigint")
.HasComment("删除人用户标识(软删除),未删除时为 null。");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasComment("展示名称。");
b.Property<string>("Email")
.HasMaxLength(128)
.HasColumnType("character varying(128)")
.HasComment("邮箱(租户内唯一)。");
b.Property<int>("FailedLoginCount")
.HasColumnType("integer")
.HasComment("登录失败次数。");
b.Property<DateTime?>("LastLoginAt")
.HasColumnType("timestamp with time zone")
.HasComment("最近登录时间UTC。");
b.Property<DateTime?>("LockedUntil")
.HasColumnType("timestamp with time zone")
.HasComment("锁定截止时间UTC。");
b.Property<long?>("MerchantId")
.HasColumnType("bigint")
.HasComment("所属商户(平台管理员为空)。");
b.Property<bool>("MustChangePassword")
.HasColumnType("boolean")
.HasComment("是否强制修改密码。");
b.Property<string>("PasswordHash")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("character varying(256)")
.HasComment("密码哈希。");
b.Property<string>("Phone")
.HasMaxLength(32)
.HasColumnType("character varying(32)")
.HasComment("手机号(租户内唯一)。");
b.Property<byte[]>("RowVersion")
.IsConcurrencyToken()
.IsRequired()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("bytea")
.HasComment("并发控制字段。");
b.Property<int>("Status")
.HasColumnType("integer")
.HasComment("账号状态。");
b.Property<long>("TenantId")
.HasColumnType("bigint")
.HasComment("所属租户 ID。");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("最近一次更新时间UTC从未更新时为 null。");
b.Property<long?>("UpdatedBy")
.HasColumnType("bigint")
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
b.HasKey("Id");
b.HasIndex("TenantId");
b.HasIndex("TenantId", "Account")
.IsUnique();
b.HasIndex("TenantId", "Email")
.IsUnique()
.HasFilter("\"Email\" IS NOT NULL");
b.HasIndex("TenantId", "Phone")
.IsUnique()
.HasFilter("\"Phone\" IS NOT NULL");
b.ToTable("identity_users", null, t =>
{
t.HasComment("管理后台账户实体(平台管理员、租户管理员或商户员工)。");
});
});
modelBuilder.Entity("TakeoutSaaS.Domain.Identity.Entities.MenuDefinition", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasComment("实体唯一标识。");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("AuthListJson")
.HasColumnType("text")
.HasComment("按钮权限列表 JSON存储 MenuAuthItemDto 数组)。");
b.Property<string>("Component")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("character varying(256)")
.HasComment("组件路径(不含 .vue。");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("创建时间UTC。");
b.Property<long?>("CreatedBy")
.HasColumnType("bigint")
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
b.Property<DateTime?>("DeletedAt")
.HasColumnType("timestamp with time zone")
.HasComment("软删除时间UTC未删除时为 null。");
b.Property<long?>("DeletedBy")
.HasColumnType("bigint")
.HasComment("删除人用户标识(软删除),未删除时为 null。");
b.Property<string>("Icon")
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasComment("图标标识。");
b.Property<bool>("IsIframe")
.HasColumnType("boolean")
.HasComment("是否 iframe。");
b.Property<bool>("KeepAlive")
.HasColumnType("boolean")
.HasComment("是否缓存。");
b.Property<string>("Link")
.HasMaxLength(512)
.HasColumnType("character varying(512)")
.HasComment("外链或 iframe 地址。");
b.Property<string>("MetaPermissions")
.IsRequired()
.HasMaxLength(1024)
.HasColumnType("character varying(1024)")
.HasComment("Meta.permissions逗号分隔。");
b.Property<string>("MetaRoles")
.IsRequired()
.HasMaxLength(1024)
.HasColumnType("character varying(1024)")
.HasComment("Meta.roles逗号分隔。");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasComment("菜单名称(前端路由 name。");
b.Property<long>("ParentId")
.HasColumnType("bigint")
.HasComment("父级菜单 ID根节点为 0。");
b.Property<string>("Path")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("character varying(256)")
.HasComment("路由路径。");
b.Property<string>("RequiredPermissions")
.IsRequired()
.HasMaxLength(1024)
.HasColumnType("character varying(1024)")
.HasComment("访问该菜单所需的权限集合(逗号分隔)。");
b.Property<int>("SortOrder")
.HasColumnType("integer")
.HasComment("排序。");
b.Property<long>("TenantId")
.HasColumnType("bigint")
.HasComment("所属租户 ID。");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)")
.HasComment("标题。");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("最近一次更新时间UTC从未更新时为 null。");
b.Property<long?>("UpdatedBy")
.HasColumnType("bigint")
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
b.HasKey("Id");
b.HasIndex("TenantId", "ParentId", "SortOrder");
b.ToTable("menu_definitions", null, t =>
{
t.HasComment("管理端菜单定义。");
});
});
modelBuilder.Entity("TakeoutSaaS.Domain.Identity.Entities.MiniUser", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasComment("实体唯一标识。");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Avatar")
.HasColumnType("text")
.HasComment("头像地址。");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("创建时间UTC。");
b.Property<long?>("CreatedBy")
.HasColumnType("bigint")
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
b.Property<DateTime?>("DeletedAt")
.HasColumnType("timestamp with time zone")
.HasComment("软删除时间UTC未删除时为 null。");
b.Property<long?>("DeletedBy")
.HasColumnType("bigint")
.HasComment("删除人用户标识(软删除),未删除时为 null。");
b.Property<string>("Nickname")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasComment("昵称。");
b.Property<string>("OpenId")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)")
.HasComment("微信 OpenId。");
b.Property<long>("TenantId")
.HasColumnType("bigint")
.HasComment("所属租户 ID。");
b.Property<string>("UnionId")
.HasMaxLength(128)
.HasColumnType("character varying(128)")
.HasComment("微信 UnionId可能为空。");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("最近一次更新时间UTC从未更新时为 null。");
b.Property<long?>("UpdatedBy")
.HasColumnType("bigint")
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
b.HasKey("Id");
b.HasIndex("TenantId");
b.HasIndex("TenantId", "OpenId")
.IsUnique();
b.ToTable("mini_users", null, t =>
{
t.HasComment("小程序用户实体。");
});
});
modelBuilder.Entity("TakeoutSaaS.Domain.Identity.Entities.Permission", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasComment("实体唯一标识。");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)")
.HasComment("权限编码(租户内唯一)。");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("创建时间UTC。");
b.Property<long?>("CreatedBy")
.HasColumnType("bigint")
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
b.Property<DateTime?>("DeletedAt")
.HasColumnType("timestamp with time zone")
.HasComment("软删除时间UTC未删除时为 null。");
b.Property<long?>("DeletedBy")
.HasColumnType("bigint")
.HasComment("删除人用户标识(软删除),未删除时为 null。");
b.Property<string>("Description")
.HasMaxLength(256)
.HasColumnType("character varying(256)")
.HasComment("描述。");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasComment("权限名称。");
b.Property<long>("ParentId")
.HasColumnType("bigint")
.HasComment("父级权限 ID根节点为 0。");
b.Property<int>("SortOrder")
.HasColumnType("integer")
.HasComment("排序值,值越小越靠前。");
b.Property<long>("TenantId")
.HasColumnType("bigint")
.HasComment("所属租户 ID。");
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(16)
.HasColumnType("character varying(16)")
.HasComment("权限类型group/leaf。");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("最近一次更新时间UTC从未更新时为 null。");
b.Property<long?>("UpdatedBy")
.HasColumnType("bigint")
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
b.HasKey("Id");
b.HasIndex("TenantId");
b.HasIndex("TenantId", "Code")
.IsUnique();
b.HasIndex("TenantId", "ParentId", "SortOrder");
b.ToTable("permissions", null, t =>
{
t.HasComment("权限定义。");
});
});
modelBuilder.Entity("TakeoutSaaS.Domain.Identity.Entities.Role", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasComment("实体唯一标识。");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasComment("角色编码(租户内唯一)。");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("创建时间UTC。");
b.Property<long?>("CreatedBy")
.HasColumnType("bigint")
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
b.Property<DateTime?>("DeletedAt")
.HasColumnType("timestamp with time zone")
.HasComment("软删除时间UTC未删除时为 null。");
b.Property<long?>("DeletedBy")
.HasColumnType("bigint")
.HasComment("删除人用户标识(软删除),未删除时为 null。");
b.Property<string>("Description")
.HasMaxLength(256)
.HasColumnType("character varying(256)")
.HasComment("描述。");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasComment("角色名称。");
b.Property<long>("TenantId")
.HasColumnType("bigint")
.HasComment("所属租户 ID。");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("最近一次更新时间UTC从未更新时为 null。");
b.Property<long?>("UpdatedBy")
.HasColumnType("bigint")
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
b.HasKey("Id");
b.HasIndex("TenantId");
b.HasIndex("TenantId", "Code")
.IsUnique();
b.ToTable("roles", null, t =>
{
t.HasComment("角色定义。");
});
});
modelBuilder.Entity("TakeoutSaaS.Domain.Identity.Entities.RolePermission", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasComment("实体唯一标识。");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("创建时间UTC。");
b.Property<long?>("CreatedBy")
.HasColumnType("bigint")
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
b.Property<DateTime?>("DeletedAt")
.HasColumnType("timestamp with time zone")
.HasComment("软删除时间UTC未删除时为 null。");
b.Property<long?>("DeletedBy")
.HasColumnType("bigint")
.HasComment("删除人用户标识(软删除),未删除时为 null。");
b.Property<long>("PermissionId")
.HasColumnType("bigint")
.HasComment("权限 ID。");
b.Property<long>("RoleId")
.HasColumnType("bigint")
.HasComment("角色 ID。");
b.Property<long>("TenantId")
.HasColumnType("bigint")
.HasComment("所属租户 ID。");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("最近一次更新时间UTC从未更新时为 null。");
b.Property<long?>("UpdatedBy")
.HasColumnType("bigint")
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
b.HasKey("Id");
b.HasIndex("TenantId");
b.HasIndex("TenantId", "RoleId", "PermissionId")
.IsUnique();
b.ToTable("role_permissions", null, t =>
{
t.HasComment("角色-权限关系。");
});
});
modelBuilder.Entity("TakeoutSaaS.Domain.Identity.Entities.RoleTemplate", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasComment("实体唯一标识。");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("创建时间UTC。");
b.Property<long?>("CreatedBy")
.HasColumnType("bigint")
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
b.Property<DateTime?>("DeletedAt")
.HasColumnType("timestamp with time zone")
.HasComment("软删除时间UTC未删除时为 null。");
b.Property<long?>("DeletedBy")
.HasColumnType("bigint")
.HasComment("删除人用户标识(软删除),未删除时为 null。");
b.Property<string>("Description")
.HasMaxLength(256)
.HasColumnType("character varying(256)")
.HasComment("模板描述。");
b.Property<bool>("IsActive")
.HasColumnType("boolean")
.HasComment("是否启用。");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)")
.HasComment("模板名称。");
b.Property<string>("TemplateCode")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasComment("模板编码(唯一)。");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("最近一次更新时间UTC从未更新时为 null。");
b.Property<long?>("UpdatedBy")
.HasColumnType("bigint")
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
b.HasKey("Id");
b.HasIndex("TemplateCode")
.IsUnique();
b.ToTable("role_templates", null, t =>
{
t.HasComment("角色模板定义(平台级)。");
});
});
modelBuilder.Entity("TakeoutSaaS.Domain.Identity.Entities.RoleTemplatePermission", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasComment("实体唯一标识。");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("创建时间UTC。");
b.Property<long?>("CreatedBy")
.HasColumnType("bigint")
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
b.Property<DateTime?>("DeletedAt")
.HasColumnType("timestamp with time zone")
.HasComment("软删除时间UTC未删除时为 null。");
b.Property<long?>("DeletedBy")
.HasColumnType("bigint")
.HasComment("删除人用户标识(软删除),未删除时为 null。");
b.Property<string>("PermissionCode")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)")
.HasComment("权限编码。");
b.Property<long>("RoleTemplateId")
.HasColumnType("bigint")
.HasComment("模板 ID。");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("最近一次更新时间UTC从未更新时为 null。");
b.Property<long?>("UpdatedBy")
.HasColumnType("bigint")
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
b.HasKey("Id");
b.HasIndex("RoleTemplateId", "PermissionCode")
.IsUnique();
b.ToTable("role_template_permissions", null, t =>
{
t.HasComment("角色模板-权限关系(平台级)。");
});
});
modelBuilder.Entity("TakeoutSaaS.Domain.Identity.Entities.UserRole", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasComment("实体唯一标识。");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("创建时间UTC。");
b.Property<long?>("CreatedBy")
.HasColumnType("bigint")
.HasComment("创建人用户标识,匿名或系统操作时为 null。");
b.Property<DateTime?>("DeletedAt")
.HasColumnType("timestamp with time zone")
.HasComment("软删除时间UTC未删除时为 null。");
b.Property<long?>("DeletedBy")
.HasColumnType("bigint")
.HasComment("删除人用户标识(软删除),未删除时为 null。");
b.Property<long>("RoleId")
.HasColumnType("bigint")
.HasComment("角色 ID。");
b.Property<long>("TenantId")
.HasColumnType("bigint")
.HasComment("所属租户 ID。");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone")
.HasComment("最近一次更新时间UTC从未更新时为 null。");
b.Property<long?>("UpdatedBy")
.HasColumnType("bigint")
.HasComment("最后更新人用户标识,匿名或系统操作时为 null。");
b.Property<long>("UserId")
.HasColumnType("bigint")
.HasComment("用户 ID。");
b.HasKey("Id");
b.HasIndex("TenantId");
b.HasIndex("TenantId", "UserId", "RoleId")
.IsUnique();
b.ToTable("user_roles", null, t =>
{
t.HasComment("用户-角色关系。");
});
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,135 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace TakeoutSaaS.Infrastructure.Migrations.IdentityDb
{
/// <inheritdoc />
public partial class GrantIdentityUserPermissionsToSuperAdmin : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(
@"WITH target_roles AS (
SELECT ""Id"" AS role_id, ""TenantId"" AS tenant_id
FROM ""roles""
WHERE ""Code"" IN ('super-admin', 'SUPER_ADMIN', 'PlatformAdmin', 'platform-admin')
AND ""DeletedAt"" IS NULL
),
target_permissions AS (
SELECT DISTINCT tr.tenant_id, pc.code
FROM target_roles tr
CROSS JOIN (VALUES
('identity:user:read'),
('identity:user:create'),
('identity:user:update'),
('identity:user:delete'),
('identity:user:status'),
('identity:user:reset-password'),
('identity:user:batch')
) AS pc(code)
)
INSERT INTO ""permissions"" (
""TenantId"",
""Name"",
""Code"",
""Description"",
""CreatedAt"",
""CreatedBy"",
""UpdatedAt"",
""UpdatedBy"",
""DeletedAt"",
""DeletedBy""
)
SELECT
tp.tenant_id,
tp.code,
tp.code,
CONCAT('Seed permission ', tp.code),
NOW(),
NULL,
NULL,
NULL,
NULL,
NULL
FROM target_permissions tp
ON CONFLICT (""TenantId"", ""Code"") DO NOTHING;"
);
migrationBuilder.Sql(
@"WITH target_roles AS (
SELECT ""Id"" AS role_id, ""TenantId"" AS tenant_id
FROM ""roles""
WHERE ""Code"" IN ('super-admin', 'SUPER_ADMIN', 'PlatformAdmin', 'platform-admin')
AND ""DeletedAt"" IS NULL
)
INSERT INTO ""role_permissions"" (
""TenantId"",
""RoleId"",
""PermissionId"",
""CreatedAt"",
""CreatedBy"",
""UpdatedAt"",
""UpdatedBy"",
""DeletedAt"",
""DeletedBy""
)
SELECT
tr.tenant_id,
tr.role_id,
p.""Id"",
NOW(),
NULL,
NULL,
NULL,
NULL,
NULL
FROM target_roles tr
JOIN ""permissions"" p
ON p.""TenantId"" = tr.tenant_id
AND p.""Code"" IN (
'identity:user:read',
'identity:user:create',
'identity:user:update',
'identity:user:delete',
'identity:user:status',
'identity:user:reset-password',
'identity:user:batch'
)
WHERE p.""DeletedAt"" IS NULL
ON CONFLICT (""TenantId"", ""RoleId"", ""PermissionId"") DO NOTHING;"
);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(
@"WITH target_roles AS (
SELECT ""Id"" AS role_id, ""TenantId"" AS tenant_id
FROM ""roles""
WHERE ""Code"" IN ('super-admin', 'SUPER_ADMIN', 'PlatformAdmin', 'platform-admin')
AND ""DeletedAt"" IS NULL
),
target_permissions AS (
SELECT ""Id"" AS permission_id, ""TenantId"" AS tenant_id
FROM ""permissions""
WHERE ""Code"" IN (
'identity:user:read',
'identity:user:create',
'identity:user:update',
'identity:user:delete',
'identity:user:status',
'identity:user:reset-password',
'identity:user:batch'
)
)
DELETE FROM ""role_permissions"" rp
USING target_roles tr, target_permissions tp
WHERE rp.""TenantId"" = tr.tenant_id
AND rp.""RoleId"" = tr.role_id
AND rp.""PermissionId"" = tp.permission_id;"
);
}
}
}