feat(order): add all-orders APIs and query workflow
All checks were successful
Build and Deploy TenantApi + SkuWorker / build-and-deploy (push) Successful in 1m51s
All checks were successful
Build and Deploy TenantApi + SkuWorker / build-and-deploy (push) Successful in 1m51s
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TakeoutSaaS.Domain.Deliveries.Entities;
|
||||
using TakeoutSaaS.Domain.Orders.Entities;
|
||||
using TakeoutSaaS.Domain.Orders.Enums;
|
||||
using TakeoutSaaS.Domain.Orders.Repositories;
|
||||
using TakeoutSaaS.Domain.Payments.Entities;
|
||||
using TakeoutSaaS.Domain.Payments.Enums;
|
||||
using TakeoutSaaS.Infrastructure.App.Persistence;
|
||||
|
||||
@@ -57,6 +59,70 @@ public sealed class EfOrderRepository(TakeoutAppDbContext context) : IOrderRepos
|
||||
return orders;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<Order>> SearchAllOrdersAsync(
|
||||
long tenantId,
|
||||
long? storeId,
|
||||
DateTime? startAt,
|
||||
DateTime? endAt,
|
||||
OrderStatus? status,
|
||||
DeliveryType? deliveryType,
|
||||
PaymentMethod? paymentMethod,
|
||||
string? keyword,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = context.Orders
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId);
|
||||
|
||||
if (storeId.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.StoreId == storeId.Value);
|
||||
}
|
||||
|
||||
if (startAt.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.CreatedAt >= startAt.Value);
|
||||
}
|
||||
|
||||
if (endAt.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.CreatedAt < endAt.Value);
|
||||
}
|
||||
|
||||
if (status.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.Status == status.Value);
|
||||
}
|
||||
|
||||
if (deliveryType.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.DeliveryType == deliveryType.Value);
|
||||
}
|
||||
|
||||
if (paymentMethod.HasValue)
|
||||
{
|
||||
query = query.Where(x =>
|
||||
context.PaymentRecords.Any(record =>
|
||||
record.TenantId == tenantId &&
|
||||
record.OrderId == x.Id &&
|
||||
record.Method == paymentMethod.Value));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(keyword))
|
||||
{
|
||||
var normalized = keyword.Trim();
|
||||
query = query.Where(x =>
|
||||
x.OrderNo.Contains(normalized) ||
|
||||
(x.CustomerPhone != null && x.CustomerPhone.Contains(normalized)));
|
||||
}
|
||||
|
||||
return await query
|
||||
.OrderByDescending(x => x.CreatedAt)
|
||||
.ThenByDescending(x => x.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<OrderItem>> GetItemsAsync(long orderId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -69,6 +135,29 @@ public sealed class EfOrderRepository(TakeoutAppDbContext context) : IOrderRepos
|
||||
return items;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyDictionary<long, IReadOnlyList<OrderItem>>> GetItemsByOrderIdsAsync(
|
||||
IReadOnlyCollection<long> orderIds,
|
||||
long tenantId,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (orderIds.Count == 0)
|
||||
{
|
||||
return new Dictionary<long, IReadOnlyList<OrderItem>>();
|
||||
}
|
||||
|
||||
var items = await context.OrderItems
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && orderIds.Contains(x.OrderId))
|
||||
.OrderBy(x => x.OrderId)
|
||||
.ThenBy(x => x.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return items
|
||||
.GroupBy(item => item.OrderId)
|
||||
.ToDictionary(group => group.Key, group => (IReadOnlyList<OrderItem>)group.ToList());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<OrderStatusHistory>> GetStatusHistoryAsync(long orderId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -93,6 +182,55 @@ public sealed class EfOrderRepository(TakeoutAppDbContext context) : IOrderRepos
|
||||
return refunds;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlySet<long>> GetRefundedOrderIdsAsync(
|
||||
IReadOnlyCollection<long> orderIds,
|
||||
long tenantId,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (orderIds.Count == 0)
|
||||
{
|
||||
return new HashSet<long>();
|
||||
}
|
||||
|
||||
var result = await context.RefundRequests
|
||||
.AsNoTracking()
|
||||
.Where(x =>
|
||||
x.TenantId == tenantId &&
|
||||
orderIds.Contains(x.OrderId) &&
|
||||
x.Status == RefundStatus.Refunded)
|
||||
.Select(x => x.OrderId)
|
||||
.Distinct()
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return result.ToHashSet();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<PaymentRecord?> GetLatestPaymentRecordAsync(long orderId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return context.PaymentRecords
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.OrderId == orderId)
|
||||
.OrderByDescending(x => x.PaidAt ?? DateTime.MinValue)
|
||||
.ThenByDescending(x => x.CreatedAt)
|
||||
.ThenByDescending(x => x.Id)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<DeliveryOrder?> GetLatestDeliveryOrderAsync(long orderId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return context.DeliveryOrders
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.OrderId == orderId)
|
||||
.OrderByDescending(x => x.DeliveredAt ?? DateTime.MinValue)
|
||||
.ThenByDescending(x => x.PickedUpAt ?? DateTime.MinValue)
|
||||
.ThenByDescending(x => x.CreatedAt)
|
||||
.ThenByDescending(x => x.Id)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddOrderAsync(Order order, CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user