using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Org.BouncyCastle.Asn1.Ocsp; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; 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_DTO.Allocate; using WIDESEA_DTO.Basic; using WIDESEA_DTO.Inbound; using WIDESEA_DTO.Outbound; using WIDESEA_IBasicService; using WIDESEA_IOutboundService; using WIDESEA_Model.Models; namespace WIDESEA_BasicService { public class InvokeMESService : IInvokeMESService { private readonly IHttpClientFactory _httpClientFactory; private readonly ILogger _logger; private string UserName = "12312"; private string Password = "1"; private readonly IRepository _feedbacktomesRepository; private readonly IRepository _stockInfoDetailRepository; private readonly IRepository _stockInfoRepository; private readonly IRepository _inboundOrderRepository; private readonly IOutboundOrderService _outboundOrderService; private readonly IOutboundOrderDetailService _outboundOrderDetailService; private readonly IOutStockLockInfoService _outStockLockInfoService; public InvokeMESService(IHttpClientFactory httpClientFactory, ILogger logger, IRepository feedbacktomesRepository, IRepository stockInfoDetailRepository, IRepository stockInfoRepository, IRepository inboundOrderRepository, IOutboundOrderService outboundOrderService, IOutboundOrderDetailService outboundOrderDetailService, IOutStockLockInfoService outStockLockInfoService) { _httpClientFactory = httpClientFactory; _logger = logger; _feedbacktomesRepository = feedbacktomesRepository; _stockInfoDetailRepository = stockInfoDetailRepository; _stockInfoRepository = stockInfoRepository; _inboundOrderRepository = inboundOrderRepository; _outboundOrderService = outboundOrderService; _outboundOrderDetailService = outboundOrderDetailService; _outStockLockInfoService = outStockLockInfoService; } /// /// 入库反馈 /// /// /// /// public async Task FeedbackInbound(FeedbackInboundRequestModel 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 FeedbackInbound : " + json); var response = await _client.PostAsync("AldMaterialWarehousing/MaterialWarehousing", content); string body = await response.Content.ReadAsStringAsync(); _logger.LogInformation("InvokeMESService FeedbackInbound body: " + body); if (!response.IsSuccessStatusCode) { throw new HttpRequestException(body); } return JsonConvert.DeserializeObject(body); } /// /// 出库反馈 /// /// /// /// public async Task FeedbackOutbound(FeedbackOutboundRequestModel 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 : " + json); var response = await _client.PostAsync("AldMaterialOutbound/MaterialOutbound", content); string body = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) { throw new HttpRequestException(body); } return JsonConvert.DeserializeObject(body); } public async Task FeedbackAllocate(AllocateDto 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 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(body); } public async Task NewMaterielToMes(MaterielToMesDTO model) { string json = JsonConvert.SerializeObject(model, new JsonSerializerSettings { ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() }); //string userDataEncoded = Uri.EscapeDataString(json); ////string baseUrl = "http://mestest.ald.com//OrBitWCFServiceR15/orbitwebapi.ashx?"; //string userTicket = await GetToken(UserName, Password); //string api = "WMS_BarcodeInformation"; var client = _httpClientFactory.CreateClient("MESUrl"); // 拼接 URL 参数 // string url = $"{client.BaseAddress}UserTicket={userTicket}&API={api}&UserData={userDataEncoded}"; client.DefaultRequestHeaders.Clear(); client.DefaultRequestHeaders.Add("Accept", "application/json"); var content = new StringContent(json, Encoding.UTF8, "application/json"); _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); } return JsonConvert.DeserializeObject(responseText); } public async Task GetToken(String username, string password) { var clientHandler = new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, }; //var client = new HttpClient(clientHandler); var client = _httpClientFactory.CreateClient("MESUrl"); client.DefaultRequestHeaders.Clear(); var request = new HttpRequestMessage { Method = HttpMethod.Post, RequestUri = new Uri($"{client.BaseAddress}UserName={username}&UserPassword={password}"), Headers ={ { "Accept", "*/*" }, { "User-Agent", "PostmanRuntime-ApipostRuntime/1.1.0" }, { "Connection", "keep-alive" }, }, Content = new MultipartFormDataContent { }, }; using (var response = await client.SendAsync(request)) { response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) { throw new HttpRequestException(body); } return body; } } /// /// /// /// /// 入库传1 出库传2 /// public async Task BatchOrderFeedbackToMes(List orderNos, int inout) { if (inout == 1) { foreach (var orderNo in orderNos) { try { var stockinfos = _stockInfoRepository.Db.Queryable("info").Where(info => info.StockStatus == 6) .Where(it => SqlFunc.Subqueryable().Where(s => s.StockId == it.Id && s.OrderNo == orderNo).Any()) .ToList(); var feeds = _feedbacktomesRepository.Db.Queryable().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) { var lists = _stockInfoDetailRepository.Db.Queryable().Where(x => x.StockId == item.Id).ToList(); if (lists.Any()) { var inboundOrder = _inboundOrderRepository.Db.Queryable().First(x => x.InboundOrderNo == lists.FirstOrDefault().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() }; var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.SupplyCode, item.BatchNo, item.InboundOrderRowNo, item.BarcodeUnit, item.WarehouseCode }) .Select(group => new FeedbackInboundDetailsModel { materialCode = group.Key.MaterielCode, supplyCode = group.Key.SupplyCode, batchNo = group.Key.BatchNo, lineNo = group.Key.InboundOrderRowNo, qty = group.Sum(x => x.BarcodeQty), // warehouseCode = group.Key.WarehouseCode=="0"?"1072": group.Key.WarehouseCode, warehouseCode = group.Key.WarehouseCode, unit = group.Key.BarcodeUnit, barcodes = group.Select(row => new FeedbackBarcodesModel { barcode = row.Barcode, qty = row.BarcodeQty }).ToList() }).ToList(); feedmodel.details = groupedData; var result = await FeedbackInbound(feedmodel); if (result != null && result.code == 200) { _feedbacktomesRepository.Db.Insertable(new Dt_FeedbackToMes { OrderNo = orderNo, PalletCode = item.PalletCode, ReportStatus = 1 }).ExecuteCommand(); } } } } } catch (Exception ex) { _logger.LogInformation("InvokeMESService BatchOrderFeedbackToMes 回写MES失败: " + ex.Message); return WebResponseContent.Instance.Error(ex.Message); } } } else if (inout == 2) { foreach (var orderNo in orderNos) { var outboundOrder = await _outboundOrderService.Db.Queryable().FirstAsync(x => x.OrderNo == orderNo); if (outboundOrder != null && outboundOrder.IsBatch == 0) { await HandleOutboundOrderToMESCompletion(outboundOrder, orderNo); } else if (outboundOrder != null && outboundOrder.IsBatch ==1) { } } } return WebResponseContent.Instance.OK(); } private async Task HandleOutboundOrderToMESCompletion(Dt_OutboundOrder outboundOrder, string orderNo) { try { if (outboundOrder.ReturnToMESStatus == 1 || outboundOrder.IsBatch == 1) { return; } var orderDetails = await _outboundOrderDetailService.Db.Queryable() .LeftJoin((o, item) => o.OrderId == item.Id) .Where((o, item) => item.OrderNo == orderNo) .Select((o, item) => o) .ToListAsync(); 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() .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() }; foreach (var detail in orderDetails) { // 获取该明细对应的条码信息(从锁定记录) var detailLocks = await _outStockLockInfoService.Db.Queryable() .Where(x => x.OrderNo == orderNo && x.OrderDetailId == detail.Id && x.Status == (int)OutLockStockStatusEnum.拣选完成) .ToListAsync(); var detailModel = new FeedbackOutboundDetailsModel { materialCode = detail.MaterielCode, lineNo = detail.lineNo, // 注意:这里可能需要调整字段名 warehouseCode = detail.WarehouseCode, qty = detail.OverOutQuantity, // 使用订单明细的已出库数量 currentDeliveryQty = detail.OverOutQuantity, unit = detail.Unit, barcodes = detailLocks.Select(lockInfo => new WIDESEA_DTO.Outbound.BarcodesModel { barcode = lockInfo.CurrentBarcode, supplyCode = lockInfo.SupplyCode, batchNo = lockInfo.BatchNo, unit = lockInfo.Unit, qty = lockInfo.PickedQty // 条码级别的数量仍用锁定记录 }).ToList() }; feedmodel.details.Add(detailModel); } var result = await FeedbackOutbound(feedmodel); if (result != null && result.code == 200) { await _outboundOrderDetailService.Db.Updateable() .SetColumns(x => x.ReturnToMESStatus == 1) .Where(x => x.OrderId == outboundOrder.Id) .ExecuteCommandAsync(); await _outboundOrderService.Db.Updateable() .SetColumns(x => x.ReturnToMESStatus == 1) .Where(x => x.OrderNo == orderNo) .ExecuteCommandAsync(); } } } catch (Exception ex) { _logger.LogError($"CheckAndUpdateOrderStatus失败 - OrderNo: {orderNo}, Error: {ex.Message}"); } } } public static class UniqueValueGenerator { // 原子计数器(线程安全,每次递增1,避免同一Ticks重复) private static long _counter = 0; /// /// 生成唯一值(支持高并发) /// /// 格式:yyyyMMdd + Ticks + 3位计数器(如2025112563867890123001) public static string Generate() { var now = DateTime.Now; string datePart = now.ToString("yyyyMMdd"); long ticksPart = now.Ticks; // 原子递增计数器(取模1000,确保计数器仅3位,控制长度) long counterPart = Interlocked.Increment(ref _counter) % 1000; // 拼接:计数器补0为3位(避免位数不一致) return $"{datePart}{ticksPart}{counterPart:D3}"; } } }