feat: 增加角色/权限管理 API 与应用层命令

This commit is contained in:
2025-12-02 16:43:46 +08:00
parent b459c7edbe
commit 35b12fb054
25 changed files with 743 additions and 1 deletions

View File

@@ -0,0 +1,78 @@
using System.ComponentModel.DataAnnotations;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
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}/permissions")]
public sealed class PermissionsController(IMediator mediator) : BaseApiController
{
/// <summary>
/// 分页查询权限。
/// </summary>
/// <remarks>
/// 示例GET /api/admin/v1/permissions?keyword=order&amp;page=1&amp;pageSize=20
/// </remarks>
[HttpGet]
[PermissionAuthorize("identity:permission:read")]
[ProducesResponseType(typeof(ApiResponse<PagedResult<PermissionDto>>), StatusCodes.Status200OK)]
public async Task<ApiResponse<PagedResult<PermissionDto>>> Search([FromQuery] SearchPermissionsQuery query, CancellationToken cancellationToken)
{
var result = await mediator.Send(query, cancellationToken);
return ApiResponse<PagedResult<PermissionDto>>.Ok(result);
}
/// <summary>
/// 创建权限。
/// </summary>
[HttpPost]
[PermissionAuthorize("identity:permission:create")]
[ProducesResponseType(typeof(ApiResponse<PermissionDto>), StatusCodes.Status200OK)]
public async Task<ApiResponse<PermissionDto>> Create([FromBody, Required] CreatePermissionCommand command, CancellationToken cancellationToken)
{
var result = await mediator.Send(command, cancellationToken);
return ApiResponse<PermissionDto>.Ok(result);
}
/// <summary>
/// 更新权限。
/// </summary>
[HttpPut("{permissionId:long}")]
[PermissionAuthorize("identity:permission:update")]
[ProducesResponseType(typeof(ApiResponse<PermissionDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<PermissionDto>), StatusCodes.Status404NotFound)]
public async Task<ApiResponse<PermissionDto>> Update(long permissionId, [FromBody, Required] UpdatePermissionCommand command, CancellationToken cancellationToken)
{
command = command with { PermissionId = permissionId };
var result = await mediator.Send(command, cancellationToken);
return result is null
? ApiResponse<PermissionDto>.Error(StatusCodes.Status404NotFound, "权限不存在")
: ApiResponse<PermissionDto>.Ok(result);
}
/// <summary>
/// 删除权限。
/// </summary>
[HttpDelete("{permissionId:long}")]
[PermissionAuthorize("identity:permission:delete")]
[ProducesResponseType(typeof(ApiResponse<bool>), StatusCodes.Status200OK)]
public async Task<ApiResponse<bool>> Delete(long permissionId, CancellationToken cancellationToken)
{
var command = new DeletePermissionCommand { PermissionId = permissionId };
var result = await mediator.Send(command, cancellationToken);
return ApiResponse<bool>.Ok(result);
}
}

View File

@@ -0,0 +1,93 @@
using System.ComponentModel.DataAnnotations;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
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?keyword=ops&amp;page=1&amp;pageSize=20
/// Header: Authorization: Bearer &lt;JWT&gt; + X-Tenant-Id
/// </remarks>
[HttpGet]
[PermissionAuthorize("identity:role:read")]
[ProducesResponseType(typeof(ApiResponse<PagedResult<RoleDto>>), StatusCodes.Status200OK)]
public async Task<ApiResponse<PagedResult<RoleDto>>> Search([FromQuery] SearchRolesQuery query, CancellationToken cancellationToken)
{
var result = await mediator.Send(query, cancellationToken);
return ApiResponse<PagedResult<RoleDto>>.Ok(result);
}
/// <summary>
/// 创建角色。
/// </summary>
[HttpPost]
[PermissionAuthorize("identity:role:create")]
[ProducesResponseType(typeof(ApiResponse<RoleDto>), StatusCodes.Status200OK)]
public async Task<ApiResponse<RoleDto>> Create([FromBody, Required] CreateRoleCommand command, CancellationToken cancellationToken)
{
var result = await mediator.Send(command, cancellationToken);
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 roleId, [FromBody, Required] UpdateRoleCommand command, CancellationToken cancellationToken)
{
command = command with { RoleId = roleId };
var result = await mediator.Send(command, cancellationToken);
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 roleId, CancellationToken cancellationToken)
{
var command = new DeleteRoleCommand { RoleId = roleId };
var result = await mediator.Send(command, cancellationToken);
return ApiResponse<bool>.Ok(result);
}
/// <summary>
/// 绑定角色权限(覆盖式)。
/// </summary>
[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)
{
command = command with { RoleId = roleId };
var result = await mediator.Send(command, cancellationToken);
return ApiResponse<bool>.Ok(result);
}
}

View File

@@ -0,0 +1,12 @@
using MediatR;
namespace TakeoutSaaS.Application.Identity.Commands;
/// <summary>
/// 为用户分配角色(覆盖式)。
/// </summary>
public sealed record AssignUserRolesCommand : IRequest<bool>
{
public long UserId { get; init; }
public long[] RoleIds { get; init; } = Array.Empty<long>();
}

View File

@@ -0,0 +1,12 @@
using MediatR;
namespace TakeoutSaaS.Application.Identity.Commands;
/// <summary>
/// 绑定角色权限(覆盖式)。
/// </summary>
public sealed record BindRolePermissionsCommand : IRequest<bool>
{
public long RoleId { get; init; }
public long[] PermissionIds { get; init; } = Array.Empty<long>();
}

View File

@@ -0,0 +1,14 @@
using MediatR;
using TakeoutSaaS.Application.Identity.Contracts;
namespace TakeoutSaaS.Application.Identity.Commands;
/// <summary>
/// 创建权限。
/// </summary>
public sealed record CreatePermissionCommand : IRequest<PermissionDto>
{
public string Name { get; init; } = string.Empty;
public string Code { get; init; } = string.Empty;
public string? Description { get; init; }
}

View File

@@ -0,0 +1,14 @@
using MediatR;
using TakeoutSaaS.Application.Identity.Contracts;
namespace TakeoutSaaS.Application.Identity.Commands;
/// <summary>
/// 创建角色。
/// </summary>
public sealed record CreateRoleCommand : IRequest<RoleDto>
{
public string Name { get; init; } = string.Empty;
public string Code { get; init; } = string.Empty;
public string? Description { get; init; }
}

View File

@@ -0,0 +1,11 @@
using MediatR;
namespace TakeoutSaaS.Application.Identity.Commands;
/// <summary>
/// 删除权限。
/// </summary>
public sealed record DeletePermissionCommand : IRequest<bool>
{
public long PermissionId { get; init; }
}

View File

@@ -0,0 +1,11 @@
using MediatR;
namespace TakeoutSaaS.Application.Identity.Commands;
/// <summary>
/// 删除角色。
/// </summary>
public sealed record DeleteRoleCommand : IRequest<bool>
{
public long RoleId { get; init; }
}

View File

@@ -0,0 +1,14 @@
using MediatR;
using TakeoutSaaS.Application.Identity.Contracts;
namespace TakeoutSaaS.Application.Identity.Commands;
/// <summary>
/// 更新权限。
/// </summary>
public sealed record UpdatePermissionCommand : IRequest<PermissionDto?>
{
public long PermissionId { get; init; }
public string Name { get; init; } = string.Empty;
public string? Description { get; init; }
}

View File

@@ -0,0 +1,14 @@
using MediatR;
using TakeoutSaaS.Application.Identity.Contracts;
namespace TakeoutSaaS.Application.Identity.Commands;
/// <summary>
/// 更新角色。
/// </summary>
public sealed record UpdateRoleCommand : IRequest<RoleDto?>
{
public long RoleId { get; init; }
public string Name { get; init; } = string.Empty;
public string? Description { get; init; }
}

View File

@@ -0,0 +1,37 @@
using System.Text.Json.Serialization;
using TakeoutSaaS.Shared.Abstractions.Serialization;
namespace TakeoutSaaS.Application.Identity.Contracts;
/// <summary>
/// 权限 DTO。
/// </summary>
public sealed class PermissionDto
{
/// <summary>
/// 权限 ID雪花序列化为字符串
/// </summary>
[JsonConverter(typeof(SnowflakeIdJsonConverter))]
public long Id { get; init; }
/// <summary>
/// 租户 ID。
/// </summary>
[JsonConverter(typeof(SnowflakeIdJsonConverter))]
public long TenantId { get; init; }
/// <summary>
/// 权限名称。
/// </summary>
public string Name { get; init; } = string.Empty;
/// <summary>
/// 权限编码(租户内唯一)。
/// </summary>
public string Code { get; init; } = string.Empty;
/// <summary>
/// 描述。
/// </summary>
public string? Description { get; init; }
}

View File

@@ -0,0 +1,37 @@
using System.Text.Json.Serialization;
using TakeoutSaaS.Shared.Abstractions.Serialization;
namespace TakeoutSaaS.Application.Identity.Contracts;
/// <summary>
/// 角色 DTO。
/// </summary>
public sealed class RoleDto
{
/// <summary>
/// 角色 ID雪花序列化为字符串
/// </summary>
[JsonConverter(typeof(SnowflakeIdJsonConverter))]
public long Id { get; init; }
/// <summary>
/// 租户 ID。
/// </summary>
[JsonConverter(typeof(SnowflakeIdJsonConverter))]
public long TenantId { get; init; }
/// <summary>
/// 角色名称。
/// </summary>
public string Name { get; init; } = string.Empty;
/// <summary>
/// 角色编码(租户内唯一)。
/// </summary>
public string Code { get; init; } = string.Empty;
/// <summary>
/// 描述。
/// </summary>
public string? Description { get; init; }
}

View File

@@ -0,0 +1,23 @@
using MediatR;
using TakeoutSaaS.Application.Identity.Commands;
using TakeoutSaaS.Domain.Identity.Repositories;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Application.Identity.Handlers;
/// <summary>
/// 用户角色分配处理器。
/// </summary>
public sealed class AssignUserRolesCommandHandler(
IUserRoleRepository userRoleRepository,
ITenantProvider tenantProvider)
: IRequestHandler<AssignUserRolesCommand, bool>
{
public async Task<bool> Handle(AssignUserRolesCommand request, CancellationToken cancellationToken)
{
var tenantId = tenantProvider.GetCurrentTenantId();
await userRoleRepository.ReplaceUserRolesAsync(tenantId, request.UserId, request.RoleIds, cancellationToken);
await userRoleRepository.SaveChangesAsync(cancellationToken);
return true;
}
}

View File

@@ -0,0 +1,23 @@
using MediatR;
using TakeoutSaaS.Application.Identity.Commands;
using TakeoutSaaS.Domain.Identity.Repositories;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Application.Identity.Handlers;
/// <summary>
/// 绑定角色权限处理器。
/// </summary>
public sealed class BindRolePermissionsCommandHandler(
IRolePermissionRepository rolePermissionRepository,
ITenantProvider tenantProvider)
: IRequestHandler<BindRolePermissionsCommand, bool>
{
public async Task<bool> Handle(BindRolePermissionsCommand request, CancellationToken cancellationToken)
{
var tenantId = tenantProvider.GetCurrentTenantId();
await rolePermissionRepository.ReplaceRolePermissionsAsync(tenantId, request.RoleId, request.PermissionIds, cancellationToken);
await rolePermissionRepository.SaveChangesAsync(cancellationToken);
return true;
}
}

View File

@@ -0,0 +1,41 @@
using MediatR;
using TakeoutSaaS.Application.Identity.Commands;
using TakeoutSaaS.Application.Identity.Contracts;
using TakeoutSaaS.Domain.Identity.Entities;
using TakeoutSaaS.Domain.Identity.Repositories;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Application.Identity.Handlers;
/// <summary>
/// 创建权限处理器。
/// </summary>
public sealed class CreatePermissionCommandHandler(
IPermissionRepository permissionRepository,
ITenantProvider tenantProvider)
: IRequestHandler<CreatePermissionCommand, PermissionDto>
{
public async Task<PermissionDto> Handle(CreatePermissionCommand request, CancellationToken cancellationToken)
{
var tenantId = tenantProvider.GetCurrentTenantId();
var permission = new Permission
{
TenantId = tenantId,
Name = request.Name,
Code = request.Code,
Description = request.Description
};
await permissionRepository.AddAsync(permission, cancellationToken);
await permissionRepository.SaveChangesAsync(cancellationToken);
return new PermissionDto
{
Id = permission.Id,
TenantId = permission.TenantId,
Name = permission.Name,
Code = permission.Code,
Description = permission.Description
};
}
}

View File

@@ -0,0 +1,41 @@
using MediatR;
using TakeoutSaaS.Application.Identity.Commands;
using TakeoutSaaS.Application.Identity.Contracts;
using TakeoutSaaS.Domain.Identity.Entities;
using TakeoutSaaS.Domain.Identity.Repositories;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Application.Identity.Handlers;
/// <summary>
/// 创建角色处理器。
/// </summary>
public sealed class CreateRoleCommandHandler(
IRoleRepository roleRepository,
ITenantProvider tenantProvider)
: IRequestHandler<CreateRoleCommand, RoleDto>
{
public async Task<RoleDto> Handle(CreateRoleCommand request, CancellationToken cancellationToken)
{
var tenantId = tenantProvider.GetCurrentTenantId();
var role = new Role
{
TenantId = tenantId,
Name = request.Name,
Code = request.Code,
Description = request.Description
};
await roleRepository.AddAsync(role, cancellationToken);
await roleRepository.SaveChangesAsync(cancellationToken);
return new RoleDto
{
Id = role.Id,
TenantId = role.TenantId,
Name = role.Name,
Code = role.Code,
Description = role.Description
};
}
}

View File

@@ -0,0 +1,23 @@
using MediatR;
using TakeoutSaaS.Application.Identity.Commands;
using TakeoutSaaS.Domain.Identity.Repositories;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Application.Identity.Handlers;
/// <summary>
/// 删除权限处理器。
/// </summary>
public sealed class DeletePermissionCommandHandler(
IPermissionRepository permissionRepository,
ITenantProvider tenantProvider)
: IRequestHandler<DeletePermissionCommand, bool>
{
public async Task<bool> Handle(DeletePermissionCommand request, CancellationToken cancellationToken)
{
var tenantId = tenantProvider.GetCurrentTenantId();
await permissionRepository.DeleteAsync(request.PermissionId, tenantId, cancellationToken);
await permissionRepository.SaveChangesAsync(cancellationToken);
return true;
}
}

View File

@@ -0,0 +1,23 @@
using MediatR;
using TakeoutSaaS.Application.Identity.Commands;
using TakeoutSaaS.Domain.Identity.Repositories;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Application.Identity.Handlers;
/// <summary>
/// 删除角色处理器。
/// </summary>
public sealed class DeleteRoleCommandHandler(
IRoleRepository roleRepository,
ITenantProvider tenantProvider)
: IRequestHandler<DeleteRoleCommand, bool>
{
public async Task<bool> Handle(DeleteRoleCommand request, CancellationToken cancellationToken)
{
var tenantId = tenantProvider.GetCurrentTenantId();
await roleRepository.DeleteAsync(request.RoleId, tenantId, cancellationToken);
await roleRepository.SaveChangesAsync(cancellationToken);
return true;
}
}

View File

@@ -0,0 +1,54 @@
using System;
using System.Linq;
using MediatR;
using TakeoutSaaS.Application.Identity.Contracts;
using TakeoutSaaS.Application.Identity.Queries;
using TakeoutSaaS.Domain.Identity.Repositories;
using TakeoutSaaS.Shared.Abstractions.Results;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Application.Identity.Handlers;
/// <summary>
/// 权限分页查询处理器。
/// </summary>
public sealed class SearchPermissionsQueryHandler(
IPermissionRepository permissionRepository,
ITenantProvider tenantProvider)
: IRequestHandler<SearchPermissionsQuery, PagedResult<PermissionDto>>
{
public async Task<PagedResult<PermissionDto>> Handle(SearchPermissionsQuery request, CancellationToken cancellationToken)
{
var tenantId = tenantProvider.GetCurrentTenantId();
var permissions = await permissionRepository.SearchAsync(tenantId, request.Keyword, cancellationToken);
var sorted = request.SortBy?.ToLowerInvariant() switch
{
"name" => request.SortDescending
? permissions.OrderByDescending(x => x.Name)
: permissions.OrderBy(x => x.Name),
"code" => request.SortDescending
? permissions.OrderByDescending(x => x.Code)
: permissions.OrderBy(x => x.Code),
_ => request.SortDescending
? permissions.OrderByDescending(x => x.CreatedAt)
: permissions.OrderBy(x => x.CreatedAt)
};
var paged = sorted
.Skip((request.Page - 1) * request.PageSize)
.Take(request.PageSize)
.ToList();
var items = paged.Select(permission => new PermissionDto
{
Id = permission.Id,
TenantId = permission.TenantId,
Name = permission.Name,
Code = permission.Code,
Description = permission.Description
}).ToList();
return new PagedResult<PermissionDto>(items, request.Page, request.PageSize, permissions.Count);
}
}

View File

@@ -0,0 +1,51 @@
using System;
using System.Linq;
using MediatR;
using TakeoutSaaS.Application.Identity.Contracts;
using TakeoutSaaS.Application.Identity.Queries;
using TakeoutSaaS.Domain.Identity.Repositories;
using TakeoutSaaS.Shared.Abstractions.Results;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Application.Identity.Handlers;
/// <summary>
/// 角色分页查询处理器。
/// </summary>
public sealed class SearchRolesQueryHandler(
IRoleRepository roleRepository,
ITenantProvider tenantProvider)
: IRequestHandler<SearchRolesQuery, PagedResult<RoleDto>>
{
public async Task<PagedResult<RoleDto>> Handle(SearchRolesQuery request, CancellationToken cancellationToken)
{
var tenantId = tenantProvider.GetCurrentTenantId();
var roles = await roleRepository.SearchAsync(tenantId, request.Keyword, cancellationToken);
var sorted = request.SortBy?.ToLowerInvariant() switch
{
"name" => request.SortDescending
? roles.OrderByDescending(x => x.Name)
: roles.OrderBy(x => x.Name),
_ => request.SortDescending
? roles.OrderByDescending(x => x.CreatedAt)
: roles.OrderBy(x => x.CreatedAt)
};
var paged = sorted
.Skip((request.Page - 1) * request.PageSize)
.Take(request.PageSize)
.ToList();
var items = paged.Select(role => new RoleDto
{
Id = role.Id,
TenantId = role.TenantId,
Name = role.Name,
Code = role.Code,
Description = role.Description
}).ToList();
return new PagedResult<RoleDto>(items, request.Page, request.PageSize, roles.Count);
}
}

View File

@@ -0,0 +1,41 @@
using MediatR;
using TakeoutSaaS.Application.Identity.Commands;
using TakeoutSaaS.Application.Identity.Contracts;
using TakeoutSaaS.Domain.Identity.Repositories;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Application.Identity.Handlers;
/// <summary>
/// 更新权限处理器。
/// </summary>
public sealed class UpdatePermissionCommandHandler(
IPermissionRepository permissionRepository,
ITenantProvider tenantProvider)
: IRequestHandler<UpdatePermissionCommand, PermissionDto?>
{
public async Task<PermissionDto?> Handle(UpdatePermissionCommand request, CancellationToken cancellationToken)
{
var tenantId = tenantProvider.GetCurrentTenantId();
var permission = await permissionRepository.FindByIdAsync(request.PermissionId, tenantId, cancellationToken);
if (permission == null)
{
return null;
}
permission.Name = request.Name;
permission.Description = request.Description;
await permissionRepository.UpdateAsync(permission, cancellationToken);
await permissionRepository.SaveChangesAsync(cancellationToken);
return new PermissionDto
{
Id = permission.Id,
TenantId = permission.TenantId,
Name = permission.Name,
Code = permission.Code,
Description = permission.Description
};
}
}

View File

@@ -0,0 +1,41 @@
using MediatR;
using TakeoutSaaS.Application.Identity.Commands;
using TakeoutSaaS.Application.Identity.Contracts;
using TakeoutSaaS.Domain.Identity.Repositories;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Application.Identity.Handlers;
/// <summary>
/// 更新角色处理器。
/// </summary>
public sealed class UpdateRoleCommandHandler(
IRoleRepository roleRepository,
ITenantProvider tenantProvider)
: IRequestHandler<UpdateRoleCommand, RoleDto?>
{
public async Task<RoleDto?> Handle(UpdateRoleCommand request, CancellationToken cancellationToken)
{
var tenantId = tenantProvider.GetCurrentTenantId();
var role = await roleRepository.FindByIdAsync(request.RoleId, tenantId, cancellationToken);
if (role == null)
{
return null;
}
role.Name = request.Name;
role.Description = request.Description;
await roleRepository.UpdateAsync(role, cancellationToken);
await roleRepository.SaveChangesAsync(cancellationToken);
return new RoleDto
{
Id = role.Id,
TenantId = role.TenantId,
Name = role.Name,
Code = role.Code,
Description = role.Description
};
}
}

View File

@@ -0,0 +1,17 @@
using MediatR;
using TakeoutSaaS.Application.Identity.Contracts;
using TakeoutSaaS.Shared.Abstractions.Results;
namespace TakeoutSaaS.Application.Identity.Queries;
/// <summary>
/// 分页查询权限。
/// </summary>
public sealed class SearchPermissionsQuery : IRequest<PagedResult<PermissionDto>>
{
public string? Keyword { get; init; }
public int Page { get; init; } = 1;
public int PageSize { get; init; } = 20;
public string? SortBy { get; init; }
public bool SortDescending { get; init; } = true;
}

View File

@@ -0,0 +1,17 @@
using MediatR;
using TakeoutSaaS.Application.Identity.Contracts;
using TakeoutSaaS.Shared.Abstractions.Results;
namespace TakeoutSaaS.Application.Identity.Queries;
/// <summary>
/// 分页查询角色。
/// </summary>
public sealed class SearchRolesQuery : IRequest<PagedResult<RoleDto>>
{
public string? Keyword { get; init; }
public int Page { get; init; } = 1;
public int PageSize { get; init; } = 20;
public string? SortBy { get; init; }
public bool SortDescending { get; init; } = true;
}