diff --git a/db.sql b/db.sql index c4ae5ff..3d29fcc 100644 --- a/db.sql +++ b/db.sql @@ -403,7 +403,7 @@ CREATE TABLE `t_order_declaration` ( `HsCode` VARCHAR(50) NULL COMMENT 'HsCode', `ImHsCode` VARCHAR(50) NULL COMMENT '进口HsCode', `ExHsCode` VARCHAR(50) NULL COMMENT '出口HsCode', - `SkuImage` VARCHAR(255) NULL COMMENT '图片', + `SkuImage` VARCHAR(512) NULL COMMENT '图片(存 storage key 或短 URL)', `Category` VARCHAR(20) NULL COMMENT '类别', `Brand` VARCHAR(50) NULL COMMENT '品牌', `BrandType` VARCHAR(50) NULL COMMENT '品牌类型', diff --git a/sql/migrate_t_order_declaration_sku_image.sql b/sql/migrate_t_order_declaration_sku_image.sql new file mode 100644 index 0000000..7daa0af --- /dev/null +++ b/sql/migrate_t_order_declaration_sku_image.sql @@ -0,0 +1,27 @@ +-- t_order_declaration.SkuImage:库内 SKU 选图后写入签名 URL 易超 VARCHAR(255) +-- 后端已改为存 storage key;列宽对齐仓库 SkuImageUrl,兼容外链 + +SET @db := DATABASE(); + +SET @need := ( + SELECT COUNT(*) + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @db + AND TABLE_NAME = 't_order_declaration' + AND COLUMN_NAME = 'SkuImage' + AND ( + DATA_TYPE <> 'varchar' + OR CHARACTER_MAXIMUM_LENGTH IS NULL + OR CHARACTER_MAXIMUM_LENGTH < 512 + ) +); + +SET @sql := IF( + @need > 0, + 'ALTER TABLE `t_order_declaration` MODIFY COLUMN `SkuImage` VARCHAR(512) NULL COMMENT ''图片(存 storage key 或短 URL)''', + 'SELECT ''skip t_order_declaration.SkuImage'' AS msg' +); + +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; diff --git a/src/Oms.Application/Dtos/Warehouse/WarehouseDtos.cs b/src/Oms.Application/Dtos/Warehouse/WarehouseDtos.cs index 85f3df5..ebdd332 100644 --- a/src/Oms.Application/Dtos/Warehouse/WarehouseDtos.cs +++ b/src/Oms.Application/Dtos/Warehouse/WarehouseDtos.cs @@ -335,6 +335,15 @@ public class WarehouseInboundSummaryOutput public int Completed { get; set; } } +public class WarehouseParcelSummaryOutput +{ + public int InTransit { get; set; } + + public int Arrived { get; set; } + + public int Stored { get; set; } +} + // --- Parcel --- public class WarehouseParcelListInput : WarehousePageInput diff --git a/src/Oms.Application/Services/Orders/Batch/OrderBatchActionExecutor.cs b/src/Oms.Application/Services/Orders/Batch/OrderBatchActionExecutor.cs index ab02c47..77113cc 100644 --- a/src/Oms.Application/Services/Orders/Batch/OrderBatchActionExecutor.cs +++ b/src/Oms.Application/Services/Orders/Batch/OrderBatchActionExecutor.cs @@ -6,6 +6,7 @@ using Oms.Application.Integrations.Itfx; using Oms.Application.Integrations.SmallBagTrack; using Oms.Application.Messaging; using Oms.Application.Shared; +using Oms.Application.Storage; using Oms.Domain.OrdersAggregate.Consts; using Oms.Domain.OrdersAggregate.Entities; using Oms.Domain.OrdersAggregate.Repositories; @@ -16,6 +17,7 @@ namespace Oms.Application.Services.Orders.Batch; public sealed class OrderBatchActionExecutor( IOrderRepository orderRepository, ICountryRepository countryRepository, + IFileStorage fileStorage, ISmallBagTrackOrderClient smallBagTrackOrderClient, ISmallBagTrackUpsertRequestBuilder smallBagTrackUpsertRequestBuilder, OrderDownstreamCredentialResolver downstreamCredentialResolver, @@ -176,6 +178,7 @@ public sealed class OrderBatchActionExecutor( bundle.Declarations, bundle.Weights, countryRepository, + fileStorage, cancellationToken); } diff --git a/src/Oms.Application/Services/Orders/Batch/OrderBatchJobCoordinator.cs b/src/Oms.Application/Services/Orders/Batch/OrderBatchJobCoordinator.cs index a313f04..b7df2fa 100644 --- a/src/Oms.Application/Services/Orders/Batch/OrderBatchJobCoordinator.cs +++ b/src/Oms.Application/Services/Orders/Batch/OrderBatchJobCoordinator.cs @@ -8,6 +8,7 @@ using Oms.Application.Dtos.Shared; using Oms.Application.Integrations.SmallBagTrack; using Oms.Application.Messaging; using Oms.Application.Shared; +using Oms.Application.Storage; using Oms.Domain.OrdersAggregate.Consts; using System.Linq.Expressions; using Oms.Domain.OrdersAggregate.Entities; @@ -23,6 +24,7 @@ public sealed class OrderBatchJobCoordinator( IOrderBatchJobRepository batchJobRepository, IOrderRepository orderRepository, ICountryRepository countryRepository, + IFileStorage fileStorage, IOrderBatchJobPublisher publisher, OrderBatchJobProcessor batchJobProcessor, IOptions batchOptions, @@ -649,6 +651,7 @@ public sealed class OrderBatchJobCoordinator( bundle.Declarations, bundle.Weights, countryRepository, + fileStorage, cancellationToken); result[orderId] = JsonConvert.SerializeObject(body); } diff --git a/src/Oms.Application/Services/Orders/OrderService.DownstreamForward.cs b/src/Oms.Application/Services/Orders/OrderService.DownstreamForward.cs index 3ac11e3..3c90eda 100644 --- a/src/Oms.Application/Services/Orders/OrderService.DownstreamForward.cs +++ b/src/Oms.Application/Services/Orders/OrderService.DownstreamForward.cs @@ -97,7 +97,8 @@ public partial class OrderService bundle.Recipient, bundle.Declarations, bundle.Weights, - CountryRepository); + CountryRepository, + FileStorage); return (bundle.Order, body); } diff --git a/src/Oms.Application/Services/Orders/OrderService.Helpers.cs b/src/Oms.Application/Services/Orders/OrderService.Helpers.cs index fc27a3d..5dbd124 100644 --- a/src/Oms.Application/Services/Orders/OrderService.Helpers.cs +++ b/src/Oms.Application/Services/Orders/OrderService.Helpers.cs @@ -743,7 +743,7 @@ public partial class OrderService }; } - private static OrderItemOutput MapDeclaration(OrderDeclaration x) + private OrderItemOutput MapDeclaration(OrderDeclaration x) { return new OrderItemOutput { @@ -759,7 +759,7 @@ public partial class OrderService HsCode = x.ExHsCode ?? x.ImHsCode ?? x.HsCode, ImHsCode = x.ImHsCode ?? x.HsCode, ExHsCode = x.ExHsCode ?? x.HsCode, - SkuImage = x.SkuImage, + SkuImage = FileStorage.GetPublicUrl(x.SkuImage), Category = x.Category, Brand = x.Brand, BrandType = x.BrandType, diff --git a/src/Oms.Application/Services/Orders/OrderService.OrderEdit.cs b/src/Oms.Application/Services/Orders/OrderService.OrderEdit.cs index 61c9183..4f0296f 100644 --- a/src/Oms.Application/Services/Orders/OrderService.OrderEdit.cs +++ b/src/Oms.Application/Services/Orders/OrderService.OrderEdit.cs @@ -894,7 +894,7 @@ public partial class OrderService _ => (OrderWeightTypeCodes.Forecast, OrderFeeChargingStageCodes.Forecast) }; - private static OrderDeclaration MapDeclarationInput(int orderId, OrderParcelItemInput x) + private OrderDeclaration MapDeclarationInput(int orderId, OrderParcelItemInput x) { return new OrderDeclaration { @@ -911,7 +911,7 @@ public partial class OrderService HsCode = x.ExHsCode ?? x.ImHsCode, ImHsCode = x.ImHsCode, ExHsCode = x.ExHsCode ?? x.ImHsCode, - SkuImage = x.SkuImage, + SkuImage = FileStorage.ToStorageKey(x.SkuImage), Category = x.Category, Brand = x.Brand, BrandType = x.BrandType, diff --git a/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs b/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs index 87b4e80..974c13e 100644 --- a/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs +++ b/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs @@ -1965,7 +1965,7 @@ public partial class OrderService HsCode = x.ExHsCode ?? x.ImHsCode, ImHsCode = x.ImHsCode, ExHsCode = x.ExHsCode, - SkuImage = x.SkuImage, + SkuImage = FileStorage.ToStorageKey(x.SkuImage), Category = x.Category, Brand = x.Brand, BrandType = x.BrandType, diff --git a/src/Oms.Application/Services/Warehouse/WarehouseService.Parcel.cs b/src/Oms.Application/Services/Warehouse/WarehouseService.Parcel.cs index 4ce24e3..1cf98cd 100644 --- a/src/Oms.Application/Services/Warehouse/WarehouseService.Parcel.cs +++ b/src/Oms.Application/Services/Warehouse/WarehouseService.Parcel.cs @@ -79,6 +79,43 @@ public partial class WarehouseService }; } + /// + /// 查询包裹生命周期状态汇总。 + /// + /// 客户 ID(可选)。 + /// 仓库 ID(可选)。 + /// 各状态数量汇总。 + [HttpGet("/warehouse/parcel/summary")] + public async Task GetParcelSummaryAsync( + [FromQuery] int? customerId = null, + [FromQuery] int? warehouseId = null) + { + var resolvedCustomerId = await ResolveListCustomerIdAsync(customerId); + var (hasCustomerFilter, customerIdValue) = CustomerFilter(resolvedCustomerId); + var hasWarehouseFilter = warehouseId.HasValue; + var warehouseIdValue = warehouseId.GetValueOrDefault(); + + var inTransit = await ParcelRepository.CountAsync(x => + (!hasCustomerFilter || x.CustomerId == customerIdValue) + && (!hasWarehouseFilter || x.WarehouseId == warehouseIdValue) + && x.LifecycleStatus == WarehouseParcelLifecycleCodes.InTransit); + var arrived = await ParcelRepository.CountAsync(x => + (!hasCustomerFilter || x.CustomerId == customerIdValue) + && (!hasWarehouseFilter || x.WarehouseId == warehouseIdValue) + && x.LifecycleStatus == WarehouseParcelLifecycleCodes.Arrived); + var stored = await ParcelRepository.CountAsync(x => + (!hasCustomerFilter || x.CustomerId == customerIdValue) + && (!hasWarehouseFilter || x.WarehouseId == warehouseIdValue) + && x.LifecycleStatus == WarehouseParcelLifecycleCodes.Stored); + + return new WarehouseParcelSummaryOutput + { + InTransit = inTransit, + Arrived = arrived, + Stored = stored + }; + } + /// /// 规范化列表状态筛选:空 / all 表示不按状态过滤;已知编码统一为规范值。 /// diff --git a/src/Oms.Application/Shared/OrderDownstreamCreateOneBodyBuilder.cs b/src/Oms.Application/Shared/OrderDownstreamCreateOneBodyBuilder.cs index 1a29df2..fadb54e 100644 --- a/src/Oms.Application/Shared/OrderDownstreamCreateOneBodyBuilder.cs +++ b/src/Oms.Application/Shared/OrderDownstreamCreateOneBodyBuilder.cs @@ -1,4 +1,5 @@ using Oms.Application.Dtos.Orders; +using Oms.Application.Storage; using Oms.Domain.OrdersAggregate.Consts; using Oms.Domain.OrdersAggregate.Entities; using Oms.Domain.SystemAggregate.Repositories; @@ -17,6 +18,7 @@ public static class OrderDownstreamCreateOneBodyBuilder IReadOnlyList declarations, IReadOnlyList weights, ICountryRepository countryRepository, + IFileStorage? fileStorage = null, CancellationToken cancellationToken = default) { var countryIds = new List(); @@ -66,7 +68,7 @@ public static class OrderDownstreamCreateOneBodyBuilder Quantity = order.Quantity, Receiver = MapReceiver(recipient, order.ConsigneeCountry, idToCode), Sender = MapSender(sender, idToCode), - Items = declarations.Select(MapItem).ToList() + Items = declarations.Select(x => MapItem(x, fileStorage)).ToList() }; } @@ -150,7 +152,7 @@ public static class OrderDownstreamCreateOneBodyBuilder }; } - private static OrderParcelItemInput MapItem(OrderDeclaration x) => + private static OrderParcelItemInput MapItem(OrderDeclaration x, IFileStorage? fileStorage) => new() { CnName = x.CnName, @@ -164,7 +166,7 @@ public static class OrderDownstreamCreateOneBodyBuilder Weight = x.Weight, ImHsCode = x.ImHsCode, ExHsCode = x.ExHsCode ?? x.HsCode, - SkuImage = x.SkuImage, + SkuImage = fileStorage?.GetPublicUrl(x.SkuImage) ?? x.SkuImage, Category = x.Category, Brand = x.Brand, BrandType = x.BrandType, diff --git a/src/Oms.Tests/OrderBatchJobCoordinatorGovernanceTests.cs b/src/Oms.Tests/OrderBatchJobCoordinatorGovernanceTests.cs index 537eb2a..aa50039 100644 --- a/src/Oms.Tests/OrderBatchJobCoordinatorGovernanceTests.cs +++ b/src/Oms.Tests/OrderBatchJobCoordinatorGovernanceTests.cs @@ -6,6 +6,7 @@ using Moq; using Oms.Application.Dtos.Orders; using Oms.Application.Messaging; using Oms.Application.Services.Orders.Batch; +using Oms.Application.Storage; using Oms.Domain.OrdersAggregate.Consts; using Oms.Domain.OrdersAggregate.Entities; using Oms.Domain.OrdersAggregate.Repositories; @@ -18,6 +19,7 @@ public class OrderBatchJobCoordinatorGovernanceTests private readonly Mock _batchJobRepository = new(MockBehavior.Strict); private readonly Mock _orderRepository = new(MockBehavior.Loose); private readonly Mock _countryRepository = new(MockBehavior.Loose); + private readonly Mock _fileStorage = new(MockBehavior.Loose); private readonly Mock _publisher = new(MockBehavior.Strict); private readonly OrderBatchJobCoordinator _coordinator; @@ -36,6 +38,7 @@ public class OrderBatchJobCoordinatorGovernanceTests _batchJobRepository.Object, _orderRepository.Object, _countryRepository.Object, + _fileStorage.Object, _publisher.Object, processor, batchOptions,