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:
2026-01-30 07:53:25 +00:00
parent 2dbd004ce0
commit 0bcad8ba7e
5 changed files with 191 additions and 0 deletions

View File

@@ -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; }
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}