Files
TakeoutSaaS.TenantApi/src/Application/TakeoutSaaS.Application/App/Orders/Handlers/SearchOrderAllListQueryHandler.cs
MSuMshk 502f80473e
All checks were successful
Build and Deploy TenantApi + SkuWorker / build-and-deploy (push) Successful in 1m50s
fix(order): move all-orders list to database pagination
2026-02-27 10:32:21 +08:00

114 lines
3.9 KiB
C#

using MediatR;
using TakeoutSaaS.Application.App.Orders.Dto;
using TakeoutSaaS.Application.App.Orders.Queries;
using TakeoutSaaS.Domain.Orders.Entities;
using TakeoutSaaS.Domain.Orders.Enums;
using TakeoutSaaS.Domain.Orders.Repositories;
using TakeoutSaaS.Shared.Abstractions.Results;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Application.App.Orders.Handlers;
/// <summary>
/// 全部订单列表查询处理器。
/// </summary>
public sealed class SearchOrderAllListQueryHandler(
IOrderRepository orderRepository,
ITenantProvider tenantProvider)
: IRequestHandler<SearchOrderAllListQuery, PagedResult<OrderAllListItemDto>>
{
/// <inheritdoc />
public async Task<PagedResult<OrderAllListItemDto>> Handle(SearchOrderAllListQuery request, CancellationToken cancellationToken)
{
var tenantId = tenantProvider.GetCurrentTenantId();
var page = Math.Max(1, request.Page);
var pageSize = Math.Clamp(request.PageSize, 1, 200);
var (pagedOrders, totalCount) = await orderRepository.SearchAllOrdersPageAsync(
tenantId,
request.StoreId,
request.StartAt,
request.EndAt,
request.Status,
request.RefundedOnly,
request.DeliveryType,
request.PaymentMethod,
request.Keyword,
request.SortBy,
request.SortDescending,
page,
pageSize,
cancellationToken);
var refundedSet = await LoadRefundedOrderIdsAsync(
pagedOrders.Select(order => order.Id).ToList(),
tenantId,
cancellationToken);
var itemsLookup = await orderRepository.GetItemsByOrderIdsAsync(
pagedOrders.Select(order => order.Id).ToList(),
tenantId,
cancellationToken);
var rows = pagedOrders
.Select(order =>
{
var isRefunded = refundedSet.Contains(order.Id);
return new OrderAllListItemDto
{
OrderId = order.Id,
OrderNo = order.OrderNo,
OrderedAt = order.CreatedAt,
DeliveryType = order.DeliveryType,
Status = order.Status,
CustomerName = string.IsNullOrWhiteSpace(order.CustomerName) ? "--" : order.CustomerName,
ItemsSummary = BuildItemSummary(order.Id, itemsLookup),
Amount = ResolveDisplayAmount(order),
IsRefunded = isRefunded,
IsDimmed = order.Status == OrderStatus.Cancelled || isRefunded
};
})
.ToList();
return new PagedResult<OrderAllListItemDto>(rows, page, pageSize, totalCount);
}
private async Task<HashSet<long>> LoadRefundedOrderIdsAsync(
IReadOnlyCollection<long> orderIds,
long tenantId,
CancellationToken cancellationToken)
{
if (orderIds.Count == 0)
{
return [];
}
var refunded = await orderRepository.GetRefundedOrderIdsAsync(orderIds, tenantId, cancellationToken);
return refunded.ToHashSet();
}
private static decimal ResolveDisplayAmount(Order order)
{
return order.PaidAmount > 0 ? order.PaidAmount : order.PayableAmount;
}
private static string BuildItemSummary(
long orderId,
IReadOnlyDictionary<long, IReadOnlyList<OrderItem>> itemsLookup)
{
if (!itemsLookup.TryGetValue(orderId, out var items) || items.Count == 0)
{
return "--";
}
var first = string.IsNullOrWhiteSpace(items[0].ProductName) ? "商品" : items[0].ProductName.Trim();
var totalQuantity = items.Sum(item => Math.Max(0, item.Quantity));
if (totalQuantity <= 0)
{
totalQuantity = items.Count;
}
return $"{first}等{totalQuantity}件";
}
}