heshaofeng
10 天以前 48710581fbbdd40eb3a743d91fa04e81531ba2ab
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_BasicService/InvokeMESService.cs
@@ -26,6 +26,7 @@
using WIDESEA_IBasicService;
using WIDESEA_IOutboundService;
using WIDESEA_Model.Models;
using WIDESEA_Model.Models.Outbound;
namespace WIDESEA_BasicService
{
@@ -45,13 +46,14 @@
        private readonly IOutboundOrderService _outboundOrderService;
        private readonly IOutboundOrderDetailService _outboundOrderDetailService;
        private readonly IOutStockLockInfoService _outStockLockInfoService;
        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)
        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)
        {
            _httpClientFactory = httpClientFactory;
            _logger = logger;
@@ -64,6 +66,7 @@
            _outStockLockInfoService = outStockLockInfoService;
            _materialUnitService = materialUnitService;
            _pickingRecoreRepository = pickingRecoreRepository;
            _interfacelogRepository = interfacelogRepository;
        }
        /// <summary>
@@ -393,7 +396,7 @@
            {
                // æŠ¢é”å¤±è´¥ï¼šè¯´æ˜Žæœ‰å¦ä¸€ä¸ªçº¿ç¨‹ï¼ˆWCS回调或人工操作)正在处理
                return WebResponseContent.Instance.OK("WMS正在处理此回传任务,请勿重复操作。");
                return WebResponseContent.Instance.Error("WMS正在处理此回传任务,请勿重复操作。");
            }
            return WebResponseContent.Instance.OK();
        }
@@ -430,218 +433,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)
            {
@@ -656,6 +507,223 @@
            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 (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 (flowControl: false, value: 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();
            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,
                        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,
                            OrderStatus = OutOrderStatusEnum.出库完成.ObjToInt(),
                        })
                        .Where(x => x.OrderNo == orderNo)
                        .ExecuteCommandAsync();
                }
            }
            return (flowControl: true, value: null);
        }
        private async Task<WebResponseContent> HandleOutboundOrderToMESCompletion(Dt_OutboundOrder outboundOrder, string orderNo)
        {
            // å‰ç½®å‚数校验:空值直接返回错误