admin/src/Oms.SqlSugarCore/Orders/OrderRepository.cs
zzh cde0ccb6b0 feat: 新增工作台与实时监控统计 API
- GET /order/workplace-stats 与 /order/workplace-trend 供工作台使用

- GET /monitor/overview 供实时监控页订单/客户/轨迹统计

- 扩展订单仓储聚合查询,status-counts 同步应用数据权限

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-04 18:24:48 +08:00

493 lines
18 KiB
C#

using Fake.DependencyInjection;
using Oms.Domain.OrdersAggregate;
using Oms.Domain.OrdersAggregate.Consts;
using Oms.Domain.OrdersAggregate.Entities;
using Oms.Domain.OrdersAggregate.Repositories;
using Oms.SqlSugarCore.Repositories;
using SqlSugar;
namespace Oms.SqlSugarCore.Orders;
public class OrderRepository : OmsSqlSugarRepository<Order>, IOrderRepository, IScopedDependency
{
public async Task<(List<Order> Items, int Total)> GetFilteredPagedListAsync(
int pageIndex,
int pageSize,
OrderListFilter filter,
CancellationToken cancellationToken = default)
{
var db = await GetDbContextAsync(cancellationToken);
var query = ApplyListFilter(db.Queryable<Order>(), filter);
var total = await query.CountAsync(cancellationToken);
var items = await query
.OrderBy(x => x.UpdateTime, OrderByType.Desc)
.OrderBy(x => x.Id, OrderByType.Desc)
.ToPageListAsync(pageIndex, pageSize, cancellationToken);
return (items, total);
}
public async Task<List<Order>> GetFilteredListAsync(
OrderListFilter filter,
CancellationToken cancellationToken = default)
{
var db = await GetDbContextAsync(cancellationToken);
return await ApplyListFilter(db.Queryable<Order>(), filter)
.OrderBy(x => x.UpdateTime, OrderByType.Desc)
.OrderBy(x => x.Id, OrderByType.Desc)
.ToListAsync(cancellationToken);
}
private static ISugarQueryable<Order> ApplyListFilter(ISugarQueryable<Order> query, OrderListFilter filter)
{
if (filter.CustomerId.HasValue)
{
var customerId = filter.CustomerId.Value;
query = query.Where(x => x.CustomerId == customerId);
}
var status = filter.Status?.Trim();
if (!string.IsNullOrWhiteSpace(status)
&& !string.Equals(status, "all", StringComparison.OrdinalIgnoreCase))
{
query = query.Where(x => x.OrderStatus == status);
}
var keyword = filter.Keyword?.Trim();
if (!string.IsNullOrWhiteSpace(keyword))
{
query = query.Where(x =>
x.ServiceNo.Contains(keyword)
|| (x.ClientNo ?? string.Empty).Contains(keyword)
|| (x.TrackNo ?? string.Empty).Contains(keyword));
}
if (filter.CreateTimeStart.HasValue)
{
var start = filter.CreateTimeStart.Value;
query = query.Where(x => x.CreateTime >= start);
}
if (filter.CreateTimeEnd.HasValue)
{
var end = filter.CreateTimeEnd.Value;
query = query.Where(x => x.CreateTime <= end);
}
query = OrderListScopeFilterApplier.Apply(query, filter);
return query;
}
public Task<Order?> GetByServiceNoAsync(string serviceNo, CancellationToken cancellationToken = default)
{
return FirstOrDefaultAsync(x => x.ServiceNo == serviceNo, cancellationToken);
}
public Task<bool> HasServiceNoAsync(string serviceNo, CancellationToken cancellationToken = default)
{
return AnyAsync(x => x.ServiceNo == serviceNo, cancellationToken);
}
public Task<Order?> GetByTrackNoAsync(string trackNo, CancellationToken cancellationToken = default)
{
return FirstOrDefaultAsync(x => x.TrackNo == trackNo, cancellationToken);
}
public async Task<Order?> GetByWaybillNoAsync(string waybillNo, CancellationToken cancellationToken = default)
{
var normalized = waybillNo.Trim();
if (string.IsNullOrWhiteSpace(normalized))
{
return null;
}
var order = await GetByServiceNoAsync(normalized, cancellationToken);
if (order != null)
{
return order;
}
order = await GetByTrackNoAsync(normalized, cancellationToken);
if (order != null)
{
return order;
}
return await FirstOrDefaultAsync(x => x.ClientNo == normalized, cancellationToken);
}
public Task<Order?> GetByCustomerAndClientNoAsync(int customerId, string clientNo, CancellationToken cancellationToken = default)
{
return FirstOrDefaultAsync(x => x.CustomerId == customerId && x.ClientNo == clientNo, cancellationToken);
}
public Task<Order?> GetByRequestIdAsync(string requestId, CancellationToken cancellationToken = default)
{
return FirstOrDefaultAsync(x => x.RequestId == requestId, cancellationToken);
}
public async Task<List<OrderSender>> GetSendersByOrderIdsAsync(IEnumerable<int> orderIds, CancellationToken cancellationToken = default)
{
var ids = orderIds.Distinct().ToArray();
if (ids.Length == 0)
{
return [];
}
var db = await GetDbContextAsync(cancellationToken);
return await db.Queryable<OrderSender>()
.Where(x => ids.Contains(x.OrderId))
.ToListAsync(cancellationToken);
}
public async Task<List<OrderRecipient>> GetRecipientsByOrderIdsAsync(IEnumerable<int> orderIds, CancellationToken cancellationToken = default)
{
var ids = orderIds.Distinct().ToArray();
if (ids.Length == 0)
{
return [];
}
var db = await GetDbContextAsync(cancellationToken);
return await db.Queryable<OrderRecipient>()
.Where(x => ids.Contains(x.OrderId))
.ToListAsync(cancellationToken);
}
public async Task<List<OrderDeclaration>> GetDeclarationsByOrderIdsAsync(IEnumerable<int> orderIds, CancellationToken cancellationToken = default)
{
var ids = orderIds.Distinct().ToArray();
if (ids.Length == 0)
{
return [];
}
var db = await GetDbContextAsync(cancellationToken);
return await db.Queryable<OrderDeclaration>()
.Where(x => ids.Contains(x.OrderId))
.ToListAsync(cancellationToken);
}
public async Task<List<OrderWeight>> GetWeightsByOrderIdsAsync(IEnumerable<int> orderIds, CancellationToken cancellationToken = default)
{
var ids = orderIds.Distinct().ToArray();
if (ids.Length == 0)
{
return [];
}
var db = await GetDbContextAsync(cancellationToken);
return await db.Queryable<OrderWeight>()
.Where(x => ids.Contains(x.OrderId))
.ToListAsync(cancellationToken);
}
public async Task<OrderSender> InsertSenderAsync(OrderSender sender, CancellationToken cancellationToken = default)
{
var db = await GetDbContextAsync(cancellationToken);
return await db.Insertable(sender).ExecuteReturnEntityAsync();
}
public async Task<OrderRecipient> InsertRecipientAsync(OrderRecipient recipient, CancellationToken cancellationToken = default)
{
var db = await GetDbContextAsync(cancellationToken);
return await db.Insertable(recipient).ExecuteReturnEntityAsync();
}
public async Task InsertDeclarationsAsync(IEnumerable<OrderDeclaration> declarations, CancellationToken cancellationToken = default)
{
var items = declarations.ToArray();
if (items.Length == 0)
{
return;
}
var db = await GetDbContextAsync(cancellationToken);
await db.Insertable(items).ExecuteCommandAsync(cancellationToken);
}
public async Task ReplaceDeclarationsAsync(int orderId, IEnumerable<OrderDeclaration> declarations, CancellationToken cancellationToken = default)
{
var items = declarations.ToArray();
var db = await GetDbContextAsync(cancellationToken);
await db.Deleteable<OrderDeclaration>().Where(x => x.OrderId == orderId).ExecuteCommandAsync(cancellationToken);
if (items.Length > 0)
{
await db.Insertable(items).ExecuteCommandAsync(cancellationToken);
}
}
public async Task InsertWeightsAsync(IEnumerable<OrderWeight> weights, CancellationToken cancellationToken = default)
{
var items = weights.ToArray();
if (items.Length == 0)
{
return;
}
var db = await GetDbContextAsync(cancellationToken);
await db.Insertable(items).ExecuteCommandAsync(cancellationToken);
}
public async Task ReplaceWeightsAsync(int orderId, IEnumerable<OrderWeight> weights, CancellationToken cancellationToken = default)
{
var items = weights.ToArray();
var db = await GetDbContextAsync(cancellationToken);
await db.Deleteable<OrderWeight>().Where(x => x.OrderId == orderId).ExecuteCommandAsync(cancellationToken);
if (items.Length > 0)
{
await db.Insertable(items).ExecuteCommandAsync(cancellationToken);
}
}
public async Task UpsertFeesAsync(int orderId, IEnumerable<OrderFee> fees, CancellationToken cancellationToken = default)
{
var items = fees.ToArray();
if (items.Length == 0)
{
return;
}
var db = await GetDbContextAsync(cancellationToken);
foreach (var fee in items)
{
fee.OrderId = orderId;
var existingList = await db.Queryable<OrderFee>()
.Where(x => x.OrderId == orderId
&& x.Type == fee.Type
&& x.CostItem == fee.CostItem
&& x.ChargingStage == fee.ChargingStage)
.Take(1)
.ToListAsync(cancellationToken);
var existing = existingList.FirstOrDefault();
if (existing == null)
{
await db.Insertable(fee).ExecuteCommandAsync(cancellationToken);
continue;
}
existing.Amount = fee.Amount;
existing.Currency = fee.Currency;
existing.PriceNo = fee.PriceNo;
existing.PriceEffectiveTime = fee.PriceEffectiveTime;
existing.DownstreamProvider = fee.DownstreamProvider;
existing.UpdateTime = DateTime.Now;
await db.Updateable(existing).ExecuteCommandAsync(cancellationToken);
}
}
public async Task DeleteReceivableFeesAsync(int orderId, CancellationToken cancellationToken = default)
{
var db = await GetDbContextAsync(cancellationToken);
await db.Deleteable<OrderFee>()
.Where(x => x.OrderId == orderId && x.Type == OrderFeeTypeCodes.Receivable)
.ExecuteCommandAsync(cancellationToken);
}
public async Task DeleteReceivableFeesByStageAsync(int orderId, string chargingStage, CancellationToken cancellationToken = default)
{
var db = await GetDbContextAsync(cancellationToken);
await db.Deleteable<OrderFee>()
.Where(x => x.OrderId == orderId
&& x.Type == OrderFeeTypeCodes.Receivable
&& x.ChargingStage == chargingStage)
.ExecuteCommandAsync(cancellationToken);
}
public async Task DeletePayableFeesAsync(int orderId, CancellationToken cancellationToken = default)
{
var db = await GetDbContextAsync(cancellationToken);
await db.Deleteable<OrderFee>()
.Where(x => x.OrderId == orderId && x.Type == OrderFeeTypeCodes.Payable)
.ExecuteCommandAsync(cancellationToken);
}
public async Task DeletePayableFeesByStageAsync(int orderId, string chargingStage, CancellationToken cancellationToken = default)
{
var db = await GetDbContextAsync(cancellationToken);
await db.Deleteable<OrderFee>()
.Where(x => x.OrderId == orderId
&& x.Type == OrderFeeTypeCodes.Payable
&& x.ChargingStage == chargingStage)
.ExecuteCommandAsync(cancellationToken);
}
public async Task UpdateSenderAsync(OrderSender sender, CancellationToken cancellationToken = default)
{
var db = await GetDbContextAsync(cancellationToken);
sender.UpdateTime = DateTime.Now;
await db.Updateable(sender).ExecuteCommandAsync(cancellationToken);
}
public async Task UpdateRecipientAsync(OrderRecipient recipient, CancellationToken cancellationToken = default)
{
var db = await GetDbContextAsync(cancellationToken);
recipient.UpdateTime = DateTime.Now;
await db.Updateable(recipient).ExecuteCommandAsync(cancellationToken);
}
public async Task<List<OrderFee>> GetFeesByOrderIdsAsync(IEnumerable<int> orderIds, CancellationToken cancellationToken = default)
{
var ids = orderIds.Distinct().ToArray();
if (ids.Length == 0)
{
return [];
}
var db = await GetDbContextAsync(cancellationToken);
return await db.Queryable<OrderFee>()
.Where(x => ids.Contains(x.OrderId))
.ToListAsync(cancellationToken);
}
public async Task<(List<OrderReceivablePayableRow> Items, int Total)> GetReceivablePayablePagedListAsync(
int pageIndex,
int pageSize,
OrderReceivablePayableFilter filter,
CancellationToken cancellationToken = default)
{
var db = await GetDbContextAsync(cancellationToken);
var orderIdsQuery = ApplyListFilter(db.Queryable<Order>(), filter).Select(x => x.Id);
var feeQuery = db.Queryable<OrderFee>()
.Where(fee => !fee.IsDeleted)
.In(fee => fee.OrderId, orderIdsQuery);
var feeType = filter.FeeType?.Trim();
if (!string.IsNullOrWhiteSpace(feeType)
&& !string.Equals(feeType, "all", StringComparison.OrdinalIgnoreCase))
{
feeQuery = feeQuery.Where(fee => fee.Type == feeType);
}
var costItem = filter.CostItem?.Trim();
if (!string.IsNullOrWhiteSpace(costItem))
{
feeQuery = feeQuery.Where(fee => fee.CostItem.Contains(costItem));
}
var chargingStage = filter.ChargingStage?.Trim();
if (!string.IsNullOrWhiteSpace(chargingStage)
&& !string.Equals(chargingStage, "all", StringComparison.OrdinalIgnoreCase))
{
feeQuery = feeQuery.Where(fee => fee.ChargingStage == chargingStage);
}
var total = await feeQuery.CountAsync(cancellationToken);
var fees = await feeQuery
.OrderBy(fee => fee.CreateTime, OrderByType.Desc)
.OrderBy(fee => fee.Id, OrderByType.Desc)
.ToPageListAsync(pageIndex, pageSize, cancellationToken);
if (fees.Count == 0)
{
return ([], total);
}
var orderIds = fees.Select(x => x.OrderId).Distinct().ToArray();
var orders = await db.Queryable<Order>()
.Where(x => orderIds.Contains(x.Id))
.ToListAsync(cancellationToken);
var orderMap = orders.ToDictionary(x => x.Id);
var items = fees
.Where(fee => orderMap.ContainsKey(fee.OrderId))
.Select(fee => new OrderReceivablePayableRow
{
Fee = fee,
Order = orderMap[fee.OrderId]
})
.ToList();
return (items, total);
}
public async Task<List<OrderStatusCountRow>> GetStatusCountsAsync(int? customerId, CancellationToken cancellationToken = default)
{
var filter = new OrderListFilter { CustomerId = customerId };
return await GetStatusCountsByFilterAsync(filter, cancellationToken);
}
public async Task<List<OrderStatusCountRow>> GetStatusCountsByFilterAsync(
OrderListFilter filter,
CancellationToken cancellationToken = default)
{
var db = await GetDbContextAsync(cancellationToken);
var query = ApplyListFilter(db.Queryable<Order>(), filter);
return await query
.GroupBy(x => x.OrderStatus)
.Select(x => new OrderStatusCountRow
{
Status = x.OrderStatus,
Count = SqlFunc.AggregateCount(x.Id)
})
.ToListAsync(cancellationToken);
}
public async Task<int> CountByFilterAndCreateTimeRangeAsync(
OrderListFilter filter,
DateTime startInclusive,
DateTime endExclusive,
CancellationToken cancellationToken = default)
{
var db = await GetDbContextAsync(cancellationToken);
var query = ApplyListFilter(db.Queryable<Order>(), filter)
.Where(x => x.CreateTime >= startInclusive && x.CreateTime < endExclusive)
.Where(x => x.OrderStatus != OrderStatusCodes.Deleted);
return await query.CountAsync(cancellationToken);
}
public async Task<int> CountByFilterAndShipTimeRangeAsync(
OrderListFilter filter,
DateTime startInclusive,
DateTime endExclusive,
CancellationToken cancellationToken = default)
{
var db = await GetDbContextAsync(cancellationToken);
var query = ApplyListFilter(db.Queryable<Order>(), filter)
.Where(x => x.ShipTime != null && x.ShipTime >= startInclusive && x.ShipTime < endExclusive)
.Where(x => x.OrderStatus != OrderStatusCodes.Deleted);
return await query.CountAsync(cancellationToken);
}
public async Task<decimal> SumFeeAmountByPayTimeRangeAsync(
OrderListFilter filter,
string feeType,
DateTime startInclusive,
DateTime endExclusive,
CancellationToken cancellationToken = default)
{
var db = await GetDbContextAsync(cancellationToken);
var orderQuery = ApplyListFilter(db.Queryable<Order>(), filter)
.Where(x => x.PayTime != null && x.PayTime >= startInclusive && x.PayTime < endExclusive)
.Where(x => x.OrderStatus != OrderStatusCodes.Deleted);
var orderIds = await orderQuery.Select(x => x.Id).ToListAsync(cancellationToken);
if (orderIds.Count == 0)
{
return 0m;
}
return await db.Queryable<OrderFee>()
.Where(x => orderIds.Contains(x.OrderId))
.Where(x => x.Type == feeType)
.SumAsync(x => x.Amount);
}
public async Task<List<Order>> GetOrdersByFilterAndCreateTimeRangeAsync(
OrderListFilter filter,
DateTime startInclusive,
DateTime endExclusive,
CancellationToken cancellationToken = default)
{
var db = await GetDbContextAsync(cancellationToken);
return await ApplyListFilter(db.Queryable<Order>(), filter)
.Where(x => x.CreateTime >= startInclusive && x.CreateTime < endExclusive)
.Where(x => x.OrderStatus != OrderStatusCodes.Deleted)
.ToListAsync(cancellationToken);
}
}