feat: 完成租户个人中心 API 首版实现

This commit is contained in:
2026-02-09 20:01:11 +08:00
parent f61554fc08
commit 2711893474
53 changed files with 2547 additions and 0 deletions

View File

@@ -0,0 +1,245 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MediatR;
using TakeoutSaaS.Application.App.Personal.Dto;
using TakeoutSaaS.Application.App.Personal.Queries;
using TakeoutSaaS.Shared.Abstractions.Results;
using TakeoutSaaS.Shared.Web.Api;
namespace TakeoutSaaS.TenantApi.Controllers;
/// <summary>
/// 租户端个人中心。
/// </summary>
/// <remarks>
/// 提供个人总览、角色概览、配额、账单、支付、操作记录与消息摘要能力。
/// </remarks>
[ApiVersion("1.0")]
[Authorize]
[Produces("application/json")]
[Route("api/tenant/v{version:apiVersion}/personal")]
public sealed class PersonalController(IMediator mediator) : BaseApiController
{
/// <summary>
/// 获取个人中心总览。
/// </summary>
/// <param name="cancellationToken">取消标记。</param>
/// <returns>总览结果。</returns>
[HttpGet("overview")]
[ProducesResponseType(typeof(ApiResponse<PersonalOverviewDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<PersonalOverviewDto>), StatusCodes.Status401Unauthorized)]
public async Task<ApiResponse<PersonalOverviewDto>> GetOverview(CancellationToken cancellationToken)
{
// 1. 查询总览
var overview = await mediator.Send(new GetPersonalOverviewQuery(), cancellationToken);
// 2. 返回结果
return ApiResponse<PersonalOverviewDto>.Ok(overview);
}
/// <summary>
/// 获取我的角色与权限概览。
/// </summary>
/// <param name="cancellationToken">取消标记。</param>
/// <returns>角色权限概览。</returns>
[HttpGet("roles")]
[ProducesResponseType(typeof(ApiResponse<PersonalRolePermissionSummaryDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<PersonalRolePermissionSummaryDto>), StatusCodes.Status401Unauthorized)]
public async Task<ApiResponse<PersonalRolePermissionSummaryDto>> GetRoles(CancellationToken cancellationToken)
{
// 1. 查询角色权限概览
var summary = await mediator.Send(new GetPersonalRolesQuery(), cancellationToken);
// 2. 返回结果
return ApiResponse<PersonalRolePermissionSummaryDto>.Ok(summary);
}
/// <summary>
/// 获取套餐与配额摘要。
/// </summary>
/// <param name="cancellationToken">取消标记。</param>
/// <returns>配额摘要。</returns>
[HttpGet("quota")]
[ProducesResponseType(typeof(ApiResponse<PersonalQuotaUsageSummaryDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<PersonalQuotaUsageSummaryDto>), StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(ApiResponse<PersonalQuotaUsageSummaryDto>), StatusCodes.Status403Forbidden)]
public async Task<ApiResponse<PersonalQuotaUsageSummaryDto>> GetQuota(CancellationToken cancellationToken)
{
// 1. 查询配额摘要
var summary = await mediator.Send(new GetPersonalQuotaQuery(), cancellationToken);
// 2. 返回结果
return ApiResponse<PersonalQuotaUsageSummaryDto>.Ok(summary);
}
/// <summary>
/// 分页查询账单记录。
/// </summary>
/// <param name="page">页码(从 1 开始)。</param>
/// <param name="pageSize">每页条数。</param>
/// <param name="from">开始时间UTC。</param>
/// <param name="to">结束时间UTC。</param>
/// <param name="cancellationToken">取消标记。</param>
/// <returns>账单分页结果。</returns>
[HttpGet("billing/statements")]
[ProducesResponseType(typeof(ApiResponse<PagedResult<PersonalBillingStatementDto>>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<PagedResult<PersonalBillingStatementDto>>), StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(ApiResponse<PagedResult<PersonalBillingStatementDto>>), StatusCodes.Status403Forbidden)]
[ProducesResponseType(typeof(ApiResponse<PagedResult<PersonalBillingStatementDto>>), StatusCodes.Status422UnprocessableEntity)]
public async Task<ApiResponse<PagedResult<PersonalBillingStatementDto>>> SearchBillingStatements(
[FromQuery] int page = 1,
[FromQuery] int pageSize = 20,
[FromQuery] DateTime? from = null,
[FromQuery] DateTime? to = null,
CancellationToken cancellationToken = default)
{
// 1. 发送查询
var result = await mediator.Send(new SearchPersonalBillingStatementsQuery
{
Page = page,
PageSize = pageSize,
From = from,
To = to
}, cancellationToken);
// 2. 返回分页结果
return ApiResponse<PagedResult<PersonalBillingStatementDto>>.Ok(result);
}
/// <summary>
/// 分页查询支付记录。
/// </summary>
/// <param name="page">页码(从 1 开始)。</param>
/// <param name="pageSize">每页条数。</param>
/// <param name="from">开始时间UTC。</param>
/// <param name="to">结束时间UTC。</param>
/// <param name="cancellationToken">取消标记。</param>
/// <returns>支付记录分页结果。</returns>
[HttpGet("billing/payments")]
[ProducesResponseType(typeof(ApiResponse<PagedResult<PersonalPaymentRecordDto>>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<PagedResult<PersonalPaymentRecordDto>>), StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(ApiResponse<PagedResult<PersonalPaymentRecordDto>>), StatusCodes.Status403Forbidden)]
[ProducesResponseType(typeof(ApiResponse<PagedResult<PersonalPaymentRecordDto>>), StatusCodes.Status422UnprocessableEntity)]
public async Task<ApiResponse<PagedResult<PersonalPaymentRecordDto>>> SearchPayments(
[FromQuery] int page = 1,
[FromQuery] int pageSize = 20,
[FromQuery] DateTime? from = null,
[FromQuery] DateTime? to = null,
CancellationToken cancellationToken = default)
{
// 1. 发送查询
var result = await mediator.Send(new SearchPersonalPaymentsQuery
{
Page = page,
PageSize = pageSize,
From = from,
To = to
}, cancellationToken);
// 2. 返回分页结果
return ApiResponse<PagedResult<PersonalPaymentRecordDto>>.Ok(result);
}
/// <summary>
/// 获取账单/配额可见角色配置。
/// </summary>
/// <param name="cancellationToken">取消标记。</param>
/// <returns>可见角色配置。</returns>
[HttpGet("visibility/roles")]
[ProducesResponseType(typeof(ApiResponse<PersonalVisibilityRoleConfigDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<PersonalVisibilityRoleConfigDto>), StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(ApiResponse<PersonalVisibilityRoleConfigDto>), StatusCodes.Status403Forbidden)]
public async Task<ApiResponse<PersonalVisibilityRoleConfigDto>> GetVisibilityRoles(CancellationToken cancellationToken)
{
// 1. 查询配置
var config = await mediator.Send(new GetPersonalVisibilityRoleConfigQuery(), cancellationToken);
// 2. 返回结果
return ApiResponse<PersonalVisibilityRoleConfigDto>.Ok(config);
}
/// <summary>
/// 更新账单/配额可见角色配置。
/// </summary>
/// <param name="command">更新请求。</param>
/// <param name="cancellationToken">取消标记。</param>
/// <returns>更新后的配置。</returns>
[HttpPut("visibility/roles")]
[ProducesResponseType(typeof(ApiResponse<PersonalVisibilityRoleConfigDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<PersonalVisibilityRoleConfigDto>), StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(ApiResponse<PersonalVisibilityRoleConfigDto>), StatusCodes.Status403Forbidden)]
[ProducesResponseType(typeof(ApiResponse<PersonalVisibilityRoleConfigDto>), StatusCodes.Status422UnprocessableEntity)]
public async Task<ApiResponse<PersonalVisibilityRoleConfigDto>> UpdateVisibilityRoles(
[FromBody] UpdatePersonalVisibilityRoleConfigCommand command,
CancellationToken cancellationToken)
{
// 1. 更新配置
var config = await mediator.Send(command, cancellationToken);
// 2. 返回结果
return ApiResponse<PersonalVisibilityRoleConfigDto>.Ok(config);
}
/// <summary>
/// 分页查询个人操作记录。
/// </summary>
/// <param name="page">页码(从 1 开始)。</param>
/// <param name="pageSize">每页条数(默认 50最大 50。</param>
/// <param name="from">开始时间UTC。</param>
/// <param name="to">结束时间UTC。</param>
/// <param name="cancellationToken">取消标记。</param>
/// <returns>操作记录分页结果。</returns>
[HttpGet("operations")]
[ProducesResponseType(typeof(ApiResponse<PagedResult<PersonalOperationLogDto>>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<PagedResult<PersonalOperationLogDto>>), StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(ApiResponse<PagedResult<PersonalOperationLogDto>>), StatusCodes.Status422UnprocessableEntity)]
public async Task<ApiResponse<PagedResult<PersonalOperationLogDto>>> SearchOperations(
[FromQuery] int page = 1,
[FromQuery] int pageSize = 50,
[FromQuery] DateTime? from = null,
[FromQuery] DateTime? to = null,
CancellationToken cancellationToken = default)
{
// 1. 发送查询
var result = await mediator.Send(new SearchPersonalOperationsQuery
{
Page = page,
PageSize = pageSize,
From = from,
To = to
}, cancellationToken);
// 2. 返回分页结果
return ApiResponse<PagedResult<PersonalOperationLogDto>>.Ok(result);
}
/// <summary>
/// 分页查询个人消息摘要。
/// </summary>
/// <param name="page">页码(从 1 开始)。</param>
/// <param name="pageSize">每页条数。</param>
/// <param name="unreadOnly">是否仅返回未读。</param>
/// <param name="cancellationToken">取消标记。</param>
/// <returns>消息摘要分页结果。</returns>
[HttpGet("notifications")]
[ProducesResponseType(typeof(ApiResponse<PagedResult<PersonalNotificationDto>>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<PagedResult<PersonalNotificationDto>>), StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(ApiResponse<PagedResult<PersonalNotificationDto>>), StatusCodes.Status422UnprocessableEntity)]
public async Task<ApiResponse<PagedResult<PersonalNotificationDto>>> SearchNotifications(
[FromQuery] int page = 1,
[FromQuery] int pageSize = 20,
[FromQuery] bool unreadOnly = false,
CancellationToken cancellationToken = default)
{
// 1. 发送查询
var result = await mediator.Send(new SearchPersonalNotificationsQuery
{
Page = page,
PageSize = pageSize,
UnreadOnly = unreadOnly
}, cancellationToken);
// 2. 返回分页结果
return ApiResponse<PagedResult<PersonalNotificationDto>>.Ok(result);
}
}