diff --git a/sql/migrate_rename_warehouse_C_to_c.sql b/sql/migrate_rename_warehouse_C_to_c.sql index c41ef77..d3a4ad7 100644 --- a/sql/migrate_rename_warehouse_C_to_c.sql +++ b/sql/migrate_rename_warehouse_C_to_c.sql @@ -1,34 +1,123 @@ --- 仓储表 C_* → c_*(小写前缀,对齐 t_* 命名风格) --- 可重复执行:源表不存在或目标表已存在则跳过 +-- 仓储表 C_* → c_*(小写 c 前缀) +-- 不依赖 DELIMITER / 存储过程,Navicat、DMS、mysql 命令行均可整段执行 +-- 可重复执行 +-- +-- 说明: +-- 1) 若库已开启 lower_case_table_names(Windows/部分云库),建表时 C_sku 磁盘上已是 c_sku, +-- 无需 RENAME,部署新代码即可;下方「诊断」应只看到 c_*。 +-- 2) Linux 且区分大小写时,须经临时表名两步改名,否则可能报 Table 'c_sku' already exists。 +-- 3) 执行前请确认已 USE 到业务库(DATABASE() 不能为 NULL)。 -DROP PROCEDURE IF EXISTS sp_rename_warehouse_table_if_needed; -DELIMITER $$ -CREATE PROCEDURE sp_rename_warehouse_table_if_needed(IN old_name VARCHAR(128), IN new_name VARCHAR(128)) -BEGIN - IF EXISTS ( - SELECT 1 FROM information_schema.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = old_name - ) AND NOT EXISTS ( - SELECT 1 FROM information_schema.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = new_name - ) THEN - SET @ddl := CONCAT('RENAME TABLE `', old_name, '` TO `', new_name, '`'); - PREPARE stmt FROM @ddl; - EXECUTE stmt; - DEALLOCATE PREPARE stmt; - END IF; -END$$ -DELIMITER ; +-- ========== 诊断(先看当前表名)========== +SELECT TABLE_NAME AS warehouse_table +FROM information_schema.TABLES +WHERE TABLE_SCHEMA = DATABASE() + AND ( + TABLE_NAME LIKE 'C\_%' ESCAPE '\\' + OR TABLE_NAME LIKE 'c\_%' ESCAPE '\\' + OR TABLE_NAME LIKE '\_\_oms\_ren\_%' ESCAPE '\\' + ) +ORDER BY TABLE_NAME; -CALL sp_rename_warehouse_table_if_needed('C_sku', 'c_sku'); -CALL sp_rename_warehouse_table_if_needed('C_bin_location', 'c_bin_location'); -CALL sp_rename_warehouse_table_if_needed('C_inventory', 'c_inventory'); -CALL sp_rename_warehouse_table_if_needed('C_inbound_notice', 'c_inbound_notice'); -CALL sp_rename_warehouse_table_if_needed('C_inbound_box', 'c_inbound_box'); -CALL sp_rename_warehouse_table_if_needed('C_inbound_box_line', 'c_inbound_box_line'); -CALL sp_rename_warehouse_table_if_needed('C_parcel', 'c_parcel'); -CALL sp_rename_warehouse_table_if_needed('C_parcel_arrival', 'c_parcel_arrival'); -CALL sp_rename_warehouse_table_if_needed('C_parcel_item', 'c_parcel_item'); -CALL sp_rename_warehouse_table_if_needed('C_sku_platform_map', 'c_sku_platform_map'); +-- ========== 单表改名:大写 C_ 存在 → 临时名 → c_* ========== +-- c_sku +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND BINARY TABLE_NAME = 'C_sku'); +SET @sql := IF(@need > 0, 'RENAME TABLE `C_sku` TO `__oms_ren_sku`', 'SELECT ''skip: C_sku'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '__oms_ren_sku'); +SET @sql := IF(@need > 0, 'RENAME TABLE `__oms_ren_sku` TO `c_sku`', 'SELECT ''skip: __oms_ren_sku'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; -DROP PROCEDURE IF EXISTS sp_rename_warehouse_table_if_needed; +-- c_bin_location +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND BINARY TABLE_NAME = 'C_bin_location'); +SET @sql := IF(@need > 0, 'RENAME TABLE `C_bin_location` TO `__oms_ren_bin_location`', 'SELECT ''skip: C_bin_location'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '__oms_ren_bin_location'); +SET @sql := IF(@need > 0, 'RENAME TABLE `__oms_ren_bin_location` TO `c_bin_location`', 'SELECT ''skip: __oms_ren_bin_location'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; + +-- c_inventory +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND BINARY TABLE_NAME = 'C_inventory'); +SET @sql := IF(@need > 0, 'RENAME TABLE `C_inventory` TO `__oms_ren_inventory`', 'SELECT ''skip: C_inventory'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '__oms_ren_inventory'); +SET @sql := IF(@need > 0, 'RENAME TABLE `__oms_ren_inventory` TO `c_inventory`', 'SELECT ''skip: __oms_ren_inventory'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; + +-- c_inbound_notice +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND BINARY TABLE_NAME = 'C_inbound_notice'); +SET @sql := IF(@need > 0, 'RENAME TABLE `C_inbound_notice` TO `__oms_ren_inbound_notice`', 'SELECT ''skip: C_inbound_notice'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '__oms_ren_inbound_notice'); +SET @sql := IF(@need > 0, 'RENAME TABLE `__oms_ren_inbound_notice` TO `c_inbound_notice`', 'SELECT ''skip: __oms_ren_inbound_notice'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; + +-- c_inbound_box +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND BINARY TABLE_NAME = 'C_inbound_box'); +SET @sql := IF(@need > 0, 'RENAME TABLE `C_inbound_box` TO `__oms_ren_inbound_box`', 'SELECT ''skip: C_inbound_box'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '__oms_ren_inbound_box'); +SET @sql := IF(@need > 0, 'RENAME TABLE `__oms_ren_inbound_box` TO `c_inbound_box`', 'SELECT ''skip: __oms_ren_inbound_box'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; + +-- c_inbound_box_line +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND BINARY TABLE_NAME = 'C_inbound_box_line'); +SET @sql := IF(@need > 0, 'RENAME TABLE `C_inbound_box_line` TO `__oms_ren_inbound_box_line`', 'SELECT ''skip: C_inbound_box_line'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '__oms_ren_inbound_box_line'); +SET @sql := IF(@need > 0, 'RENAME TABLE `__oms_ren_inbound_box_line` TO `c_inbound_box_line`', 'SELECT ''skip: __oms_ren_inbound_box_line'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; + +-- c_parcel +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND BINARY TABLE_NAME = 'C_parcel'); +SET @sql := IF(@need > 0, 'RENAME TABLE `C_parcel` TO `__oms_ren_parcel`', 'SELECT ''skip: C_parcel'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '__oms_ren_parcel'); +SET @sql := IF(@need > 0, 'RENAME TABLE `__oms_ren_parcel` TO `c_parcel`', 'SELECT ''skip: __oms_ren_parcel'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; + +-- c_parcel_arrival +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND BINARY TABLE_NAME = 'C_parcel_arrival'); +SET @sql := IF(@need > 0, 'RENAME TABLE `C_parcel_arrival` TO `__oms_ren_parcel_arrival`', 'SELECT ''skip: C_parcel_arrival'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '__oms_ren_parcel_arrival'); +SET @sql := IF(@need > 0, 'RENAME TABLE `__oms_ren_parcel_arrival` TO `c_parcel_arrival`', 'SELECT ''skip: __oms_ren_parcel_arrival'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; + +-- c_parcel_item +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND BINARY TABLE_NAME = 'C_parcel_item'); +SET @sql := IF(@need > 0, 'RENAME TABLE `C_parcel_item` TO `__oms_ren_parcel_item`', 'SELECT ''skip: C_parcel_item'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '__oms_ren_parcel_item'); +SET @sql := IF(@need > 0, 'RENAME TABLE `__oms_ren_parcel_item` TO `c_parcel_item`', 'SELECT ''skip: __oms_ren_parcel_item'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; + +-- c_sku_platform_map +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND BINARY TABLE_NAME = 'C_sku_platform_map'); +SET @sql := IF(@need > 0, 'RENAME TABLE `C_sku_platform_map` TO `__oms_ren_sku_platform_map`', 'SELECT ''skip: C_sku_platform_map'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; +SET @need := (SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '__oms_ren_sku_platform_map'); +SET @sql := IF(@need > 0, 'RENAME TABLE `__oms_ren_sku_platform_map` TO `c_sku_platform_map`', 'SELECT ''skip: __oms_ren_sku_platform_map'' AS info'); +PREPARE _s FROM @sql; EXECUTE _s; DEALLOCATE PREPARE _s; + +-- ========== 执行后复查 ========== +SELECT TABLE_NAME AS warehouse_table_after +FROM information_schema.TABLES +WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME LIKE 'c\_%' ESCAPE '\\' +ORDER BY TABLE_NAME; + +-- ========== 若上面全是 skip 且已是 c_*:无需再改,直接部署后端即可 ========== +-- 若仍残留 C_*(区分大小写 Linux),可手工一次性执行(会失败则勿重复跑): +-- RENAME TABLE +-- `C_sku` TO `__oms_ren_sku`, `C_bin_location` TO `__oms_ren_bin_location`, +-- `C_inventory` TO `__oms_ren_inventory`, `C_inbound_notice` TO `__oms_ren_inbound_notice`, +-- `C_inbound_box` TO `__oms_ren_inbound_box`, `C_inbound_box_line` TO `__oms_ren_inbound_box_line`, +-- `C_parcel` TO `__oms_ren_parcel`, `C_parcel_arrival` TO `__oms_ren_parcel_arrival`, +-- `C_parcel_item` TO `__oms_ren_parcel_item`, `C_sku_platform_map` TO `__oms_ren_sku_platform_map`; +-- RENAME TABLE +-- `__oms_ren_sku` TO `c_sku`, `__oms_ren_bin_location` TO `c_bin_location`, +-- `__oms_ren_inventory` TO `c_inventory`, `__oms_ren_inbound_notice` TO `c_inbound_notice`, +-- `__oms_ren_inbound_box` TO `c_inbound_box`, `__oms_ren_inbound_box_line` TO `c_inbound_box_line`, +-- `__oms_ren_parcel` TO `c_parcel`, `__oms_ren_parcel_arrival` TO `c_parcel_arrival`, +-- `__oms_ren_parcel_item` TO `c_parcel_item`, `__oms_ren_sku_platform_map` TO `c_sku_platform_map`; diff --git a/src/Oms.Application/Services/Orders/Batch/OrderBatchJobCoordinator.cs b/src/Oms.Application/Services/Orders/Batch/OrderBatchJobCoordinator.cs index d0f0c01..4e29a8b 100644 --- a/src/Oms.Application/Services/Orders/Batch/OrderBatchJobCoordinator.cs +++ b/src/Oms.Application/Services/Orders/Batch/OrderBatchJobCoordinator.cs @@ -400,6 +400,9 @@ public sealed class OrderBatchJobCoordinator( return await MapJobOutputAsync(job, cancellationToken); } + public Task GetJobEntityAsync(int jobId, CancellationToken cancellationToken = default) => + batchJobRepository.GetByIdAsync(jobId, cancellationToken); + public async Task> GetJobItemsAsync(int jobId, CancellationToken cancellationToken = default) { var job = await batchJobRepository.GetByIdAsync(jobId, cancellationToken); @@ -541,39 +544,39 @@ public sealed class OrderBatchJobCoordinator( var runningCount = items.Count(x => x.Status == OrderBatchJobItemStatusCodes.Running); var cancelledCount = items.Count(x => x.Status == OrderBatchJobItemStatusCodes.Cancelled); - if (job.SuccessCount != successCount || job.FailCount != failCount) - { - job.SuccessCount = successCount; - job.FailCount = failCount; - } - + // 只读聚合:查询接口不得写库,避免与 MQ 消费者并发更新 ConcurrencyStamp 冲突。 + var status = job.Status; + var finishedAt = job.FinishedAt; if (items.Count > 0 && items.All(x => x.Status is OrderBatchJobItemStatusCodes.Success or OrderBatchJobItemStatusCodes.Failed or OrderBatchJobItemStatusCodes.Cancelled)) { - job.Status = cancelledCount == items.Count + status = cancelledCount == items.Count ? OrderBatchJobStatusCodes.Cancelled : failCount > 0 && successCount == 0 ? OrderBatchJobStatusCodes.Failed : OrderBatchJobStatusCodes.Completed; - job.FinishedAt ??= DateTime.Now; - await batchJobRepository.UpdateJobAsync(job, cancellationToken); + finishedAt ??= items + .Where(x => x.FinishedAt.HasValue) + .Select(x => x.FinishedAt) + .OrderByDescending(x => x) + .FirstOrDefault(); } var output = new OrderBatchJobOutput { JobId = job.Id, Action = job.Action, - Status = job.Status, + Status = status, Total = job.Total, - SuccessCount = job.SuccessCount, - FailCount = job.FailCount, + SuccessCount = successCount, + FailCount = failCount, PendingCount = pendingCount, RunningCount = runningCount, CancelledCount = cancelledCount, StartedAt = job.StartedAt, - FinishedAt = job.FinishedAt, + FinishedAt = finishedAt, CreateTime = job.CreateTime, }; ApplyImportMetadata(output, job); diff --git a/src/Oms.Application/Services/Orders/OrderService.BatchJob.cs b/src/Oms.Application/Services/Orders/OrderService.BatchJob.cs index e2124ad..992e100 100644 --- a/src/Oms.Application/Services/Orders/OrderService.BatchJob.cs +++ b/src/Oms.Application/Services/Orders/OrderService.BatchJob.cs @@ -59,7 +59,7 @@ public partial class OrderService [HttpGet("/order/batch/jobs/{id:int}/items")] public async Task> GetBatchJobItemsAsync([FromRoute] int id) { - var job = await BatchJobCoordinator.GetJobAsync(id); + var job = await BatchJobCoordinator.GetJobEntityAsync(id); if (job == null) { throw new BusinessException("批量任务不存在");