pan
6 天以前 36a26a54dd06a2751c409f50fe1e4dfadb191d56
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_BasicService/InvokeMESService.cs
@@ -5,6 +5,7 @@
using Org.BouncyCastle.Asn1.Ocsp;
using SqlSugar;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
@@ -25,33 +26,10 @@
using WIDESEA_IBasicService;
using WIDESEA_IOutboundService;
using WIDESEA_Model.Models;
using WIDESEA_Model.Models.Outbound;
namespace WIDESEA_BasicService
{
    public class TransferDataDto
    {
        public DateTime TransferTime { get; set; }
        public List<TransferItemDto> Items { get; set; }
    }
    public class TransferItemDto
    {
        public string MaterialCode { get; set; }
        public decimal TotalQuantity { get; set; }
        public List<LocationInfoDto> Locations { get; set; }
    }
    public class LocationInfoDto
    {
        public string LocationCode { get; set; }
        public decimal Quantity { get; set; }
        public List<BarcodeInfoDto> Barcodes { get; set; }
    }
    public class BarcodeInfoDto
    {
        public string Barcode { get; set; }
        public decimal Quantity { get; set; }
    }
    public class InvokeMESService : IInvokeMESService
    {
        private readonly IHttpClientFactory _httpClientFactory;
@@ -63,12 +41,20 @@
        private readonly IRepository<Dt_StockInfoDetail> _stockInfoDetailRepository;
        private readonly IRepository<Dt_StockInfo> _stockInfoRepository;
        private readonly IRepository<Dt_InboundOrder> _inboundOrderRepository;
        private readonly IRepository<Dt_InboundOrderDetail> _inboundOrderDetailRepository;
        private readonly IRepository<Dt_PickingRecord> _pickingRecoreRepository;
        private readonly IMaterialUnitService _materialUnitService;
        private readonly IOutboundOrderService _outboundOrderService;
        private readonly IOutboundOrderDetailService _outboundOrderDetailService;
        private readonly IOutStockLockInfoService _outStockLockInfoService;
        public InvokeMESService(IHttpClientFactory httpClientFactory, ILogger<InvokeMESService> logger, IRepository<Dt_FeedbackToMes> feedbacktomesRepository, IRepository<Dt_StockInfoDetail> stockInfoDetailRepository, IRepository<Dt_StockInfo> stockInfoRepository, IRepository<Dt_InboundOrder> inboundOrderRepository, IOutboundOrderService outboundOrderService, IOutboundOrderDetailService outboundOrderDetailService, IOutStockLockInfoService outStockLockInfoService, IMaterialUnitService materialUnitService, IRepository<Dt_PickingRecord> pickingRecoreRepository)
        private readonly IRepository<Dt_InterfaceLog> _interfacelogRepository;
        // å­˜å‚¨èµ„源ID及其对应的锁对象。使用 ConcurrentDictionary ç¡®ä¿å¯¹å­—典操作本身的线程安全。
        private static readonly ConcurrentDictionary<string, object> _resourceLocks = new ConcurrentDictionary<string, object>();
        // å…¨å±€é™æ€é”ï¼šç”¨äºŽä¿æŠ¤ _resourceLocks å­—典中 GetOrAdd æˆ– TryRemove æ—¶çš„竞争
        private static readonly object _globalLocker = new object();
        public InvokeMESService(IHttpClientFactory httpClientFactory, ILogger<InvokeMESService> logger, IRepository<Dt_FeedbackToMes> feedbacktomesRepository, IRepository<Dt_StockInfoDetail> stockInfoDetailRepository, IRepository<Dt_StockInfo> stockInfoRepository, IRepository<Dt_InboundOrder> inboundOrderRepository, IOutboundOrderService outboundOrderService, IOutboundOrderDetailService outboundOrderDetailService, IOutStockLockInfoService outStockLockInfoService, IMaterialUnitService materialUnitService, IRepository<Dt_PickingRecord> pickingRecoreRepository, IRepository<Dt_InterfaceLog> interfacelogRepository, IRepository<Dt_InboundOrderDetail> inboundOrderDetailRepository)
        {
            _httpClientFactory = httpClientFactory;
            _logger = logger;
@@ -81,6 +67,8 @@
            _outStockLockInfoService = outStockLockInfoService;
            _materialUnitService = materialUnitService;
            _pickingRecoreRepository = pickingRecoreRepository;
            _interfacelogRepository = interfacelogRepository;
            _inboundOrderDetailRepository = inboundOrderDetailRepository;
        }
        /// <summary>
@@ -129,7 +117,7 @@
            _client.DefaultRequestHeaders.Clear();
            _client.DefaultRequestHeaders.Add("Accept", "application/json");
            _logger.LogInformation("InvokeMESService  FeedbackOutbound :  "+ model.orderNo +"  , " + json);
            _logger.LogInformation("InvokeMESService  FeedbackOutbound :  " + model.orderNo + "  , " + json);
            var response = await _client.PostAsync("AldMaterialOutbound/MaterialOutbound", content);
            string body = await response.Content.ReadAsStringAsync();
@@ -248,7 +236,8 @@
            }
        }
        /// <summary>
        /// 
        /// </summary>
@@ -257,141 +246,197 @@
        /// <returns></returns>
        public async Task<WebResponseContent> BatchOrderFeedbackToMes(List<string> orderNos, int inout)
        {
            if (inout == 1)
            try
            {
                foreach (var orderNo in orderNos)
                if (inout == 1)
                {
                    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);
                                _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 1, Remark = "" })
                                                 .Where(it => it.InboundOrderNo == orderNo).ExecuteCommand();
                                var inboundOrder = _inboundOrderRepository.Db.Queryable<Dt_InboundOrder>().First(x => x.InboundOrderNo == orderNo);
                                if (inboundOrder != null)
                                {
                                    if (inboundOrder.OrderType == (int)InOrderTypeEnum.AllocatInbound)//调拨入库
                                    _inboundOrderDetailRepository.Db.Updateable<Dt_InboundOrderDetail>().SetColumns(it => new Dt_InboundOrderDetail { ReturnToMESStatus = 1 })
                                         .Where(it => it.OrderId == inboundOrder.Id).ExecuteCommand();
                                }
                                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 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)//调拨入库
                                        {
                                            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 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>()
                                        };
                                            };
                                        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
                                            var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.InboundOrderRowNo, item.BarcodeUnit, item.WarehouseCode })
                                               .Select(group => new AllocateDtoDetail
                                               {
                                                   Barcode = row.Barcode,
                                                   Qty = row.BarcodeQty,
                                                   BatchNo = row.BatchNo,
                                                   SupplyCode = row.SupplyCode,
                                                   Unit = row.BarcodeUnit
                                               }).ToList()
                                           }).ToList();
                                        allocatefeedmodel.Details = groupedData;
                                                   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();
                                            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
                                        else
                                        {
                                            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 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
                                            var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.SupplyCode, item.BatchNo, item.InboundOrderRowNo, item.BarcodeUnit, item.WarehouseCode })
                                               .Select(group => new FeedbackInboundDetailsModel
                                               {
                                                   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();
                                                   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 feedstockinfos = _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 feedstomes = _feedbacktomesRepository.Db.Queryable<Dt_FeedbackToMes>().Where(x => x.OrderNo == orderNo && x.ReportStatus == 1).Select(o => o.PalletCode).ToList();
                                                var feedunreports = feedstockinfos.Where(x => !feedstomes.Contains(x.PalletCode)).ToList();
                                                if (feedunreports != null && !feedunreports.Any())
                                                {
                                                    _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 1, Remark = "" })
                                                     .Where(it => it.InboundOrderNo == orderNo).ExecuteCommand();
                                                    var feedinboundOrder = _inboundOrderRepository.Db.Queryable<Dt_InboundOrder>().First(x => x.InboundOrderNo == orderNo);
                                                    if (feedinboundOrder != null)
                                                    {
                                                        _inboundOrderDetailRepository.Db.Updateable<Dt_InboundOrderDetail>().SetColumns(it => new Dt_InboundOrderDetail { ReturnToMESStatus = 1 })
                                                             .Where(it => it.OrderId == feedinboundOrder.Id).ExecuteCommand();
                                                    }
                                                }
                                                return WebResponseContent.Instance.Error("回传成功!");
                                            }
                                            else
                                            {
                                                _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 2, Remark = result.message })
                                                .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
                                                _inboundOrderDetailRepository.Db.Updateable<Dt_InboundOrderDetail>().SetColumns(it => new Dt_InboundOrderDetail { ReturnToMESStatus = 2 })
                                              .Where(it => it.OrderId == inboundOrder.Id).ExecuteCommand();
                                                return WebResponseContent.Instance.Error("回传异常!");
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogInformation("InvokeMESService  BatchOrderFeedbackToMes å›žå†™MES失败:  " + ex.Message);
                        return WebResponseContent.Instance.Error(ex.Message);
                    }
                        catch (Exception ex)
                        {
                            _logger.LogInformation("InvokeMESService  BatchOrderFeedbackToMes å›žå†™MES失败:  " + ex.Message);
                            return WebResponseContent.Instance.Error(ex.Message);
                        }
                    }
                }
            }
            else if (inout == 2)
            {
                foreach (var orderNo in orderNos)
                else if (inout == 2)
                {
                    var outboundOrder = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().FirstAsync(x => x.OrderNo == orderNo);
                    if (outboundOrder != null && outboundOrder.IsBatch == 0)
                    foreach (var orderNo in orderNos)
                    {
                        var result = await HandleOutboundOrderToMESCompletion(outboundOrder, orderNo);
                        return result;
                        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 if (outboundOrder != null && outboundOrder.IsBatch == 1)
                    {
                        var result = await HandleOutboundOrderBatchToMESCompletion(outboundOrder, orderNo);
                        return result;
                    }
                }
                //}
                //else
                //{
                //    // æŠ¢é”å¤±è´¥ï¼šè¯´æ˜Žæœ‰å¦ä¸€ä¸ªçº¿ç¨‹ï¼ˆWCS回调或人工操作)正在处理
                //    return WebResponseContent.Instance.Error("WMS正在处理此回传任务,请勿重复操作。");
                //}
            }
            catch (Exception ex)
            {
                _logger.LogInformation("InvokeMESService  BatchOrderFeedbackToMes :  " + ex.Message);
            }
            finally
            {
                // 2. ã€é‡Šæ”¾å†…存锁】无论成功失败,必须释放
                // MemoryLockManager.ReleaseLock(orderNos[0]);
            }
            return WebResponseContent.Instance.OK();
        }
@@ -404,10 +449,10 @@
            try
            {
                // æ ¡éªŒï¼šå·²å›žä¼ ç›´æŽ¥è¿”回错误
                if (outboundOrder.ReturnToMESStatus == 1)
                {
                    return WebResponseContent.Instance.Error("该单已经回传!");
                }
                //if (outboundOrder.ReturnToMESStatus == 1)
                //{
                //    return WebResponseContent.Instance.Error("该单已经回传!");
                //}
                // æŸ¥è¯¢è®¢å•明细(仅查询未回传成功的)
                var orderDetails = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
@@ -428,218 +473,66 @@
                    return WebResponseContent.Instance.Error("没有需要回传的分拣记录");
                var documentNo = UniqueValueGenerator.Generate();
                var feedModel = new FeedbackOutboundRequestModel
                var groups = pickingRecords.GroupBy(x => x.FeedBackMesDocumentNo).ToList();
                foreach (var group in groups)
                {
                    reqCode = Guid.NewGuid().ToString(),
                    reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                    business_type = outboundOrder.BusinessType,
                    factoryArea = outboundOrder.FactoryArea,
                    operationType = 1,
                    Operator = outboundOrder.Operator!=""? outboundOrder.Operator:App.User.UserName,
                    orderNo = outboundOrder.UpperOrderNo,
                    documentsNO = documentNo,
                    status = outboundOrder.OrderStatus,
                    details = new List<FeedbackOutboundDetailsModel>()
                };
                var detailIds = new List<int>();
                // å¡«å……明细和条码信息
                foreach (var detail in orderDetails)
                {
                    // æŸ¥è¯¢è¯¥æ˜Žç»†å¯¹åº”的锁定条码记录
                    var detailPicks = pickingRecords.Where(x => x.OrderNo == orderNo
                                   && detail.Id == x.OrderDetailId).ToList();
                    if (!detailPicks.Any())
                    {
                        continue;
                    }
                    var detailModel = new FeedbackOutboundDetailsModel
                    {
                        materialCode = detail.MaterielCode,
                        lineNo = detail.lineNo,
                        warehouseCode = detail.WarehouseCode,
                        qty = detail.BarcodeQty,
                        currentDeliveryQty = 0,
                        unit = detail.BarcodeUnit,
                        barcodes = new List<WIDESEA_DTO.Outbound.BarcodesModel>()
                    };
                    // å¡«å……条码信息(含单位转换)
                    foreach (var item in detailPicks)
                    List<Dt_PickingRecord> records = group.ToList(); // è¯¥åˆ†ç»„下的所有记录
                    if (string.IsNullOrEmpty(group.Key))
                    {
                        if (item.PickQuantity <= 0)
                        var emptydocumentNo = UniqueValueGenerator.Generate();
                        records.ForEach(x => { x.FeedBackMesDocumentNo = emptydocumentNo; });
                        var result = await _pickingRecoreRepository.Db.Updateable(records).ExecuteCommandAsync();
                        var interfacelog = new Dt_InterfaceLog
                        {
                            continue;
                        }
                        var barModel = new WIDESEA_DTO.Outbound.BarcodesModel
                        {
                            barcode = item.Barcode,
                            supplyCode = item.SupplyCode,
                            batchNo = item.BatchNo,
                            unit = item.BarcodeUnit,
                            qty = item.PickQuantity
                            Content = JsonConvert.SerializeObject(records),
                            DocumentNo = emptydocumentNo,
                            OrderNo = orderNo,
                            OrderType = "2",
                        };
                        _interfacelogRepository.AddData(interfacelog);
                        // å•位不一致时转换
                        if (detail.BarcodeUnit != detail.Unit)
                        if (result > 0)
                        {
                            var convertResult = await _materialUnitService.ConvertAsync(
                                item.MaterielCode, item.PickQuantity, detail.Unit, detail.BarcodeUnit);
                            barModel.unit = convertResult.Unit;
                            barModel.qty = convertResult.Quantity;
                        }
                        else
                        {
                            barModel.qty = item.PickQuantity;
                        }
                        detailModel.currentDeliveryQty += barModel.qty;
                        detailModel.barcodes.Add(barModel);
                    }
                    detailIds.Add(detail.Id);
                    feedModel.details.Add(detailModel);
                }
                            (bool _flowControl, WebResponseContent _value) = await FeedBackBatchToMes(outboundOrder, orderNo, orderDetails, pickingRecords, emptydocumentNo);
                feedModel.details = feedModel.details.GroupBy(item => new { item.materialCode, item.lineNo, item.warehouseCode, item.unit,item.qty  }).Select(group => new FeedbackOutboundDetailsModel
                {
                    materialCode = group.Key.materialCode,
                    lineNo = group.Key.lineNo,
                    warehouseCode = group.Key.warehouseCode,
                    qty = group.Key.qty,
                    currentDeliveryQty = group.Sum(x => x.currentDeliveryQty),
                    unit = group.Key.unit,
                    barcodes = group.SelectMany(x => x.barcodes.GroupBy(o => new { o.barcode, o.supplyCode, o.batchNo, o.unit }).Select(row => new WIDESEA_DTO.Outbound.BarcodesModel
                    {
                        barcode = row.Key.barcode,
                        supplyCode = row.Key.supplyCode,
                        batchNo = row.Key.batchNo,
                        unit = row.Key.unit,
                        qty = row.Sum(y => y.qty)
                    })).ToList()
                }).ToList();
                            return _value;
                var allCompleted = true;
                // ç­›é€‰å¾…回传的明细(ReturnToMESStatus=0)
                var pendingDetails = orderDetails.Where(x => x.ReturnToMESStatus == 0).ToList();
                foreach (var detail in pendingDetails)
                {
                    if (detail.OverOutQuantity < detail.NeedOutQuantity)
                    {
                        allCompleted = false;
                    }
                }
                // å­˜åœ¨å›žä¼ å¤±è´¥çš„æ˜Žç»†ï¼ˆReturnToMESStatus=2),标记未完成
                if (orderDetails.Any(x => x.ReturnToMESStatus == 2))
                {
                    allCompleted = false;
                }
                //  æ›´æ–°è®¢å•状态
                int newStatus = allCompleted ? (int)OutOrderStatusEnum.出库完成 : (int)OutOrderStatusEnum.出库中;
                if (outboundOrder.OrderStatus != newStatus)
                {
                    int updateCount = await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                        .SetColumns(x => x.OrderStatus == newStatus)
                        .Where(x => x.OrderNo == orderNo)
                        .ExecuteCommandAsync();
                    if (updateCount <= 0)
                    {
                        _logger.LogWarning($"更新出库单状态失败 - OrderNo: {orderNo}, ç›®æ ‡çŠ¶æ€: {newStatus}");
                    }
                }
                // è°ƒç”¨MES回传接口
                var mesResult = await FeedbackOutbound(feedModel);
                if (mesResult == null || mesResult.code != 200)
                {
                    // æ›´æ–°æ˜Žç»†ä¸ºå›žä¼ å¤±è´¥ï¼ˆReturnToMESStatus=2)
                    await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                        .SetColumns(it => new Dt_OutboundOrderDetail
                        {
                            ReturnToMESStatus = 2,
                            documentsNO = documentNo,
                        })
                        .Where(x => detailIds.Contains(x.Id))
                        .ExecuteCommandAsync();
                    return WebResponseContent.Instance.Error($"回传MES失败");
                }
                foreach (var record in pickingRecords.Where(x => detailIds.Contains(x.OrderDetailId)).ToList())
                {
                    record.ReturnToMESStatus = 1;
                }
                var updates = pickingRecords.Where(x => detailIds.Contains(x.OrderDetailId)).ToList();
                updates.ForEach(x =>
                {
                    x.ReturnToMESStatus = 1;
                });
                await _pickingRecoreRepository.Db.Updateable(updates).ExecuteCommandAsync();
                if (allCompleted)
                {
                    //MES回传成功:更新明细为回传成功状态
                    await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                        .SetColumns(it => new Dt_OutboundOrderDetail
                        {
                            ReturnToMESStatus = 1,
                            documentsNO = documentNo,
                        })
                        .Where(x => detailIds.Contains(x.Id))
                        .ExecuteCommandAsync();
                }
                // æ ¡éªŒæ˜¯å¦æ‰€æœ‰æ˜Žç»†éƒ½å®Œæˆï¼Œæ›´æ–°è®¢å•最终状态
                if (allCompleted && newStatus == (int)OutOrderStatusEnum.出库完成)
                {
                    await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                        .SetColumns(x => new Dt_OutboundOrder
                        {
                            ReturnToMESStatus = 1,
                            OrderStatus = newStatus
                        })
                        .Where(x => x.OrderNo == orderNo)
                        .ExecuteCommandAsync();
                }
                else
                {
                    // äºŒæ¬¡æ ¡éªŒæ˜¯å¦æ‰€æœ‰æœªå›žä¼ æ˜Žç»†éƒ½å·²å®Œæˆ
                    var dbOrderDetails = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                        .LeftJoin<Dt_OutboundOrder>((o, item) => o.OrderId == item.Id)
                        .Where((o, item) => item.OrderNo == orderNo && item.ReturnToMESStatus != 1)
                        .Select((o, item) => o)
                        .ToListAsync();
                    var secAllCompleted = true;
                    foreach (var detail in dbOrderDetails.Where(x => x.ReturnToMESStatus == 0).ToList())
                    {
                        if (detail.OverOutQuantity < detail.NeedOutQuantity)
                        {
                            secAllCompleted = false;
                            break;
                        }
                    }
                    if (secAllCompleted)
                    else
                    {
                        await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                            .SetColumns(it => new Dt_OutboundOrder
                        var ilog = _interfacelogRepository.QueryFirst(x => x.DocumentNo == group.Key);
                        if (ilog == null)
                        {
                            var interfacelog = new Dt_InterfaceLog
                            {
                                ReturnToMESStatus = 1,
                                OrderStatus = OutOrderStatusEnum.出库完成.ObjToInt(),
                            })
                            .Where(x => x.OrderNo == orderNo)
                            .ExecuteCommandAsync();
                                Content = JsonConvert.SerializeObject(records),
                                DocumentNo = group.Key,
                                OrderNo = orderNo,
                                OrderType = "2",
                            };
                            _interfacelogRepository.AddData(interfacelog);
                        }
                        (bool _flowControl, WebResponseContent _value) = await FeedBackBatchToMes(outboundOrder, orderNo, orderDetails, pickingRecords, group.Key);
                        return _value;
                    }
                }
                //var documentNo = UniqueValueGenerator.Generate();
                //(bool flowControl, WebResponseContent value) = await FeedBackBatchToMes(outboundOrder, orderNo, orderDetails, pickingRecords, documentNo);
                //if (!flowControl)
                //{
                //    return value;
                //}
                // å›žä¼ æˆåŠŸçš„æœ€ç»ˆè¿”å›ž
                response = WebResponseContent.Instance.OK($"回传MES成功,单据号:{documentNo}");
                response = WebResponseContent.Instance.OK($"回传MES成功,单据号:{orderNo}");
            }
            catch (Exception ex)
            {
@@ -652,6 +545,231 @@
            }
            return response;
        }
        private async Task<(bool flowControl, WebResponseContent value)> FeedBackBatchToMes(Dt_OutboundOrder outboundOrder, string orderNo, List<Dt_OutboundOrderDetail> orderDetails, List<Dt_PickingRecord> pickingRecords, string documentNo)
        {
            var feedModel = new FeedbackOutboundRequestModel
            {
                reqCode = Guid.NewGuid().ToString(),
                reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                business_type = outboundOrder.BusinessType,
                factoryArea = outboundOrder.FactoryArea,
                operationType = 1,
                Operator = outboundOrder.Operator != "" ? outboundOrder.Operator : App.User.UserName,
                orderNo = outboundOrder.UpperOrderNo,
                documentsNO = documentNo,
                status = outboundOrder.OrderStatus,
                details = new List<FeedbackOutboundDetailsModel>()
            };
            var detailIds = new List<int>();
            // å¡«å……明细和条码信息
            foreach (var detail in orderDetails)
            {
                // æŸ¥è¯¢è¯¥æ˜Žç»†å¯¹åº”的锁定条码记录
                var detailPicks = pickingRecords.Where(x => x.OrderNo == orderNo
                               && detail.Id == x.OrderDetailId).ToList();
                if (!detailPicks.Any())
                {
                    continue;
                }
                var detailModel = new FeedbackOutboundDetailsModel
                {
                    materialCode = detail.MaterielCode,
                    lineNo = detail.lineNo,
                    warehouseCode = detail.WarehouseCode,
                    qty = detail.BarcodeQty,
                    currentDeliveryQty = 0,
                    unit = detail.BarcodeUnit,
                    barcodes = new List<WIDESEA_DTO.Outbound.BarcodesModel>()
                };
                // å¡«å……条码信息(含单位转换)
                foreach (var item in detailPicks)
                {
                    if (item.PickQuantity <= 0)
                    {
                        continue;
                    }
                    var barModel = new WIDESEA_DTO.Outbound.BarcodesModel
                    {
                        barcode = item.Barcode,
                        supplyCode = item.SupplyCode,
                        batchNo = item.BatchNo,
                        unit = item.BarcodeUnit,
                        qty = item.PickQuantity
                    };
                    // å•位不一致时转换
                    if (detail.BarcodeUnit != detail.Unit)
                    {
                        var convertResult = await _materialUnitService.ConvertAsync(
                            item.MaterielCode, item.PickQuantity, detail.Unit, detail.BarcodeUnit);
                        barModel.unit = convertResult.Unit;
                        barModel.qty = convertResult.Quantity;
                    }
                    else
                    {
                        barModel.qty = item.PickQuantity;
                    }
                    detailModel.currentDeliveryQty += barModel.qty;
                    detailModel.barcodes.Add(barModel);
                }
                detailIds.Add(detail.Id);
                feedModel.details.Add(detailModel);
            }
            feedModel.details = feedModel.details.GroupBy(item => new { item.materialCode, item.lineNo, item.warehouseCode, item.unit, item.qty }).Select(group => new FeedbackOutboundDetailsModel
            {
                materialCode = group.Key.materialCode,
                lineNo = group.Key.lineNo,
                warehouseCode = group.Key.warehouseCode,
                qty = group.Key.qty,
                currentDeliveryQty = group.Sum(x => x.currentDeliveryQty),
                unit = group.Key.unit,
                barcodes = group.SelectMany(x => x.barcodes.GroupBy(o => new { o.barcode, o.supplyCode, o.batchNo, o.unit }).Select(row => new WIDESEA_DTO.Outbound.BarcodesModel
                {
                    barcode = row.Key.barcode,
                    supplyCode = row.Key.supplyCode,
                    batchNo = row.Key.batchNo,
                    unit = row.Key.unit,
                    qty = row.Sum(y => y.qty)
                })).ToList()
            }).ToList();
            var allCompleted = true;
            // ç­›é€‰å¾…回传的明细(ReturnToMESStatus=0)
            var pendingDetails = orderDetails.Where(x => x.ReturnToMESStatus == 0).ToList();
            foreach (var detail in pendingDetails)
            {
                if (detail.OverOutQuantity < detail.NeedOutQuantity)
                {
                    allCompleted = false;
                }
            }
            // å­˜åœ¨å›žä¼ å¤±è´¥çš„æ˜Žç»†ï¼ˆReturnToMESStatus=2),标记未完成
            if (orderDetails.Any(x => x.ReturnToMESStatus == 2))
            {
                allCompleted = false;
            }
            //  æ›´æ–°è®¢å•状态
            int newStatus = allCompleted ? (int)OutOrderStatusEnum.出库完成 : (int)OutOrderStatusEnum.出库中;
            if (allCompleted && outboundOrder.OrderStatus != newStatus)
            {
                int updateCount = await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                    .SetColumns(x => x.OrderStatus == newStatus)
                    .Where(x => x.OrderNo == orderNo)
                    .ExecuteCommandAsync();
                if (updateCount <= 0)
                {
                    _logger.LogWarning($"更新出库单状态失败 - OrderNo: {orderNo}, ç›®æ ‡çŠ¶æ€: {newStatus}");
                }
            }
            // è°ƒç”¨MES回传接口
            var mesResult = await FeedbackOutbound(feedModel);
            if (mesResult == null || mesResult.code != 200)
            {
                var messages = mesResult?.message ?? "";
                await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                   .SetColumns(x => new Dt_OutboundOrder
                   {
                       ReturnToMESStatus = 2,
                       Remark = messages,
                   })
                   .Where(x => x.OrderNo == orderNo)
                   .ExecuteCommandAsync();
                // æ›´æ–°æ˜Žç»†ä¸ºå›žä¼ å¤±è´¥ï¼ˆReturnToMESStatus=2)
                await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                    .SetColumns(it => new Dt_OutboundOrderDetail
                    {
                        ReturnToMESStatus = 2,
                        documentsNO = documentNo,
                    })
                    .Where(x => detailIds.Contains(x.Id))
                    .ExecuteCommandAsync();
                return (flowControl: false, value: WebResponseContent.Instance.Error($"回传MES失败"));
            }
            var updates = pickingRecords.Where(x => detailIds.Contains(x.OrderDetailId)).ToList();
            updates.ForEach(x =>
            {
                x.ReturnToMESStatus = 1;
            });
            await _pickingRecoreRepository.Db.Updateable(updates).ExecuteCommandAsync();
            await _interfacelogRepository.Db.Updateable<Dt_InterfaceLog>()
                                             .SetColumns(x => x.ReturnToMESStatus == 1)
                                             .Where(x => x.OrderNo == orderNo)
                                             .ExecuteCommandAsync();
            if (allCompleted)
            {
                //MES回传成功:更新明细为回传成功状态
                await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                    .SetColumns(it => new Dt_OutboundOrderDetail
                    {
                        ReturnToMESStatus = 1,
                        documentsNO = documentNo,
                    })
                    .Where(x => detailIds.Contains(x.Id))
                    .ExecuteCommandAsync();
            }
            // æ ¡éªŒæ˜¯å¦æ‰€æœ‰æ˜Žç»†éƒ½å®Œæˆï¼Œæ›´æ–°è®¢å•最终状态
            if (allCompleted && newStatus == (int)OutOrderStatusEnum.出库完成)
            {
                await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                    .SetColumns(x => new Dt_OutboundOrder
                    {
                        ReturnToMESStatus = 1,
                        Remark = "",
                        OrderStatus = newStatus
                    })
                    .Where(x => x.OrderNo == orderNo)
                    .ExecuteCommandAsync();
            }
            else
            {
                // äºŒæ¬¡æ ¡éªŒæ˜¯å¦æ‰€æœ‰æœªå›žä¼ æ˜Žç»†éƒ½å·²å®Œæˆ
                var dbOrderDetails = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .LeftJoin<Dt_OutboundOrder>((o, item) => o.OrderId == item.Id)
                    .Where((o, item) => item.OrderNo == orderNo && item.ReturnToMESStatus != 1)
                    .Select((o, item) => o)
                    .ToListAsync();
                var secAllCompleted = true;
                foreach (var detail in dbOrderDetails.Where(x => x.ReturnToMESStatus == 0).ToList())
                {
                    if (detail.OverOutQuantity < detail.NeedOutQuantity)
                    {
                        secAllCompleted = false;
                        break;
                    }
                }
                if (secAllCompleted)
                {
                    await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                        .SetColumns(it => new Dt_OutboundOrder
                        {
                            ReturnToMESStatus = 1,
                            Remark = "",
                            OrderStatus = OutOrderStatusEnum.出库完成.ObjToInt(),
                        })
                        .Where(x => x.OrderNo == orderNo)
                        .ExecuteCommandAsync();
                }
            }
            return (flowControl: true, value: WebResponseContent.Instance.OK($"回传MES成功,单据号:{orderNo}"));
        }
        private async Task<WebResponseContent> HandleOutboundOrderToMESCompletion(Dt_OutboundOrder outboundOrder, string orderNo)
@@ -773,7 +891,7 @@
                            .ExecuteCommandAsync();
                        await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                            .SetColumns(x => x.ReturnToMESStatus == 1)
                            .SetColumns(it => new Dt_OutboundOrder { ReturnToMESStatus = 1, Remark = "" })
                            .Where(x => x.OrderNo == orderNo)
                            .ExecuteCommandAsync();
@@ -788,6 +906,17 @@
                    {
                        var errorMsg = $"OrderNo: {orderNo} å›žä¼ MES失败,错误码:{result.code},错误信息:{result.message ?? "无"}";
                        _logger.LogError(errorMsg);
                        await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                          .SetColumns(x => x.ReturnToMESStatus == 2)
                          .Where(x => x.OrderId == outboundOrder.Id)
                          .ExecuteCommandAsync();
                        await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                            .SetColumns(it => new Dt_OutboundOrder { ReturnToMESStatus = 2, Remark = result.message })
                            .Where(x => x.OrderNo == orderNo)
                            .ExecuteCommandAsync();
                        return WebResponseContent.Instance.Error(errorMsg);
                    }
                }
@@ -817,12 +946,212 @@
            var now = DateTime.Now;
            string datePart = now.ToString("MMdd");
            long ticksPart = now.Ticks;
            // æ‹¼æŽ¥ï¼šè®¡æ•°å™¨è¡¥0为3位(避免位数不一致)
            return $"{datePart}{ticksPart}";
        }
        public static string GenerateCount()
        {
            var now = DateTime.Now;
            string datePart = now.ToString("yyyyMMddHHmmss");
            // åŽŸå­é€’å¢žè®¡æ•°å™¨ï¼ˆå–æ¨¡1000,确保计数器仅3位,控制长度)
            long counterPart = Interlocked.Increment(ref _counter) % 1000;
            // æ‹¼æŽ¥ï¼šè®¡æ•°å™¨è¡¥0为3位(避免位数不一致)
            return $"{datePart}{ticksPart}";
            return $"{datePart}{counterPart:D3}";
        }
    }
    public static class MemoryLockManager
    {
        // å­˜å‚¨èµ„源锁的元数据(锁对象、持有线程、占用时间、超时时间)
        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; } // æ˜¯å¦å·²é‡Šæ”¾
        }
        // èµ„源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>
        /// å°è¯•锁定资源(带超时自动释放)
        /// </summary>
        /// <param name="resourceId">资源ID</param>
        /// <param name="timeoutSeconds">超时时间(默认3-5秒随机)</param>
        /// <returns>是否成功获取锁</returns>
        public static bool TryAcquireLock(string resourceId, int? timeoutSeconds = 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)
            {
                // èŽ·å–æˆ–åˆ›å»ºé”å…ƒæ•°æ®
                lockMeta = _resourceLocks.GetOrAdd(resourceId, key => new LockMetadata());
                // é˜²æ­¢é‡å¤èŽ·å–ï¼ˆå½“å‰çº¿ç¨‹å·²æŒæœ‰é”ï¼‰
                if (lockMeta.HoldingThreadId == currentThreadId && !lockMeta.IsReleased)
                    return true; // çº¿ç¨‹å¯é‡å…¥
            }
            // å°è¯•获取锁(非阻塞)
            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>
        /// é‡Šæ”¾èµ„源锁
        /// </summary>
        /// <param name="resourceId">资源ID</param>
        /// <param name="force">是否强制释放(超时自动释放时使用)</param>
        public static void ReleaseLock(string resourceId, bool force = false)
        {
            if (string.IsNullOrEmpty(resourceId))
                throw new ArgumentNullException(nameof(resourceId));
            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)
                    {
                        // ä»…当锁未被重新持有且已释放时清理
                        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}");
                    }
                }
            }
        }
    }
}