补充代发进度 Tab 计数、PrimarySkuCodes 与 Im/Ex HS 编码输出;新增 t_order_exception_event 幂等迁移(Handler*、ConcurrencyStamp)并同步 db.sql。 Co-authored-by: Cursor <cursoragent@cursor.com>
1345 lines
56 KiB
C#
1345 lines
56 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.Domain.WarehouseAggregate.Consts;
|
|
using Oms.Domain.WarehouseAggregate.Entities;
|
|
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);
|
|
query = ApplyListSort(query, filter);
|
|
var items = await query
|
|
.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 ApplyListSort(ApplyListFilter(db.Queryable<Order>(), filter), filter)
|
|
.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(o =>
|
|
o.ServiceNo.Contains(keyword)
|
|
|| (o.ClientNo ?? string.Empty).Contains(keyword)
|
|
|| (o.TrackNo ?? string.Empty).Contains(keyword)
|
|
|| (o.DownstreamServiceNo ?? string.Empty).Contains(keyword)
|
|
|| SqlFunc.Subqueryable<OrderStockBind>()
|
|
.Where(b => b.OrderId == o.Id
|
|
&& !b.IsDeleted
|
|
&& (b.Status == OrderStockBindStatusCodes.Bound
|
|
|| b.Status == OrderStockBindStatusCodes.Deducted)
|
|
&& b.TrackingNo != null
|
|
&& b.TrackingNo.Contains(keyword))
|
|
.Any());
|
|
}
|
|
|
|
if (filter.WarehouseId is > 0)
|
|
{
|
|
var warehouseId = filter.WarehouseId.Value;
|
|
query = query.Where(o =>
|
|
o.WarehouseId == warehouseId
|
|
|| SqlFunc.Subqueryable<OrderStockBind>()
|
|
.Where(b => b.OrderId == o.Id
|
|
&& !b.IsDeleted
|
|
&& b.WarehouseId == warehouseId
|
|
&& (b.Status == OrderStockBindStatusCodes.Bound
|
|
|| b.Status == OrderStockBindStatusCodes.Deducted))
|
|
.Any());
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
var handleStatus = filter.HandleStatus?.Trim();
|
|
if (!string.IsNullOrWhiteSpace(handleStatus)
|
|
&& !string.Equals(handleStatus, "all", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (OrderHandleStatusCodes.IsProcessed(handleStatus))
|
|
{
|
|
query = query.Where(x => x.HandleStatus == OrderHandleStatusCodes.Processed);
|
|
}
|
|
else
|
|
{
|
|
query = query.Where(x =>
|
|
x.HandleStatus == null
|
|
|| x.HandleStatus == string.Empty
|
|
|| x.HandleStatus == OrderHandleStatusCodes.Pending);
|
|
}
|
|
}
|
|
|
|
var orderSource = filter.OrderSource?.Trim();
|
|
if (!string.IsNullOrWhiteSpace(orderSource)
|
|
&& !string.Equals(orderSource, "all", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = query.Where(x => x.OrderSource == orderSource);
|
|
}
|
|
|
|
var fulfillmentType = filter.FulfillmentType?.Trim();
|
|
if (!string.IsNullOrWhiteSpace(fulfillmentType)
|
|
&& !string.Equals(fulfillmentType, "all", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = query.Where(x => x.FulfillmentType == fulfillmentType);
|
|
}
|
|
|
|
var stockBindStatus = filter.StockBindStatus?.Trim();
|
|
if (!string.IsNullOrWhiteSpace(stockBindStatus)
|
|
&& !string.Equals(stockBindStatus, "all", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = ApplyStockBindStatusFilter(query, stockBindStatus);
|
|
}
|
|
|
|
var fulfillProgress = filter.FulfillProgress?.Trim();
|
|
if (!string.IsNullOrWhiteSpace(fulfillProgress)
|
|
&& !string.Equals(fulfillProgress, "all", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = ApplyFulfillProgressFilter(query, fulfillProgress);
|
|
}
|
|
|
|
query = ApplyFieldFilters(query, filter.FieldFilters);
|
|
|
|
query = OrderListScopeFilterApplier.Apply(query, filter);
|
|
|
|
return query;
|
|
}
|
|
|
|
private static ISugarQueryable<Order> ApplyStockBindStatusFilter(
|
|
ISugarQueryable<Order> query,
|
|
string stockBindStatus)
|
|
{
|
|
if (string.Equals(stockBindStatus, OrderStockBindStatusCodes.Bound, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return query.Where(o =>
|
|
SqlFunc.Subqueryable<OrderStockBind>()
|
|
.Where(b => b.OrderId == o.Id && !b.IsDeleted && b.Status == OrderStockBindStatusCodes.Bound)
|
|
.Any()
|
|
&& !SqlFunc.Subqueryable<OrderStockBind>()
|
|
.Where(b => b.OrderId == o.Id && !b.IsDeleted && b.Status == OrderStockBindStatusCodes.Deducted)
|
|
.Any());
|
|
}
|
|
|
|
if (string.Equals(stockBindStatus, OrderStockBindStatusCodes.Deducted, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return query.Where(o =>
|
|
SqlFunc.Subqueryable<OrderStockBind>()
|
|
.Where(b => b.OrderId == o.Id && !b.IsDeleted && b.Status == OrderStockBindStatusCodes.Deducted)
|
|
.Any());
|
|
}
|
|
|
|
if (string.Equals(stockBindStatus, "none", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return query.Where(o =>
|
|
!SqlFunc.Subqueryable<OrderStockBind>()
|
|
.Where(b => b.OrderId == o.Id && !b.IsDeleted
|
|
&& (b.Status == OrderStockBindStatusCodes.Bound || b.Status == OrderStockBindStatusCodes.Deducted))
|
|
.Any());
|
|
}
|
|
|
|
return query;
|
|
}
|
|
|
|
private static ISugarQueryable<Order> ApplyFulfillProgressFilter(
|
|
ISugarQueryable<Order> query,
|
|
string fulfillProgress)
|
|
{
|
|
if (string.Equals(fulfillProgress, FulfillmentProgressCodes.PendingBind, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return query.Where(o =>
|
|
(o.FulfillmentType ?? FulfillmentTypeCodes.Direct) == FulfillmentTypeCodes.Warehouse
|
|
&& !SqlFunc.Subqueryable<OrderStockBind>()
|
|
.Where(b => b.OrderId == o.Id
|
|
&& !b.IsDeleted
|
|
&& (b.Status == OrderStockBindStatusCodes.Bound
|
|
|| b.Status == OrderStockBindStatusCodes.Deducted))
|
|
.Any());
|
|
}
|
|
|
|
if (string.Equals(fulfillProgress, FulfillmentProgressCodes.OutboundDone, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return query.Where(o =>
|
|
SqlFunc.Subqueryable<OrderStockBind>()
|
|
.Where(b => b.OrderId == o.Id
|
|
&& !b.IsDeleted
|
|
&& b.Status == OrderStockBindStatusCodes.Deducted)
|
|
.Any());
|
|
}
|
|
|
|
if (string.Equals(fulfillProgress, FulfillmentProgressCodes.DomesticInTransit, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return ApplyStockBindStatusFilter(query, OrderStockBindStatusCodes.Bound).Where(o =>
|
|
(o.FulfillmentType ?? FulfillmentTypeCodes.Direct) == FulfillmentTypeCodes.Warehouse
|
|
&& SqlFunc.Subqueryable<OrderStockBind>()
|
|
.InnerJoin<CParcel>((b, p) => b.ParcelId == p.Id)
|
|
.Where((b, p) => b.OrderId == o.Id
|
|
&& !b.IsDeleted
|
|
&& b.Status == OrderStockBindStatusCodes.Bound
|
|
&& p.LifecycleStatus == WarehouseParcelLifecycleCodes.InTransit)
|
|
.Any());
|
|
}
|
|
|
|
if (string.Equals(fulfillProgress, FulfillmentProgressCodes.ArrivedPendingPick, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return ApplyStockBindStatusFilter(query, OrderStockBindStatusCodes.Bound).Where(o =>
|
|
(o.FulfillmentType ?? FulfillmentTypeCodes.Direct) == FulfillmentTypeCodes.Warehouse
|
|
&& !SqlFunc.Subqueryable<OrderStockBind>()
|
|
.InnerJoin<CParcel>((b, p) => b.ParcelId == p.Id)
|
|
.Where((b, p) => b.OrderId == o.Id
|
|
&& !b.IsDeleted
|
|
&& b.Status == OrderStockBindStatusCodes.Bound
|
|
&& p.LifecycleStatus == WarehouseParcelLifecycleCodes.InTransit)
|
|
.Any());
|
|
}
|
|
|
|
return query;
|
|
}
|
|
|
|
private static ISugarQueryable<Order> ApplyListSort(
|
|
ISugarQueryable<Order> query,
|
|
OrderListFilter filter)
|
|
{
|
|
var field = filter.SortField?.Trim();
|
|
var asc = string.Equals(filter.SortOrder, "asc", StringComparison.OrdinalIgnoreCase);
|
|
var orderType = asc ? OrderByType.Asc : OrderByType.Desc;
|
|
|
|
if (string.IsNullOrWhiteSpace(field))
|
|
{
|
|
return query
|
|
.OrderBy(x => x.UpdateTime, OrderByType.Desc)
|
|
.OrderBy(x => x.Id, OrderByType.Desc);
|
|
}
|
|
|
|
if (string.Equals(field, "createTime", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = query.OrderBy(x => x.CreateTime, orderType);
|
|
}
|
|
else if (string.Equals(field, "updateTime", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = query.OrderBy(x => x.UpdateTime, orderType);
|
|
}
|
|
else if (string.Equals(field, "serviceNo", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = query.OrderBy(x => x.ServiceNo, orderType);
|
|
}
|
|
else if (string.Equals(field, "clientNo", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = query.OrderBy(x => x.ClientNo, orderType);
|
|
}
|
|
else if (string.Equals(field, "trackNo", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = query.OrderBy(x => x.TrackNo, orderType);
|
|
}
|
|
else if (string.Equals(field, "payableAmount", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = query.OrderBy(x => x.PayableAmount, orderType);
|
|
}
|
|
else if (string.Equals(field, "payTime", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = query.OrderBy(x => x.PayTime, orderType);
|
|
}
|
|
else if (string.Equals(field, "shipTime", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = query.OrderBy(x => x.ShipTime, orderType);
|
|
}
|
|
else if (string.Equals(field, "receiveTime", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = query.OrderBy(x => x.ReceiveTime, orderType);
|
|
}
|
|
else if (string.Equals(field, "signTime", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = query.OrderBy(x => x.SignTime, orderType);
|
|
}
|
|
else if (string.Equals(field, "primaryBinCode", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = query.OrderBy(o =>
|
|
SqlFunc.Subqueryable<OrderStockBind>()
|
|
.InnerJoin<CBinLocation>((b, bin) => b.BinLocationId == bin.Id)
|
|
.Where((b, bin) => b.OrderId == o.Id
|
|
&& !b.IsDeleted
|
|
&& b.Status == OrderStockBindStatusCodes.Bound)
|
|
.Min((b, bin) => bin.BinCode),
|
|
orderType);
|
|
query = query.OrderBy(o =>
|
|
SqlFunc.Subqueryable<OrderStockBind>()
|
|
.Where(b => b.OrderId == o.Id
|
|
&& !b.IsDeleted
|
|
&& b.Status == OrderStockBindStatusCodes.Bound
|
|
&& b.TrackingNo != null
|
|
&& b.TrackingNo != string.Empty)
|
|
.Min(b => b.TrackingNo),
|
|
orderType);
|
|
}
|
|
else
|
|
{
|
|
query = query.OrderBy(x => x.UpdateTime, OrderByType.Desc);
|
|
}
|
|
|
|
return query.OrderBy(x => x.Id, OrderByType.Desc);
|
|
}
|
|
|
|
private static ISugarQueryable<Order> ApplyFieldFilters(
|
|
ISugarQueryable<Order> query,
|
|
List<OrderListFieldFilter>? filters)
|
|
{
|
|
if (filters == null || filters.Count == 0)
|
|
{
|
|
return query;
|
|
}
|
|
|
|
foreach (var filter in filters)
|
|
{
|
|
var field = filter.Field?.Trim() ?? string.Empty;
|
|
var op = (filter.Op ?? "like").Trim().ToLowerInvariant();
|
|
var value = filter.Value?.Trim() ?? string.Empty;
|
|
var values = ResolveInValues(filter);
|
|
|
|
if (string.Equals(field, "serviceNo", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = ApplyStringField(query, op, value, values,
|
|
(q, v) => q.Where(x => x.ServiceNo.Contains(v)),
|
|
(q, v) => q.Where(x => x.ServiceNo == v),
|
|
(q, v) => q.Where(x => x.ServiceNo != v),
|
|
(q, list) => q.Where(x => list.Contains(x.ServiceNo)),
|
|
(q, list) => q.Where(x => !list.Contains(x.ServiceNo)),
|
|
q => q.Where(x => x.ServiceNo == null || x.ServiceNo == string.Empty),
|
|
q => q.Where(x => x.ServiceNo != null && x.ServiceNo != string.Empty));
|
|
}
|
|
else if (string.Equals(field, "clientNo", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = ApplyStringField(query, op, value, values,
|
|
(q, v) => q.Where(x => (x.ClientNo ?? string.Empty).Contains(v)),
|
|
(q, v) => q.Where(x => x.ClientNo == v),
|
|
(q, v) => q.Where(x => x.ClientNo != v),
|
|
(q, list) => q.Where(x => x.ClientNo != null && list.Contains(x.ClientNo)),
|
|
(q, list) => q.Where(x => x.ClientNo == null || !list.Contains(x.ClientNo)),
|
|
q => q.Where(x => x.ClientNo == null || x.ClientNo == string.Empty),
|
|
q => q.Where(x => x.ClientNo != null && x.ClientNo != string.Empty));
|
|
}
|
|
else if (string.Equals(field, "trackNo", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = ApplyStringField(query, op, value, values,
|
|
(q, v) => q.Where(x => (x.TrackNo ?? string.Empty).Contains(v)),
|
|
(q, v) => q.Where(x => x.TrackNo == v),
|
|
(q, v) => q.Where(x => x.TrackNo != v),
|
|
(q, list) => q.Where(x => x.TrackNo != null && list.Contains(x.TrackNo)),
|
|
(q, list) => q.Where(x => x.TrackNo == null || !list.Contains(x.TrackNo)),
|
|
q => q.Where(x => x.TrackNo == null || x.TrackNo == string.Empty),
|
|
q => q.Where(x => x.TrackNo != null && x.TrackNo != string.Empty));
|
|
}
|
|
else if (string.Equals(field, "downstreamServiceNo", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = ApplyStringField(query, op, value, values,
|
|
(q, v) => q.Where(x => (x.DownstreamServiceNo ?? string.Empty).Contains(v)),
|
|
(q, v) => q.Where(x => x.DownstreamServiceNo == v),
|
|
(q, v) => q.Where(x => x.DownstreamServiceNo != v),
|
|
(q, list) => q.Where(x => x.DownstreamServiceNo != null && list.Contains(x.DownstreamServiceNo)),
|
|
(q, list) => q.Where(x => x.DownstreamServiceNo == null || !list.Contains(x.DownstreamServiceNo)),
|
|
q => q.Where(x => x.DownstreamServiceNo == null || x.DownstreamServiceNo == string.Empty),
|
|
q => q.Where(x => x.DownstreamServiceNo != null && x.DownstreamServiceNo != string.Empty));
|
|
}
|
|
else if (string.Equals(field, "orderStatus", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = ApplyStringField(query, op, value, values,
|
|
(q, v) => q.Where(x => x.OrderStatus.Contains(v)),
|
|
(q, v) => q.Where(x => x.OrderStatus == v),
|
|
(q, v) => q.Where(x => x.OrderStatus != v),
|
|
(q, list) => q.Where(x => list.Contains(x.OrderStatus)),
|
|
(q, list) => q.Where(x => !list.Contains(x.OrderStatus)),
|
|
q => q.Where(x => x.OrderStatus == null || x.OrderStatus == string.Empty),
|
|
q => q.Where(x => x.OrderStatus != null && x.OrderStatus != string.Empty));
|
|
}
|
|
else if (string.Equals(field, "productCode", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = ApplyStringField(query, op, value, values,
|
|
(q, v) => q.Where(x => (x.ProductCode ?? string.Empty).Contains(v)),
|
|
(q, v) => q.Where(x => x.ProductCode == v),
|
|
(q, v) => q.Where(x => x.ProductCode != v),
|
|
(q, list) => q.Where(x => x.ProductCode != null && list.Contains(x.ProductCode)),
|
|
(q, list) => q.Where(x => x.ProductCode == null || !list.Contains(x.ProductCode)),
|
|
q => q.Where(x => x.ProductCode == null || x.ProductCode == string.Empty),
|
|
q => q.Where(x => x.ProductCode != null && x.ProductCode != string.Empty));
|
|
}
|
|
else if (string.Equals(field, "channelCode", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = ApplyStringField(query, op, value, values,
|
|
(q, v) => q.Where(x => (x.ChannelCode ?? string.Empty).Contains(v)),
|
|
(q, v) => q.Where(x => x.ChannelCode == v),
|
|
(q, v) => q.Where(x => x.ChannelCode != v),
|
|
(q, list) => q.Where(x => x.ChannelCode != null && list.Contains(x.ChannelCode)),
|
|
(q, list) => q.Where(x => x.ChannelCode == null || !list.Contains(x.ChannelCode)),
|
|
q => q.Where(x => x.ChannelCode == null || x.ChannelCode == string.Empty),
|
|
q => q.Where(x => x.ChannelCode != null && x.ChannelCode != string.Empty));
|
|
}
|
|
else if (string.Equals(field, "orderSource", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = ApplyStringField(query, op, value, values,
|
|
(q, v) => q.Where(x => (x.OrderSource ?? string.Empty).Contains(v)),
|
|
(q, v) => q.Where(x => x.OrderSource == v),
|
|
(q, v) => q.Where(x => x.OrderSource != v),
|
|
(q, list) => q.Where(x => x.OrderSource != null && list.Contains(x.OrderSource)),
|
|
(q, list) => q.Where(x => x.OrderSource == null || !list.Contains(x.OrderSource)),
|
|
q => q.Where(x => x.OrderSource == null || x.OrderSource == string.Empty),
|
|
q => q.Where(x => x.OrderSource != null && x.OrderSource != string.Empty));
|
|
}
|
|
else if (string.Equals(field, "fulfillmentType", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = ApplyStringField(query, op, value, values,
|
|
(q, v) => q.Where(x => (x.FulfillmentType ?? FulfillmentTypeCodes.Direct).Contains(v)),
|
|
(q, v) => q.Where(x => x.FulfillmentType == v),
|
|
(q, v) => q.Where(x => x.FulfillmentType != v),
|
|
(q, list) => q.Where(x => x.FulfillmentType != null && list.Contains(x.FulfillmentType)),
|
|
(q, list) => q.Where(x => x.FulfillmentType == null || !list.Contains(x.FulfillmentType)),
|
|
q => q.Where(x => x.FulfillmentType == null || x.FulfillmentType == string.Empty),
|
|
q => q.Where(x => x.FulfillmentType != null && x.FulfillmentType != string.Empty));
|
|
}
|
|
else if (string.Equals(field, "predeclareFailReason", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = ApplyStringField(query, op, value, values,
|
|
(q, v) => q.Where(x => (x.PredeclareFailReason ?? string.Empty).Contains(v)),
|
|
(q, v) => q.Where(x => x.PredeclareFailReason == v),
|
|
(q, v) => q.Where(x => x.PredeclareFailReason != v),
|
|
(q, list) => q.Where(x => x.PredeclareFailReason != null && list.Contains(x.PredeclareFailReason)),
|
|
(q, list) => q.Where(x => x.PredeclareFailReason == null || !list.Contains(x.PredeclareFailReason)),
|
|
q => q.Where(x => x.PredeclareFailReason == null || x.PredeclareFailReason == string.Empty),
|
|
q => q.Where(x => x.PredeclareFailReason != null && x.PredeclareFailReason != string.Empty));
|
|
}
|
|
else if (string.Equals(field, "allocationPendingReason", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = ApplyStringField(query, op, value, values,
|
|
(q, v) => q.Where(x => (x.AllocationPendingReason ?? string.Empty).Contains(v)),
|
|
(q, v) => q.Where(x => x.AllocationPendingReason == v),
|
|
(q, v) => q.Where(x => x.AllocationPendingReason != v),
|
|
(q, list) => q.Where(x => x.AllocationPendingReason != null && list.Contains(x.AllocationPendingReason)),
|
|
(q, list) => q.Where(x => x.AllocationPendingReason == null || !list.Contains(x.AllocationPendingReason)),
|
|
q => q.Where(x => x.AllocationPendingReason == null || x.AllocationPendingReason == string.Empty),
|
|
q => q.Where(x => x.AllocationPendingReason != null && x.AllocationPendingReason != string.Empty));
|
|
}
|
|
else if (string.Equals(field, "isPaid", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (byte.TryParse(value, out var paid) || TryParseBoolByte(value, out paid))
|
|
{
|
|
query = op switch
|
|
{
|
|
"ne" => query.Where(x => x.IsPaid != paid),
|
|
_ => query.Where(x => x.IsPaid == paid)
|
|
};
|
|
}
|
|
}
|
|
else if (string.Equals(field, "payableAmount", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
query = ApplyDecimalField(query, op, value, filter.Value2,
|
|
(q, n) => q.Where(x => x.PayableAmount == n),
|
|
(q, n) => q.Where(x => x.PayableAmount != n),
|
|
(q, n) => q.Where(x => x.PayableAmount > n),
|
|
(q, n) => q.Where(x => x.PayableAmount >= n),
|
|
(q, n) => q.Where(x => x.PayableAmount < n),
|
|
(q, n) => q.Where(x => x.PayableAmount <= n),
|
|
(q, a, b) => q.Where(x => x.PayableAmount >= a && x.PayableAmount <= b),
|
|
q => q.Where(x => x.PayableAmount == null),
|
|
q => q.Where(x => x.PayableAmount != null));
|
|
}
|
|
else if (IsDateField(field))
|
|
{
|
|
query = ApplyDateField(query, field, op, value, filter.Value2);
|
|
}
|
|
}
|
|
|
|
return query;
|
|
}
|
|
|
|
private static bool IsDateField(string field) =>
|
|
string.Equals(field, "createTime", StringComparison.OrdinalIgnoreCase)
|
|
|| string.Equals(field, "payTime", StringComparison.OrdinalIgnoreCase)
|
|
|| string.Equals(field, "shipTime", StringComparison.OrdinalIgnoreCase)
|
|
|| string.Equals(field, "receiveTime", StringComparison.OrdinalIgnoreCase)
|
|
|| string.Equals(field, "signTime", StringComparison.OrdinalIgnoreCase);
|
|
|
|
private static List<string> ResolveInValues(OrderListFieldFilter filter)
|
|
{
|
|
if (filter.Values is { Count: > 0 })
|
|
{
|
|
return filter.Values
|
|
.Where(x => !string.IsNullOrWhiteSpace(x))
|
|
.Select(x => x.Trim())
|
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
|
.ToList();
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(filter.Value))
|
|
{
|
|
return [];
|
|
}
|
|
|
|
return filter.Value
|
|
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
|
.Where(x => !string.IsNullOrWhiteSpace(x))
|
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
|
.ToList();
|
|
}
|
|
|
|
private static bool TryParseBoolByte(string value, out byte paid)
|
|
{
|
|
paid = 0;
|
|
if (string.Equals(value, "true", StringComparison.OrdinalIgnoreCase)
|
|
|| string.Equals(value, "1", StringComparison.OrdinalIgnoreCase)
|
|
|| string.Equals(value, "yes", StringComparison.OrdinalIgnoreCase)
|
|
|| value == "是")
|
|
{
|
|
paid = 1;
|
|
return true;
|
|
}
|
|
|
|
if (string.Equals(value, "false", StringComparison.OrdinalIgnoreCase)
|
|
|| string.Equals(value, "0", StringComparison.OrdinalIgnoreCase)
|
|
|| string.Equals(value, "no", StringComparison.OrdinalIgnoreCase)
|
|
|| value == "否")
|
|
{
|
|
paid = 0;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static ISugarQueryable<Order> ApplyStringField(
|
|
ISugarQueryable<Order> query,
|
|
string op,
|
|
string value,
|
|
List<string> values,
|
|
Func<ISugarQueryable<Order>, string, ISugarQueryable<Order>> like,
|
|
Func<ISugarQueryable<Order>, string, ISugarQueryable<Order>> eq,
|
|
Func<ISugarQueryable<Order>, string, ISugarQueryable<Order>> ne,
|
|
Func<ISugarQueryable<Order>, List<string>, ISugarQueryable<Order>> inn,
|
|
Func<ISugarQueryable<Order>, List<string>, ISugarQueryable<Order>> notIn,
|
|
Func<ISugarQueryable<Order>, ISugarQueryable<Order>> empty,
|
|
Func<ISugarQueryable<Order>, ISugarQueryable<Order>> notEmpty)
|
|
{
|
|
return op switch
|
|
{
|
|
"empty" or "isnull" => empty(query),
|
|
"isnotnull" => notEmpty(query),
|
|
"eq" => eq(query, value),
|
|
"ne" => ne(query, value),
|
|
"in" when values.Count > 0 => inn(query, values),
|
|
"notin" when values.Count > 0 => notIn(query, values),
|
|
"like" => like(query, value),
|
|
_ => like(query, value)
|
|
};
|
|
}
|
|
|
|
private static ISugarQueryable<Order> ApplyDecimalField(
|
|
ISugarQueryable<Order> query,
|
|
string op,
|
|
string value,
|
|
string? value2,
|
|
Func<ISugarQueryable<Order>, decimal, ISugarQueryable<Order>> eq,
|
|
Func<ISugarQueryable<Order>, decimal, ISugarQueryable<Order>> ne,
|
|
Func<ISugarQueryable<Order>, decimal, ISugarQueryable<Order>> gt,
|
|
Func<ISugarQueryable<Order>, decimal, ISugarQueryable<Order>> gte,
|
|
Func<ISugarQueryable<Order>, decimal, ISugarQueryable<Order>> lt,
|
|
Func<ISugarQueryable<Order>, decimal, ISugarQueryable<Order>> lte,
|
|
Func<ISugarQueryable<Order>, decimal, decimal, ISugarQueryable<Order>> between,
|
|
Func<ISugarQueryable<Order>, ISugarQueryable<Order>> empty,
|
|
Func<ISugarQueryable<Order>, ISugarQueryable<Order>> notEmpty)
|
|
{
|
|
if (op is "empty" or "isnull")
|
|
{
|
|
return empty(query);
|
|
}
|
|
|
|
if (op is "isnotnull")
|
|
{
|
|
return notEmpty(query);
|
|
}
|
|
|
|
if (!decimal.TryParse(value, out var n))
|
|
{
|
|
return query;
|
|
}
|
|
|
|
if (op == "between")
|
|
{
|
|
if (!decimal.TryParse(value2, out var n2))
|
|
{
|
|
return query;
|
|
}
|
|
|
|
var lo = Math.Min(n, n2);
|
|
var hi = Math.Max(n, n2);
|
|
return between(query, lo, hi);
|
|
}
|
|
|
|
return op switch
|
|
{
|
|
"eq" or "like" => eq(query, n),
|
|
"ne" => ne(query, n),
|
|
"gt" => gt(query, n),
|
|
"gte" => gte(query, n),
|
|
"lt" => lt(query, n),
|
|
"lte" => lte(query, n),
|
|
_ => eq(query, n)
|
|
};
|
|
}
|
|
|
|
private static ISugarQueryable<Order> ApplyDateField(
|
|
ISugarQueryable<Order> query,
|
|
string field,
|
|
string op,
|
|
string value,
|
|
string? value2)
|
|
{
|
|
if (op is "empty" or "isnull")
|
|
{
|
|
return field.ToLowerInvariant() switch
|
|
{
|
|
"paytime" => query.Where(x => x.PayTime == null),
|
|
"shiptime" => query.Where(x => x.ShipTime == null),
|
|
"receivetime" => query.Where(x => x.ReceiveTime == null),
|
|
"signtime" => query.Where(x => x.SignTime == null),
|
|
_ => query
|
|
};
|
|
}
|
|
|
|
if (op is "isnotnull")
|
|
{
|
|
return field.ToLowerInvariant() switch
|
|
{
|
|
"paytime" => query.Where(x => x.PayTime != null),
|
|
"shiptime" => query.Where(x => x.ShipTime != null),
|
|
"receivetime" => query.Where(x => x.ReceiveTime != null),
|
|
"signtime" => query.Where(x => x.SignTime != null),
|
|
_ => query
|
|
};
|
|
}
|
|
|
|
if (!DateTime.TryParse(value, out var dt))
|
|
{
|
|
return query;
|
|
}
|
|
|
|
if (op == "between")
|
|
{
|
|
if (!DateTime.TryParse(value2, out var dt2))
|
|
{
|
|
return query;
|
|
}
|
|
|
|
var start = dt <= dt2 ? dt : dt2;
|
|
var end = dt <= dt2 ? dt2 : dt;
|
|
return ApplyDateCompare(query, field, "between", start, end);
|
|
}
|
|
|
|
return ApplyDateCompare(query, field, op, dt, null);
|
|
}
|
|
|
|
private static ISugarQueryable<Order> ApplyDateCompare(
|
|
ISugarQueryable<Order> query,
|
|
string field,
|
|
string op,
|
|
DateTime dt,
|
|
DateTime? dt2)
|
|
{
|
|
var key = field.ToLowerInvariant();
|
|
if (key == "createtime")
|
|
{
|
|
return op switch
|
|
{
|
|
"eq" => query.Where(x => x.CreateTime >= dt.Date && x.CreateTime < dt.Date.AddDays(1)),
|
|
"ne" => query.Where(x => x.CreateTime < dt.Date || x.CreateTime >= dt.Date.AddDays(1)),
|
|
"gt" => query.Where(x => x.CreateTime > dt),
|
|
"gte" => query.Where(x => x.CreateTime >= dt),
|
|
"lt" => query.Where(x => x.CreateTime < dt),
|
|
"lte" => query.Where(x => x.CreateTime <= dt),
|
|
"between" when dt2.HasValue => query.Where(x => x.CreateTime >= dt && x.CreateTime <= dt2.Value),
|
|
_ => query
|
|
};
|
|
}
|
|
|
|
if (key == "paytime")
|
|
{
|
|
return op switch
|
|
{
|
|
"eq" => query.Where(x => x.PayTime >= dt.Date && x.PayTime < dt.Date.AddDays(1)),
|
|
"gt" => query.Where(x => x.PayTime > dt),
|
|
"gte" => query.Where(x => x.PayTime >= dt),
|
|
"lt" => query.Where(x => x.PayTime < dt),
|
|
"lte" => query.Where(x => x.PayTime <= dt),
|
|
"between" when dt2.HasValue => query.Where(x => x.PayTime >= dt && x.PayTime <= dt2.Value),
|
|
_ => query
|
|
};
|
|
}
|
|
|
|
if (key == "shiptime")
|
|
{
|
|
return op switch
|
|
{
|
|
"eq" => query.Where(x => x.ShipTime >= dt.Date && x.ShipTime < dt.Date.AddDays(1)),
|
|
"gt" => query.Where(x => x.ShipTime > dt),
|
|
"gte" => query.Where(x => x.ShipTime >= dt),
|
|
"lt" => query.Where(x => x.ShipTime < dt),
|
|
"lte" => query.Where(x => x.ShipTime <= dt),
|
|
"between" when dt2.HasValue => query.Where(x => x.ShipTime >= dt && x.ShipTime <= dt2.Value),
|
|
_ => query
|
|
};
|
|
}
|
|
|
|
if (key == "receivetime")
|
|
{
|
|
return op switch
|
|
{
|
|
"eq" => query.Where(x => x.ReceiveTime >= dt.Date && x.ReceiveTime < dt.Date.AddDays(1)),
|
|
"gt" => query.Where(x => x.ReceiveTime > dt),
|
|
"gte" => query.Where(x => x.ReceiveTime >= dt),
|
|
"lt" => query.Where(x => x.ReceiveTime < dt),
|
|
"lte" => query.Where(x => x.ReceiveTime <= dt),
|
|
"between" when dt2.HasValue => query.Where(x => x.ReceiveTime >= dt && x.ReceiveTime <= dt2.Value),
|
|
_ => query
|
|
};
|
|
}
|
|
|
|
if (key == "signtime")
|
|
{
|
|
return op switch
|
|
{
|
|
"eq" => query.Where(x => x.SignTime >= dt.Date && x.SignTime < dt.Date.AddDays(1)),
|
|
"gt" => query.Where(x => x.SignTime > dt),
|
|
"gte" => query.Where(x => x.SignTime >= dt),
|
|
"lt" => query.Where(x => x.SignTime < dt),
|
|
"lte" => query.Where(x => x.SignTime <= dt),
|
|
"between" when dt2.HasValue => query.Where(x => x.SignTime >= dt && x.SignTime <= dt2.Value),
|
|
_ => query
|
|
};
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
order = await FirstOrDefaultAsync(x => x.DownstreamServiceNo == 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<Dictionary<int, OrderDownstreamCreateOneBundle>> GetDownstreamCreateOneBundlesAsync(
|
|
IEnumerable<int> orderIds,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var ids = orderIds.Where(x => x > 0).Distinct().ToArray();
|
|
if (ids.Length == 0)
|
|
{
|
|
return new Dictionary<int, OrderDownstreamCreateOneBundle>();
|
|
}
|
|
|
|
var db = await GetDbContextAsync(cancellationToken);
|
|
var orders = await db.Queryable<Order>()
|
|
.Where(x => ids.Contains(x.Id))
|
|
.ToListAsync(cancellationToken);
|
|
if (orders.Count == 0)
|
|
{
|
|
return new Dictionary<int, OrderDownstreamCreateOneBundle>();
|
|
}
|
|
|
|
var foundIds = orders.Select(x => x.Id).ToArray();
|
|
var senders = await db.Queryable<OrderSender>()
|
|
.Where(x => foundIds.Contains(x.OrderId))
|
|
.ToListAsync(cancellationToken);
|
|
var recipients = await db.Queryable<OrderRecipient>()
|
|
.Where(x => foundIds.Contains(x.OrderId))
|
|
.ToListAsync(cancellationToken);
|
|
var declarations = await db.Queryable<OrderDeclaration>()
|
|
.Where(x => foundIds.Contains(x.OrderId))
|
|
.ToListAsync(cancellationToken);
|
|
var weights = await db.Queryable<OrderWeight>()
|
|
.Where(x => foundIds.Contains(x.OrderId))
|
|
.ToListAsync(cancellationToken);
|
|
|
|
var sendersByOrder = senders.GroupBy(x => x.OrderId).ToDictionary(g => g.Key, g => g.First());
|
|
var recipientsByOrder = recipients.GroupBy(x => x.OrderId).ToDictionary(g => g.Key, g => g.First());
|
|
var declarationsByOrder = declarations.GroupBy(x => x.OrderId)
|
|
.ToDictionary(g => g.Key, g => (IReadOnlyList<OrderDeclaration>)g.ToList());
|
|
var weightsByOrder = weights.GroupBy(x => x.OrderId)
|
|
.ToDictionary(g => g.Key, g => (IReadOnlyList<OrderWeight>)g.ToList());
|
|
|
|
var result = new Dictionary<int, OrderDownstreamCreateOneBundle>(orders.Count);
|
|
foreach (var order in orders)
|
|
{
|
|
result[order.Id] = new OrderDownstreamCreateOneBundle
|
|
{
|
|
Order = order,
|
|
Sender = sendersByOrder.GetValueOrDefault(order.Id),
|
|
Recipient = recipientsByOrder.GetValueOrDefault(order.Id),
|
|
Declarations = declarationsByOrder.GetValueOrDefault(order.Id) ?? [],
|
|
Weights = weightsByOrder.GetValueOrDefault(order.Id) ?? [],
|
|
};
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
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<Dictionary<string, (int Pending, int Processed)>> GetHandleStatusCountsByFilterAsync(
|
|
OrderListFilter filter,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var db = await GetDbContextAsync(cancellationToken);
|
|
var baseFilter = new OrderListFilter
|
|
{
|
|
Keyword = filter.Keyword,
|
|
CreateTimeStart = filter.CreateTimeStart,
|
|
CreateTimeEnd = filter.CreateTimeEnd,
|
|
CustomerId = filter.CustomerId,
|
|
FieldFilters = filter.FieldFilters,
|
|
SortField = filter.SortField,
|
|
SortOrder = filter.SortOrder,
|
|
ScopeOrganizationIds = filter.ScopeOrganizationIds,
|
|
ScopeSecretLevels = filter.ScopeSecretLevels,
|
|
ScopeDataScope = filter.ScopeDataScope,
|
|
ScopePersonalOwnerMapId = filter.ScopePersonalOwnerMapId,
|
|
ScopePersonalOwnerAccount = filter.ScopePersonalOwnerAccount,
|
|
ScopeOwnerMapIds = filter.ScopeOwnerMapIds,
|
|
ScopeBizFilters = filter.ScopeBizFilters,
|
|
ScopeCustomFilters = filter.ScopeCustomFilters
|
|
};
|
|
|
|
var result = new Dictionary<string, (int Pending, int Processed)>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
[OrderStatusCodes.Problematic] = (0, 0),
|
|
[OrderStatusCodes.Claimed] = (0, 0)
|
|
};
|
|
|
|
foreach (var status in new[] { OrderStatusCodes.Problematic, OrderStatusCodes.Claimed })
|
|
{
|
|
baseFilter.Status = status;
|
|
var query = ApplyListFilter(db.Queryable<Order>(), baseFilter);
|
|
var rows = await query
|
|
.Select(x => x.HandleStatus)
|
|
.ToListAsync(cancellationToken);
|
|
|
|
var pending = rows.Count(OrderHandleStatusCodes.IsPending);
|
|
var processed = rows.Count(OrderHandleStatusCodes.IsProcessed);
|
|
result[status] = (pending, processed);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<Dictionary<string, int>> GetFulfillProgressCountsByFilterAsync(
|
|
OrderListFilter filter,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var db = await GetDbContextAsync(cancellationToken);
|
|
var baseFilter = new OrderListFilter
|
|
{
|
|
Keyword = filter.Keyword,
|
|
CreateTimeStart = filter.CreateTimeStart,
|
|
CreateTimeEnd = filter.CreateTimeEnd,
|
|
CustomerId = filter.CustomerId,
|
|
WarehouseId = filter.WarehouseId,
|
|
FulfillmentType = filter.FulfillmentType,
|
|
StockBindStatus = filter.StockBindStatus,
|
|
OrderSource = filter.OrderSource,
|
|
FieldFilters = filter.FieldFilters,
|
|
SortField = filter.SortField,
|
|
SortOrder = filter.SortOrder,
|
|
ScopeOrganizationIds = filter.ScopeOrganizationIds,
|
|
ScopeSecretLevels = filter.ScopeSecretLevels,
|
|
ScopeDataScope = filter.ScopeDataScope,
|
|
ScopePersonalOwnerMapId = filter.ScopePersonalOwnerMapId,
|
|
ScopePersonalOwnerAccount = filter.ScopePersonalOwnerAccount,
|
|
ScopeOwnerMapIds = filter.ScopeOwnerMapIds,
|
|
ScopeBizFilters = filter.ScopeBizFilters,
|
|
ScopeCustomFilters = filter.ScopeCustomFilters,
|
|
};
|
|
|
|
async Task<int> CountAsync(string? fulfillProgress)
|
|
{
|
|
var query = ApplyListFilter(db.Queryable<Order>(), baseFilter);
|
|
if (!string.IsNullOrWhiteSpace(fulfillProgress))
|
|
{
|
|
query = ApplyFulfillProgressFilter(query, fulfillProgress);
|
|
}
|
|
|
|
return await query.CountAsync(cancellationToken);
|
|
}
|
|
|
|
var result = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
["all"] = await CountAsync(null),
|
|
[FulfillmentProgressCodes.PendingBind] = await CountAsync(FulfillmentProgressCodes.PendingBind),
|
|
[FulfillmentProgressCodes.DomesticInTransit] = await CountAsync(FulfillmentProgressCodes.DomesticInTransit),
|
|
[FulfillmentProgressCodes.ArrivedPendingPick] = await CountAsync(FulfillmentProgressCodes.ArrivedPendingPick),
|
|
[FulfillmentProgressCodes.OutboundDone] = await CountAsync(FulfillmentProgressCodes.OutboundDone),
|
|
};
|
|
return result;
|
|
}
|
|
|
|
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> SumPayableAmountByCreateTimeRangeAsync(
|
|
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)
|
|
.Where(x => x.PayableAmount != null);
|
|
|
|
return await query.SumAsync(x => x.PayableAmount!.Value);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|