diff --git a/src/Oms.Application/Dtos/Customers/CustomerAccountFlowPaginatedInput.cs b/src/Oms.Application/Dtos/Customers/CustomerAccountFlowPaginatedInput.cs
index 50198ad..11e5a95 100644
--- a/src/Oms.Application/Dtos/Customers/CustomerAccountFlowPaginatedInput.cs
+++ b/src/Oms.Application/Dtos/Customers/CustomerAccountFlowPaginatedInput.cs
@@ -24,4 +24,24 @@ public class CustomerAccountFlowPaginatedInput
public DateTime? StartTime { get; set; }
public DateTime? EndTime { get; set; }
+
+ ///
+ /// 变动金额下限(含)。
+ ///
+ public decimal? AmountMin { get; set; }
+
+ ///
+ /// 变动金额上限(含)。
+ ///
+ public decimal? AmountMax { get; set; }
+
+ ///
+ /// 排序字段:createTime / amount。
+ ///
+ public string? SortField { get; set; }
+
+ ///
+ /// asc / desc。
+ ///
+ public string? SortOrder { get; set; }
}
diff --git a/src/Oms.Application/Dtos/Customers/CustomerPaginatedInput.cs b/src/Oms.Application/Dtos/Customers/CustomerPaginatedInput.cs
index c361d92..a765c86 100644
--- a/src/Oms.Application/Dtos/Customers/CustomerPaginatedInput.cs
+++ b/src/Oms.Application/Dtos/Customers/CustomerPaginatedInput.cs
@@ -41,4 +41,14 @@ public class CustomerPaginatedInput
/// 所属顾问筛选。
///
public string? CustomerService { get; set; }
+
+ ///
+ /// 排序字段:createTime / updateTime / customerCode / customerName / balance。
+ ///
+ public string? SortField { get; set; }
+
+ ///
+ /// asc / desc。
+ ///
+ public string? SortOrder { get; set; }
}
diff --git a/src/Oms.Application/Dtos/Orders/OrderExportInput.cs b/src/Oms.Application/Dtos/Orders/OrderExportInput.cs
index 6d8e8e8..1378635 100644
--- a/src/Oms.Application/Dtos/Orders/OrderExportInput.cs
+++ b/src/Oms.Application/Dtos/Orders/OrderExportInput.cs
@@ -34,4 +34,19 @@ public class OrderExportInput
///
[Newtonsoft.Json.JsonProperty("createTimeEnd")]
public DateTime? CreateTimeEnd { get; set; }
+
+ ///
+ /// 表头字段级筛选(白名单)。
+ ///
+ [Newtonsoft.Json.JsonProperty("filters")]
+ [global::System.Text.Json.Serialization.JsonPropertyName("filters")]
+ public List? Filters { get; set; }
+
+ [Newtonsoft.Json.JsonProperty("sortField")]
+ [global::System.Text.Json.Serialization.JsonPropertyName("sortField")]
+ public string? SortField { get; set; }
+
+ [Newtonsoft.Json.JsonProperty("sortOrder")]
+ [global::System.Text.Json.Serialization.JsonPropertyName("sortOrder")]
+ public string? SortOrder { get; set; }
}
diff --git a/src/Oms.Application/Dtos/Orders/OrderListFieldFilterDto.cs b/src/Oms.Application/Dtos/Orders/OrderListFieldFilterDto.cs
new file mode 100644
index 0000000..d1f5f0f
--- /dev/null
+++ b/src/Oms.Application/Dtos/Orders/OrderListFieldFilterDto.cs
@@ -0,0 +1,36 @@
+namespace Oms.Application.Dtos.Orders;
+
+///
+/// 订单列表字段级筛选(表头 filters[],白名单字段 + DataPerm 风格算子)。
+///
+public class OrderListFieldFilterDto
+{
+ ///
+ /// 字段白名单见 OrderService.NormalizeOrderFieldFilters。
+ ///
+ [Newtonsoft.Json.JsonProperty("field")]
+ [global::System.Text.Json.Serialization.JsonPropertyName("field")]
+ public string Field { get; set; } = string.Empty;
+
+ ///
+ /// like / eq / ne / in / notIn / gt / gte / lt / lte / between / empty / isNull / isNotNull。
+ ///
+ [Newtonsoft.Json.JsonProperty("op")]
+ [global::System.Text.Json.Serialization.JsonPropertyName("op")]
+ public string Op { get; set; } = "like";
+
+ [Newtonsoft.Json.JsonProperty("value")]
+ [global::System.Text.Json.Serialization.JsonPropertyName("value")]
+ public string? Value { get; set; }
+
+ [Newtonsoft.Json.JsonProperty("value2")]
+ [global::System.Text.Json.Serialization.JsonPropertyName("value2")]
+ public string? Value2 { get; set; }
+
+ ///
+ /// IN / NOT IN 多值(优先于 value 逗号拆分)。
+ ///
+ [Newtonsoft.Json.JsonProperty("values")]
+ [global::System.Text.Json.Serialization.JsonPropertyName("values")]
+ public List? Values { get; set; }
+}
diff --git a/src/Oms.Application/Dtos/Orders/OrderPaginatedInput.cs b/src/Oms.Application/Dtos/Orders/OrderPaginatedInput.cs
index 90a4787..a33c92b 100644
--- a/src/Oms.Application/Dtos/Orders/OrderPaginatedInput.cs
+++ b/src/Oms.Application/Dtos/Orders/OrderPaginatedInput.cs
@@ -62,4 +62,25 @@ public class OrderPaginatedInput
///
[Newtonsoft.Json.JsonProperty("createTimeEnd")]
public DateTime? CreateTimeEnd { get; set; }
+
+ ///
+ /// 表头字段级筛选(白名单);与 keyword 并存时 AND。
+ ///
+ [Newtonsoft.Json.JsonProperty("filters")]
+ [global::System.Text.Json.Serialization.JsonPropertyName("filters")]
+ public List? Filters { get; set; }
+
+ ///
+ /// 排序字段白名单:createTime / updateTime / serviceNo / clientNo / trackNo / payableAmount / payTime / shipTime / receiveTime / signTime。
+ ///
+ [Newtonsoft.Json.JsonProperty("sortField")]
+ [global::System.Text.Json.Serialization.JsonPropertyName("sortField")]
+ public string? SortField { get; set; }
+
+ ///
+ /// asc / desc。
+ ///
+ [Newtonsoft.Json.JsonProperty("sortOrder")]
+ [global::System.Text.Json.Serialization.JsonPropertyName("sortOrder")]
+ public string? SortOrder { get; set; }
}
diff --git a/src/Oms.Application/Dtos/Shared/ListFieldFilterDto.cs b/src/Oms.Application/Dtos/Shared/ListFieldFilterDto.cs
new file mode 100644
index 0000000..89e3b9e
--- /dev/null
+++ b/src/Oms.Application/Dtos/Shared/ListFieldFilterDto.cs
@@ -0,0 +1,33 @@
+namespace Oms.Application.Dtos.Shared;
+
+///
+/// 列表字段级筛选(表头 filters[],白名单字段由各接口自行校验)。
+///
+public class ListFieldFilterDto
+{
+ [Newtonsoft.Json.JsonProperty("field")]
+ [global::System.Text.Json.Serialization.JsonPropertyName("field")]
+ public string Field { get; set; } = string.Empty;
+
+ ///
+ /// like / eq / ne / in / notIn / gt / gte / lt / lte / between / empty / isNull / isNotNull。
+ ///
+ [Newtonsoft.Json.JsonProperty("op")]
+ [global::System.Text.Json.Serialization.JsonPropertyName("op")]
+ public string Op { get; set; } = "like";
+
+ [Newtonsoft.Json.JsonProperty("value")]
+ [global::System.Text.Json.Serialization.JsonPropertyName("value")]
+ public string? Value { get; set; }
+
+ [Newtonsoft.Json.JsonProperty("value2")]
+ [global::System.Text.Json.Serialization.JsonPropertyName("value2")]
+ public string? Value2 { get; set; }
+
+ ///
+ /// IN / NOT IN 多值(优先于 value 逗号拆分)。
+ ///
+ [Newtonsoft.Json.JsonProperty("values")]
+ [global::System.Text.Json.Serialization.JsonPropertyName("values")]
+ public List? Values { get; set; }
+}
diff --git a/src/Oms.Application/Services/Customers/CustomerAccountFlowService.cs b/src/Oms.Application/Services/Customers/CustomerAccountFlowService.cs
index 1ae82e2..319b263 100644
--- a/src/Oms.Application/Services/Customers/CustomerAccountFlowService.cs
+++ b/src/Oms.Application/Services/Customers/CustomerAccountFlowService.cs
@@ -41,11 +41,7 @@ public class CustomerAccountFlowService : ApplicationService
predicate,
pageIndex,
pageSize,
- new Dictionary
- {
- [nameof(CustomerAccountFlow.CreateTime)] = false,
- [nameof(CustomerAccountFlow.Id)] = false
- });
+ BuildAccountFlowSortDictionary(input));
var total = await FlowRepository.CountAsync(predicate);
var customerIds = entities.Select(x => x.CustomerId).Distinct().ToArray();
@@ -94,27 +90,48 @@ public class CustomerAccountFlowService : ApplicationService
&& !string.Equals(bizType, "all", StringComparison.OrdinalIgnoreCase);
var hasCustomerFilter = customerId.HasValue;
var customerIdValue = customerId.GetValueOrDefault();
-
- if (input.StartTime.HasValue && input.EndTime.HasValue)
- {
- var start = input.StartTime.Value;
- var end = input.EndTime.Value;
- return flow =>
- (!hasCustomerFilter || flow.CustomerId == customerIdValue) &&
- (!hasBizType || flow.BizType == bizType) &&
- (!hasKeyword ||
- flow.SerialNumber.Contains(keyword!) ||
- (flow.BizNo ?? string.Empty).Contains(keyword!)) &&
- flow.CreateTime >= start &&
- flow.CreateTime <= end;
- }
+ var hasStart = input.StartTime.HasValue;
+ var hasEnd = input.EndTime.HasValue;
+ var start = input.StartTime.GetValueOrDefault();
+ var end = input.EndTime.GetValueOrDefault();
+ var hasAmountMin = input.AmountMin.HasValue;
+ var hasAmountMax = input.AmountMax.HasValue;
+ var amountMin = input.AmountMin.GetValueOrDefault();
+ var amountMax = input.AmountMax.GetValueOrDefault();
return flow =>
(!hasCustomerFilter || flow.CustomerId == customerIdValue) &&
(!hasBizType || flow.BizType == bizType) &&
(!hasKeyword ||
flow.SerialNumber.Contains(keyword!) ||
- (flow.BizNo ?? string.Empty).Contains(keyword!));
+ (flow.BizNo ?? string.Empty).Contains(keyword!)) &&
+ (!hasStart || flow.CreateTime >= start) &&
+ (!hasEnd || flow.CreateTime <= end) &&
+ (!hasAmountMin || flow.Amount >= amountMin) &&
+ (!hasAmountMax || flow.Amount <= amountMax);
+ }
+
+ private static Dictionary BuildAccountFlowSortDictionary(
+ CustomerAccountFlowPaginatedInput input)
+ {
+ var field = input.SortField?.Trim();
+ var asc = string.Equals(input.SortOrder, "asc", StringComparison.OrdinalIgnoreCase);
+ if (string.Equals(field, "amount", StringComparison.OrdinalIgnoreCase))
+ {
+ return new Dictionary
+ {
+ [nameof(CustomerAccountFlow.Amount)] = asc,
+ [nameof(CustomerAccountFlow.Id)] = false
+ };
+ }
+
+ return new Dictionary
+ {
+ [nameof(CustomerAccountFlow.CreateTime)] = string.Equals(field, "createTime", StringComparison.OrdinalIgnoreCase)
+ ? asc
+ : false,
+ [nameof(CustomerAccountFlow.Id)] = false
+ };
}
private static string GetBizTypeName(string bizType)
diff --git a/src/Oms.Application/Services/Customers/CustomerService.cs b/src/Oms.Application/Services/Customers/CustomerService.cs
index 5bb0d7c..07ddc94 100644
--- a/src/Oms.Application/Services/Customers/CustomerService.cs
+++ b/src/Oms.Application/Services/Customers/CustomerService.cs
@@ -66,7 +66,7 @@ public class CustomerService : ApplicationService
predicate,
pageIndex,
pageSize,
- new Dictionary { [nameof(Customer.UpdateTime)] = false, [nameof(Customer.Id)] = false });
+ BuildCustomerSortDictionary(input));
var totalCount = await CustomerRepository.CountAsync(predicate);
var auditUserNameMap = await UserDisplayNameResolver.ResolveManyAsync(
@@ -424,6 +424,40 @@ public class CustomerService : ApplicationService
(string.IsNullOrWhiteSpace(customerService) || customer.CustomerService == customerService);
}
+ private static Dictionary BuildCustomerSortDictionary(CustomerPaginatedInput input)
+ {
+ var field = input.SortField?.Trim();
+ var asc = string.Equals(input.SortOrder, "asc", StringComparison.OrdinalIgnoreCase);
+ // Dictionary value: true=asc, false=desc(与仓库约定一致)
+ if (string.Equals(field, "createTime", StringComparison.OrdinalIgnoreCase))
+ {
+ return new Dictionary { [nameof(Customer.CreateTime)] = asc, [nameof(Customer.Id)] = false };
+ }
+
+ if (string.Equals(field, "customerCode", StringComparison.OrdinalIgnoreCase))
+ {
+ return new Dictionary { [nameof(Customer.CustomerCode)] = asc, [nameof(Customer.Id)] = false };
+ }
+
+ if (string.Equals(field, "customerName", StringComparison.OrdinalIgnoreCase))
+ {
+ return new Dictionary { [nameof(Customer.CustomerName)] = asc, [nameof(Customer.Id)] = false };
+ }
+
+ if (string.Equals(field, "balance", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(field, "accountBalance", StringComparison.OrdinalIgnoreCase))
+ {
+ return new Dictionary { [nameof(Customer.Balance)] = asc, [nameof(Customer.Id)] = false };
+ }
+
+ if (string.Equals(field, "updateTime", StringComparison.OrdinalIgnoreCase))
+ {
+ return new Dictionary { [nameof(Customer.UpdateTime)] = asc, [nameof(Customer.Id)] = false };
+ }
+
+ return new Dictionary { [nameof(Customer.UpdateTime)] = false, [nameof(Customer.Id)] = false };
+ }
+
private async Task GetCustomerOrThrowAsync(int id)
{
var entity = await CustomerRepository.FirstOrDefaultAsync(x => x.Id == id);
diff --git a/src/Oms.Application/Services/Orders/OrderService.Monitor.cs b/src/Oms.Application/Services/Orders/OrderService.Monitor.cs
index dcad38a..ac443f2 100644
--- a/src/Oms.Application/Services/Orders/OrderService.Monitor.cs
+++ b/src/Oms.Application/Services/Orders/OrderService.Monitor.cs
@@ -282,6 +282,9 @@ public partial class OrderService
CreateTimeStart = start,
CreateTimeEnd = filter.CreateTimeEnd,
CustomerId = filter.CustomerId,
+ FieldFilters = filter.FieldFilters,
+ SortField = filter.SortField,
+ SortOrder = filter.SortOrder,
ScopeOrganizationIds = filter.ScopeOrganizationIds,
ScopeSecretLevels = filter.ScopeSecretLevels,
ScopeDataScope = filter.ScopeDataScope,
diff --git a/src/Oms.Application/Services/Orders/OrderService.cs b/src/Oms.Application/Services/Orders/OrderService.cs
index 14f753e..01e3fb2 100644
--- a/src/Oms.Application/Services/Orders/OrderService.cs
+++ b/src/Oms.Application/Services/Orders/OrderService.cs
@@ -68,9 +68,14 @@ public partial class OrderService : ApplicationService
/// 按状态聚合统计订单数量(单次查询,供 Tab 角标展示)。
///
[HttpGet("/order/status-counts")]
- public async Task GetStatusCountsAsync()
+ public async Task GetStatusCountsAsync(
+ [FromQuery] DateTime? createTimeStart = null,
+ [FromQuery] DateTime? createTimeEnd = null)
{
var filter = await BuildScopedListFilterAsync();
+ filter.CreateTimeStart = createTimeStart;
+ filter.CreateTimeEnd = createTimeEnd;
+ EnsureOrderListTimeWindow(filter);
var rows = await OrderRepository.GetStatusCountsByFilterAsync(filter);
var counts = rows.ToDictionary(
x => x.Status,
@@ -316,7 +321,14 @@ public partial class OrderService : ApplicationService
null);
var filter = BuildListFilter(input, customerId);
await ApplyInternalOrderDataScopeAsync(filter);
- var orders = await OrderRepository.GetFilteredListAsync(filter);
+
+ const int maxExportRows = 10000;
+ var (orders, total) = await OrderRepository.GetFilteredPagedListAsync(1, maxExportRows, filter);
+ if (total > maxExportRows)
+ {
+ throw new BusinessException($"导出结果超过 {maxExportRows} 条(当前 {total}),请缩小筛选条件后再导出");
+ }
+
var fileName = $"orders-{DateTime.Now:yyyyMMddHHmmss}.xlsx";
var directory = GetOrderExportDirectory();
Directory.CreateDirectory(directory);
@@ -507,28 +519,266 @@ public partial class OrderService : ApplicationService
};
}
- private static OrderListFilter BuildListFilter(OrderPaginatedInput input, int? customerId) =>
- new()
+ private static OrderListFilter BuildListFilter(OrderPaginatedInput input, int? customerId)
+ {
+ var filter = new OrderListFilter
{
Status = input.Status,
HandleStatus = input.HandleStatus,
OrderSource = input.OrderSource,
- Keyword = input.Keyword,
+ Keyword = NormalizeOrderKeyword(input.Keyword),
CreateTimeStart = input.CreateTimeStart,
CreateTimeEnd = input.CreateTimeEnd,
- CustomerId = customerId
+ CustomerId = customerId,
+ FieldFilters = NormalizeOrderFieldFilters(input.Filters),
+ SortField = NormalizeOrderSortField(input.SortField),
+ SortOrder = NormalizeOrderSortOrder(input.SortOrder)
};
+ EnsureOrderListTimeWindow(filter);
+ return filter;
+ }
- private static OrderListFilter BuildListFilter(OrderExportInput input, int? customerId) =>
- new()
+ private static OrderListFilter BuildListFilter(OrderExportInput input, int? customerId)
+ {
+ var filter = new OrderListFilter
{
Status = input.Status,
OrderSource = input.OrderSource,
- Keyword = input.Keyword,
+ Keyword = NormalizeOrderKeyword(input.Keyword),
CreateTimeStart = input.CreateTimeStart,
CreateTimeEnd = input.CreateTimeEnd,
- CustomerId = customerId
+ CustomerId = customerId,
+ FieldFilters = NormalizeOrderFieldFilters(input.Filters),
+ SortField = NormalizeOrderSortField(input.SortField),
+ SortOrder = NormalizeOrderSortOrder(input.SortOrder)
};
+ EnsureOrderListTimeWindow(filter);
+ return filter;
+ }
+
+ /// 订单列表默认时间窗(天)。无精确单号且未传时间时兜底,防全表扫。
+ private const int OrderListDefaultDays = 90;
+
+ ///
+ /// 无创建时间且非「精确单号查询」时,自动限制近 N 天。
+ ///
+ private static void EnsureOrderListTimeWindow(OrderListFilter filter)
+ {
+ if (filter.CreateTimeStart.HasValue || filter.CreateTimeEnd.HasValue)
+ {
+ return;
+ }
+
+ if (HasNarrowOrderLookup(filter))
+ {
+ return;
+ }
+
+ filter.CreateTimeStart = DateTime.Now.Date.AddDays(-(OrderListDefaultDays - 1));
+ }
+
+ private static bool HasNarrowOrderLookup(OrderListFilter filter)
+ {
+ if (!string.IsNullOrWhiteSpace(filter.Keyword))
+ {
+ return true;
+ }
+
+ if (filter.FieldFilters == null || filter.FieldFilters.Count == 0)
+ {
+ return false;
+ }
+
+ var numberFields = new HashSet(StringComparer.OrdinalIgnoreCase)
+ {
+ "serviceNo", "clientNo", "trackNo", "downstreamServiceNo"
+ };
+
+ return filter.FieldFilters.Any(f =>
+ numberFields.Contains(f.Field)
+ && (f.Op is "eq" or "in" or "like")
+ && (!string.IsNullOrWhiteSpace(f.Value) || (f.Values?.Count ?? 0) > 0));
+ }
+
+ private static readonly HashSet OrderFieldFilterWhitelist = new(StringComparer.OrdinalIgnoreCase)
+ {
+ "serviceNo",
+ "clientNo",
+ "trackNo",
+ "downstreamServiceNo",
+ "createTime",
+ "orderStatus",
+ "productCode",
+ "channelCode",
+ "orderSource",
+ "payableAmount",
+ "isPaid",
+ "predeclareFailReason",
+ "allocationPendingReason",
+ "payTime",
+ "shipTime",
+ "receiveTime",
+ "signTime"
+ };
+
+ private static readonly HashSet OrderFieldFilterOps = new(StringComparer.OrdinalIgnoreCase)
+ {
+ "like", "eq", "ne", "in", "notIn", "gt", "gte", "lt", "lte", "between",
+ "empty", "isNull", "isNotNull"
+ };
+
+ private static readonly HashSet OrderSortFieldWhitelist = new(StringComparer.OrdinalIgnoreCase)
+ {
+ "createTime",
+ "updateTime",
+ "serviceNo",
+ "clientNo",
+ "trackNo",
+ "payableAmount",
+ "payTime",
+ "shipTime",
+ "receiveTime",
+ "signTime"
+ };
+
+ /// 单次列表最多接受的 filters 条数。
+ private const int MaxOrderFieldFilters = 20;
+
+ /// IN / NOT IN 单字段最多值数量(防几万单号打爆 SQL/参数包)。
+ private const int MaxOrderInValues = 500;
+
+ /// 单值最大长度(单号/文本)。
+ private const int MaxOrderFilterValueLength = 128;
+
+ /// keyword 最大长度。
+ private const int MaxOrderKeywordLength = 200;
+
+ private static string? NormalizeOrderKeyword(string? keyword)
+ {
+ var text = keyword?.Trim();
+ if (string.IsNullOrWhiteSpace(text))
+ {
+ return null;
+ }
+
+ return text.Length <= MaxOrderKeywordLength
+ ? text
+ : text[..MaxOrderKeywordLength];
+ }
+
+ private static string? TruncateFilterValue(string? value)
+ {
+ var text = value?.Trim();
+ if (string.IsNullOrWhiteSpace(text))
+ {
+ return null;
+ }
+
+ return text.Length <= MaxOrderFilterValueLength
+ ? text
+ : text[..MaxOrderFilterValueLength];
+ }
+
+ private static string? NormalizeOrderSortField(string? sortField)
+ {
+ var field = sortField?.Trim();
+ if (string.IsNullOrWhiteSpace(field) || !OrderSortFieldWhitelist.Contains(field))
+ {
+ return null;
+ }
+
+ return field;
+ }
+
+ private static string? NormalizeOrderSortOrder(string? sortOrder)
+ {
+ var order = sortOrder?.Trim();
+ if (string.IsNullOrWhiteSpace(order))
+ {
+ return null;
+ }
+
+ if (string.Equals(order, "asc", StringComparison.OrdinalIgnoreCase))
+ {
+ return "asc";
+ }
+
+ if (string.Equals(order, "desc", StringComparison.OrdinalIgnoreCase))
+ {
+ return "desc";
+ }
+
+ return null;
+ }
+
+ private static List? NormalizeOrderFieldFilters(
+ List? filters)
+ {
+ if (filters == null || filters.Count == 0)
+ {
+ return null;
+ }
+
+ var result = new List();
+ foreach (var f in filters.Take(MaxOrderFieldFilters))
+ {
+ var field = f.Field?.Trim() ?? string.Empty;
+ var op = string.IsNullOrWhiteSpace(f.Op) ? "like" : f.Op.Trim();
+ if (!OrderFieldFilterWhitelist.Contains(field) || !OrderFieldFilterOps.Contains(op))
+ {
+ continue;
+ }
+
+ op = op.ToLowerInvariant();
+ if (string.Equals(op, "isNull", StringComparison.OrdinalIgnoreCase))
+ {
+ op = "empty";
+ }
+
+ var values = (f.Values ?? [])
+ .Select(x => TruncateFilterValue(x))
+ .Where(x => !string.IsNullOrWhiteSpace(x))
+ .Cast()
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .Take(MaxOrderInValues)
+ .ToList();
+ if (values.Count == 0 && !string.IsNullOrWhiteSpace(f.Value)
+ && (op is "in" or "notin"))
+ {
+ values = f.Value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
+ .Select(x => TruncateFilterValue(x))
+ .Where(x => !string.IsNullOrWhiteSpace(x))
+ .Cast()
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .Take(MaxOrderInValues)
+ .ToList();
+ }
+
+ var needsValue = op is not ("empty" or "isnotnull");
+ var truncatedValue = TruncateFilterValue(f.Value);
+ var truncatedValue2 = TruncateFilterValue(f.Value2);
+ if (needsValue && values.Count == 0 && string.IsNullOrWhiteSpace(truncatedValue))
+ {
+ continue;
+ }
+
+ if (op is "between" && string.IsNullOrWhiteSpace(truncatedValue2) && values.Count < 2)
+ {
+ continue;
+ }
+
+ result.Add(new OrderListFieldFilter
+ {
+ Field = field,
+ Op = op,
+ Value = truncatedValue,
+ Value2 = truncatedValue2,
+ Values = values.Count > 0 ? values : null
+ });
+ }
+
+ return result.Count == 0 ? null : result;
+ }
private async Task EnsureCustomerCanAccessOrderAsync(Order order)
{
diff --git a/src/Oms.Domain/OrdersAggregate/OrderListFieldFilter.cs b/src/Oms.Domain/OrdersAggregate/OrderListFieldFilter.cs
new file mode 100644
index 0000000..f8b4228
--- /dev/null
+++ b/src/Oms.Domain/OrdersAggregate/OrderListFieldFilter.cs
@@ -0,0 +1,17 @@
+namespace Oms.Domain.OrdersAggregate;
+
+///
+/// 订单列表字段级筛选(仓储侧已规范化)。
+///
+public class OrderListFieldFilter
+{
+ public string Field { get; set; } = string.Empty;
+
+ public string Op { get; set; } = "like";
+
+ public string? Value { get; set; }
+
+ public string? Value2 { get; set; }
+
+ public List? Values { get; set; }
+}
diff --git a/src/Oms.Domain/OrdersAggregate/OrderListFilter.cs b/src/Oms.Domain/OrdersAggregate/OrderListFilter.cs
index f7e7a34..df7e5c2 100644
--- a/src/Oms.Domain/OrdersAggregate/OrderListFilter.cs
+++ b/src/Oms.Domain/OrdersAggregate/OrderListFilter.cs
@@ -19,6 +19,21 @@ public class OrderListFilter
public int? CustomerId { get; set; }
+ ///
+ /// 表头字段级筛选(已规范化的白名单条件)。
+ ///
+ public List? FieldFilters { get; set; }
+
+ ///
+ /// 已规范化的排序字段(白名单);空则默认 UpdateTime Desc。
+ ///
+ public string? SortField { get; set; }
+
+ ///
+ /// asc / desc。
+ ///
+ public string? SortOrder { get; set; }
+
public List? ScopeOrganizationIds { get; set; }
public List? ScopeSecretLevels { get; set; }
diff --git a/src/Oms.SqlSugarCore/Orders/OrderRepository.cs b/src/Oms.SqlSugarCore/Orders/OrderRepository.cs
index 862c0bb..d0279ae 100644
--- a/src/Oms.SqlSugarCore/Orders/OrderRepository.cs
+++ b/src/Oms.SqlSugarCore/Orders/OrderRepository.cs
@@ -19,9 +19,8 @@ public class OrderRepository : OmsSqlSugarRepository, IOrderRepository, I
var db = await GetDbContextAsync(cancellationToken);
var query = ApplyListFilter(db.Queryable(), filter);
var total = await query.CountAsync(cancellationToken);
+ query = ApplyListSort(query, filter);
var items = await query
- .OrderBy(x => x.UpdateTime, OrderByType.Desc)
- .OrderBy(x => x.Id, OrderByType.Desc)
.ToPageListAsync(pageIndex, pageSize, cancellationToken);
return (items, total);
}
@@ -31,9 +30,7 @@ public class OrderRepository : OmsSqlSugarRepository, IOrderRepository, I
CancellationToken cancellationToken = default)
{
var db = await GetDbContextAsync(cancellationToken);
- return await ApplyListFilter(db.Queryable(), filter)
- .OrderBy(x => x.UpdateTime, OrderByType.Desc)
- .OrderBy(x => x.Id, OrderByType.Desc)
+ return await ApplyListSort(ApplyListFilter(db.Queryable(), filter), filter)
.ToListAsync(cancellationToken);
}
@@ -98,11 +95,502 @@ public class OrderRepository : OmsSqlSugarRepository, IOrderRepository, I
query = query.Where(x => x.OrderSource == orderSource);
}
+ query = ApplyFieldFilters(query, filter.FieldFilters);
+
query = OrderListScopeFilterApplier.Apply(query, filter);
return query;
}
+ private static ISugarQueryable ApplyListSort(
+ ISugarQueryable 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
+ {
+ query = query.OrderBy(x => x.UpdateTime, OrderByType.Desc);
+ }
+
+ return query.OrderBy(x => x.Id, OrderByType.Desc);
+ }
+
+ private static ISugarQueryable ApplyFieldFilters(
+ ISugarQueryable query,
+ List? 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, "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 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 ApplyStringField(
+ ISugarQueryable query,
+ string op,
+ string value,
+ List values,
+ Func, string, ISugarQueryable> like,
+ Func, string, ISugarQueryable> eq,
+ Func, string, ISugarQueryable> ne,
+ Func, List, ISugarQueryable> inn,
+ Func, List, ISugarQueryable> notIn,
+ Func, ISugarQueryable> empty,
+ Func, ISugarQueryable> 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 ApplyDecimalField(
+ ISugarQueryable query,
+ string op,
+ string value,
+ string? value2,
+ Func, decimal, ISugarQueryable> eq,
+ Func, decimal, ISugarQueryable> ne,
+ Func, decimal, ISugarQueryable> gt,
+ Func, decimal, ISugarQueryable> gte,
+ Func, decimal, ISugarQueryable> lt,
+ Func, decimal, ISugarQueryable> lte,
+ Func, decimal, decimal, ISugarQueryable> between,
+ Func, ISugarQueryable> empty,
+ Func, ISugarQueryable> 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 ApplyDateField(
+ ISugarQueryable 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 ApplyDateCompare(
+ ISugarQueryable 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 GetByServiceNoAsync(string serviceNo, CancellationToken cancellationToken = default)
{
return FirstOrDefaultAsync(x => x.ServiceNo == serviceNo, cancellationToken);
@@ -523,6 +1011,9 @@ public class OrderRepository : OmsSqlSugarRepository, IOrderRepository, I
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,