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; /// /// 租户角色管理(实例层)。 /// [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; /// /// 租户角色分页。 /// [HttpGet] [PermissionAuthorize("identity:role:read")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] public async Task>> List( long tenantId, [FromQuery] SearchRolesQuery query, CancellationToken cancellationToken) { // 1. 校验路由租户与上下文一致(超管租户 1000000000001 放行) var currentTenantId = tenantProvider.GetCurrentTenantId(); if (currentTenantId != PlatformRootTenantId && tenantId != currentTenantId) { return ApiResponse>.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>.Ok(result); } /// /// 角色详情(含权限)。 /// [HttpGet("{roleId:long}")] [PermissionAuthorize("identity:role:read")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Detail(long tenantId, long roleId, CancellationToken cancellationToken) { // 1. 校验租户上下文(超管租户 1000000000001 放行) var currentTenantId = tenantProvider.GetCurrentTenantId(); if (currentTenantId != PlatformRootTenantId && tenantId != currentTenantId) { return ApiResponse.Error(StatusCodes.Status400BadRequest, "租户上下文不一致"); } // 2. 查询角色详情 var result = await mediator.Send(new RoleDetailQuery { RoleId = roleId, TenantId = tenantId }, cancellationToken); // 3. 返回数据或 404 return result is null ? ApiResponse.Error(StatusCodes.Status404NotFound, "角色不存在") : ApiResponse.Ok(result); } /// /// 创建角色。 /// [HttpPost] [PermissionAuthorize("identity:role:create")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Create( long tenantId, [FromBody, Required] CreateRoleCommand command, CancellationToken cancellationToken) { // 1. 校验租户上下文(超管租户 1000000000001 放行) var currentTenantId = tenantProvider.GetCurrentTenantId(); if (currentTenantId != PlatformRootTenantId && tenantId != currentTenantId) { return ApiResponse.Error(StatusCodes.Status400BadRequest, "租户上下文不一致"); } // 2. 创建角色 var result = await mediator.Send(command with { TenantId = tenantId }, cancellationToken); // 3. 返回创建结果 return ApiResponse.Ok(result); } /// /// 更新角色。 /// [HttpPut("{roleId:long}")] [PermissionAuthorize("identity:role:update")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] public async Task> Update( long tenantId, long roleId, [FromBody, Required] UpdateRoleCommand command, CancellationToken cancellationToken) { // 1. 校验租户上下文(超管租户 1000000000001 放行) var currentTenantId = tenantProvider.GetCurrentTenantId(); if (currentTenantId != PlatformRootTenantId && tenantId != currentTenantId) { return ApiResponse.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.Error(StatusCodes.Status404NotFound, "角色不存在") : ApiResponse.Ok(result); } /// /// 删除角色。 /// [HttpDelete("{roleId:long}")] [PermissionAuthorize("identity:role:delete")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> Delete(long tenantId, long roleId, CancellationToken cancellationToken) { // 1. 校验租户上下文(超管租户 1000000000001 放行) var currentTenantId = tenantProvider.GetCurrentTenantId(); if (currentTenantId != PlatformRootTenantId && tenantId != currentTenantId) { return ApiResponse.Error(StatusCodes.Status400BadRequest, "租户上下文不一致"); } // 2. 执行删除 var command = new DeleteRoleCommand { RoleId = roleId, TenantId = tenantId }; var result = await mediator.Send(command, cancellationToken); // 3. 返回结果 return ApiResponse.Ok(result); } /// /// 获取角色权限列表。 /// [HttpGet("{roleId:long}/permissions")] [PermissionAuthorize("identity:role:read")] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status404NotFound)] public async Task>> GetPermissions( long tenantId, long roleId, CancellationToken cancellationToken) { // 1. 校验租户上下文(超管租户 1000000000001 放行) var currentTenantId = tenantProvider.GetCurrentTenantId(); if (currentTenantId != PlatformRootTenantId && tenantId != currentTenantId) { return ApiResponse>.Error(StatusCodes.Status400BadRequest, "租户上下文不一致"); } // 2. 查询角色详情并提取权限 var detail = await mediator.Send(new RoleDetailQuery { RoleId = roleId, TenantId = tenantId }, cancellationToken); if (detail is null) { return ApiResponse>.Error(StatusCodes.Status404NotFound, "角色不存在"); } // 3. 返回权限集合 return ApiResponse>.Ok(detail.Permissions); } /// /// 覆盖角色权限。 /// [HttpPut("{roleId:long}/permissions")] [PermissionAuthorize("identity:role:bind-permission")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task> BindPermissions( long tenantId, long roleId, [FromBody, Required] BindRolePermissionsCommand command, CancellationToken cancellationToken) { // 1. 校验租户上下文(超管租户 1000000000001 放行) var currentTenantId = tenantProvider.GetCurrentTenantId(); if (currentTenantId != PlatformRootTenantId && tenantId != currentTenantId) { return ApiResponse.Error(StatusCodes.Status400BadRequest, "租户上下文不一致"); } // 2. 绑定角色 ID command = command with { RoleId = roleId, TenantId = tenantId }; // 3. 覆盖授权 var result = await mediator.Send(command, cancellationToken); return ApiResponse.Ok(result); } }