fix: allow super admin tenant-scoped role ops
This commit is contained in:
@@ -31,15 +31,24 @@ public sealed class TenantRolesController(IMediator mediator, ITenantProvider te
|
|||||||
[FromQuery] SearchRolesQuery query,
|
[FromQuery] SearchRolesQuery query,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 1. 校验路由租户与上下文一致
|
// 1. 校验路由租户与上下文一致(超管 tenantId=0 放行)
|
||||||
var currentTenantId = tenantProvider.GetCurrentTenantId();
|
var currentTenantId = tenantProvider.GetCurrentTenantId();
|
||||||
if (tenantId != currentTenantId)
|
if (currentTenantId != 0 && tenantId != currentTenantId)
|
||||||
{
|
{
|
||||||
return ApiResponse<PagedResult<RoleDto>>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
|
return ApiResponse<PagedResult<RoleDto>>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 查询角色分页
|
// 2. 绑定租户并查询角色分页
|
||||||
var result = await mediator.Send(query, cancellationToken);
|
var request = new SearchRolesQuery
|
||||||
|
{
|
||||||
|
TenantId = tenantId,
|
||||||
|
Keyword = query.Keyword,
|
||||||
|
Page = query.Page,
|
||||||
|
PageSize = query.PageSize,
|
||||||
|
SortBy = query.SortBy,
|
||||||
|
SortDescending = query.SortDescending
|
||||||
|
};
|
||||||
|
var result = await mediator.Send(request, cancellationToken);
|
||||||
|
|
||||||
// 3. 返回分页数据
|
// 3. 返回分页数据
|
||||||
return ApiResponse<PagedResult<RoleDto>>.Ok(result);
|
return ApiResponse<PagedResult<RoleDto>>.Ok(result);
|
||||||
@@ -54,15 +63,15 @@ public sealed class TenantRolesController(IMediator mediator, ITenantProvider te
|
|||||||
[ProducesResponseType(typeof(ApiResponse<RoleDetailDto>), StatusCodes.Status404NotFound)]
|
[ProducesResponseType(typeof(ApiResponse<RoleDetailDto>), StatusCodes.Status404NotFound)]
|
||||||
public async Task<ApiResponse<RoleDetailDto>> Detail(long tenantId, long roleId, CancellationToken cancellationToken)
|
public async Task<ApiResponse<RoleDetailDto>> Detail(long tenantId, long roleId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 1. 校验租户上下文
|
// 1. 校验租户上下文(超管 tenantId=0 放行)
|
||||||
var currentTenantId = tenantProvider.GetCurrentTenantId();
|
var currentTenantId = tenantProvider.GetCurrentTenantId();
|
||||||
if (tenantId != currentTenantId)
|
if (currentTenantId != 0 && tenantId != currentTenantId)
|
||||||
{
|
{
|
||||||
return ApiResponse<RoleDetailDto>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
|
return ApiResponse<RoleDetailDto>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 查询角色详情
|
// 2. 查询角色详情
|
||||||
var result = await mediator.Send(new RoleDetailQuery { RoleId = roleId }, cancellationToken);
|
var result = await mediator.Send(new RoleDetailQuery { RoleId = roleId, TenantId = tenantId }, cancellationToken);
|
||||||
|
|
||||||
// 3. 返回数据或 404
|
// 3. 返回数据或 404
|
||||||
return result is null
|
return result is null
|
||||||
@@ -81,15 +90,15 @@ public sealed class TenantRolesController(IMediator mediator, ITenantProvider te
|
|||||||
[FromBody, Required] CreateRoleCommand command,
|
[FromBody, Required] CreateRoleCommand command,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 1. 校验租户上下文
|
// 1. 校验租户上下文(超管 tenantId=0 放行)
|
||||||
var currentTenantId = tenantProvider.GetCurrentTenantId();
|
var currentTenantId = tenantProvider.GetCurrentTenantId();
|
||||||
if (tenantId != currentTenantId)
|
if (currentTenantId != 0 && tenantId != currentTenantId)
|
||||||
{
|
{
|
||||||
return ApiResponse<RoleDto>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
|
return ApiResponse<RoleDto>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 创建角色
|
// 2. 创建角色
|
||||||
var result = await mediator.Send(command, cancellationToken);
|
var result = await mediator.Send(command with { TenantId = tenantId }, cancellationToken);
|
||||||
|
|
||||||
// 3. 返回创建结果
|
// 3. 返回创建结果
|
||||||
return ApiResponse<RoleDto>.Ok(result);
|
return ApiResponse<RoleDto>.Ok(result);
|
||||||
@@ -108,15 +117,15 @@ public sealed class TenantRolesController(IMediator mediator, ITenantProvider te
|
|||||||
[FromBody, Required] UpdateRoleCommand command,
|
[FromBody, Required] UpdateRoleCommand command,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 1. 校验租户上下文
|
// 1. 校验租户上下文(超管 tenantId=0 放行)
|
||||||
var currentTenantId = tenantProvider.GetCurrentTenantId();
|
var currentTenantId = tenantProvider.GetCurrentTenantId();
|
||||||
if (tenantId != currentTenantId)
|
if (currentTenantId != 0 && tenantId != currentTenantId)
|
||||||
{
|
{
|
||||||
return ApiResponse<RoleDto>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
|
return ApiResponse<RoleDto>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 绑定角色 ID
|
// 2. 绑定角色 ID
|
||||||
command = command with { RoleId = roleId };
|
command = command with { RoleId = roleId, TenantId = tenantId };
|
||||||
|
|
||||||
// 3. 执行更新
|
// 3. 执行更新
|
||||||
var result = await mediator.Send(command, cancellationToken);
|
var result = await mediator.Send(command, cancellationToken);
|
||||||
@@ -135,15 +144,15 @@ public sealed class TenantRolesController(IMediator mediator, ITenantProvider te
|
|||||||
[ProducesResponseType(typeof(ApiResponse<bool>), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(ApiResponse<bool>), StatusCodes.Status200OK)]
|
||||||
public async Task<ApiResponse<bool>> Delete(long tenantId, long roleId, CancellationToken cancellationToken)
|
public async Task<ApiResponse<bool>> Delete(long tenantId, long roleId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 1. 校验租户上下文
|
// 1. 校验租户上下文(超管 tenantId=0 放行)
|
||||||
var currentTenantId = tenantProvider.GetCurrentTenantId();
|
var currentTenantId = tenantProvider.GetCurrentTenantId();
|
||||||
if (tenantId != currentTenantId)
|
if (currentTenantId != 0 && tenantId != currentTenantId)
|
||||||
{
|
{
|
||||||
return ApiResponse<bool>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
|
return ApiResponse<bool>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 执行删除
|
// 2. 执行删除
|
||||||
var command = new DeleteRoleCommand { RoleId = roleId };
|
var command = new DeleteRoleCommand { RoleId = roleId, TenantId = tenantId };
|
||||||
var result = await mediator.Send(command, cancellationToken);
|
var result = await mediator.Send(command, cancellationToken);
|
||||||
|
|
||||||
// 3. 返回结果
|
// 3. 返回结果
|
||||||
@@ -162,15 +171,15 @@ public sealed class TenantRolesController(IMediator mediator, ITenantProvider te
|
|||||||
long roleId,
|
long roleId,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 1. 校验租户上下文
|
// 1. 校验租户上下文(超管 tenantId=0 放行)
|
||||||
var currentTenantId = tenantProvider.GetCurrentTenantId();
|
var currentTenantId = tenantProvider.GetCurrentTenantId();
|
||||||
if (tenantId != currentTenantId)
|
if (currentTenantId != 0 && tenantId != currentTenantId)
|
||||||
{
|
{
|
||||||
return ApiResponse<IReadOnlyList<PermissionDto>>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
|
return ApiResponse<IReadOnlyList<PermissionDto>>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 查询角色详情并提取权限
|
// 2. 查询角色详情并提取权限
|
||||||
var detail = await mediator.Send(new RoleDetailQuery { RoleId = roleId }, cancellationToken);
|
var detail = await mediator.Send(new RoleDetailQuery { RoleId = roleId, TenantId = tenantId }, cancellationToken);
|
||||||
if (detail is null)
|
if (detail is null)
|
||||||
{
|
{
|
||||||
return ApiResponse<IReadOnlyList<PermissionDto>>.Error(StatusCodes.Status404NotFound, "角色不存在");
|
return ApiResponse<IReadOnlyList<PermissionDto>>.Error(StatusCodes.Status404NotFound, "角色不存在");
|
||||||
@@ -192,15 +201,15 @@ public sealed class TenantRolesController(IMediator mediator, ITenantProvider te
|
|||||||
[FromBody, Required] BindRolePermissionsCommand command,
|
[FromBody, Required] BindRolePermissionsCommand command,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 1. 校验租户上下文
|
// 1. 校验租户上下文(超管 tenantId=0 放行)
|
||||||
var currentTenantId = tenantProvider.GetCurrentTenantId();
|
var currentTenantId = tenantProvider.GetCurrentTenantId();
|
||||||
if (tenantId != currentTenantId)
|
if (currentTenantId != 0 && tenantId != currentTenantId)
|
||||||
{
|
{
|
||||||
return ApiResponse<bool>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
|
return ApiResponse<bool>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 绑定角色 ID
|
// 2. 绑定角色 ID
|
||||||
command = command with { RoleId = roleId };
|
command = command with { RoleId = roleId, TenantId = tenantId };
|
||||||
|
|
||||||
// 3. 覆盖授权
|
// 3. 覆盖授权
|
||||||
var result = await mediator.Send(command, cancellationToken);
|
var result = await mediator.Send(command, cancellationToken);
|
||||||
|
|||||||
@@ -8,5 +8,6 @@ namespace TakeoutSaaS.Application.Identity.Commands;
|
|||||||
public sealed record BindRolePermissionsCommand : IRequest<bool>
|
public sealed record BindRolePermissionsCommand : IRequest<bool>
|
||||||
{
|
{
|
||||||
public long RoleId { get; init; }
|
public long RoleId { get; init; }
|
||||||
|
public long? TenantId { get; init; }
|
||||||
public long[] PermissionIds { get; init; } = Array.Empty<long>();
|
public long[] PermissionIds { get; init; } = Array.Empty<long>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ namespace TakeoutSaaS.Application.Identity.Commands;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed record CreateRoleCommand : IRequest<RoleDto>
|
public sealed record CreateRoleCommand : IRequest<RoleDto>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 租户 ID(空则取当前上下文)。
|
||||||
|
/// </summary>
|
||||||
|
public long? TenantId { get; init; }
|
||||||
|
|
||||||
public string Name { get; init; } = string.Empty;
|
public string Name { get; init; } = string.Empty;
|
||||||
public string Code { get; init; } = string.Empty;
|
public string Code { get; init; } = string.Empty;
|
||||||
public string? Description { get; init; }
|
public string? Description { get; init; }
|
||||||
|
|||||||
@@ -8,4 +8,9 @@ namespace TakeoutSaaS.Application.Identity.Commands;
|
|||||||
public sealed record DeleteRoleCommand : IRequest<bool>
|
public sealed record DeleteRoleCommand : IRequest<bool>
|
||||||
{
|
{
|
||||||
public long RoleId { get; init; }
|
public long RoleId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 租户 ID(空则取当前上下文)。
|
||||||
|
/// </summary>
|
||||||
|
public long? TenantId { get; init; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ namespace TakeoutSaaS.Application.Identity.Commands;
|
|||||||
public sealed record UpdateRoleCommand : IRequest<RoleDto?>
|
public sealed record UpdateRoleCommand : IRequest<RoleDto?>
|
||||||
{
|
{
|
||||||
public long RoleId { get; init; }
|
public long RoleId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 租户 ID(空则取当前上下文)。
|
||||||
|
/// </summary>
|
||||||
|
public long? TenantId { get; init; }
|
||||||
|
|
||||||
public string Name { get; init; } = string.Empty;
|
public string Name { get; init; } = string.Empty;
|
||||||
public string? Description { get; init; }
|
public string? Description { get; init; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public sealed class BindRolePermissionsCommandHandler(
|
|||||||
public async Task<bool> Handle(BindRolePermissionsCommand request, CancellationToken cancellationToken)
|
public async Task<bool> Handle(BindRolePermissionsCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 1. 获取租户上下文
|
// 1. 获取租户上下文
|
||||||
var tenantId = tenantProvider.GetCurrentTenantId();
|
var tenantId = request.TenantId ?? tenantProvider.GetCurrentTenantId();
|
||||||
|
|
||||||
// 2. 覆盖式绑定权限
|
// 2. 覆盖式绑定权限
|
||||||
await rolePermissionRepository.ReplaceRolePermissionsAsync(tenantId, request.RoleId, request.PermissionIds, cancellationToken);
|
await rolePermissionRepository.ReplaceRolePermissionsAsync(tenantId, request.RoleId, request.PermissionIds, cancellationToken);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public sealed class CreateRoleCommandHandler(
|
|||||||
public async Task<RoleDto> Handle(CreateRoleCommand request, CancellationToken cancellationToken)
|
public async Task<RoleDto> Handle(CreateRoleCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 1. 获取租户上下文
|
// 1. 获取租户上下文
|
||||||
var tenantId = tenantProvider.GetCurrentTenantId();
|
var tenantId = request.TenantId ?? tenantProvider.GetCurrentTenantId();
|
||||||
|
|
||||||
// 2. 构建角色实体
|
// 2. 构建角色实体
|
||||||
var role = new Role
|
var role = new Role
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public sealed class DeleteRoleCommandHandler(
|
|||||||
public async Task<bool> Handle(DeleteRoleCommand request, CancellationToken cancellationToken)
|
public async Task<bool> Handle(DeleteRoleCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 1. 获取租户上下文
|
// 1. 获取租户上下文
|
||||||
var tenantId = tenantProvider.GetCurrentTenantId();
|
var tenantId = request.TenantId ?? tenantProvider.GetCurrentTenantId();
|
||||||
|
|
||||||
// 2. 删除角色
|
// 2. 删除角色
|
||||||
await roleRepository.DeleteAsync(request.RoleId, tenantId, cancellationToken);
|
await roleRepository.DeleteAsync(request.RoleId, tenantId, cancellationToken);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public sealed class RoleDetailQueryHandler(
|
|||||||
public async Task<RoleDetailDto?> Handle(RoleDetailQuery request, CancellationToken cancellationToken)
|
public async Task<RoleDetailDto?> Handle(RoleDetailQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 1. 获取租户上下文并查询角色
|
// 1. 获取租户上下文并查询角色
|
||||||
var tenantId = tenantProvider.GetCurrentTenantId();
|
var tenantId = request.TenantId ?? tenantProvider.GetCurrentTenantId();
|
||||||
var role = await roleRepository.FindByIdAsync(request.RoleId, tenantId, cancellationToken);
|
var role = await roleRepository.FindByIdAsync(request.RoleId, tenantId, cancellationToken);
|
||||||
if (role is null)
|
if (role is null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public sealed class SearchRolesQueryHandler(
|
|||||||
public async Task<PagedResult<RoleDto>> Handle(SearchRolesQuery request, CancellationToken cancellationToken)
|
public async Task<PagedResult<RoleDto>> Handle(SearchRolesQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 1. 获取租户上下文并查询角色
|
// 1. 获取租户上下文并查询角色
|
||||||
var tenantId = tenantProvider.GetCurrentTenantId();
|
var tenantId = request.TenantId ?? tenantProvider.GetCurrentTenantId();
|
||||||
var roles = await roleRepository.SearchAsync(tenantId, request.Keyword, cancellationToken);
|
var roles = await roleRepository.SearchAsync(tenantId, request.Keyword, cancellationToken);
|
||||||
|
|
||||||
// 2. 排序
|
// 2. 排序
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public sealed class UpdateRoleCommandHandler(
|
|||||||
public async Task<RoleDto?> Handle(UpdateRoleCommand request, CancellationToken cancellationToken)
|
public async Task<RoleDto?> Handle(UpdateRoleCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 1. 获取租户上下文并查询角色
|
// 1. 获取租户上下文并查询角色
|
||||||
var tenantId = tenantProvider.GetCurrentTenantId();
|
var tenantId = request.TenantId ?? tenantProvider.GetCurrentTenantId();
|
||||||
var role = await roleRepository.FindByIdAsync(request.RoleId, tenantId, cancellationToken);
|
var role = await roleRepository.FindByIdAsync(request.RoleId, tenantId, cancellationToken);
|
||||||
if (role == null)
|
if (role == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,4 +12,9 @@ public sealed class RoleDetailQuery : IRequest<RoleDetailDto?>
|
|||||||
/// 角色 ID。
|
/// 角色 ID。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public long RoleId { get; init; }
|
public long RoleId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 租户 ID(空则取当前上下文)。
|
||||||
|
/// </summary>
|
||||||
|
public long? TenantId { get; init; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ namespace TakeoutSaaS.Application.Identity.Queries;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class SearchRolesQuery : IRequest<PagedResult<RoleDto>>
|
public sealed class SearchRolesQuery : IRequest<PagedResult<RoleDto>>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 指定查询的租户 ID(空则取当前上下文)。
|
||||||
|
/// </summary>
|
||||||
|
public long? TenantId { get; init; }
|
||||||
|
|
||||||
public string? Keyword { get; init; }
|
public string? Keyword { get; init; }
|
||||||
public int Page { get; init; } = 1;
|
public int Page { get; init; } = 1;
|
||||||
public int PageSize { get; init; } = 20;
|
public int PageSize { get; init; } = 20;
|
||||||
|
|||||||
Reference in New Issue
Block a user