feat: 实现租户角色列表查询和删除接口
- 获取租户角色列表:GET /api/admin/v1/tenants/{tenantId}/roles
- 删除租户角色:DELETE /api/admin/v1/tenants/{tenantId}/roles/{roleId}
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,41 @@ namespace TakeoutSaaS.AdminApi.Controllers;
|
|||||||
[Route("api/admin/v{version:apiVersion}/tenants/{tenantId:long}/roles")]
|
[Route("api/admin/v{version:apiVersion}/tenants/{tenantId:long}/roles")]
|
||||||
public sealed class TenantRolesController(IMediator mediator) : BaseApiController
|
public sealed class TenantRolesController(IMediator mediator) : BaseApiController
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取租户角色列表。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tenantId">租户 ID。</param>
|
||||||
|
/// <param name="keyword">关键字(角色名称/编码)。</param>
|
||||||
|
/// <param name="page">页码(从 1 开始)。</param>
|
||||||
|
/// <param name="pageSize">每页条数。</param>
|
||||||
|
/// <param name="cancellationToken">取消标记。</param>
|
||||||
|
/// <returns>角色分页列表。</returns>
|
||||||
|
[HttpGet]
|
||||||
|
[PermissionAuthorize("identity:role:read")]
|
||||||
|
[ProducesResponseType(typeof(ApiResponse<PagedResult<RoleDto>>), StatusCodes.Status200OK)]
|
||||||
|
public async Task<ApiResponse<PagedResult<RoleDto>>> List(
|
||||||
|
long tenantId,
|
||||||
|
[FromQuery] string? keyword,
|
||||||
|
[FromQuery] int page = 1,
|
||||||
|
[FromQuery] int pageSize = 20,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
// 1. 构造查询
|
||||||
|
var query = new ListTenantRolesQuery
|
||||||
|
{
|
||||||
|
TenantId = tenantId,
|
||||||
|
Keyword = keyword,
|
||||||
|
Page = page,
|
||||||
|
PageSize = pageSize
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. 执行查询
|
||||||
|
var result = await mediator.Send(query, cancellationToken);
|
||||||
|
|
||||||
|
// 3. 返回分页结果
|
||||||
|
return ApiResponse<PagedResult<RoleDto>>.Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建租户角色。
|
/// 创建租户角色。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -125,4 +160,29 @@ public sealed class TenantRolesController(IMediator mediator) : BaseApiControlle
|
|||||||
// 3. 返回更新结果
|
// 3. 返回更新结果
|
||||||
return ApiResponse<bool>.Ok(result);
|
return ApiResponse<bool>.Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除租户角色。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tenantId">租户 ID。</param>
|
||||||
|
/// <param name="roleId">角色 ID。</param>
|
||||||
|
/// <param name="cancellationToken">取消标记。</param>
|
||||||
|
/// <returns>删除结果。</returns>
|
||||||
|
[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. 构造命令
|
||||||
|
var command = new DeleteTenantRoleCommand { TenantId = tenantId, RoleId = roleId };
|
||||||
|
|
||||||
|
// 2. 执行删除
|
||||||
|
var result = await mediator.Send(command, cancellationToken);
|
||||||
|
|
||||||
|
// 3. 返回删除结果
|
||||||
|
return ApiResponse<bool>.Ok(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace TakeoutSaaS.Application.Identity.Commands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除租户角色命令。
|
||||||
|
/// </summary>
|
||||||
|
public sealed record DeleteTenantRoleCommand : IRequest<bool>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 租户 ID(由路由绑定)。
|
||||||
|
/// </summary>
|
||||||
|
public long TenantId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色 ID(由路由绑定)。
|
||||||
|
/// </summary>
|
||||||
|
public long RoleId { get; init; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using MediatR;
|
||||||
|
using TakeoutSaaS.Application.Identity.Commands;
|
||||||
|
using TakeoutSaaS.Domain.Identity.Enums;
|
||||||
|
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||||
|
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||||
|
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||||
|
|
||||||
|
namespace TakeoutSaaS.Application.Identity.Handlers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除租户角色命令处理器。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class DeleteTenantRoleCommandHandler(IRoleRepository roleRepository)
|
||||||
|
: IRequestHandler<DeleteTenantRoleCommand, bool>
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<bool> Handle(DeleteTenantRoleCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
// 1. 校验角色存在
|
||||||
|
var role = await roleRepository.FindByIdAsync(PortalType.Tenant, request.TenantId, request.RoleId, cancellationToken);
|
||||||
|
if (role is null)
|
||||||
|
{
|
||||||
|
throw new BusinessException(ErrorCodes.NotFound, "角色不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 执行软删除
|
||||||
|
await roleRepository.DeleteAsync(PortalType.Tenant, request.TenantId, request.RoleId, cancellationToken);
|
||||||
|
await roleRepository.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
// 3. 返回成功
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using MediatR;
|
||||||
|
using TakeoutSaaS.Application.Identity.Contracts;
|
||||||
|
using TakeoutSaaS.Application.Identity.Queries;
|
||||||
|
using TakeoutSaaS.Domain.Identity.Enums;
|
||||||
|
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||||
|
using TakeoutSaaS.Shared.Abstractions.Results;
|
||||||
|
|
||||||
|
namespace TakeoutSaaS.Application.Identity.Handlers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取租户角色列表查询处理器。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ListTenantRolesQueryHandler(IRoleRepository roleRepository)
|
||||||
|
: IRequestHandler<ListTenantRolesQuery, PagedResult<RoleDto>>
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<PagedResult<RoleDto>> Handle(ListTenantRolesQuery request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
// 1. 查询租户下所有角色
|
||||||
|
var roles = await roleRepository.SearchAsync(PortalType.Tenant, request.TenantId, request.Keyword, cancellationToken);
|
||||||
|
|
||||||
|
// 2. 计算分页参数
|
||||||
|
var totalCount = roles.Count;
|
||||||
|
var page = Math.Max(1, request.Page);
|
||||||
|
var pageSize = Math.Clamp(request.PageSize, 1, 100);
|
||||||
|
|
||||||
|
// 3. 应用分页
|
||||||
|
var pagedRoles = roles
|
||||||
|
.OrderBy(r => r.Code)
|
||||||
|
.Skip((page - 1) * pageSize)
|
||||||
|
.Take(pageSize)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
// 4. 映射 DTO
|
||||||
|
var dtos = pagedRoles.Select(role => new RoleDto
|
||||||
|
{
|
||||||
|
Portal = role.Portal,
|
||||||
|
Id = role.Id,
|
||||||
|
TenantId = role.TenantId,
|
||||||
|
Code = role.Code,
|
||||||
|
Name = role.Name,
|
||||||
|
Description = role.Description
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
|
// 5. 返回分页结果
|
||||||
|
return new PagedResult<RoleDto>(dtos, page, pageSize, totalCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using MediatR;
|
||||||
|
using TakeoutSaaS.Application.Identity.Contracts;
|
||||||
|
using TakeoutSaaS.Shared.Abstractions.Results;
|
||||||
|
|
||||||
|
namespace TakeoutSaaS.Application.Identity.Queries;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取租户角色列表查询。
|
||||||
|
/// </summary>
|
||||||
|
public sealed record ListTenantRolesQuery : IRequest<PagedResult<RoleDto>>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 租户 ID。
|
||||||
|
/// </summary>
|
||||||
|
public long TenantId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关键字(角色名称/编码)。
|
||||||
|
/// </summary>
|
||||||
|
public string? Keyword { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 页码(从 1 开始)。
|
||||||
|
/// </summary>
|
||||||
|
public int Page { get; init; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 每页条数。
|
||||||
|
/// </summary>
|
||||||
|
public int PageSize { get; init; } = 20;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user