| | |
| | | using Microsoft.Extensions.Logging; |
| | | using Dm.filter; |
| | | using MailKit.Search; |
| | | using Microsoft.Extensions.Logging; |
| | | using Newtonsoft.Json; |
| | | using Org.BouncyCastle.Asn1.Ocsp; |
| | | using SqlSugar; |
| | | using System; |
| | | using System.Collections.Concurrent; |
| | | using System.Collections.Generic; |
| | | using System.Linq; |
| | | using System.Net; |
| | | using System.Net.Http; |
| | | using System.Reflection.Metadata; |
| | | using System.Security.Policy; |
| | | using System.Text; |
| | | using System.Threading.Tasks; |
| | | using WIDESEA_Common.OrderEnum; |
| | | using WIDESEA_Common.StockEnum; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_Core.BaseRepository; |
| | | using WIDESEA_Core.Helper; |
| | | using WIDESEA_DTO.Allocate; |
| | | using WIDESEA_DTO.Basic; |
| | | using WIDESEA_DTO.Inbound; |
| | | using WIDESEA_DTO.Outbound; |
| | | using WIDESEA_IBasicService; |
| | | using WIDESEA_IOutboundService; |
| | | using WIDESEA_Model.Models; |
| | | using WIDESEA_Model.Models.Outbound; |
| | | |
| | | namespace WIDESEA_BasicService |
| | | { |
| | |
| | | private readonly IRepository<Dt_StockInfoDetail> _stockInfoDetailRepository; |
| | | private readonly IRepository<Dt_StockInfo> _stockInfoRepository; |
| | | private readonly IRepository<Dt_InboundOrder> _inboundOrderRepository; |
| | | public InvokeMESService(IHttpClientFactory httpClientFactory, ILogger<InvokeMESService> logger, IRepository<Dt_FeedbackToMes> feedbacktomesRepository, IRepository<Dt_StockInfoDetail> stockInfoDetailRepository, IRepository<Dt_StockInfo> stockInfoRepository, 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; |
| | | 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; |
| | |
| | | _stockInfoDetailRepository = stockInfoDetailRepository; |
| | | _stockInfoRepository = stockInfoRepository; |
| | | _inboundOrderRepository = inboundOrderRepository; |
| | | _outboundOrderService = outboundOrderService; |
| | | _outboundOrderDetailService = outboundOrderDetailService; |
| | | _outStockLockInfoService = outStockLockInfoService; |
| | | _materialUnitService = materialUnitService; |
| | | _pickingRecoreRepository = pickingRecoreRepository; |
| | | _interfacelogRepository = interfacelogRepository; |
| | | _inboundOrderDetailRepository = inboundOrderDetailRepository; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | /// <exception cref="HttpRequestException"></exception> |
| | | public async Task<ResponseModel> FeedbackInbound(FeedbackInboundRequestModel model) |
| | | { |
| | | string json =JsonConvert.SerializeObject(model, new JsonSerializerSettings |
| | | { |
| | | string json = JsonConvert.SerializeObject(model, new JsonSerializerSettings |
| | | { |
| | | ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() |
| | | }); |
| | | var content = new StringContent(json, Encoding.UTF8, "application/json"); |
| | |
| | | |
| | | return JsonConvert.DeserializeObject<ResponseModel>(body); |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// åºåºåé¦ |
| | | /// </summary> |
| | |
| | | _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(); |
| | |
| | | |
| | | throw new HttpRequestException(body); |
| | | } |
| | | |
| | | _logger.LogInformation("InvokeMESService FeedbackOutbound body: " + body); |
| | | |
| | | return JsonConvert.DeserializeObject<ResponseModel>(body); |
| | | } |
| | | |
| | | public async Task<ResponseModel> FeedbackAllocate(AllocateDto model) |
| | | public async Task<ResponseModel> FeedbackAllocate(AllocateDto model) |
| | | { |
| | | _logger.LogInformation($"InvokeMESService FeedbackAllocate åºååå: {JsonConvert.SerializeObject(model)}"); |
| | | string json = JsonConvert.SerializeObject(model, new JsonSerializerSettings |
| | | { |
| | | ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() |
| | | ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver(), |
| | | NullValueHandling = NullValueHandling.Include |
| | | }); |
| | | var content = new StringContent(json, Encoding.UTF8, "application/json"); |
| | | var _client = _httpClientFactory.CreateClient("MESUrl"); |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /// <summary> |
| | | /// |
| | | /// </summary> |
| | |
| | | /// <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()) { |
| | | return WebResponseContent.Instance.Error("没æéè¦åä¼ çæ°æ®"); |
| | | } |
| | | foreach (var item in unreports) |
| | | try |
| | | { |
| | | 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) |
| | | { |
| | | 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>() |
| | | _inboundOrderDetailRepository.Db.Updateable<Dt_InboundOrderDetail>().SetColumns(it => new Dt_InboundOrderDetail { ReturnToMESStatus = 1 }) |
| | | .Where(it => it.OrderId == inboundOrder.Id).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) |
| | | 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) |
| | | { |
| | | _feedbacktomesRepository.Db.Insertable(new Dt_FeedbackToMes { OrderNo = orderNo, PalletCode = item.PalletCode, ReportStatus = 1 }).ExecuteCommand(); |
| | | 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 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 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(); |
| | | } |
| | | } |
| | | } |
| | | 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(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogInformation("InvokeMESService BatchOrderFeedbackToMes ååMES失败: " + ex.Message); |
| | | return WebResponseContent.Instance.Error(ex.Message); |
| | | } |
| | | |
| | | } |
| | | catch (Exception ex) |
| | | } |
| | | else if (inout == 2) |
| | | { |
| | | foreach (var orderNo in orderNos) |
| | | { |
| | | _logger.LogInformation("InvokeMESService BatchOrderFeedbackToMes ååMES失败: " + ex.Message); |
| | | return WebResponseContent.Instance.Error(ex.Message); |
| | | 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 |
| | | //{ |
| | | // // æ¢é失败ï¼è¯´ææå¦ä¸ä¸ªçº¿ç¨ï¼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(); |
| | | } |
| | | |
| | | private async Task<WebResponseContent> HandleOutboundOrderBatchToMESCompletion(Dt_OutboundOrder outboundOrder, string orderNo) |
| | | { |
| | | // å®ä¹é»è®¤è¿åï¼æåæï¼ |
| | | WebResponseContent response = WebResponseContent.Instance.OK("åä¼ MESå¤ç宿"); |
| | | //0 = æªåä¼ ï¼1 = å·²åä¼ æåï¼2 = åä¼ å¤±è´¥ |
| | | try |
| | | { |
| | | // æ ¡éªï¼å·²åä¼ ç´æ¥è¿åé误 |
| | | //if (outboundOrder.ReturnToMESStatus == 1) |
| | | //{ |
| | | // return WebResponseContent.Instance.Error("该åå·²ç»åä¼ ï¼"); |
| | | //} |
| | | |
| | | // æ¥è¯¢è®¢åæç»ï¼ä»
æ¥è¯¢æªåä¼ æåçï¼ |
| | | var orderDetails = 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(); |
| | | |
| | | if (!orderDetails.Any()) |
| | | { |
| | | return WebResponseContent.Instance.Error("ææ éè¦å¤çç订åæç»"); |
| | | } |
| | | |
| | | |
| | | var pickingRecords = await _pickingRecoreRepository.Db.Queryable<Dt_PickingRecord>().Where(x => x.OrderNo == orderNo && x.ReturnToMESStatus != 1 && !x.IsCancelled).ToListAsync(); |
| | | |
| | | if (!pickingRecords.Any()) |
| | | return WebResponseContent.Instance.Error("没æéè¦åä¼ çåæ£è®°å½"); |
| | | |
| | | |
| | | 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(), |
| | | 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) |
| | | { |
| | | // åç½®åæ°æ ¡éªï¼ç©ºå¼ç´æ¥è¿åé误 |
| | | if (outboundOrder == null) |
| | | { |
| | | return WebResponseContent.Instance.Error("åºåºåå®ä½ä¸ºç©ºï¼æ æ³å¤çåä¼ MES"); |
| | | } |
| | | |
| | | if (string.IsNullOrWhiteSpace(orderNo)) |
| | | { |
| | | return WebResponseContent.Instance.Error("订åå·ä¸ºç©ºï¼æ æ³å¤çåä¼ MES"); |
| | | } |
| | | |
| | | try |
| | | { |
| | | |
| | | if (outboundOrder.ReturnToMESStatus == 1) |
| | | { |
| | | return WebResponseContent.Instance.Error($"OrderNo: {orderNo}, 该åå·²ç»åä¼ ï¼"); |
| | | } |
| | | |
| | | |
| | | var orderDetails = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .LeftJoin<Dt_OutboundOrder>((o, item) => o.OrderId == item.Id) |
| | | .Where((o, item) => item.OrderNo == orderNo) |
| | | .Select((o, item) => o) |
| | | .ToListAsync(); |
| | | |
| | | // æ æç»åºæ¯è¿åè¦å |
| | | if (!orderDetails.Any()) |
| | | { |
| | | return WebResponseContent.Instance.Error($"OrderNo: {orderNo} æªæ¥è¯¢å°è®¢åæç»"); |
| | | } |
| | | |
| | | // 夿æ¯å¦æææç»å®æåºåº |
| | | bool allCompleted = true; |
| | | foreach (var detail in orderDetails) |
| | | { |
| | | if (detail.OverOutQuantity < detail.NeedOutQuantity) |
| | | { |
| | | allCompleted = false; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // æ´æ°è®¢åç¶æï¼ä¿®æ£è¯æ³é误ï¼== â =ï¼ |
| | | int newStatus = allCompleted ? (int)OutOrderStatusEnum.åºåºå®æ : (int)OutOrderStatusEnum.åºåºä¸; |
| | | if (outboundOrder.OrderStatus != newStatus) |
| | | { |
| | | await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>() |
| | | .SetColumns(x => x.OrderStatus == newStatus) // å
³é®ä¿®æ£ï¼èµå¼èé夿 |
| | | .Where(x => x.OrderNo == orderNo) |
| | | .ExecuteCommandAsync(); |
| | | } |
| | | |
| | | // ä»
忣宿æ¶åMESåé¦ |
| | | if (allCompleted && newStatus == (int)OutOrderStatusEnum.åºåºå®æ) |
| | | { |
| | | 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, |
| | | orderNo = outboundOrder.UpperOrderNo, |
| | | documentsNO = outboundOrder.OrderNo, |
| | | status = outboundOrder.OrderStatus, |
| | | details = new List<FeedbackOutboundDetailsModel>() |
| | | }; |
| | | |
| | | // æå»ºæç»æ°æ® |
| | | foreach (var detail in orderDetails) |
| | | { |
| | | var detailLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && |
| | | x.OrderDetailId == detail.Id && |
| | | (x.Status == (int)OutLockStockStatusEnum.æ£é宿 || x.Status == (int)OutLockStockStatusEnum.å·²ååº)) |
| | | .ToListAsync(); |
| | | |
| | | var groupdata = detailLocks.GroupBy(item => new { item.MaterielCode, item.lineNo, item.BarcodeUnit, item.WarehouseCode }) |
| | | .Select(group => new FeedbackOutboundDetailsModel |
| | | { |
| | | materialCode = group.Key.MaterielCode, |
| | | lineNo = group.Key.lineNo, |
| | | warehouseCode = group.Key.WarehouseCode, |
| | | qty = group.Sum(x => x.PickedQty), |
| | | currentDeliveryQty = group.Sum(x => x.PickedQty), |
| | | unit = group.Key.BarcodeUnit, |
| | | barcodes = group.Select(lockInfo => new WIDESEA_DTO.Outbound.BarcodesModel |
| | | { |
| | | barcode = lockInfo.CurrentBarcode, |
| | | supplyCode = lockInfo.SupplyCode, |
| | | batchNo = lockInfo.BatchNo, |
| | | unit = lockInfo.BarcodeUnit, |
| | | qty = lockInfo.PickedQty |
| | | }).ToList() |
| | | }).ToList(); |
| | | |
| | | feedmodel.details.AddRange(groupdata); |
| | | } |
| | | |
| | | // è°ç¨MESæ¥å£ |
| | | var result = await FeedbackOutbound(feedmodel); |
| | | if (result == null) |
| | | { |
| | | return WebResponseContent.Instance.Error($"OrderNo: {orderNo} MESåä¼ æ¥å£è¿å空"); |
| | | } |
| | | |
| | | if (result.code == 200) |
| | | { |
| | | // åä¼ æåï¼æ´æ°åä¼ ç¶æ |
| | | await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>() |
| | | .SetColumns(x => x.ReturnToMESStatus == 1) |
| | | .Where(x => x.OrderId == outboundOrder.Id) |
| | | .ExecuteCommandAsync(); |
| | | |
| | | await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>() |
| | | .SetColumns(it => new Dt_OutboundOrder { ReturnToMESStatus = 1, Remark = "" }) |
| | | .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 |
| | | { |
| | | 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); |
| | | } |
| | | } |
| | | return WebResponseContent.Instance.OK("订åç¶æå·²æ´æ°ï¼æªå®å
¨å®æåæ£æ éåä¼ MES"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | // å
¨å±å¼å¸¸æè·ï¼è¿åé误+è®°å½è¯¦ç»æ¥å¿ |
| | | var errorMsg = $"OrderNo: {orderNo} å¤çåä¼ MESæ¶åçå¼å¸¸ï¼{ex.Message}"; |
| | | _logger.LogError(ex, errorMsg); // è®°å½å æ ä¿¡æ¯ä¾¿äºææ¥ |
| | | return WebResponseContent.Instance.Error("å¤çåä¼ MESæ¶åçå¼å¸¸ï¼è¯·è系管çå"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | public static class UniqueValueGenerator |
| | | { |
| | | // åå计æ°å¨ï¼çº¿ç¨å®å
¨ï¼æ¯æ¬¡éå¢1ï¼é¿å
åä¸Tickséå¤ï¼ |
| | | private static long _counter = 0; |
| | | |
| | | /// <summary> |
| | | /// çæå¯ä¸å¼ï¼æ¯æé«å¹¶åï¼ |
| | | /// </summary> |
| | | /// <returns>æ ¼å¼ï¼yyyyMMdd + Ticks + 3ä½è®¡æ°å¨ï¼å¦2025112563867890123001ï¼</returns> |
| | | public static string Generate() |
| | | { |
| | | 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}{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}"); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |