From 5f1294d6dea53d286f5e7029839d37bf490e32bb Mon Sep 17 00:00:00 2001
From: wanshenmean <cathay_xy@163.com>
Date: 星期二, 28 四月 2026 00:05:36 +0800
Subject: [PATCH] feat(任务管理): 新增组盘拆盘操作及任务创建功能

---
 Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Manual.cs |  252 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 252 insertions(+), 0 deletions(-)

diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Manual.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Manual.cs
index b799f1e..a550faf 100644
--- a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Manual.cs
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Manual.cs
@@ -1,9 +1,13 @@
 using System.ComponentModel;
 using System.Reflection;
 using Newtonsoft.Json;
+using SqlSugar;
+using WIDESEA_Common.Constants;
+using WIDESEA_Common.StockEnum;
 using WIDESEA_Common.TaskEnum;
 using WIDESEA_Core;
 using WIDESEA_Core.Helper;
+using WIDESEA_DTO.MES;
 using WIDESEA_DTO.Task;
 using WIDESEA_Model.Models;
 
@@ -328,6 +332,254 @@
             return descAttr?.Description ?? taskStatus.ToString();
         }
 
+        /// <summary>
+        /// 缁勭洏/鎷嗙洏鎿嶄綔骞跺垱寤哄叆搴撲换鍔★紝瀹屾垚鍚庝笅鍙慦CS
+        /// </summary>
+        /// <param name="dto">缁勭洏/鎷嗙洏鎿嶄綔鍙傛暟</param>
+        /// <returns>鎿嶄綔缁撴灉</returns>
+        public async Task<WebResponseContent> PalletOperationAndCreateTaskAsync(PalletOperationTaskDto dto)
+        {
+            try
+            {
+                // 1. 鍙傛暟鏍¢獙
+                if (string.IsNullOrWhiteSpace(dto.PalletCode))
+                    return WebResponseContent.Instance.Error("鎵樼洏鍙蜂笉鑳戒负绌�");
+
+                if (string.IsNullOrWhiteSpace(dto.Action)
+                    || (dto.Action != "缁勭洏" && dto.Action != "鎷嗙洏"))
+                    return WebResponseContent.Instance.Error("鎵ц鍔ㄤ綔蹇呴』鏄�'缁勭洏'鎴�'鎷嗙洏'");
+
+                if (string.IsNullOrWhiteSpace(dto.LineId))
+                    return WebResponseContent.Instance.Error("绾夸綋缂栧彿涓嶈兘涓虹┖");
+
+                if (string.IsNullOrWhiteSpace(dto.WarehouseCode))
+                    return WebResponseContent.Instance.Error("浠撳簱缂栧彿涓嶈兘涓虹┖");
+
+                if (string.IsNullOrWhiteSpace(dto.RobotName))
+                    return WebResponseContent.Instance.Error("鏈烘鎵嬪悕绉颁笉鑳戒负绌�");
+
+                // 缁勭洏鏃剁數鑺垪琛ㄥ繀浼�
+                if (dto.Action == "缁勭洏" && (dto.Cells == null || !dto.Cells.Any()))
+                    return WebResponseContent.Instance.Error("缁勭洏鏃剁數鑺垪琛ㄤ笉鑳戒负绌�");
+
+                // 2. 鏍规嵁浠撳簱缂栧彿鏌ヨ浠撳簱淇℃伅锛岃幏鍙� WarehouseId銆乼argetAddress 鍜� roadway
+                var warehouse = _sqlSugarClient.Queryable<Dt_Warehouse>()
+                    .First(w => w.WarehouseCode == dto.WarehouseCode);
+                if (warehouse == null)
+                    return WebResponseContent.Instance.Error($"鏈壘鍒颁粨搴撶紪鍙蜂负[{dto.WarehouseCode}]鐨勪粨搴�");
+
+                int warehouseId = warehouse.WarehouseId;
+                string targetAddress = warehouse.WarehouseCode;
+                string roadway = warehouse.WarehouseCode;
+
+                // 3. 鏍规嵁鍔ㄤ綔绫诲瀷鎵ц涓嶅悓娴佺▼
+                if (dto.Action == "缁勭洏")
+                    return await ExecuteGroupPalletAsync(dto, warehouseId, targetAddress, roadway);
+
+                return await ExecuteSplitPalletAsync(dto, warehouseId, targetAddress, roadway);
+            }
+            catch (Exception ex)
+            {
+                return WebResponseContent.Instance.Error($"缁勭洏/鎷嗙洏鎿嶄綔寮傚父: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 鎵ц缁勭洏鎿嶄綔锛氭坊鍔犲簱瀛� 鈫� 鐩存帴璋冪敤MES缁戝畾 鈫� 鍒涘缓鍏ュ簱浠诲姟 鈫� 涓嬪彂WCS
+        /// </summary>
+        private async Task<WebResponseContent> ExecuteGroupPalletAsync(
+            PalletOperationTaskDto dto, int warehouseId, string targetAddress, string roadway)
+        {
+            return await _unitOfWorkManage.BeginTranAsync(async () =>
+            {
+                // 1. 鍒涘缓搴撳瓨涓昏褰曪紙涓嶇粦瀹氳揣浣嶏級
+                var stockInfo = new Dt_StockInfo
+                {
+                    PalletCode = dto.PalletCode,
+                    PalletType = 0,
+                    WarehouseId = warehouseId,
+                    LocationId = 0,
+                    StockStatus = 0,
+                    MesUploadStatus = 0
+                };
+                var stockId = await _stockInfoService.Repository.AddDataAsync(stockInfo);
+                if (stockId <= 0)
+                    return WebResponseContent.Instance.Error("鍒涘缓搴撳瓨璁板綍澶辫触");
+
+                // 2. 鎵归噺鍒涘缓搴撳瓨鏄庣粏
+                var details = dto.Cells.Select((cell, i) => new Dt_StockInfoDetail
+                {
+                    StockId = (int)stockId,
+                    MaterielCode = cell.SfcCode,
+                    MaterielName = "",
+                    OrderNo = "",
+                    ProductionDate = DateTime.Now.ToString("yyyy-MM-dd"),
+                    EffectiveDate = "",
+                    SerialNumber = cell.SfcCode,
+                    StockQuantity = 1,
+                    OutboundQuantity = 0,
+                    Status = 0,
+                    InboundOrderRowNo = Convert.ToInt32(cell.Channel),
+                    Remark = cell.Channel
+                }).ToList();
+                if (await _sqlSugarClient.Insertable(details).ExecuteCommandAsync() <= 0)
+                    return WebResponseContent.Instance.Error("鍒涘缓搴撳瓨鏄庣粏澶辫触");
+
+                // 3. 璋冪敤MES缁戝畾
+                var (equipmentCode, resourceCode, token) = ResolveMesConfig(dto.RobotName);
+                var bindRequest = new BindContainerRequest
+                {
+                    ContainerCode = dto.PalletCode,
+                    EquipmentCode = equipmentCode,
+                    ResourceCode = resourceCode,
+                    LocalTime = DateTime.Now,
+                    OperationType = StockConstants.MES_BIND_OPERATION_TYPE,
+                    ContainerSfcList = dto.Cells.Select((cell, i) => new ContainerSfcItem
+                    {
+                        Sfc = cell.SfcCode,
+                        Location = cell.Channel
+                    }).ToList()
+                };
+                var mesResult = string.IsNullOrWhiteSpace(token)
+                    ? _mesService.BindContainer(bindRequest)
+                    : _mesService.BindContainer(bindRequest, token);
+                if (!CheckMesResult(mesResult, out var mesError))
+                    return WebResponseContent.Instance.Error($"MES缁勭洏缁戝畾澶辫触: {mesError}");
+
+                // 4. 鍒涘缓鍏ュ簱浠诲姟骞朵笅鍙慦CS
+                return await CreateTaskAndDispatchAsync(dto, warehouseId, targetAddress, roadway,
+                    TaskInboundTypeEnum.Inbound, "缁勭洏鎴愬姛骞跺垱寤哄叆搴撲换鍔�");
+            });
+        }
+
+        /// <summary>
+        /// 鎵ц鎷嗙洏鎿嶄綔锛氭煡璇㈠簱瀛� 鈫� 鐩存帴璋冪敤MES瑙g粦 鈫� 娓呴櫎搴撳瓨璐т綅 鈫� 鍒涘缓绌烘墭鐩樺叆搴撲换鍔� 鈫� 涓嬪彂WCS
+        /// </summary>
+        private async Task<WebResponseContent> ExecuteSplitPalletAsync(
+            PalletOperationTaskDto dto, int warehouseId, string targetAddress, string roadway)
+        {
+            return await _unitOfWorkManage.BeginTranAsync(async () =>
+            {
+                // 1. 瀵艰埅鏌ヨ搴撳瓨鍙婃槑缁�
+                var stockInfo = await _stockInfoService.Repository
+                    .QueryDataNavFirstAsync(s => s.PalletCode == dto.PalletCode);
+                if (stockInfo == null)
+                    return WebResponseContent.Instance.Error($"鏈壘鍒版墭鐩榌{dto.PalletCode}]鐨勫簱瀛樿褰�");
+
+                var sfcList = stockInfo.Details?.Select(d => d.SerialNumber).ToList()
+                    ?? new List<string>();
+                if (!sfcList.Any())
+                    return WebResponseContent.Instance.Error($"鎵樼洏[{dto.PalletCode}]涓嬫棤鐢佃姱鏁版嵁");
+
+                // 2. 璋冪敤MES瑙g粦
+                var (equipmentCode, resourceCode, token) = ResolveMesConfig(dto.RobotName);
+                var unbindRequest = new UnBindContainerRequest
+                {
+                    EquipmentCode = equipmentCode,
+                    ResourceCode = resourceCode,
+                    LocalTime = DateTime.Now,
+                    ContainCode = dto.PalletCode,
+                    SfcList = sfcList
+                };
+                var mesResult = string.IsNullOrWhiteSpace(token)
+                    ? _mesService.UnBindContainer(unbindRequest)
+                    : _mesService.UnBindContainer(unbindRequest, token);
+                if (!CheckMesResult(mesResult, out var mesError))
+                    return WebResponseContent.Instance.Error($"MES鎷嗙洏瑙g粦澶辫触: {mesError}");
+
+                // 3. 娓呴櫎搴撳瓨缁戝畾鐨勮揣浣�
+                stockInfo.LocationId = 0;
+                stockInfo.LocationCode = null;
+                await _sqlSugarClient.Updateable(stockInfo)
+                    .UpdateColumns(s => new { s.LocationId, s.LocationCode })
+                    .ExecuteCommandAsync();
+
+                // 4. 鍒涘缓绌烘墭鐩樺叆搴撲换鍔″苟涓嬪彂WCS
+                return await CreateTaskAndDispatchAsync(dto, warehouseId, targetAddress, roadway,
+                    TaskInboundTypeEnum.InEmpty, "鎷嗙洏鎴愬姛骞跺垱寤虹┖鎵樼洏鍏ュ簱浠诲姟");
+            });
+        }
+
+        /// <summary>
+        /// 瑙f瀽MES璁惧閰嶇疆锛岃繑鍥� (equipmentCode, resourceCode, token)
+        /// </summary>
+        private (string equipmentCode, string resourceCode, string token) ResolveMesConfig(string deviceName)
+        {
+            var config = _mesDeviceConfigService.GetByDeviceName(deviceName);
+            return (
+                config?.EquipmentCode ?? StockConstants.MES_EQUIPMENT_CODE,
+                config?.ResourceCode ?? StockConstants.MES_RESOURCE_CODE,
+                config?.Token
+            );
+        }
+
+        /// <summary>
+        /// 妫�鏌ES杩斿洖缁撴灉锛屽け璐ユ椂閫氳繃 error 杈撳嚭閿欒淇℃伅
+        /// </summary>
+        private bool CheckMesResult(HttpResponseResult<MesResponse> result, out string error)
+        {
+            if (result?.Data != null && result.Data.IsSuccess)
+            {
+                error = null;
+                return true;
+            }
+
+            error = result?.Data?.Msg ?? result?.ErrorMessage ?? "鏈煡閿欒";
+            return false;
+        }
+
+        /// <summary>
+        /// 鍒涘缓鍏ュ簱浠诲姟骞朵笅鍙慦CS
+        /// </summary>
+        private async Task<WebResponseContent> CreateTaskAndDispatchAsync(
+            PalletOperationTaskDto dto, int warehouseId, string targetAddress, string roadway,
+            TaskInboundTypeEnum taskType, string successMessage)
+        {
+            int taskNum = await BaseDal.GetTaskNo();
+            var task = new Dt_Task
+            {
+                TaskNum = taskNum,
+                PalletCode = dto.PalletCode,
+                SourceAddress = dto.LineId,
+                TargetAddress = targetAddress,
+                TaskType = taskType.GetHashCode(),
+                TaskStatus = TaskInStatusEnum.InNew.GetHashCode(),
+                Grade = dto.Grade,
+                Roadway = roadway,
+                WarehouseId = warehouseId,
+                CurrentAddress = dto.LineId,
+                NextAddress = targetAddress,
+                Creater = "manual",
+                CreateDate = DateTime.Now,
+                ModifyDate = DateTime.Now
+            };
+
+            if (await BaseDal.AddDataAsync(task) <= 0)
+                return WebResponseContent.Instance.Error("鍒涘缓浠诲姟澶辫触");
+
+            var wmsTaskDto = new WMSTaskDTO
+            {
+                TaskNum = task.TaskNum,
+                PalletCode = task.PalletCode,
+                SourceAddress = task.SourceAddress,
+                TargetAddress = task.TargetAddress,
+                TaskType = task.TaskType,
+                Roadway = task.Roadway,
+                TaskStatus = task.TaskStatus,
+                WarehouseId = task.WarehouseId
+            };
+
+            var wcsResult = _httpClientHelper.Post<WebResponseContent>(
+                "http://localhost:9292/api/Task/ReceiveManualTask",
+                new List<WMSTaskDTO> { wmsTaskDto }.ToJson());
+
+            if (!wcsResult.IsSuccess || !wcsResult.Data.Status)
+                return WebResponseContent.Instance.Error(
+                    $"浠诲姟宸插垱寤轰絾鍙戦�佺粰WCS澶辫触: {wcsResult.ErrorMessage}\r\n {wcsResult.Data?.Message}");
+
+            return WebResponseContent.Instance.OK($"{successMessage}锛屼换鍔″彿: {taskNum}");
+        }
+
         #endregion 鎵嬪姩浠诲姟
     }
 }

--
Gitblit v1.9.3