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

260 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.Web.Api;
namespace TakeoutSaaS.AdminApi.Controllers;
/// <summary>
/// 角色管理。
/// </summary>
[ApiVersion("1.0")]
[Authorize]
[Route("api/admin/v{version:apiVersion}/roles")]
public sealed class RolesController(IMediator mediator) : BaseApiController
{
/// <summary>
/// 获取预置角色模板列表。
/// </summary>
/// <remarks>
/// 示例GET /api/admin/v1/roles/templates
/// </remarks>
/// <returns>角色模板列表。</returns>
[HttpGet("templates")]
[PermissionAuthorize("identity:role:read")]
[ProducesResponseType(typeof(ApiResponse<IReadOnlyList<RoleTemplateDto>>), StatusCodes.Status200OK)]
public async Task<ApiResponse<IReadOnlyList<RoleTemplateDto>>> ListTemplates([FromQuery] bool? isActive, CancellationToken cancellationToken)
{
// 1. 查询模板列表
var result = await mediator.Send(new ListRoleTemplatesQuery { IsActive = isActive }, cancellationToken);
// 2. 返回模板集合
return ApiResponse<IReadOnlyList<RoleTemplateDto>>.Ok(result);
}
/// <summary>
/// 获取单个角色模板详情。
/// </summary>
/// <remarks>
/// 示例GET /api/admin/v1/roles/templates/tenant-admin
/// </remarks>
/// <returns>角色模板详情。</returns>
[HttpGet("templates/{templateCode}")]
[PermissionAuthorize("identity:role:read")]
[ProducesResponseType(typeof(ApiResponse<RoleTemplateDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<RoleTemplateDto>), StatusCodes.Status404NotFound)]
public async Task<ApiResponse<RoleTemplateDto>> GetTemplate(string templateCode, CancellationToken cancellationToken)
{
// 1. 查询指定模板
var result = await mediator.Send(new GetRoleTemplateQuery { TemplateCode = templateCode }, cancellationToken);
// 2. 返回模板或 404
return result is null
? ApiResponse<RoleTemplateDto>.Error(StatusCodes.Status404NotFound, "角色模板不存在")
: ApiResponse<RoleTemplateDto>.Ok(result);
}
/// <summary>
/// 创建角色模板。
/// </summary>
/// <returns>创建的角色模板信息。</returns>
[HttpPost("templates")]
[PermissionAuthorize("role-template:create")]
[ProducesResponseType(typeof(ApiResponse<RoleTemplateDto>), StatusCodes.Status200OK)]
public async Task<ApiResponse<RoleTemplateDto>> CreateTemplate([FromBody, Required] CreateRoleTemplateCommand command, CancellationToken cancellationToken)
{
// 1. 创建模板
var result = await mediator.Send(command, cancellationToken);
// 2. 返回创建结果
return ApiResponse<RoleTemplateDto>.Ok(result);
}
/// <summary>
/// 更新角色模板。
/// </summary>
/// <returns>更新后的角色模板信息。</returns>
[HttpPut("templates/{templateCode}")]
[PermissionAuthorize("role-template:update")]
[ProducesResponseType(typeof(ApiResponse<RoleTemplateDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<RoleTemplateDto>), StatusCodes.Status404NotFound)]
public async Task<ApiResponse<RoleTemplateDto>> UpdateTemplate(
string templateCode,
[FromBody, Required] UpdateRoleTemplateCommand command,
CancellationToken cancellationToken)
{
// 1. 绑定模板编码
command = command with { TemplateCode = templateCode };
// 2. 执行更新
var result = await mediator.Send(command, cancellationToken);
// 3. 返回更新结果或 404
return result is null
? ApiResponse<RoleTemplateDto>.Error(StatusCodes.Status404NotFound, "角色模板不存在")
: ApiResponse<RoleTemplateDto>.Ok(result);
}
/// <summary>
/// 删除角色模板。
/// </summary>
/// <returns>删除结果。</returns>
[HttpDelete("templates/{templateCode}")]
[PermissionAuthorize("role-template:delete")]
[ProducesResponseType(typeof(ApiResponse<bool>), StatusCodes.Status200OK)]
public async Task<ApiResponse<bool>> DeleteTemplate(string templateCode, CancellationToken cancellationToken)
{
// 1. 删除模板
var result = await mediator.Send(new DeleteRoleTemplateCommand { TemplateCode = templateCode }, cancellationToken);
// 2. 返回执行结果
return ApiResponse<bool>.Ok(result);
}
/// <summary>
/// 按模板复制角色并绑定权限。
/// </summary>
/// <remarks>
/// 示例POST /api/admin/v1/roles/templates/store-manager/copy
/// Body: { "roleName": "新区店长" }
/// </remarks>
/// <returns>创建的角色信息。</returns>
[HttpPost("templates/{templateCode}/copy")]
[PermissionAuthorize("identity:role:create")]
[ProducesResponseType(typeof(ApiResponse<RoleDto>), StatusCodes.Status200OK)]
public async Task<ApiResponse<RoleDto>> CopyFromTemplate(
string templateCode,
[FromBody, Required] CopyRoleTemplateCommand command,
CancellationToken cancellationToken)
{
// 1. 绑定模板编码
command = command with { TemplateCode = templateCode };
// 2. 复制模板并返回角色
var result = await mediator.Send(command, cancellationToken);
return ApiResponse<RoleDto>.Ok(result);
}
/// <summary>
/// 为当前租户批量初始化预置角色模板。
/// </summary>
/// <remarks>
/// 示例POST /api/admin/v1/roles/templates/init
/// Body: { "templateCodes": ["tenant-admin","store-manager","store-staff"] }
/// </remarks>
/// <returns>创建的角色列表。</returns>
[HttpPost("templates/init")]
[PermissionAuthorize("identity:role:create")]
[ProducesResponseType(typeof(ApiResponse<IReadOnlyList<RoleDto>>), StatusCodes.Status200OK)]
public async Task<ApiResponse<IReadOnlyList<RoleDto>>> InitializeTemplates(
[FromBody] InitializeRoleTemplatesCommand? command,
CancellationToken cancellationToken)
{
// 1. 确保命令实例存在
command ??= new InitializeRoleTemplatesCommand();
// 2. 执行初始化
var result = await mediator.Send(command, cancellationToken);
return ApiResponse<IReadOnlyList<RoleDto>>.Ok(result);
}
/// <summary>
/// 分页查询角色。
/// </summary>
/// <remarks>
/// 示例:
/// GET /api/admin/v1/roles?keyword=ops&amp;page=1&amp;pageSize=20
/// Header: Authorization: Bearer &lt;JWT&gt; + X-Tenant-Id
/// </remarks>
/// <returns>角色分页结果。</returns>
[HttpGet]
[PermissionAuthorize("identity:role:read")]
[ProducesResponseType(typeof(ApiResponse<PagedResult<RoleDto>>), StatusCodes.Status200OK)]
public async Task<ApiResponse<PagedResult<RoleDto>>> Search([FromQuery] SearchRolesQuery query, CancellationToken cancellationToken)
{
// 1. 查询角色分页
var result = await mediator.Send(query, cancellationToken);
// 2. 返回分页数据
return ApiResponse<PagedResult<RoleDto>>.Ok(result);
}
/// <summary>
/// 创建角色。
/// </summary>
/// <returns>创建的角色信息。</returns>
[HttpPost]
[PermissionAuthorize("identity:role:create")]
[ProducesResponseType(typeof(ApiResponse<RoleDto>), StatusCodes.Status200OK)]
public async Task<ApiResponse<RoleDto>> Create([FromBody, Required] CreateRoleCommand command, CancellationToken cancellationToken)
{
// 1. 创建角色
var result = await mediator.Send(command, cancellationToken);
// 2. 返回创建结果
return ApiResponse<RoleDto>.Ok(result);
}
/// <summary>
/// 更新角色。
/// </summary>
/// <returns>更新后的角色信息。</returns>
[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 roleId, [FromBody, Required] UpdateRoleCommand command, CancellationToken cancellationToken)
{
// 1. 绑定角色标识
command = command with { RoleId = roleId };
// 2. 执行更新
var result = await mediator.Send(command, cancellationToken);
// 3. 返回更新结果或 404
return result is null
? ApiResponse<RoleDto>.Error(StatusCodes.Status404NotFound, "角色不存在")
: ApiResponse<RoleDto>.Ok(result);
}
/// <summary>
/// 删除角色。
/// </summary>
/// <returns>删除结果。</returns>
[HttpDelete("{roleId:long}")]
[PermissionAuthorize("identity:role:delete")]
[ProducesResponseType(typeof(ApiResponse<bool>), StatusCodes.Status200OK)]
public async Task<ApiResponse<bool>> Delete(long roleId, CancellationToken cancellationToken)
{
// 1. 构建删除命令
var command = new DeleteRoleCommand { RoleId = roleId };
// 2. 执行删除
var result = await mediator.Send(command, cancellationToken);
return ApiResponse<bool>.Ok(result);
}
/// <summary>
/// 绑定角色权限(覆盖式)。
/// </summary>
/// <returns>是否绑定成功。</returns>
[HttpPut("{roleId:long}/permissions")]
[PermissionAuthorize("identity:role:bind-permission")]
[ProducesResponseType(typeof(ApiResponse<bool>), StatusCodes.Status200OK)]
public async Task<ApiResponse<bool>> BindPermissions(long roleId, [FromBody, Required] BindRolePermissionsCommand command, CancellationToken cancellationToken)
{
// 1. 绑定角色标识
command = command with { RoleId = roleId };
// 2. 执行覆盖式授权
var result = await mediator.Send(command, cancellationToken);
return ApiResponse<bool>.Ok(result);
}
}