pan
2025-12-04 db215dc033392d843cec846f0a8f9dbff2578c6a
ÏîÄ¿´úÂë/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;
@@ -28,30 +29,6 @@
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;
@@ -68,6 +45,12 @@
        private readonly IOutboundOrderService _outboundOrderService;
        private readonly IOutboundOrderDetailService _outboundOrderDetailService;
        private readonly IOutStockLockInfoService _outStockLockInfoService;
        // å­˜å‚¨èµ„源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)
        {
            _httpClientFactory = httpClientFactory;
@@ -129,7 +112,7 @@
            _client.DefaultRequestHeaders.Clear();
            _client.DefaultRequestHeaders.Add("Accept", "application/json");
            _logger.LogInformation("InvokeMESService  FeedbackOutbound :  " + json);
            _logger.LogInformation("InvokeMESService  FeedbackOutbound :  " + model.orderNo + "  , " + json);
            var response = await _client.PostAsync("AldMaterialOutbound/MaterialOutbound", content);
            string body = await response.Content.ReadAsStringAsync();
@@ -139,7 +122,7 @@
                throw new HttpRequestException(body);
            }
            _logger.LogInformation("InvokeMESService  FeedbackOutbound  body:  " + body);
            return JsonConvert.DeserializeObject<ResponseModel>(body);
        }
@@ -248,57 +231,7 @@
            }
        }
        /// <summary>
        /// å‡†å¤‡å›žä¼ æ•°æ®
        /// </summary>
        private TransferDataDto PrepareTransferData(List<Dt_PickingRecord> pickingRecords)
        {
            var transferData = new TransferDataDto
            {
                TransferTime = DateTime.Now,
                Items = new List<TransferItemDto>()
            };
            // æŒ‰ç‰©æ–™åˆ†ç»„
            var materialGroups = pickingRecords
                .GroupBy(x => x.MaterielCode)
                .ToList();
            foreach (var group in materialGroups)
            {
                var item = new TransferItemDto
                {
                    MaterialCode = group.Key,
                    TotalQuantity = group.Sum(x => x.PickQuantity),
                    Locations = new List<LocationInfoDto>()
                };
                // æŒ‰è´§ä½åˆ†ç»„
                var locationGroups = group
                    .GroupBy(x => x.LocationCode)
                    .ToList();
                foreach (var locationGroup in locationGroups)
                {
                    var location = new LocationInfoDto
                    {
                        LocationCode = locationGroup.Key,
                        Quantity = locationGroup.Sum(x => x.PickQuantity),
                        Barcodes = locationGroup.Select(x => new BarcodeInfoDto
                        {
                            Barcode = x.Barcode,
                            Quantity = x.PickQuantity
                        }).ToList()
                    };
                    item.Locations.Add(location);
                }
                transferData.Items.Add(item);
            }
            return transferData;
        }
        /// <summary>
        /// 
@@ -308,141 +241,159 @@
        /// <returns></returns>
        public async Task<WebResponseContent> BatchOrderFeedbackToMes(List<string> orderNos, int inout)
        {
            if (inout == 1)
            // 1. ã€å†…存锁抢占】
            if (MemoryLockManager.TryAcquireLock(orderNos[0]))
            {
                foreach (var orderNo in orderNos)
                try
                {
                    try
                    if (inout == 1)
                    {
                        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())
                        foreach (var orderNo in orderNos)
                        {
                            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())
                            try
                            {
                                var inboundOrder = _inboundOrderRepository.Db.Queryable<Dt_InboundOrder>().First(x => x.InboundOrderNo == lists.FirstOrDefault().OrderNo);
                                if (inboundOrder != null)
                                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())
                                {
                                    if (inboundOrder.OrderType == (int)InOrderTypeEnum.AllocatInbound)//调拨入库
                                    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 allocate = SqlSugarHelper.DbWMS.Queryable<Dt_AllocateOrder>().Where(x => x.OrderNo == inboundOrder.InboundOrderNo).First();
                                        var allocatefeedmodel = new AllocateDto
                                        var inboundOrder = _inboundOrderRepository.Db.Queryable<Dt_InboundOrder>().First(x => x.InboundOrderNo == lists.FirstOrDefault().OrderNo);
                                        if (inboundOrder != null)
                                        {
                                            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>()
                                            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>()
                                        };
                                                };
                                        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);
                            }
                        }
                    }
                    catch (Exception ex)
                    else if (inout == 2)
                    {
                        _logger.LogInformation("InvokeMESService  BatchOrderFeedbackToMes å›žå†™MES失败:  " + ex.Message);
                        return WebResponseContent.Instance.Error(ex.Message);
                    }
                        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;
                            }
                        }
                    }
                }
                finally
                {
                    // 2. ã€é‡Šæ”¾å†…存锁】无论成功失败,必须释放
                    MemoryLockManager.ReleaseLock(orderNos[0]);
                }
            }
            else if (inout == 2)
            else
            {
                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;
                    }
                }
                // æŠ¢é”å¤±è´¥ï¼šè¯´æ˜Žæœ‰å¦ä¸€ä¸ªçº¿ç¨‹ï¼ˆWCS回调或人工操作)正在处理
                return WebResponseContent.Instance.OK("WMS正在处理此回传任务,请勿重复操作。");
            }
            return WebResponseContent.Instance.OK();
        }
@@ -489,7 +440,7 @@
                    business_type = outboundOrder.BusinessType,
                    factoryArea = outboundOrder.FactoryArea,
                    operationType = 1,
                    Operator = outboundOrder.Operator,
                    Operator = outboundOrder.Operator != "" ? outboundOrder.Operator : App.User.UserName,
                    orderNo = outboundOrder.UpperOrderNo,
                    documentsNO = documentNo,
                    status = outboundOrder.OrderStatus,
@@ -544,7 +495,7 @@
                        else
                        {
                            barModel.qty = item.PickQuantity;
                        }
                        }
                        detailModel.currentDeliveryQty += barModel.qty;
                        detailModel.barcodes.Add(barModel);
                    }
@@ -552,7 +503,7 @@
                    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
                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,
@@ -828,6 +779,11 @@
                            .Where(x => x.OrderNo == orderNo)
                            .ExecuteCommandAsync();
                        await _pickingRecoreRepository.Db.Updateable<Dt_PickingRecord>()
                                 .SetColumns(x => x.ReturnToMESStatus == 1)
                            .Where(x => x.OrderNo == orderNo)
                            .ExecuteCommandAsync();
                        return WebResponseContent.Instance.OK("回传MES成功");
                    }
                    else
@@ -861,14 +817,78 @@
        public static string Generate()
        {
            var now = DateTime.Now;
            string datePart = now.ToString("yyyyMMdd");
            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}{counterPart:D3}";
            return $"{datePart}{counterPart:D3}";
        }
    }
    public static class MemoryLockManager
    {
        // å­˜å‚¨èµ„源ID及其对应的锁对象。使用 ConcurrentDictionary ç¡®ä¿å¯¹å­—典操作本身的线程安全。
        private static readonly ConcurrentDictionary<string, object> _resourceLocks = new ConcurrentDictionary<string, object>();
        // å…¨å±€é™æ€é”ï¼šç”¨äºŽä¿æŠ¤ _resourceLocks å­—典中 GetOrAdd æˆ– TryRemove æ—¶çš„竞争
        private static readonly object _globalLocker = new object();
        /// <summary>
        /// å°è¯•锁定一个资源ID。
        /// </summary>
        /// <param name="resourceId">要锁定的资源ID(例如 InboundRecord ID)</param>
        /// <returns>是否成功获取锁</returns>
        public static bool TryAcquireLock(string resourceId)
        {
            object lockObject = null;
            // æ ¸å¿ƒæ€è·¯ï¼šä¸ºæ¯ä¸ªèµ„源创建一个唯一的锁对象
            lock (_globalLocker)
            {
                // å¦‚果资源ID不在字典中,则添加一个新的锁对象
                // å¦åˆ™ï¼Œä½¿ç”¨å·²å­˜åœ¨çš„锁对象
                lockObject = _resourceLocks.GetOrAdd(resourceId, new object());
            }
            // å°è¯•获取资源特定的锁
            // ä½¿ç”¨ Monitor.TryEnter é¿å…é˜»å¡žï¼Œå¹¶å®žçŽ°éžé˜»å¡žçš„æŠ¢é”
            return Monitor.TryEnter(lockObject);
        }
        /// <summary>
        /// é‡Šæ”¾èµ„源ID的锁定。
        /// </summary>
        /// <param name="resourceId">要释放的资源ID</param>
        public static void ReleaseLock(string resourceId)
        {
            if (_resourceLocks.TryGetValue(resourceId, out object lockObject))
            {
                // ç¡®ä¿é‡Šæ”¾çš„æ˜¯å½“前线程持有的锁
                if (Monitor.IsEntered(lockObject))
                {
                    Monitor.Exit(lockObject);
                    // é‡Šæ”¾é”åŽï¼Œå°è¯•从字典中移除这个锁对象,清理内存。
                    // å¿…须在 Monitor.Exit ä¹‹åŽæ‰§è¡Œã€‚
                    lock (_globalLocker)
                    {
                        _resourceLocks.TryRemove(resourceId, out _);
                    }
                }
            }
        }
    }
}