Files
TakeoutSaaS.TenantApi/src/Api/TakeoutSaaS.AdminApi/Controllers/TenantRolesController.cs

221 lines
8.8 KiB
C#

using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
using TakeoutSaaS.Application.Identity.Commands;
using TakeoutSaaS.Application.Identity.Contracts;
using TakeoutSaaS.Application.Identity.Queries;
using TakeoutSaaS.Module.Authorization.Attributes;
using TakeoutSaaS.Shared.Abstractions.Results;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
using TakeoutSaaS.Shared.Web.Api;
namespace TakeoutSaaS.AdminApi.Controllers;
/// <summary>
/// 租户角色管理(实例层)。
/// </summary>
[ApiVersion("1.0")]
[Authorize]
[Route("api/admin/v{version:apiVersion}/tenants/{tenantId:long}/roles")]
public sealed class TenantRolesController(IMediator mediator, ITenantProvider tenantProvider) : BaseApiController
{
private const long PlatformRootTenantId = 1000000000001;
/// <summary>
/// 租户角色分页。
/// </summary>
[HttpGet]
[PermissionAuthorize("identity:role:read")]
[ProducesResponseType(typeof(ApiResponse<PagedResult<RoleDto>>), StatusCodes.Status200OK)]
public async Task<ApiResponse<PagedResult<RoleDto>>> List(
long tenantId,
[FromQuery] SearchRolesQuery query,
CancellationToken cancellationToken)
{
// 1. 校验路由租户与上下文一致(超管租户 1000000000001 放行)
var currentTenantId = tenantProvider.GetCurrentTenantId();
if (currentTenantId != PlatformRootTenantId && tenantId != currentTenantId)
{
return ApiResponse<PagedResult<RoleDto>>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
}
// 2. 绑定租户并查询角色分页
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. 返回分页数据
return ApiResponse<PagedResult<RoleDto>>.Ok(result);
}
/// <summary>
/// 角色详情(含权限)。
/// </summary>
[HttpGet("{roleId:long}")]
[PermissionAuthorize("identity:role:read")]
[ProducesResponseType(typeof(ApiResponse<RoleDetailDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<RoleDetailDto>), StatusCodes.Status404NotFound)]
public async Task<ApiResponse<RoleDetailDto>> Detail(long tenantId, long roleId, CancellationToken cancellationToken)
{
// 1. 校验租户上下文(超管租户 1000000000001 放行)
var currentTenantId = tenantProvider.GetCurrentTenantId();
if (currentTenantId != PlatformRootTenantId && tenantId != currentTenantId)
{
return ApiResponse<RoleDetailDto>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
}
// 2. 查询角色详情
var result = await mediator.Send(new RoleDetailQuery { RoleId = roleId, TenantId = tenantId }, cancellationToken);
// 3. 返回数据或 404
return result is null
? ApiResponse<RoleDetailDto>.Error(StatusCodes.Status404NotFound, "角色不存在")
: ApiResponse<RoleDetailDto>.Ok(result);
}
/// <summary>
/// 创建角色。
/// </summary>
[HttpPost]
[PermissionAuthorize("identity:role:create")]
[ProducesResponseType(typeof(ApiResponse<RoleDto>), StatusCodes.Status200OK)]
public async Task<ApiResponse<RoleDto>> Create(
long tenantId,
[FromBody, Required] CreateRoleCommand command,
CancellationToken cancellationToken)
{
// 1. 校验租户上下文(超管租户 1000000000001 放行)
var currentTenantId = tenantProvider.GetCurrentTenantId();
if (currentTenantId != PlatformRootTenantId && tenantId != currentTenantId)
{
return ApiResponse<RoleDto>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
}
// 2. 创建角色
var result = await mediator.Send(command with { TenantId = tenantId }, cancellationToken);
// 3. 返回创建结果
return ApiResponse<RoleDto>.Ok(result);
}
/// <summary>
/// 更新角色。
/// </summary>
[HttpPut("{roleId:long}")]
[PermissionAuthorize("identity:role:update")]
[ProducesResponseType(typeof(ApiResponse<RoleDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<RoleDto>), StatusCodes.Status404NotFound)]
public async Task<ApiResponse<RoleDto>> Update(
long tenantId,
long roleId,
[FromBody, Required] UpdateRoleCommand command,
CancellationToken cancellationToken)
{
// 1. 校验租户上下文(超管租户 1000000000001 放行)
var currentTenantId = tenantProvider.GetCurrentTenantId();
if (currentTenantId != PlatformRootTenantId && tenantId != currentTenantId)
{
return ApiResponse<RoleDto>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
}
// 2. 绑定角色 ID
command = command with { RoleId = roleId, TenantId = tenantId };
// 3. 执行更新
var result = await mediator.Send(command, cancellationToken);
// 4. 返回结果或 404
return result is null
? ApiResponse<RoleDto>.Error(StatusCodes.Status404NotFound, "角色不存在")
: ApiResponse<RoleDto>.Ok(result);
}
/// <summary>
/// 删除角色。
/// </summary>
[HttpDelete("{roleId:long}")]
[PermissionAuthorize("identity:role:delete")]
[ProducesResponseType(typeof(ApiResponse<bool>), StatusCodes.Status200OK)]
public async Task<ApiResponse<bool>> Delete(long tenantId, long roleId, CancellationToken cancellationToken)
{
// 1. 校验租户上下文(超管租户 1000000000001 放行)
var currentTenantId = tenantProvider.GetCurrentTenantId();
if (currentTenantId != PlatformRootTenantId && tenantId != currentTenantId)
{
return ApiResponse<bool>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
}
// 2. 执行删除
var command = new DeleteRoleCommand { RoleId = roleId, TenantId = tenantId };
var result = await mediator.Send(command, cancellationToken);
// 3. 返回结果
return ApiResponse<bool>.Ok(result);
}
/// <summary>
/// 获取角色权限列表。
/// </summary>
[HttpGet("{roleId:long}/permissions")]
[PermissionAuthorize("identity:role:read")]
[ProducesResponseType(typeof(ApiResponse<IReadOnlyList<PermissionDto>>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<IReadOnlyList<PermissionDto>>), StatusCodes.Status404NotFound)]
public async Task<ApiResponse<IReadOnlyList<PermissionDto>>> GetPermissions(
long tenantId,
long roleId,
CancellationToken cancellationToken)
{
// 1. 校验租户上下文(超管租户 1000000000001 放行)
var currentTenantId = tenantProvider.GetCurrentTenantId();
if (currentTenantId != PlatformRootTenantId && tenantId != currentTenantId)
{
return ApiResponse<IReadOnlyList<PermissionDto>>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
}
// 2. 查询角色详情并提取权限
var detail = await mediator.Send(new RoleDetailQuery { RoleId = roleId, TenantId = tenantId }, cancellationToken);
if (detail is null)
{
return ApiResponse<IReadOnlyList<PermissionDto>>.Error(StatusCodes.Status404NotFound, "角色不存在");
}
// 3. 返回权限集合
return ApiResponse<IReadOnlyList<PermissionDto>>.Ok(detail.Permissions);
}
/// <summary>
/// 覆盖角色权限。
/// </summary>
[HttpPut("{roleId:long}/permissions")]
[PermissionAuthorize("identity:role:bind-permission")]
[ProducesResponseType(typeof(ApiResponse<bool>), StatusCodes.Status200OK)]
public async Task<ApiResponse<bool>> BindPermissions(
long tenantId,
long roleId,
[FromBody, Required] BindRolePermissionsCommand command,
CancellationToken cancellationToken)
{
// 1. 校验租户上下文(超管租户 1000000000001 放行)
var currentTenantId = tenantProvider.GetCurrentTenantId();
if (currentTenantId != PlatformRootTenantId && tenantId != currentTenantId)
{
return ApiResponse<bool>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致");
}
// 2. 绑定角色 ID
command = command with { RoleId = roleId, TenantId = tenantId };
// 3. 覆盖授权
var result = await mediator.Send(command, cancellationToken);
return ApiResponse<bool>.Ok(result);
}
}