From 36a26a54dd06a2751c409f50fe1e4dfadb191d56 Mon Sep 17 00:00:00 2001
From: pan <antony1029@163.com>
Date: 星期二, 09 十二月 2025 19:51:35 +0800
Subject: [PATCH] 提交
---
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_BasicService/InvokeMESService.cs | 1036 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 978 insertions(+), 58 deletions(-)
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_BasicService/InvokeMESService.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_BasicService/InvokeMESService.cs"
index e41ddf5..c2cb477 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_BasicService/InvokeMESService.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_BasicService/InvokeMESService.cs"
@@ -1,22 +1,32 @@
-锘縰sing Microsoft.Extensions.Logging;
+锘縰sing 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
{
@@ -31,7 +41,20 @@
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;
@@ -39,11 +62,27 @@
_stockInfoDetailRepository = stockInfoDetailRepository;
_stockInfoRepository = stockInfoRepository;
_inboundOrderRepository = inboundOrderRepository;
+ _outboundOrderService = outboundOrderService;
+ _outboundOrderDetailService = outboundOrderDetailService;
+ _outStockLockInfoService = outStockLockInfoService;
+ _materialUnitService = materialUnitService;
+ _pickingRecoreRepository = pickingRecoreRepository;
+ _interfacelogRepository = interfacelogRepository;
+ _inboundOrderDetailRepository = inboundOrderDetailRepository;
}
+ /// <summary>
+ /// 鍏ュ簱鍙嶉
+ /// </summary>
+ /// <param name="model"></param>
+ /// <returns></returns>
+ /// <exception cref="HttpRequestException"></exception>
public async Task<ResponseModel> FeedbackInbound(FeedbackInboundRequestModel model)
{
- string json = JsonConvert.SerializeObject(model);
+ string json = JsonConvert.SerializeObject(model, new JsonSerializerSettings
+ {
+ ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
+ });
var content = new StringContent(json, Encoding.UTF8, "application/json");
var _client = _httpClientFactory.CreateClient("MESUrl");
_client.DefaultRequestHeaders.Clear();
@@ -61,13 +100,24 @@
return JsonConvert.DeserializeObject<ResponseModel>(body);
}
- public async Task FeedbackOutbound(FeedbackOutboundRequestModel model)
+ /// <summary>
+ /// 鍑哄簱鍙嶉
+ /// </summary>
+ /// <param name="model"></param>
+ /// <returns></returns>
+ /// <exception cref="HttpRequestException"></exception>
+ public async Task<ResponseModel> FeedbackOutbound(FeedbackOutboundRequestModel model)
{
- string json = JsonConvert.SerializeObject(model);
+ string json = JsonConvert.SerializeObject(model, new JsonSerializerSettings
+ {
+ ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
+ });
var content = new StringContent(json, Encoding.UTF8, "application/json");
var _client = _httpClientFactory.CreateClient("MESUrl");
_client.DefaultRequestHeaders.Clear();
_client.DefaultRequestHeaders.Add("Accept", "application/json");
+
+ _logger.LogInformation("InvokeMESService FeedbackOutbound : " + model.orderNo + " , " + json);
var response = await _client.PostAsync("AldMaterialOutbound/MaterialOutbound", content);
string body = await response.Content.ReadAsStringAsync();
@@ -77,13 +127,42 @@
throw new HttpRequestException(body);
}
+ _logger.LogInformation("InvokeMESService FeedbackOutbound body: " + body);
- // JsonConvert.DeserializeObject<ResponseModel>(body);
+ return JsonConvert.DeserializeObject<ResponseModel>(body);
+ }
+
+ 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(),
+ NullValueHandling = NullValueHandling.Include
+ });
+ var content = new StringContent(json, Encoding.UTF8, "application/json");
+ var _client = _httpClientFactory.CreateClient("MESUrl");
+ _client.DefaultRequestHeaders.Clear();
+ _client.DefaultRequestHeaders.Add("Accept", "application/json");
+ _logger.LogInformation("InvokeMESService FeedbackAllocate : " + json);
+ var response = await _client.PostAsync("AldAllocationOperation/AllocationOperation", content);
+ string body = await response.Content.ReadAsStringAsync();
+ _logger.LogInformation("InvokeMESService FeedbackAllocate body: " + body);
+ if (!response.IsSuccessStatusCode)
+ {
+
+ throw new HttpRequestException(body);
+ }
+
+ return JsonConvert.DeserializeObject<ResponseModel>(body);
}
public async Task<ResponseModel> NewMaterielToMes(MaterielToMesDTO model)
{
- string json = JsonConvert.SerializeObject(model);
+ string json = JsonConvert.SerializeObject(model, new JsonSerializerSettings
+ {
+ ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
+ });
//string userDataEncoded = Uri.EscapeDataString(json);
@@ -104,8 +183,11 @@
var content = new StringContent(json, Encoding.UTF8, "application/json");
- using var response = await client.PostAsync("", content);
+ _logger.LogInformation("InvokeMESService NewMaterielToMes : " + json);
+
+ using var response = await client.PostAsync("AldBarcodeInformation/BarcodeInformation", content);
var responseText = await response.Content.ReadAsStringAsync();
+ _logger.LogInformation("InvokeMESService NewMaterielToMes body: " + responseText);
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException(responseText);
@@ -154,6 +236,8 @@
}
}
+
+
/// <summary>
///
/// </summary>
@@ -162,76 +246,912 @@
/// <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,
- 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.Unit, item.WarehouseCode })
- .Select(group => new FeedbackInboundDetailsModel
- {
- materialCode = group.Key.MaterielCode,
- supplyCode = group.Key.SupplyCode,
- batchNo = group.Key.BatchNo,
- lineNo = group.Key.InboundOrderRowNo,
- // warehouseCode = group.Key.WarehouseCode=="0"?"1072": group.Key.WarehouseCode,
- warehouseCode = "1072",
- unit = group.Key.Unit,
- barcodes = group.Select(row => new FeedbackBarcodesModel
- {
- barcode = row.Barcode,
- qty = row.StockQuantity
- }).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();
+ }
+ }
+ return WebResponseContent.Instance.Error("鍥炰紶鎴愬姛锛�");
+ }
+ else
+ {
+ _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 2, Remark = result.message })
+ .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
+ _inboundOrderDetailRepository.Db.Updateable<Dt_InboundOrderDetail>().SetColumns(it => new Dt_InboundOrderDetail { ReturnToMESStatus = 2 })
+ .Where(it => it.OrderId == inboundOrder.Id).ExecuteCommand();
+ return WebResponseContent.Instance.Error("鍥炰紶寮傚父锛�");
+ }
+ }
}
}
}
}
+ catch (Exception ex)
+ {
+ _logger.LogInformation("InvokeMESService BatchOrderFeedbackToMes 鍥炲啓MES澶辫触: " + ex.Message);
+ return WebResponseContent.Instance.Error(ex.Message);
+ }
+
}
- catch (Exception ex)
+ }
+ 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鍥炶皟鎴栦汉宸ユ搷浣滐級姝e湪澶勭悊
+
+ // return WebResponseContent.Instance.Error("WMS姝e湪澶勭悊姝ゅ洖浼犱换鍔★紝璇峰嬁閲嶅鎿嶄綔銆�");
+ //}
+ }
+ 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 = $"澶勭悊鍑哄簱鍗曞洖浼燤ES鏃跺彂鐢熷紓甯� - 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();
+ }
+
+ // 浠呭垎鎷e畬鎴愭椂鍚慚ES鍙嶉
+ 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.鎷i�夊畬鎴� || 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>鏍煎紡锛歽yyyMMdd + 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}");
+ }
+ }
+ }
+ }
}
}
--
Gitblit v1.9.3