From f4ddd993b9ea4df5edbbd4350e5a868efe778aec Mon Sep 17 00:00:00 2001 From: zzh <1368735060@qq.com> Date: Sat, 18 Jul 2026 11:10:53 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=AE=A1=E7=90=86=E7=AB=AF=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E8=BF=94=E5=9B=9E=E5=AE=A2=E6=88=B7=E5=90=8D=E7=A7=B0?= =?UTF-8?q?/=E7=BC=96=E7=A0=81=EF=BC=8C=E5=B9=B6=E5=8A=A0=E5=9B=BA?= =?UTF-8?q?=E7=BB=91=E5=AE=9A=E4=B8=8E=E5=AE=A2=E6=88=B7=E4=BC=9A=E8=AF=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 订单/包裹/库存/店铺授权/店铺订单列表补充 customerName、customerCode;库存绑定按会话客户解析并兼容可用量;修复客户 mapId 识别与 ConcurrencyStamp 迁移。 Co-authored-by: Cursor --- .gitignore | 5 ++ SimpleAdmin.sln.DotSettings.user | 25 -------- sql/fix_concurrency_stamp_missing.sql | 63 +++++++++++++++++++ sql/migrate_t_order_stock_bind.sql | 16 ++++- .../Dtos/Orders/OrderOutput.cs | 10 +++ .../Dtos/PlatformStore/PlatformStoreDtos.cs | 8 +++ .../Dtos/Warehouse/WarehouseDtos.cs | 10 +++ .../Services/Orders/OrderService.Helpers.cs | 20 ++++++ .../Services/Orders/OrderService.StockBind.cs | 27 ++++++-- .../Services/Orders/OrderService.cs | 13 +++- .../Orders/PlatformStoreOrderService.cs | 31 ++++++++- .../Services/Orders/PlatformStoreService.cs | 26 +++++++- .../Warehouse/WarehouseService.Inventory.cs | 19 +++++- .../Warehouse/WarehouseService.Parcel.cs | 18 +++++- .../Shared/OmsSessionHelper.cs | 41 ++++++++++-- 15 files changed, 283 insertions(+), 49 deletions(-) delete mode 100644 SimpleAdmin.sln.DotSettings.user create mode 100644 sql/fix_concurrency_stamp_missing.sql diff --git a/.gitignore b/.gitignore index 02f9e1c..e72706e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,8 +9,13 @@ riderModule.iml .idea/ /.vs /Fake.sln.DotSettings.user +*.DotSettings.user +SimpleAdmin.sln.DotSettings.user Logs/ +*.log +api-*.log +build-warnings.txt node_modules/ diff --git a/SimpleAdmin.sln.DotSettings.user b/SimpleAdmin.sln.DotSettings.user deleted file mode 100644 index 39396e3..0000000 --- a/SimpleAdmin.sln.DotSettings.user +++ /dev/null @@ -1,25 +0,0 @@ - - ForceIncluded - ForceIncluded - ForceIncluded - ForceIncluded - ForceIncluded - ForceIncluded - ForceIncluded - ForceIncluded - ForceIncluded - ForceIncluded - ForceIncluded - ForceIncluded - ForceIncluded - ForceIncluded - ForceIncluded - ForceIncluded - ForceIncluded - ForceIncluded - ForceIncluded - ForceIncluded - ForceIncluded - <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from &lt;src&gt;\&lt;SimpleAdmin.Api.IntegrationTests&gt;" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Project Location="D:\my\itfx\admin\src\SimpleAdmin.Api.IntegrationTests" Presentation="&lt;src&gt;\&lt;SimpleAdmin.Api.IntegrationTests&gt;" /> -</SessionState> \ No newline at end of file diff --git a/sql/fix_concurrency_stamp_missing.sql b/sql/fix_concurrency_stamp_missing.sql new file mode 100644 index 0000000..6658e5a --- /dev/null +++ b/sql/fix_concurrency_stamp_missing.sql @@ -0,0 +1,63 @@ +-- 给缺少 ConcurrencyStamp 的 OMS 表补列(可重复执行) +-- Fake AggregateRoot 自带该字段,SqlSugar 查询会带上;库表没有就会报: +-- Unknown column 'ConcurrencyStamp' in 'field list' +-- +-- 注意:不要用 DEFAULT (UUID())。在 STATEMENT/MIXED 复制下会触发: +-- 1674 Statement is unsafe because it uses a system function that may return a different value on the replica. +-- 默认用固定 GUID;插入/更新时由 ORM 写入真实 UUID。 +-- +-- 用法:在业务库执行本脚本;若仍报错,把报错 SQL 里的表名追加到下方列表。 + +SET @tables := 't_order_stock_bind,C_inventory,C_parcel,C_parcel_item,C_parcel_arrival,C_sku,C_bin_location,C_inbound_notice,C_inbound_box,C_inbound_box_line,C_sku_platform_map,t_sales_store,t_sales_store_order,t_sales_store_order_line,t_customer_sender_address'; + +DROP PROCEDURE IF EXISTS sp_add_concurrency_stamp_if_missing; +DELIMITER $$ +CREATE PROCEDURE sp_add_concurrency_stamp_if_missing() +BEGIN + DECLARE done INT DEFAULT 0; + DECLARE tname VARCHAR(128); + DECLARE cur CURSOR FOR + SELECT TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(@tables, ',', n.n), ',', -1)) AS t + FROM ( + SELECT a.N + b.N * 10 + 1 AS n + FROM + (SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) a, + (SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) b + ) n + WHERE n.n <= 1 + (LENGTH(@tables) - LENGTH(REPLACE(@tables, ',', ''))); + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; + + OPEN cur; + read_loop: LOOP + FETCH cur INTO tname; + IF done THEN + LEAVE read_loop; + END IF; + IF tname IS NULL OR tname = '' THEN + ITERATE read_loop; + END IF; + IF NOT EXISTS ( + SELECT 1 FROM information_schema.TABLES + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = tname + ) THEN + ITERATE read_loop; + END IF; + IF NOT EXISTS ( + SELECT 1 FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = tname AND COLUMN_NAME = 'ConcurrencyStamp' + ) THEN + SET @ddl := CONCAT( + 'ALTER TABLE `', tname, + '` ADD COLUMN `ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT ''00000000-0000-0000-0000-000000000000'' COMMENT ''并发戳''' + ); + PREPARE stmt FROM @ddl; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; + END LOOP; + CLOSE cur; +END$$ +DELIMITER ; + +CALL sp_add_concurrency_stamp_if_missing(); +DROP PROCEDURE IF EXISTS sp_add_concurrency_stamp_if_missing; diff --git a/sql/migrate_t_order_stock_bind.sql b/sql/migrate_t_order_stock_bind.sql index 4b5afd8..3df34ca 100644 --- a/sql/migrate_t_order_stock_bind.sql +++ b/sql/migrate_t_order_stock_bind.sql @@ -1,5 +1,6 @@ -- 物流单绑定包裹/库存:t_order_stock_bind + C_inventory/C_parcel_item 冻结与可用字段 --- 见 oms系统设计-050801.md §4.2.11 / §4.4.6 +-- 见 oms系统设计-050801.md §4.2.11 / §4.4.6 / §3.1.2 +-- ConcurrencyStamp 默认用固定 GUID,避免 DEFAULT (UUID()) 触发复制 1674 -- 可重复执行 CREATE TABLE IF NOT EXISTS `t_order_stock_bind` ( @@ -23,6 +24,7 @@ CREATE TABLE IF NOT EXISTS `t_order_stock_bind` ( `AttributeText` VARCHAR(256) NULL, `Remark` VARCHAR(512) NULL, `IsDeleted` TINYINT NOT NULL DEFAULT 0, + `ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000' COMMENT '并发戳', `CreateUserId` INT NOT NULL, `CreateTime` DATETIME NOT NULL, `UpdateUserId` INT NULL, @@ -33,6 +35,18 @@ CREATE TABLE IF NOT EXISTS `t_order_stock_bind` ( KEY `idx_osb_sku` (`CustomerId`, `WarehouseId`, `SkuId`, `Status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='物流单仓储绑定明细'; +-- 已建表但缺 ConcurrencyStamp 时补列(可重复执行) +SET @col_exists := ( + SELECT COUNT(1) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 't_order_stock_bind' AND COLUMN_NAME = 'ConcurrencyStamp' +); +SET @sql := IF( + @col_exists = 0, + 'ALTER TABLE `t_order_stock_bind` ADD COLUMN `ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT ''00000000-0000-0000-0000-000000000000'' COMMENT ''并发戳'' AFTER `IsDeleted`', + 'SELECT ''t_order_stock_bind.ConcurrencyStamp already exists'' AS info' +); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + -- C_inventory.FrozenQty SET @col_exists := ( SELECT COUNT(1) FROM information_schema.COLUMNS diff --git a/src/Oms.Application/Dtos/Orders/OrderOutput.cs b/src/Oms.Application/Dtos/Orders/OrderOutput.cs index 6e375d6..2ac4836 100644 --- a/src/Oms.Application/Dtos/Orders/OrderOutput.cs +++ b/src/Oms.Application/Dtos/Orders/OrderOutput.cs @@ -95,6 +95,16 @@ public class OrderOutput /// public int? CustomerId { get; set; } + /// + /// 客户名称。 + /// + public string? CustomerName { get; set; } + + /// + /// 客户编码。 + /// + public string? CustomerCode { get; set; } + /// /// 订单来源。 /// diff --git a/src/Oms.Application/Dtos/PlatformStore/PlatformStoreDtos.cs b/src/Oms.Application/Dtos/PlatformStore/PlatformStoreDtos.cs index 16a0f34..2cc2f38 100644 --- a/src/Oms.Application/Dtos/PlatformStore/PlatformStoreDtos.cs +++ b/src/Oms.Application/Dtos/PlatformStore/PlatformStoreDtos.cs @@ -24,6 +24,10 @@ public class PlatformStoreOutput public int CustomerId { get; set; } + public string? CustomerName { get; set; } + + public string? CustomerCode { get; set; } + public string PlatformCode { get; set; } = string.Empty; public string PlatformName { get; set; } = string.Empty; @@ -186,6 +190,10 @@ public class PlatformStoreOrderOutput public int CustomerId { get; set; } + public string? CustomerName { get; set; } + + public string? CustomerCode { get; set; } + public int StoreId { get; set; } public string StoreNickname { get; set; } = string.Empty; diff --git a/src/Oms.Application/Dtos/Warehouse/WarehouseDtos.cs b/src/Oms.Application/Dtos/Warehouse/WarehouseDtos.cs index f858314..5aa9e78 100644 --- a/src/Oms.Application/Dtos/Warehouse/WarehouseDtos.cs +++ b/src/Oms.Application/Dtos/Warehouse/WarehouseDtos.cs @@ -373,6 +373,10 @@ public class WarehouseParcelOutput public int CustomerId { get; set; } + public string? CustomerName { get; set; } + + public string? CustomerCode { get; set; } + public int WarehouseId { get; set; } public string TrackingNo { get; set; } = string.Empty; @@ -441,6 +445,12 @@ public class WarehouseInventoryListInput : WarehousePageInput public class WarehouseInventoryOutput { + public int CustomerId { get; set; } + + public string? CustomerName { get; set; } + + public string? CustomerCode { get; set; } + public int SkuId { get; set; } public string SkuCode { get; set; } = string.Empty; diff --git a/src/Oms.Application/Services/Orders/OrderService.Helpers.cs b/src/Oms.Application/Services/Orders/OrderService.Helpers.cs index bf023c5..cf1e869 100644 --- a/src/Oms.Application/Services/Orders/OrderService.Helpers.cs +++ b/src/Oms.Application/Services/Orders/OrderService.Helpers.cs @@ -79,6 +79,16 @@ public partial class OrderService var auditUserNameMap = await UserDisplayNameResolver.ResolveManyAsync( orders.SelectMany(x => new[] { x.CreateUserId, x.UpdateUserId })); + var customerIds = orders + .Where(x => x.CustomerId is > 0) + .Select(x => x.CustomerId!.Value) + .Distinct() + .ToArray(); + var customers = customerIds.Length == 0 + ? [] + : await CustomerRepository.GetListAsync(x => customerIds.Contains(x.Id)); + var customerMap = customers.ToDictionary(x => x.Id); + return orders.Select(order => { senderMap.TryGetValue(order.Id, out var sender); @@ -91,6 +101,14 @@ public partial class OrderService var receivableAmount = feeOutputs .Where(x => x.Type == OrderFeeTypeCodes.Receivable) .Sum(x => x.Amount); + string? customerName = null; + string? customerCode = null; + if (order.CustomerId is > 0 && customerMap.TryGetValue(order.CustomerId.Value, out var customer)) + { + customerName = customer.CustomerName; + customerCode = customer.CustomerCode; + } + return new OrderOutput { Id = order.Id, @@ -119,6 +137,8 @@ public partial class OrderService ? warehouse.WarehouseName : null, CustomerId = order.CustomerId, + CustomerName = customerName, + CustomerCode = customerCode, OrderSource = string.IsNullOrWhiteSpace(order.OrderSource) ? OrderSourceCodes.Manual : order.OrderSource, diff --git a/src/Oms.Application/Services/Orders/OrderService.StockBind.cs b/src/Oms.Application/Services/Orders/OrderService.StockBind.cs index d4bbf01..f67978f 100644 --- a/src/Oms.Application/Services/Orders/OrderService.StockBind.cs +++ b/src/Oms.Application/Services/Orders/OrderService.StockBind.cs @@ -52,12 +52,12 @@ public partial class OrderService var order = await GetOrderOrThrowAsync(input.OrderId); EnsureOrderCanBindStock(order); var warehouseId = ResolveBindWarehouseId(order, input.WarehouseId); - var customerId = RequireOrderCustomerId(order); + var customerId = await ResolveStockBindCustomerIdAsync(order); var inventories = await StockBindInventoryRepository.GetListAsync(x => x.CustomerId == customerId && x.WarehouseId == warehouseId - && x.AvailableQty > 0); + && (x.AvailableQty > 0 || x.Qty > x.FrozenQty)); var skuIds = inventories.Select(x => x.SkuId).Distinct().ToArray(); var skus = skuIds.Length == 0 @@ -111,7 +111,7 @@ public partial class OrderService var order = await GetOrderOrThrowAsync(input.OrderId); EnsureOrderCanBindStock(order); var warehouseId = ResolveBindWarehouseId(order, input.WarehouseId); - var customerId = RequireOrderCustomerId(order); + var customerId = await ResolveStockBindCustomerIdAsync(order); var lifecycle = input.LifecycleStatus?.Trim(); var filterLifecycle = !string.IsNullOrWhiteSpace(lifecycle) @@ -194,7 +194,7 @@ public partial class OrderService var order = await GetOrderOrThrowAsync(input.OrderId); EnsureOrderCanBindStock(order); await EnsureNoDeductedBindsAsync(order.Id); - var customerId = RequireOrderCustomerId(order); + var customerId = await ResolveStockBindCustomerIdAsync(order); var warehouseId = ResolveBindWarehouseId(order, null); var items = input.Items ?? []; @@ -500,7 +500,7 @@ public partial class OrderService x.CustomerId == customerId && x.WarehouseId == warehouseId && x.SkuId == skuId - && x.AvailableQty > 0); + && (x.AvailableQty > 0 || x.Qty > x.FrozenQty)); rows = rows.OrderBy(x => x.BinLocationId).ToList(); } @@ -641,6 +641,23 @@ public partial class OrderService return order.CustomerId.Value; } + /// + /// 绑定客户上下文:客户登录与仓储模块一致(会话客户);管理端按订单客户。 + /// + private async Task ResolveStockBindCustomerIdAsync(Order order) + { + var sessionCustomerId = await OmsSessionHelper.ResolveListCustomerIdAsync( + CustomerRepository, + CurrentUser, + null); + if (sessionCustomerId is > 0) + { + return sessionCustomerId.Value; + } + + return RequireOrderCustomerId(order); + } + private static int ResolveBindWarehouseId(Order order, int? requestedWarehouseId) { if (requestedWarehouseId is > 0) diff --git a/src/Oms.Application/Services/Orders/OrderService.cs b/src/Oms.Application/Services/Orders/OrderService.cs index 9b98ca5..f13ac6a 100644 --- a/src/Oms.Application/Services/Orders/OrderService.cs +++ b/src/Oms.Application/Services/Orders/OrderService.cs @@ -483,10 +483,19 @@ public partial class OrderService : ApplicationService return; } - if (order.CustomerId != customerId.Value) + if (order.CustomerId == customerId.Value) { - throw new BusinessException("订单不存在"); + return; } + + // 兼容历史脏数据:下单人是当前客户账号,但订单 CustomerId 未写对 + var mapId = OmsSessionHelper.TryGetCustomerMapId(CurrentUser); + if (mapId is > 0 && order.CreateUserId == mapId.Value) + { + return; + } + + throw new BusinessException("订单不存在"); } private async Task GetOrderOrThrowAsync(int id) diff --git a/src/Oms.Application/Services/Orders/PlatformStoreOrderService.cs b/src/Oms.Application/Services/Orders/PlatformStoreOrderService.cs index ae2df79..45f11ee 100644 --- a/src/Oms.Application/Services/Orders/PlatformStoreOrderService.cs +++ b/src/Oms.Application/Services/Orders/PlatformStoreOrderService.cs @@ -71,6 +71,12 @@ public class PlatformStoreOrderService : ApplicationService : await OrderRepository.GetListAsync(x => linkedOrderIds.Contains(x.Id)); var linkedOrderMap = linkedOrders.ToDictionary(x => x.Id); + var customerIds = entities.Select(x => x.CustomerId).Distinct().ToArray(); + var customers = customerIds.Length == 0 + ? [] + : await CustomerRepository.GetListAsync(x => customerIds.Contains(x.Id)); + var customerMap = customers.ToDictionary(x => x.Id); + return new OmsPageResult { Items = entities.Select(x => @@ -82,7 +88,14 @@ public class PlatformStoreOrderService : ApplicationService linkedServiceNo = linkedOrder.ServiceNo; } - return MapOrderOutput(x, store?.StoreNickname ?? string.Empty, [], linkedServiceNo: linkedServiceNo); + customerMap.TryGetValue(x.CustomerId, out var customer); + return MapOrderOutput( + x, + store?.StoreNickname ?? string.Empty, + [], + linkedServiceNo: linkedServiceNo, + customerName: customer?.CustomerName, + customerCode: customer?.CustomerCode); }).ToList(), Total = total }; @@ -110,8 +123,16 @@ public class PlatformStoreOrderService : ApplicationService : await SkuRepository.GetListAsync(x => skuIds.Contains(x.Id)); var skuMap = skus.ToDictionary(x => x.Id); var linkedServiceNo = await ResolveLinkedServiceNoAsync(order.LinkedOrderId); + var customer = await CustomerRepository.FirstOrDefaultAsync(x => x.Id == order.CustomerId); - return MapOrderOutput(order, store?.StoreNickname ?? string.Empty, lines, skuMap, linkedServiceNo); + return MapOrderOutput( + order, + store?.StoreNickname ?? string.Empty, + lines, + skuMap, + linkedServiceNo, + customer?.CustomerName, + customer?.CustomerCode); } /// @@ -692,11 +713,15 @@ public class PlatformStoreOrderService : ApplicationService string storeNickname, IReadOnlyCollection lines, IReadOnlyDictionary? skuMap = null, - string? linkedServiceNo = null) => + string? linkedServiceNo = null, + string? customerName = null, + string? customerCode = null) => new() { Id = order.Id, CustomerId = order.CustomerId, + CustomerName = customerName, + CustomerCode = customerCode, StoreId = order.StoreId, StoreNickname = storeNickname, PlatformCode = order.PlatformCode, diff --git a/src/Oms.Application/Services/Orders/PlatformStoreService.cs b/src/Oms.Application/Services/Orders/PlatformStoreService.cs index 3244f7e..c7ce7c4 100644 --- a/src/Oms.Application/Services/Orders/PlatformStoreService.cs +++ b/src/Oms.Application/Services/Orders/PlatformStoreService.cs @@ -43,9 +43,15 @@ public class PlatformStoreService : ApplicationService new Dictionary { [nameof(SalesStore.UpdateTime)] = false, [nameof(SalesStore.Id)] = false }); var total = await StoreRepository.CountAsync(predicate); + var customerIds = entities.Select(x => x.CustomerId).Distinct().ToArray(); + var customers = customerIds.Length == 0 + ? [] + : await CustomerRepository.GetListAsync(x => customerIds.Contains(x.Id)); + var customerMap = customers.ToDictionary(x => x.Id); + return new OmsPageResult { - Items = entities.Select(MapOutput).ToList(), + Items = entities.Select(x => MapOutput(x, customerMap)).ToList(), Total = total }; } @@ -445,11 +451,24 @@ public class PlatformStoreService : ApplicationService }; } - private static PlatformStoreOutput MapOutput(SalesStore entity) => - new() + private static PlatformStoreOutput MapOutput( + SalesStore entity, + IReadOnlyDictionary? customerMap = null) + { + string? customerName = null; + string? customerCode = null; + if (customerMap != null && customerMap.TryGetValue(entity.CustomerId, out var customer)) + { + customerName = customer.CustomerName; + customerCode = customer.CustomerCode; + } + + return new PlatformStoreOutput { Id = entity.Id, CustomerId = entity.CustomerId, + CustomerName = customerName, + CustomerCode = customerCode, PlatformCode = entity.PlatformCode, PlatformName = ResolvePlatformName(entity.PlatformCode), StoreNickname = entity.StoreNickname, @@ -462,6 +481,7 @@ public class PlatformStoreService : ApplicationService Remark = entity.Remark, CreateTime = entity.CreateTime }; + } private static string ResolvePlatformName(string platformCode) => platformCode.ToLowerInvariant() switch { diff --git a/src/Oms.Application/Services/Warehouse/WarehouseService.Inventory.cs b/src/Oms.Application/Services/Warehouse/WarehouseService.Inventory.cs index 152bd54..e40e552 100644 --- a/src/Oms.Application/Services/Warehouse/WarehouseService.Inventory.cs +++ b/src/Oms.Application/Services/Warehouse/WarehouseService.Inventory.cs @@ -32,14 +32,24 @@ public partial class WarehouseService (!hasCustomerFilter || x.CustomerId == customerIdValue) && skuIds.Contains(x.Id)); var skuMap = skus.ToDictionary(x => x.Id); + var customerIds = inventories.Select(x => x.CustomerId).Distinct().ToArray(); + var customers = customerIds.Length == 0 + ? [] + : await CustomerRepository.GetListAsync(x => customerIds.Contains(x.Id)); + var customerMap = customers.ToDictionary(x => x.Id); + var keyword = input.Keyword?.Trim(); var grouped = inventories - .GroupBy(x => new { x.SkuId, x.WarehouseId }) + .GroupBy(x => new { x.CustomerId, x.SkuId, x.WarehouseId }) .Select(g => { skuMap.TryGetValue(g.Key.SkuId, out var sku); + customerMap.TryGetValue(g.Key.CustomerId, out var customer); return new WarehouseInventoryOutput { + CustomerId = g.Key.CustomerId, + CustomerName = customer?.CustomerName, + CustomerCode = customer?.CustomerCode, SkuId = g.Key.SkuId, SkuCode = sku?.SkuCode ?? string.Empty, SkuName = sku?.SkuName ?? string.Empty, @@ -53,8 +63,11 @@ public partial class WarehouseService .Where(x => string.IsNullOrWhiteSpace(keyword) || x.SkuCode.Contains(keyword!, StringComparison.OrdinalIgnoreCase) - || x.SkuName.Contains(keyword!, StringComparison.OrdinalIgnoreCase)) - .OrderBy(x => x.SkuCode) + || x.SkuName.Contains(keyword!, StringComparison.OrdinalIgnoreCase) + || (x.CustomerName?.Contains(keyword!, StringComparison.OrdinalIgnoreCase) ?? false) + || (x.CustomerCode?.Contains(keyword!, StringComparison.OrdinalIgnoreCase) ?? false)) + .OrderBy(x => x.CustomerCode) + .ThenBy(x => x.SkuCode) .ThenBy(x => x.WarehouseId) .ToList(); diff --git a/src/Oms.Application/Services/Warehouse/WarehouseService.Parcel.cs b/src/Oms.Application/Services/Warehouse/WarehouseService.Parcel.cs index fc1a776..53b1641 100644 --- a/src/Oms.Application/Services/Warehouse/WarehouseService.Parcel.cs +++ b/src/Oms.Application/Services/Warehouse/WarehouseService.Parcel.cs @@ -55,9 +55,25 @@ public partial class WarehouseService ? [] : await ParcelItemRepository.GetListAsync(x => parcelIds.Contains(x.ParcelId)); + var customerIds = entities.Select(x => x.CustomerId).Distinct().ToArray(); + var customers = customerIds.Length == 0 + ? [] + : await CustomerRepository.GetListAsync(x => customerIds.Contains(x.Id)); + var customerMap = customers.ToDictionary(x => x.Id); + return new OmsPageResult { - Items = entities.Select(x => MapParcelOutput(x, items.Where(i => i.ParcelId == x.Id))).ToList(), + Items = entities.Select(x => + { + var output = MapParcelOutput(x, items.Where(i => i.ParcelId == x.Id)); + if (customerMap.TryGetValue(x.CustomerId, out var customer)) + { + output.CustomerName = customer.CustomerName; + output.CustomerCode = customer.CustomerCode; + } + + return output; + }).ToList(), Total = total }; } diff --git a/src/Oms.Application/Shared/OmsSessionHelper.cs b/src/Oms.Application/Shared/OmsSessionHelper.cs index aaf8ba8..3b603fd 100644 --- a/src/Oms.Application/Shared/OmsSessionHelper.cs +++ b/src/Oms.Application/Shared/OmsSessionHelper.cs @@ -1,3 +1,4 @@ +using System.Security.Claims; using Fake.Application; using Fake.Users; using Oms.Domain.CustomersAggregate.Entities; @@ -15,16 +16,32 @@ public static class OmsSessionHelper /// public const string LoginTypeClaim = "loginType"; + /// + /// JWT 中 RBAC mapId Claim 名称(与 FakeClaimTypes.UserId 对齐)。 + /// + public const string MapIdClaim = "mapId"; + private const string CustomerLoginType = "1"; + private const string CustomerRoleName = "Customer"; /// /// 当前会话是否为客户门户登录。 /// - public static bool IsCustomerLogin(ICurrentUser currentUser) => - string.Equals( - currentUser.FindClaimOrNull(LoginTypeClaim)?.Value, - CustomerLoginType, - StringComparison.Ordinal); + public static bool IsCustomerLogin(ICurrentUser currentUser) + { + if (string.Equals( + currentUser.FindClaimOrNull(LoginTypeClaim)?.Value, + CustomerLoginType, + StringComparison.Ordinal)) + { + return true; + } + + // 兼容 loginType 丢失但 role=Customer 仍在的令牌 + var role = currentUser.FindClaimOrNull(ClaimTypes.Role)?.Value + ?? currentUser.FindClaimOrNull("role")?.Value; + return string.Equals(role, CustomerRoleName, StringComparison.OrdinalIgnoreCase); + } /// /// 客户登录时返回 mapId(对应 t_customer.UserId / 订单 CreateUserId);管理员返回 null。 @@ -37,7 +54,19 @@ public static class OmsSessionHelper } var userId = currentUser.GetId(); - return userId > 0 ? userId : null; + if (userId > 0) + { + return userId; + } + + // GetId 未绑到 mapId 时,直接读 claim,避免客户会话被当成管理员(不过滤) + var mapIdRaw = currentUser.FindClaimOrNull(MapIdClaim)?.Value; + if (int.TryParse(mapIdRaw, out var mapId) && mapId > 0) + { + return mapId; + } + + return null; } ///