pan
7 天以前 42cb7e13165395327f2f9de6c1def858a18424b2
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_BasicService/InvokeMESService.cs
@@ -245,147 +245,145 @@
        public async Task<WebResponseContent> BatchOrderFeedbackToMes(List<string> orderNos, int inout)
        {
            try
            {             // 1. ã€å†…存锁抢占】
                //if (MemoryLockManager.TryAcquireLock(orderNos[0]))
                //{
                        if (inout == 1)
            {
                if (inout == 1)
                {
                    foreach (var orderNo in orderNos)
                    {
                        try
                        {
                            foreach (var orderNo in orderNos)
                            var stockinfos = _stockInfoRepository.Db.Queryable<Dt_StockInfo>("info").Where(info => info.StockStatus == 6)
                                            .Where(it => SqlFunc.Subqueryable<Dt_StockInfoDetail>().Where(s => s.StockId == it.Id && s.OrderNo == orderNo).Any())
                                            .ToList();
                            var feeds = _feedbacktomesRepository.Db.Queryable<Dt_FeedbackToMes>().Where(x => x.OrderNo == orderNo && x.ReportStatus == 1).Select(o => o.PalletCode).ToList();
                            var unreports = stockinfos.Where(x => !feeds.Contains(x.PalletCode)).ToList();
                            if (unreports != null && !unreports.Any())
                            {
                                try
                                return WebResponseContent.Instance.Error("没有需要回传的数据");
                            }
                            foreach (var item in unreports)
                            {
                                var lists = _stockInfoDetailRepository.Db.Queryable<Dt_StockInfoDetail>().Where(x => x.StockId == item.Id).ToList();
                                if (lists.Any())
                                {
                                    var stockinfos = _stockInfoRepository.Db.Queryable<Dt_StockInfo>("info").Where(info => info.StockStatus == 6)
                                                    .Where(it => SqlFunc.Subqueryable<Dt_StockInfoDetail>().Where(s => s.StockId == it.Id && s.OrderNo == orderNo).Any())
                                                    .ToList();
                                    var feeds = _feedbacktomesRepository.Db.Queryable<Dt_FeedbackToMes>().Where(x => x.OrderNo == orderNo && x.ReportStatus == 1).Select(o => o.PalletCode).ToList();
                                    var unreports = stockinfos.Where(x => !feeds.Contains(x.PalletCode)).ToList();
                                    if (unreports != null && !unreports.Any())
                                    var inboundOrder = _inboundOrderRepository.Db.Queryable<Dt_InboundOrder>().First(x => x.InboundOrderNo == lists.FirstOrDefault().OrderNo);
                                    if (inboundOrder != null)
                                    {
                                        return WebResponseContent.Instance.Error("没有需要回传的数据");
                                    }
                                    foreach (var item in unreports)
                                    {
                                        var lists = _stockInfoDetailRepository.Db.Queryable<Dt_StockInfoDetail>().Where(x => x.StockId == item.Id).ToList();
                                        if (lists.Any())
                                        if (inboundOrder.OrderType == (int)InOrderTypeEnum.AllocatInbound)//调拨入库
                                        {
                                            var inboundOrder = _inboundOrderRepository.Db.Queryable<Dt_InboundOrder>().First(x => x.InboundOrderNo == lists.FirstOrDefault().OrderNo);
                                            if (inboundOrder != null)
                                            var allocate = SqlSugarHelper.DbWMS.Queryable<Dt_AllocateOrder>().Where(x => x.OrderNo == inboundOrder.InboundOrderNo).First();
                                            var allocatefeedmodel = new AllocateDto
                                            {
                                                if (inboundOrder.OrderType == (int)InOrderTypeEnum.AllocatInbound)//调拨入库
                                                {
                                                    var allocate = SqlSugarHelper.DbWMS.Queryable<Dt_AllocateOrder>().Where(x => x.OrderNo == inboundOrder.InboundOrderNo).First();
                                                    var allocatefeedmodel = new AllocateDto
                                                    {
                                                        ReqCode = Guid.NewGuid().ToString(),
                                                        ReqTime = DateTime.Now.ToString(),
                                                        BusinessType = "2",
                                                        FactoryArea = inboundOrder.FactoryArea,
                                                        OperationType = 1,
                                                        Operator = inboundOrder.Operator,
                                                        OrderNo = inboundOrder.UpperOrderNo,
                                                        fromWarehouse = allocate?.FromWarehouse ?? "",
                                                        toWarehouse = allocate?.ToWarehouse ?? "",
                                                        Details = new List<AllocateDtoDetail>()
                                                ReqCode = Guid.NewGuid().ToString(),
                                                ReqTime = DateTime.Now.ToString(),
                                                BusinessType = "2",
                                                FactoryArea = inboundOrder.FactoryArea,
                                                OperationType = 1,
                                                Operator = inboundOrder.Operator,
                                                OrderNo = inboundOrder.UpperOrderNo,
                                                fromWarehouse = allocate?.FromWarehouse ?? "",
                                                toWarehouse = allocate?.ToWarehouse ?? "",
                                                Details = new List<AllocateDtoDetail>()
                                                    };
                                            };
                                                    var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.InboundOrderRowNo, item.BarcodeUnit, item.WarehouseCode })
                                                       .Select(group => new AllocateDtoDetail
                                                       {
                                                           MaterialCode = group.Key.MaterielCode,
                                                           LineNo = group.Key.InboundOrderRowNo,
                                                           WarehouseCode = group.Key.WarehouseCode,
                                                           Qty = group.Sum(x => x.BarcodeQty),
                                                           Unit = group.Key.BarcodeUnit,
                                                           Barcodes = group.Select(row => new BarcodeInfo
                                                           {
                                                               Barcode = row.Barcode,
                                                               Qty = row.BarcodeQty,
                                                               BatchNo = row.BatchNo,
                                                               SupplyCode = row.SupplyCode,
                                                               Unit = row.BarcodeUnit
                                                           }).ToList()
                                                       }).ToList();
                                                    allocatefeedmodel.Details = groupedData;
                                            var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.InboundOrderRowNo, item.BarcodeUnit, item.WarehouseCode })
                                               .Select(group => new AllocateDtoDetail
                                               {
                                                   MaterialCode = group.Key.MaterielCode,
                                                   LineNo = group.Key.InboundOrderRowNo,
                                                   WarehouseCode = group.Key.WarehouseCode,
                                                   Qty = group.Sum(x => x.BarcodeQty),
                                                   Unit = group.Key.BarcodeUnit,
                                                   Barcodes = group.Select(row => new BarcodeInfo
                                                   {
                                                       Barcode = row.Barcode,
                                                       Qty = row.BarcodeQty,
                                                       BatchNo = row.BatchNo,
                                                       SupplyCode = row.SupplyCode,
                                                       Unit = row.BarcodeUnit
                                                   }).ToList()
                                               }).ToList();
                                            allocatefeedmodel.Details = groupedData;
                                                    var result = await FeedbackAllocate(allocatefeedmodel);
                                                    if (result != null && result.code == 200)
                                                    {
                                                        _feedbacktomesRepository.Db.Insertable(new Dt_FeedbackToMes { OrderNo = orderNo, PalletCode = item.PalletCode, ReportStatus = 1 }).ExecuteCommand();
                                                    }
                                                }
                                                else
                                                {
                                                    var feedmodel = new FeedbackInboundRequestModel
                                                    {
                                                        reqCode = Guid.NewGuid().ToString(),
                                                        reqTime = DateTime.Now.ToString(),
                                                        business_type = inboundOrder.BusinessType,
                                                        factoryArea = inboundOrder.FactoryArea,
                                                        operationType = 1,
                                                        Operator = inboundOrder.Operator,
                                                        orderNo = inboundOrder.UpperOrderNo,
                                                        status = inboundOrder.OrderStatus,
                                                        details = new List<FeedbackInboundDetailsModel>()
                                            var result = await FeedbackAllocate(allocatefeedmodel);
                                            if (result != null && result.code == 200)
                                            {
                                                _feedbacktomesRepository.Db.Insertable(new Dt_FeedbackToMes { OrderNo = orderNo, PalletCode = item.PalletCode, ReportStatus = 1 }).ExecuteCommand();
                                            }
                                        }
                                        else
                                        {
                                            var feedmodel = new FeedbackInboundRequestModel
                                            {
                                                reqCode = Guid.NewGuid().ToString(),
                                                reqTime = DateTime.Now.ToString(),
                                                business_type = inboundOrder.BusinessType,
                                                factoryArea = inboundOrder.FactoryArea,
                                                operationType = 1,
                                                Operator = inboundOrder.Operator,
                                                orderNo = inboundOrder.UpperOrderNo,
                                                status = inboundOrder.OrderStatus,
                                                details = new List<FeedbackInboundDetailsModel>()
                                                    };
                                            };
                                                    var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.SupplyCode, item.BatchNo, item.InboundOrderRowNo, item.BarcodeUnit, item.WarehouseCode })
                                                       .Select(group => new FeedbackInboundDetailsModel
                                                       {
                                                           materialCode = group.Key.MaterielCode,
                                                           supplyCode = group.Key.SupplyCode,
                                                           batchNo = group.Key.BatchNo,
                                                           lineNo = group.Key.InboundOrderRowNo,
                                                           qty = group.Sum(x => x.BarcodeQty),
                                                           // warehouseCode = group.Key.WarehouseCode=="0"?"1072": group.Key.WarehouseCode,
                                                           warehouseCode = group.Key.WarehouseCode,
                                                           unit = group.Key.BarcodeUnit,
                                                           barcodes = group.Select(row => new FeedbackBarcodesModel
                                                           {
                                                               barcode = row.Barcode,
                                                               qty = row.BarcodeQty
                                                           }).ToList()
                                                       }).ToList();
                                                    feedmodel.details = groupedData;
                                                    var result = await FeedbackInbound(feedmodel);
                                                    if (result != null && result.code == 200)
                                                    {
                                                        _feedbacktomesRepository.Db.Insertable(new Dt_FeedbackToMes { OrderNo = orderNo, PalletCode = item.PalletCode, ReportStatus = 1 }).ExecuteCommand();
                                                    }
                                                }
                                            var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.SupplyCode, item.BatchNo, item.InboundOrderRowNo, item.BarcodeUnit, item.WarehouseCode })
                                               .Select(group => new FeedbackInboundDetailsModel
                                               {
                                                   materialCode = group.Key.MaterielCode,
                                                   supplyCode = group.Key.SupplyCode,
                                                   batchNo = group.Key.BatchNo,
                                                   lineNo = group.Key.InboundOrderRowNo,
                                                   qty = group.Sum(x => x.BarcodeQty),
                                                   // warehouseCode = group.Key.WarehouseCode=="0"?"1072": group.Key.WarehouseCode,
                                                   warehouseCode = group.Key.WarehouseCode,
                                                   unit = group.Key.BarcodeUnit,
                                                   barcodes = group.Select(row => new FeedbackBarcodesModel
                                                   {
                                                       barcode = row.Barcode,
                                                       qty = row.BarcodeQty
                                                   }).ToList()
                                               }).ToList();
                                            feedmodel.details = groupedData;
                                            var result = await FeedbackInbound(feedmodel);
                                            if (result != null && result.code == 200)
                                            {
                                                _feedbacktomesRepository.Db.Insertable(new Dt_FeedbackToMes { OrderNo = orderNo, PalletCode = item.PalletCode, ReportStatus = 1 }).ExecuteCommand();
                                            }
                                        }
                                    }
                                }
                                catch (Exception ex)
                                {
                                    _logger.LogInformation("InvokeMESService  BatchOrderFeedbackToMes å›žå†™MES失败:  " + ex.Message);
                                    return WebResponseContent.Instance.Error(ex.Message);
                                }
                            }
                        }
                        else if (inout == 2)
                        catch (Exception ex)
                        {
                            foreach (var orderNo in orderNos)
                            {
                                var outboundOrder = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().FirstAsync(x => x.OrderNo == orderNo);
                                if (outboundOrder != null && outboundOrder.IsBatch == 0)
                                {
                                    var result = await HandleOutboundOrderToMESCompletion(outboundOrder, orderNo);
                                    return result;
                                }
                                else if (outboundOrder != null && outboundOrder.IsBatch == 1)
                                {
                                    var result = await HandleOutboundOrderBatchToMESCompletion(outboundOrder, orderNo);
                                    return result;
                                }
                            }
                            _logger.LogInformation("InvokeMESService  BatchOrderFeedbackToMes å›žå†™MES失败:  " + ex.Message);
                            return WebResponseContent.Instance.Error(ex.Message);
                        }
                    }
                }
                else if (inout == 2)
                {
                    foreach (var orderNo in orderNos)
                    {
                        var outboundOrder = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().FirstAsync(x => x.OrderNo == orderNo);
                        if (outboundOrder != null && outboundOrder.IsBatch == 0)
                        {
                            var result = await HandleOutboundOrderToMESCompletion(outboundOrder, orderNo);
                            return result;
                        }
                        else if (outboundOrder != null && outboundOrder.IsBatch == 1)
                        {
                            var result = await HandleOutboundOrderBatchToMESCompletion(outboundOrder, orderNo);
                            return result;
                        }
                    }
                }
                //}
                //else
                //{
@@ -394,14 +392,14 @@
                //    return WebResponseContent.Instance.Error("WMS正在处理此回传任务,请勿重复操作。");
                //}
            }
            catch(Exception ex)
            catch (Exception ex)
            {
                _logger.LogInformation("InvokeMESService  BatchOrderFeedbackToMes :  " +ex.Message);
                _logger.LogInformation("InvokeMESService  BatchOrderFeedbackToMes :  " + ex.Message);
            }
            finally
            {
                // 2. ã€é‡Šæ”¾å†…存锁】无论成功失败,必须释放
               // MemoryLockManager.ReleaseLock(orderNos[0]);
                // MemoryLockManager.ReleaseLock(orderNos[0]);
            }
            return WebResponseContent.Instance.OK();
        }
@@ -913,52 +911,188 @@
    public static class MemoryLockManager
    {
        // å­˜å‚¨èµ„源ID及其对应的锁对象。使用 ConcurrentDictionary ç¡®ä¿å¯¹å­—典操作本身的线程安全。
        private static readonly ConcurrentDictionary<string, object> _resourceLocks = new ConcurrentDictionary<string, object>();
        // å­˜å‚¨èµ„源锁的元数据(锁对象、持有线程、占用时间、超时时间)
        private class LockMetadata
        {
            public object LockObject { get; } = new object();
            public int HoldingThreadId { get; set; } = -1; // æŒæœ‰é”çš„线程ID
            public DateTime AcquireTime { get; set; } // èŽ·å–é”çš„æ—¶é—´
            public TimeSpan Timeout { get; set; } // é”è¶…æ—¶æ—¶é—´
            public bool IsReleased { get; set; } // æ˜¯å¦å·²é‡Šæ”¾
        }
        // å…¨å±€é™æ€é”ï¼šç”¨äºŽä¿æŠ¤ _resourceLocks å­—典中 GetOrAdd æˆ– TryRemove æ—¶çš„竞争
        private static readonly object _globalLocker = new object();
        // èµ„源ID -> é”å…ƒæ•°æ®
        private static readonly ConcurrentDictionary<string, LockMetadata> _resourceLocks = new();
        // å…¨å±€é”ï¼ˆä¿æŠ¤é”å…ƒæ•°æ®çš„创建/删除)
        private static readonly object _globalLocker = new();
        // éšæœºæ•°ç”Ÿæˆå™¨ï¼ˆç”¨äºŽç”Ÿæˆ3-5秒随机超时)
        private static readonly Random _random = new Random();
        /// <summary>
        /// å°è¯•锁定一个资源ID。
        /// å°è¯•锁定资源(带超时自动释放)
        /// </summary>
        /// <param name="resourceId">要锁定的资源ID(例如 InboundRecord ID)</param>
        /// <param name="resourceId">资源ID</param>
        /// <param name="timeoutSeconds">超时时间(默认3-5秒随机)</param>
        /// <returns>是否成功获取锁</returns>
        public static bool TryAcquireLock(string resourceId)
        public static bool TryAcquireLock(string resourceId, int? timeoutSeconds = null)
        {
            object lockObject = null;
            if (string.IsNullOrEmpty(resourceId))
                throw new ArgumentNullException(nameof(resourceId));
            // æ ¸å¿ƒæ€è·¯ï¼šä¸ºæ¯ä¸ªèµ„源创建一个唯一的锁对象
            // ç¡®å®šè¶…时时间(3-5秒随机)
            var timeout = TimeSpan.FromSeconds(timeoutSeconds ?? _random.Next(3, 6));
            var currentThreadId = Thread.CurrentThread.ManagedThreadId;
            LockMetadata lockMeta = null;
            lock (_globalLocker)
            {
                // å¦‚果资源ID不在字典中,则添加一个新的锁对象
                // å¦åˆ™ï¼Œä½¿ç”¨å·²å­˜åœ¨çš„锁对象
                lockObject = _resourceLocks.GetOrAdd(resourceId, new object());
                // èŽ·å–æˆ–åˆ›å»ºé”å…ƒæ•°æ®
                lockMeta = _resourceLocks.GetOrAdd(resourceId, key => new LockMetadata());
                // é˜²æ­¢é‡å¤èŽ·å–ï¼ˆå½“å‰çº¿ç¨‹å·²æŒæœ‰é”ï¼‰
                if (lockMeta.HoldingThreadId == currentThreadId && !lockMeta.IsReleased)
                    return true; // çº¿ç¨‹å¯é‡å…¥
            }
            // å°è¯•获取资源特定的锁
            // ä½¿ç”¨ Monitor.TryEnter é¿å…é˜»å¡žï¼Œå¹¶å®žçŽ°éžé˜»å¡žçš„æŠ¢é”
            return Monitor.TryEnter(lockObject);
            // å°è¯•获取锁(非阻塞)
            if (!Monitor.TryEnter(lockMeta.LockObject))
                return false;
            // æ ‡è®°é”æŒæœ‰çŠ¶æ€
            lockMeta.HoldingThreadId = currentThreadId;
            lockMeta.AcquireTime = DateTime.Now;
            lockMeta.Timeout = timeout;
            lockMeta.IsReleased = false;
            // å¯åŠ¨è¶…æ—¶è‡ªåŠ¨é‡Šæ”¾ä»»åŠ¡
            _ = Task.Delay(timeout).ContinueWith(_ =>
            {
                try
                {
                    ReleaseLock(resourceId, force: true);
                }
                catch (Exception ex)
                {
                    // è®°å½•超时释放异常
                    Console.WriteLine($"资源[{resourceId}]超时自动释放失败:{ex.Message}");
                }
            });
            return true;
        }
        /// <summary>
        /// é‡Šæ”¾èµ„源ID的锁定。
        /// é‡Šæ”¾èµ„源锁
        /// </summary>
        /// <param name="resourceId">要释放的资源ID</param>
        public static void ReleaseLock(string resourceId)
        /// <param name="resourceId">资源ID</param>
        /// <param name="force">是否强制释放(超时自动释放时使用)</param>
        public static void ReleaseLock(string resourceId, bool force = false)
        {
            if (_resourceLocks.TryGetValue(resourceId, out object lockObject))
            {
                // ç¡®ä¿é‡Šæ”¾çš„æ˜¯å½“前线程持有的锁
                if (Monitor.IsEntered(lockObject))
                {
                    Monitor.Exit(lockObject);
            if (string.IsNullOrEmpty(resourceId))
                throw new ArgumentNullException(nameof(resourceId));
                    // é‡Šæ”¾é”åŽï¼Œå°è¯•从字典中移除这个锁对象,清理内存。
                    // å¿…须在 Monitor.Exit ä¹‹åŽæ‰§è¡Œã€‚
            if (!_resourceLocks.TryGetValue(resourceId, out var lockMeta))
                return;
            var currentThreadId = Thread.CurrentThread.ManagedThreadId;
            // æ ¡éªŒé‡Šæ”¾åˆæ³•性:仅持有锁的线程或强制释放可执行
            if (!force && lockMeta.HoldingThreadId != currentThreadId)
            {
                // éžæŒæœ‰çº¿ç¨‹å°è¯•释放,抛出异常或返回(根据业务选择)
                throw new InvalidOperationException($"线程[{currentThreadId}]无权释放资源[{resourceId}]的锁(当前持有线程:{lockMeta.HoldingThreadId})");
            }
            // åŒé‡æ ¡éªŒé”çŠ¶æ€
            lock (_globalLocker)
            {
                if (lockMeta.IsReleased)
                    return;
                // ç¡®ä¿é”è¢«å½“前线程持有(强制释放除外)
                if (Monitor.IsEntered(lockMeta.LockObject))
                {
                    try
                    {
                        Monitor.Exit(lockMeta.LockObject);
                    }
                    catch (SynchronizationLockException)
                    {
                        // å·²è¢«é‡Šæ”¾ï¼Œå¿½ç•¥
                        return;
                    }
                }
                // æ ‡è®°é”å·²é‡Šæ”¾
                lockMeta.IsReleased = true;
                lockMeta.HoldingThreadId = -1;
                // å»¶è¿Ÿæ¸…理锁元数据(避免并发创建)
                // ç­‰å¾…1秒后清理,防止刚释放就有新线程抢锁导致重复创建
                _ = Task.Delay(1000).ContinueWith(_ =>
                {
                    lock (_globalLocker)
                    {
                        _resourceLocks.TryRemove(resourceId, out _);
                        // ä»…当锁未被重新持有且已释放时清理
                        if (_resourceLocks.TryGetValue(resourceId, out var meta)
                            && meta.IsReleased
                            && meta.HoldingThreadId == -1)
                        {
                            _resourceLocks.TryRemove(resourceId, out var _resid);
                        }
                    }
                });
            }
        }
        /// <summary>
        /// æ£€æŸ¥èµ„源是否被锁定
        /// </summary>
        public static bool IsLocked(string resourceId)
        {
            if (!_resourceLocks.TryGetValue(resourceId, out var meta))
                return false;
            return !meta.IsReleased && meta.HoldingThreadId != -1;
        }
        public static void TestUsed()
        {
            string orderNo = "testt";
            bool lockAcquired = false;
            try
            {
                // å°è¯•获取锁(自动3-5秒超时)
                lockAcquired = MemoryLockManager.TryAcquireLock(orderNo);
                if (lockAcquired)
                {
                    // æ‰§è¡Œä¸šåŠ¡é€»è¾‘ï¼ˆå¦‚å¤„ç†è®¢å•ï¼‰
                    Console.WriteLine($"线程[{Thread.CurrentThread.ManagedThreadId}]获取锁:{orderNo}");
                    // æ¨¡æ‹Ÿä¸šåŠ¡è€—æ—¶ï¼ˆæµ‹è¯•è¶…æ—¶é‡Šæ”¾ï¼‰
                    // Thread.Sleep(6000);
                }
                else
                {
                    Console.WriteLine($"资源[{orderNo}]被占用,获取锁失败");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"业务处理异常:{ex.Message}");
            }
            finally
            {
                // é‡Šæ”¾é”ï¼ˆä»…当成功获取时)
                if (lockAcquired)
                {
                    try
                    {
                        MemoryLockManager.ReleaseLock(orderNo);
                        Console.WriteLine($"线程[{Thread.CurrentThread.ManagedThreadId}]释放锁:{orderNo}");
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"释放锁失败:{ex.Message}");
                    }
                }
            }