| | |
| | | using Org.BouncyCastle.Asn1.Ocsp; |
| | | using SqlSugar; |
| | | using System; |
| | | using System.Collections.Concurrent; |
| | | using System.Collections.Generic; |
| | | using System.Linq; |
| | | using System.Net; |
| | |
| | | using WIDESEA_IBasicService; |
| | | using WIDESEA_IOutboundService; |
| | | using WIDESEA_Model.Models; |
| | | using WIDESEA_Model.Models.Outbound; |
| | | |
| | | namespace WIDESEA_BasicService |
| | | { |
| | |
| | | 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) |
| | | { |
| | | _httpClientFactory = httpClientFactory; |
| | | _logger = logger; |
| | |
| | | _outStockLockInfoService = outStockLockInfoService; |
| | | _materialUnitService = materialUnitService; |
| | | _pickingRecoreRepository = pickingRecoreRepository; |
| | | _interfacelogRepository = interfacelogRepository; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | } |
| | | |
| | | |
| | | |
| | | /// <summary> |
| | | /// |
| | | /// </summary> |
| | |
| | | /// <param name="inout">å
¥åºä¼ 1 åºåºä¼ 2</param> |
| | | /// <returns></returns> |
| | | public async Task<WebResponseContent> BatchOrderFeedbackToMes(List<string> orderNos, int inout) |
| | | { |
| | | // 1. ãå
å鿢å ã |
| | | if (MemoryLockManager.TryAcquireLock(orderNos[0])) |
| | | { |
| | | try |
| | | { |
| | | if (inout == 1) |
| | | { |
| | |
| | | } |
| | | |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | // 2. ãéæ¾å
åéãæ è®ºæå失败ï¼å¿
须鿾 |
| | | MemoryLockManager.ReleaseLock(orderNos[0]); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // æ¢é失败ï¼è¯´ææå¦ä¸ä¸ªçº¿ç¨ï¼WCSåè°æäººå·¥æä½ï¼æ£å¨å¤ç |
| | | |
| | | return WebResponseContent.Instance.Error("WMSæ£å¨å¤çæ¤åä¼ ä»»å¡ï¼è¯·å¿é夿ä½ã"); |
| | | } |
| | | return WebResponseContent.Instance.OK(); |
| | | } |
| | | |
| | |
| | | return WebResponseContent.Instance.Error("没æéè¦åä¼ çåæ£è®°å½"); |
| | | |
| | | |
| | | var documentNo = UniqueValueGenerator.Generate(); |
| | | var groups = pickingRecords.GroupBy(x => x.FeedBackMesDocumentNo).ToList(); |
| | | foreach (var group in groups) |
| | | { |
| | | |
| | | List<Dt_PickingRecord> records = group.ToList(); // 该åç»ä¸çææè®°å½ |
| | | if (string.IsNullOrEmpty(group.Key)) |
| | | { |
| | | var emptydocumentNo = UniqueValueGenerator.Generate(); |
| | | records.ForEach(x => { x.FeedBackMesDocumentNo = emptydocumentNo; }); |
| | | var result = await _pickingRecoreRepository.Db.Updateable(records).ExecuteCommandAsync(); |
| | | |
| | | var interfacelog = new Dt_InterfaceLog |
| | | { |
| | | Content = JsonConvert.SerializeObject(records), |
| | | DocumentNo = emptydocumentNo, |
| | | OrderNo = orderNo, |
| | | OrderType = "2", |
| | | }; |
| | | _interfacelogRepository.AddData(interfacelog); |
| | | |
| | | if (result > 0) |
| | | { |
| | | (bool _flowControl, WebResponseContent _value) = await FeedBackBatchToMes(outboundOrder, orderNo, orderDetails, pickingRecords, emptydocumentNo); |
| | | |
| | | return _value; |
| | | |
| | | } |
| | | } |
| | | else |
| | | { |
| | | var ilog = _interfacelogRepository.QueryFirst(x => x.DocumentNo == group.Key); |
| | | if (ilog == null) |
| | | { |
| | | var interfacelog = new Dt_InterfaceLog |
| | | { |
| | | 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æåï¼åæ®å·ï¼{orderNo}"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | // å
¨å±å¼å¸¸æè·ï¼è®°å½è¯¦ç»æ¥å¿ + è¿åé误 |
| | | string errorMsg = $"å¤çåºåºååä¼ MESæ¶åçå¼å¸¸ - OrderNo: {orderNo}, Error: {ex.Message}, StackTrace: {ex.StackTrace}"; |
| | | _logger.LogError(ex, errorMsg); // è®°å½å¸¦å¼å¸¸å æ çæ¥å¿ |
| | | |
| | | // å¼å¸¸è¿åï¼ç»å端çå好æç¤ºï¼éèå æ ä¿¡æ¯ï¼ |
| | | response = WebResponseContent.Instance.Error("å¤çåä¼ MESæ¶åçå¼å¸¸ï¼è¯·è系管çå"); |
| | | } |
| | | |
| | | 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(), |
| | |
| | | .Where(x => detailIds.Contains(x.Id)) |
| | | .ExecuteCommandAsync(); |
| | | |
| | | return WebResponseContent.Instance.Error($"åä¼ MES失败"); |
| | | return (flowControl: false, value: WebResponseContent.Instance.Error($"åä¼ MES失败")); |
| | | } |
| | | foreach (var record in pickingRecords.Where(x => detailIds.Contains(x.OrderDetailId)).ToList()) |
| | | { |
| | |
| | | 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åä¼ æåï¼æ´æ°æç»ä¸ºåä¼ æåç¶æ |
| | |
| | | } |
| | | } |
| | | |
| | | // åä¼ æåçæç»è¿å |
| | | response = WebResponseContent.Instance.OK($"åä¼ MESæåï¼åæ®å·ï¼{documentNo}"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | // å
¨å±å¼å¸¸æè·ï¼è®°å½è¯¦ç»æ¥å¿ + è¿åé误 |
| | | string errorMsg = $"å¤çåºåºååä¼ MESæ¶åçå¼å¸¸ - OrderNo: {orderNo}, Error: {ex.Message}, StackTrace: {ex.StackTrace}"; |
| | | _logger.LogError(ex, errorMsg); // è®°å½å¸¦å¼å¸¸å æ çæ¥å¿ |
| | | |
| | | // å¼å¸¸è¿åï¼ç»å端çå好æç¤ºï¼éèå æ ä¿¡æ¯ï¼ |
| | | response = WebResponseContent.Instance.Error("å¤çåä¼ MESæ¶åçå¼å¸¸ï¼è¯·è系管çå"); |
| | | } |
| | | |
| | | return response; |
| | | return (flowControl: true, value: null); |
| | | } |
| | | |
| | | private async Task<WebResponseContent> HandleOutboundOrderToMESCompletion(Dt_OutboundOrder outboundOrder, string orderNo) |
| | |
| | | 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 _); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |