diff --git a/db.sql b/db.sql
index 009c706..b2c744a 100644
--- a/db.sql
+++ b/db.sql
@@ -214,7 +214,7 @@ CREATE TABLE `t_order` (
CREATE TABLE `t_order_file` (
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键,自增',
`OrderId` INT NOT NULL COMMENT '订单Id',
- `FileType` VARCHAR(32) NOT NULL COMMENT '附件类型:tradeProof交易证明/systemLabel系统面单/downstreamLabel下游面单/lastMileLabel末端面单/infraredImage红外图片',
+ `FileType` VARCHAR(32) NOT NULL COMMENT '附件类型:tradeProof交易证明/systemLabel系统面单/downstreamLabel下游面单/lastMileLabel末端面单/infraredImage红外图片/pod签收证明',
`FileName` VARCHAR(255) NULL COMMENT '原始文件名',
`FileUrl` VARCHAR(512) NOT NULL COMMENT '文件地址(相对路径或外链)',
`ContentType` VARCHAR(128) NULL COMMENT '文件MIME类型',
diff --git a/src/Oms.Application/Dtos/OpenApi/OpenApiQueryDtos.cs b/src/Oms.Application/Dtos/OpenApi/OpenApiQueryDtos.cs
new file mode 100644
index 0000000..a1299f9
--- /dev/null
+++ b/src/Oms.Application/Dtos/OpenApi/OpenApiQueryDtos.cs
@@ -0,0 +1,92 @@
+namespace Oms.Application.Dtos.OpenApi;
+
+///
+/// 按客户单号 / 服务单号 / 跟踪号定位订单(至少填一项)。
+///
+public class OpenApiOrderLookupInput
+{
+ public string? ClientNo { get; set; }
+
+ public string? ServiceNo { get; set; }
+
+ public string? TrackNo { get; set; }
+}
+
+public class OpenApiTrackQueryInput : OpenApiOrderLookupInput
+{
+}
+
+public class OpenApiFilesGetInput : OpenApiOrderLookupInput
+{
+ ///
+ /// 附件类型:tradeProof / systemLabel / lastMileLabel / infraredImage / pod。不含 downstreamLabel。
+ ///
+ public string? FileType { get; set; }
+}
+
+public class OpenApiTrackQueryOutput
+{
+ public int OrderId { get; set; }
+
+ public string? ClientNo { get; set; }
+
+ public string? ServiceNo { get; set; }
+
+ public string? TrackNo { get; set; }
+
+ public List Events { get; set; } = [];
+}
+
+public class OpenApiTrackEventDto
+{
+ public string? TrackCode { get; set; }
+
+ public string? Status { get; set; }
+
+ public string? Content { get; set; }
+
+ public string? EnContent { get; set; }
+
+ public string? Location { get; set; }
+
+ public DateTime? Time { get; set; }
+
+ public List Files { get; set; } = [];
+}
+
+public class OpenApiTrackFileDto
+{
+ public string? FileName { get; set; }
+
+ public string? Url { get; set; }
+}
+
+public class OpenApiFilesGetOutput
+{
+ public int OrderId { get; set; }
+
+ public string? ClientNo { get; set; }
+
+ public string? ServiceNo { get; set; }
+
+ public string FileType { get; set; } = string.Empty;
+
+ public List Files { get; set; } = [];
+}
+
+public class OpenApiFileItemDto
+{
+ public int Id { get; set; }
+
+ public string? FileName { get; set; }
+
+ public string FileUrl { get; set; } = string.Empty;
+
+ public string? ContentType { get; set; }
+
+ public long? FileSize { get; set; }
+
+ public string? Source { get; set; }
+
+ public DateTime CreateTime { get; set; }
+}
diff --git a/src/Oms.Application/Integrations/SmallBagTrack/ISmallBagTrackOrderClient.cs b/src/Oms.Application/Integrations/SmallBagTrack/ISmallBagTrackOrderClient.cs
index c3e8190..15b8a0b 100644
--- a/src/Oms.Application/Integrations/SmallBagTrack/ISmallBagTrackOrderClient.cs
+++ b/src/Oms.Application/Integrations/SmallBagTrack/ISmallBagTrackOrderClient.cs
@@ -1,3 +1,5 @@
+using Oms.Application.Dtos.OpenApi;
+
namespace Oms.Application.Integrations.SmallBagTrack;
public interface ISmallBagTrackOrderClient
@@ -45,4 +47,11 @@ public interface ISmallBagTrackOrderClient
int linkOrderType,
string? authorization = null,
CancellationToken cancellationToken = default);
+
+ ///
+ /// 查询小包轨迹(POST ProviderService/Track/Query)。
+ ///
+ Task> QuerySmallBagTracksAsync(
+ string orderNo,
+ CancellationToken cancellationToken = default);
}
diff --git a/src/Oms.Application/Integrations/SmallBagTrack/SmallBagTrackOrderClient.cs b/src/Oms.Application/Integrations/SmallBagTrack/SmallBagTrackOrderClient.cs
index ecb80a1..1d1189f 100644
--- a/src/Oms.Application/Integrations/SmallBagTrack/SmallBagTrackOrderClient.cs
+++ b/src/Oms.Application/Integrations/SmallBagTrack/SmallBagTrackOrderClient.cs
@@ -1,8 +1,10 @@
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
+using Fake.Application;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
+using Oms.Application.Dtos.OpenApi;
namespace Oms.Application.Integrations.SmallBagTrack;
@@ -424,6 +426,178 @@ internal sealed class SmallBagTrackOrderClient(
}
}
+ public async Task> QuerySmallBagTracksAsync(
+ string orderNo,
+ CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrWhiteSpace(orderNo))
+ {
+ throw new BusinessException("服务单号为空,无法查询轨迹");
+ }
+
+ var settings = await configProvider.GetAsync(cancellationToken);
+ if (!settings.Enabled)
+ {
+ throw new BusinessException("轨迹服务未启用");
+ }
+
+ if (string.IsNullOrWhiteSpace(settings.BaseUrl))
+ {
+ throw new BusinessException("轨迹服务未配置");
+ }
+
+ if (string.IsNullOrWhiteSpace(settings.AppKey) || string.IsNullOrWhiteSpace(settings.AppSecret))
+ {
+ throw new BusinessException("轨迹服务未配置");
+ }
+
+ var appKey = settings.AppKey.Trim();
+ var url = $"{settings.BaseUrl.Trim().TrimEnd('/')}/ProviderService/Track/Query";
+ var body = new
+ {
+ appKey,
+ orderType = SmallBagTrackLifecycleEvents.OrderType,
+ orderNos = new[] { orderNo.Trim() }
+ };
+
+ var response = await SendSignedPostAsync(
+ settings,
+ appKey,
+ url,
+ body,
+ authorization: null,
+ cancellationToken);
+
+ if (!response.Response.IsSuccessStatusCode)
+ {
+ throw new BusinessException("轨迹查询失败");
+ }
+
+ return ParseTrackEvents(response.ResponseBody);
+ }
+
+ private static List ParseTrackEvents(string json)
+ {
+ if (string.IsNullOrWhiteSpace(json))
+ {
+ return [];
+ }
+
+ using var doc = JsonDocument.Parse(json);
+ var root = doc.RootElement;
+ var arr = root;
+ if (root.ValueKind == JsonValueKind.Object
+ && (root.TryGetProperty("data", out var data) || root.TryGetProperty("Data", out data)))
+ {
+ arr = data;
+ }
+
+ if (arr.ValueKind != JsonValueKind.Array)
+ {
+ return [];
+ }
+
+ var events = new List();
+ foreach (var orderEl in arr.EnumerateArray())
+ {
+ if (orderEl.ValueKind != JsonValueKind.Object)
+ {
+ continue;
+ }
+
+ if (!orderEl.TryGetProperty("trackDetails", out var details)
+ && !orderEl.TryGetProperty("TrackDetails", out details))
+ {
+ continue;
+ }
+
+ if (details.ValueKind != JsonValueKind.Array)
+ {
+ continue;
+ }
+
+ foreach (var el in details.EnumerateArray())
+ {
+ var content = ReadJsonString(el, "content", "Content");
+ if (string.IsNullOrWhiteSpace(content) || content == "--")
+ {
+ continue;
+ }
+
+ events.Add(new OpenApiTrackEventDto
+ {
+ TrackCode = ReadJsonString(el, "code", "Code"),
+ Status = ReadJsonString(el, "status", "Status"),
+ Content = content,
+ EnContent = ReadJsonString(el, "enContent", "EnContent"),
+ Location = ReadJsonString(el, "location", "Location"),
+ Time = ReadJsonTime(el),
+ Files = ReadJsonFiles(el)
+ });
+ }
+ }
+
+ return events;
+ }
+
+ private static List ReadJsonFiles(JsonElement el)
+ {
+ if (!el.TryGetProperty("files", out var files) && !el.TryGetProperty("Files", out files))
+ {
+ return [];
+ }
+
+ if (files.ValueKind != JsonValueKind.Array)
+ {
+ return [];
+ }
+
+ var list = new List();
+ foreach (var f in files.EnumerateArray())
+ {
+ list.Add(new OpenApiTrackFileDto
+ {
+ FileName = ReadJsonString(f, "fileName", "FileName"),
+ Url = ReadJsonString(f, "downloadUrl", "DownloadUrl", "url", "Url")
+ });
+ }
+
+ return list;
+ }
+
+ private static DateTime? ReadJsonTime(JsonElement el)
+ {
+ if (!el.TryGetProperty("time", out var time) && !el.TryGetProperty("Time", out time))
+ {
+ return null;
+ }
+
+ if (time.ValueKind == JsonValueKind.String && time.TryGetDateTime(out var dt))
+ {
+ return dt;
+ }
+
+ if (time.ValueKind == JsonValueKind.Number && time.TryGetInt64(out var unix))
+ {
+ return DateTimeOffset.FromUnixTimeSeconds(unix > 10_000_000_000 ? unix / 1000 : unix).UtcDateTime;
+ }
+
+ return null;
+ }
+
+ private static string? ReadJsonString(JsonElement el, params string[] names)
+ {
+ foreach (var name in names)
+ {
+ if (el.TryGetProperty(name, out var p) && p.ValueKind == JsonValueKind.String)
+ {
+ return p.GetString();
+ }
+ }
+
+ return null;
+ }
+
private async Task SendSignedPostAsync(
SmallBagTrackOptions settings,
string appKey,
diff --git a/src/Oms.Application/Services/Customers/CustomerOpenApiCredentialService.cs b/src/Oms.Application/Services/Customers/CustomerOpenApiCredentialService.cs
index 1381ab6..787e720 100644
--- a/src/Oms.Application/Services/Customers/CustomerOpenApiCredentialService.cs
+++ b/src/Oms.Application/Services/Customers/CustomerOpenApiCredentialService.cs
@@ -181,13 +181,14 @@ public class CustomerOpenApiCredentialService : ApplicationService
{
if (!OmsSessionHelper.IsCustomerLogin(CurrentUser))
{
- throw new BusinessException("仅客户门户可调用此接口");
+ throw new BusinessException(
+ "当前是管理后台登录,不能调用客户门户接口。请到「客户」列表打开对应客户的开放接口凭证;客户请从客户门户登录后操作");
}
var mapId = OmsSessionHelper.TryGetCustomerMapId(CurrentUser);
if (!mapId.HasValue || mapId.Value <= 0)
{
- throw new BusinessException("客户未登录");
+ throw new BusinessException("当前登录不是客户账号,无法读取开放接口凭证");
}
var customer = await CustomerRepository.FirstOrDefaultAsync(x => x.UserId == mapId.Value);
diff --git a/src/Oms.Application/Services/OpenApi/OpenApiService.cs b/src/Oms.Application/Services/OpenApi/OpenApiService.cs
index 0076fbb..6d661e3 100644
--- a/src/Oms.Application/Services/OpenApi/OpenApiService.cs
+++ b/src/Oms.Application/Services/OpenApi/OpenApiService.cs
@@ -6,8 +6,10 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Oms.Application.Dtos.OpenApi;
using Oms.Application.Dtos.Orders;
+using Oms.Application.Integrations.SmallBagTrack;
using Oms.Application.Services.Orders;
using Oms.Application.Shared;
+using Oms.Domain.OrdersAggregate.Consts;
namespace Oms.Application.Services.OpenApi;
@@ -33,6 +35,15 @@ public class OpenApiService : ApplicationService
private OrderService OrderService =>
LazyServiceProvider.GetRequiredService();
+ private OrderFileManager OrderFileManager =>
+ LazyServiceProvider.GetRequiredService();
+
+ private OpenApiCustomerOrderResolver OrderResolver =>
+ LazyServiceProvider.GetRequiredService();
+
+ private ISmallBagTrackOrderClient TrackClient =>
+ LazyServiceProvider.GetRequiredService();
+
private IHttpContextAccessor HttpContextAccessor =>
LazyServiceProvider.GetRequiredService();
@@ -84,7 +95,8 @@ public class OpenApiService : ApplicationService
throw new BusinessException("请求体不能为空");
}
- var data = await OrderService.CreateParcelAsync(input.ToOrderParcelCreateInput(customer.Id));
+ var data = await OrderService.CreateParcelForCustomerOpenApiAsync(
+ input.ToOrderParcelCreateInput(customer.Id));
return OpenApiEnvelope.Ok(data);
}
catch (BusinessException ex)
@@ -103,6 +115,131 @@ public class OpenApiService : ApplicationService
}
}
+ ///
+ /// 查询轨迹。Header 同创建包裹。按 clientNo / serviceNo / trackNo 定位本客户订单。
+ ///
+ [AllowAnonymous]
+ [HttpPost("/openapi/track/query")]
+ public async Task> QueryTrackAsync(
+ [FromHeader(Name = CustomerOpenApiAuthHelper.AppKeyHeaderName)] string? appKey,
+ [FromHeader(Name = CustomerOpenApiAuthHelper.TimestampHeaderName)] string? timestamp,
+ [FromHeader(Name = CustomerOpenApiAuthHelper.SignHeaderName)] string? sign)
+ {
+ try
+ {
+ var rawBody = await ReadRawBodyAsync();
+ var (customer, _) = await AuthHelper.ResolveSignedAsync(appKey, timestamp, sign, rawBody);
+ var input = DeserializeBody(rawBody);
+ var order = await OrderResolver.ResolveAsync(customer.Id, input);
+ if (string.IsNullOrWhiteSpace(order.ServiceNo))
+ {
+ throw new BusinessException("订单无服务单号,无法查询轨迹");
+ }
+
+ var events = await TrackClient.QuerySmallBagTracksAsync(order.ServiceNo);
+ return OpenApiEnvelope.Ok(new OpenApiTrackQueryOutput
+ {
+ OrderId = order.Id,
+ ClientNo = order.ClientNo,
+ ServiceNo = order.ServiceNo,
+ TrackNo = order.TrackNo,
+ Events = events
+ });
+ }
+ catch (BusinessException ex)
+ {
+ return OpenApiEnvelope.Fail(ex.Message);
+ }
+ catch (JsonException)
+ {
+ return OpenApiEnvelope.Fail("请求体格式错误");
+ }
+ catch (Exception ex)
+ {
+ return OpenApiEnvelope.Fail(
+ string.IsNullOrWhiteSpace(ex.Message) ? "系统异常" : ex.Message,
+ "500");
+ }
+ }
+
+ ///
+ /// 按附件类型获取文件。Header 同创建包裹。
+ /// fileType:tradeProof / systemLabel / lastMileLabel / infraredImage / pod。不含 downstreamLabel。
+ ///
+ [AllowAnonymous]
+ [HttpPost("/openapi/files/get")]
+ public async Task> GetFilesAsync(
+ [FromHeader(Name = CustomerOpenApiAuthHelper.AppKeyHeaderName)] string? appKey,
+ [FromHeader(Name = CustomerOpenApiAuthHelper.TimestampHeaderName)] string? timestamp,
+ [FromHeader(Name = CustomerOpenApiAuthHelper.SignHeaderName)] string? sign)
+ {
+ try
+ {
+ var rawBody = await ReadRawBodyAsync();
+ var (customer, _) = await AuthHelper.ResolveSignedAsync(appKey, timestamp, sign, rawBody);
+ var input = DeserializeBody(rawBody);
+ if (string.IsNullOrWhiteSpace(input.FileType))
+ {
+ throw new BusinessException("请传入 fileType");
+ }
+
+ if (!OrderFileTypeCodes.IsCustomerOpenApiAllowed(input.FileType))
+ {
+ throw new BusinessException("不支持的附件类型");
+ }
+
+ var order = await OrderResolver.ResolveAsync(customer.Id, input);
+ var files = await OrderFileManager.GetByTypeAsync(order.Id, input.FileType);
+ return OpenApiEnvelope.Ok(new OpenApiFilesGetOutput
+ {
+ OrderId = order.Id,
+ ClientNo = order.ClientNo,
+ ServiceNo = order.ServiceNo,
+ FileType = OrderFileTypeCodes.Normalize(input.FileType),
+ Files = files.Select(x => new OpenApiFileItemDto
+ {
+ Id = x.Id,
+ FileName = x.FileName,
+ FileUrl = x.FileUrl,
+ ContentType = x.ContentType,
+ FileSize = x.FileSize,
+ Source = x.Source,
+ CreateTime = x.CreateTime
+ }).ToList()
+ });
+ }
+ catch (BusinessException ex)
+ {
+ return OpenApiEnvelope.Fail(ex.Message);
+ }
+ catch (JsonException)
+ {
+ return OpenApiEnvelope.Fail("请求体格式错误");
+ }
+ catch (Exception ex)
+ {
+ return OpenApiEnvelope.Fail(
+ string.IsNullOrWhiteSpace(ex.Message) ? "系统异常" : ex.Message,
+ "500");
+ }
+ }
+
+ private T DeserializeBody(string rawBody)
+ {
+ if (string.IsNullOrWhiteSpace(rawBody))
+ {
+ throw new BusinessException("请求体不能为空");
+ }
+
+ var input = JsonSerializer.Deserialize(rawBody, BodyJsonOptions);
+ if (input == null)
+ {
+ throw new BusinessException("请求体不能为空");
+ }
+
+ return input;
+ }
+
private async Task ReadRawBodyAsync()
{
var http = HttpContextAccessor.HttpContext
diff --git a/src/Oms.Application/Services/Orders/OrderFileManager.cs b/src/Oms.Application/Services/Orders/OrderFileManager.cs
index 62e273e..3cf553b 100644
--- a/src/Oms.Application/Services/Orders/OrderFileManager.cs
+++ b/src/Oms.Application/Services/Orders/OrderFileManager.cs
@@ -66,6 +66,25 @@ public sealed class OrderFileManager(
return result;
}
+ public async Task> GetByTypeAsync(
+ int orderId,
+ string fileType,
+ CancellationToken cancellationToken = default)
+ {
+ if (!OrderFileTypeCodes.IsValid(fileType))
+ {
+ throw new BusinessException("不支持的附件类型");
+ }
+
+ var type = OrderFileTypeCodes.Normalize(fileType);
+ var list = await orderFileRepository.GetByOrderAndTypeListAsync(orderId, type, cancellationToken);
+ return list
+ .OrderBy(x => x.Sort)
+ .ThenBy(x => x.Id)
+ .Select(MapItem)
+ .ToList();
+ }
+
public async Task GetGroupedAsync(
int orderId,
bool customerView,
@@ -302,7 +321,8 @@ public sealed class OrderFileManager(
or OrderFileTypeCodes.DownstreamLabel
or OrderFileTypeCodes.LastMileLabel;
var isInfraredOrProof = fileType is OrderFileTypeCodes.InfraredImage
- or OrderFileTypeCodes.TradeProof;
+ or OrderFileTypeCodes.TradeProof
+ or OrderFileTypeCodes.Pod;
if (isLabelType)
{
@@ -325,7 +345,7 @@ public sealed class OrderFileManager(
{
if (!ImageExt.Contains(ext) && !string.Equals(ext, ".pdf", StringComparison.OrdinalIgnoreCase))
{
- throw new BusinessException("交易证明/红外图片仅支持图片或 pdf");
+ throw new BusinessException("交易证明/红外图片/POD 仅支持图片或 pdf");
}
if (file.Length > MaxPdfBytes)
diff --git a/src/Oms.Application/Services/Orders/OrderService.cs b/src/Oms.Application/Services/Orders/OrderService.cs
index f052699..4d7ab7f 100644
--- a/src/Oms.Application/Services/Orders/OrderService.cs
+++ b/src/Oms.Application/Services/Orders/OrderService.cs
@@ -531,6 +531,46 @@ public partial class OrderService : ApplicationService
return output;
}
+ ///
+ /// 客户 OpenAPI 创单:同一客户相同 clientNo 视为幂等,返回已存在订单,不新建。
+ ///
+ public async Task CreateParcelForCustomerOpenApiAsync(OrderParcelCreateInput input)
+ {
+ NormalizeParcelCreateInput(input);
+ if (!string.IsNullOrWhiteSpace(input.ClientNo) && input.CustomerId is > 0)
+ {
+ var existing = await OrderRepository.GetByCustomerAndClientNoAsync(
+ input.CustomerId.Value,
+ input.ClientNo);
+ if (existing != null)
+ {
+ return await BuildParcelCreateOutputAsync(existing);
+ }
+ }
+
+ try
+ {
+ return await CreateParcelAsync(input);
+ }
+ catch (BusinessException ex) when (ex.Message == "客户单号已存在")
+ {
+ if (string.IsNullOrWhiteSpace(input.ClientNo) || input.CustomerId is not > 0)
+ {
+ throw;
+ }
+
+ var raced = await OrderRepository.GetByCustomerAndClientNoAsync(
+ input.CustomerId.Value,
+ input.ClientNo);
+ if (raced == null)
+ {
+ throw;
+ }
+
+ return await BuildParcelCreateOutputAsync(raced);
+ }
+ }
+
private async Task TryAttachPickupAfterParcelCreateAsync(
OrderParcelCreateInput input,
OrderParcelCreateOutput output)
diff --git a/src/Oms.Application/Shared/OpenApiCustomerOrderResolver.cs b/src/Oms.Application/Shared/OpenApiCustomerOrderResolver.cs
new file mode 100644
index 0000000..565cbce
--- /dev/null
+++ b/src/Oms.Application/Shared/OpenApiCustomerOrderResolver.cs
@@ -0,0 +1,54 @@
+using Fake.Application;
+using Fake.DependencyInjection;
+using Oms.Application.Dtos.OpenApi;
+using Oms.Domain.OrdersAggregate.Entities;
+using Oms.Domain.OrdersAggregate.Repositories;
+
+namespace Oms.Application.Shared;
+
+///
+/// OpenAPI 按客户凭证解析自有订单。
+///
+public class OpenApiCustomerOrderResolver : IScopedDependency
+{
+ private readonly IOrderRepository _orderRepository;
+
+ public OpenApiCustomerOrderResolver(IOrderRepository orderRepository)
+ {
+ _orderRepository = orderRepository;
+ }
+
+ public async Task ResolveAsync(int customerId, OpenApiOrderLookupInput input)
+ {
+ var clientNo = input.ClientNo?.Trim();
+ var serviceNo = input.ServiceNo?.Trim();
+ var trackNo = input.TrackNo?.Trim();
+ if (string.IsNullOrWhiteSpace(clientNo)
+ && string.IsNullOrWhiteSpace(serviceNo)
+ && string.IsNullOrWhiteSpace(trackNo))
+ {
+ throw new BusinessException("请传入 clientNo、serviceNo 或 trackNo");
+ }
+
+ Order? order = null;
+ if (!string.IsNullOrWhiteSpace(clientNo))
+ {
+ order = await _orderRepository.GetByCustomerAndClientNoAsync(customerId, clientNo);
+ }
+ else if (!string.IsNullOrWhiteSpace(serviceNo))
+ {
+ order = await _orderRepository.GetByServiceNoAsync(serviceNo);
+ }
+ else if (!string.IsNullOrWhiteSpace(trackNo))
+ {
+ order = await _orderRepository.GetByTrackNoAsync(trackNo);
+ }
+
+ if (order == null || order.CustomerId != customerId)
+ {
+ throw new BusinessException("订单不存在");
+ }
+
+ return order;
+ }
+}
diff --git a/src/Oms.Domain/OrdersAggregate/Consts/OrderFileTypeCodes.cs b/src/Oms.Domain/OrdersAggregate/Consts/OrderFileTypeCodes.cs
index f82a107..986fd4c 100644
--- a/src/Oms.Domain/OrdersAggregate/Consts/OrderFileTypeCodes.cs
+++ b/src/Oms.Domain/OrdersAggregate/Consts/OrderFileTypeCodes.cs
@@ -10,6 +10,7 @@ public static class OrderFileTypeCodes
public const string DownstreamLabel = "downstreamLabel";
public const string LastMileLabel = "lastMileLabel";
public const string InfraredImage = "infraredImage";
+ public const string Pod = "pod";
public const int MaxFilesPerType = 20;
@@ -19,7 +20,8 @@ public static class OrderFileTypeCodes
SystemLabel,
DownstreamLabel,
LastMileLabel,
- InfraredImage
+ InfraredImage,
+ Pod
};
///
@@ -31,6 +33,19 @@ public static class OrderFileTypeCodes
public static bool IsValid(string? fileType) =>
!string.IsNullOrWhiteSpace(fileType) && All.Contains(fileType.Trim());
+ ///
+ /// 客户 OpenAPI 可查询的附件类型。不含下游面单。
+ ///
+ public static bool IsCustomerOpenApiAllowed(string? fileType)
+ {
+ if (!IsValid(fileType))
+ {
+ return false;
+ }
+
+ return !string.Equals(fileType.Trim(), DownstreamLabel, StringComparison.OrdinalIgnoreCase);
+ }
+
public static string Normalize(string fileType) => fileType.Trim();
}
diff --git a/src/Oms.Domain/OrdersAggregate/Entities/OrderFile.cs b/src/Oms.Domain/OrdersAggregate/Entities/OrderFile.cs
index 3e6ecdd..a6105d8 100644
--- a/src/Oms.Domain/OrdersAggregate/Entities/OrderFile.cs
+++ b/src/Oms.Domain/OrdersAggregate/Entities/OrderFile.cs
@@ -9,7 +9,7 @@ public class OrderFile : OmsAuditedAggregateRoot
public int OrderId { get; set; }
///
- /// 附件类型:tradeProof / systemLabel / downstreamLabel / lastMileLabel / infraredImage。
+ /// 附件类型:tradeProof / systemLabel / downstreamLabel / lastMileLabel / infraredImage / pod。
///
[SugarColumn(Length = 32)]
public string FileType { get; set; } = string.Empty;