refactor: 移除租户端控制器
This commit is contained in:
@@ -12,7 +12,7 @@ using TakeoutSaaS.Shared.Web.Api;
|
||||
namespace TakeoutSaaS.AdminApi.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// 字典标签覆盖管理。
|
||||
/// 字典标签覆盖管理(平台端)。
|
||||
/// </summary>
|
||||
[ApiVersion("1.0")]
|
||||
[Authorize]
|
||||
@@ -22,85 +22,6 @@ public sealed class DictionaryLabelOverridesController(
|
||||
ICurrentUserAccessor currentUserAccessor)
|
||||
: BaseApiController
|
||||
{
|
||||
#region 租户端 API(租户覆盖系统字典)
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前租户的标签覆盖列表。
|
||||
/// </summary>
|
||||
[HttpGet("~/api/admin/v{version:apiVersion}/tenants/{tenantId:long}/dictionary/label-overrides")]
|
||||
[PermissionAuthorize("dictionary:override:read")]
|
||||
[ProducesResponseType(typeof(ApiResponse<IReadOnlyList<LabelOverrideDto>>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<IReadOnlyList<LabelOverrideDto>>> ListTenantOverrides(
|
||||
long tenantId,
|
||||
[FromQuery] OverrideType? overrideType,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 校验租户标识
|
||||
if (tenantId <= 0)
|
||||
{
|
||||
return ApiResponse<IReadOnlyList<LabelOverrideDto>>.Error(StatusCodes.Status400BadRequest, "租户标识无效");
|
||||
}
|
||||
|
||||
// 2. (空行后) 查询租户覆盖列表
|
||||
var result = await labelOverrideService.GetOverridesAsync(tenantId, overrideType, cancellationToken);
|
||||
return ApiResponse<IReadOnlyList<LabelOverrideDto>>.Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 租户覆盖系统字典项的标签。
|
||||
/// </summary>
|
||||
[HttpPost("~/api/admin/v{version:apiVersion}/tenants/{tenantId:long}/dictionary/label-overrides")]
|
||||
[PermissionAuthorize("dictionary:override:update")]
|
||||
[ProducesResponseType(typeof(ApiResponse<LabelOverrideDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<LabelOverrideDto>> CreateTenantOverride(
|
||||
long tenantId,
|
||||
[FromBody] UpsertLabelOverrideRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 校验租户标识
|
||||
if (tenantId <= 0)
|
||||
{
|
||||
return ApiResponse<LabelOverrideDto>.Error(StatusCodes.Status400BadRequest, "租户标识无效");
|
||||
}
|
||||
|
||||
// 2. (空行后) 执行租户覆盖
|
||||
var operatorId = currentUserAccessor.UserId;
|
||||
var result = await labelOverrideService.UpsertTenantOverrideAsync(tenantId, request, operatorId, cancellationToken);
|
||||
return ApiResponse<LabelOverrideDto>.Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 租户删除自己的标签覆盖。
|
||||
/// </summary>
|
||||
[HttpDelete("~/api/admin/v{version:apiVersion}/tenants/{tenantId:long}/dictionary/label-overrides/{dictionaryItemId:long}")]
|
||||
[PermissionAuthorize("dictionary:override:delete")]
|
||||
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status404NotFound)]
|
||||
public async Task<ApiResponse<object>> DeleteTenantOverride(long tenantId, long dictionaryItemId, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 校验租户标识
|
||||
if (tenantId <= 0)
|
||||
{
|
||||
return ApiResponse<object>.Error(StatusCodes.Status400BadRequest, "租户标识无效");
|
||||
}
|
||||
|
||||
// 2. (空行后) 执行删除
|
||||
var operatorId = currentUserAccessor.UserId;
|
||||
var success = await labelOverrideService.DeleteOverrideAsync(
|
||||
tenantId,
|
||||
dictionaryItemId,
|
||||
operatorId,
|
||||
allowPlatformEnforcement: false,
|
||||
cancellationToken);
|
||||
return success
|
||||
? ApiResponse.Success()
|
||||
: ApiResponse.Error(ErrorCodes.NotFound, "覆盖配置不存在");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 平台端 API(平台管理所有租户的覆盖)
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定租户的所有标签覆盖(平台管理员用)。
|
||||
/// </summary>
|
||||
@@ -154,6 +75,4 @@ public sealed class DictionaryLabelOverridesController(
|
||||
? ApiResponse.Success()
|
||||
: ApiResponse.Error(ErrorCodes.NotFound, "覆盖配置不存在");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using TakeoutSaaS.Application.Dictionary.Contracts;
|
||||
using TakeoutSaaS.Application.Dictionary.Models;
|
||||
using TakeoutSaaS.Application.Dictionary.Services;
|
||||
using TakeoutSaaS.Module.Authorization.Attributes;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
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}/tenants/{tenantId:long}/dictionary/overrides")]
|
||||
public sealed class DictionaryOverridesController(
|
||||
DictionaryOverrideService overrideService)
|
||||
: BaseApiController
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取当前租户的覆盖配置列表。
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[PermissionAuthorize("dictionary:override:read")]
|
||||
[ProducesResponseType(typeof(ApiResponse<IReadOnlyList<OverrideConfigDto>>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<IReadOnlyList<OverrideConfigDto>>> List(long tenantId, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 校验租户标识
|
||||
if (tenantId <= 0)
|
||||
{
|
||||
return ApiResponse<IReadOnlyList<OverrideConfigDto>>.Error(StatusCodes.Status400BadRequest, "租户标识无效");
|
||||
}
|
||||
|
||||
// 2. (空行后) 查询覆盖配置
|
||||
var result = await overrideService.GetOverridesAsync(tenantId, cancellationToken);
|
||||
return ApiResponse<IReadOnlyList<OverrideConfigDto>>.Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定字典分组的覆盖配置。
|
||||
/// </summary>
|
||||
[HttpGet("{groupCode}")]
|
||||
[PermissionAuthorize("dictionary:override:read")]
|
||||
[ProducesResponseType(typeof(ApiResponse<OverrideConfigDto>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status404NotFound)]
|
||||
public async Task<ApiResponse<OverrideConfigDto>> Detail(long tenantId, string groupCode, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 校验租户标识
|
||||
if (tenantId <= 0)
|
||||
{
|
||||
return ApiResponse<OverrideConfigDto>.Error(StatusCodes.Status400BadRequest, "租户标识无效");
|
||||
}
|
||||
|
||||
// 2. (空行后) 查询覆盖配置
|
||||
var result = await overrideService.GetOverrideAsync(tenantId, groupCode, cancellationToken);
|
||||
return result == null
|
||||
? ApiResponse<OverrideConfigDto>.Error(ErrorCodes.NotFound, "覆盖配置不存在")
|
||||
: ApiResponse<OverrideConfigDto>.Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启用覆盖模式。
|
||||
/// </summary>
|
||||
[HttpPost("{groupCode}/enable")]
|
||||
[PermissionAuthorize("dictionary:override:update")]
|
||||
[ProducesResponseType(typeof(ApiResponse<OverrideConfigDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<OverrideConfigDto>> Enable(long tenantId, string groupCode, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 校验租户标识
|
||||
if (tenantId <= 0)
|
||||
{
|
||||
return ApiResponse<OverrideConfigDto>.Error(StatusCodes.Status400BadRequest, "租户标识无效");
|
||||
}
|
||||
|
||||
// 2. (空行后) 启用覆盖模式
|
||||
var result = await overrideService.EnableOverrideAsync(tenantId, groupCode, cancellationToken);
|
||||
return ApiResponse<OverrideConfigDto>.Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 禁用覆盖模式。
|
||||
/// </summary>
|
||||
[HttpPost("{groupCode}/disable")]
|
||||
[PermissionAuthorize("dictionary:override:update")]
|
||||
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status404NotFound)]
|
||||
public async Task<ApiResponse<object>> Disable(long tenantId, string groupCode, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 校验租户标识
|
||||
if (tenantId <= 0)
|
||||
{
|
||||
return ApiResponse<object>.Error(StatusCodes.Status400BadRequest, "租户标识无效");
|
||||
}
|
||||
|
||||
// 2. (空行后) 禁用覆盖模式
|
||||
var success = await overrideService.DisableOverrideAsync(tenantId, groupCode, cancellationToken);
|
||||
return success
|
||||
? ApiResponse.Success()
|
||||
: ApiResponse.Error(ErrorCodes.NotFound, "覆盖配置不存在");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新隐藏的系统字典项。
|
||||
/// </summary>
|
||||
[HttpPut("{groupCode}/hidden-items")]
|
||||
[PermissionAuthorize("dictionary:override:update")]
|
||||
[ProducesResponseType(typeof(ApiResponse<OverrideConfigDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<OverrideConfigDto>> UpdateHiddenItems(
|
||||
long tenantId,
|
||||
string groupCode,
|
||||
[FromBody] DictionaryOverrideHiddenItemsRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 校验租户标识
|
||||
if (tenantId <= 0)
|
||||
{
|
||||
return ApiResponse<OverrideConfigDto>.Error(StatusCodes.Status400BadRequest, "租户标识无效");
|
||||
}
|
||||
|
||||
// 2. (空行后) 更新隐藏项
|
||||
var result = await overrideService.UpdateHiddenItemsAsync(tenantId, groupCode, request.HiddenItemIds, cancellationToken);
|
||||
return ApiResponse<OverrideConfigDto>.Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新自定义排序。
|
||||
/// </summary>
|
||||
[HttpPut("{groupCode}/sort-order")]
|
||||
[PermissionAuthorize("dictionary:override:update")]
|
||||
[ProducesResponseType(typeof(ApiResponse<OverrideConfigDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<OverrideConfigDto>> UpdateSortOrder(
|
||||
long tenantId,
|
||||
string groupCode,
|
||||
[FromBody] DictionaryOverrideSortOrderRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 校验租户标识
|
||||
if (tenantId <= 0)
|
||||
{
|
||||
return ApiResponse<OverrideConfigDto>.Error(StatusCodes.Status400BadRequest, "租户标识无效");
|
||||
}
|
||||
|
||||
// 2. (空行后) 更新自定义排序
|
||||
var result = await overrideService.UpdateCustomSortOrderAsync(tenantId, groupCode, request.SortOrder, cancellationToken);
|
||||
return ApiResponse<OverrideConfigDto>.Ok(result);
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using TakeoutSaaS.AdminApi.Contracts.Requests;
|
||||
using TakeoutSaaS.Application.App.Tenants.Commands;
|
||||
using TakeoutSaaS.Application.App.Tenants.Dto;
|
||||
using TakeoutSaaS.Application.App.Tenants.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}/tenants/{tenantId:long}/billings")]
|
||||
public sealed class TenantBillingsController(IMediator mediator) : BaseApiController
|
||||
{
|
||||
/// <summary>
|
||||
/// 分页查询账单。
|
||||
/// </summary>
|
||||
/// <returns>租户账单分页结果。</returns>
|
||||
[HttpGet]
|
||||
[PermissionAuthorize("tenant-bill:read")]
|
||||
[ProducesResponseType(typeof(ApiResponse<PagedResult<TenantBillingDto>>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<PagedResult<TenantBillingDto>>> Search(long tenantId, [FromQuery] SearchTenantBillsRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 组装查询对象(TenantId 仅来自路由,避免与 QueryString 重复)
|
||||
var query = new SearchTenantBillsQuery
|
||||
{
|
||||
TenantId = tenantId,
|
||||
Status = request.Status,
|
||||
From = request.From,
|
||||
To = request.To,
|
||||
Page = request.Page,
|
||||
PageSize = request.PageSize,
|
||||
};
|
||||
|
||||
// 2. 查询账单列表
|
||||
var result = await mediator.Send(query, cancellationToken);
|
||||
|
||||
// 3. 返回分页结果
|
||||
return ApiResponse<PagedResult<TenantBillingDto>>.Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 账单详情。
|
||||
/// </summary>
|
||||
/// <returns>租户账单详情。</returns>
|
||||
[HttpGet("{billingId:long}")]
|
||||
[PermissionAuthorize("tenant-bill:read")]
|
||||
[ProducesResponseType(typeof(ApiResponse<TenantBillingDto>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ApiResponse<TenantBillingDto>), StatusCodes.Status404NotFound)]
|
||||
public async Task<ApiResponse<TenantBillingDto>> Detail(long tenantId, long billingId, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 查询账单详情
|
||||
var result = await mediator.Send(new GetTenantBillQuery { TenantId = tenantId, BillingId = billingId }, cancellationToken);
|
||||
|
||||
// 2. 返回详情或 404
|
||||
return result is null
|
||||
? ApiResponse<TenantBillingDto>.Error(StatusCodes.Status404NotFound, "账单不存在")
|
||||
: ApiResponse<TenantBillingDto>.Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建账单。
|
||||
/// </summary>
|
||||
/// <returns>创建的账单信息。</returns>
|
||||
[HttpPost]
|
||||
[PermissionAuthorize("tenant-bill:create")]
|
||||
[ProducesResponseType(typeof(ApiResponse<TenantBillingDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<TenantBillingDto>> Create(long tenantId, [FromBody, Required] CreateTenantBillingCommand command, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 绑定租户标识
|
||||
command = command with { TenantId = tenantId };
|
||||
|
||||
// 2. 创建账单
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
return ApiResponse<TenantBillingDto>.Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标记账单已支付。
|
||||
/// </summary>
|
||||
/// <returns>标记支付后的账单信息。</returns>
|
||||
[HttpPost("{billingId:long}/pay")]
|
||||
[PermissionAuthorize("tenant-bill:pay")]
|
||||
[ProducesResponseType(typeof(ApiResponse<TenantBillingDto>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ApiResponse<TenantBillingDto>), StatusCodes.Status404NotFound)]
|
||||
public async Task<ApiResponse<TenantBillingDto>> MarkPaid(long tenantId, long billingId, [FromBody, Required] MarkTenantBillingPaidCommand command, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 绑定租户与账单标识
|
||||
command = command with { TenantId = tenantId, BillingId = billingId };
|
||||
|
||||
// 2. 标记支付状态
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
|
||||
// 3. 返回结果或 404
|
||||
return result is null
|
||||
? ApiResponse<TenantBillingDto>.Error(StatusCodes.Status404NotFound, "账单不存在")
|
||||
: ApiResponse<TenantBillingDto>.Ok(result);
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using TakeoutSaaS.Application.App.Tenants.Commands;
|
||||
using TakeoutSaaS.Application.App.Tenants.Dto;
|
||||
using TakeoutSaaS.Application.App.Tenants.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}/tenants/{tenantId:long}/notifications")]
|
||||
public sealed class TenantNotificationsController(IMediator mediator) : BaseApiController
|
||||
{
|
||||
/// <summary>
|
||||
/// 分页查询通知。
|
||||
/// </summary>
|
||||
/// <returns>租户通知分页结果。</returns>
|
||||
[HttpGet]
|
||||
[PermissionAuthorize("tenant-notification:read")]
|
||||
[ProducesResponseType(typeof(ApiResponse<PagedResult<TenantNotificationDto>>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<PagedResult<TenantNotificationDto>>> Search(long tenantId, [FromQuery] SearchTenantNotificationsQuery query, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 绑定租户标识
|
||||
query = query with { TenantId = tenantId };
|
||||
|
||||
// 2. 查询通知列表
|
||||
var result = await mediator.Send(query, cancellationToken);
|
||||
|
||||
// 3. 返回分页结果
|
||||
return ApiResponse<PagedResult<TenantNotificationDto>>.Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标记通知已读。
|
||||
/// </summary>
|
||||
/// <returns>标记已读后的通知信息。</returns>
|
||||
[HttpPost("{notificationId:long}/read")]
|
||||
[PermissionAuthorize("tenant-notification:update")]
|
||||
[ProducesResponseType(typeof(ApiResponse<TenantNotificationDto>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ApiResponse<TenantNotificationDto>), StatusCodes.Status404NotFound)]
|
||||
public async Task<ApiResponse<TenantNotificationDto>> MarkRead(long tenantId, long notificationId, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 标记通知为已读
|
||||
var result = await mediator.Send(new MarkTenantNotificationReadCommand { TenantId = tenantId, NotificationId = notificationId }, cancellationToken);
|
||||
|
||||
// 2. 返回结果或 404
|
||||
return result is null
|
||||
? ApiResponse<TenantNotificationDto>.Error(StatusCodes.Status404NotFound, "通知不存在")
|
||||
: ApiResponse<TenantNotificationDto>.Ok(result);
|
||||
}
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
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}/tenants/{tenantId:long}/roles")]
|
||||
public sealed class TenantRolesController(IMediator mediator) : BaseApiController
|
||||
{
|
||||
/// <summary>
|
||||
/// 租户角色分页。
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[PermissionAuthorize("identity:role:read")]
|
||||
[ProducesResponseType(typeof(ApiResponse<PagedResult<RoleDto>>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<PagedResult<RoleDto>>> List(
|
||||
long tenantId,
|
||||
[FromQuery] SearchRolesQuery query,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 绑定租户并查询角色分页
|
||||
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);
|
||||
|
||||
// 2. 返回分页数据
|
||||
return ApiResponse<PagedResult<RoleDto>>.Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 角色详情(含权限)。
|
||||
/// </summary>
|
||||
[HttpGet("{roleId:long}")]
|
||||
[PermissionAuthorize("identity:role:read")]
|
||||
[ProducesResponseType(typeof(ApiResponse<RoleDetailDto>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ApiResponse<RoleDetailDto>), StatusCodes.Status404NotFound)]
|
||||
public async Task<ApiResponse<RoleDetailDto>> Detail(long tenantId, long roleId, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 查询角色详情
|
||||
var result = await mediator.Send(new RoleDetailQuery { RoleId = roleId, TenantId = tenantId }, cancellationToken);
|
||||
|
||||
// 2. 返回数据或 404
|
||||
return result is null
|
||||
? ApiResponse<RoleDetailDto>.Error(StatusCodes.Status404NotFound, "角色不存在")
|
||||
: ApiResponse<RoleDetailDto>.Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建角色。
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[PermissionAuthorize("identity:role:create")]
|
||||
[ProducesResponseType(typeof(ApiResponse<RoleDto>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<RoleDto>> Create(
|
||||
long tenantId,
|
||||
[FromBody, Required] CreateRoleCommand command,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 创建角色
|
||||
var result = await mediator.Send(command with { TenantId = tenantId }, cancellationToken);
|
||||
|
||||
// 2. 返回创建结果
|
||||
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 tenantId,
|
||||
long roleId,
|
||||
[FromBody, Required] UpdateRoleCommand command,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 绑定角色 ID
|
||||
command = command with { RoleId = roleId, TenantId = tenantId };
|
||||
|
||||
// 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>
|
||||
[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 DeleteRoleCommand { RoleId = roleId, TenantId = tenantId };
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
|
||||
// 2. 返回结果
|
||||
return ApiResponse<bool>.Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取角色权限列表。
|
||||
/// </summary>
|
||||
[HttpGet("{roleId:long}/permissions")]
|
||||
[PermissionAuthorize("identity:role:read")]
|
||||
[ProducesResponseType(typeof(ApiResponse<IReadOnlyList<PermissionDto>>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ApiResponse<IReadOnlyList<PermissionDto>>), StatusCodes.Status404NotFound)]
|
||||
public async Task<ApiResponse<IReadOnlyList<PermissionDto>>> GetPermissions(
|
||||
long tenantId,
|
||||
long roleId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 查询角色详情并提取权限
|
||||
var detail = await mediator.Send(new RoleDetailQuery { RoleId = roleId, TenantId = tenantId }, cancellationToken);
|
||||
if (detail is null)
|
||||
{
|
||||
return ApiResponse<IReadOnlyList<PermissionDto>>.Error(StatusCodes.Status404NotFound, "角色不存在");
|
||||
}
|
||||
|
||||
// 2. 返回权限集合
|
||||
return ApiResponse<IReadOnlyList<PermissionDto>>.Ok(detail.Permissions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 覆盖角色权限。
|
||||
/// </summary>
|
||||
[HttpPut("{roleId:long}/permissions")]
|
||||
[PermissionAuthorize("identity:role:bind-permission")]
|
||||
[ProducesResponseType(typeof(ApiResponse<bool>), StatusCodes.Status200OK)]
|
||||
public async Task<ApiResponse<bool>> BindPermissions(
|
||||
long tenantId,
|
||||
long roleId,
|
||||
[FromBody, Required] BindRolePermissionsCommand command,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 绑定角色 ID
|
||||
command = command with { RoleId = roleId, TenantId = tenantId };
|
||||
|
||||
// 2. 覆盖授权
|
||||
var result = await mediator.Send(command, cancellationToken);
|
||||
return ApiResponse<bool>.Ok(result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user