From 461524a7a1def4532b9af4ab40733f899f360db7 Mon Sep 17 00:00:00 2001
From: heshaofeng <heshaofeng@hnkhzn.com>
Date: 星期四, 09 四月 2026 14:39:45 +0800
Subject: [PATCH] Merge branch 'htq20251215' of http://115.159.85.185:8098/r/ZhongRui/ALDbanyunxiangmu into htq20251215

---
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs | 1640 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 1,414 insertions(+), 226 deletions(-)

diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs"
index 61daea9..10ebb01 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs"
@@ -1,11 +1,17 @@
-锘縰sing Microsoft.Extensions.Logging;
+锘縰sing MailKit.Search;
+using Microsoft.Extensions.Logging;
 using Newtonsoft.Json;
 using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup;
+using SqlSugar;
 using System;
+using System.Collections;
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using System.Transactions;
+using WIDESEA_BasicService;
 using WIDESEA_Common.CommonEnum;
 using WIDESEA_Common.LocationEnum;
 using WIDESEA_Common.OrderEnum;
@@ -13,10 +19,17 @@
 using WIDESEA_Common.StockEnum;
 using WIDESEA_Common.TaskEnum;
 using WIDESEA_Core;
+using WIDESEA_Core.CodeConfigEnum;
+using WIDESEA_Core.DB;
 using WIDESEA_Core.Helper;
+using WIDESEA_Core.Seed;
 using WIDESEA_DTO.Basic;
 using WIDESEA_DTO.Stock;
+using WIDESEA_IBasicService;
 using WIDESEA_Model.Models;
+using WIDESEA_Model.Models.Basic;
+using WIDESEA_Model.Models.Check;
+using WIDESEA_Model.Models.Outbound;
 
 namespace WIDESEA_TaskInfoService
 {
@@ -27,101 +40,101 @@
         /// </summary>
         /// <param name="inTask"></param>
         /// <returns></returns>
-        public async Task<WebResponseContent> PalletOutboundTask(string endStation, string palletCode = "")
+        public async Task<WebResponseContent> PalletOutboundTask(int num, int locationType)
         {
+            WebResponseContent content = new WebResponseContent();
             try
             {
-                Dt_StockInfo stockInfo;
-                if (string.IsNullOrEmpty(palletCode))
-                {
-                    stockInfo = _stockRepository.Db.Queryable<Dt_StockInfo>().Where(x => x.PalletType == PalletTypeEnum.Empty.ObjToInt() && x.StockStatus == StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt() && !string.IsNullOrWhiteSpace(x.LocationCode)).First();
-                }
-                else
-                {
-                    stockInfo = _stockRepository.Db.Queryable<Dt_StockInfo>().Where(x => x.PalletType == PalletTypeEnum.Empty.ObjToInt() && x.PalletCode == palletCode && x.StockStatus == StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt()).First();
-                }
+                Dictionary<string, SqlSugar.OrderByType> orderByDict = new Dictionary<string, SqlSugar.OrderByType>()
+                {                       
+                     { nameof(Dt_LocationInfo.Layer), SqlSugar.OrderByType.Asc },
+                     { nameof(Dt_LocationInfo.Row), SqlSugar.OrderByType.Asc },
+                     { nameof(Dt_LocationInfo.Column), SqlSugar.OrderByType.Asc },
+                     { nameof(Dt_LocationInfo.Depth), SqlSugar.OrderByType.Desc },
+                };
 
-                if (stockInfo == null)
+                var query = _stockRepository.Db.Queryable<Dt_StockInfo>()
+                    .Where(x => x.PalletType == PalletTypeEnum.Empty.ObjToInt()
+                             && x.StockStatus == StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt())
+                    .WhereIF(locationType != 0, x => x.LocationType == locationType)
+                    .LeftJoin<Dt_LocationInfo>((s, l) => s.LocationCode == l.LocationCode);
+
+                if (query.Count() == 0)
                 {
                     return WebResponseContent.Instance.Error("鏈壘鍒扮┖鎵樼洏搴撳瓨");
                 }
-                Dt_LocationInfo locationInfo = _locationInfoService.Repository.QueryFirst(x => x.LocationCode == stockInfo.LocationCode);
-                if (locationInfo == null)
+
+                bool isFirstOrder = true;
+                foreach (var item in orderByDict)
                 {
-                    return WebResponseContent.Instance.Error("鏈壘鍒扮┖鎵樼洏搴撳瓨瀵瑰簲鐨勮揣浣嶄俊鎭�");
-                }
+                    string fieldName = item.Key.Equals("Column", StringComparison.OrdinalIgnoreCase)
+                        ? $"l.[{item.Key}]"
+                        : $"l.{item.Key}";
 
-                Dt_Task task = new Dt_Task()
-                {
-                    CurrentAddress = stockInfo.LocationCode,
-                    Grade = 0,
-                    NextAddress = endStation,
-                    PalletCode = stockInfo.PalletCode,
-                    Roadway = locationInfo.RoadwayNo,
-                    SourceAddress = stockInfo.LocationCode,
-                    TargetAddress = endStation,
-                    TaskStatus = TaskStatusEnum.New.ObjToInt(),
-                    TaskType = TaskTypeEnum.OutEmpty.ObjToInt(),
-                    WarehouseId = stockInfo.WarehouseId,
-                    PalletType = stockInfo.PalletType
+                    string sortSql = $"{fieldName} {(item.Value == SqlSugar.OrderByType.Asc ? "ASC" : "DESC")}";
 
-                };
-                int beforeStatus = locationInfo.LocationStatus;
-                _unitOfWorkManage.BeginTran();
-                stockInfo.StockStatus = StockStatusEmun.鍑哄簱閿佸畾.ObjToInt();
-                locationInfo.LocationStatus = LocationStatusEnum.Lock.ObjToInt();
-
-                int taskId = BaseDal.AddData(task);
-                task.TaskId = taskId;
-
-                _stockService.StockInfoService.UpdateData(stockInfo);
-
-                _locationInfoService.UpdateData(locationInfo);
-
-                _recordService.LocationStatusChangeRecordSetvice.AddLocationStatusChangeRecord(locationInfo, beforeStatus, StockChangeType.Outbound.ObjToInt(), "", task.TaskNum);
-
-                _unitOfWorkManage.CommitTran();
-
-                TaskModel esstask = new TaskModel()
-                {
-                    taskType = "carry",
-                    taskGroupCode = "",
-                    groupPriority = 0,
-                    tasks = new List<TasksType>
+                    if (isFirstOrder)
                     {
-                            new()
-                            {
-                                taskCode=task.TaskNum.ToString(),
-                                taskPriority=0,
-                                taskDescribe=new TaskDescribeType{
-                                containerCode=stockInfo.PalletCode,
-                                containerType= "CT_KUBOT_STANDARD",
-                                fromLocationCode=stockInfo.LocationCode??"",
-                                toStationCode="",
-                                toLocationCode=endStation,
-                                deadline=0,storageTag=""
-                                }
-                            }
+                        query = query.OrderBy(sortSql);
+                        isFirstOrder = false;
                     }
-                };
-                var result = await _eSSApiService.CreateTaskAsync(esstask);
-
-                _logger.LogInformation("鍒涘缓浠诲姟PalletOutboundTask 杩斿洖:  " + result);
-                if (result)
-                {
-                    return WebResponseContent.Instance.OK(200);
-                }
-                else
-                {
-                    return WebResponseContent.Instance.Error("涓嬪彂鏈哄櫒浜轰换鍔″け璐ワ紒");
+                    else
+                    {
+                        query = query.OrderBy(sortSql);
+                    }
                 }
 
+                var stockInfos = await query.Take(num).ToListAsync();
+
+                foreach (var stockInfo in stockInfos)
+                {
+                    Dt_LocationInfo locationInfo = _locationInfoService.Repository.QueryFirst(x => x.LocationCode == stockInfo.LocationCode);
+                    if (locationInfo == null)
+                    {
+                        return WebResponseContent.Instance.Error("鏈壘鍒扮┖鎵樼洏搴撳瓨瀵瑰簲鐨勮揣浣嶄俊鎭�");
+                    }
+
+                    Dt_Task task = new Dt_Task()
+                    {
+                        CurrentAddress = stockInfo.LocationCode,
+                        Grade = 0,
+                        NextAddress = "1-2",
+                        PalletCode = stockInfo.PalletCode,
+                        Roadway = locationInfo.RoadwayNo,
+                        SourceAddress = stockInfo.LocationCode,
+                        TargetAddress = "1-2",
+                        TaskStatus = TaskStatusEnum.New.ObjToInt(),
+                        TaskType = TaskTypeEnum.OutEmpty.ObjToInt(),
+                        WarehouseId = stockInfo.WarehouseId,
+                        PalletType = stockInfo.PalletType
+
+                    };
+                    int beforeStatus = locationInfo.LocationStatus;
+                    _unitOfWorkManage.BeginTran();
+                    stockInfo.StockStatus = StockStatusEmun.鍑哄簱閿佸畾.ObjToInt();
+                    locationInfo.LocationStatus = LocationStatusEnum.Lock.ObjToInt();
+
+                    int taskId = BaseDal.AddData(task);
+                    task.TaskId = taskId;
+
+                    _stockService.StockInfoService.UpdateData(stockInfo);
+
+                    _locationInfoService.UpdateData(locationInfo);
+
+                    _recordService.LocationStatusChangeRecordSetvice.AddLocationStatusChangeRecord(locationInfo, beforeStatus, StockChangeType.Outbound.ObjToInt(), "", task.TaskNum);
+
+                    _unitOfWorkManage.CommitTran();
+
+                }
+                return content.OK("绌烘墭鍑哄簱鎴愬姛!");
             }
             catch (Exception ex)
             {
                 return WebResponseContent.Instance.Error(ex.Message);
             }
         }
+
+        
 
 
         /// <summary>
@@ -140,64 +153,115 @@
             {
                 throw new Exception("鏈壘鍒板嚭搴撳崟鏄庣粏淇℃伅");
             }
-            if (outboundOrderDetails.FirstOrDefault(x => x.OrderDetailStatus > OrderDetailStatusEnum.New.ObjToInt() && x.OrderDetailStatus != OrderDetailStatusEnum.AssignOverPartial.ObjToInt()) != null)
+            //if (outboundOrderDetails.FirstOrDefault(x => x.OrderDetailStatus > OrderDetailStatusEnum.New.ObjToInt() && x.OrderDetailStatus != OrderDetailStatusEnum.AssignOverPartial.ObjToInt()) != null)
+            //{
+            //    throw new Exception("鎵�閫夊嚭搴撳崟鏄庣粏瀛樺湪鍑哄簱涓垨宸插畬鎴�");
+            //}
+
+            if (outboundOrderDetails.FirstOrDefault(x => x.OrderDetailStatus > OrderDetailStatusEnum.Outbound.ObjToInt() && x.OrderDetailStatus != OrderDetailStatusEnum.AssignOverPartial.ObjToInt()) != null)
             {
-                throw new Exception("鎵�閫夊嚭搴撳崟鏄庣粏瀛樺湪鍑哄簱涓垨宸插畬鎴�");
+                throw new Exception("鎵�閫夊嚭搴撳崟鏄庣粏瀛樺湪宸插畬鎴愮姸鎬侊紝鏃犳硶閲嶆柊鍒嗛厤");
             }
+
             List<Dt_StockInfo>? stockInfos = null;
             List<Dt_OutboundOrderDetail>? orderDetails = null;
             List<Dt_OutStockLockInfo>? outStockLockInfos = null;
             List<Dt_LocationInfo>? locationInfos = null;
-            //if (outboundOrderDetail.OrderDetailStatus == OrderDetailStatusEnum.New.ObjToInt())
+
+            CleanupPreviousInvalidLocks(outboundOrderDetails);
+            // 寮�鍚簨鍔★紝浣跨敤鏁版嵁搴撹绾ч攣
+            using (var transaction = _outboundOrderDetailService.Db.Ado.UseTran())
             {
-                (List<Dt_StockInfo>, List<Dt_OutboundOrderDetail>, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>) result = _outboundOrderDetailService.AssignStockOutbound(outboundOrderDetails);
-                if (result.Item1 != null && result.Item1.Count > 0)
+                try
                 {
-                    Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == outboundOrderDetails.FirstOrDefault().OrderId);
-                    TaskTypeEnum typeEnum = outboundOrder.OrderType switch
+                    // 浣跨敤鎮茶閿侀攣瀹氳鍗曟槑缁�
+                    var lockedOrderDetails = new List<Dt_OutboundOrderDetail>();
+                    foreach (var key in keys)
                     {
-                        (int)OutOrderTypeEnum.Issue => TaskTypeEnum.Outbound,
-                        (int)OutOrderTypeEnum.Allocate => TaskTypeEnum.OutAllocate,
-                        (int)OutOrderTypeEnum.Quality => TaskTypeEnum.OutQuality,
-                        _ => new TaskTypeEnum()
-                    };
-                    tasks = GetTasks(result.Item1, typeEnum, outStation);
-                    tasks.ForEach(x =>
-                    {
-                        x.OrderNo = outboundOrder.OrderNo;
-                    });
-                    result.Item2.ForEach(x =>
-                    {
-                        x.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt();
-                    });
-                    result.Item3.ForEach(x =>
-                    {
-                        x.Status = OutLockStockStatusEnum.鍑哄簱涓�.ObjToInt();
-                    });
+                        var detail = _outboundOrderDetailService.Db.Ado.SqlQuerySingle<Dt_OutboundOrderDetail>(
+                            "SELECT * FROM Dt_OutboundOrderDetail WITH (UPDLOCK) WHERE Id = @Id",
+                            new { Id = key });
 
-                    stockInfos = result.Item1;
-                    orderDetails = result.Item2;
-                    outStockLockInfos = result.Item3;
-                    locationInfos = result.Item4;
+                        if (detail != null)
+                        {
+                            lockedOrderDetails.Add(detail);
+                        }
+                    }
+
+                    if (!lockedOrderDetails.Any())
+                    {
+                        throw new Exception("鏈壘鍒板嚭搴撳崟鏄庣粏淇℃伅");
+                    }
+                    (List<Dt_StockInfo>, List<Dt_OutboundOrderDetail>, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>) result = _outboundOrderDetailService.AssignStockOutbound(outboundOrderDetails);
+                    if (result.Item1 != null && result.Item1.Count > 0)
+                    {
+                        Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == outboundOrderDetails.FirstOrDefault().OrderId);
+                        TaskTypeEnum typeEnum = outboundOrder.OrderType switch
+                        {
+                            (int)OutOrderTypeEnum.Issue => TaskTypeEnum.Outbound,
+                            (int)OutOrderTypeEnum.Allocate => TaskTypeEnum.OutAllocate,
+                            (int)OutOrderTypeEnum.Quality => TaskTypeEnum.OutQuality,
+                            _ => TaskTypeEnum.Outbound
+                        };
+                        tasks = GetTasks(result.Item1, typeEnum, outStation);
+                        tasks.ForEach(x =>
+                        {
+                            x.OrderNo = outboundOrder.OrderNo;
+                        });
+                        result.Item2.ForEach(x =>
+                        {
+                            x.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt();
+                        });
+                        result.Item3.ForEach(x =>
+                        {
+                            x.Status = OutLockStockStatusEnum.鍑哄簱涓�.ObjToInt();
+                        });
+
+                        stockInfos = result.Item1;
+                        orderDetails = result.Item2;
+                        outStockLockInfos = result.Item3;
+                        locationInfos = result.Item4;
+                        transaction.CommitTran();
+                    }
+                    else
+                    {
+                        transaction.RollbackTran();
+                        throw new Exception("鏃犲簱瀛�");
+                    }
                 }
-                else
+                catch (Exception)
                 {
-                    throw new Exception("鏃犲簱瀛�");
+                    transaction.RollbackTran();
+                    throw;
                 }
+                return (tasks, stockInfos, orderDetails, outStockLockInfos, locationInfos);
             }
-            //else
-            //{
-            //    List<Dt_OutStockLockInfo> stockLockInfos = _outboundService.OutboundStockLockInfoService.GetByOrderDetailId(outboundOrderDetail.OrderId, OutLockStockStatusEnum.宸插垎閰�);
-            //    if (stockLockInfos != null && stockLockInfos.Count > 0)
-            //    {
-            //        List<Dt_StockInfo> stocks = _stockService.StockInfoService.Repository.GetStockInfosByPalletCodes(stockLockInfos.Select(x => x.PalletCode).Distinct().ToList());
-            //        tasks = GetTasks(stocks);
-            //    }
-            //}
-
-            return (tasks, stockInfos, orderDetails, outStockLockInfos, locationInfos);
         }
+        /// <summary>
+        /// 娓呯悊涔嬪墠鐨勬棤鏁堥攣瀹氳褰�
+        /// </summary>
+        private void CleanupPreviousInvalidLocks(List<Dt_OutboundOrderDetail> orderDetails)
+        {
+            var orderIds = orderDetails.Select(x => x.OrderId).Distinct().ToList();
+            var orderNos = _outboundOrderService.Db.Queryable<Dt_OutboundOrder>()
+                .Where(x => orderIds.Contains(x.Id))
+                .Select(x => x.OrderNo)
+                .ToList();
 
+            // 娓呯悊鐘舵�佷负"宸查噴鏀�"鎴�"鍥炲簱涓�"鐨勬棫閿佸畾璁板綍
+            foreach (var orderNo in orderNos)
+            {
+                _outStockLockInfoService.Db.Updateable<Dt_OutStockLockInfo>()
+                     .SetColumns(x => new Dt_OutStockLockInfo
+                     {
+                         Status = (int)OutLockStockStatusEnum.宸查噴鏀�
+                     })
+                     .Where(x => x.OrderNo == orderNo &&
+                                (x.Status == (int)OutLockStockStatusEnum.鍥炲簱涓� ||
+                                 x.Status == (int)OutLockStockStatusEnum.宸查噴鏀�))
+                     .ExecuteCommand();
+            }
+        }
 
         /// <summary>
         /// 鐢熸垚鍑哄簱浠诲姟鍚庢暟鎹洿鏂板埌鏁版嵁搴�
@@ -209,6 +273,481 @@
         /// <param name="locationInfos"></param>
         /// <returns></returns>
         public async Task<WebResponseContent> GenerateOutboundTaskDataUpdateAsync(List<Dt_Task> tasks, List<Dt_StockInfo>? stockInfos = null, List<Dt_OutboundOrderDetail>? outboundOrderDetails = null, List<Dt_OutStockLockInfo>? outStockLockInfos = null, List<Dt_LocationInfo>? locationInfos = null)
+        {
+            try
+            {
+                _unitOfWorkManage.BeginTran();
+
+                BaseDal.AddData(tasks);
+                if (stockInfos != null && stockInfos.Count > 0 && outboundOrderDetails != null && outboundOrderDetails.Count > 0 && outStockLockInfos != null && outStockLockInfos.Count > 0 && locationInfos != null && locationInfos.Count > 0)
+                {
+                    stockInfos.ForEach(x =>
+                    {
+                        x.StockStatus = StockStatusEmun.鍑哄簱閿佸畾.ObjToInt();
+                    });
+                    outboundOrderDetails.ForEach(x =>
+                    {
+                        x.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt();
+                    });
+                    Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == outboundOrderDetails.FirstOrDefault().OrderId);
+                    if (outboundOrder.OrderStatus != OutOrderStatusEnum.鍑哄簱涓�.ObjToInt())
+                    {
+                        _outboundOrderService.Repository.UpdateData(outboundOrder);
+                    }
+                    else
+                    {
+                        outboundOrder.OrderStatus = OutOrderStatusEnum.鍑哄簱涓�.ObjToInt();
+                    }
+                    outboundOrder.Operator = App.User.UserName;
+                    _outboundOrderService.Repository.UpdateData(outboundOrder);
+                    WebResponseContent content = _outboundOrderDetailService.LockOutboundStockDataUpdate(stockInfos, outboundOrderDetails, outStockLockInfos, locationInfos, tasks: tasks);
+
+                    if (!content.Status)
+                    {
+                        _unitOfWorkManage.RollbackTran();
+                        return content;
+                    }
+                }
+                else if (outboundOrderDetails != null && outboundOrderDetails.Count > 0)
+                {
+                    outboundOrderDetails.ForEach(x =>
+                    {
+                        x.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt();
+                    });
+                    Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == outboundOrderDetails.FirstOrDefault().OrderId);
+                    if (outboundOrder.OrderStatus != OutOrderStatusEnum.鍑哄簱涓�.ObjToInt())
+                    {
+                        _outboundOrderService.Repository.UpdateData(outboundOrder);
+                    }
+                    else
+                    {
+                        outboundOrder.OrderStatus = OutOrderStatusEnum.鍑哄簱涓�.ObjToInt();
+                    }
+                    outboundOrder.Operator = App.User.UserName;
+                    _outboundOrderService.Repository.UpdateData(outboundOrder);
+                    _outboundOrderDetailService.Repository.UpdateData(outboundOrderDetails);
+                }
+                _unitOfWorkManage.CommitTran();
+                return WebResponseContent.Instance.OK();
+                //TaskModel esstask = new TaskModel()
+                //{
+                //    taskType = "carry",
+                //    taskGroupCode = "",
+                //    groupPriority = 0,
+                //    tasks = new List<TasksType>()
+                //};
+
+                //foreach (var task in tasks)
+                //{
+                //    esstask.
+                //       tasks.Add(new TasksType
+                //       {
+                //           taskCode = task.TaskNum.ToString(),
+                //           taskPriority = 0,
+                //           taskDescribe = new TaskDescribeType
+                //           {
+                //               containerCode = task.PalletCode,
+                //               containerType = "CT_KUBOT_STANDARD",
+                //               fromLocationCode = task.SourceAddress ?? "",
+                //               toStationCode = "",
+                //               toLocationCode = task.TargetAddress,
+                //               deadline = 0,
+                //               storageTag = ""
+                //           }
+                //       }
+                //   );
+                //}
+                //var result = await _eSSApiService.CreateTaskAsync(esstask);
+
+                //_logger.LogInformation("鍒涘缓浠诲姟PalletOutboundTask 杩斿洖:  " + result);
+                //if (result)
+                //{
+                //    return WebResponseContent.Instance.OK();
+                //}
+                //else
+                //{
+                //    return WebResponseContent.Instance.Error("涓嬪彂鏈哄櫒浜轰换鍔″け璐ワ紒");
+                //}
+            }
+            catch (Exception ex)
+            {
+                _unitOfWorkManage.RollbackTran();
+                return WebResponseContent.Instance.Error(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 搴撳瓨鏁版嵁杞嚭搴撲换鍔�
+        /// </summary>
+        /// <param name="stockInfos"></param>
+        /// <returns></returns>
+        public List<Dt_Task> GetTasks(List<Dt_StockInfo> stockInfos, TaskTypeEnum taskType, string outStation)
+        {
+            List<Dt_Task> tasks = new List<Dt_Task>();
+            List<Dt_LocationInfo> locationInfos = _locationInfoService.Repository.QueryData(x => stockInfos.Select(x => x.LocationCode).Contains(x.LocationCode));
+            for (int i = 0; i < stockInfos.Count; i++)
+            {
+                Dt_StockInfo stockInfo = stockInfos[i];
+
+                if (stockInfo != null)
+                {
+                    Dt_LocationInfo? locationInfo = locationInfos.FirstOrDefault(x => x.LocationCode == stockInfo.LocationCode);
+                    if (!tasks.Exists(x => x.PalletCode == stockInfo.PalletCode))
+                    {
+                        Dt_Task task = new()
+                        {
+                            CurrentAddress = stockInfo.LocationCode,
+                            Grade = 0,
+                            PalletCode = stockInfo.PalletCode,
+                            NextAddress = "",
+                            Roadway = locationInfo.RoadwayNo,
+                            SourceAddress = stockInfo.LocationCode,
+                            TargetAddress = outStation,
+                            TaskStatus = TaskStatusEnum.New.ObjToInt(),
+                            TaskType = taskType.ObjToInt(),
+                            //TaskNum = BaseDal.GetTaskNum(nameof(SequenceEnum.SeqTaskNum)),
+                            PalletType = stockInfo.PalletType,
+                            WarehouseId = stockInfo.WarehouseId,
+                        };
+                        //if (taskType != TaskTypeEnum.OutEmpty)
+                        //{
+                        //    task.MaterielCode = stockInfo.Details?.Where(x => x.StockId == stockInfo.Id).FirstOrDefault()?.MaterielCode;
+                        //    task.Quantity = (float)stockInfo.Details?.Where(x => x.StockId == stockInfo.Id).Sum(x => x.StockQuantity);
+                        //    task.BatchNo = stockInfo.Details?.Where(x => x.StockId == stockInfo.Id).FirstOrDefault()?.BatchNo;
+                        //}
+
+                        tasks.Add(task);
+                    }
+                }
+            }
+            return tasks;
+        }
+
+
+
+        #region 鍐呭瓨閿佺鐞嗗櫒
+        private static readonly ConcurrentDictionary<string, SemaphoreSlim> _normalmaterialLocks =
+            new ConcurrentDictionary<string, SemaphoreSlim>();
+        private static readonly ConcurrentDictionary<string, DateTime> _normallockLastUsed =
+            new ConcurrentDictionary<string, DateTime>();
+        private static readonly object _normalcleanupLock = new object();
+        private static DateTime _normallastCleanupTime = DateTime.MinValue;
+
+        /// <summary>
+        /// 鑾峰彇鐗╂枡绾у唴瀛橀攣
+        /// </summary>
+        private SemaphoreSlim GetNormalMaterialSemaphore(string materialCode, string batchNo, string supplyCode)
+        {
+            // 鍒涘缓閿侀敭锛氱墿鏂�+鎵规+渚涘簲鍟�
+            string lockKey = $"MaterialLock_{materialCode}_{batchNo}_{supplyCode}";
+
+            // 娓呯悊闀挎椂闂翠笉鐢ㄧ殑閿侊紙姣忓皬鏃舵竻鐞嗕竴娆★級
+            var now = DateTime.Now;
+            if ((now - _normallastCleanupTime).TotalHours >= 1)
+            {
+                lock (_normalcleanupLock)
+                {
+                    if ((now - _normallastCleanupTime).TotalHours >= 1)
+                    {
+                        var keysToRemove = _normallockLastUsed
+                            .Where(kvp => (now - kvp.Value).TotalHours > 2)
+                            .Select(kvp => kvp.Key)
+                            .ToList();
+
+                        foreach (var key in keysToRemove)
+                        {
+                            if (_normalmaterialLocks.TryRemove(key, out var _semaphore))
+                            {
+                                _semaphore.Dispose();
+                            }
+                            _normallockLastUsed.TryRemove(key, out _);
+                        }
+
+                        _normallastCleanupTime = now;
+                    }
+                }
+            }
+
+            // 鑾峰彇鎴栧垱寤轰俊鍙烽噺
+            var semaphore = _normalmaterialLocks.GetOrAdd(lockKey, _ => new SemaphoreSlim(1, 1));
+            _normallockLastUsed[lockKey] = now;
+
+            return semaphore;
+        }
+
+        /// <summary>
+        /// 鏇存柊鍐呭瓨閿佹渶鍚庝娇鐢ㄦ椂闂�
+        /// </summary>
+        private void UpdateNormalMaterialLockUsedTime(string materialCode, string batchNo, string supplyCode)
+        {
+            string lockKey = $"MaterialLock_{materialCode}_{batchNo}_{supplyCode}";
+            _normallockLastUsed[lockKey] = DateTime.Now;
+        }
+        #endregion
+        /// <summary>
+        /// 鐢熸垚鍑哄簱浠诲姟
+        /// </summary>
+        /// <param name="keys">鍑哄簱鍗曟槑缁嗕富閿�</param>
+        /// <returns></returns>
+        public async Task<WebResponseContent> GenerateOutboundTasksAsync(int[] keys, string outStation)
+        {
+            try
+            {
+                List<Dt_Task> tasks = new List<Dt_Task>();
+                List<StockSelectViewDTO> stockSelectViews = new List<StockSelectViewDTO>();
+                List<Dt_StockInfo> stockInfos = new List<Dt_StockInfo>();
+                List<Dt_OutboundOrderDetail> outboundOrderDetails = new List<Dt_OutboundOrderDetail>();
+                List<Dt_OutStockLockInfo> outStockLockInfos = new List<Dt_OutStockLockInfo>();
+                List<Dt_LocationInfo> locationInfos = new List<Dt_LocationInfo>();
+                // 鍏堣幏鍙栨墍鏈夎鍗曟槑缁嗭紝纭畾闇�瑕侀攣瀹氱殑鐗╂枡
+                var orderDetails = _outboundOrderDetailService.Repository.QueryData(x => keys.Contains(x.Id));
+                if (orderDetails == null || orderDetails.Count == 0)
+                {
+                    return WebResponseContent.Instance.Error("鏈壘鍒板嚭搴撳崟鏄庣粏淇℃伅");
+                }
+
+                // 鑾峰彇鎵�鏈夐渶瑕侀攣瀹氱殑鐗╂枡鍒嗙粍
+                var materialGroups = orderDetails
+                    .GroupBy(x => new { x.MaterielCode, x.BatchNo, x.SupplyCode })
+                    .Select(g => new
+                    {
+                        g.Key.MaterielCode,
+                        g.Key.BatchNo,
+                        g.Key.SupplyCode,
+                        Count = g.Count()
+                    })
+                    .ToList();
+
+                // 鎸夐『搴忚幏鍙栨墍鏈夌墿鏂欑殑鍐呭瓨閿侊紙鎸夌墿鏂欎唬鐮佹帓搴忎互閬垮厤姝婚攣锛�
+                var semaphores = new List<SemaphoreSlim>();
+                var acquiredLocks = new List<(string MaterialCode, string BatchNo, string SupplyCode)>();
+
+                try
+                {
+                    foreach (var group in materialGroups.OrderBy(g => g.MaterielCode).ThenBy(g => g.BatchNo))
+                    {
+                        var semaphore = GetMaterialSemaphore(group.MaterielCode, group.BatchNo, group.SupplyCode);
+
+                        // 绛夊緟鑾峰彇閿侊紝鏈�澶氱瓑寰�30绉�
+                        bool lockAcquired = await semaphore.WaitAsync(TimeSpan.FromSeconds(30));
+
+                        if (!lockAcquired)
+                        {
+                            // 濡傛灉鑾峰彇閿佸け璐ワ紝閲婃斁宸茶幏鍙栫殑鎵�鏈夐攣
+                            foreach (var acquiredSemaphore in semaphores)
+                            {
+                                acquiredSemaphore.Release();
+                            }
+                            return WebResponseContent.Instance.Error($"鐗╂枡[{group.MaterielCode}]鎵规[{group.BatchNo}]鍒嗛厤绻佸繖锛岃绋嶅悗閲嶈瘯");
+                        }
+
+                        semaphores.Add(semaphore);
+                        acquiredLocks.Add((group.MaterielCode, group.BatchNo, group.SupplyCode));
+                    }
+
+                    (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) result = OutboundTaskDataHandle(keys, outStation);
+                    if (result.Item2 != null && result.Item2.Count > 0)
+                    {
+                        stockInfos.AddRange(result.Item2);
+                    }
+                    if (result.Item3 != null && result.Item3.Count > 0)
+                    {
+                        outboundOrderDetails.AddRange(result.Item3);
+                    }
+                    if (result.Item4 != null && result.Item4.Count > 0)
+                    {
+                        outStockLockInfos.AddRange(result.Item4);
+                    }
+                    if (result.Item5 != null && result.Item5.Count > 0)
+                    {
+                        locationInfos.AddRange(result.Item5);
+                    }
+                    if (result.Item1 != null && result.Item1.Count > 0)
+                    {
+                        tasks.AddRange(result.Item1);
+                    }
+
+                    WebResponseContent content = await GenerateOutboundTaskDataUpdateAsync(tasks, stockInfos, outboundOrderDetails, outStockLockInfos, locationInfos);
+                    return content;
+                }
+                finally
+                {
+                    // 閲婃斁鎵�鏈夊唴瀛橀攣骞舵洿鏂颁娇鐢ㄦ椂闂�
+                    foreach (var semaphore in semaphores)
+                    {
+                        semaphore.Release();
+                    }
+
+                    foreach (var lockInfo in acquiredLocks)
+                    {
+                        UpdateMaterialLockUsedTime(lockInfo.MaterialCode, lockInfo.BatchNo, lockInfo.SupplyCode);
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                _unitOfWorkManage.RollbackTran();
+                return WebResponseContent.Instance.Error(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 鏅轰粨璋冩櫤浠�
+        /// </summary>
+        /// <param name="orderDetailId"></param>
+        /// <param name="stockSelectViews"></param>
+        /// <param name="station"></param>
+        /// <returns></returns>
+        public async Task<WebResponseContent> GenerateAllocatOutboundTask(int orderDetailId, List<StockSelectViewDTO> stockSelectViews, string station = null)
+        {
+            try
+            {
+                //var allocorder = _allocateOrderRepository.Db.Queryable<Dt_AllocateOrder>().Includes(x => x.Details).Where(x => x.Details.Any(o => o.Id == orderDetailId)).First();
+                ////var allocorder = _allocateOrderDetailRepository.Db.Queryable<Dt_AllocateOrderDetail>()
+                ////               .LeftJoin<Dt_AllocateOrder>((detail, order) => detail.OrderId == order.Id)
+                ////               .Where((detail, order) => order.Id == orderDetailId)
+                ////               .Select((detail, order) => order)
+                ////               .First();
+                //if (allocorder == null)
+                //{
+                //    return WebResponseContent.Instance.Error("鎵句笉鍒板崟鎹�");
+                //}
+
+                var outboundOrder = SqlSugarHelper.DbWMS.Queryable<Dt_OutboundOrder>().Includes(x => x.Details).Where(x => x.Details.Any(o => o.Id == orderDetailId)).First();
+ 
+                if (outboundOrder == null)
+                {
+                    return WebResponseContent.Instance.Error("鎵句笉鍒板嚭搴撳崟鎹�");
+                }
+                if (outboundOrder.Details == null || !outboundOrder.Details.Any()) 
+                {
+                    return WebResponseContent.Instance.Error("鎵句笉鍒板嚭搴撴槑缁嗗崟鎹�");
+                }
+                //var orderdetail = outboundOrder.Details.Where(outItem => allocorder.Details.Any(allocItem => allocItem.MaterielCode == outItem.MaterielCode && allocItem.LineNo == outItem.lineNo
+                // && allocItem.BarcodeQty == outItem.BarcodeQty && allocItem.WarehouseCode == outItem.WarehouseCode && allocItem.BarcodeUnit == outItem.BarcodeUnit)).First();
+                //if (orderdetail == null)
+                //{
+                //    return WebResponseContent.Instance.Error("鎵句笉鍒板嚭搴撴槑缁嗗崟鎹�");
+                //}
+
+                (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) result = OutboundTaskDataHandle(outboundOrder.Details.First().Id, stockSelectViews, station);
+
+                WebResponseContent content = await GenerateOutboundTaskDataUpdate(result.Item1, result.Item2, result.Item3, result.Item4, result.Item5);
+
+                return content;
+            }
+            catch (Exception ex)
+            {
+                return WebResponseContent.Instance.Error(ex.Message);
+            }
+        }
+        /// <summary>
+        /// 鐢熸垚鍑哄簱浠诲姟
+        /// </summary>
+        /// <param name="orderDetailId"></param>
+        /// <param name="stockSelectViews"></param>
+        /// <returns></returns>
+        public async Task<WebResponseContent> GenerateOutboundTask(int orderDetailId, List<StockSelectViewDTO> stockSelectViews, string station = null)
+        {
+            try
+            {
+                var orderNo = _reCheckOrderRepository.Db.Queryable<Dt_ReCheckOrder>().First(x => x.Id == orderDetailId)?.OrderNo;
+                var outboundOrder = _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().Includes(x => x.Details).First(x => x.UpperOrderNo == orderNo);
+                if (outboundOrder == null)
+                {
+                    return WebResponseContent.Instance.Error("鎵句笉鍒板崟鎹�");
+                }
+                (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) result = OutboundTaskDataHandle(outboundOrder.Details.First().Id, stockSelectViews, station);
+
+                WebResponseContent content = await GenerateOutboundTaskDataUpdate(result.Item1, result.Item2, result.Item3, result.Item4, result.Item5);
+
+                return content;
+            }
+            catch (Exception ex)
+            {
+                return WebResponseContent.Instance.Error(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 鍑哄簱浠诲姟鏁版嵁澶勭悊
+        /// </summary>
+        /// <param name="orderDetailId"></param>
+        /// <param name="stockSelectViews"></param>
+        /// <returns></returns>
+        /// <exception cref="Exception"></exception>
+        public (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) OutboundTaskDataHandle(int orderDetailId, List<StockSelectViewDTO> stockSelectViews, string station = null)
+        {
+            List<Dt_Task> tasks = new List<Dt_Task>();
+            Dt_OutboundOrderDetail outboundOrderDetail = _outboundOrderDetailService.Repository.QueryFirst(x => x.Id == orderDetailId);
+
+            if (outboundOrderDetail == null)
+            {
+                throw new Exception("鏈壘鍒板嚭搴撳崟鏄庣粏淇℃伅");
+            }
+
+            //if (stockSelectViews.Sum(x => x.UseableQuantity) > outboundOrderDetail.OrderQuantity - outboundOrderDetail.LockQuantity)
+            //{
+            //    throw new Exception("閫夋嫨鏁伴噺瓒呭嚭鍗曟嵁鏁伴噺");
+            //}
+            List<Dt_StockInfo>? stockInfos = null;
+            Dt_OutboundOrderDetail? orderDetail = null;
+            List<Dt_OutStockLockInfo>? outStockLockInfos = null;
+            List<Dt_LocationInfo>? locationInfos = null;
+            if (outboundOrderDetail.OrderDetailStatus == OrderDetailStatusEnum.New.ObjToInt() ||
+                outboundOrderDetail.OrderDetailStatus == OrderDetailStatusEnum.Outbound.ObjToInt())
+            {
+                (List<Dt_StockInfo>, Dt_OutboundOrderDetail, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>) result = _outboundOrderDetailService.AssignStockOutbound(outboundOrderDetail, stockSelectViews);
+                if (result.Item1 != null && result.Item1.Count > 0)
+                {
+                    Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == outboundOrderDetail.OrderId);
+                    TaskTypeEnum typeEnum = outboundOrder.OrderType switch
+                    {
+                        (int)OutOrderTypeEnum.Issue => TaskTypeEnum.Outbound,
+                        (int)OutOrderTypeEnum.Allocate => TaskTypeEnum.OutAllocate,
+                        (int)OutOrderTypeEnum.Quality => TaskTypeEnum.OutQuality,
+                        _ => TaskTypeEnum.Outbound
+                    };
+                    tasks = GetTasks(result.Item1, typeEnum, station);
+                    result.Item2.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt();
+                    result.Item3.ForEach(x =>
+                    {
+                        x.Status = OutLockStockStatusEnum.鍑哄簱涓�.ObjToInt();
+                    });
+
+                    stockInfos = result.Item1;
+                    orderDetail = result.Item2;
+                    outStockLockInfos = result.Item3;
+                    locationInfos = result.Item4;
+                }
+                else
+                {
+                    throw new Exception("鏃犲簱瀛�");
+                }
+            }
+            else
+            {
+                List<Dt_OutStockLockInfo> stockLockInfos = _outStockLockInfoService.GetByOrderDetailId(outboundOrderDetail.OrderId, OutLockStockStatusEnum.宸插垎閰�);
+                if (stockLockInfos != null && stockLockInfos.Count > 0)
+                {
+                    List<Dt_StockInfo> stocks = _stockService.StockInfoService.GetStockInfosByPalletCodes(stockLockInfos.Select(x => x.PalletCode).Distinct().ToList());
+                    tasks = GetTasks(stocks, TaskTypeEnum.Outbound,station);
+                }
+            }
+
+            return (tasks, stockInfos, orderDetail == null ? null : new List<Dt_OutboundOrderDetail> { orderDetail }, outStockLockInfos, locationInfos);
+        }
+
+        /// <summary>
+        /// 鐢熸垚鍑哄簱浠诲姟鍚庢暟鎹洿鏂板埌鏁版嵁搴�
+        /// </summary>
+        /// <param name="tasks"></param>
+        /// <param name="stockInfos"></param>
+        /// <param name="outboundOrderDetails"></param>
+        /// <param name="outStockLockInfos"></param>
+        /// <param name="locationInfos"></param>
+        /// <returns></returns>
+        public async Task<WebResponseContent> GenerateOutboundTaskDataUpdate(List<Dt_Task> tasks, List<Dt_StockInfo>? stockInfos = null, List<Dt_OutboundOrderDetail>? outboundOrderDetails = null, List<Dt_OutStockLockInfo>? outStockLockInfos = null, List<Dt_LocationInfo>? locationInfos = null)
         {
             try
             {
@@ -252,150 +791,799 @@
                     _outboundOrderDetailService.Repository.UpdateData(outboundOrderDetails);
                 }
                 _unitOfWorkManage.CommitTran();
-                TaskModel esstask = new TaskModel()
-                {
-                    taskType = "carry",
-                    taskGroupCode = "",
-                    groupPriority = 0,
-                    tasks = new List<TasksType>()                   
-                };
+                return WebResponseContent.Instance.OK();
+                //PushTasksToWCS(tasks);
+                //TaskModel esstask = new TaskModel()
+                //{
+                //    taskType = "carry",
+                //    taskGroupCode = "",
+                //    groupPriority = 0,
+                //    tasks = new List<TasksType>()
+                //};
 
-                foreach (var task in tasks)
-                {
-                    esstask.
-                       tasks.Add(new TasksType
-                       {
-                           taskCode = task.TaskNum.ToString(),
-                           taskPriority = 0,
-                           taskDescribe = new TaskDescribeType
-                           {
-                               containerCode = task.PalletCode,
-                               containerType = "CT_KUBOT_STANDARD",
-                               fromLocationCode = task.SourceAddress ?? "",
-                               toStationCode = "",
-                               toLocationCode = task.TargetAddress,
-                               deadline = 0,
-                               storageTag = ""
-                           }
-                       }
-                   );
-                }
-                var result = await _eSSApiService.CreateTaskAsync(esstask);
+                //foreach (var task in tasks)
+                //{
+                //    esstask.
+                //       tasks.Add(new TasksType
+                //       {
+                //           taskCode = task.TaskNum.ToString(),
+                //           taskPriority = 0,
+                //           taskDescribe = new TaskDescribeType
+                //           {
+                //               containerCode = task.PalletCode,
+                //               containerType = "CT_KUBOT_STANDARD",
+                //               fromLocationCode = task.SourceAddress ?? "",
+                //               toStationCode = "",
+                //               toLocationCode = task.TargetAddress,
+                //               deadline = 0,
+                //               storageTag = ""
+                //           }
+                //       }
+                //   );
+                //}
+                //var result = await _eSSApiService.CreateTaskAsync(esstask);
 
-                _logger.LogInformation("鍒涘缓浠诲姟PalletOutboundTask 杩斿洖:  " + result);
-                if (result)
-                {
-                    return WebResponseContent.Instance.OK();
-                }
-                else
-                {
-                    return WebResponseContent.Instance.Error("涓嬪彂鏈哄櫒浜轰换鍔″け璐ワ紒");
-                }                               
+                //_logger.LogInformation("鍒涘缓浠诲姟PalletOutboundTask 杩斿洖:  " + result);
+                //if (result)
+                //{
+                //    return WebResponseContent.Instance.OK();
+                //}
+                //else
+                //{
+                //    return WebResponseContent.Instance.Error("涓嬪彂鏈哄櫒浜轰换鍔″け璐ワ紒");
+                //}
+
             }
             catch (Exception ex)
             {
                 _unitOfWorkManage.RollbackTran();
                 return WebResponseContent.Instance.Error(ex.Message);
             }
+
         }
 
+        
+        #region 鍒嗘壒鍒嗛厤搴撳瓨
+        #region 鍐呭瓨閿佺鐞嗗櫒
+        private static readonly ConcurrentDictionary<string, SemaphoreSlim> _materialLocks =
+            new ConcurrentDictionary<string, SemaphoreSlim>();
+        private static readonly ConcurrentDictionary<string, DateTime> _lockLastUsed =
+            new ConcurrentDictionary<string, DateTime>();
+        private static readonly object _cleanupLock = new object();
+        private static DateTime _lastCleanupTime = DateTime.MinValue;
+
         /// <summary>
-        /// 搴撳瓨鏁版嵁杞嚭搴撲换鍔�
+        /// 鑾峰彇鐗╂枡绾у唴瀛橀攣
         /// </summary>
-        /// <param name="stockInfos"></param>
-        /// <returns></returns>
-        public List<Dt_Task> GetTasks(List<Dt_StockInfo> stockInfos, TaskTypeEnum taskType, string outStation)
+        private SemaphoreSlim GetMaterialSemaphore(string materialCode, string batchNo, string supplyCode)
         {
-            List<Dt_Task> tasks = new List<Dt_Task>();
-            List<Dt_LocationInfo> locationInfos = _locationInfoService.Repository.QueryData(x => stockInfos.Select(x => x.LocationCode).Contains(x.LocationCode));
-            for (int i = 0; i < stockInfos.Count; i++)
+            // 鍒涘缓閿侀敭锛氱墿鏂�+鎵规+渚涘簲鍟�
+            string lockKey = $"MaterialLock_{materialCode}_{batchNo}_{supplyCode}";
+
+            // 娓呯悊闀挎椂闂翠笉鐢ㄧ殑閿侊紙姣忓皬鏃舵竻鐞嗕竴娆★級
+            var now = DateTime.Now;
+            if ((now - _lastCleanupTime).TotalHours >= 1)
             {
-                Dt_StockInfo stockInfo = stockInfos[i];
-
-                if (stockInfo != null)
+                lock (_cleanupLock)
                 {
-                    Dt_LocationInfo? locationInfo = locationInfos.FirstOrDefault(x => x.LocationCode == stockInfo.LocationCode);
-                    if (!tasks.Exists(x => x.PalletCode == stockInfo.PalletCode))
+                    if ((now - _lastCleanupTime).TotalHours >= 1)
                     {
-                        Dt_Task task = new()
-                        {
-                            CurrentAddress = stockInfo.LocationCode,
-                            Grade = 0,
-                            PalletCode = stockInfo.PalletCode,
-                            NextAddress = "",
-                            Roadway = locationInfo.RoadwayNo,
-                            SourceAddress = stockInfo.LocationCode,
-                            TargetAddress = outStation,
-                            TaskStatus = TaskStatusEnum.New.ObjToInt(),
-                            TaskType = taskType.ObjToInt(),
-                            // TaskNum = BaseDal.GetTaskNum(nameof(SequenceEnum.SeqTaskNum)),
-                            PalletType = stockInfo.PalletType,
-                            WarehouseId = stockInfo.WarehouseId,
-                            
-                        };
-                        //if (taskType != TaskTypeEnum.OutEmpty)
-                        //{
-                        //    task.MaterielCode = stockInfo.Details?.Where(x => x.StockId == stockInfo.Id).FirstOrDefault()?.MaterielCode;
-                        //    task.Quantity = (float)stockInfo.Details?.Where(x => x.StockId == stockInfo.Id).Sum(x => x.StockQuantity);
-                        //    task.BatchNo = stockInfo.Details?.Where(x => x.StockId == stockInfo.Id).FirstOrDefault()?.BatchNo;
-                        //}
+                        var keysToRemove = _lockLastUsed
+                            .Where(kvp => (now - kvp.Value).TotalHours > 2)
+                            .Select(kvp => kvp.Key)
+                            .ToList();
 
-                        tasks.Add(task);
+                        foreach (var key in keysToRemove)
+                        {
+                            if (_materialLocks.TryRemove(key, out var _semaphore))
+                            {
+                                _semaphore.Dispose();
+                            }
+                            _lockLastUsed.TryRemove(key, out _);
+                        }
+
+                        _lastCleanupTime = now;
                     }
                 }
             }
-            return tasks;
+
+            // 鑾峰彇鎴栧垱寤轰俊鍙烽噺
+            var semaphore = _materialLocks.GetOrAdd(lockKey, _ => new SemaphoreSlim(1, 1));
+            _lockLastUsed[lockKey] = now;
+
+            return semaphore;
         }
 
-
         /// <summary>
-        /// 鐢熸垚鍑哄簱浠诲姟
+        /// 閲婃斁鍐呭瓨閿佸苟鏇存柊鏈�鍚庝娇鐢ㄦ椂闂�
         /// </summary>
-        /// <param name="keys">鍑哄簱鍗曟槑缁嗕富閿�</param>
-        /// <returns></returns>
-        public async Task<WebResponseContent> GenerateOutboundTasksAsync(int[] keys,string outStation) 
+        private void UpdateMaterialLockUsedTime(string materialCode, string batchNo, string supplyCode)
+        {
+            string lockKey = $"MaterialLock_{materialCode}_{batchNo}_{supplyCode}";
+            _lockLastUsed[lockKey] = DateTime.Now;
+        }
+        #endregion
+        /// <summary>
+        /// 鍒嗘壒鍒嗛厤搴撳瓨
+        /// </summary>
+        public async Task<WebResponseContent> GenerateOutboundBatchTasksAsync(int orderDetailId, decimal batchQuantity, string outStation)
         {
             try
             {
-                List<Dt_Task> tasks = new List<Dt_Task>();
-                List<StockSelectViewDTO> stockSelectViews = new List<StockSelectViewDTO>();
-                List<Dt_StockInfo> stockInfos = new List<Dt_StockInfo>();
-                List<Dt_OutboundOrderDetail> outboundOrderDetails = new List<Dt_OutboundOrderDetail>();
-                List<Dt_OutStockLockInfo> outStockLockInfos = new List<Dt_OutStockLockInfo>();
-                List<Dt_LocationInfo> locationInfos = new List<Dt_LocationInfo>();
+                // 鍏堣幏鍙栬鍗曟槑缁嗕俊鎭紝纭畾鐗╂枡
+                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
+                    .FirstAsync(x => x.Id == orderDetailId);
 
-                (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) result = OutboundTaskDataHandle(keys,outStation);
-                if (result.Item2 != null && result.Item2.Count > 0)
-                {
-                    stockInfos.AddRange(result.Item2);
-                }
-                if (result.Item3 != null && result.Item3.Count > 0)
-                {
-                    outboundOrderDetails.AddRange(result.Item3);
-                }
-                if (result.Item4 != null && result.Item4.Count > 0)
-                {
-                    outStockLockInfos.AddRange(result.Item4);
-                }
-                if (result.Item5 != null && result.Item5.Count > 0)
-                {
-                    locationInfos.AddRange(result.Item5);
-                }
-                if (result.Item1 != null && result.Item1.Count > 0)
-                {
-                    tasks.AddRange(result.Item1);
-                }
+                if (orderDetail == null)
+                    return WebResponseContent.Instance.Error("鏈壘鍒拌鍗曟槑缁嗕俊鎭�");
 
-                WebResponseContent content = await GenerateOutboundTaskDataUpdateAsync(tasks, stockInfos, outboundOrderDetails, outStockLockInfos, locationInfos);
-                return content;
+                // 鑾峰彇鐗╂枡绾у唴瀛橀攣
+                var semaphore = GetMaterialSemaphore(orderDetailId + orderDetail.MaterielCode, orderDetail.BatchNo, orderDetail.SupplyCode);
+
+                // 绛夊緟鑾峰彇鍐呭瓨閿侊紝鏈�澶氱瓑寰�30绉�
+                bool memoryLockAcquired = await semaphore.WaitAsync(TimeSpan.FromSeconds(30));
+
+                if (!memoryLockAcquired)
+                    return WebResponseContent.Instance.Error("绯荤粺绻佸繖锛岃绋嶅悗閲嶈瘯");
+
+                try
+                {
+                    List<Dt_Task> tasks = new List<Dt_Task>();
+                    List<Dt_StockInfo> stockInfos = new List<Dt_StockInfo>();
+                    List<Dt_OutboundOrderDetail> outboundOrderDetails = new List<Dt_OutboundOrderDetail>();
+                    List<Dt_OutStockLockInfo> outStockLockInfos = new List<Dt_OutStockLockInfo>();
+                    List<Dt_LocationInfo> locationInfos = new List<Dt_LocationInfo>();
+
+                    (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) result = await BatchAllocateStockDataHandle(orderDetailId, batchQuantity, outStation);
+
+                    if (result.Item2 != null && result.Item2.Count > 0)
+                    {
+                        stockInfos.AddRange(result.Item2);
+                    }
+                    if (result.Item3 != null && result.Item3.Count > 0)
+                    {
+                        outboundOrderDetails.AddRange(result.Item3);
+                    }
+                    if (result.Item4 != null && result.Item4.Count > 0)
+                    {
+                        outStockLockInfos.AddRange(result.Item4);
+                    }
+                    if (result.Item5 != null && result.Item5.Count > 0)
+                    {
+                        locationInfos.AddRange(result.Item5);
+                    }
+                    if (result.Item1 != null && result.Item1.Count > 0)
+                    {
+                        tasks.AddRange(result.Item1);
+                    }
+
+                    WebResponseContent content = await GenerateOutboundTaskDataUpdateAsync(tasks, stockInfos, outboundOrderDetails, outStockLockInfos, locationInfos);
+                    return content;
+                }
+                finally
+                {
+                    // 閲婃斁鍐呭瓨閿�
+                    semaphore.Release();
+                    UpdateMaterialLockUsedTime(orderDetail.MaterielCode, orderDetail.BatchNo, orderDetail.SupplyCode);
+                }
             }
             catch (Exception ex)
             {
                 _unitOfWorkManage.RollbackTran();
-                return WebResponseContent.Instance.Error(ex.Message);
+                _logger.LogError($"鍒嗘壒鍒嗛厤搴撳瓨澶辫触 -  OrderDetailId: {orderDetailId}, Quantity: {batchQuantity}, Error: {ex.Message}");
+                return WebResponseContent.Instance.Error($"鍒嗘壒鍒嗛厤澶辫触锛歿ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 鍒嗘壒鍒嗛厤搴撳瓨鏁版嵁澶勭悊
+        /// </summary>
+        public async Task<(List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?)>
+            BatchAllocateStockDataHandle(int orderDetailId, decimal batchQuantity, string outStation)
+        {
+            List<Dt_Task> tasks = new List<Dt_Task>();
+
+            // 鑾峰彇璁㈠崟鏄庣粏
+            var outboundOrderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>().With("UPDLOCK")
+                .FirstAsync(x => x.Id == orderDetailId);
+
+            if (outboundOrderDetail == null)
+            {
+                throw new Exception("鏈壘鍒板嚭搴撳崟鏄庣粏淇℃伅");
+            }
+            var outboundOrder = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().FirstAsync(x => x.Id == outboundOrderDetail.OrderId);
+            if (outboundOrder == null)
+            {
+                throw new Exception("鏈壘鍒板嚭搴撳崟淇℃伅");
+            }
+            // 楠岃瘉璁㈠崟鏄庣粏鐘舵��
+            if (outboundOrderDetail.OrderDetailStatus > OrderDetailStatusEnum.Outbound.ObjToInt() &&
+                outboundOrderDetail.OrderDetailStatus != OrderDetailStatusEnum.AssignOverPartial.ObjToInt())
+            {
+                throw new Exception("鎵�閫夊嚭搴撳崟鏄庣粏瀛樺湪鍑哄簱涓垨宸插畬鎴�");
+            }
+
+            // 楠岃瘉鍒嗛厤鏁伴噺
+            decimal allocatedQty = outboundOrderDetail.AllocatedQuantity;
+            decimal overOutQty = outboundOrderDetail.OverOutQuantity;
+            decimal needOutQty = outboundOrderDetail.NeedOutQuantity;
+            decimal availableQty = needOutQty - allocatedQty - overOutQty;
+
+            if (availableQty <= 0)
+                throw new Exception("鏃犲彲鍒嗛厤鏁伴噺");
+
+            if (batchQuantity > availableQty)
+                throw new Exception($"鍒嗛厤鏁伴噺涓嶈兘瓒呰繃鍙垎閰嶆暟閲弡availableQty}");
+
+            List<Dt_StockInfo>? stockInfos = null;
+            List<Dt_OutboundOrderDetail>? orderDetails = null;
+            List<Dt_OutStockLockInfo>? outStockLockInfos = null;
+            List<Dt_LocationInfo>? locationInfos = null;
+
+            // 鐢熸垚鎵规鍙�
+            string batchNo = await GenerateBatchNo();
+
+            // 鍒嗛厤搴撳瓨
+            (List<Dt_StockInfo>, List<Dt_OutboundOrderDetail>, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>) allocateResult =
+                await _outboundOrderDetailService.AssignStockForBatch(outboundOrderDetail, batchQuantity, batchNo);
+
+            if (allocateResult.Item1 != null && allocateResult.Item1.Count > 0)
+            {
+                // 鍒涘缓鍒嗘壒璁板綍
+                await CreateBatchRecord(outboundOrder.OrderNo, orderDetailId, batchQuantity, batchNo);
+
+                TaskTypeEnum typeEnum = outboundOrder.OrderType switch
+                {
+                    (int)OutOrderTypeEnum.Issue => TaskTypeEnum.Outbound,
+                    (int)OutOrderTypeEnum.Allocate => TaskTypeEnum.OutAllocate,
+                    (int)OutOrderTypeEnum.Quality => TaskTypeEnum.OutQuality,
+                    _ => TaskTypeEnum.Outbound
+                };
+
+                tasks = GetTasks(allocateResult.Item1, typeEnum, outStation);
+                tasks.ForEach(x =>
+                {
+                    x.OrderNo = outboundOrder.OrderNo;
+                });
+
+                allocateResult.Item2.ForEach(x =>
+                {
+                    x.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt();
+                });
+
+                allocateResult.Item3.ForEach(x =>
+                {
+                    x.Status = OutLockStockStatusEnum.鍑哄簱涓�.ObjToInt();
+                });
+
+                stockInfos = allocateResult.Item1;
+                orderDetails = allocateResult.Item2;
+                outStockLockInfos = allocateResult.Item3;
+                locationInfos = allocateResult.Item4;
+            }
+            else
+            {
+                throw new Exception("鏃犲簱瀛�");
+            }
+
+            return (tasks, stockInfos, orderDetails, outStockLockInfos, locationInfos);
+        }
+
+
+
+
+
+        /// <summary>
+        /// 鏇存柊璁㈠崟鏄庣粏鐘舵��
+        /// </summary>
+        private void UpdateOrderDetailStatus(List<Dt_OutboundOrderDetail> details, decimal allocatedQuantity, decimal needQuantity)
+        {
+            foreach (var detail in details)
+            {
+                // 鏍规嵁鍒嗛厤鎯呭喌鏇存柊鐘舵��
+                if (allocatedQuantity >= needQuantity)
+                {
+                    detail.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt();
+                }
+                else
+                {
+                    detail.OrderDetailStatus = OrderDetailStatusEnum.AssignOverPartial.ObjToInt();
+                }
             }
         }
 
 
+
+        private async Task<string> GenerateBatchNo()
+        {
+            var batchNo = UniqueValueGenerator.Generate();
+
+            return $"Out{batchNo} ";
+        }
+
+        private async Task<Dt_OutboundBatch> CreateBatchRecord(string orderNo, int orderDetailId, decimal batchQuantity, string batchNo)
+        {
+            var batchRecord = new Dt_OutboundBatch
+            {
+                BatchNo = batchNo,
+                OrderNo = orderNo,
+                OrderDetailId = orderDetailId,
+                BatchQuantity = batchQuantity,
+                BatchStatus = (int)BatchStatusEnum.鍒嗛厤涓�,
+                Operator = App.User.UserName
+            };
+
+            await _OutboundBatchRepository.Db.Insertable(batchRecord).ExecuteCommandAsync();
+            return batchRecord;
+        }
+
+        #endregion
+
+        /// <summary>
+        /// 閫夊畾搴撳瓨鐢熸垚鐩樼偣鍗曞嚭搴�
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public async Task<WebResponseContent> TakeOutbound(List<StockViewDTO> stockViews, string outStation)
+        {
+            WebResponseContent content = new WebResponseContent();
+            try
+            {
+                var allFactoryAreas = stockViews.SelectMany(sv => sv.Details)
+                                                .Select(x => x.FactoryArea)
+                                                .GroupBy(x => x)
+                                                .ToList();
+                if (allFactoryAreas.Count >= 2)
+                {
+                    return content.Error($"璇烽�夋嫨鍚屼竴鍘傚尯鍖哄煙鐨勫簱瀛樿繘琛岀洏鐐癸紝褰撳墠娑夊強{allFactoryAreas.Count}涓笉鍚岀殑鍘傚尯");
+                }
+                List<int> ids = stockViews.Select(x => x.StockId).ToList();
+                //鑾峰彇搴撳瓨
+                List<Dt_StockInfo> stockInfos = _stockRepository.Db.Queryable<Dt_StockInfo>().Where(x => ids.Contains(x.Id)).Includes(x => x.Details).ToList();
+                if (stockInfos.Count != stockViews.Count)
+                {
+                    StockViewDTO? stockViewDTO = stockViews.FirstOrDefault(x => !stockInfos.Select(x => x.PalletCode).ToList().Contains(x.PalletCode));
+                    return content.Error($"鏈壘鍒皗stockViewDTO?.PalletCode}搴撳瓨");
+                }
+                //鑾峰彇璐т綅
+                List<string> locStrs = stockInfos.Select(x => x.LocationCode).ToList();
+                List<Dt_LocationInfo> locationInfos =_locationInfoService.Db.Queryable<Dt_LocationInfo>().Where(x => locStrs.Contains(x.LocationCode)).ToList();
+                if (stockInfos.Count != locationInfos.Count)
+                {
+                    string? locStr = locStrs.FirstOrDefault(x => !locationInfos.Select(x => x.LocationCode).ToList().Contains(x));
+                    return content.Error($"鏈壘鍒皗locStr}璐т綅鏁版嵁");
+                }
+                Dt_TakeStockOrder takeStockOrder = new Dt_TakeStockOrder()
+                {
+                    WarehouseId = stockInfos.FirstOrDefault().WarehouseId,
+                    TakeStockStatus = TakeStockStatusEnum.鐩樼偣涓�.ObjToInt(),
+                    OrderNo = CreateCodeByRule(nameof(RuleCodeEnum.PDCodeRule)),
+                    AllPalletCode = string.Join(",", stockInfos.Select(item => item.PalletCode).Where(palletCode => !string.IsNullOrEmpty(palletCode))),
+                    Remark = outStation
+                    
+                };
+                foreach (var item in stockInfos)
+                {
+                    if (item.Details.Count <= 0)
+                    {
+                        return content.Error($"鏈壘鍒皗item.PalletCode}搴撳瓨鏄庣粏鏁版嵁");
+                    }
+                    Dt_LocationInfo? locationInfo = locationInfos.FirstOrDefault(x => x.LocationCode == item.LocationCode);
+                    if (locationInfo == null || (locationInfo.EnableStatus == EnableStatusEnum.Disable.ObjToInt() || locationInfo.EnableStatus != EnableStatusEnum.Normal.ObjToInt()) || locationInfo.LocationStatus != LocationStatusEnum.InStock.ObjToInt() || item.StockStatus != StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt())
+                    {
+                        return content.Error($"{item.PalletCode}璐т綅鎴栧簱瀛樼姸鎬佷笉婊¤冻鍑哄簱鏉′欢");
+                    }   
+                }
+                List<Dt_Task> tasks = GetTasks(stockInfos, TaskTypeEnum.OutInventory,outStation);
+                if (tasks == null || tasks.Count <= 0)
+                {
+                    return content.Error($"鐢熸垚浠诲姟澶辫触");
+                }
+                stockInfos.ForEach(x =>
+                {
+                    x.StockStatus = StockStatusEmun.鍑哄簱閿佸畾.ObjToInt();
+                });
+                tasks.ForEach(x =>
+                {
+                    x.OrderNo = takeStockOrder.OrderNo;
+                });
+                locationInfos.ForEach(x =>
+                {
+                    x.LocationStatus = LocationStatusEnum.Lock.ObjToInt();
+                });
+                _unitOfWorkManage.BeginTran();
+                //鏇存柊搴撳瓨鐘舵��
+                _stockRepository.UpdateData(stockInfos);
+                _takeStockOrder.AddData(takeStockOrder);
+                //鏂板缓浠诲姟
+                BaseDal.AddData(tasks);
+                _locationInfoService.UpdateData(locationInfos);
+                _unitOfWorkManage.CommitTran();
+                content.OK();
+                //TaskModel esstask = new TaskModel()
+                //{
+                //    taskType = "carry",
+                //    taskGroupCode = "",
+                //    groupPriority = 0,
+                //    tasks = new List<TasksType>()
+                //};
+
+                //foreach (var task in tasks)
+                //{
+                //    esstask.
+                //       tasks.Add(new TasksType
+                //       {
+                //           taskCode = task.TaskNum.ToString(),
+                //           taskPriority = 0,
+                //           taskDescribe = new TaskDescribeType
+                //           {
+                //               containerCode = task.PalletCode,
+                //               containerType = "CT_KUBOT_STANDARD",
+                //               fromLocationCode = task.SourceAddress ?? "",
+                //               toStationCode = "",
+                //               toLocationCode = task.TargetAddress,
+                //               deadline = 0,
+                //               storageTag = ""
+                //           }
+                //       }
+                //   );
+                //}
+                //var result = await _eSSApiService.CreateTaskAsync(esstask);
+
+                //_logger.LogInformation("鍒涘缓浠诲姟PalletOutboundTask 杩斿洖:  " + result);
+                //if (result)
+                //{
+                //    return WebResponseContent.Instance.OK();
+                //}
+                //else
+                //{
+                //    return WebResponseContent.Instance.Error("涓嬪彂鏈哄櫒浜轰换鍔″け璐ワ紒");
+                //}
+                //content.OK();
+            }
+            catch (Exception ex)
+            {
+                _unitOfWorkManage.RollbackTran();
+                return await Task.FromResult(WebResponseContent.Instance.Error(ex.Message));
+            }
+            return content;
+        }
+        /// <summary>
+        /// 鍗曟嵁鐢熸垚鏂规硶
+        /// </summary>
+        static object lock_code = new object();
+        public string CreateCodeByRule(string ruleCode)
+        {
+            lock (lock_code)
+            {
+
+                string code = string.Empty;
+                DateTime dateTime = DateTime.Now;
+                DateTime now = DateTime.Now;
+                try
+                {
+                    if (string.IsNullOrEmpty(ruleCode))
+                        throw new ArgumentNullException(nameof(ruleCode));
+                    SqlSugarClient sugarClient = new SqlSugarClient(new ConnectionConfig
+                    {
+                        IsAutoCloseConnection = true,
+                        DbType = DbType.SqlServer,
+                        ConnectionString = DBContext.ConnectionString
+                    });
+                    Dt_CodeRuleConfig codeRuleConfig = sugarClient.Queryable<Dt_CodeRuleConfig>().Where(x => x.RuleCode == ruleCode).First();
+                    if (codeRuleConfig == null)
+                        throw new ArgumentNullException(nameof(codeRuleConfig));
+                    if (codeRuleConfig.ModifyDate != null)
+                    {
+                        dateTime = Convert.ToDateTime(codeRuleConfig.ModifyDate);
+                    }
+                    else
+                    {
+                        dateTime = Convert.ToDateTime(codeRuleConfig.CreateDate);
+                    }
+
+                    if (now.Year == dateTime.Year && now.Month == dateTime.Month && now.Day == dateTime.Day)
+                    {
+                        now = dateTime;
+                        codeRuleConfig.CurrentVal = Convert.ToInt32(codeRuleConfig.CurrentVal) + 1;
+                    }
+                    else
+                    {
+                        codeRuleConfig.CurrentVal = 1;
+                    }
+                    codeRuleConfig.ModifyDate = DateTime.Now;
+                    code = codeRuleConfig.StartStr + codeRuleConfig.Format;
+                    code = code.Replace($"[{CodeFormatTypeEnum.YYYY}]", now.Year.ToString().PadLeft(4, '0'));
+                    code = code.Replace($"[{CodeFormatTypeEnum.MM}]", now.Month.ToString().PadLeft(2, '0'));
+                    code = code.Replace($"[{CodeFormatTypeEnum.DD}]", now.Day.ToString().PadLeft(2, '0'));
+                    code = code.Replace($"[{CodeFormatTypeEnum.ST}]", codeRuleConfig.StartStr?.ToString() ?? "");
+                    code = code.Replace($"[{CodeFormatTypeEnum.NUM}]", codeRuleConfig.CurrentVal.ToString().PadLeft(codeRuleConfig.Length, '0'));
+                    Dictionary<string, object> keyValuePairs = new Dictionary<string, object>() { { nameof(codeRuleConfig.CurrentVal), codeRuleConfig.CurrentVal }, { nameof(codeRuleConfig.Id), codeRuleConfig.Id }, { nameof(codeRuleConfig.ModifyDate), DateTime.Now } };
+                    sugarClient.Updateable(keyValuePairs).AS(MainDb.CodeRuleConfig).WhereColumns(nameof(codeRuleConfig.Id)).ExecuteCommand();
+                    sugarClient.Updateable(codeRuleConfig);
+
+                }
+                catch (Exception ex)
+                {
+
+                }
+                return code;
+            }
+        }
+
+        /// <summary>
+        /// 閫夊畾搴撳瓨鍚屽尯鍩熺Щ搴�
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public async Task<WebResponseContent> AreaOutbound(List<StockViewDTO> stockViews)
+        {
+            WebResponseContent content = new WebResponseContent();
+            try
+            {
+                List<int> ids = stockViews.Select(x => x.StockId).ToList();
+                //鑾峰彇搴撳瓨
+                List<Dt_StockInfo> stockInfos = _stockRepository.Db.Queryable<Dt_StockInfo>().Where(x => ids.Contains(x.Id)).Includes(x => x.Details).ToList();
+                if (stockInfos.Count != stockViews.Count)
+                {
+                    StockViewDTO? stockViewDTO = stockViews.FirstOrDefault(x => !stockInfos.Select(x => x.PalletCode).ToList().Contains(x.PalletCode));
+                    return content.Error($"鏈壘鍒皗stockViewDTO?.PalletCode}搴撳瓨");
+                }
+                //鑾峰彇璐т綅
+                List<string> locStrs = stockInfos.Select(x => x.LocationCode).ToList();
+                List<Dt_LocationInfo> locationInfos = _locationInfoService.Db.Queryable<Dt_LocationInfo>().Where(x => locStrs.Contains(x.LocationCode)).ToList();
+                if (stockInfos.Count != locationInfos.Count)
+                {
+                    string? locStr = locStrs.FirstOrDefault(x => !locationInfos.Select(x => x.LocationCode).ToList().Contains(x));
+                    return content.Error($"鏈壘鍒皗locStr}璐т綅鏁版嵁");
+                }
+               
+                foreach (var item in stockInfos)
+                {
+                    Dt_LocationInfo? locationInfo = locationInfos.FirstOrDefault(x => x.LocationCode == item.LocationCode);
+                    if (locationInfo == null || (locationInfo.EnableStatus == EnableStatusEnum.Disable.ObjToInt() || locationInfo.EnableStatus != EnableStatusEnum.Normal.ObjToInt()) || item.StockStatus != StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt())
+                    {
+                        return content.Error($"{item.PalletCode}璐т綅鎴栧簱瀛樼姸鎬佷笉婊¤冻鍑哄簱鏉′欢");
+                    }
+                }
+                List<Dt_Task> tasks = AreaGetTasks(stockInfos, TaskTypeEnum.AreaRelocation);
+                if (tasks == null || tasks.Count <= 0)
+                {
+                    return content.Error($"鐢熸垚浠诲姟澶辫触");
+                }
+                stockInfos.ForEach(x =>
+                {
+                    x.StockStatus = StockStatusEmun.鍑哄簱閿佸畾.ObjToInt();
+                });
+                tasks.ForEach(x =>
+                {
+                    x.OrderNo = "鏃犲崟鎹Щ搴�";
+                });
+                locationInfos.ForEach(x =>
+                {
+                    x.LocationStatus = LocationStatusEnum.Lock.ObjToInt();
+                });
+                _unitOfWorkManage.BeginTran();
+                //鏇存柊搴撳瓨鐘舵��
+                _stockRepository.UpdateData(stockInfos);
+                //鏂板缓浠诲姟
+                BaseDal.AddData(tasks);
+                _locationInfoService.UpdateData(locationInfos);
+                _unitOfWorkManage.CommitTran();
+                content.OK();
+            }
+            catch (Exception ex)
+            {
+                _unitOfWorkManage.RollbackTran();
+                return await Task.FromResult(WebResponseContent.Instance.Error(ex.Message));
+            }
+            return content;
+        }
+
+        public List<Dt_Task> AreaGetTasks(List<Dt_StockInfo> stockInfos, TaskTypeEnum taskType)
+        {
+            // 浣跨敤 TransactionScope 鍖呰9鏁翠釜鎿嶄綔锛岀‘淇濇墍鏈夋暟鎹簱鎿嶄綔鍦ㄥ悓涓�浜嬪姟涓�
+            using (var scope = new TransactionScope(TransactionScopeOption.Required,
+                new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
+            {
+                try
+                {
+                    List<Dt_Task> tasks = new List<Dt_Task>();
+                    List<Dt_LocationInfo> locationInfos = _locationInfoService.Repository.QueryData(
+                        x => stockInfos.Select(x => x.LocationCode).Contains(x.LocationCode));
+
+                    for (int i = 0; i < stockInfos.Count; i++)
+                    {
+                        Dt_StockInfo stockInfo = stockInfos[i];
+
+                        if (stockInfo != null)
+                        {
+                            Dt_LocationInfo? locationInfo = locationInfos.FirstOrDefault(x => x.LocationCode == stockInfo.LocationCode);
+                            var newLocation = _locationInfoService.AssignLocation(stockInfo.LocationType);
+
+                            if (newLocation == null)
+                            {
+                                throw new Exception($"鍦▄stockInfo.PalletCode}鏃舵病鏈夌┖闂插簱浣嶅彲杩涜鍚屽尯鍩熺Щ搴擄紝璇锋鏌ユ垨鍑忓皯绉诲簱鏂欑");
+                            }
+
+                            if (!tasks.Exists(x => x.PalletCode == stockInfo.PalletCode))
+                            {
+                                Dt_Task task = new()
+                                {
+                                    CurrentAddress = stockInfo.LocationCode,
+                                    Grade = 0,
+                                    PalletCode = stockInfo.PalletCode,
+                                    NextAddress = "",
+                                    Roadway = locationInfo.RoadwayNo,
+                                    SourceAddress = stockInfo.LocationCode,
+                                    TargetAddress = newLocation.LocationCode,
+                                    TaskStatus = TaskStatusEnum.New.ObjToInt(),
+                                    TaskType = taskType.ObjToInt(),
+                                    PalletType = stockInfo.PalletType,
+                                    WarehouseId = stockInfo.WarehouseId,
+                                };
+                                tasks.Add(task);
+                            }
+
+                            newLocation.LocationStatus = LocationStatusEnum.Lock.ObjToInt();
+                            _locationInfoService.UpdateData(newLocation);
+                        }
+                    }
+
+                    // 鎻愪氦浜嬪姟
+                    scope.Complete();
+                    return tasks;
+                }
+                catch (Exception ex)
+                {
+                    // TransactionScope 浼氳嚜鍔ㄥ洖婊氫簨鍔★紝鏃犻渶鎵嬪姩 Rollback
+                    throw new Exception(ex.Message);
+                }
+            }
+        }
+
+        /// <summary>
+        /// 閫夊畾搴撳瓨璺ㄥ尯鍩熺Щ搴�
+        /// </summary>
+        /// <param name="stockViews"></param>
+        /// <param name="targetLocationType">鐩爣璐т綅绫诲瀷</param>
+        /// <returns></returns>
+        public async Task<WebResponseContent> CrossAreaOutbound(List<StockViewDTO> stockViews, int targetLocationType)
+        {
+            WebResponseContent content = new WebResponseContent();
+            try
+            {
+                if(targetLocationType == (int)LocationTypeEnum.Electronic)
+                {
+                    return content.Error("鐢靛瓙浠撲笉鍏佽璺ㄥ尯鍩熺Щ搴�");
+                }
+                List<int> ids = stockViews.Select(x => x.StockId).ToList();
+                List<Dt_StockInfo> stockInfos = _stockRepository.Db.Queryable<Dt_StockInfo>().Where(x => ids.Contains(x.Id)).Includes(x => x.Details).ToList();
+
+                if (stockInfos.Count != stockViews.Count)
+                {
+                    StockViewDTO? stockViewDTO = stockViews.FirstOrDefault(x => !stockInfos.Select(x => x.PalletCode).Contains(x.PalletCode));
+                    return content.Error($"鏈壘鍒皗stockViewDTO?.PalletCode}搴撳瓨");
+                }
+
+                List<string> locStrs = stockInfos.Select(x => x.LocationCode).ToList();
+                List<Dt_LocationInfo> locationInfos = _locationInfoService.Db.Queryable<Dt_LocationInfo>().Where(x => locStrs.Contains(x.LocationCode)).ToList();
+
+                if (stockInfos.Count != locationInfos.Count)
+                {
+                    string? locStr = locStrs.FirstOrDefault(x => !locationInfos.Select(x => x.LocationCode).Contains(x));
+                    return content.Error($"鏈壘鍒皗locStr}璐т綅鏁版嵁");
+                }
+
+                foreach (var item in stockInfos)
+                {
+                    if (item.PalletType != PalletTypeEnum.Empty.ObjToInt())
+                    {
+                        return content.Error($"鎵樼洏銆恵item.PalletCode}銆戦潪绌虹锛屼粎绌虹鍏佽璺ㄥ尯鍩熺Щ搴擄紒");
+                    }
+                    Dt_LocationInfo? locationInfo = locationInfos.FirstOrDefault(x => x.LocationCode == item.LocationCode);
+                    if (locationInfo == null || locationInfo.EnableStatus != EnableStatusEnum.Normal.ObjToInt() || item.StockStatus != StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt())
+                    {
+                        return content.Error($"{item.PalletCode}璐т綅鎴栧簱瀛樼姸鎬佷笉婊¤冻鍑哄簱鏉′欢");
+                    }
+                }
+
+                List<Dt_Task> tasks = CrossAreaGetTasks(stockInfos, targetLocationType, TaskTypeEnum.CrossAreaRelocation);
+
+                if (tasks == null || tasks.Count <= 0)
+                {
+                    return content.Error("鐢熸垚璺ㄥ尯鍩熺Щ搴撲换鍔″け璐�");
+                }
+
+                stockInfos.ForEach(x =>
+                {
+                    x.StockStatus = StockStatusEmun.鍑哄簱閿佸畾.ObjToInt();
+                });
+
+                tasks.ForEach(x =>
+                {
+                    x.OrderNo = "璺ㄥ尯鍩熺Щ搴�";
+                });
+
+                locationInfos.ForEach(x =>
+                {
+                    x.LocationStatus = LocationStatusEnum.Lock.ObjToInt();
+                });
+
+                _unitOfWorkManage.BeginTran();
+                _stockRepository.UpdateData(stockInfos);
+                BaseDal.AddData(tasks);
+                _locationInfoService.UpdateData(locationInfos);
+                _unitOfWorkManage.CommitTran();
+
+                content.OK();
+            }
+            catch (Exception ex)
+            {
+                _unitOfWorkManage.RollbackTran();
+                return await Task.FromResult(WebResponseContent.Instance.Error(ex.Message));
+            }
+            return content;
+        }
+
+
+        /// <summary>
+        /// 鐢熸垚璺ㄥ尯鍩熺Щ搴撲换鍔�
+        /// </summary>
+        /// <param name="stockInfos">搴撳瓨鍒楄〃</param>
+        /// <param name="targetAreaCode">鐩爣鍖哄煙缂栫爜</param>
+        /// <param name="taskType">浠诲姟绫诲瀷</param>
+        /// <returns></returns>
+        public List<Dt_Task> CrossAreaGetTasks(List<Dt_StockInfo> stockInfos, int targetLocationType, TaskTypeEnum taskType)
+        {
+            using (var scope = new TransactionScope(TransactionScopeOption.Required,
+                new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
+            {
+                try
+                {
+                    List<Dt_Task> tasks = new List<Dt_Task>();
+
+                    foreach (var stockInfo in stockInfos)
+                    {
+                        Dt_LocationInfo newLocation = _locationInfoService.AssignLocation(targetLocationType);
+
+                        if (newLocation == null)
+                        {
+                            throw new Exception($"{stockInfo.PalletCode} 娌℃湁绌洪棽璐т綅鍙繘琛岃法鍖哄煙绉诲簱");
+                        }
+
+                        if (!tasks.Exists(x => x.PalletCode == stockInfo.PalletCode))
+                        {
+                            Dt_Task task = new()
+                            {
+                                CurrentAddress = stockInfo.LocationCode,
+                                Grade = 0,
+                                PalletCode = stockInfo.PalletCode,
+                                NextAddress = "",
+                                Roadway = newLocation.RoadwayNo,
+                                SourceAddress = stockInfo.LocationCode,
+                                TargetAddress = newLocation.LocationCode,
+                                TaskStatus = TaskStatusEnum.New.ObjToInt(),
+                                TaskType = taskType.ObjToInt(),
+                                PalletType = stockInfo.PalletType,
+                                WarehouseId = stockInfo.WarehouseId,
+                            };
+                            tasks.Add(task);
+                        }
+
+                        newLocation.LocationStatus = LocationStatusEnum.Lock.ObjToInt();
+                        _locationInfoService.UpdateData(newLocation);
+                    }
+
+                    scope.Complete();
+                    return tasks;
+                }
+                catch (Exception ex)
+                {
+                    throw new Exception(ex.Message);
+                }
+            }
+        }
     }
+
 }

--
Gitblit v1.9.3