fix: 权限固定为全局
This commit is contained in:
@@ -69,8 +69,8 @@ public sealed class PermissionsController(IMediator mediator) : BaseApiControlle
|
||||
[ProducesResponseType(typeof(ApiResponse<PermissionDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<PermissionDto>> Create([FromBody, Required] CreatePermissionCommand command, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
return ApiResponse<PermissionDto>.Ok(result);
|
||||
// 1. 权限已固定,禁止新增
|
||||
return await Task.FromResult(ApiResponse<PermissionDto>.Error(StatusCodes.Status403Forbidden, "权限已固定,禁止新增"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -86,11 +86,8 @@ public sealed class PermissionsController(IMediator mediator) : BaseApiControlle
|
||||
[ProducesResponseType(typeof(ApiResponse<PermissionDto>), StatusCodes.Status404NotFound)]
|
||||
public async Task<ApiResponse<PermissionDto>> Update(long permissionId, [FromBody, Required] UpdatePermissionCommand command, CancellationToken cancellationToken)
|
||||
{
|
||||
command = command with { PermissionId = permissionId };
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
return result is null
|
||||
? ApiResponse<PermissionDto>.Error(StatusCodes.Status404NotFound, "权限不存在")
|
||||
: ApiResponse<PermissionDto>.Ok(result);
|
||||
// 1. 权限已固定,禁止修改
|
||||
return await Task.FromResult(ApiResponse<PermissionDto>.Error(StatusCodes.Status403Forbidden, "权限已固定,禁止修改"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -104,8 +101,7 @@ public sealed class PermissionsController(IMediator mediator) : BaseApiControlle
|
||||
[ProducesResponseType(typeof(ApiResponse<bool>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<bool>> Delete(long permissionId, CancellationToken cancellationToken)
|
||||
{
|
||||
var command = new DeletePermissionCommand { PermissionId = permissionId };
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
return ApiResponse<bool>.Ok(result);
|
||||
// 1. 权限已固定,禁止删除
|
||||
return await Task.FromResult(ApiResponse<bool>.Error(StatusCodes.Status403Forbidden, "权限已固定,禁止删除"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public sealed class PermissionDto
|
||||
public long Id { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 租户 ID。
|
||||
/// 租户 ID(固定权限时为基准租户)。
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(SnowflakeIdJsonConverter))]
|
||||
public long TenantId { get; init; }
|
||||
@@ -42,7 +42,7 @@ public sealed class PermissionDto
|
||||
public string Name { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 权限编码(租户内唯一)。
|
||||
/// 权限编码(全局唯一)。
|
||||
/// </summary>
|
||||
public string Code { get; init; } = string.Empty;
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ using TakeoutSaaS.Application.Identity.Commands;
|
||||
using TakeoutSaaS.Application.Identity.Contracts;
|
||||
using TakeoutSaaS.Domain.Identity.Entities;
|
||||
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.Identity.Handlers;
|
||||
@@ -23,10 +25,16 @@ public sealed class CreatePermissionCommandHandler(
|
||||
/// <returns>创建后的权限 DTO。</returns>
|
||||
public async Task<PermissionDto> Handle(CreatePermissionCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 获取租户上下文
|
||||
// 1. 权限固定时禁止新增
|
||||
if (!PermissionPolicy.CanMaintainPermissions)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.Forbidden, "权限已固定,禁止新增");
|
||||
}
|
||||
|
||||
// 2. 获取租户上下文
|
||||
var tenantId = tenantProvider.GetCurrentTenantId();
|
||||
|
||||
// 2. 构建权限实体
|
||||
// 3. 构建权限实体
|
||||
var normalizedType = string.IsNullOrWhiteSpace(request.Type)
|
||||
? "leaf"
|
||||
: request.Type.Trim().ToLowerInvariant();
|
||||
@@ -44,11 +52,11 @@ public sealed class CreatePermissionCommandHandler(
|
||||
Description = request.Description
|
||||
};
|
||||
|
||||
// 3. 持久化
|
||||
// 4. 持久化
|
||||
await permissionRepository.AddAsync(permission, cancellationToken);
|
||||
await permissionRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 4. 返回 DTO
|
||||
// 5. 返回 DTO
|
||||
return new PermissionDto
|
||||
{
|
||||
Id = permission.Id,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using MediatR;
|
||||
using TakeoutSaaS.Application.Identity.Commands;
|
||||
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.Identity.Handlers;
|
||||
@@ -21,14 +23,20 @@ public sealed class DeletePermissionCommandHandler(
|
||||
/// <returns>执行结果。</returns>
|
||||
public async Task<bool> Handle(DeletePermissionCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 获取租户上下文
|
||||
// 1. 权限固定时禁止删除
|
||||
if (!PermissionPolicy.CanMaintainPermissions)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.Forbidden, "权限已固定,禁止删除");
|
||||
}
|
||||
|
||||
// 2. 获取租户上下文
|
||||
var tenantId = tenantProvider.GetCurrentTenantId();
|
||||
|
||||
// 2. 删除权限
|
||||
// 3. 删除权限
|
||||
await permissionRepository.DeleteAsync(request.PermissionId, tenantId, cancellationToken);
|
||||
await permissionRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 3. 返回执行结果
|
||||
// 4. 返回执行结果
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ using MediatR;
|
||||
using TakeoutSaaS.Application.Identity.Commands;
|
||||
using TakeoutSaaS.Application.Identity.Contracts;
|
||||
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.Identity.Handlers;
|
||||
@@ -22,7 +24,13 @@ public sealed class UpdatePermissionCommandHandler(
|
||||
/// <returns>更新后的权限 DTO 或 null。</returns>
|
||||
public async Task<PermissionDto?> Handle(UpdatePermissionCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 获取租户上下文并查询权限
|
||||
// 1. 权限固定时禁止修改
|
||||
if (!PermissionPolicy.CanMaintainPermissions)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.Forbidden, "权限已固定,禁止修改");
|
||||
}
|
||||
|
||||
// 2. 获取租户上下文并查询权限
|
||||
var tenantId = tenantProvider.GetCurrentTenantId();
|
||||
var permission = await permissionRepository.FindByIdAsync(request.PermissionId, tenantId, cancellationToken);
|
||||
if (permission == null)
|
||||
@@ -30,7 +38,7 @@ public sealed class UpdatePermissionCommandHandler(
|
||||
return null;
|
||||
}
|
||||
|
||||
// 2. 更新字段
|
||||
// 3. 更新字段
|
||||
var normalizedType = string.IsNullOrWhiteSpace(request.Type)
|
||||
? "leaf"
|
||||
: request.Type.Trim().ToLowerInvariant();
|
||||
@@ -45,11 +53,11 @@ public sealed class UpdatePermissionCommandHandler(
|
||||
permission.Name = request.Name;
|
||||
permission.Description = request.Description;
|
||||
|
||||
// 3. 持久化
|
||||
// 4. 持久化
|
||||
await permissionRepository.UpdateAsync(permission, cancellationToken);
|
||||
await permissionRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 4. 返回 DTO
|
||||
// 5. 返回 DTO
|
||||
return new PermissionDto
|
||||
{
|
||||
Id = permission.Id,
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace TakeoutSaaS.Application.Identity;
|
||||
|
||||
/// <summary>
|
||||
/// 权限管理策略。
|
||||
/// </summary>
|
||||
public static class PermissionPolicy
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否允许维护权限定义(固定权限时为 false)。
|
||||
/// </summary>
|
||||
public static bool CanMaintainPermissions => false;
|
||||
}
|
||||
@@ -28,7 +28,7 @@ public sealed class Permission : MultiTenantEntityBase
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 权限编码(租户内唯一)。
|
||||
/// 权限编码(全局唯一)。
|
||||
/// </summary>
|
||||
public string Code { get; set; } = string.Empty;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ public sealed class EfPermissionRepository(IdentityDbContext dbContext) : IPermi
|
||||
=> dbContext.Permissions
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(x => x.Id == permissionId && x.TenantId == tenantId && x.DeletedAt == null, cancellationToken);
|
||||
.FirstOrDefaultAsync(x => x.Id == permissionId && x.DeletedAt == null, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// 根据权限编码获取权限。
|
||||
@@ -33,7 +33,7 @@ public sealed class EfPermissionRepository(IdentityDbContext dbContext) : IPermi
|
||||
=> dbContext.Permissions
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(x => x.Code == code && x.TenantId == tenantId && x.DeletedAt == null, cancellationToken);
|
||||
.FirstOrDefaultAsync(x => x.Code == code && x.DeletedAt == null, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// 根据权限编码集合批量获取权限。
|
||||
@@ -51,11 +51,11 @@ public sealed class EfPermissionRepository(IdentityDbContext dbContext) : IPermi
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
|
||||
// 2. 按租户筛选权限
|
||||
// 2. 读取全局权限(已固定)
|
||||
return dbContext.Permissions
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.DeletedAt == null && normalizedCodes.Contains(x.Code))
|
||||
.Where(x => x.DeletedAt == null && normalizedCodes.Contains(x.Code))
|
||||
.ToListAsync(cancellationToken)
|
||||
.ContinueWith(t => (IReadOnlyList<Permission>)t.Result, cancellationToken);
|
||||
}
|
||||
@@ -71,7 +71,7 @@ public sealed class EfPermissionRepository(IdentityDbContext dbContext) : IPermi
|
||||
=> dbContext.Permissions
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.DeletedAt == null && permissionIds.Contains(x.Id))
|
||||
.Where(x => x.DeletedAt == null && permissionIds.Contains(x.Id))
|
||||
.ToListAsync(cancellationToken)
|
||||
.ContinueWith(t => (IReadOnlyList<Permission>)t.Result, cancellationToken);
|
||||
|
||||
@@ -88,7 +88,7 @@ public sealed class EfPermissionRepository(IdentityDbContext dbContext) : IPermi
|
||||
var query = dbContext.Permissions
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.DeletedAt == null);
|
||||
.Where(x => x.DeletedAt == null);
|
||||
if (!string.IsNullOrWhiteSpace(keyword))
|
||||
{
|
||||
// 2. 追加关键字过滤
|
||||
@@ -139,7 +139,7 @@ public sealed class EfPermissionRepository(IdentityDbContext dbContext) : IPermi
|
||||
public async Task DeleteAsync(long permissionId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. 查询目标权限
|
||||
var entity = await dbContext.Permissions.FirstOrDefaultAsync(x => x.Id == permissionId && x.TenantId == tenantId, cancellationToken);
|
||||
var entity = await dbContext.Permissions.FirstOrDefaultAsync(x => x.Id == permissionId, cancellationToken);
|
||||
if (entity != null)
|
||||
{
|
||||
// 2. 删除实体
|
||||
|
||||
@@ -112,25 +112,21 @@ public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger
|
||||
});
|
||||
}
|
||||
|
||||
// 6.6 确保权限存在
|
||||
// 6.6 读取全局权限定义(固定权限,不再按租户生成)
|
||||
var existingPermissions = await context.Permissions
|
||||
.Where(p => p.TenantId == userOptions.TenantId && permissions.Contains(p.Code))
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
.Where(p => permissions.Contains(p.Code))
|
||||
.ToListAsync(cancellationToken);
|
||||
var existingPermissionCodes = existingPermissions.Select(p => p.Code).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var code in permissions)
|
||||
var existingPermissionCodes = existingPermissions
|
||||
.Select(p => p.Code)
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var missingPermissionCodes = permissions
|
||||
.Where(code => !existingPermissionCodes.Contains(code))
|
||||
.ToArray();
|
||||
if (missingPermissionCodes.Length > 0)
|
||||
{
|
||||
if (existingPermissionCodes.Contains(code))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
context.Permissions.Add(new DomainPermission
|
||||
{
|
||||
TenantId = userOptions.TenantId,
|
||||
Code = code,
|
||||
Name = code,
|
||||
Description = $"Seed permission {code}"
|
||||
});
|
||||
logger.LogWarning("发现未配置的全局权限编码,已忽略:{Codes}", string.Join(", ", missingPermissionCodes));
|
||||
}
|
||||
|
||||
// 6.7 保存基础角色/权限
|
||||
@@ -140,9 +136,7 @@ public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger
|
||||
var roleEntities = await context.Roles
|
||||
.Where(r => r.TenantId == userOptions.TenantId && roles.Contains(r.Code))
|
||||
.ToListAsync(cancellationToken);
|
||||
var permissionEntities = await context.Permissions
|
||||
.Where(p => p.TenantId == userOptions.TenantId && permissions.Contains(p.Code))
|
||||
.ToListAsync(cancellationToken);
|
||||
var permissionEntities = existingPermissions;
|
||||
|
||||
// 6.9 重置用户角色
|
||||
var existingUserRoles = await context.UserRoles
|
||||
|
||||
Reference in New Issue
Block a user