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.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;
|
|
namespace WIDESEA_BasicService
|
{
|
public class TransferDataDto
|
{
|
public DateTime TransferTime { get; set; }
|
public List<TransferItemDto> Items { get; set; }
|
}
|
|
public class TransferItemDto
|
{
|
public string MaterialCode { get; set; }
|
public decimal TotalQuantity { get; set; }
|
public List<LocationInfoDto> Locations { get; set; }
|
}
|
public class LocationInfoDto
|
{
|
public string LocationCode { get; set; }
|
public decimal Quantity { get; set; }
|
public List<BarcodeInfoDto> Barcodes { get; set; }
|
}
|
|
public class BarcodeInfoDto
|
{
|
public string Barcode { get; set; }
|
public decimal Quantity { get; set; }
|
}
|
public class InvokeMESService : IInvokeMESService
|
{
|
private readonly IHttpClientFactory _httpClientFactory;
|
private readonly ILogger<InvokeMESService> _logger;
|
private string UserName = "12312";
|
private string Password = "1";
|
|
private readonly IRepository<Dt_FeedbackToMes> _feedbacktomesRepository;
|
private readonly IRepository<Dt_StockInfoDetail> _stockInfoDetailRepository;
|
private readonly IRepository<Dt_StockInfo> _stockInfoRepository;
|
private readonly IRepository<Dt_InboundOrder> _inboundOrderRepository;
|
private readonly IRepository<Dt_PickingRecord> _pickingRecoreRepository;
|
private readonly IMaterialUnitService _materialUnitService;
|
private readonly IOutboundOrderService _outboundOrderService;
|
private readonly IOutboundOrderDetailService _outboundOrderDetailService;
|
private readonly IOutStockLockInfoService _outStockLockInfoService;
|
public InvokeMESService(IHttpClientFactory httpClientFactory, ILogger<InvokeMESService> logger, IRepository<Dt_FeedbackToMes> feedbacktomesRepository, IRepository<Dt_StockInfoDetail> stockInfoDetailRepository, IRepository<Dt_StockInfo> stockInfoRepository, IRepository<Dt_InboundOrder> inboundOrderRepository, IOutboundOrderService outboundOrderService, IOutboundOrderDetailService outboundOrderDetailService, IOutStockLockInfoService outStockLockInfoService, IMaterialUnitService materialUnitService, IRepository<Dt_PickingRecord> pickingRecoreRepository)
|
{
|
_httpClientFactory = httpClientFactory;
|
_logger = logger;
|
_feedbacktomesRepository = feedbacktomesRepository;
|
_stockInfoDetailRepository = stockInfoDetailRepository;
|
_stockInfoRepository = stockInfoRepository;
|
_inboundOrderRepository = inboundOrderRepository;
|
_outboundOrderService = outboundOrderService;
|
_outboundOrderDetailService = outboundOrderDetailService;
|
_outStockLockInfoService = outStockLockInfoService;
|
_materialUnitService = materialUnitService;
|
_pickingRecoreRepository = pickingRecoreRepository;
|
}
|
|
/// <summary>
|
/// 入库反馈
|
/// </summary>
|
/// <param name="model"></param>
|
/// <returns></returns>
|
/// <exception cref="HttpRequestException"></exception>
|
public async Task<ResponseModel> 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<ResponseModel>(body);
|
}
|
|
/// <summary>
|
/// 出库反馈
|
/// </summary>
|
/// <param name="model"></param>
|
/// <returns></returns>
|
/// <exception cref="HttpRequestException"></exception>
|
public async Task<ResponseModel> 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<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, 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<ResponseModel>(responseText);
|
}
|
|
public async Task<string> 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;
|
}
|
}
|
|
/// <summary>
|
/// 准备回传数据
|
/// </summary>
|
private TransferDataDto PrepareTransferData(List<Dt_PickingRecord> pickingRecords)
|
{
|
var transferData = new TransferDataDto
|
{
|
TransferTime = DateTime.Now,
|
Items = new List<TransferItemDto>()
|
};
|
|
// 按物料分组
|
var materialGroups = pickingRecords
|
.GroupBy(x => x.MaterielCode)
|
.ToList();
|
|
foreach (var group in materialGroups)
|
{
|
var item = new TransferItemDto
|
{
|
MaterialCode = group.Key,
|
TotalQuantity = group.Sum(x => x.PickQuantity),
|
Locations = new List<LocationInfoDto>()
|
};
|
|
// 按货位分组
|
var locationGroups = group
|
.GroupBy(x => x.LocationCode)
|
.ToList();
|
|
foreach (var locationGroup in locationGroups)
|
{
|
var location = new LocationInfoDto
|
{
|
LocationCode = locationGroup.Key,
|
Quantity = locationGroup.Sum(x => x.PickQuantity),
|
Barcodes = locationGroup.Select(x => new BarcodeInfoDto
|
{
|
Barcode = x.Barcode,
|
Quantity = x.PickQuantity
|
}).ToList()
|
};
|
|
item.Locations.Add(location);
|
}
|
|
transferData.Items.Add(item);
|
}
|
|
return transferData;
|
}
|
|
/// <summary>
|
///
|
/// </summary>
|
/// <param name="orderNos"></param>
|
/// <param name="inout">入库传1 出库传2</param>
|
/// <returns></returns>
|
public async Task<WebResponseContent> BatchOrderFeedbackToMes(List<string> orderNos, int inout)
|
{
|
if (inout == 1)
|
{
|
foreach (var orderNo in orderNos)
|
{
|
try
|
{
|
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)
|
{
|
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)
|
{
|
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();
|
}
|
}
|
}
|
}
|
}
|
}
|
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<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;
|
}
|
}
|
|
}
|
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 documentNo = UniqueValueGenerator.Generate();
|
|
|
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 = 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 = 0,
|
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.qty += barModel.qty;
|
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 }).Select(group => new FeedbackOutboundDetailsModel
|
{
|
materialCode = group.Key.materialCode,
|
lineNo = group.Key.lineNo,
|
warehouseCode = group.Key.warehouseCode,
|
qty = group.Sum(x => x.qty),
|
currentDeliveryQty = group.Sum(x => x.currentDeliveryQty),
|
unit = group.Key.unit,
|
barcodes = group.SelectMany(x => x.barcodes.GroupBy(o => new { o.barcode, o.supplyCode, o.batchNo, o.unit }).Select(row => new WIDESEA_DTO.Outbound.BarcodesModel
|
{
|
barcode = row.Key.barcode,
|
supplyCode = row.Key.supplyCode,
|
batchNo = row.Key.batchNo,
|
unit = row.Key.unit,
|
qty = row.Sum(y => y.qty)
|
})).ToList()
|
}).ToList();
|
|
var allCompleted = true;
|
|
// 筛选待回传的明细(ReturnToMESStatus=0)
|
var pendingDetails = orderDetails.Where(x => x.ReturnToMESStatus == 0).ToList();
|
foreach (var detail in pendingDetails)
|
{
|
if (detail.OverOutQuantity < detail.NeedOutQuantity)
|
{
|
allCompleted = false;
|
}
|
}
|
|
// 存在回传失败的明细(ReturnToMESStatus=2),标记未完成
|
if (orderDetails.Any(x => x.ReturnToMESStatus == 2))
|
{
|
allCompleted = false;
|
}
|
|
// 更新订单状态
|
int newStatus = allCompleted ? (int)OutOrderStatusEnum.出库完成 : (int)OutOrderStatusEnum.出库中;
|
if (outboundOrder.OrderStatus != newStatus)
|
{
|
|
int updateCount = await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
|
.SetColumns(x => x.OrderStatus == newStatus)
|
.Where(x => x.OrderNo == orderNo)
|
.ExecuteCommandAsync();
|
|
if (updateCount <= 0)
|
{
|
_logger.LogWarning($"更新出库单状态失败 - OrderNo: {orderNo}, 目标状态: {newStatus}");
|
|
}
|
}
|
|
|
// 调用MES回传接口
|
var mesResult = await FeedbackOutbound(feedModel);
|
if (mesResult == null || mesResult.code != 200)
|
{
|
|
// 更新明细为回传失败(ReturnToMESStatus=2)
|
await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
|
.SetColumns(it => new Dt_OutboundOrderDetail
|
{
|
ReturnToMESStatus = 2,
|
documentsNO = documentNo,
|
})
|
.Where(x => detailIds.Contains(x.Id))
|
.ExecuteCommandAsync();
|
|
return WebResponseContent.Instance.Error($"回传MES失败");
|
}
|
foreach (var record in pickingRecords.Where(x => detailIds.Contains(x.OrderDetailId)).ToList())
|
{
|
record.ReturnToMESStatus = 1;
|
}
|
var updates = pickingRecords.Where(x => detailIds.Contains(x.OrderDetailId)).ToList();
|
updates.ForEach(x =>
|
{
|
x.ReturnToMESStatus = 1;
|
});
|
await _pickingRecoreRepository.Db.Updateable(updates).ExecuteCommandAsync();
|
if (allCompleted)
|
{
|
//MES回传成功:更新明细为回传成功状态
|
await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
|
.SetColumns(it => new Dt_OutboundOrderDetail
|
{
|
ReturnToMESStatus = 1,
|
documentsNO = documentNo,
|
})
|
.Where(x => detailIds.Contains(x.Id))
|
.ExecuteCommandAsync();
|
}
|
// 校验是否所有明细都完成,更新订单最终状态
|
if (allCompleted && newStatus == (int)OutOrderStatusEnum.出库完成)
|
{
|
await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
|
.SetColumns(x => new Dt_OutboundOrder
|
{
|
ReturnToMESStatus = 1,
|
OrderStatus = newStatus
|
})
|
.Where(x => x.OrderNo == orderNo)
|
.ExecuteCommandAsync();
|
}
|
else
|
{
|
// 二次校验是否所有未回传明细都已完成
|
var dbOrderDetails = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
|
.LeftJoin<Dt_OutboundOrder>((o, item) => o.OrderId == item.Id)
|
.Where((o, item) => item.OrderNo == orderNo && item.ReturnToMESStatus != 1)
|
.Select((o, item) => o)
|
.ToListAsync();
|
|
var secAllCompleted = true;
|
foreach (var detail in dbOrderDetails.Where(x => x.ReturnToMESStatus == 0).ToList())
|
{
|
if (detail.OverOutQuantity < detail.NeedOutQuantity)
|
{
|
secAllCompleted = false;
|
break;
|
}
|
}
|
|
if (secAllCompleted)
|
{
|
await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
|
.SetColumns(it => new Dt_OutboundOrder
|
{
|
ReturnToMESStatus = 1,
|
OrderStatus = OutOrderStatusEnum.出库完成.ObjToInt(),
|
})
|
.Where(x => x.OrderNo == orderNo)
|
.ExecuteCommandAsync();
|
}
|
}
|
|
// 回传成功的最终返回
|
response = WebResponseContent.Instance.OK($"回传MES成功,单据号:{documentNo}");
|
}
|
catch (Exception ex)
|
{
|
// 全局异常捕获:记录详细日志 + 返回错误
|
string errorMsg = $"处理出库单回传MES时发生异常 - OrderNo: {orderNo}, Error: {ex.Message}, StackTrace: {ex.StackTrace}";
|
_logger.LogError(ex, errorMsg); // 记录带异常堆栈的日志
|
|
// 异常返回(给前端的友好提示,隐藏堆栈信息)
|
response = WebResponseContent.Instance.Error("处理回传MES时发生异常,请联系管理员");
|
}
|
|
return response;
|
}
|
|
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(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);
|
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("yyyyMMdd");
|
long ticksPart = now.Ticks;
|
|
// 原子递增计数器(取模1000,确保计数器仅3位,控制长度)
|
long counterPart = Interlocked.Increment(ref _counter) % 1000;
|
|
// 拼接:计数器补0为3位(避免位数不一致)
|
return $"{datePart}{ticksPart}{counterPart:D3}";
|
}
|
}
|
}
|