From 5bf10c1dafe485d506ec534f98e5220a3b83dd17 Mon Sep 17 00:00:00 2001
From: wanshenmean <cathay_xy@163.com>
Date: 星期四, 16 四月 2026 23:16:46 +0800
Subject: [PATCH] feat(WCS&WMS): 机械手扫码NG处理与线体条码读取与添加批量MES绑定解绑接口
---
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotPrefixCommandHandler.cs | 61 +-
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Stock/SplitPalletConfirmRequestDto.cs | 13
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Common/HttpEnum/ConfigKey.cs | 12
Code/WMS/WIDESEA_WMSServer/Database/Scripts/20260416_Dt_SplitTemp.sql | 15
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockController.cs | 22 +
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotBarcodeGenerator.cs | 37 -
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Stock/GroupPalletConfirmRequestDto.cs | 13
Code/WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockSerivce.cs | 146 ++++++
Code/WMS/WIDESEA_WMSServer/WIDESEA_IStockService/IStockService.cs | 14
Code/WMS/WIDESEA_WMSServer/WIDESEA_Model/Models/Stock/Dt_SplitTemp.cs | 36 +
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs | 64 ++-
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs | 84 ++-
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotSocketState.cs | 8
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/HostedService/ApiRouteCacheWarmupHostedService.cs | 6
Code/docs/superpowers/plans/2026-04-16-BatchMesBinding-Plan.md | 485 ++++++++++++++++++++++++
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs | 32 +
Code/docs/superpowers/specs/2026-04-16-BatchMesBinding-Design.md | 106 +++++
17 files changed, 1,048 insertions(+), 106 deletions(-)
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Common/HttpEnum/ConfigKey.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Common/HttpEnum/ConfigKey.cs
index c946d5d..f170c0a 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Common/HttpEnum/ConfigKey.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Common/HttpEnum/ConfigKey.cs
@@ -82,7 +82,17 @@
/// <summary>
/// 绌烘墭鐩樺嚭搴撳畬鎴愶紙鏍规嵁浠诲姟鍙峰拰鎵樼洏鍙烽�氱煡WMS绌烘墭鐩樺嚭搴撳畬鎴愶級
/// </summary>
- OutboundFinishTaskTray
+ OutboundFinishTaskTray,
+
+ /// <summary>
+ /// 鎵归噺鎷嗙洏纭
+ /// </summary>
+ SplitPalletConfirm,
+
+ /// <summary>
+ /// 鎵归噺缁勭洏纭
+ /// </summary>
+ GroupPalletConfirm
#endregion WMS鎺ュ彛
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/HostedService/ApiRouteCacheWarmupHostedService.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/HostedService/ApiRouteCacheWarmupHostedService.cs
index 3ef6513..046380e 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/HostedService/ApiRouteCacheWarmupHostedService.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/HostedService/ApiRouteCacheWarmupHostedService.cs
@@ -28,7 +28,9 @@
(nameof(ConfigKey.CreateRobotGroupPalletTask), "Task/CreateRobotGroupPalletTask"),
(nameof(ConfigKey.CreateRobotChangePalletTask), "Task/CreateRobotChangePalletTask"),
(nameof(ConfigKey.CreateRobotSplitPalletTask), "Task/CreateRobotSplitPalletTask"),
- (nameof(ConfigKey.OutboundFinishTaskTray), "Task/OutboundFinishTaskTray")
+ (nameof(ConfigKey.OutboundFinishTaskTray), "Task/OutboundFinishTaskTray"),
+ (nameof(ConfigKey.SplitPalletConfirm), "Stock/SplitPalletConfirm"),
+ (nameof(ConfigKey.GroupPalletConfirm), "Stock/GroupPalletConfirm")
};
private readonly ICacheService _cache;
@@ -53,7 +55,7 @@
warmedCount++;
}
- _logger.LogInformation(":API路由缓存预热完成。计数={Count}", warmedCount);
+ _logger.LogInformation("锟斤拷API路锟缴伙拷锟斤拷预锟斤拷锟斤拷伞锟斤拷锟斤拷锟�={Count}", warmedCount);
return Task.CompletedTask;
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotBarcodeGenerator.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotBarcodeGenerator.cs
index 0e5474b..f40a882 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotBarcodeGenerator.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotBarcodeGenerator.cs
@@ -1,36 +1,27 @@
+using Masuit.Tools;
+using WIDESEAWCS_QuartzJob;
+
namespace WIDESEAWCS_Tasks
{
/// <summary>
- /// 鏈烘鎵嬫潯鐮佺敓鎴愬櫒 - 璐熻矗鐢熸垚鎵樼洏鏉$爜
+ /// 鏈烘鎵嬫潯鐮佽鍙栧櫒 - 璐熻矗璇诲彇鐢佃姱鏉$爜
/// </summary>
- /// <remarks>
- /// 鏉$爜鏍煎紡锛氬墠缂�锛堝彲閫夛級+ 鏃ユ湡锛坹yyyMMdd锛�+ 鏃堕棿锛圚Hmmss锛�+ 闅忔満鏁帮紙100-999锛�
- /// 渚嬪锛歍RAY20260326093045123
- /// </remarks>
public static class RobotBarcodeGenerator
{
/// <summary>
- /// 鐢熸垚鎵樼洏鏉$爜
+ /// 璇诲彇绾夸綋鏉$爜
/// </summary>
- /// <param name="prefix">鏉$爜鍓嶇紑锛岄粯璁や负绌哄瓧绗︿覆锛屼緥濡� "TRAY"</param>
- /// <returns>鐢熸垚鐨勬潯鐮佸瓧绗︿覆锛屾牸寮忥細鍓嶇紑+鏃ユ湡+鏃堕棿+闅忔満鏁�</returns>
+ /// <param name="prefix">DB鐐逛綅锛屼緥濡� "DB40.990"</param>
+ /// <returns>璇诲彇鍒扮殑鐢佃姱鏉$爜</returns>
public static string GenerateTrayBarcode(string prefix = "")
{
- // 鑾峰彇褰撳墠鏃ユ湡锛屾牸寮忓寲涓� yyyyMMdd锛�8浣嶆暟瀛楋級
- // 渚嬪锛�20260326
- string datePart = DateTime.Now.ToString("yyyyMMdd");
-
- // 鑾峰彇褰撳墠鏃堕棿锛堟椂鍒嗙锛夛紝鏍煎紡鍖栦负 HHmmss锛�6浣嶆暟瀛楋級
- // 渚嬪锛�093045 琛ㄧず 09:30:45
- string timePart = DateTime.Now.ToString("HHmmss");
-
- // 鐢熸垚3浣嶉殢鏈烘暟锛岃寖鍥� 100-999锛岀‘淇濇潯鐮佸敮涓�鎬�
- // 浣跨敤 Random.Shared 鑾峰彇绾跨▼瀹夊叏鐨勯殢鏈烘暟鐢熸垚鍣�
- string randomPart = Random.Shared.Next(100, 1000).ToString();
-
- // 缁勫悎鎵�鏈夐儴鍒嗭細鍓嶇紑 + 鏃ユ湡 + 鏃堕棿 + 闅忔満鏁�
- // 濡傛灉鍓嶇紑涓虹┖锛屽垯鐩存帴杩斿洖鏃ユ湡+鏃堕棿+闅忔満鏁扮殑缁勫悎
- return prefix + datePart + timePart + randomPart;
+ var device = Storage.Devices.Where(d => d.DeviceName == "A鍖篲涓�娉ㄨ緭閫佺嚎").FirstOrDefault();
+ if (!device.IsNullOrEmpty() && device != null && device.Communicator.IsConnected)
+ {
+ var trayBarcode = device.Communicator.Read<string>(prefix);
+ return trayBarcode;
+ }
+ return "";
}
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotSocketState.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotSocketState.cs
index e5981d2..ac3f7ad 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotSocketState.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotSocketState.cs
@@ -203,5 +203,13 @@
/// 4: 鏀惧亣鐢佃姱鍒�5鍙蜂綅锛堟祦鍚態 Phase2锛�
/// </remarks>
public int ChangePalletPhase { get; set; }
+
+ /// <summary>
+ /// 鏄惁鎵爜NG
+ /// </summary>
+ /// <remarks>
+ /// 鎷夊甫绾夸笂鐢佃姱鎵爜鏄惁NG銆�
+ /// </remarks>
+ public bool IsScanNG { get; set; } = false;
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs
index e30d316..c50f942 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs
@@ -164,7 +164,8 @@
/// </remarks>
/// <param name="task">瑕佷笅鍙戠殑浠诲姟瀵硅薄</param>
/// <param name="state">鏈哄櫒浜哄綋鍓嶇姸鎬�</param>
- public async Task SendSocketRobotPickAsync(Dt_RobotTask task, RobotSocketState state)
+ /// <param name="isScanNG">鏄惁鎵爜NG</param>
+ public async Task SendSocketRobotPickAsync(Dt_RobotTask task, RobotSocketState state, bool isScanNG)
{
// 鏋勫缓鍙栬揣鎸囦护锛屾牸寮忥細Pickbattery,{婧愬湴鍧�}
string taskString = $"Pickbattery,{task.RobotSourceAddress}";
@@ -183,6 +184,11 @@
// 灏嗕换鍔″叧鑱斿埌鐘舵�佸璞�
state.CurrentTask = task;
+
+ if(isScanNG)
+ {
+ state.IsScanNG = true;
+ }
// 淇濇寔鍘熻涔夛細浠呭湪鐘舵�佸畨鍏ㄥ啓鍏ユ垚鍔熷悗鍐嶆洿鏂颁换鍔$姸鎬�
// 杩欐牱鍙互纭繚鐘舵�佸拰浠诲姟璁板綍鐨勪竴鑷存��
@@ -514,37 +520,37 @@
}
// 瑙f瀽杩斿洖鐨勪换鍔′俊鎭�
- var taskInfos = JsonConvert.DeserializeObject<List<Dt_Task>>(content.Data.ToJson() ?? string.Empty) ?? new List<Dt_Task>();
- var taskInfo = taskInfos.FirstOrDefault();
+ //var taskInfos = JsonConvert.DeserializeObject<List<Dt_Task>>(content.Data.ToJson() ?? string.Empty) ?? new List<Dt_Task>();
+ //var taskInfo = taskInfos.FirstOrDefault();
- // 鑾峰彇婧愬湴鍧�
- string sourceAddress = taskDTO.SourceAddress;
+ //// 鑾峰彇婧愬湴鍧�
+ //string sourceAddress = taskDTO.SourceAddress;
- // 鏌ユ壘婧愬湴鍧�瀵瑰簲鐨勮緭閫佺嚎璁惧
- IDevice? device = Storage.Devices.FirstOrDefault(x => x.DeviceProDTOs.Any(d => d.DeviceChildCode == sourceAddress));
+ //// 鏌ユ壘婧愬湴鍧�瀵瑰簲鐨勮緭閫佺嚎璁惧
+ //IDevice? device = Storage.Devices.FirstOrDefault(x => x.DeviceProDTOs.Any(d => d.DeviceChildCode == sourceAddress));
- if (device != null)
- {
- // 灏嗚澶囪浆鎹负杈撻�佺嚎绫诲瀷
- CommonConveyorLine conveyorLine = (CommonConveyorLine)device;
+ //if (device != null)
+ //{
+ // // 灏嗚澶囪浆鎹负杈撻�佺嚎绫诲瀷
+ // CommonConveyorLine conveyorLine = (CommonConveyorLine)device;
- // 璁剧疆杈撻�佺嚎鐨勭洰鏍囧湴鍧�
- conveyorLine.SetValue(ConveyorLineDBNameNew.Target, taskInfo.NextAddress, sourceAddress);
+ // // 璁剧疆杈撻�佺嚎鐨勭洰鏍囧湴鍧�
+ // conveyorLine.SetValue(ConveyorLineDBNameNew.Target, taskInfo.NextAddress, sourceAddress);
- // 璁剧疆杈撻�佺嚎鐨勪换鍔″彿
- conveyorLine.SetValue(ConveyorLineDBNameNew.TaskNo, taskInfo.TaskNum, sourceAddress);
+ // // 璁剧疆杈撻�佺嚎鐨勪换鍔″彿
+ // conveyorLine.SetValue(ConveyorLineDBNameNew.TaskNo, taskInfo.TaskNum, sourceAddress);
- // 瑙﹀彂杈撻�佺嚎寮�濮嬫墽琛岋紙鍐欏叆 WCS_ACK = 1锛�
- conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, sourceAddress);
+ // // 瑙﹀彂杈撻�佺嚎寮�濮嬫墽琛岋紙鍐欏叆 WCS_ACK = 1锛�
+ // conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, sourceAddress);
- // 鏇存柊浠诲姟鐘舵�佸埌涓嬩竴闃舵
- if (_taskService.UpdateTaskStatusToNext(taskInfo).Status)
- {
- _logger.LogInformation("HandleInboundTaskAsync锛氬叆搴撲换鍔″鐞嗘垚鍔燂紝浠诲姟鍙�: {TaskNum}", taskInfo.TaskNum);
- QuartzLogger.Info($"HandleInboundTaskAsync锛氬叆搴撲换鍔″鐞嗘垚鍔燂紝浠诲姟鍙�: {taskInfo.TaskNum}", state.RobotCrane?.DeviceName ?? "Unknown");
- return true;
- }
- }
+ // // 鏇存柊浠诲姟鐘舵�佸埌涓嬩竴闃舵
+ // if (_taskService.UpdateTaskStatusToNext(taskInfo).Status)
+ // {
+ // _logger.LogInformation("HandleInboundTaskAsync锛氬叆搴撲换鍔″鐞嗘垚鍔燂紝浠诲姟鍙�: {TaskNum}", taskInfo.TaskNum);
+ // QuartzLogger.Info($"HandleInboundTaskAsync锛氬叆搴撲换鍔″鐞嗘垚鍔燂紝浠诲姟鍙�: {taskInfo.TaskNum}", state.RobotCrane?.DeviceName ?? "Unknown");
+ // return true;
+ // }
+ //}
return false;
}
@@ -632,5 +638,33 @@
{
return _httpClientHelper.Post<WebResponseContent>(configKey, stockDTO.ToJson());
}
+
+ /// <summary>
+ /// 璋冪敤鎵归噺鎷嗙洏纭 API
+ /// </summary>
+ /// <remarks>
+ /// 褰撴媶鐩樹换鍔″叏閮ㄥ彇瀹屾椂璋冪敤锛屼竴娆℃�т笂浼犳暣涓墭鐩樼殑瑙g粦鏁版嵁鍒� MES銆�
+ /// </remarks>
+ /// <param name="palletCode">婧愭墭鐩樺彿</param>
+ /// <returns>HTTP 鍝嶅簲缁撴灉</returns>
+ public HttpResponseResult<WebResponseContent> PostSplitPalletConfirmAsync(string palletCode)
+ {
+ var request = new { PalletCode = palletCode };
+ return _httpClientHelper.Post<WebResponseContent>(nameof(ConfigKey.SplitPalletConfirm), request.ToJson());
+ }
+
+ /// <summary>
+ /// 璋冪敤鎵归噺缁勭洏纭 API
+ /// </summary>
+ /// <remarks>
+ /// 褰撶粍鐩樹换鍔″叏閮ㄦ斁瀹屾椂璋冪敤锛屼竴娆℃�т笂浼犳暣涓墭鐩樼殑缁戝畾鏁版嵁鍒� MES銆�
+ /// </remarks>
+ /// <param name="palletCode">鐩爣鎵樼洏鍙�</param>
+ /// <returns>HTTP 鍝嶅簲缁撴灉</returns>
+ public HttpResponseResult<WebResponseContent> PostGroupPalletConfirmAsync(string palletCode)
+ {
+ var request = new { PalletCode = palletCode };
+ return _httpClientHelper.Post<WebResponseContent>(nameof(ConfigKey.GroupPalletConfirm), request.ToJson());
+ }
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotPrefixCommandHandler.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotPrefixCommandHandler.cs
index 9eb7830..691bde5 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotPrefixCommandHandler.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotPrefixCommandHandler.cs
@@ -145,20 +145,27 @@
// 浠庢暟鎹簱閲嶆柊鏌ヨ褰撳墠浠诲姟锛堢‘淇濊幏鍙栨渶鏂扮姸鎬侊級
var task = await _robotTaskService.Repository.QueryFirstAsync(x => x.RobotTaskId == state.CurrentTask.RobotTaskId);
- // 鏍规嵁鍛戒护鍓嶇紑鍒嗗彂澶勭悊
- if (cmd.StartsWith("pickfinished"))
+ if (task != null)
{
- // 澶勭悊鍙栬揣瀹屾垚
- await HandlePickFinishedAsync(state, positions, task);
- }
- else if (cmd.StartsWith("putfinished"))
- {
- // 澶勭悊鏀捐揣瀹屾垚
- await HandlePutFinishedAsync(state, positions, task);
- }
+ // 鏍规嵁鍛戒护鍓嶇紑鍒嗗彂澶勭悊
+ if (cmd.StartsWith("pickfinished"))
+ {
+ // 澶勭悊鍙栬揣瀹屾垚
+ await HandlePickFinishedAsync(state, positions, task);
+ }
+ else if (cmd.StartsWith("putfinished"))
+ {
+ // 澶勭悊鏀捐揣瀹屾垚
+ await HandlePutFinishedAsync(state, positions, task);
+ }
- // 鍥炲啓鍘熸秷鎭埌瀹㈡埛绔紙淇濇寔鍘熸湁琛屼负锛�
- await _socketClientGateway.SendMessageAsync(client, message);
+ // 鍥炲啓鍘熸秷鎭埌瀹㈡埛绔紙淇濇寔鍘熸湁琛屼负锛�
+ await _socketClientGateway.SendMessageAsync(client, message);
+ }
+ else
+ {
+ Console.WriteLine($"RobotJob HandleAsync Warning: Current task not found for RobotTaskId {state.CurrentTask.RobotTaskId}");
+ }
}
catch (Exception ex)
{
@@ -306,9 +313,12 @@
putSuccess = result.Data.Status && result.IsSuccess;
// 澧炲姞浠诲姟璁℃暟
- state.RobotTaskTotalNum += positions.Length;
- if (task != null)
- task.RobotTaskTotalNum -= positions.Length;
+ if (!state.IsScanNG)
+ {
+ state.RobotTaskTotalNum += positions.Length;
+ if (task != null)
+ task.RobotTaskTotalNum -= positions.Length;
+ }
}
}
@@ -317,6 +327,7 @@
{
// 鏇存柊褰撳墠鍔ㄤ綔涓�"鏀捐揣瀹屾垚"
state.CurrentAction = "PutFinished";
+ state.IsScanNG = false;
// 闈炵粍鐩樹换鍔℃椂澧炲姞璁℃暟锛堢粍鐩樹换鍔″凡鍦ㄤ笂闈㈤�掑锛�
if (!state.IsGroupPallet)
@@ -325,18 +336,18 @@
if (task != null)
task.RobotTaskTotalNum -= positions.Length;
}
- }
- // 濡傛灉浠诲姟瀛樺湪
- if (task != null)
- {
- // 鏇存柊浠诲姟鐘舵�佷负"鏈哄櫒浜烘斁璐у畬鎴�"
- task.RobotTaskState = TaskRobotStatusEnum.RobotPutFinish.GetHashCode();
-
- // 瀹夊叏鏇存柊鐘舵�佸埌 Redis
- if (_stateManager.TryUpdateStateSafely(state.IPAddress, state))
+ // 濡傛灉浠诲姟瀛樺湪
+ if (task != null)
{
- await _robotTaskService.Repository.UpdateDataAsync(task);
+ // 鏇存柊浠诲姟鐘舵�佷负"鏈哄櫒浜烘斁璐у畬鎴�"
+ task.RobotTaskState = TaskRobotStatusEnum.RobotPutFinish.GetHashCode();
+
+ // 瀹夊叏鏇存柊鐘舵�佸埌 Redis
+ if (_stateManager.TryUpdateStateSafely(state.IPAddress, state))
+ {
+ await _robotTaskService.Repository.UpdateDataAsync(task);
+ }
}
}
}
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs
index d2b057b..e0b0a03 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs
@@ -143,6 +143,14 @@
{
if (state.ChangePalletPhase == 0)
{
+ // 璋冪敤鎵归噺鎷嗙洏纭鎺ュ彛锛堟崲鐩樺彇瀹岄樁娈碉級
+ var sourcePallet = state.CurrentTask.RobotSourceAddressPalletCode;
+ var confirmResult = _taskProcessor.PostSplitPalletConfirmAsync(sourcePallet);
+ if (!confirmResult.IsSuccess)
+ {
+ QuartzLogger.Error($"鎵归噺鎷嗙洏纭澶辫触: {confirmResult.ErrorMessage}", state.RobotCrane?.DeviceName ?? "Unknown");
+ }
+
// 鎵�鏈夐樁娈靛畬鎴愶紝澶勭悊鍏ュ簱
if (await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: true))
{
@@ -165,6 +173,14 @@
// 鎷嗙洏浠诲姟锛氱洿鎺ュ鐞嗗叆搴�
if (robotTaskType == RobotTaskTypeEnum.SplitPallet)
{
+ // 璋冪敤鎵归噺鎷嗙洏纭鎺ュ彛
+ var sourcePallet = state.CurrentTask.RobotSourceAddressPalletCode;
+ var confirmResult = _taskProcessor.PostSplitPalletConfirmAsync(sourcePallet);
+ if (!confirmResult.IsSuccess)
+ {
+ QuartzLogger.Error($"鎵归噺鎷嗙洏纭澶辫触: {confirmResult.ErrorMessage}", state.RobotCrane?.DeviceName ?? "Unknown");
+ }
+
if (await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: true))
{
// 鍏ュ簱鎴愬姛锛屽垹闄や换鍔¤褰�
@@ -198,6 +214,14 @@
{
if (state.ChangePalletPhase == 0)
{
+ // 璋冪敤鎵归噺缁勭洏纭鎺ュ彛锛堟崲鐩樻斁瀹岄樁娈碉級
+ var targetPallet = state.CurrentTask.RobotTargetAddressPalletCode;
+ var confirmResult = _taskProcessor.PostGroupPalletConfirmAsync(targetPallet);
+ if (!confirmResult.IsSuccess)
+ {
+ QuartzLogger.Error($"鎵归噺缁勭洏纭澶辫触: {confirmResult.ErrorMessage}", state.RobotCrane?.DeviceName ?? "Unknown");
+ }
+
// 鎵�鏈夐樁娈靛畬鎴愶紝澶勭悊鍏ュ簱
if (await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: false))
{
@@ -226,6 +250,14 @@
// 缁勭洏浠诲姟锛氱洿鎺ュ鐞嗗叆搴�
if (robotTaskType == RobotTaskTypeEnum.GroupPallet)
{
+ // 璋冪敤鎵归噺缁勭洏纭鎺ュ彛
+ var targetPallet = state.CurrentTask.RobotTargetAddressPalletCode;
+ var confirmResult = _taskProcessor.PostGroupPalletConfirmAsync(targetPallet);
+ if (!confirmResult.IsSuccess)
+ {
+ QuartzLogger.Error($"鎵归噺缁勭洏纭澶辫触: {confirmResult.ErrorMessage}", state.RobotCrane?.DeviceName ?? "Unknown");
+ }
+
// 澶勭悊鍏ュ簱浠诲姟鍥炰紶
// useSourceAddress: false 琛ㄧず浣跨敤鐩爣鍦板潃锛堢粍鐩樺満鏅級
if (await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: false))
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs
index 4414558..2ddad09 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs
@@ -5,6 +5,7 @@
using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
+using WIDESEAWCS_Tasks.SocketServer;
using WIDESEAWCS_Tasks.Workflow.Abstractions;
namespace WIDESEAWCS_Tasks.Workflow
@@ -163,11 +164,12 @@
{
string taskString;
+ var state = _stateManager.GetState(ipAddress);
+
// 鎹㈢洏浠诲姟浣跨敤鎵规鏍煎紡
if (task.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode())
{
int targetNormalCount = task.RobotTaskTotalNum;
- var state = _stateManager.GetState(ipAddress);
int currentCompletedCount = state?.RobotTaskTotalNum ?? 0;
bool isFlowA = task.RobotSourceAddressLineCode is "11001" or "11010";
@@ -229,7 +231,20 @@
else
{
// 闈炴崲鐩樹换鍔★細浣跨敤鍘熸湁鏍煎紡
- taskString = $"Putbattery,{task.RobotTargetAddress}";
+ if (state != null && state.IsGroupPallet && task.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode())
+ {
+ // 缁勭洏浠诲姟锛氭斁璐ч渶鍒ゆ柇鏄惁NG锛屽鏋淣G鍒欐斁鍒癗G鍙�
+ if (state.IsScanNG)
+ {
+ taskString = $"Putbattery,NG";
+ }
+ else
+ {
+ taskString = $"Putbattery,{task.RobotTargetAddress}";
+ }
+ }
+ else
+ taskString = $"Putbattery,{task.RobotTargetAddress}";
}
bool result = await _clientManager.SendToClientAsync(ipAddress, taskString);
@@ -301,46 +316,49 @@
// 濡傛灉鏄粍鐩樹换鍔�
if (task.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode())
{
- // 鐢熸垚鎵樼洏鏉$爜鍓嶇紑
- const string prefix = "TRAY";
-
- // 鐢熸垚涓や釜鎵樼洏鏉$爜锛堢敤浜庣粍鐩樻搷浣滐級锛堟祴璇曠敤锛屽悗缁鍙栫嚎浣撴潯鐮侊級
- string trayBarcode1 = RobotBarcodeGenerator.GenerateTrayBarcode(prefix);
- string trayBarcode2 = RobotBarcodeGenerator.GenerateTrayBarcode(prefix);
+ // 璇诲彇绾夸綋鐢佃姱鏉$爜
+ string trayBarcode1 = RobotBarcodeGenerator.GenerateTrayBarcode("DB40.990");
+ string trayBarcode2 = RobotBarcodeGenerator.GenerateTrayBarcode("DB40.1020");
// 濡傛灉鏉$爜鐢熸垚鎴愬姛
if (!string.IsNullOrEmpty(trayBarcode1) && !string.IsNullOrEmpty(trayBarcode2))
{
- if(stateForUpdate.CellBarcode.Contains(trayBarcode1)|| stateForUpdate.CellBarcode.Contains(trayBarcode2))
+ if (stateForUpdate.CellBarcode.Contains(trayBarcode1) || stateForUpdate.CellBarcode.Contains(trayBarcode2))
{
- _logger.LogError("HandlePutFinishedStateAsync锛氱敓鎴愮殑鎵樼洏鏉$爜宸插瓨鍦紝鍙兘瀛樺湪閲嶅锛屼换鍔″彿: {TaskNum}", task.RobotTaskNum);
- QuartzLogger.Error($"鐢熸垚鐨勬墭鐩樻潯鐮佸凡瀛樺湪锛屽彲鑳藉瓨鍦ㄩ噸澶�", stateForUpdate.RobotCrane.DeviceName);
+ _logger.LogError("HandlePutFinishedStateAsync锛氳鍙栫殑鎵樼洏鏉$爜宸插瓨鍦紝鍙兘瀛樺湪閲嶅锛屼换鍔″彿: {TaskNum}", task.RobotTaskNum);
+ QuartzLogger.Error($"璇诲彇鐨勬墭鐩樻潯鐮佸凡瀛樺湪锛屽彲鑳藉瓨鍦ㄩ噸澶�", stateForUpdate.RobotCrane.DeviceName);
// 鏉$爜閲嶅锛岃褰曢敊璇棩蹇楀苟鍋滄鍚庣画鎿嶄綔(鍚庣画鏀捐揣鏃朵細鐢ㄥ埌杩欎簺鏉$爜淇℃伅锛屼緵鍚庣画鏀捐揣鏃朵娇鐢紝璋冭瘯鍚庡彲鑳戒細鍙栨秷姝ら�昏緫)
- return;
+
+ // 鍙戦�佸彇璐ф寚浠� 鏍囪鎵爜NG锛屾斁璐ф椂涓嶄娇鐢ㄨ繖浜涙潯鐮侊紝骞舵斁鍏G鍙�
+ await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate, true);
}
else
{
- _logger.LogInformation("HandlePutFinishedStateAsync锛氱敓鎴愮殑鎵樼洏鏉$爜鍞竴锛岀户缁墽琛岋紝浠诲姟鍙�: {TaskNum}", task.RobotTaskNum);
- QuartzLogger.Info($"鐢熸垚鐨勬墭鐩樻潯鐮佸敮涓�锛岀户缁墽琛�", stateForUpdate.RobotCrane.DeviceName);
+ _logger.LogInformation("HandlePutFinishedStateAsync锛氳鍙栫殑鎵樼洏鏉$爜鍞竴锛岀户缁墽琛岋紝浠诲姟鍙�: {TaskNum}", task.RobotTaskNum);
+ QuartzLogger.Info($"璇诲彇鐨勬墭鐩樻潯鐮佸敮涓�锛岀户缁墽琛�", stateForUpdate.RobotCrane.DeviceName);
// 灏嗘潯鐮佹坊鍔犲埌鐘舵�佷腑锛屼緵鍚庣画鏀捐揣鏃朵娇鐢�
stateForUpdate.CellBarcode.Add(trayBarcode1);
stateForUpdate.CellBarcode.Add(trayBarcode2);
}
- // 璁板綍鏃ュ織锛氱敓鎴愭墭鐩樻潯鐮佹垚鍔�
- _logger.LogInformation("HandlePutFinishedStateAsync锛氱敓鎴愭墭鐩樻潯鐮佹垚鍔�: {Barcode1}+{Barcode2}锛屼换鍔″彿: {TaskNum}", trayBarcode1, trayBarcode2, task.RobotTaskNum);
- QuartzLogger.Info($"鐢熸垚鎵樼洏鏉$爜鎴愬姛: {trayBarcode1}+{trayBarcode2}", stateForUpdate.RobotCrane.DeviceName);
+ // 璁板綍鏃ュ織锛氳鍙栨墭鐩樻潯鐮佹垚鍔�
+ _logger.LogInformation("HandlePutFinishedStateAsync锛氳鍙栨墭鐩樻潯鐮佹垚鍔�: {Barcode1}+{Barcode2}锛屼换鍔″彿: {TaskNum}", trayBarcode1, trayBarcode2, task.RobotTaskNum);
+ QuartzLogger.Info($"璇诲彇鎵樼洏鏉$爜鎴愬姛: {trayBarcode1}+{trayBarcode2}", stateForUpdate.RobotCrane.DeviceName);
// 鍙戦�佸彇璐ф寚浠�
- await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate);
+ await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate, false);
}
else
{
- // 鏉$爜鐢熸垚澶辫触锛岃褰曢敊璇棩蹇�
- _logger.LogError("HandlePutFinishedStateAsync锛氱敓鎴愭墭鐩樻潯鐮佸け璐ワ紝浠诲姟鍙�: {TaskNum}", task.RobotTaskNum);
- QuartzLogger.Error($"鐢熸垚鎵樼洏鏉$爜澶辫触", stateForUpdate.RobotCrane.DeviceName);
+ // 鏉$爜璇诲彇澶辫触锛岃褰曢敊璇棩蹇�
+ _logger.LogError("HandlePutFinishedStateAsync锛氳鍙栨墭鐩樻潯鐮佸け璐ワ紝浠诲姟鍙�: {TaskNum}", task.RobotTaskNum);
+ QuartzLogger.Error($"璇诲彇鎵樼洏鏉$爜澶辫触", stateForUpdate.RobotCrane.DeviceName);
+
+
+ // 鍙戦�佸彇璐ф寚浠� 鏍囪鎵爜NG锛屾斁璐ф椂涓嶄娇鐢ㄨ繖浜涙潯鐮侊紝骞舵斁鍏G鍙�
+ await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate, true);
}
}
else if (task.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode())
@@ -355,7 +373,7 @@
// 鐩爣鏁伴噺涓�48锛氱洿鎺ヨ蛋鍘熸湁閫昏緫锛屼笉杩涘叆鎵规妯″紡
if (targetNormalCount == targetTotal)
{
- await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate);
+ await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate, false);
return;
}
@@ -524,7 +542,7 @@
else
{
// 闈炵粍鐩樹换鍔★紝鐩存帴鍙戦�佸彇璐ф寚浠�
- await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate);
+ await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate, false);
}
}
}
diff --git a/Code/WMS/WIDESEA_WMSServer/Database/Scripts/20260416_Dt_SplitTemp.sql b/Code/WMS/WIDESEA_WMSServer/Database/Scripts/20260416_Dt_SplitTemp.sql
new file mode 100644
index 0000000..3a42a53
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSServer/Database/Scripts/20260416_Dt_SplitTemp.sql
@@ -0,0 +1,15 @@
+-- 鎷嗙洏涓存椂琛細鐢ㄤ簬鏆傚瓨鎷嗙洏浠诲姟鐢佃姱鍒楄〃锛屼緵鎵归噺纭鏃朵娇鐢�
+IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Dt_SplitTemp]') AND type in (N'U'))
+BEGIN
+ CREATE TABLE [dbo].[Dt_SplitTemp](
+ [Id] [int] IDENTITY(1,1) NOT NULL,
+ [PalletCode] [nvarchar](50) NOT NULL,
+ [SfcList] [nvarchar](max) NOT NULL,
+ [CreateTime] [datetime] NOT NULL DEFAULT GETDATE(),
+ CONSTRAINT [PK_Dt_SplitTemp] PRIMARY KEY CLUSTERED ([Id] ASC)
+ );
+
+ -- 鍞竴绱㈠紩闃叉鍚屼竴鎵樼洏閲嶅鍐欏叆
+ CREATE UNIQUE NONCLUSTERED INDEX [IX_Dt_SplitTemp_PalletCode] ON [dbo].[Dt_SplitTemp]([PalletCode] ASC);
+END
+GO
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Stock/GroupPalletConfirmRequestDto.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Stock/GroupPalletConfirmRequestDto.cs
new file mode 100644
index 0000000..4afa6b1
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Stock/GroupPalletConfirmRequestDto.cs
@@ -0,0 +1,13 @@
+namespace WIDESEA_DTO.Stock
+{
+ /// <summary>
+ /// 鎵归噺缁勭洏纭璇锋眰DTO
+ /// </summary>
+ public class GroupPalletConfirmRequestDto
+ {
+ /// <summary>
+ /// 鐩爣鎵樼洏鍙�
+ /// </summary>
+ public string PalletCode { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Stock/SplitPalletConfirmRequestDto.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Stock/SplitPalletConfirmRequestDto.cs
new file mode 100644
index 0000000..6f5ef2c
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Stock/SplitPalletConfirmRequestDto.cs
@@ -0,0 +1,13 @@
+namespace WIDESEA_DTO.Stock
+{
+ /// <summary>
+ /// 鎵归噺鎷嗙洏纭璇锋眰DTO
+ /// </summary>
+ public class SplitPalletConfirmRequestDto
+ {
+ /// <summary>
+ /// 婧愭墭鐩樺彿
+ /// </summary>
+ public string PalletCode { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_IStockService/IStockService.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_IStockService/IStockService.cs
index 6d835c6..82306b3 100644
--- a/Code/WMS/WIDESEA_WMSServer/WIDESEA_IStockService/IStockService.cs
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_IStockService/IStockService.cs
@@ -55,5 +55,19 @@
/// <param name="stock">搴撳瓨淇℃伅鏁版嵁浼犺緭瀵硅薄</param>
/// <returns>鎿嶄綔缁撴灉</returns>
Task<WebResponseContent> UpdateStockInfoAsync(StockInfoDTO stock);
+
+ /// <summary>
+ /// 鎵归噺鎷嗙洏纭 - 涓�娆℃�ц皟鐢∕ES瑙g粦鏁翠釜鎵樼洏
+ /// </summary>
+ /// <param name="palletCode">婧愭墭鐩樺彿</param>
+ /// <returns>鎿嶄綔缁撴灉</returns>
+ Task<WebResponseContent> SplitPalletConfirmAsync(string palletCode);
+
+ /// <summary>
+ /// 鎵归噺缁勭洏纭 - 涓�娆℃�ц皟鐢∕ES缁戝畾鏁翠釜鎵樼洏
+ /// </summary>
+ /// <param name="palletCode">鐩爣鎵樼洏鍙�</param>
+ /// <returns>鎿嶄綔缁撴灉</returns>
+ Task<WebResponseContent> GroupPalletConfirmAsync(string palletCode);
}
}
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_Model/Models/Stock/Dt_SplitTemp.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_Model/Models/Stock/Dt_SplitTemp.cs
new file mode 100644
index 0000000..26eab06
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_Model/Models/Stock/Dt_SplitTemp.cs
@@ -0,0 +1,36 @@
+using SqlSugar;
+using WIDESEA_Core.DB.Models;
+
+namespace WIDESEA_Model.Models
+{
+ /// <summary>
+ /// 鎷嗙洏涓存椂琛� - 鐢ㄤ簬鏆傚瓨鎷嗙洏浠诲姟鐢佃姱鍒楄〃锛屼緵鎵归噺纭鏃朵娇鐢�
+ /// </summary>
+ [SugarTable(nameof(Dt_SplitTemp), "鎷嗙洏涓存椂琛�")]
+ public class Dt_SplitTemp
+ {
+ /// <summary>
+ /// 涓婚敭
+ /// </summary>
+ [SugarColumn(IsPrimaryKey = true, IsIdentity = true, ColumnDescription = "涓婚敭")]
+ public int Id { get; set; }
+
+ /// <summary>
+ /// 鎵樼洏鍙�
+ /// </summary>
+ [SugarColumn(IsNullable = false, Length = 50, ColumnDescription = "鎵樼洏鍙�")]
+ public string PalletCode { get; set; }
+
+ /// <summary>
+ /// 鐢佃姱鏉$爜鍒楄〃锛圝SON鏍煎紡锛�
+ /// </summary>
+ [SugarColumn(IsNullable = false, Length = -1, ColumnDescription = "鐢佃姱鏉$爜鍒楄〃JSON")]
+ public string SfcList { get; set; }
+
+ /// <summary>
+ /// 鍒涘缓鏃堕棿
+ /// </summary>
+ [SugarColumn(IsNullable = false, ColumnDescription = "鍒涘缓鏃堕棿")]
+ public DateTime CreateTime { get; set; } = DateTime.Now;
+ }
+}
\ No newline at end of file
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockSerivce.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockSerivce.cs
index 6b13364..8fde2b8 100644
--- a/Code/WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockSerivce.cs
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockSerivce.cs
@@ -1,4 +1,5 @@
-锘縰sing SqlSugar;
+锘縰sing Newtonsoft.Json;
+using SqlSugar;
using WIDESEA_Common.Constants;
using WIDESEA_Common.StockEnum;
using WIDESEA_Core;
@@ -41,6 +42,11 @@
public IWarehouseService _warehouseService { get; }
/// <summary>
+ /// SqlSugar瀹㈡埛绔紙鐢ㄤ簬涓存椂琛ㄦ搷浣滐級
+ /// </summary>
+ public ISqlSugarClient SqlSugarClient { get; }
+
+ /// <summary>
/// Mes鎺ュ彛鏈嶅姟
/// </summary>
public IMesService _mesService { get; }
@@ -58,7 +64,8 @@
IStockInfoDetail_HtyService stockInfoDetail_HtyService,
IStockInfo_HtyService stockInfo_HtyService,
IMesService mesService,
- IWarehouseService warehouseService)
+ IWarehouseService warehouseService,
+ ISqlSugarClient sqlSugarClient)
{
StockInfoDetailService = stockInfoDetailService;
StockInfoService = stockInfoService;
@@ -66,6 +73,7 @@
StockInfo_HtyService = stockInfo_HtyService;
_mesService = mesService;
_warehouseService = warehouseService;
+ SqlSugarClient = sqlSugarClient;
}
/// <summary>
@@ -179,11 +187,11 @@
result = StockInfoService.Repository.AddData(entity, x => x.Details);
if (!result) return content.Error("缁勭洏澶辫触");
- //var mesResult = _mesService.BindContainer(bindRequest);
- //if (mesResult == null || mesResult.Data == null || !mesResult.Data.IsSuccess)
- //{
- // return content.Error($"缁勭洏鎴愬姛锛屼絾MES缁戝畾澶辫触: {mesResult?.Data?.Msg ?? mesResult?.ErrorMessage ?? "鏈煡閿欒"}");
- //}
+ var mesResult = _mesService.BindContainer(bindRequest);
+ if (mesResult == null || mesResult.Data == null || !mesResult.Data.IsSuccess)
+ {
+ return content.Error($"缁勭洏鎴愬姛锛屼絾MES缁戝畾澶辫触: {mesResult?.Data?.Msg ?? mesResult?.ErrorMessage ?? "鏈煡閿欒"}");
+ }
return content.OK("缁勭洏鎴愬姛");
});
}
@@ -303,6 +311,30 @@
{
if (stock == null || string.IsNullOrWhiteSpace(stock.SourcePalletNo))
return content.Error("婧愭墭鐩樺彿涓嶈兘涓虹┖");
+
+ // 骞傜瓑鍐欏叆锛氭鏌ヤ复鏃惰〃鏄惁宸叉湁璇ユ墭鐩樿褰曪紝鏃犲垯鍐欏叆
+ var existingTemp = SqlSugarClient.Queryable<Dt_SplitTemp>()
+ .Where(t => t.PalletCode == stock.SourcePalletNo)
+ .First();
+ if (existingTemp == null)
+ {
+ // 鏌ヨ璇ユ墭鐩樺綋鍓嶆墍鏈夌數鑺紝瀛樺叆涓存椂琛�
+ var sourceStockForTemp = StockInfoService.Repository.QueryFirst(s => s.PalletCode == stock.SourcePalletNo);
+ if (sourceStockForTemp != null)
+ {
+ var allDetails = StockInfoDetailService.Repository.QueryData(d => d.StockId == sourceStockForTemp.Id);
+ if (allDetails != null && allDetails.Any())
+ {
+ var sfcListJson = JsonConvert.SerializeObject(allDetails.Select(d => d.SerialNumber).ToList());
+ await SqlSugarClient.Insertable(new Dt_SplitTemp
+ {
+ PalletCode = stock.SourcePalletNo,
+ SfcList = sfcListJson,
+ CreateTime = DateTime.Now
+ }).ExecuteCommandAsync();
+ }
+ }
+ }
return await ExecuteWithinTransactionAsync(async () =>
{
@@ -426,5 +458,105 @@
OutboundDate = s.OutboundDate
}).ToList();
}
+
+ /// <summary>
+ /// 鎵归噺鎷嗙洏纭 - 涓�娆℃�ц皟鐢∕ES瑙g粦鏁翠釜鎵樼洏
+ /// </summary>
+ /// <param name="palletCode">婧愭墭鐩樺彿</param>
+ /// <returns>鎿嶄綔缁撴灉</returns>
+ public async Task<WebResponseContent> SplitPalletConfirmAsync(string palletCode)
+ {
+ WebResponseContent content = new WebResponseContent();
+ try
+ {
+ if (string.IsNullOrWhiteSpace(palletCode))
+ return content.Error("鎵樼洏鍙蜂笉鑳戒负绌�");
+
+ // 1. 浠庝复鏃惰〃璇诲彇鐢佃姱鍒楄〃
+ var tempRecord = SqlSugarClient.Queryable<Dt_SplitTemp>()
+ .Where(t => t.PalletCode == palletCode)
+ .First();
+ if (tempRecord == null)
+ return content.Error("鏈壘鍒版媶鐩樹复鏃惰褰曪紝璇峰厛鎵ц鎷嗙洏鎿嶄綔");
+
+ var sfcList = JsonConvert.DeserializeObject<List<string>>(tempRecord.SfcList);
+ if (sfcList == null || !sfcList.Any())
+ return content.Error("涓存椂琛ㄤ腑鐢佃姱鍒楄〃涓虹┖");
+
+ // 2. 璋冪敤MES瑙g粦
+ var unbindRequest = new UnBindContainerRequest
+ {
+ EquipmentCode = StockConstants.MES_EQUIPMENT_CODE,
+ ResourceCode = StockConstants.MES_RESOURCE_CODE,
+ LocalTime = DateTime.Now,
+ ContainCode = palletCode,
+ SfcList = sfcList
+ };
+ var unbindResult = _mesService.UnBindContainer(unbindRequest);
+ if (unbindResult == null || unbindResult.Data == null || !unbindResult.Data.IsSuccess)
+ {
+ return content.Error($"MES瑙g粦澶辫触: {unbindResult?.Data?.Msg ?? unbindResult?.ErrorMessage ?? "鏈煡閿欒"}");
+ }
+
+ // 3. 鍒犻櫎涓存椂琛ㄨ褰�
+ await SqlSugarClient.Deleteable<Dt_SplitTemp>().Where(t => t.PalletCode == palletCode).ExecuteCommandAsync();
+
+ return content.OK("鎵归噺鎷嗙洏纭鎴愬姛");
+ }
+ catch (Exception ex)
+ {
+ return content.Error($"鎵归噺鎷嗙洏纭澶辫触: {ex.Message}");
+ }
+ }
+
+ /// <summary>
+ /// 鎵归噺缁勭洏纭 - 涓�娆℃�ц皟鐢∕ES缁戝畾鏁翠釜鎵樼洏
+ /// </summary>
+ /// <param name="palletCode">鐩爣鎵樼洏鍙�</param>
+ /// <returns>鎿嶄綔缁撴灉</returns>
+ public async Task<WebResponseContent> GroupPalletConfirmAsync(string palletCode)
+ {
+ WebResponseContent content = new WebResponseContent();
+ try
+ {
+ if (string.IsNullOrWhiteSpace(palletCode))
+ return content.Error("鎵樼洏鍙蜂笉鑳戒负绌�");
+
+ // 1. 鏌ヨ璇ユ墭鐩樹笅鐨勬墍鏈夌數鑺槑缁�
+ var stockInfo = StockInfoService.Repository.QueryFirst(s => s.PalletCode == palletCode);
+ if (stockInfo == null)
+ return content.Error("鎵樼洏涓嶅瓨鍦�");
+
+ var details = StockInfoDetailService.Repository.QueryData(d => d.StockId == stockInfo.Id);
+ if (details == null || !details.Any())
+ return content.Error("鎵樼洏涓嬫棤鐢佃姱鏁版嵁");
+
+ // 2. 璋冪敤MES缁戝畾
+ var bindRequest = new BindContainerRequest
+ {
+ ContainerCode = palletCode,
+ EquipmentCode = StockConstants.MES_EQUIPMENT_CODE,
+ ResourceCode = StockConstants.MES_RESOURCE_CODE,
+ LocalTime = DateTime.Now,
+ OperationType = StockConstants.MES_BIND_OPERATION_TYPE,
+ ContainerSfcList = details.Select(d => new ContainerSfcItem
+ {
+ Sfc = d.SerialNumber,
+ Location = d.InboundOrderRowNo.ToString()
+ }).ToList()
+ };
+ var bindResult = _mesService.BindContainer(bindRequest);
+ if (bindResult == null || bindResult.Data == null || !bindResult.Data.IsSuccess)
+ {
+ return content.Error($"MES缁戝畾澶辫触: {bindResult?.Data?.Msg ?? bindResult?.ErrorMessage ?? "鏈煡閿欒"}");
+ }
+
+ return content.OK("鎵归噺缁勭洏纭鎴愬姛");
+ }
+ catch (Exception ex)
+ {
+ return content.Error($"鎵归噺缁勭洏纭澶辫触: {ex.Message}");
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockController.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockController.cs
index 30c6dd7..2076b82 100644
--- a/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockController.cs
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockController.cs
@@ -63,5 +63,27 @@
{
return await Service.UpdateStockInfoAsync(stock);
}
+
+ /// <summary>
+ /// 鎵归噺鎷嗙洏纭 - WCS鎷嗙洏浠诲姟鍏ㄩ儴鍙栧畬鏃惰皟鐢�
+ /// </summary>
+ /// <param name="dto">鎷嗙洏纭璇锋眰</param>
+ /// <returns>鎿嶄綔缁撴灉</returns>
+ [HttpPost("SplitPalletConfirm"), AllowAnonymous]
+ public async Task<WebResponseContent> SplitPalletConfirm([FromBody] SplitPalletConfirmRequestDto dto)
+ {
+ return await Service.SplitPalletConfirmAsync(dto.PalletCode);
+ }
+
+ /// <summary>
+ /// 鎵归噺缁勭洏纭 - WCS缁勭洏浠诲姟鍏ㄩ儴鏀惧畬鏃惰皟鐢�
+ /// </summary>
+ /// <param name="dto">缁勭洏纭璇锋眰</param>
+ /// <returns>鎿嶄綔缁撴灉</returns>
+ [HttpPost("GroupPalletConfirm"), AllowAnonymous]
+ public async Task<WebResponseContent> GroupPalletConfirm([FromBody] GroupPalletConfirmRequestDto dto)
+ {
+ return await Service.GroupPalletConfirmAsync(dto.PalletCode);
+ }
}
}
diff --git a/Code/docs/superpowers/plans/2026-04-16-BatchMesBinding-Plan.md b/Code/docs/superpowers/plans/2026-04-16-BatchMesBinding-Plan.md
new file mode 100644
index 0000000..5fc786c
--- /dev/null
+++ b/Code/docs/superpowers/plans/2026-04-16-BatchMesBinding-Plan.md
@@ -0,0 +1,485 @@
+# 鎵归噺 MES 缁戝畾涓庤В缁戞帴鍙e疄鏂借鍒�
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** 鏂板涓や釜 WMS 鎺ュ彛锛圫plitPalletConfirm銆丟roupPalletConfirm锛夛紝渚� WCS 鍦ㄤ换鍔¢樁娈靛畬鎴愭椂涓�娆℃�т笂浼犳墭鐩樼骇鍒殑 MES 缁戝畾/瑙g粦鏁版嵁锛屽噺灏� MES 璋冪敤娆℃暟銆�
+
+**Architecture:**
+- `Dt_SplitTemp` 涓存椂琛細鎷嗙洏寮�濮嬫椂骞傜瓑鍐欏叆鎵樼洏鐢佃姱鍒楄〃锛孋onfirm 鏃惰鍙栧苟鍒犻櫎
+- `SplitPalletAsync` 鏀归�狅細姣忔璋冪敤鏃舵鏌ヤ复鏃惰〃锛屾棤璁板綍鍒欏啓鍏ワ紝鏈夎褰曞垯璺宠繃
+- `SplitPalletConfirm`锛氫粠涓存椂琛ㄨ鍙栫數鑺� 鈫� 璋冪敤 MES UnBindContainer 鈫� 鍒犻櫎涓存椂琛ㄨ褰�
+- `GroupPalletConfirm`锛氭寜鎵樼洏鍙锋煡 Dt_StockInfoDetail 鈫� 璋冪敤 MES BindContainer
+
+**Tech Stack:** .NET 6/8, C#, SqlSugar ORM, ASP.NET Core WebAPI
+
+---
+
+## 鏂囦欢鍙樻洿姒傝
+
+| 鎿嶄綔 | 鏂囦欢 |
+|------|------|
+| 鏂板 | `WIDESEA_Model/Models/Stock/Dt_SplitTemp.cs` |
+| 鏂板 | `WIDESEA_DTO/Stock/SplitPalletConfirmRequestDto.cs` |
+| 鏂板 | `WIDESEA_DTO/Stock/GroupPalletConfirmRequestDto.cs` |
+| 淇敼 | `WIDESEA_IStockService/IStockService.cs` |
+| 淇敼 | `WIDESEA_StockService/StockService.cs` |
+| 淇敼 | `WIDESEA_WMSServer/Controllers/Stock/StockController.cs` |
+| 淇敼 | 鏁版嵁搴擄細鏂板 `Dt_SplitTemp` 琛� |
+
+---
+
+## Task 1: 鏂板缓涓存椂琛ㄥ疄浣� Dt_SplitTemp
+
+**Files:**
+- Create: `WMS/WIDESEA_WMSServer/WIDESEA_Model/Models/Stock/Dt_SplitTemp.cs`
+
+- [ ] **Step 1: 鍒涘缓 Dt_SplitTemp 瀹炰綋**
+
+```csharp
+using SqlSugar;
+using WIDESEA_Core.DB.Models;
+
+namespace WIDESEA_Model.Models
+{
+ /// <summary>
+ /// 鎷嗙洏涓存椂琛� - 鐢ㄤ簬鏆傚瓨鎷嗙洏浠诲姟鐢佃姱鍒楄〃锛屼緵鎵归噺纭鏃朵娇鐢�
+ /// </summary>
+ [SugarTable(nameof(Dt_SplitTemp), "鎷嗙洏涓存椂琛�")]
+ public class Dt_SplitTemp
+ {
+ /// <summary>
+ /// 涓婚敭
+ /// </summary>
+ [SugarColumn(IsPrimaryKey = true, IsIdentity = true, ColumnDescription = "涓婚敭")]
+ public int Id { get; set; }
+
+ /// <summary>
+ /// 鎵樼洏鍙�
+ /// </summary>
+ [SugarColumn(IsNullable = false, Length = 50, ColumnDescription = "鎵樼洏鍙�")]
+ public string PalletCode { get; set; }
+
+ /// <summary>
+ /// 鐢佃姱鏉$爜鍒楄〃锛圝SON鏍煎紡锛�
+ /// </summary>
+ [SugarColumn(IsNullable = false, Length = -1, ColumnDescription = "鐢佃姱鏉$爜鍒楄〃JSON")]
+ public string SfcList { get; set; }
+
+ /// <summary>
+ /// 鍒涘缓鏃堕棿
+ /// </summary>
+ [SugarColumn(IsNullable = false, ColumnDescription = "鍒涘缓鏃堕棿")]
+ public DateTime CreateTime { get; set; } = DateTime.Now;
+ }
+}
+```
+
+- [ ] **Step 2: Commit**
+
+```bash
+git add WMS/WIDESEA_WMSServer/WIDESEA_Model/Models/Stock/Dt_SplitTemp.cs
+git commit -m "feat(Stock): 鏂板Dt_SplitTemp鎷嗙洏涓存椂琛ㄥ疄浣�
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 2: 鏂板缓璇锋眰 DTO
+
+**Files:**
+- Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Stock/SplitPalletConfirmRequestDto.cs`
+- Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Stock/GroupPalletConfirmRequestDto.cs`
+
+- [ ] **Step 1: 鍒涘缓 SplitPalletConfirmRequestDto**
+
+```csharp
+namespace WIDESEA_DTO.Stock
+{
+ /// <summary>
+ /// 鎵归噺鎷嗙洏纭璇锋眰DTO
+ /// </summary>
+ public class SplitPalletConfirmRequestDto
+ {
+ /// <summary>
+ /// 婧愭墭鐩樺彿
+ /// </summary>
+ public string PalletCode { get; set; }
+ }
+}
+```
+
+- [ ] **Step 2: 鍒涘缓 GroupPalletConfirmRequestDto**
+
+```csharp
+namespace WIDESEA_DTO.Stock
+{
+ /// <summary>
+ /// 鎵归噺缁勭洏纭璇锋眰DTO
+ /// </summary>
+ public class GroupPalletConfirmRequestDto
+ {
+ /// <summary>
+ /// 鐩爣鎵樼洏鍙�
+ /// </summary>
+ public string PalletCode { get; set; }
+ }
+}
+```
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add WMS/WIDESEA_WMSServer/WIDESEA_DTO/Stock/SplitPalletConfirmRequestDto.cs WMS/WIDESEA_WMSServer/WIDESEA_DTO/Stock/GroupPalletConfirmRequestDto.cs
+git commit -m "feat(DTO): 鏂板鎵归噺缁勭洏鎷嗙洏纭璇锋眰DTO
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 3: 淇敼 IStockService 鎺ュ彛
+
+**Files:**
+- Modify: `WMS/WIDESEA_WMSServer/WIDESEA_IStockService/IStockService.cs`锛堝湪鎺ュ彛鏈熬娣诲姞涓や釜鏂版柟娉曪級
+
+- [ ] **Step 1: 鍦� IStockService 鎺ュ彛娣诲姞涓や釜鏂版柟娉曞0鏄�**
+
+鍦� `UpdateStockInfoAsync` 鏂规硶澹版槑涔嬪悗銆佹帴鍙g粨鏉� `}` 涔嬪墠娣诲姞锛�
+
+```csharp
+/// <summary>
+/// 鎵归噺鎷嗙洏纭 - 涓�娆℃�ц皟鐢∕ES瑙g粦鏁翠釜鎵樼洏
+/// </summary>
+/// <param name="palletCode">婧愭墭鐩樺彿</param>
+/// <returns>鎿嶄綔缁撴灉</returns>
+Task<WebResponseContent> SplitPalletConfirmAsync(string palletCode);
+
+/// <summary>
+/// 鎵归噺缁勭洏纭 - 涓�娆℃�ц皟鐢∕ES缁戝畾鏁翠釜鎵樼洏
+/// </summary>
+/// <param name="palletCode">鐩爣鎵樼洏鍙�</param>
+/// <returns>鎿嶄綔缁撴灉</returns>
+Task<WebResponseContent> GroupPalletConfirmAsync(string palletCode);
+```
+
+- [ ] **Step 2: Commit**
+
+```bash
+git add WMS/WIDESEA_WMSServer/WIDESEA_IStockService/IStockService.cs
+git commit -m "feat(IStockService): 鏂板SplitPalletConfirmAsync鍜孏roupPalletConfirmAsync鎺ュ彛
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 4: 淇敼 StockService 瀹炵幇 - SplitPalletConfirmAsync 鍜� GroupPalletConfirmAsync
+
+**Files:**
+- Modify: `WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockService.cs`
+
+- [ ] **Step 1: 娣诲姞 ISqlSugarClient 娉ㄥ叆鍜� Dt_SplitTemp 瀹炰綋**
+
+鍦� `StockService` 绫讳腑娣诲姞锛�
+
+```csharp
+using SqlSugar;
+using WIDESEA_Model.Models;
+using Newtonsoft.Json;
+```
+
+鍦ㄧ被涓坊鍔犲睘鎬э細
+
+```csharp
+/// <summary>
+/// SqlSugar瀹㈡埛绔紙鐢ㄤ簬涓存椂琛ㄦ搷浣滐級
+/// </summary>
+public ISqlSugarClient SqlSugarClient { get; }
+```
+
+鏋勯�犲嚱鏁颁腑娉ㄥ叆锛�
+
+```csharp
+public StockService(
+ ...,
+ ISqlSugarClient sqlSugarClient) // 娣诲姞鍒板弬鏁版湯灏�
+{
+ ...
+ SqlSugarClient = sqlSugarClient;
+}
+```
+
+- [ ] **Step 2: 瀹炵幇 SplitPalletConfirmAsync 鏂规硶**
+
+鍦ㄧ被鏈熬锛坄UpdateStockInfoAsync` 鏂规硶涔嬪悗銆乣CreateDetailHistory` 涔嬪墠锛夋坊鍔狅細
+
+```csharp
+/// <summary>
+/// 鎵归噺鎷嗙洏纭 - 涓�娆℃�ц皟鐢∕ES瑙g粦鏁翠釜鎵樼洏
+/// </summary>
+/// <param name="palletCode">婧愭墭鐩樺彿</param>
+/// <returns>鎿嶄綔缁撴灉</returns>
+public async Task<WebResponseContent> SplitPalletConfirmAsync(string palletCode)
+{
+ WebResponseContent content = new WebResponseContent();
+ try
+ {
+ if (string.IsNullOrWhiteSpace(palletCode))
+ return content.Error("鎵樼洏鍙蜂笉鑳戒负绌�");
+
+ // 1. 浠庝复鏃惰〃璇诲彇鐢佃姱鍒楄〃
+ var tempRecord = SqlSugarClient.Queryable<Dt_SplitTemp>()
+ .Where(t => t.PalletCode == palletCode)
+ .First();
+ if (tempRecord == null)
+ return content.Error("鏈壘鍒版媶鐩樹复鏃惰褰曪紝璇峰厛鎵ц鎷嗙洏鎿嶄綔");
+
+ var sfcList = JsonConvert.DeserializeObject<List<string>>(tempRecord.SfcList);
+ if (sfcList == null || !sfcList.Any())
+ return content.Error("涓存椂琛ㄤ腑鐢佃姱鍒楄〃涓虹┖");
+
+ // 2. 璋冪敤MES瑙g粦
+ var unbindRequest = new UnBindContainerRequest
+ {
+ EquipmentCode = StockConstants.MES_EQUIPMENT_CODE,
+ ResourceCode = StockConstants.MES_RESOURCE_CODE,
+ LocalTime = DateTime.Now,
+ ContainCode = palletCode,
+ SfcList = sfcList
+ };
+ var unbindResult = _mesService.UnBindContainer(unbindRequest);
+ if (unbindResult == null || unbindResult.Data == null || !unbindResult.Data.IsSuccess)
+ {
+ return content.Error($"MES瑙g粦澶辫触: {unbindResult?.Data?.Msg ?? unbindResult?.ErrorMessage ?? "鏈煡閿欒"}");
+ }
+
+ // 3. 鍒犻櫎涓存椂琛ㄨ褰�
+ SqlSugarClient.Deleteable<Dt_SplitTemp>().Where(t => t.PalletCode == palletCode).ExecuteCommand();
+
+ return content.OK("鎵归噺鎷嗙洏纭鎴愬姛");
+ }
+ catch (Exception ex)
+ {
+ return content.Error($"鎵归噺鎷嗙洏纭澶辫触: {ex.Message}");
+ }
+}
+
+/// <summary>
+/// 鎵归噺缁勭洏纭 - 涓�娆℃�ц皟鐢∕ES缁戝畾鏁翠釜鎵樼洏
+/// </summary>
+/// <param name="palletCode">鐩爣鎵樼洏鍙�</param>
+/// <returns>鎿嶄綔缁撴灉</returns>
+public async Task<WebResponseContent> GroupPalletConfirmAsync(string palletCode)
+{
+ WebResponseContent content = new WebResponseContent();
+ try
+ {
+ if (string.IsNullOrWhiteSpace(palletCode))
+ return content.Error("鎵樼洏鍙蜂笉鑳戒负绌�");
+
+ // 1. 鏌ヨ璇ユ墭鐩樹笅鐨勬墍鏈夌數鑺槑缁�
+ var stockInfo = StockInfoService.Repository.QueryFirst(s => s.PalletCode == palletCode);
+ if (stockInfo == null)
+ return content.Error("鎵樼洏涓嶅瓨鍦�");
+
+ var details = StockInfoDetailService.Repository.QueryData(d => d.StockId == stockInfo.Id);
+ if (!details.Any())
+ return content.Error("鎵樼洏涓嬫棤鐢佃姱鏁版嵁");
+
+ // 2. 璋冪敤MES缁戝畾
+ var bindRequest = new BindContainerRequest
+ {
+ ContainerCode = palletCode,
+ EquipmentCode = StockConstants.MES_EQUIPMENT_CODE,
+ ResourceCode = StockConstants.MES_RESOURCE_CODE,
+ LocalTime = DateTime.Now,
+ OperationType = StockConstants.MES_BIND_OPERATION_TYPE,
+ ContainerSfcList = details.Select(d => new ContainerSfcItem
+ {
+ Sfc = d.SerialNumber,
+ Location = d.InboundOrderRowNo.ToString()
+ }).ToList()
+ };
+ var bindResult = _mesService.BindContainer(bindRequest);
+ if (bindResult == null || bindResult.Data == null || !bindResult.Data.IsSuccess)
+ {
+ return content.Error($"MES缁戝畾澶辫触: {bindResult?.Data?.Msg ?? bindResult?.ErrorMessage ?? "鏈煡閿欒"}");
+ }
+
+ return content.OK("鎵归噺缁勭洏纭鎴愬姛");
+ }
+ catch (Exception ex)
+ {
+ return content.Error($"鎵归噺缁勭洏纭澶辫触: {ex.Message}");
+ }
+}
+```
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockService.cs
+git commit -m "feat(StockService): 瀹炵幇SplitPalletConfirmAsync鍜孏roupPalletConfirmAsync
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 5: 淇敼 SplitPalletAsync - 娣诲姞涓存椂琛ㄥ箓绛夊啓鍏ラ�昏緫
+
+**Files:**
+- Modify: `WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockService.cs`
+
+- [ ] **Step 1: 鍦� SplitPalletAsync 鏂规硶寮�澶存坊鍔犱复鏃惰〃鍐欏叆閫昏緫**
+
+鍦� `SplitPalletAsync` 鏂规硶鐨� `try` 鍧楀紑澶达紙`if (stock == null ...` 涔嬪悗锛夈�佸湪浜嬪姟 `ExecuteWithinTransactionAsync` 璋冪敤涔嬪墠锛屾坊鍔狅細
+
+```csharp
+// 骞傜瓑鍐欏叆锛氭鏌ヤ复鏃惰〃鏄惁宸叉湁璇ユ墭鐩樿褰曪紝鏃犲垯鍐欏叆
+var existingTemp = SqlSugarClient.Queryable<Dt_SplitTemp>()
+ .Where(t => t.PalletCode == stock.SourcePalletNo)
+ .First();
+if (existingTemp == null)
+{
+ // 鏌ヨ璇ユ墭鐩樺綋鍓嶆墍鏈夌數鑺紝瀛樺叆涓存椂琛�
+ var sourceStockForTemp = StockInfoService.Repository.QueryFirst(s => s.PalletCode == stock.SourcePalletNo);
+ if (sourceStockForTemp != null)
+ {
+ var allDetails = StockInfoDetailService.Repository.QueryData(d => d.StockId == sourceStockForTemp.Id);
+ if (allDetails.Any())
+ {
+ var sfcListJson = JsonConvert.SerializeObject(allDetails.Select(d => d.SerialNumber).ToList());
+ SqlSugarClient.Insertable(new Dt_SplitTemp
+ {
+ PalletCode = stock.SourcePalletNo,
+ SfcList = sfcListJson,
+ CreateTime = DateTime.Now
+ }).ExecuteCommand();
+ }
+ }
+}
+```
+
+娉ㄦ剰锛氳繖娈典唬鐮佸湪 `return await ExecuteWithinTransactionAsync(...)` 涔嬪墠鎵ц锛屼笉鍦ㄤ簨鍔″唴銆�
+
+- [ ] **Step 2: Commit**
+
+```bash
+git add WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockService.cs
+git commit -m "feat(SplitPalletAsync): 娣诲姞涓存椂琛ㄥ箓绛夊啓鍏ラ�昏緫
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 6: 淇敼 StockController - 娣诲姞鏂拌矾鐢�
+
+**Files:**
+- Modify: `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockController.cs`
+
+- [ ] **Step 1: 鍦� StockController 娣诲姞涓や釜鏂拌矾鐢�**
+
+鍦� `UpdateStockInfoAsync` 鏂规硶涔嬪悗銆佺被缁撴潫 `}` 涔嬪墠娣诲姞锛�
+
+```csharp
+/// <summary>
+/// 鎵归噺鎷嗙洏纭 - WCS鎷嗙洏浠诲姟鍏ㄩ儴鍙栧畬鏃惰皟鐢�
+/// </summary>
+/// <param name="dto">鎷嗙洏纭璇锋眰</param>
+/// <returns>鎿嶄綔缁撴灉</returns>
+[HttpPost("SplitPalletConfirm"), AllowAnonymous]
+public async Task<WebResponseContent> SplitPalletConfirm([FromBody] SplitPalletConfirmRequestDto dto)
+{
+ return await Service.SplitPalletConfirmAsync(dto.PalletCode);
+}
+
+/// <summary>
+/// 鎵归噺缁勭洏纭 - WCS缁勭洏浠诲姟鍏ㄩ儴鏀惧畬鏃惰皟鐢�
+/// </summary>
+/// <param name="dto">缁勭洏纭璇锋眰</param>
+/// <returns>鎿嶄綔缁撴灉</returns>
+[HttpPost("GroupPalletConfirm"), AllowAnonymous]
+public async Task<WebResponseContent> GroupPalletConfirm([FromBody] GroupPalletConfirmRequestDto dto)
+{
+ return await Service.GroupPalletConfirmAsync(dto.PalletCode);
+}
+```
+
+鍚屾椂鍦ㄦ枃浠堕《閮ㄦ坊鍔� using锛�
+
+```csharp
+using WIDESEA_DTO.Stock;
+```
+
+- [ ] **Step 2: Commit**
+
+```bash
+git add WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockController.cs
+git commit -m "feat(StockController): 鏂板SplitPalletConfirm鍜孏roupPalletConfirm鎺ュ彛璺敱
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 7: 鏁版嵁搴撳彉鏇磋剼鏈�
+
+**Files:**
+- Create: `WMS/WIDESEA_WMSServer/Database/Scripts/20260416_Dt_SplitTemp.sql`
+
+- [ ] **Step 1: 鍒涘缓涓存椂琛� DDL 鑴氭湰**
+
+```sql
+-- 鎷嗙洏涓存椂琛細鐢ㄤ簬鏆傚瓨鎷嗙洏浠诲姟鐢佃姱鍒楄〃锛屼緵鎵归噺纭鏃朵娇鐢�
+IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Dt_SplitTemp]') AND type in (N'U'))
+BEGIN
+ CREATE TABLE [dbo].[Dt_SplitTemp](
+ [Id] [int] IDENTITY(1,1) NOT NULL,
+ [PalletCode] [nvarchar](50) NOT NULL,
+ [SfcList] [nvarchar](max) NOT NULL,
+ [CreateTime] [datetime] NOT NULL DEFAULT GETDATE(),
+ CONSTRAINT [PK_Dt_SplitTemp] PRIMARY KEY CLUSTERED ([Id] ASC)
+ );
+
+ -- 鍙�夛細娣诲姞鍞竴绱㈠紩闃叉鍚屼竴鎵樼洏閲嶅鍐欏叆
+ CREATE UNIQUE NONCLUSTERED INDEX [IX_Dt_SplitTemp_PalletCode] ON [dbo].[Dt_SplitTemp]([PalletCode] ASC);
+END
+GO
+```
+
+- [ ] **Step 2: Commit**
+
+```bash
+git add WMS/WIDESEA_WMSServer/Database/Scripts/20260416_Dt_SplitTemp.sql
+git commit -m "feat(db): 鏂板Dt_SplitTemp鎷嗙洏涓存椂琛�
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 8: 鏋勫缓楠岃瘉
+
+- [ ] **Step 1: 杩愯 dotnet build 楠岃瘉缂栬瘧閫氳繃**
+
+```bash
+cd D:\Git\ShanMeiXinNengYuan\Code\WMS\WIDESEA_WMSServer
+dotnet build WIDESEA_WMSServer.sln
+```
+
+Expected: Build succeeded with no errors.
+
+---
+
+## 鑷娓呭崟
+
+- [ ] 鎵�鏈� public 鏂规硶鍧囨湁 XML 鏂囨。娉ㄩ噴
+- [ ] `Dt_SplitTemp.SfcList` 浣跨敤 `nvarchar(max)` 瀛樺偍 JSON
+- [ ] `SplitPalletConfirmAsync` 璇诲彇涓存椂琛ㄥ悗鍒犻櫎璁板綍
+- [ ] `SplitPalletAsync` 涓殑涓存椂琛ㄥ啓鍏ュ湪浜嬪姟澶栨墽琛�
+- [ ] `GroupPalletConfirmAsync` 浠� `Dt_StockInfoDetail` 鏌ョ數鑺紝涓嶆煡涓存椂琛�
+- [ ] 涓や釜鏂� Controller 鏂规硶鍧囨爣璁� `[AllowAnonymous]`
+- [ ] 鏁版嵁搴撹剼鏈惈 IF NOT EXISTS 闃叉閲嶅鍒涘缓
diff --git a/Code/docs/superpowers/specs/2026-04-16-BatchMesBinding-Design.md b/Code/docs/superpowers/specs/2026-04-16-BatchMesBinding-Design.md
new file mode 100644
index 0000000..dfe443a
--- /dev/null
+++ b/Code/docs/superpowers/specs/2026-04-16-BatchMesBinding-Design.md
@@ -0,0 +1,106 @@
+# 鎵归噺 MES 缁戝畾涓庤В缁戞帴鍙h璁�
+
+## 鑳屾櫙
+
+褰撳墠 `StockSerivce` 涓� `GroupPalletAsync`銆乣ChangePalletAsync`銆乣SplitPalletAsync` 姣忔璋冪敤閮戒細瑙﹀彂涓�娆� MES 鎺ュ彛銆�
+
+WCS 鏈哄櫒浜轰换鍔℃寜鎵规鍙栨斁锛屼竴涓墭鐩樺彲鑳介渶瑕佸娆� MES 璋冪敤锛堝鎹㈢洏闇�瑕佸厛瑙g粦鍐嶇粦瀹氾級銆備负鍑忓皯 MES 璋冪敤娆℃暟锛屾柊澧炰袱涓壒閲忕‘璁ゆ帴鍙d緵 WCS 鍦ㄤ换鍔¢樁娈靛畬鎴愭椂涓�娆℃�т笂浼犳墭鐩樼骇鍒殑缁戝畾/瑙g粦鏁版嵁銆�
+
+## 鏂板鎺ュ彛
+
+### 1. SplitPalletConfirm 鈥� 鎵归噺鎷嗙洏纭
+
+**瑙﹀彂鏃舵満**锛歐CS 鎷嗙洏浠诲姟/鎹㈢洏浠诲姟鍏ㄩ儴鍙栧畬
+
+**WCS 璋冪敤鏂瑰紡**锛歚POST /api/Stock/SplitPalletConfirm`
+
+**璇锋眰鍙傛暟**锛�
+```csharp
+public class SplitPalletConfirmRequest
+{
+ /// <summary>
+ /// 婧愭墭鐩樺彿
+ /// </summary>
+ public string PalletCode { get; set; }
+}
+```
+
+**澶勭悊娴佺▼**锛�
+1. WMS 鏍规嵁 `PalletCode` 浠庝复鏃惰〃 `Dt_SplitTemp` 璇诲彇棰勫瓨鐢佃姱鍒楄〃
+2. 璋冪敤 MES `UnBindContainer`锛堜竴娆℃�т笂浼犳暣鎵樼數鑺級
+3. 鍒犻櫎涓存椂琛� `Dt_SplitTemp` 涓搴旇褰�
+
+**涓存椂琛ㄥ啓鍏ユ椂鏈�**锛歚SplitPalletAsync` 姣忔琚皟鐢ㄦ椂锛屽厛妫�鏌� `Dt_SplitTemp` 涓槸鍚﹀瓨鍦ㄨ鎵樼洏璁板綍锛涗笉瀛樺湪鍒欏皢褰撳墠鎵樼洏瀵瑰簲鐨勬墍鏈夌數鑺潯鐮佸啓鍏ヤ复鏃惰〃锛涘凡瀛樺湪鍒欒烦杩囧啓鍏ャ��
+
+**涓存椂琛ㄧ粨鏋�**锛坄Dt_SplitTemp`锛夛細
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| Id | int | 涓婚敭 |
+| PalletCode | string | 鎵樼洏鍙� |
+| SfcList | string | 鐢佃姱鏉$爜鍒楄〃锛圝SON鏁扮粍锛� |
+| CreateTime | DateTime | 鍒涘缓鏃堕棿 |
+
+---
+
+### 2. GroupPalletConfirm 鈥� 鎵归噺缁勭洏纭
+
+**瑙﹀彂鏃舵満**锛歐CS 缁勭洏浠诲姟/鎹㈢洏浠诲姟鍏ㄩ儴鏀惧畬
+
+**WCS 璋冪敤鏂瑰紡**锛歚POST /api/Stock/GroupPalletConfirm`
+
+**璇锋眰鍙傛暟**锛�
+```csharp
+public class GroupPalletConfirmRequest
+{
+ /// <summary>
+ /// 鐩爣鎵樼洏鍙�
+ /// </summary>
+ public string PalletCode { get; set; }
+}
+```
+
+**澶勭悊娴佺▼**锛�
+1. WMS 鏍规嵁 `PalletCode` 鏌ヨ `Dt_StockInfoDetail` 涓鎵樼洏涓嬬殑鎵�鏈夌數鑺槑缁�
+2. 璋冪敤 MES `BindContainer`锛堜竴娆℃�т笂浼犳暣鎵樼數鑺粦瀹氾級
+3. 杩斿洖缁撴灉
+
+**娉ㄦ剰**锛氱數鑺暟鎹湪缁勭洏浠诲姟鏀捐揣杩囩▼涓凡鐢� WCS 閫氳繃鍏朵粬鎺ュ彛鍐欏叆 `Dt_StockInfoDetail`锛學MS 涓嶉渶瑕侀澶栧瓨鍌�
+
+---
+
+## 鎹㈢洏浠诲姟瀹屾暣娴佺▼
+
+```
+鎹㈢洏浠诲姟锛�
+ 鍏ㄩ儴鍙栧畬 鈫� SplitPalletConfirm(婧愭墭鐩�) 鈫� MES UnBindContainer
+ 鍏ㄩ儴鏀惧畬 鈫� GroupPalletConfirm(鐩爣鎵樼洏) 鈫� MES BindContainer
+```
+
+## 鐜版湁鎺ュ彛澶勭悊
+
+- `GroupPalletAsync`銆乣ChangePalletAsync`銆乣SplitPalletAsync` 淇濈暀
+- 鏂版帴鍙d笌鐜版湁鎺ュ彛骞跺瓨锛學CS 鏍规嵁浠诲姟鍦烘櫙閫夋嫨璋冪敤
+- 鐜版湁鎺ュ彛缁х画鎵挎媴鍗曟/闈炴壒閲忓満鏅殑 MES 璋冪敤
+
+## WCS 渚ф敼閫犺鐐�
+
+- 鎷嗙洏/鎹㈢洏浠诲姟寮�濮嬫椂锛學CS 璋冪敤鐜版湁 `SplitPalletAsync` 鎺ュ彛锛沇MS 鍦� `SplitPalletAsync` 鍐呴儴鍏堟鏌� `Dt_SplitTemp` 鏄惁宸叉湁璇ユ墭鐩樿褰曪紝鏃犲垯鍐欏叆锛屾湁鍒欒烦杩囷紙骞傜瓑鍐欏叆锛�
+- 缁勭洏浠诲姟鍏ㄩ儴鏀惧畬鏃惰皟鐢� `GroupPalletConfirm`
+- 鎹㈢洏浠诲姟鍏ㄩ儴鍙栧畬鏃惰皟鐢� `SplitPalletConfirm`锛屽叏閮ㄦ斁瀹屾椂璋冪敤 `GroupPalletConfirm`
+
+## 鏂囦欢鍙樻洿
+
+| 鎿嶄綔 | 鏂囦欢 |
+|------|------|
+| 鏂板 | `WIDESEA_DTO/Stock/SplitPalletConfirmRequestDto.cs` |
+| 鏂板 | `WIDESEA_DTO/Stock/GroupPalletConfirmRequestDto.cs` |
+| 鏂板 | `WIDESEA_Model/Models/Dt_SplitTemp.cs` |
+| 淇敼 | `WIDESEA_IStockService/IStockService.cs`锛堟柊澧炴帴鍙e畾涔夛級 |
+| 淇敼 | `WIDESEA_StockService/StockService.cs`锛堝疄鐜版壒閲忕‘璁ら�昏緫锛� |
+| 淇敼 | `WIDESEA_WMSServer/Controllers/Stock/StockInfoDetailController.cs`锛堟柊澧� API 璺敱锛� |
+| 淇敼 | 鏁版嵁搴擄細鏂板 `Dt_SplitTemp` 琛� |
+
+## 椋庨櫓涓庣害鏉�
+
+- 涓存椂琛� `Dt_SplitTemp` 闇�瑕佹湁娓呯悊鏈哄埗锛岄槻姝㈠紓甯告儏鍐典笅鏁版嵁娈嬬暀
+- MES 鎺ュ彛璋冪敤澶辫触鏃讹紝涓存椂琛ㄦ暟鎹笉鍥炴粴锛屼笅娆¢噸璇曟椂鍙兘閲嶅瑙g粦锛岄渶 MES 渚у箓绛夋敮鎸�
--
Gitblit v1.9.3