feat: 管理端列表返回客户名称/编码,并加固绑定与客户会话
订单/包裹/库存/店铺授权/店铺订单列表补充 customerName、customerCode;库存绑定按会话客户解析并兼容可用量;修复客户 mapId 识别与 ConcurrencyStamp 迁移。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
dc3071c90d
commit
f4ddd993b9
5
.gitignore
vendored
5
.gitignore
vendored
@ -9,8 +9,13 @@ riderModule.iml
|
|||||||
.idea/
|
.idea/
|
||||||
/.vs
|
/.vs
|
||||||
/Fake.sln.DotSettings.user
|
/Fake.sln.DotSettings.user
|
||||||
|
*.DotSettings.user
|
||||||
|
SimpleAdmin.sln.DotSettings.user
|
||||||
|
|
||||||
Logs/
|
Logs/
|
||||||
|
*.log
|
||||||
|
api-*.log
|
||||||
|
build-warnings.txt
|
||||||
|
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
|
|||||||
@ -1,25 +0,0 @@
|
|||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AClient_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fc7d66b24252bb52da52d1262d99fd654ffc18aedde818f147c3daf6aee3e63_003FClient_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityMaintenance_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F38bf349295324397b2c739e7e632b4bd2d5000_003F49_003Fee705f67_003FEntityMaintenance_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIAggregateRoot_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6015bad29db142679ead208ae4ecd55c7800_003F0e_003F66feb813_003FIAggregateRoot_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIAggregateRoot_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6015bad29db142679ead208ae4ecd55c7800_003F0e_003F66feb813_003FIAggregateRoot_002Ecs_002Fz_003A2_002D0/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIAggregateRoot_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa1494e50f25d476db4fff59a546c2b857800_003F4e_003Fb27c424d_003FIAggregateRoot_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIAggregateRoot_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa1494e50f25d476db4fff59a546c2b857800_003F4e_003Fb27c424d_003FIAggregateRoot_002Ecs_002Fz_003A2_002D1/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIAggregateRoot_00601_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6015bad29db142679ead208ae4ecd55c7800_003F9a_003Fea4337c7_003FIAggregateRoot_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIAggregateRoot_00601_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa1494e50f25d476db4fff59a546c2b857800_003F7b_003Fe45647d2_003FIAggregateRoot_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIAggregateRoot_00601_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa1494e50f25d476db4fff59a546c2b857800_003F7b_003Fe45647d2_003FIAggregateRoot_00601_002Ecs_002Fz_003A2_002D1/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIEntity_00601_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa1494e50f25d476db4fff59a546c2b857800_003F59_003F8ca0c929_003FIEntity_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIRepository_00601_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6015bad29db142679ead208ae4ecd55c7800_003F85_003Fa6db724e_003FIRepository_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIRepository_00601_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F98ce6bfbfce34b1eabb7b03e820f0eea7800_003F66_003F56b27e93_003FIRepository_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIRepository_00601_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa1494e50f25d476db4fff59a546c2b857800_003Fcc_003F73f30804_003FIRepository_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AObjectMethodExecutorAwaitable_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F61426bb2f5bd47ebb048d0e7051942f91de910_003Fe1_003F409ba3a2_003FObjectMethodExecutorAwaitable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AQueryBuilder_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F38bf349295324397b2c739e7e632b4bd2d5000_003Fc7_003F431a0b15_003FQueryBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AReflectionInoCacheService_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F38bf349295324397b2c739e7e632b4bd2d5000_003Fbb_003F446201ec_003FReflectionInoCacheService_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AReflectionInoCore_00601_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F38bf349295324397b2c739e7e632b4bd2d5000_003Fc1_003F8f35c8ed_003FReflectionInoCore_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASqlBuilderProvider_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F38bf349295324397b2c739e7e632b4bd2d5000_003F71_003F77c8fbe4_003FSqlBuilderProvider_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASqlSugarClient_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F38bf349295324397b2c739e7e632b4bd2d5000_003Fe8_003Ffcf6435d_003FSqlSugarClient_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASqlSugarProvider_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F38bf349295324397b2c739e7e632b4bd2d5000_003F26_003Fce9a7a77_003FSqlSugarProvider_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AUnauthorizedAccessException_002Ecs_002Fl_003AC_0021_003FUsers_003FBSI_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F3e10648195244ebfa276c71cb5b3f877c90750_003F74_003F8418b441_003FUnauthorizedAccessException_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
|
||||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=9c7e285e_002D68dc_002D4e8e_002D88cb_002De8df37de5da5/@EntryIndexedValue"><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></s:String></wpf:ResourceDictionary>
|
|
||||||
63
sql/fix_concurrency_stamp_missing.sql
Normal file
63
sql/fix_concurrency_stamp_missing.sql
Normal file
@ -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;
|
||||||
@ -1,5 +1,6 @@
|
|||||||
-- 物流单绑定包裹/库存:t_order_stock_bind + C_inventory/C_parcel_item 冻结与可用字段
|
-- 物流单绑定包裹/库存: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` (
|
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,
|
`AttributeText` VARCHAR(256) NULL,
|
||||||
`Remark` VARCHAR(512) NULL,
|
`Remark` VARCHAR(512) NULL,
|
||||||
`IsDeleted` TINYINT NOT NULL DEFAULT 0,
|
`IsDeleted` TINYINT NOT NULL DEFAULT 0,
|
||||||
|
`ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000' COMMENT '并发戳',
|
||||||
`CreateUserId` INT NOT NULL,
|
`CreateUserId` INT NOT NULL,
|
||||||
`CreateTime` DATETIME NOT NULL,
|
`CreateTime` DATETIME NOT NULL,
|
||||||
`UpdateUserId` INT 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`)
|
KEY `idx_osb_sku` (`CustomerId`, `WarehouseId`, `SkuId`, `Status`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='物流单仓储绑定明细';
|
) 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
|
-- C_inventory.FrozenQty
|
||||||
SET @col_exists := (
|
SET @col_exists := (
|
||||||
SELECT COUNT(1) FROM information_schema.COLUMNS
|
SELECT COUNT(1) FROM information_schema.COLUMNS
|
||||||
|
|||||||
@ -95,6 +95,16 @@ public class OrderOutput
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int? CustomerId { get; set; }
|
public int? CustomerId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 客户名称。
|
||||||
|
/// </summary>
|
||||||
|
public string? CustomerName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 客户编码。
|
||||||
|
/// </summary>
|
||||||
|
public string? CustomerCode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 订单来源。
|
/// 订单来源。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -24,6 +24,10 @@ public class PlatformStoreOutput
|
|||||||
|
|
||||||
public int CustomerId { get; set; }
|
public int CustomerId { get; set; }
|
||||||
|
|
||||||
|
public string? CustomerName { get; set; }
|
||||||
|
|
||||||
|
public string? CustomerCode { get; set; }
|
||||||
|
|
||||||
public string PlatformCode { get; set; } = string.Empty;
|
public string PlatformCode { get; set; } = string.Empty;
|
||||||
|
|
||||||
public string PlatformName { get; set; } = string.Empty;
|
public string PlatformName { get; set; } = string.Empty;
|
||||||
@ -186,6 +190,10 @@ public class PlatformStoreOrderOutput
|
|||||||
|
|
||||||
public int CustomerId { get; set; }
|
public int CustomerId { get; set; }
|
||||||
|
|
||||||
|
public string? CustomerName { get; set; }
|
||||||
|
|
||||||
|
public string? CustomerCode { get; set; }
|
||||||
|
|
||||||
public int StoreId { get; set; }
|
public int StoreId { get; set; }
|
||||||
|
|
||||||
public string StoreNickname { get; set; } = string.Empty;
|
public string StoreNickname { get; set; } = string.Empty;
|
||||||
|
|||||||
@ -373,6 +373,10 @@ public class WarehouseParcelOutput
|
|||||||
|
|
||||||
public int CustomerId { get; set; }
|
public int CustomerId { get; set; }
|
||||||
|
|
||||||
|
public string? CustomerName { get; set; }
|
||||||
|
|
||||||
|
public string? CustomerCode { get; set; }
|
||||||
|
|
||||||
public int WarehouseId { get; set; }
|
public int WarehouseId { get; set; }
|
||||||
|
|
||||||
public string TrackingNo { get; set; } = string.Empty;
|
public string TrackingNo { get; set; } = string.Empty;
|
||||||
@ -441,6 +445,12 @@ public class WarehouseInventoryListInput : WarehousePageInput
|
|||||||
|
|
||||||
public class WarehouseInventoryOutput
|
public class WarehouseInventoryOutput
|
||||||
{
|
{
|
||||||
|
public int CustomerId { get; set; }
|
||||||
|
|
||||||
|
public string? CustomerName { get; set; }
|
||||||
|
|
||||||
|
public string? CustomerCode { get; set; }
|
||||||
|
|
||||||
public int SkuId { get; set; }
|
public int SkuId { get; set; }
|
||||||
|
|
||||||
public string SkuCode { get; set; } = string.Empty;
|
public string SkuCode { get; set; } = string.Empty;
|
||||||
|
|||||||
@ -79,6 +79,16 @@ public partial class OrderService
|
|||||||
var auditUserNameMap = await UserDisplayNameResolver.ResolveManyAsync(
|
var auditUserNameMap = await UserDisplayNameResolver.ResolveManyAsync(
|
||||||
orders.SelectMany(x => new[] { x.CreateUserId, x.UpdateUserId }));
|
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 =>
|
return orders.Select(order =>
|
||||||
{
|
{
|
||||||
senderMap.TryGetValue(order.Id, out var sender);
|
senderMap.TryGetValue(order.Id, out var sender);
|
||||||
@ -91,6 +101,14 @@ public partial class OrderService
|
|||||||
var receivableAmount = feeOutputs
|
var receivableAmount = feeOutputs
|
||||||
.Where(x => x.Type == OrderFeeTypeCodes.Receivable)
|
.Where(x => x.Type == OrderFeeTypeCodes.Receivable)
|
||||||
.Sum(x => x.Amount);
|
.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
|
return new OrderOutput
|
||||||
{
|
{
|
||||||
Id = order.Id,
|
Id = order.Id,
|
||||||
@ -119,6 +137,8 @@ public partial class OrderService
|
|||||||
? warehouse.WarehouseName
|
? warehouse.WarehouseName
|
||||||
: null,
|
: null,
|
||||||
CustomerId = order.CustomerId,
|
CustomerId = order.CustomerId,
|
||||||
|
CustomerName = customerName,
|
||||||
|
CustomerCode = customerCode,
|
||||||
OrderSource = string.IsNullOrWhiteSpace(order.OrderSource)
|
OrderSource = string.IsNullOrWhiteSpace(order.OrderSource)
|
||||||
? OrderSourceCodes.Manual
|
? OrderSourceCodes.Manual
|
||||||
: order.OrderSource,
|
: order.OrderSource,
|
||||||
|
|||||||
@ -52,12 +52,12 @@ public partial class OrderService
|
|||||||
var order = await GetOrderOrThrowAsync(input.OrderId);
|
var order = await GetOrderOrThrowAsync(input.OrderId);
|
||||||
EnsureOrderCanBindStock(order);
|
EnsureOrderCanBindStock(order);
|
||||||
var warehouseId = ResolveBindWarehouseId(order, input.WarehouseId);
|
var warehouseId = ResolveBindWarehouseId(order, input.WarehouseId);
|
||||||
var customerId = RequireOrderCustomerId(order);
|
var customerId = await ResolveStockBindCustomerIdAsync(order);
|
||||||
|
|
||||||
var inventories = await StockBindInventoryRepository.GetListAsync(x =>
|
var inventories = await StockBindInventoryRepository.GetListAsync(x =>
|
||||||
x.CustomerId == customerId
|
x.CustomerId == customerId
|
||||||
&& x.WarehouseId == warehouseId
|
&& x.WarehouseId == warehouseId
|
||||||
&& x.AvailableQty > 0);
|
&& (x.AvailableQty > 0 || x.Qty > x.FrozenQty));
|
||||||
|
|
||||||
var skuIds = inventories.Select(x => x.SkuId).Distinct().ToArray();
|
var skuIds = inventories.Select(x => x.SkuId).Distinct().ToArray();
|
||||||
var skus = skuIds.Length == 0
|
var skus = skuIds.Length == 0
|
||||||
@ -111,7 +111,7 @@ public partial class OrderService
|
|||||||
var order = await GetOrderOrThrowAsync(input.OrderId);
|
var order = await GetOrderOrThrowAsync(input.OrderId);
|
||||||
EnsureOrderCanBindStock(order);
|
EnsureOrderCanBindStock(order);
|
||||||
var warehouseId = ResolveBindWarehouseId(order, input.WarehouseId);
|
var warehouseId = ResolveBindWarehouseId(order, input.WarehouseId);
|
||||||
var customerId = RequireOrderCustomerId(order);
|
var customerId = await ResolveStockBindCustomerIdAsync(order);
|
||||||
|
|
||||||
var lifecycle = input.LifecycleStatus?.Trim();
|
var lifecycle = input.LifecycleStatus?.Trim();
|
||||||
var filterLifecycle = !string.IsNullOrWhiteSpace(lifecycle)
|
var filterLifecycle = !string.IsNullOrWhiteSpace(lifecycle)
|
||||||
@ -194,7 +194,7 @@ public partial class OrderService
|
|||||||
var order = await GetOrderOrThrowAsync(input.OrderId);
|
var order = await GetOrderOrThrowAsync(input.OrderId);
|
||||||
EnsureOrderCanBindStock(order);
|
EnsureOrderCanBindStock(order);
|
||||||
await EnsureNoDeductedBindsAsync(order.Id);
|
await EnsureNoDeductedBindsAsync(order.Id);
|
||||||
var customerId = RequireOrderCustomerId(order);
|
var customerId = await ResolveStockBindCustomerIdAsync(order);
|
||||||
var warehouseId = ResolveBindWarehouseId(order, null);
|
var warehouseId = ResolveBindWarehouseId(order, null);
|
||||||
var items = input.Items ?? [];
|
var items = input.Items ?? [];
|
||||||
|
|
||||||
@ -500,7 +500,7 @@ public partial class OrderService
|
|||||||
x.CustomerId == customerId
|
x.CustomerId == customerId
|
||||||
&& x.WarehouseId == warehouseId
|
&& x.WarehouseId == warehouseId
|
||||||
&& x.SkuId == skuId
|
&& x.SkuId == skuId
|
||||||
&& x.AvailableQty > 0);
|
&& (x.AvailableQty > 0 || x.Qty > x.FrozenQty));
|
||||||
rows = rows.OrderBy(x => x.BinLocationId).ToList();
|
rows = rows.OrderBy(x => x.BinLocationId).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -641,6 +641,23 @@ public partial class OrderService
|
|||||||
return order.CustomerId.Value;
|
return order.CustomerId.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 绑定客户上下文:客户登录与仓储模块一致(会话客户);管理端按订单客户。
|
||||||
|
/// </summary>
|
||||||
|
private async Task<int> 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)
|
private static int ResolveBindWarehouseId(Order order, int? requestedWarehouseId)
|
||||||
{
|
{
|
||||||
if (requestedWarehouseId is > 0)
|
if (requestedWarehouseId is > 0)
|
||||||
|
|||||||
@ -483,10 +483,19 @@ public partial class OrderService : ApplicationService
|
|||||||
return;
|
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<Order> GetOrderOrThrowAsync(int id)
|
private async Task<Order> GetOrderOrThrowAsync(int id)
|
||||||
|
|||||||
@ -71,6 +71,12 @@ public class PlatformStoreOrderService : ApplicationService
|
|||||||
: await OrderRepository.GetListAsync(x => linkedOrderIds.Contains(x.Id));
|
: await OrderRepository.GetListAsync(x => linkedOrderIds.Contains(x.Id));
|
||||||
var linkedOrderMap = linkedOrders.ToDictionary(x => 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<PlatformStoreOrderOutput>
|
return new OmsPageResult<PlatformStoreOrderOutput>
|
||||||
{
|
{
|
||||||
Items = entities.Select(x =>
|
Items = entities.Select(x =>
|
||||||
@ -82,7 +88,14 @@ public class PlatformStoreOrderService : ApplicationService
|
|||||||
linkedServiceNo = linkedOrder.ServiceNo;
|
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(),
|
}).ToList(),
|
||||||
Total = total
|
Total = total
|
||||||
};
|
};
|
||||||
@ -110,8 +123,16 @@ public class PlatformStoreOrderService : ApplicationService
|
|||||||
: await SkuRepository.GetListAsync(x => skuIds.Contains(x.Id));
|
: await SkuRepository.GetListAsync(x => skuIds.Contains(x.Id));
|
||||||
var skuMap = skus.ToDictionary(x => x.Id);
|
var skuMap = skus.ToDictionary(x => x.Id);
|
||||||
var linkedServiceNo = await ResolveLinkedServiceNoAsync(order.LinkedOrderId);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -692,11 +713,15 @@ public class PlatformStoreOrderService : ApplicationService
|
|||||||
string storeNickname,
|
string storeNickname,
|
||||||
IReadOnlyCollection<SalesStoreOrderLine> lines,
|
IReadOnlyCollection<SalesStoreOrderLine> lines,
|
||||||
IReadOnlyDictionary<int, Domain.WarehouseAggregate.Entities.CSku>? skuMap = null,
|
IReadOnlyDictionary<int, Domain.WarehouseAggregate.Entities.CSku>? skuMap = null,
|
||||||
string? linkedServiceNo = null) =>
|
string? linkedServiceNo = null,
|
||||||
|
string? customerName = null,
|
||||||
|
string? customerCode = null) =>
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
Id = order.Id,
|
Id = order.Id,
|
||||||
CustomerId = order.CustomerId,
|
CustomerId = order.CustomerId,
|
||||||
|
CustomerName = customerName,
|
||||||
|
CustomerCode = customerCode,
|
||||||
StoreId = order.StoreId,
|
StoreId = order.StoreId,
|
||||||
StoreNickname = storeNickname,
|
StoreNickname = storeNickname,
|
||||||
PlatformCode = order.PlatformCode,
|
PlatformCode = order.PlatformCode,
|
||||||
|
|||||||
@ -43,9 +43,15 @@ public class PlatformStoreService : ApplicationService
|
|||||||
new Dictionary<string, bool> { [nameof(SalesStore.UpdateTime)] = false, [nameof(SalesStore.Id)] = false });
|
new Dictionary<string, bool> { [nameof(SalesStore.UpdateTime)] = false, [nameof(SalesStore.Id)] = false });
|
||||||
var total = await StoreRepository.CountAsync(predicate);
|
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<PlatformStoreOutput>
|
return new OmsPageResult<PlatformStoreOutput>
|
||||||
{
|
{
|
||||||
Items = entities.Select(MapOutput).ToList(),
|
Items = entities.Select(x => MapOutput(x, customerMap)).ToList(),
|
||||||
Total = total
|
Total = total
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -445,11 +451,24 @@ public class PlatformStoreService : ApplicationService
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PlatformStoreOutput MapOutput(SalesStore entity) =>
|
private static PlatformStoreOutput MapOutput(
|
||||||
new()
|
SalesStore entity,
|
||||||
|
IReadOnlyDictionary<int, Domain.CustomersAggregate.Entities.Customer>? 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,
|
Id = entity.Id,
|
||||||
CustomerId = entity.CustomerId,
|
CustomerId = entity.CustomerId,
|
||||||
|
CustomerName = customerName,
|
||||||
|
CustomerCode = customerCode,
|
||||||
PlatformCode = entity.PlatformCode,
|
PlatformCode = entity.PlatformCode,
|
||||||
PlatformName = ResolvePlatformName(entity.PlatformCode),
|
PlatformName = ResolvePlatformName(entity.PlatformCode),
|
||||||
StoreNickname = entity.StoreNickname,
|
StoreNickname = entity.StoreNickname,
|
||||||
@ -462,6 +481,7 @@ public class PlatformStoreService : ApplicationService
|
|||||||
Remark = entity.Remark,
|
Remark = entity.Remark,
|
||||||
CreateTime = entity.CreateTime
|
CreateTime = entity.CreateTime
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static string ResolvePlatformName(string platformCode) => platformCode.ToLowerInvariant() switch
|
private static string ResolvePlatformName(string platformCode) => platformCode.ToLowerInvariant() switch
|
||||||
{
|
{
|
||||||
|
|||||||
@ -32,14 +32,24 @@ public partial class WarehouseService
|
|||||||
(!hasCustomerFilter || x.CustomerId == customerIdValue) && skuIds.Contains(x.Id));
|
(!hasCustomerFilter || x.CustomerId == customerIdValue) && skuIds.Contains(x.Id));
|
||||||
var skuMap = skus.ToDictionary(x => 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 keyword = input.Keyword?.Trim();
|
||||||
var grouped = inventories
|
var grouped = inventories
|
||||||
.GroupBy(x => new { x.SkuId, x.WarehouseId })
|
.GroupBy(x => new { x.CustomerId, x.SkuId, x.WarehouseId })
|
||||||
.Select(g =>
|
.Select(g =>
|
||||||
{
|
{
|
||||||
skuMap.TryGetValue(g.Key.SkuId, out var sku);
|
skuMap.TryGetValue(g.Key.SkuId, out var sku);
|
||||||
|
customerMap.TryGetValue(g.Key.CustomerId, out var customer);
|
||||||
return new WarehouseInventoryOutput
|
return new WarehouseInventoryOutput
|
||||||
{
|
{
|
||||||
|
CustomerId = g.Key.CustomerId,
|
||||||
|
CustomerName = customer?.CustomerName,
|
||||||
|
CustomerCode = customer?.CustomerCode,
|
||||||
SkuId = g.Key.SkuId,
|
SkuId = g.Key.SkuId,
|
||||||
SkuCode = sku?.SkuCode ?? string.Empty,
|
SkuCode = sku?.SkuCode ?? string.Empty,
|
||||||
SkuName = sku?.SkuName ?? string.Empty,
|
SkuName = sku?.SkuName ?? string.Empty,
|
||||||
@ -53,8 +63,11 @@ public partial class WarehouseService
|
|||||||
.Where(x =>
|
.Where(x =>
|
||||||
string.IsNullOrWhiteSpace(keyword)
|
string.IsNullOrWhiteSpace(keyword)
|
||||||
|| x.SkuCode.Contains(keyword!, StringComparison.OrdinalIgnoreCase)
|
|| x.SkuCode.Contains(keyword!, StringComparison.OrdinalIgnoreCase)
|
||||||
|| x.SkuName.Contains(keyword!, StringComparison.OrdinalIgnoreCase))
|
|| x.SkuName.Contains(keyword!, StringComparison.OrdinalIgnoreCase)
|
||||||
.OrderBy(x => x.SkuCode)
|
|| (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)
|
.ThenBy(x => x.WarehouseId)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
|||||||
@ -55,9 +55,25 @@ public partial class WarehouseService
|
|||||||
? []
|
? []
|
||||||
: await ParcelItemRepository.GetListAsync(x => parcelIds.Contains(x.ParcelId));
|
: 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<WarehouseParcelOutput>
|
return new OmsPageResult<WarehouseParcelOutput>
|
||||||
{
|
{
|
||||||
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
|
Total = total
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
using System.Security.Claims;
|
||||||
using Fake.Application;
|
using Fake.Application;
|
||||||
using Fake.Users;
|
using Fake.Users;
|
||||||
using Oms.Domain.CustomersAggregate.Entities;
|
using Oms.Domain.CustomersAggregate.Entities;
|
||||||
@ -15,16 +16,32 @@ public static class OmsSessionHelper
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const string LoginTypeClaim = "loginType";
|
public const string LoginTypeClaim = "loginType";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// JWT 中 RBAC mapId Claim 名称(与 FakeClaimTypes.UserId 对齐)。
|
||||||
|
/// </summary>
|
||||||
|
public const string MapIdClaim = "mapId";
|
||||||
|
|
||||||
private const string CustomerLoginType = "1";
|
private const string CustomerLoginType = "1";
|
||||||
|
private const string CustomerRoleName = "Customer";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当前会话是否为客户门户登录。
|
/// 当前会话是否为客户门户登录。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool IsCustomerLogin(ICurrentUser currentUser) =>
|
public static bool IsCustomerLogin(ICurrentUser currentUser)
|
||||||
string.Equals(
|
{
|
||||||
currentUser.FindClaimOrNull(LoginTypeClaim)?.Value,
|
if (string.Equals(
|
||||||
CustomerLoginType,
|
currentUser.FindClaimOrNull(LoginTypeClaim)?.Value,
|
||||||
StringComparison.Ordinal);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 客户登录时返回 mapId(对应 t_customer.UserId / 订单 CreateUserId);管理员返回 null。
|
/// 客户登录时返回 mapId(对应 t_customer.UserId / 订单 CreateUserId);管理员返回 null。
|
||||||
@ -37,7 +54,19 @@ public static class OmsSessionHelper
|
|||||||
}
|
}
|
||||||
|
|
||||||
var userId = currentUser.GetId<int>();
|
var userId = currentUser.GetId<int>();
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user