using Amazon.Runtime.Internal.Util; using DS.Module.Core; using DS.Module.Core.Log; using DS.Module.SqlSugar; using DS.Module.UserModule; using DS.WMS.Core.Op.Dtos; using DS.WMS.Core.Op.Entity; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Mapster; using Microsoft.AspNetCore.Mvc; using DS.WMS.Core.Op.Interface; using SqlSugar.IOC; using Newtonsoft.Json; using NLog; using DS.WMS.Core.Sys.Interface; using Microsoft.AspNetCore.Http; using LanguageExt.Common; using DS.Module.Core.Helpers; using NPOI.SS.Formula.Functions; using System.Text.RegularExpressions; using DS.WMS.Core.Sys.Method; using DS.WMS.Core.Map.Dtos; using Org.BouncyCastle.Ocsp; using DS.WMS.Core.Code.Entity; using DS.WMS.Core.Map.Entity; using DS.WMS.Core.Code.Dtos; using System.Net.Http.Headers; using DS.Module.DjyServiceStatus; using NPOI.SS.UserModel; using DS.WMS.Core.Info.Interface; using Microsoft.Extensions.FileSystemGlobbing; using DS.WMS.Core.Info.Entity; using DS.WMS.Core.Info.Dtos; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; using AngleSharp.Dom.Events; using Microsoft.AspNetCore.Authorization; using DS.WMS.Core.Map.Interface; using DS.WMS.Core.Code.Interface; using DS.WMS.Core.Code.Method; using DS.WMS.Core.Map.Method; using DS.Module.Core.Extensions; using DS.Module.Core.Constants; using DS.Module.Core.Data; using DS.WMS.Core.TaskPlat.Dtos; namespace DS.WMS.Core.Op.Method { public class BookingSlotService : IBookingSlotService { private readonly IServiceProvider _serviceProvider; private readonly ISqlSugarClient db; private readonly IUser user; private readonly ISaasDbService saasService; private readonly ISeaExportService _seaExportService; private readonly IBookingLabelService _bookingLabelService; private readonly ILogAuditService _logAuditService; private readonly ISysCacheService _sysCacheService; private readonly ISysFileService _sysFileService; private readonly IBookingSlotStockService _bookingSlotStockService; private readonly IOpFileService _opFileService; private readonly IConfigService _configService; private readonly IClientContactService _clientContactService; private readonly IClientInfoService _clientInfoService; private readonly IDjyServiceStatusService _djyServiceStatusService; private readonly IWebHostEnvironment _environment; private readonly IMappingCarrierService _mappingCarrierService; private readonly ICodeCtnService _codeCtnService; private readonly ICodePortService _codePortService; private readonly IMappingPortService _mappingPortService; private readonly ICodeCountryService _codeCountryService; private readonly string bcCompareUrl; private static readonly NLog.Logger Logger = LogManager.GetCurrentClassLogger(); const string CONST_BC_FILE_CODE = "bc"; const string CONST_BC_FILE_NAME = "Booking Confirmation"; const string CONST_BC_NOTICE_FILE_CODE = "bc_notice"; const string CONST_BC_NOTICE_FILE_NAME = "Booking Confirmation Notice"; const string CONST_BC_MODIFY_FILE_CODE = "bc_modify"; const string CONST_BC_MODIFY_FILE_NAME = "Booking Amendment"; const string CONST_BC_MODIFY_NOTICE_FILE_CODE = "bc_modifynotice"; const string CONST_BC_MODIFY_NOTICE_FILE_NAME = "Booking Amendment Notice"; //收货地名称解析装货港港口 const string RECEIPT_TO_PORTLOAD = "ReceiptToPortLoad"; //交货地名称解析卸货港港口 const string DELIVERY_TO_PORT = "DeliveryToPort"; //BC任务或舱位生成订舱订单时客户联系人必填 const string CONST_CREATE_BOOKING_NEED_CONTACT = "BC_TASK_OR_SLOT_BOOKING_NEED_CONTACT"; //船公司基础映射模块 const string CONST_MAPPING_CARRIER_MODULE = "CarrierBaseMapping"; public BookingSlotService(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; db = _serviceProvider.GetRequiredService(); user = _serviceProvider.GetRequiredService(); saasService = _serviceProvider.GetRequiredService(); _seaExportService = _serviceProvider.GetRequiredService(); _bookingLabelService = _serviceProvider.GetRequiredService(); _logAuditService = _serviceProvider.GetRequiredService(); _sysCacheService = _serviceProvider.GetRequiredService(); _sysFileService = _serviceProvider.GetRequiredService(); _bookingSlotStockService = _serviceProvider.GetRequiredService(); _opFileService = _serviceProvider.GetRequiredService(); _configService = _serviceProvider.GetRequiredService(); _djyServiceStatusService = _serviceProvider.GetRequiredService(); _environment = _serviceProvider.GetRequiredService(); _codeCtnService = _serviceProvider.GetRequiredService(); _codePortService = _serviceProvider.GetRequiredService(); _mappingPortService = _serviceProvider.GetRequiredService(); _mappingCarrierService = _serviceProvider.GetRequiredService(); _codeCountryService = _serviceProvider.GetRequiredService(); bcCompareUrl = AppSetting.app(new string[] { "BCCompare", "Url" }); } #region 保存舱位 /// /// 保存舱位 /// /// 舱位详情 /// 返回输出 public async Task> Save(BookingSlotBaseSaveInput input) { BookingSlotBase model = null; var tenantDb = saasService.GetBizDbScopeById(user.TenantId); if (input.Id > 0) //修改 { var c = tenantDb.Queryable().Where(x => x.SlotBookingNo == input.SlotBookingNo && input.Id != input.Id).Count(); if (c > 0) { //订舱提单号已存在 throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotSlotBookingNoExists))); } model = tenantDb.Queryable().First(x => x.Id == input.Id); var oldObj = model.Adapt(); input.Adapt(model); // 1.判断新的舱位信息的7个库存统计维度是否发生变化 // 2.如果有变化,则需要更新旧的库存信息 bool isNeedUpdateOldStock = false; if (oldObj.Vessel != model.Vessel || oldObj.Voyno != model.Voyno || oldObj.BookingSlotType != model.BookingSlotType || oldObj.CarrierCode != model.CarrierCode || oldObj.PortLoadId != model.PortLoadCode || oldObj.PortDischargeId != model.PortDischargeCode) { isNeedUpdateOldStock = true; } await tenantDb.Updateable(model).ExecuteCommandAsync(); if (isNeedUpdateOldStock) { //更新库存 await _bookingSlotStockService.BookingSlotStock(new BookingSlotStockUpdateModel { BookingSlotType = oldObj.BookingSlotType, CarrierCode = oldObj.CarrierCode, ContractNo = oldObj.ContractNo, Vessel = oldObj.Vessel, Voyno = oldObj.Voyno, PortLoadId = oldObj.PortLoadId, PortDischargeId = oldObj.PortDischargeId, TenantId = long.Parse(user.TenantId) }); } var delCtnList = tenantDb.Queryable().Where(x => x.SlotId == model.Id).ToList(); if (delCtnList.Count > 0) await tenantDb.Deleteable(delCtnList).ExecuteCommandAsync(); if (input.CtnList != null) { foreach (var ctn in input.CtnList) { var newCtn = ctn.Adapt(); newCtn.SlotId = model.Id; await tenantDb.Insertable(newCtn).ExecuteCommandAsync(); } } #region 关联订舱信息修改 if (input.BookingSlotSaleInfoList != null) { foreach (var item in input.BookingSlotSaleInfoList) { if (!item.UpdateFlag) { continue; } var allocation = await tenantDb.Queryable().FirstAsync(x => x.Id == item.Id); if (allocation == null) { //保存失败,原因:更新关联订舱时未查询到订舱(关联表Id:{item.Id}) throw new Exception(string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotSaveFailAllocRecordNull)), item.Id)); } // 更新关联表 item.Adapt(allocation); await tenantDb.Ado.BeginTranAsync(); try { await tenantDb.Updateable(allocation).UpdateColumns(x => new { x.CustomerId, x.CustomerName, x.CustServiceId, x.CustService, x.Sale, x.SaleId, x.Op, x.OpId, x.Doc, x.DocId, x.BusinessId, x.Business, x.Shipper, x.SaleTime, x.GoodsName, x.SellingPrice, x.UpdateBy, //x.UpdatedUserName, x.UpdateTime, }).ExecuteCommandAsync(); // 更新订舱表 //var bookingOrder = await _repBookingOrder.FirstOrDefaultAsync(x => x.Id == allocation.BOOKING_ID); var bookingOrder = _seaExportService.GetSeaExportInfo(allocation.BookingId.ToString()).Data; /* var oldBookingOrder = bookingOrder.Adapt(); if (bookingOrder != null) { bookingOrder.CUSTOMERID = allocation.CUSTOMERID; bookingOrder.CUSTOMERNAME = allocation.CUSTOMERNAME; bookingOrder.CUSTSERVICE = allocation.CUSTSERVICE; bookingOrder.CUSTSERVICEID = allocation.CUSTSERVICEID; bookingOrder.SALE = allocation.SALE; bookingOrder.SALEID = allocation.SALEID; bookingOrder.OP = allocation.OP; bookingOrder.OPID = allocation.OPID; bookingOrder.DOC = allocation.DOC; bookingOrder.DOCID = allocation.DOCID; bookingOrder.BUSINESS = allocation.BUSINESS; bookingOrder.BUSINESSID = allocation.BUSINESSID; await _repBookingOrder.AsUpdateable(bookingOrder).UpdateColumns(x => new { x.CUSTOMERID, x.CUSTOMERNAME, x.CUSTSERVICE, x.CUSTSERVICEID, x.SALE, x.SALEID, x.OP, x.OPID, x.DOC, x.DOCID, x.UpdatedUserId, x.UpdatedUserName, x.UpdatedTime, }).ExecuteCommandAsync(); Parallel.For(0, 1, (n) => { //bookingOrderService.SaveLog(bookingOrder, oldBookingOrder, "舱位关联更新"); // 推送东胜 //bookingOrderService.SendBookingOrder(new long[] { allocation.BOOKING_ID }); }); } */ await tenantDb.Ado.CommitTranAsync(); } catch (Exception) { await tenantDb.Ado.RollbackTranAsync(); //await ex.LogAsync(Db); //return DataResult.Failed(MultiLanguageConst.Operation_Failed); //_repAllocation.CurrentRollbackTran(); throw; } } } #endregion //Parallel.For(0, 1, (n) => //{ // _ = InsLog("Update", model.Id, typeof(BookingSlotBaseSaveInput), oldObj, input, // nameof(BookingSlotBaseApiSaveDto.CtnList), // nameof(BookingSlotBaseApiSaveDto.BookingSlotSaleInfoList)); //}); } else { var c = tenantDb.Queryable().Where(x => x.SlotBookingNo == input.SlotBookingNo).Count(); if (c > 0) { //订舱提单号已存在 throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotSlotBookingNoExists))); } model = input.Adapt(); await tenantDb.Insertable(model).ExecuteReturnEntityAsync(); foreach (var ctn in input.CtnList) { var newCtn = ctn.Adapt(); newCtn.SlotId = model.Id; await tenantDb.Insertable(newCtn).ExecuteCommandAsync(); } //await InsLog("Add", model.Id, "新增舱位"); } //更新库存 await _bookingSlotStockService.BookingSlotStock(new BookingSlotStockUpdateModel { BookingSlotType = model.BookingSlotType, CarrierCode = model.CarrierCode, ContractNo = model.ContractNo, Vessel = model.Vessel, Voyno = model.Voyno, PortLoadId = model.PortLoadCode, PortDischargeId = model.PortDischargeCode, TenantId = long.Parse(user.TenantId) }); var inputDto = new BookingSlotBaseApiDto { DataObj = new BookingSlotBaseApiSaveDto { PortDischargeId = model.PortDischargeCode, PortDischarge = model.PortDischarge, PortLoadId = model.PortLoadCode, PortLoad = model.PortLoad, PlaceDelivery = model.PlaceDelivery, PlaceDeliveryId = model.PlaceDeliveryCode, PlaceReceipt = model.PlaceReceipt, PlaceReceiptId = model.PlaceReceiptCode, } }; //这里自动匹配标签 await GenerateSlotLabel(inputDto, model.Id); return await Detail(model.Id); } #endregion #region 自动生成舱位标签 /// /// 自动生成舱位标签 /// /// 舱位详情 /// 舱位ID /// private async Task> GenerateSlotLabel(BookingSlotBaseApiDto dto, long id) { try { /* 1、获取所有的可用标签 2、只执行REGEX_PATTERN_TXT有值的标签记录。 3、根据提取的标签JSON执行验证,符合条件的自动标记本标签 */ var tenantDb = saasService.GetBizDbScopeById(user.TenantId); var labelList = _bookingLabelService.List(1).GetAwaiter().GetResult().Data; List ruleList = new List(); if (labelList.Count > 0) { for (int i = 0; i < labelList.Count; i++) { if (!string.IsNullOrWhiteSpace(labelList[i].RegexPatternTxt)) { try { var regList = JsonConvert.DeserializeObject>(labelList[i].RegexPatternTxt); if (regList != null && regList.Count > 0) { bool isSucc = true; for (int j = 0; j < regList.Count; j++) { var operEnum = (LabelRegexOperEnum)System.Enum.Parse(typeof(LabelRegexOperEnum), regList[j].oper); if (regList[j].name.Equals("PORTLOADID", StringComparison.OrdinalIgnoreCase)) { isSucc = CheckLabel("PORTLOADID", dto.DataObj.PortLoadId, regList[j].val, regList[j].master, operEnum); if (isSucc) { ruleList.Add(labelList[i]); break; } else { if (regList[j].master) break; continue; } } else if (regList[j].name.Equals("PLACERECEIPT", StringComparison.OrdinalIgnoreCase)) { isSucc = CheckLabel("PLACERECEIPT", dto.DataObj.PlaceReceipt, regList[j].val, regList[j].master, operEnum); if (isSucc) { ruleList.Add(labelList[i]); break; } else { if (regList[j].master) break; continue; } } else if (regList[j].name.Equals("PORTLOAD", StringComparison.OrdinalIgnoreCase)) { isSucc = CheckLabel("PORTLOAD", dto.DataObj.PortLoad, regList[j].val, regList[j].master, operEnum); if (isSucc) { ruleList.Add(labelList[i]); break; } else { if (regList[j].master) break; continue; } } else if (regList[j].name.Equals("PORTDISCHARGEID", StringComparison.OrdinalIgnoreCase)) { isSucc = CheckLabel("PORTDISCHARGEID", dto.DataObj.PortDischargeId, regList[j].val, regList[j].master, operEnum); if (isSucc) { ruleList.Add(labelList[i]); break; } else { if (regList[j].master) break; continue; } } else if (regList[j].name.Equals("PORTDISCHARGE", StringComparison.OrdinalIgnoreCase)) { isSucc = CheckLabel("PORTDISCHARGE", dto.DataObj.PortDischarge, regList[j].val, regList[j].master, operEnum); if (isSucc) { ruleList.Add(labelList[i]); break; } else { if (regList[j].master) break; continue; } } else if (regList[j].name.Equals("PLACEDELIVERY", StringComparison.OrdinalIgnoreCase)) { isSucc = CheckLabel("PLACEDELIVERY", dto.DataObj.PlaceDelivery, regList[j].val, regList[j].master, operEnum); if (isSucc) { ruleList.Add(labelList[i]); break; } else { if (regList[j].master) break; continue; } } } if (isSucc) { Logger.Log(NLog.LogLevel.Info, $"标签对应到有效规则,{labelList[i].Name}"); } } } catch (Exception innEx) { Logger.Log(NLog.LogLevel.Info, $"标签对应失败,{labelList[i].Name},原因:{innEx.Message}"); } } else { continue; } } if (ruleList.Count > 0) { var bindModel = new BindLabelDto { BusinessIdArray = new[] { id }, LabelIdArray = ruleList.Select(a => a.Id.Value).ToArray() }; Logger.Log(NLog.LogLevel.Info, $"标签绑定请求,{JsonConvert.SerializeObject(bindModel)}"); await _bookingLabelService.SetLabel(bindModel); Logger.Log(NLog.LogLevel.Info, $"标签绑定请求完成"); } } } catch (Exception e) { Logger.Log(NLog.LogLevel.Error, $"自动生成舱位标签失败,原因:{e.Message}"); return DataResult.Failed(string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotSlotBookingNoExists)), e.Message)); } return DataResult.Success(string.Empty); } #endregion #region 判断标签识别 /// /// 判断标签识别 /// /// 字段名称 /// 字段值 /// 判断值 /// 是否必校验 /// 操作符枚举 /// true-满足识别 false-不满足识别 private bool CheckLabel(string name, string val, string checkVal, bool isMaster, LabelRegexOperEnum operEnum) { if (operEnum == LabelRegexOperEnum.equal) { if (!string.IsNullOrWhiteSpace(val) && val.Equals(checkVal, StringComparison.OrdinalIgnoreCase)) { return true; } } else if (operEnum == LabelRegexOperEnum.startwith) { if (!string.IsNullOrWhiteSpace(val) && val.StartsWith(checkVal, StringComparison.OrdinalIgnoreCase)) { return true; } } else if (operEnum == LabelRegexOperEnum.like) { if (!string.IsNullOrWhiteSpace(val) && val.Contains(checkVal, StringComparison.OrdinalIgnoreCase)) { return true; } } else if (operEnum == LabelRegexOperEnum.notequal) { if (!string.IsNullOrWhiteSpace(val)) { if (!val.Equals(checkVal, StringComparison.OrdinalIgnoreCase)) { return true; } else { if (isMaster) return false; } } } else if (operEnum == LabelRegexOperEnum.notexists) { if (!string.IsNullOrWhiteSpace(val)) { if (!val.Contains(checkVal, StringComparison.OrdinalIgnoreCase)) { return true; } else { if (isMaster) return false; } } } else if (operEnum == LabelRegexOperEnum.notstartwith) { if (!string.IsNullOrWhiteSpace(val)) { if (!val.StartsWith(checkVal, StringComparison.OrdinalIgnoreCase)) { return true; } else { if (isMaster) return false; } } } return false; } #endregion #region 获取舱位详情 /// /// 获取舱位详情 /// /// 舱位主键 /// 返回舱位详情 public async Task> Detail(long id) { var tenantDb = saasService.GetBizDbScopeById(user.TenantId); var slotBase = await tenantDb.Queryable().FirstAsync(u => u.Id == id); if (slotBase == null) { //未查询到此舱位信息,已删除或不存在 throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotBaseInfoNull))); } var ctns = await tenantDb.Queryable().Where(x => x.SlotId == id).ToListAsync(); var rtn = slotBase.Adapt(); rtn.CtnList = ctns.Adapt>(); rtn.LogList = _logAuditService.GetAuditLogBookingList(slotBase.Id).GetAwaiter().GetResult()?.Data; // 赋值关联的订舱列表 // 查询舱位绑定的销售信息,赋值到舱位对象中 List saleInfoList = await tenantDb.Queryable() .Where(x => x.BookingSlotId == id) .Select(x => new BookingSlotSaleInfoDto { Id = x.Id, BookingId = x.BookingId, BookingSlotId = x.BookingSlotId, CustomerId = x.CustomerId, CustomerName = x.CustomerName, CustServiceId = x.CustServiceId, CustService = x.CustService, SaleId = x.SaleId, Sale = x.Sale, OpId = x.OpId, Op = x.Op, DocId = x.DocId, Doc = x.Doc, BusinessId = x.BusinessId, Business = x.Business, SaleTime = x.SaleTime, Shipper = x.Shipper, GoodsName = x.GoodsName, SellingPrice = x.SellingPrice, CreatedUserId = x.CreateBy }).ToListAsync(); /* if (saleInfoList.Any()) { // 判断是否启用了委托单位查看控制权限 var tenantParamList = await _cache.GetAllTenantParam(); var isEnableCustomerAuthority = tenantParamList.FirstOrDefault(x => x.TenantId == UserManager.TENANT_ID && x.ParaCode == TenantParamCode.IS_ENABLE_CUSTOMER_AUTHORITY)?.ItemCode == "YES"; List userList = null; List userListStr = null; if (isEnableCustomerAuthority) { userList = await _sysDataUserMenuService.GetDataScopeList(MenuConst.MenuDjyCustomer); if (userList != null && userList.Count > 0) { userListStr = userList.Select(x => x.ToString()).ToList(); // 遍历销售信息,如果销售信息中的“销售、操作、单证、客服、创建人”中不在当前登陆人的权限范围,则隐藏客户信息 foreach (BookingSlotSaleInfoDto saleInfoItem in saleInfoList) { if (!userList.Contains(saleInfoItem.CreatedUserId ?? 0) && !userListStr.Contains(saleInfoItem.OPID) && !userListStr.Contains(saleInfoItem.DOCID) && !userListStr.Contains(saleInfoItem.SALEID) && !userListStr.Contains(saleInfoItem.CUSTSERVICEID)) { saleInfoItem.CUSTOMERID = 0; saleInfoItem.CUSTOMERNAME = "--"; saleInfoItem.OPID = ""; saleInfoItem.OP = "--"; saleInfoItem.DOCID = ""; saleInfoItem.DOC = "--"; saleInfoItem.SALEID = ""; saleInfoItem.SALE = "--"; saleInfoItem.SHIPPER = "--"; saleInfoItem.GOODSNAME = "--"; saleInfoItem.CUSTSERVICEID = ""; saleInfoItem.CUSTSERVICE = "--"; } } } } } */ rtn.BookingSlotSaleInfoList = saleInfoList; return DataResult.Success(rtn); } #endregion #region 舱位接收保存、取消接口 /// /// 舱位接收保存、取消接口 /// /// 请求详情(JSON) /// BC附件 /// BC修改附件 /// 返回回执 public async Task> ApiReceive(string jsonData, IFormFile file = null, IFormFile modifyFile = null) { long id = 0; try { Logger.Log(NLog.LogLevel.Info, $"jsonData={jsonData} 接收请求舱位报文"); BookingSlotBaseApiDto dto = JsonConvert.DeserializeObject(jsonData); DynameFileInfo bcFile = null; DynameFileInfo bcNoticeFile = null; if (file != null) { bcFile = new DynameFileInfo { FileBytes = file.ToByteArray(), FileName = file.FileName }; } if (modifyFile != null) { bcNoticeFile = new DynameFileInfo { FileBytes = modifyFile.ToByteArray(), FileName = modifyFile.FileName }; } var rlt = await InnerApiReceive(dto, bcFile, bcNoticeFile); id = rlt.Data; } catch (Exception ex) { Logger.Log(NLog.LogLevel.Error, $"执行舱位失败,原因:{ex.Message}"); return DataResult.FailedData(id, string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotApiReceiveError)), ex.Message)); } if (id == 0) return DataResult.FailedData(id); return DataResult.Success(id); } /// /// 舱位接收保存、取消接口(任务台使用) /// public async Task> ApiReceiveTask(TaskFlowDataContext dataContext) { //var a = user.UserId; //var b = user.UserName; //var c = user.TenantId; var messageInfo = dataContext.Get(TaskFlowDataNameConst.TaskManageOrderMessageInfo); if (messageInfo == null) { throw new ArgumentNullException($"缺少参数:{TaskFlowDataNameConst.TaskManageOrderMessageInfo}"); } DynameFileInfo bcFileInfo = null; DynameFileInfo bcNoticeFileInfo = null; var bcfile = dataContext.Get(TaskFlowDataNameConst.BCFile); if (bcfile != null) { bcFileInfo = new DynameFileInfo { FileBytes = bcfile.ToByteArray(), FileName = bcfile.FileName }; } var bcNoticeFile = dataContext.Get(TaskFlowDataNameConst.AmendmentFile); if (bcNoticeFile != null) { bcNoticeFileInfo = new DynameFileInfo { FileBytes = bcNoticeFile.ToByteArray(), FileName = bcNoticeFile.FileName }; } var taskBCInfoDto = messageInfo.Main.BCInfo; var allMapCarrierList = await _mappingCarrierService.GetAllList(); MappingCarrierRes? carrierInfo = null; if (allMapCarrierList.Succeeded) { //carrierInfo = allMapCarrierList.Data.FirstOrDefault(t => t.LinkId == taskBCInfoDto.CarrierId && t.Module == CONST_MAPPING_CARRIER_MODULE); carrierInfo = allMapCarrierList.Data.Where(t => t.MapCode.Equals(taskBCInfoDto.CarrierId, StringComparison.OrdinalIgnoreCase) && t.Module == CONST_MAPPING_CARRIER_MODULE).FirstOrDefault(); } BookingSlotBaseApiDto slotModel = new BookingSlotBaseApiDto { DataObj = new BookingSlotBaseApiSaveDto { CarrierId = carrierInfo?.LinkId, CarrierCode = carrierInfo?.Code, SlotBookingNo = taskBCInfoDto.MBLNo, BookingParty = taskBCInfoDto.BookingParty, BookingSlotType = taskBCInfoDto.BookingSlotType, BookingSlotTypeName = taskBCInfoDto.BookingSlotTypeName, Vessel = taskBCInfoDto.Vessel, Voyno = taskBCInfoDto.VoyNo, VGMSubmissionCutDate = taskBCInfoDto.VGMCutoffTime, //WeekAt = taskBCInfoDto.WeekAt, CarriageType = taskBCInfoDto.CarriageType, CarriageTypeName = taskBCInfoDto.CarriageTypeName, ContractNo = taskBCInfoDto.ContractNo, CtnStat = taskBCInfoDto.CtnStat, CYCutDate = taskBCInfoDto.CYCutoffTime, DetensionFreeDays = taskBCInfoDto.DetensionFreeDays, ETD = taskBCInfoDto.ETD, ETA = taskBCInfoDto.ETA, LaneCode = taskBCInfoDto.LaneCode, LaneName = taskBCInfoDto.LaneName, ManifestCutDate = taskBCInfoDto.ManifestCutDate, MDGFCutDate = taskBCInfoDto.MDGFCutDate, PlaceDelivery = taskBCInfoDto.PlaceDelivery, PlaceReceipt = taskBCInfoDto.PlaceReceipt, PortDischarge = taskBCInfoDto.PortDischarge, PortLoad = taskBCInfoDto.Portload, SICutDate = taskBCInfoDto.SICutDate, //CustomSICutDate = taskBCInfoDto.CustomSICutDate, TransferPort1 = taskBCInfoDto.TransferPort1, TransferPort2 = taskBCInfoDto.TransferPort2, PriceCalculationDate = taskBCInfoDto.PriceCalculationDate, CtnList = new List() }, OpType = "add" }; if (int.TryParse(taskBCInfoDto.WeekAt, out int week)) { slotModel.DataObj.WeekAt = week; } if (taskBCInfoDto.CtnList.Count > 0) { taskBCInfoDto.CtnList.ForEach(t => { BookingSlotCtnSaveInput ctn = new BookingSlotCtnSaveInput { CtnAll = t.CtnALL, CtnNum = t.CTNNUM.HasValue ? t.CTNNUM.Value : 1 }; slotModel.DataObj.CtnList.Add(ctn); }); } var rlt = await InnerApiReceive(slotModel, bcFileInfo, bcNoticeFileInfo); return rlt; } #endregion #region 舱位接收保存、取消接口(内部接口) /// /// 舱位接收保存、取消接口(内部接口) /// /// 舱位详情 /// 原文件 /// 修改文件 /// 返回回执 public async Task> InnerApiReceive(BookingSlotBaseApiDto dto, DynameFileInfo file = null, DynameFileInfo modifyFile = null) { long id = 0; var tenantDb = saasService.GetBizDbScopeById(user.TenantId); var slotBase = await tenantDb.Queryable().Where(x => x.Id == 1814212362220212224).ToListAsync(); //接口方法直接调用save、delete等方法会报错,可能因为非token授权登录导致,故重写一遍保存、删除代码 if (dto.OpType == "add" || dto.OpType == "update" || dto.OpType == "del" || dto.OpType == "cancellation") { //翻译船公司 if (!string.IsNullOrWhiteSpace(dto.DataObj.CarrierCode) && string.IsNullOrWhiteSpace(dto.DataObj.CarrierCode)) { var allCarrierList = await _mappingCarrierService.GetAllList(); if (allCarrierList.Succeeded) { var carrierInfo = allCarrierList.Data.Where(t => t.Code.Equals(dto.DataObj.CarrierCode, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); if (carrierInfo != null) { dto.DataObj.Carrier = carrierInfo.MapName?.Trim(); } } } //翻译箱型代码 if (dto.DataObj.CtnList != null && dto.DataObj.CtnList.Count > 0 && dto.DataObj.CtnList.Any(t => string.IsNullOrWhiteSpace(t.CtnCode))) { List ctnCodeList = new List(); var allCtnCodeList = await _codeCtnService.GetAllList(); if (allCtnCodeList.Succeeded) { ctnCodeList = allCtnCodeList.Data; } dto.DataObj.CtnList.ForEach(t => { if (!string.IsNullOrWhiteSpace(t.CtnAll) && string.IsNullOrWhiteSpace(t.CtnCode)) { var ctnCode = ctnCodeList.FirstOrDefault(a => !string.IsNullOrWhiteSpace(a.CtnName) && a.CtnName.Equals(t.CtnAll, StringComparison.OrdinalIgnoreCase)); if (ctnCode != null) t.CtnCode = $"{ctnCode.CtnSize}{ctnCode.CtnType}"; } }); } List portCodeList = new List(); // 解析收货地,得到装货港名称及五字码 if (!string.IsNullOrWhiteSpace(dto.DataObj.PlaceReceipt)) { var portEnName = dto.DataObj.PlaceReceipt.Split(',')[0]?.Trim(); //这里CMA的收货地全称放在了括号里面 if (dto.DataObj.CarrierCode.Equals("CMA", StringComparison.OrdinalIgnoreCase)) { if (!string.IsNullOrWhiteSpace(dto.DataObj.PlaceReceipt)) dto.DataObj.PlaceReceipt = dto.DataObj.PlaceReceipt.Replace("(", "(").Replace(")", ")"); if (dto.DataObj.PlaceReceipt.IndexOf("(") >= 0) { string currStr = Regex.Match(dto.DataObj.PlaceReceipt, "(?<=\\().*(?=\\))").Value?.Trim(); portEnName = currStr.Split(',')[0]?.Trim(); } } if (!string.IsNullOrWhiteSpace(portEnName)) { var allPortCodeList = await _codePortService.GetAllList(); if (allPortCodeList.Succeeded) { portCodeList = allPortCodeList.Data; } //var cachePortLoad = await _cache.GetAllCodePortLoad(); var portInfo = await PlaceReceiptToPortload(portEnName, portCodeList, () => _mappingPortService.GetAllList()); if (!portInfo.Succeeded) { Logger.Log(NLog.LogLevel.Info, $"通过收货地城市名称未匹配到港口信息,订舱编号:{dto.DataObj.SlotBookingNo}"); } else { dto.DataObj.PortLoad = portInfo.Data.PortName; dto.DataObj.PortLoadId = portInfo.Data.EdiCode; } } else { Logger.Log(NLog.LogLevel.Info, $"收货地分割后得到的城市名称为空,订舱编号:{dto.DataObj.SlotBookingNo}"); } } else { Logger.Log(NLog.LogLevel.Info, $"收货地为空,订舱编号:{dto.DataObj.SlotBookingNo}"); } // 解析交货地,得到为卸货港名称及五字码, 以及国家信息 if (!string.IsNullOrWhiteSpace(dto.DataObj.PlaceDelivery)) { var portEnName = dto.DataObj.PlaceDelivery.Split(',')[0]?.Trim(); //这里CMA的收货地全称放在了括号里面 if (dto.DataObj.CarrierCode.Equals("CMA", StringComparison.OrdinalIgnoreCase)) { if (!string.IsNullOrWhiteSpace(dto.DataObj.PlaceDelivery)) dto.DataObj.PlaceDelivery = dto.DataObj.PlaceDelivery.Replace("(", "(").Replace(")", ")"); if (dto.DataObj.PlaceDelivery.IndexOf("(") >= 0) { string currStr = Regex.Match(dto.DataObj.PlaceDelivery, "(?<=\\().*(?=\\))").Value?.Trim(); portEnName = currStr.Split(',')[0]?.Trim(); } } if (!string.IsNullOrWhiteSpace(portEnName)) { if (portCodeList.Count == 0) { var allPortCodeList = await _codePortService.GetAllList(); if (allPortCodeList.Succeeded) { portCodeList = allPortCodeList.Data; } } var portInfo = await PlaceDeliveryToPort(portEnName, portCodeList, () => _mappingPortService.GetAllList()); if (portInfo.Succeeded) { Logger.Log(NLog.LogLevel.Info, $"通过交货地城市名称未匹配到港口信息,订舱编号:{dto.DataObj.SlotBookingNo}"); } else { var allCountryCodeList = await _codeCountryService.GetAllList(); if (allCountryCodeList.Succeeded) { var countryInfo = allCountryCodeList.Data.FirstOrDefault(p => p.Id == portInfo.Data?.CountryId); dto.DataObj.PortDischargeCountry = countryInfo?.CountryEnName; dto.DataObj.PortDischargeCountryCode = countryInfo?.CountryCode; } dto.DataObj.PortDischarge = portInfo.Data?.PortName; dto.DataObj.PortDischargeId = portInfo.Data?.EdiCode; } } else { Logger.Log(NLog.LogLevel.Info, $"交货地分割后得到的城市名称为空,订舱编号:{dto.DataObj.SlotBookingNo}"); } } else { Logger.Log(NLog.LogLevel.Info, $"交货地为空,订舱编号:{dto.DataObj.SlotBookingNo}"); } if (string.IsNullOrWhiteSpace(dto.DataObj.CtnStat)) { if (dto.DataObj.CtnList != null && dto.DataObj.CtnList.Count > 0) { dto.DataObj.CtnStat = string.Join(",", dto.DataObj.CtnList.GroupBy(a => a.CtnAll).Select(a => $"{a.Key}*{a.Select(b => b.CtnNum).Sum()}").ToArray()); } } //自动转换对应标签 BookingSlotBase model = null; if (dto.OpType == "add") { var c = tenantDb.Queryable().Where(x => x.SlotBookingNo == dto.DataObj.SlotBookingNo).Count(); if (c > 0) { //订舱提单号已存在 throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotSlotBookingNoExists))); } model = dto.DataObj.Adapt(); await tenantDb.Insertable(model).ExecuteReturnEntityAsync(); id = model.Id; foreach (var ctn in dto.DataObj.CtnList) { var newCtn = ctn.Adapt(); newCtn.SlotId = model.Id; await tenantDb.Insertable(newCtn).ExecuteCommandAsync(); } //await InsLog("Add", model.Id, "新增舱位"); string batchNo = GuidHelper.GetSnowflakeId(); //处理附件 if (file != null) { Logger.Log(NLog.LogLevel.Info, $"请求文件名:{file.FileName}"); var fileRlt = await _sysFileService.SaveFileDirect(model.Id.ToString(), file.FileBytes, batchNo, file.FileName, "bcfiles"); var fileFullPath = fileRlt.Data; Logger.Log(NLog.LogLevel.Info, $"保存文件路径:{fileFullPath}"); if (!string.IsNullOrWhiteSpace(fileFullPath)) { //将格式单附件写入订舱的附件 SaveEDIFile(id, fileFullPath, file.FileName, long.Parse(user.TenantId), CONST_BC_FILE_CODE, CONST_BC_FILE_NAME).GetAwaiter(); } } if (modifyFile != null) { Logger.Log(NLog.LogLevel.Info, $"请求文件名(变更文件):{modifyFile.FileName}"); var fileRlt = await _sysFileService.SaveFileDirect(model.Id.ToString(), modifyFile.FileBytes, batchNo, modifyFile.FileName, "bcnoticefiles"); var fileFullPath = fileRlt.Data; Logger.Log(NLog.LogLevel.Info, $"保存文件路径(变更文件):{fileFullPath}"); if (!string.IsNullOrWhiteSpace(fileFullPath)) { //将格式单附件写入订舱的附件 SaveEDIFile(id, fileFullPath, modifyFile.FileName, long.Parse(user.TenantId), CONST_BC_NOTICE_FILE_CODE, CONST_BC_NOTICE_FILE_NAME).GetAwaiter(); } } //触发标签自动绑定 await GenerateSlotLabel(dto, id); } else if (dto.OpType == "update") { model = await tenantDb.Queryable().FirstAsync(x => x.SlotBookingNo == dto.DataObj.SlotBookingNo); if (model == null) { //throw Oops.Bah($"未找到订舱编号为 {dto.DataObj.SlotBookingNo} 的数据"); } id = model.Id; //生成待比对详情 ParserBCInfoDto bcSrcDto = new ParserBCInfoDto(); try { bcSrcDto = model.Adapt(); } catch (Exception ex) { Logger.Log(NLog.LogLevel.Info, $"slotId={model.Id} 映射数据库对象请求对应异常,原因:{ex.Message}"); //throw Oops.Bah($"slotId={model.Id} 映射数据库对象请求对应异常,原因:{ex.Message}"); } ParserBCInfoDto bcTargetDto = new ParserBCInfoDto(); try { bcTargetDto = dto.DataObj.Adapt(); } catch (Exception ex) { Logger.Log(NLog.LogLevel.Info, $"slotId={model.Id} 映射推送的舱位请求对应异常,原因:{ex.Message}"); //throw Oops.Bah($"slotId={model.Id} 映射推送的舱位请求对应异常,原因:{ex.Message}"); } Logger.Log(NLog.LogLevel.Info, $"slotId={model.Id} 开始处理重要提醒"); //执行差异重要提醒 //await MeasureDiffCautionTask(bcSrcDto, bcTargetDto, model.Id); Logger.Log(NLog.LogLevel.Info, $"slotId={model.Id} 处理重要提醒结束"); //提取箱信息 var ctnList = tenantDb.Queryable() .Where(x => x.SlotId == model.Id).ToList(); if (ctnList != null) { bcSrcDto.CtnList = ctnList.GroupBy(x => x.CtnAll) .Select(x => { return new ParserBCCTNInfoDto { CtnALL = x.Key, CtnNum = x.ToList() .Sum(a => a.CtnNum) }; }).ToList(); } if (dto.DataObj.CtnList != null && dto.DataObj.CtnList.Count > 0) { bcTargetDto.CtnList = dto.DataObj.CtnList.GroupBy(x => x.CtnAll) .Select(x => { return new ParserBCCTNInfoDto { CtnALL = x.Key, CtnNum = x.ToList() .Sum(a => a.CtnNum) }; }).ToList(); } var oldObj = model.Adapt(); dto.DataObj.Adapt(model); // 1.判断新的舱位信息的7个库存统计维度是否发生变化 // 2.如果有变化,则需要更新旧的库存信息 bool isNeedUpdateOldStock = false; if (oldObj.Vessel != model.Vessel || oldObj.Voyno != model.Voyno || oldObj.BookingSlotType != model.BookingSlotType || oldObj.CarrierId != model.CarrierId || oldObj.PortLoadId != model.PortLoadCode || oldObj.PortDischargeId != model.PortDischargeCode) { isNeedUpdateOldStock = true; } await tenantDb.Updateable(model).ExecuteCommandAsync(); if (isNeedUpdateOldStock) { //BookingSlotStock:Update await _bookingSlotStockService.BookingSlotStock(new BookingSlotStockUpdateModel { BookingSlotType = oldObj.BookingSlotType, CarrierCode = oldObj.CarrierCode, ContractNo = oldObj.ContractNo, Vessel = oldObj.Vessel, Voyno = oldObj.Voyno, PortLoadId = oldObj.PortLoadId, PortDischargeId = oldObj.PortDischargeId, TenantId = long.Parse(user.TenantId) }); } var currCtnList = await tenantDb.Queryable().Where(p => p.SlotId == model.Id).ToListAsync(); if (currCtnList.Count > 0) { currCtnList.ForEach(async p => { await tenantDb.Deleteable(p).ExecuteCommandAsync(); }); } foreach (var ctn in dto.DataObj.CtnList) { var newCtn = ctn.Adapt(); newCtn.SlotId = model.Id; await tenantDb.Insertable(newCtn).ExecuteCommandAsync(); } //await InsLog("Update", model.Id, typeof(BookingSlotBaseApiSaveDto), oldObj, dto.DataObj, nameof(BookingSlotBaseApiSaveDto.CtnList), nameof(BookingSlotBaseApiSaveDto.BookingSlotSaleInfoList)); string batchNo = GuidHelper.GetSnowflakeId(); //处理附件 if (file != null) { Logger.Log(NLog.LogLevel.Info, $"请求文件名:{file.FileName}"); var fileRlt = await _sysFileService.SaveFileDirect(model.Id.ToString(), file.FileBytes, batchNo, file.FileName, "bcmoidfyfiles"); var fileFullPath = fileRlt.Data; Logger.Log(NLog.LogLevel.Info, $"保存文件路径:{fileFullPath}"); if (!string.IsNullOrWhiteSpace(fileFullPath)) { //将格式单附件写入订舱的附件 SaveEDIFile(id, fileFullPath, file.FileName, long.Parse(user.TenantId), CONST_BC_MODIFY_FILE_CODE, CONST_BC_MODIFY_FILE_NAME).GetAwaiter(); } } if (modifyFile != null) { Logger.Log(NLog.LogLevel.Info, $"请求文件名(变更文件):{modifyFile.FileName}"); var fileRlt = await _sysFileService.SaveFileDirect(model.Id.ToString(), modifyFile.FileBytes, batchNo, modifyFile.FileName, "bcmodifynoticefiles"); var fileFullPath = fileRlt.Data; Logger.Log(NLog.LogLevel.Info, $"保存文件路径(变更文件):{fileFullPath}"); if (!string.IsNullOrWhiteSpace(fileFullPath)) { //将格式单附件写入订舱的附件 SaveEDIFile(id, fileFullPath, modifyFile.FileName, long.Parse(user.TenantId), CONST_BC_MODIFY_NOTICE_FILE_CODE, CONST_BC_MODIFY_NOTICE_FILE_NAME).GetAwaiter(); } } //一般更新数据指的是Booking Amendment,需要与舱位进行数据比对 await PushCompareBCInfo(bcSrcDto, bcTargetDto, id, dto.BatchNo); //触发标签自动绑定 await GenerateSlotLabel(dto, id); } else if (dto.OpType == "del") { var slotNO = dto.DataObj.SlotBookingNo; model = await tenantDb.Queryable().FirstAsync(x => x.SlotBookingNo == slotNO); if (model == null) { //throw Oops.Bah($"未找到订舱编号为 {slotNO} 的数据"); } id = model.Id; model.Deleted = true; await tenantDb.Updateable(model).ExecuteCommandAsync(); var ctns = await tenantDb.Queryable().Where(x => x.SlotId == model.Id).ToListAsync(); foreach (var ctn in ctns) { ctn.Deleted = true; await tenantDb.Updateable(ctn).ExecuteCommandAsync(); } //await InsLog("Del", model.Id, "取消舱位"); } else if (dto.OpType == "cancellation") { // 更新标志 var slotNO = dto.DataObj.SlotBookingNo; model = await tenantDb.Queryable().FirstAsync(x => x.SlotBookingNo == slotNO); if (model == null) { //throw Oops.Bah($"未找到订舱编号为 {slotNO} 的数据"); } id = model.Id; model.IsCancellation = true; model.CancellationDate = DateTime.Now; await tenantDb.Updateable(model).EnableDiffLogEvent().ExecuteCommandAsync(); // 删除该舱位相关的订舱关联关系 var slotList = await tenantDb.Queryable().Where(a => a.BookingSlotId == id).ToListAsync(); var slotIdList = slotList.Select(s => s.Id); await tenantDb.Updateable() .SetColumns(a => a.Deleted == true) .SetColumns(a => a.UpdateTime == DateTime.Now) .SetColumns(a => a.UpdateBy == long.Parse(user.UserId)) //.SetColumns(a => a.UpdatedUserName == UserManager.Name) .Where(a => slotIdList.Contains(a.Id)) .ExecuteCommandAsync(); await tenantDb.Updateable() .SetColumns(a => a.Deleted == true) .SetColumns(a => a.UpdateTime == DateTime.Now) .SetColumns(a => a.UpdateBy == long.Parse(user.UserId)) //.SetColumns(a => a.UpdatedUserName == UserManager.Name) .Where(a => slotIdList.Contains(a.SlotAllocId)) .ExecuteCommandAsync(); //await InsLog("Cancellation", model.Id, "取消舱位"); } //更新库存 await _bookingSlotStockService.BookingSlotStock(new BookingSlotStockUpdateModel { BookingSlotType = model.BookingSlotType, CarrierCode = model.CarrierCode, ContractNo = model.ContractNo, Vessel = model.Vessel, Voyno = model.Voyno, PortLoadId = model.PortLoadCode, PortDischargeId = model.PortDischargeCode, TenantId = long.Parse(user.TenantId) }); } else { //throw Oops.Bah("操作类型参数有误"); } if (id == 0) return DataResult.FailedData(id); return DataResult.Success(id); } #endregion #region 异步写入附件表 /// /// 异步写入附件表 /// /// 订舱ID /// 文件路径 /// 文件名 /// 租户ID /// 附件类型代码 /// 附件类型名称 /// 附件模块代码 /// private async Task SaveEDIFile(long boookId, string FilePath, string fileName, long tenantId, string fileTypeCode = "bc", string fileTypeName = "Booking Confirmation", string moudle = "BookingSlot") { /* 直接将附件信息写入附件表 */ var tenantDb = saasService.GetBizDbScopeById(user.TenantId); var newFile = new OpFile { Id = SnowFlakeSingle.Instance.NextId(), FileName = fileName, FilePath = FilePath, TypeCode = fileTypeCode, TypeName = fileTypeName, LinkId = boookId, }; await tenantDb.Insertable(newFile).ExecuteCommandAsync(); } #endregion #region 根据收货地港口英文名解析出起始港对象 /// /// 根据收货地港口英文名解析出起始港对象 /// /// 收货地港口英文名 /// 起始港缓存 /// 起始港缓存映射 /// 起始港对象 private async Task> PlaceReceiptToPortload(string portEnName, List cachePortLoad, Func>>> cacheMapPortLoadFunc) { CodePortRes portInfo = null; if (string.IsNullOrEmpty(portEnName)) { return DataResult.FailedData(portInfo); } // 匹配方式1:精准匹配 portInfo = cachePortLoad.FirstOrDefault(x => x.PortName.Equals(portEnName, StringComparison.OrdinalIgnoreCase)); if (portInfo != null) return DataResult.Success(portInfo); // 匹配方式2:起始模糊匹配 portInfo = cachePortLoad.FirstOrDefault(x => x.PortName.StartsWith(portEnName, StringComparison.OrdinalIgnoreCase)); if (portInfo != null) return DataResult.Success(portInfo); // 匹配方式3:完整模糊匹配 portInfo = cachePortLoad.FirstOrDefault(x => x.PortName.Contains(portEnName, StringComparison.OrdinalIgnoreCase)); if (portInfo != null) return DataResult.Success(portInfo); // 匹配方式4:精准映射匹配 var mapCachePortLoad = await cacheMapPortLoadFunc(); var map = mapCachePortLoad.Data.FirstOrDefault(x => x.Module == RECEIPT_TO_PORTLOAD && x.MapName.Equals(portEnName, StringComparison.OrdinalIgnoreCase)); if (map != null) { portInfo = cachePortLoad.FirstOrDefault(x => x.Id == map.LinkId); if (portInfo != null) return DataResult.Success(portInfo); } return DataResult.FailedData(portInfo); } #endregion #region 根据交货地港口英文名解析出目的港对象 /// /// 根据交货地港口英文名解析出目的港对象 /// /// 交货地港口英文名 /// 目的港缓存 /// 目的港缓存映射 /// 目的港对象 private async Task> PlaceDeliveryToPort(string portEnName, List cachePort, Func>>> cacheMapPortFunc) { CodePortRes portInfo = null; if (string.IsNullOrEmpty(portEnName)) { return DataResult.FailedData(portInfo); } // 匹配方式1:精准匹配 portInfo = cachePort.FirstOrDefault(x => x.PortName.Equals(portEnName, StringComparison.OrdinalIgnoreCase)); if (portInfo != null) return DataResult.Success(portInfo); // 匹配方式2:起始模糊匹配 portInfo = cachePort.FirstOrDefault(x => x.PortName.StartsWith(portEnName, StringComparison.OrdinalIgnoreCase)); if (portInfo != null) return DataResult.Success(portInfo); // 匹配方式3:完整模糊匹配 portInfo = cachePort.FirstOrDefault(x => x.PortName.Contains(portEnName, StringComparison.OrdinalIgnoreCase)); if (portInfo != null) return DataResult.Success(portInfo); // 匹配方式4:精准映射匹配 var mapCachePort = await cacheMapPortFunc(); var map = mapCachePort.Data.FirstOrDefault(x => x.Module == DELIVERY_TO_PORT && x.MapName.Equals(portEnName, StringComparison.OrdinalIgnoreCase)); if (map != null) { portInfo = cachePort.FirstOrDefault(x => x.Id == map.LinkId); if (portInfo != null) return DataResult.Success(portInfo); } return DataResult.FailedData(portInfo); } #endregion #region 推送BC变更比对 /// /// 推送BC变更比对 /// /// 原舱位详情 /// 变更后舱位详情 /// 舱位主键 /// 请求批次号用来区分对应的哪个批次任务 /// [NonAction] public async Task PushCompareBCInfo(ParserBCInfoDto bcSrcDto, ParserBCInfoDto bcTargetDto, long slotId, string reqBatchNo) { var tenantDb = saasService.GetBizDbScopeById(user.TenantId); string batchNo = GuidHelper.GetSnowflakeId(); DateTime bDate = DateTime.Now; Logger.Log(NLog.LogLevel.Info, $"批次={batchNo} slotId={slotId} 开始请求比对结果"); var rlt = await ExcuteCompare(bcSrcDto, bcTargetDto); var compareResult = rlt.Data; Logger.Log(NLog.LogLevel.Info, $"批次={batchNo} slotId={slotId} 请求比对结果完成,结果={JsonConvert.SerializeObject(compareResult)}"); DateTime eDate = DateTime.Now; TimeSpan ts = eDate.Subtract(bDate); var timeDiff = ts.TotalMilliseconds; if (compareResult != null) { Logger.Log(NLog.LogLevel.Info, "批次={no} 请求完成,耗时:{timeDiff}ms. 结果{msg}", batchNo, timeDiff, compareResult.succ ? "成功" : "失败"); } if (compareResult != null) { DateTime nowDate = DateTime.Now; var hisInfo = tenantDb.Queryable().First(a => a.CompareBatchNo == reqBatchNo); if (hisInfo == null) { BookingSlotCompare entity = new BookingSlotCompare { SlotId = slotId, CompareBatchNo = reqBatchNo, CompareDiffNum = compareResult.extra.IsExistsDiff ? compareResult.extra.ShowDetailList.Count : 0, CreateTime = nowDate, UpdateTime = nowDate, CreateBy = long.Parse(user.UserId), CreateUserName = user.UserName, UpdateBy = long.Parse(user.UserId), UpdateUserName = user.UserName, CompareType = "BC_MODIFY", CompareRlt = JsonConvert.SerializeObject(compareResult.extra.ShowDetailList), }; await tenantDb.Insertable(entity).ExecuteCommandAsync(); } else { hisInfo.CompareDiffNum = compareResult.extra.IsExistsDiff ? compareResult.extra.ShowDetailList.Count : 0; hisInfo.UpdateTime = nowDate; hisInfo.UpdateBy = long.Parse(user.UserId); hisInfo.UpdateUserName = user.UserName; hisInfo.CompareRlt = JsonConvert.SerializeObject(compareResult.extra.ShowDetailList); await tenantDb.Updateable(hisInfo).UpdateColumns(it => new { it.CompareDiffNum, it.CompareRlt, it.UpdateTime, it.UpdateBy, it.UpdateUserName }).ExecuteCommandAsync(); } //throw Oops.Oh($"舱位主键{slotId}请求BC比对失败,返回为空"); //if (compareResult.extra.ShowDetailList == null || compareResult.extra.ShowDetailList.Count == 0) //{ // new EmailNoticeHelper().SendEmailNotice($"MBLNO={bcSrcDto.MBLNo} 与舱位比对差异失败,比对结果为0", $"MBLNO={bcSrcDto.MBLNo} 与舱位比对差异失败,比对结果为0", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList()); //} } else { //new EmailNoticeHelper().SendEmailNotice($"MBLNO={bcSrcDto.MBLNo} 与舱位比对差异失败,未获取到比对结果", $"MBLNO={bcSrcDto.MBLNo} 与舱位比对差异失败,未获取到比对结果", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList()); } } #endregion #region 请求BC比对 /// /// 请求BC比对 /// /// BC详情 /// BC变更后详情 /// 返回回执 public async Task> ExcuteCompare(ParserBCInfoDto bcSrcDto, ParserBCInfoDto bcTargetDto) { TaskManageExcuteResultDto model = null; /* 1、读取配置文件中的规则引擎URL 2、填充请求的类,并生成JSON报文 3、POST请求接口,并记录回执。 4、返回信息。 */ var url = bcCompareUrl; //App.Configuration["BCCompareUrl"]; using (var httpClient = new HttpClient()) { try { using (var reduceAttach = new MultipartFormDataContent()) { var dataContent = new ByteArrayContent(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(bcSrcDto))); dataContent.Headers.ContentDisposition = new ContentDispositionHeaderValue($"form-data") { Name = "srcJson" }; reduceAttach.Add(dataContent); var dataContent2 = new ByteArrayContent(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(bcTargetDto))); dataContent2.Headers.ContentDisposition = new ContentDispositionHeaderValue($"form-data") { Name = "destJson" }; reduceAttach.Add(dataContent2); //请求 var response = httpClient.PostAsync(url, reduceAttach).Result; var result = response.Content.ReadAsStringAsync().Result; model = JsonConvert.DeserializeObject(result); Logger.Log(NLog.LogLevel.Info, $"推送BC比返回结果:{JsonConvert.SerializeObject(model)}"); } } catch (Exception ex) { Logger.Log(NLog.LogLevel.Error, "推送BC比对异常,原因:{error}", ex.Message); // throw Oops.Oh($"推送BC比对异常,原因:{ex.Message}"); } } if (model != null) return DataResult.Success(model); return DataResult.FailedData(model); ; } #endregion #region 获取合票详情(生成合票需要先调此方法) /// /// 获取合票详情(生成合票需要先调此方法) /// /// /// public async Task GetMergeList(QueryMergeSlotDto model) { BookingSlotMergeResultDto rlt = new BookingSlotMergeResultDto(); var tenantDb = saasService.GetBizDbScopeById(user.TenantId); List slotList = new List(); if (model != null) { /* 合票时需要校对的内容 */ //船公司、船名场次、合约号、承运方式(直达、中转)、订舱方式(合约订舱)、装货地、卸货地 slotList = tenantDb.Queryable().Where(a => model.MergeList.Contains(a.Id) && a.Deleted == false).ToList(); //校验查询结果 if (model.MergeList.Count != slotList.Count) { var lostArg = model.MergeList.GroupJoin(slotList, l => l, r => r.Id, (l, r) => { var currList = r.ToList(); if (currList.Count == 0) return 0; return l; }).Where(a => a > 0).ToArray(); //部分舱位信息提取失败,请确认舱位是否存在或已作废 throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotMergeCheckPartRecordDeletedOrNoExists))); } if (slotList.Any(a => string.IsNullOrWhiteSpace(a.CarrierCode) || string.IsNullOrWhiteSpace(a.Vessel) || string.IsNullOrWhiteSpace(a.Voyno) || (!a.BookingSlotType.Equals("SPOT_ORDER") && string.IsNullOrWhiteSpace(a.ContractNo)) || string.IsNullOrWhiteSpace(a.CarriageType) || string.IsNullOrWhiteSpace(a.BookingSlotType) || string.IsNullOrWhiteSpace(a.PortLoadCode) || string.IsNullOrWhiteSpace(a.PortDischargeCode))) { //舱位合票校验失败,部分舱位的以下信息船公司、船名场次、合约号、承运方式、订舱方式、装货地、卸货地有空值情况 throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotMergeCheckPartRecordNoConsistent))); } var checkList = slotList.Select(a => new { key = $"{a.CarrierCode}_{a.Vessel}_{a.Voyno}_{a.ContractNo}_{a.CarriageType}_{a.BookingSlotType}_{a.PortLoadCode}_{a.PortDischargeCode}", obj = a }).ToList(); //如果汇总去重不惟一,不能进行合票操作 if (checkList.GroupBy(a => a.key).Count() > 1) { //舱位合票校验失败,船公司、船名场次、合约号、承运方式、订舱方式、装货地、卸货地不统一不能合票 throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotMergeCheckPartRecordNoConsistentFail))); } } var list = await GetAvailableSlots(null, model.MergeList, null); //舱位合票失败,校验库存为不可用,请确认正确的库存 if (list.Count == 0) throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotMergeCheckStockEmpty))); //这里取完库存后,在跟请求的舱位做匹配,如果舱位没有库存了,提示错误终止合票 var stockCheckList = slotList.GroupJoin(list, l => l.Id, r => r.Id, (l, r) => { var currList = r.ToList(); if (currList.Count == 0) return new { Succ = false, No = l.SlotBookingNo }; return new { Succ = true, No = l.SlotBookingNo }; }).Where(a => !a.Succ).ToList(); if (stockCheckList.Count > 0) { //舱位合票失败,舱位提单号:{string.Join(",", stockCheckList.Select(a => a.No).ToArray())} 缺少库存 throw new Exception(string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotMergeCheckPartRecordNoConsistentFail)), string.Join(",", stockCheckList.Select(a => a.No).ToArray()))); } rlt.slotDetailList = list; string ctnStat = string.Empty; if (list.Count > 0) { rlt.ctnStat = string.Join(",", list.SelectMany(a => a.CtnList).OrderBy(a => a.CtnAll).GroupBy(a => a.CtnAll) .Select(a => $"{a.Key}*{a.Sum(b => b.CtnNum)}").ToArray()); } if (slotList.Any(a => !string.IsNullOrWhiteSpace(a.LoadGuaranteeFlag))) { rlt.importantNotes = string.Join(";", slotList.Where(a => !string.IsNullOrWhiteSpace(a.LoadGuaranteeFlag)) .GroupBy(a => a.LoadGuaranteeFlagName) .Select(a => string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotMergeCheckPartRecordNoConsistentFail)), string.Join(",", a.Select(b => b.SlotBookingNo).ToArray()), a.Key)).ToArray()); } // 查询可用舱位及箱子列表 return rlt; } #endregion #region 舱位引入 /// /// 分页查询可用的舱位及箱子列表 /// /// 可选:舱位查询条件 /// 可选:分页信息 public async Task> GetAvailableSlots([FromQuery] BookingSlotBaseDto input, [FromQuery] PageWithTotal pageInfo) { var result = await GetAvailableSlots(input, null, pageInfo); SqlSugarPagedList pageResult = new() { PageIndex = pageInfo.PageNo, PageSize = pageInfo.PageSize, TotalCount = pageInfo.Total, Items = result, }; return pageResult; } #endregion #region 查询可用的舱位及箱子列表 /// /// 查询可用的舱位及箱子列表 /// /// 筛选条件1:舱位信息、箱型 /// 筛选条件2:舱位主键列表 /// 筛选条件3:分页 /// 可用的舱位列表(含可用的箱子列表) [NonAction] public async Task> GetAvailableSlots(BookingSlotBaseDto slotInput = null, List slotIdListInput = null, PageWithTotal pageInfo = null) { slotInput ??= new(); slotIdListInput ??= new(); string[] ctnCodeList = null; if (!string.IsNullOrEmpty(slotInput.CtnStat)) { ctnCodeList = slotInput.CtnStat.Split(','); } var tenantDb = saasService.GetBizDbScopeById(user.TenantId); // 1. 【舱位基础表】与【箱子表】做关联,并根据【舱位主键】、【箱型】做分组,统计出【总的箱量】,作为queryable1 var queryable1 = tenantDb.Queryable((bas, ctn) => bas.Id == ctn.SlotId) .Where(bas => bas.IsCancellation == false) .WhereIF(!string.IsNullOrEmpty(slotInput.SlotBookingNo), bas => bas.SlotBookingNo == slotInput.SlotBookingNo) .WhereIF(!string.IsNullOrEmpty(slotInput.PortLoad), bas => bas.PortLoad.Contains(slotInput.PortLoad)) .WhereIF(!string.IsNullOrEmpty(slotInput.PortDischarge), bas => bas.PortDischarge.Contains(slotInput.PortDischarge)) .WhereIF(!string.IsNullOrEmpty(slotInput.Vessel), bas => bas.Vessel.Contains(slotInput.Vessel)) .WhereIF(!string.IsNullOrEmpty(slotInput.Voyno), bas => bas.Voyno.Contains(slotInput.Voyno)) .WhereIF(!string.IsNullOrEmpty(slotInput.CarriageType), bas => bas.CarriageType == slotInput.CarriageType) .WhereIF(!string.IsNullOrEmpty(slotInput.BookingSlotType), bas => bas.BookingSlotType == slotInput.BookingSlotType) .WhereIF(ctnCodeList != null, (bas, ctn) => ctnCodeList.Contains(ctn.CtnCode)) .WhereIF(slotIdListInput.Any(), (bas) => slotIdListInput.Contains(bas.Id)) .GroupBy((bas, ctn) => new { bas.Id, ctn.CtnCode }) .Select((bas, ctn) => new { id = bas.Id, ctnCode = ctn.CtnCode, numAll = SqlFunc.AggregateSum(ctn.CtnNum) }) .MergeTable(); // 2. 【已引入舱位表】与【已使用的箱子表】做关联,并根据【舱位主键】、【箱型】做分组,统计出【已使用的箱量】,作为queryable2 var queryable2 = tenantDb.Queryable((alc, ctn) => alc.Id == ctn.SlotAllocId) .WhereIF(slotIdListInput.Any(), (alc, ctn) => slotIdListInput.Contains(alc.BookingSlotId)) .GroupBy((alc, ctn) => new { alc.BookingSlotId, ctn.CtnCode }) .Select((alc, ctn) => new { id = alc.BookingSlotId, ctnCode = ctn.CtnCode, numUse = SqlFunc.AggregateSum(ctn.CtnNum) }) .MergeTable(); // 3. 将queryable1 左连接 queryable2,使用【总的箱量】减去【已使用的箱量】,得到【剩余的箱量】,添加【剩余的箱量】> 0 的条件,作为queryable3 var queryable3 = queryable1.LeftJoin(queryable2, (q1, q2) => q1.id == q2.id && q1.ctnCode == q2.ctnCode) .Select((q1, q2) => new { q1.id, q1.ctnCode, q1.numAll, numResidue = SqlFunc.IsNull(q1.numAll - q2.numUse, q1.numAll) }) .MergeTable() .Where(r => r.numResidue > 0); // 4. 执行ToList(),得到可用的【舱位主键】、【箱型】、【箱量】列表 RefAsync total = 0; var canUselist = pageInfo == null ? await queryable3.ToListAsync() : await queryable3.ToPageListAsync(pageInfo.PageNo, pageInfo.PageSize, total); if (pageInfo != null) { pageInfo.Total = total; } // 查询舱位列表 var baseIdList = canUselist.Select(c => c.id); List baseList = await tenantDb.Queryable() .Where(u => baseIdList.Contains(u.Id)) .ToListAsync(); List ctnCodeCache = new List(); var allCtnist = await _codeCtnService.GetAllList(); if (allCtnist.Succeeded) { ctnCodeCache = allCtnist.Data; } // 构建结果 List result = baseList.Adapt>(); foreach (var item in result) { var ctnList = canUselist.Where(c => c.id == item.Id).ToList(); if (ctnList?.Any() == true) { item.CtnList = ctnList.Select(c => new BookingSlotCtnDto() { CtnCode = c.ctnCode, CtnNum = c.numResidue, TotalNum = c.numAll, CtnAll = ctnCodeCache.FirstOrDefault(e => $"{e.CtnSize}{e.CtnType}" == c.ctnCode)?.CtnName ?? throw new Exception($"舱位信息中存在未收录的箱型:{c.ctnCode},需要在箱型字典中补充"), }).ToList(); } } return result; } #endregion #region 获取附件 /// /// 获取附件 /// /// 舱位主键 /// 返回附件列表 public async Task>> GetFile(long id) { return _opFileService.GetOpFileList(id.ToString()); } #endregion #region 检查指定订舱记录,是否可以引入舱位列表 /// /// 检查指定订舱记录,是否可以引入舱位列表 /// /// 待引入的舱位列表 /// 待关联的订舱记录 /// isExists:指定订舱记录是否已经引入过舱位数据,isEnough:现有舱位及箱子是否满足需求,message:提示信息 public async Task<(bool isExists, bool isEnough, string message)> CheckImportSlots(List slots, long bookingOrderId) { slots ??= new List(); var tenantDb = saasService.GetBizDbScopeById(user.TenantId); // 判断是否已存在引用关系 if (bookingOrderId != 0 && await tenantDb.Queryable().AnyAsync(a => a.BookingId == bookingOrderId)) { //订舱主键{bookingOrderId}已引用舱位 return (true, false, string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotCreateContaNull)), bookingOrderId)); } var slotIdList = slots.Select(s => s.Id).ToList(); // 查询可用舱位及箱子列表 var latestSlotList = await GetAvailableSlots(null, slotIdList); // 判断余量是否满足需求 foreach (var inSlotItem in slots) { var latestSlot = latestSlotList.FirstOrDefault(b => b.Id == inSlotItem.Id); if (latestSlot == null) { //订舱编号为{inSlotItem.SlotBookingNo}的舱位已被占用或取消,请重新引入 return (false, false, $"订舱编号为{inSlotItem.SlotBookingNo}的舱位已被占用或取消,请重新引入"); } if (inSlotItem.CtnList?.Any() == false) { //每个舱位至少选择一个箱子,订舱编号:{inSlotItem.SlotBookingNo} return (false, false, $"每个舱位至少选择一个箱子,订舱编号:{inSlotItem.SlotBookingNo}"); } foreach (var inCtnItem in inSlotItem.CtnList) { var latestCtn = latestSlot.CtnList.FirstOrDefault(c => c.CtnCode == inCtnItem.CtnCode); if (latestCtn == null) { //订舱编号为{latestSlot.SlotBookingNo}的舱位中,箱型为{inCtnItem.CtnAll}的箱子已被占用或取消,请重新引入 return (false, false, $"订舱编号为{latestSlot.SlotBookingNo}的舱位中,箱型为{inCtnItem.CtnAll}的箱子已被占用或取消,请重新引入"); } if (latestCtn.CtnNum < inCtnItem.CtnNum) { //订舱编号为{latestSlot.SlotBookingNo}的舱位中,箱型为{inCtnItem.CtnAll}的箱子当前剩余{latestCtn.CtnNum}个,少于所需的{inCtnItem.CtnNum}个,请重新引入 return (false, false, $"订舱编号为{latestSlot.SlotBookingNo}的舱位中,箱型为{inCtnItem.CtnAll}的箱子当前剩余{latestCtn.CtnNum}个,少于所需的{inCtnItem.CtnNum}个,请重新引入"); } } } //可以引入 return (false, true, $"可以引入"); } public static object ImportLockObj = new object(); #endregion #region 生成订舱订单 /// /// 生成订舱订单 /// /// 生成订舱订单请求 /// 返回回执 public async Task> CreateBookingOrder(BookingGenerateDto model) { if (model.CustomerId == null || string.IsNullOrWhiteSpace(model.CustomerName)) { //请选择委托单位 throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotCreateCustomerNull))); } if (model.CtnList == null || !model.CtnList.Any()) { //请选择要使用的箱信息 throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotCreateContaNull))); } //舱位ID不能为空 if (!model.SlotId.HasValue || model.SlotId.Value < 1) throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotCreateSlotIdNull))); var tenantDb = saasService.GetBizDbScopeById(user.TenantId); var slotInfo = await tenantDb.Queryable() .FirstAsync(a => a.Id == model.SlotId.Value); //舱位信息不存在或已作废 if (slotInfo == null) throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotCreateRecordDeletedOrNoExists))); // 判断是否已存在引用关系 //if (_repAllocation.IsExists(a => a.BOOKING_SLOT_ID == slotInfo.Id // && a.BOOKING_ID > 0 && a.IsDeleted == false)) //{ // throw Oops.Oh($"舱位已有对应的订舱订单,不能重复执行"); //} //var ctnList = _repCtn.AsQueryable().Where(a => a.SLOT_ID == slotInfo.Id && a.IsDeleted == false) // .ToList(); var fileList = _opFileService.GetOpFileList(slotInfo.Id.ToString()).Data; var paramConfig = _configService.GetConfig(CONST_CREATE_BOOKING_NEED_CONTACT, long.Parse(user.TenantId), false).GetAwaiter().GetResult()?.Data?.Value; if (model.CustomerContactList != null && model.CustomerContactList.Count > 0) { //取委托客户下面所有的联系人列表 var djyCustomerInfo = _clientInfoService.GetClientInfoWithContact(new Info.Dtos.QueryClientInfo { ClientId = model.CustomerId.Value, IsController = true }).GetAwaiter().GetResult().Data; if (djyCustomerInfo == null) { Logger.Log(NLog.LogLevel.Info, $"委托单位{model.CustomerName} 获取失败,委托单位不存在或已作废 SlotId={model.SlotId.Value}"); //委托单位{0} 获取失败,委托单位不存在或已作废 throw new Exception(string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotCreateContaNull)), model.CustomerName)); } if (djyCustomerInfo.ClientContactList == null && djyCustomerInfo.ClientContactList.Count < 1) { Logger.Log(NLog.LogLevel.Info, $"委托单位{model.CustomerName} 获取相关联系人失败,委托单位相关联系人为空 SlotId={model.SlotId.Value}"); //委托单位{0} 获取相关联系人失败,委托单位相关联系人为空 throw new Exception(string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotCreateCustomerContractInfoNull)), model.CustomerName)); } model.CustomerContactList.ForEach(contact => { var djyCustomerContactMan = djyCustomerInfo.ClientContactList .FirstOrDefault(a => a.Id == contact.CustomerContactId); if (djyCustomerContactMan == null) { Logger.Log(NLog.LogLevel.Info, $"委托单位{model.CustomerName} 联系人 {contact.Name}获取失败,联系人不存在或已作废 SlotId={model.SlotId.Value}"); //委托单位 {0} 联系人 {1} 获取失败,联系人不存在或已作废 throw new Exception(string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotCreateCustomerContractDeletedOrNoExists)), model.CustomerName, contact.Name)); } }); } else { if (paramConfig != null && paramConfig.Equals("ENABLE", StringComparison.OrdinalIgnoreCase)) { //生成订舱时往来单位联系人必填,请修改 throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotCreateCustomerContractNotNull))); } } var bookingOrderId = await GenerateBookingOrder(slotInfo, fileList, model, null); return DataResult.Success(bookingOrderId, MultiLanguageConst.DataCreateSuccess); } #endregion #region 生成订舱 /// /// 生成订舱 /// /// 舱位详情 /// 舱位附件列表 /// 订舱请求详情 /// 合票的主舱位提单号(合票时必传) /// 返回订舱ID private async Task GenerateBookingOrder(BookingSlotBase bookingSlotBase, List bookingSlotFileList, BookingGenerateDto generateModel, string masterBookingSlotNo) { long id = 0; try { var tenantDb = saasService.GetBizDbScopeById(user.TenantId); /* 1、新增订舱 2、推送服务项目 3、推送附件 */ var allMapCarrierList = await _mappingCarrierService.GetAllList(); MappingCarrierRes carrierInfo = null; if (allMapCarrierList.Succeeded) { carrierInfo = allMapCarrierList.Data.FirstOrDefault(t => t.LinkId == bookingSlotBase.CarrierId.Value && t.Module == CONST_MAPPING_CARRIER_MODULE); } var custNo = bookingSlotBase.SlotBookingNo.Trim(); SeaExportReq bkModel = new SeaExportReq { CustomerId = generateModel.CustomerId.Value, CustomerName = generateModel.CustomerName, Carrier = carrierInfo?.MapName?.Trim(), CarrierId = bookingSlotBase.CarrierId.Value, BookingNo = custNo, //MBLNO = bookingSlotBase.SLOT_BOOKING_NO.Trim(), ContractNo = !string.IsNullOrWhiteSpace(bookingSlotBase.ContractNo) ? bookingSlotBase.ContractNo : "", Vessel = bookingSlotBase.Vessel?.ToUpper()?.Trim(), Voyno = bookingSlotBase.Voyno?.ToUpper()?.Trim(), InnerVoyno = bookingSlotBase.Voyno?.ToUpper()?.Trim(), ETD = bookingSlotBase.ETD, ETA = bookingSlotBase.ETA, SaleId = generateModel.SaleId.HasValue ? generateModel.SaleId.Value : 0, Sale = generateModel.SaleName, OperatorId = generateModel.OpId.HasValue ? generateModel.OpId.Value : 0, //op = generateModel.OpName, Doc = generateModel.DocId.HasValue ? generateModel.DocId.Value : 0, //DOC = generateModel.DocName, //ROUTEID = generateModel.RouteID?.ToString(), //ROUTE = generateModel.Route, //CZRemark = generateModel.CZRemark, //ShenQingXiangShi = generateModel.ShenQingXiangShi, //LineManageID = generateModel.LineManageID?.ToString(), //LineName = generateModel.LineManage, //CLOSEVGMDATE = bookingSlotBase.VGMSubmissionCutDate, ClosingDate = bookingSlotBase.CYCutDate, CloseDocDate = bookingSlotBase.SICutDate, CustomerService = generateModel.CustServiceId.HasValue ? generateModel.CustServiceId.Value : 0, //CUSTSERVICE = generateModel.CustServiceName, LoadPort = bookingSlotBase.PortLoad, LoadPortId = bookingSlotBase.PortLoadId.HasValue ? bookingSlotBase.PortLoadId.Value : 0, DischargePortId = bookingSlotBase.PortDischargeId.HasValue ? bookingSlotBase.PortLoadId.Value : 0, DischargePort = bookingSlotBase.PortDischarge, ReceiptPlace = bookingSlotBase.PlaceReceipt, ReceiptPlaceId = bookingSlotBase.PlaceReceiptId.HasValue ? bookingSlotBase.PlaceReceiptId.Value : 0, DeliveryPlace = bookingSlotBase.PlaceDelivery, DeliveryPlaceId = bookingSlotBase.PlaceDeliveryId.HasValue ? bookingSlotBase.PlaceDeliveryId.Value : 0, CtnInfo = new List() }; // 判断是否为拆票的舱位,如果为拆票,提单号需要加上ABCD... var selectNum = generateModel.CtnList.Sum(x => x.CtnNum); Logger.Log(NLog.LogLevel.Info, "根据舱位生成订舱,selectNum:{selectNum}", selectNum); var allNum = await tenantDb.Queryable().Where(x => x.SlotId == generateModel.SlotId).SumAsync(x => x.CtnNum); Logger.Log(NLog.LogLevel.Info, "根据舱位生成订舱,allNum:{allNum}", allNum); //bkModel.IsSplit = selectNum != allNum; bkModel.SplitOrMergeFlag = selectNum != allNum ? 1 : 0; if (!string.IsNullOrWhiteSpace(masterBookingSlotNo)) bkModel.SplitOrMergeFlag = 2; //拆票逻辑 if (bkModel.SplitOrMergeFlag == 1) { //var sql = _repBookingOrder.AsQueryable().Where(" MBLNO like @mblno ", new { mblno = custNo + '_' }).OrderByDescending(x => x.Id).Select(x => x.MBLNO).ToSqlString(); //var currentMblno = await _repBookingOrder.AsQueryable().Where(" MBLNO like @mblno ", new { mblno = custNo + '_' }) // .OrderByDescending(x => x.Id) // .Select(x => x.MBLNO) // .FirstAsync(); //获取所有订舱编号是舱位提单号,并且是已经拆单的订舱记录 var orderList = tenantDb.Queryable().Where(a => a.BookingNo == custNo && a.SplitOrMergeFlag == 1 && a.Deleted == false).ToList(); if (orderList.Count == 0) { bkModel.MBLNO = $"{custNo}A"; bkModel.BookingNo = custNo; bkModel.HBLNO = custNo; } else { var maxChar = orderList.Select(a => { var startIndx = a.BookingNo.Length; return a.MBLNO.Substring(startIndx); }).Max(); //获取的历史拆票后缀异常,maxChar={maxChar} if (maxChar.Length != 1 || !Regex.IsMatch(maxChar, "[A-Z]{1}")) throw new Exception(string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotCreateCustomerContractNotNull)), maxChar)); bkModel.MBLNO = $"{custNo}{LetterIndexUtil.GetNextLetter(maxChar[0])}"; bkModel.BookingNo = custNo; bkModel.HBLNO = custNo; } //if (currentMblno == null) //{ // bkModel.MBLNO = custNo + "A"; //} //else //{ // var lastLetter = currentMblno.Substring(currentMblno.Length - 1, 1)[0]; // var newMblno = custNo + LetterIndexUtil.GetNextLetter(lastLetter); // bkModel.MBLNO = newMblno; //} } else if (bkModel.SplitOrMergeFlag == 2) { //合票 var orderList = tenantDb.Queryable().Where(a => a.BookingNo == custNo && a.SplitOrMergeFlag == 2 && a.MBLNO == custNo && a.Deleted == false).ToList(); if (orderList.Count == 0) { bkModel.MBLNO = custNo; bkModel.BookingNo = custNo; bkModel.HBLNO = masterBookingSlotNo; } else { //舱位提单号:{custNo} 已有订舱记录不能重复操作 throw new Exception(string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotSplitMergeHasOrder)), custNo)); } } else { bkModel.MBLNO = bookingSlotBase.SlotBookingNo.Trim(); } Logger.Log(NLog.LogLevel.Info, "根据舱位生成订舱,得到MBLNO:{MBLNO}", bkModel.MBLNO); List ctnCodeList = new List(); var ctnCacheRlt = await _codeCtnService.GetAllList(); if (ctnCacheRlt.Succeeded) { ctnCodeList = ctnCacheRlt.Data; } if (generateModel.CtnList != null && generateModel.CtnList.Count > 0) { generateModel.CtnList.ForEach(t => { var ctnCode = ctnCodeList.FirstOrDefault(a => !string.IsNullOrWhiteSpace(a.CtnName) && a.CtnName.Equals(t.CtnAll, StringComparison.OrdinalIgnoreCase)); OpCtnReq ctn = new OpCtnReq { CtnCode = $"{ctnCode?.CtnSize}{ctnCode?.CtnType}", CtnAll = t.CtnAll, CtnNum = t.CtnNum }; bkModel.CtnInfo.Add(ctn); }); } // 验证舱位是否可用 var slotInfo = bookingSlotBase.Adapt(); slotInfo.CtnList = generateModel.CtnList; var importSlots = new List() { slotInfo }; var checkResult = await CheckImportSlots(importSlots, 0); if (!checkResult.isEnough) { throw new Exception(checkResult.message); } Logger.Log(NLog.LogLevel.Info, "根据舱位生成订舱,开始调用Save保存订舱"); var bkRlt = await _seaExportService.EditSeaExport(bkModel); Logger.Log(NLog.LogLevel.Info, $"根据舱位生成订舱,调用Save保存订舱完成,id:{bkRlt.Data}"); id = long.Parse(bkRlt.Data.ToString()); string batchNo = GuidHelper.GetSnowflakeId(); if (id > 0) { ////对应订舱和舱位关系 var allocRlt = await ImportSlots(new ImportSlotsDto { slots = importSlots, bookingOrderId = id, isCheck = false, generateModel = generateModel }); Logger.Log(NLog.LogLevel.Info, "根据舱位生成订舱,引入订舱关系完成"); //更新舱位的拆合单标记 var slotEntity = tenantDb.Queryable().First(a => a.Id == bookingSlotBase.Id && a.Deleted == false); if (slotEntity != null) { slotEntity.SplitOrMergeFlag = bkModel.SplitOrMergeFlag; //更新舱位的拆合单标记 await tenantDb.Updateable(slotEntity).UpdateColumns(a => new { a.SplitOrMergeFlag }).ExecuteCommandAsync(); } //这里如果指定了委托单位的邮件联系人,则推送订舱联系人 if (generateModel.CustomerContactList != null && generateModel.CustomerContactList.Count > 0) { var bookingContactList = tenantDb.Queryable() .Where(a => a.BusinessId == id && a.Deleted == false).ToList(); var djyCustomerInfo = _clientInfoService.GetClientInfoWithContact(new Info.Dtos.QueryClientInfo { ClientId = generateModel.CustomerId.Value, IsController = true }) .GetAwaiter().GetResult().Data; generateModel.CustomerContactList.ForEach(contact => { ClientContactRes djyCustomerContactMan = null; if (djyCustomerInfo.ClientContactList != null && djyCustomerInfo.ClientContactList.Count > 0) { djyCustomerContactMan = djyCustomerInfo.ClientContactList.FirstOrDefault(a => a.Id == contact.CustomerContactId); } if (djyCustomerContactMan != null) { var bookingContact = bookingContactList .FirstOrDefault(x => x.Email.Equals(djyCustomerContactMan.Email, StringComparison.OrdinalIgnoreCase)); if (bookingContact == null) { bookingContact = new BusinessOrderContact { Name = djyCustomerContactMan.ShortName, BusinessId = id, Email = djyCustomerContactMan.Email, Note = djyCustomerContactMan.Note, CreateTime = DateTime.Now, CreateBy = long.Parse(user.UserId), //CreatedUserName = UserManager.Name }; tenantDb.Insertable(bookingContact).ExecuteCommand(); //_bookingOrderContactRepository.Insert(bookingContact); } else { bookingContact.Name = djyCustomerContactMan.ShortName; bookingContact.Email = djyCustomerContactMan.Email; bookingContact.Note = djyCustomerContactMan.Note; bookingContact.UpdateTime = DateTime.Now; bookingContact.UpdateBy = long.Parse(user.UserId); //bookingContact.UpdatedUserName = UserManager.Name; tenantDb.Updateable(bookingContact).UpdateColumns(it => new { it.Name, it.Email, it.Note, it.UpdateTime, it.UpdateBy, //it.UpdatedUserName }).ExecuteCommand(); } } }); } if (generateModel.ProjectList != null && generateModel.ProjectList.Count > 0) { //写入服务项目 var prjRlt = _djyServiceStatusService.SaveServiceProject(new EmbedServiceProjectDto { BusinessId = id.ToString(), ProjectCodes = generateModel.ProjectList.Distinct().ToArray(), }); Logger.Log(NLog.LogLevel.Info, $"推送订舱的服务项目完成 id={id} rlt={JsonConvert.SerializeObject(prjRlt)}"); } //var opt = App.GetOptions(); //var dirAbs = opt.basePath; //if (string.IsNullOrEmpty(dirAbs)) //{ // dirAbs = App.WebHostEnvironment.WebRootPath; //} var basePath = AppSetting.app(new string[] { "FileSettings", "BasePath" }); var relativePath = AppSetting.app(new string[] { "FileSettings", "RelativePath" }); var dirAbs = string.Empty; var fileRelaPath = string.Empty; var fileAbsPath = string.Empty; if (string.IsNullOrEmpty(basePath)) { dirAbs = Path.Combine(_environment.WebRootPath, relativePath); } else { dirAbs = Path.Combine(basePath, relativePath); } if (bookingSlotFileList.Any(a => a.TypeCode.Equals("bc", StringComparison.OrdinalIgnoreCase))) { var file = bookingSlotFileList.OrderByDescending(a => a.CreateTime) .FirstOrDefault(a => a.TypeCode.Equals("bc", StringComparison.OrdinalIgnoreCase)); var fileFullPath = Path.Combine(dirAbs, file.FilePath); if (File.Exists(fileFullPath)) { //如果确认文件读取成功 var bookFilePath = _sysFileService.MoveFile(id.ToString(), fileFullPath, batchNo , false, null, true).GetAwaiter().GetResult().Data; //将格式单附件写入订舱的附件 SaveEDIFile(id, bookFilePath, new System.IO.FileInfo(bookFilePath).Name, long.Parse(user.TenantId), CONST_BC_FILE_CODE, CONST_BC_FILE_NAME).GetAwaiter(); } } if (bookingSlotFileList.Any(a => a.TypeCode.Equals("bc_notice", StringComparison.OrdinalIgnoreCase))) { var file = bookingSlotFileList.OrderByDescending(a => a.CreateTime) .FirstOrDefault(a => a.TypeCode.Equals("bc_notice", StringComparison.OrdinalIgnoreCase)); var fileFullPath = Path.Combine(dirAbs, file.FilePath); if (File.Exists(fileFullPath)) { //如果确认文件读取成功 var bookFilePath = _sysFileService.MoveFile(id.ToString(), fileFullPath, batchNo , false, "bcnoticefile", true).GetAwaiter().GetResult().Data; //将格式单附件写入订舱的附件 SaveEDIFile(id, bookFilePath, new System.IO.FileInfo(bookFilePath).Name, long.Parse(user.TenantId), CONST_BC_FILE_CODE, CONST_BC_FILE_NAME).GetAwaiter(); } } } Logger.Log(NLog.LogLevel.Info, $"MBLNO:{bookingSlotBase.SlotBookingNo} 生成订舱订单成功 id={id}"); } catch (Exception ex) { Logger.Log(NLog.LogLevel.Info, $"MBLNO:{bookingSlotBase.SlotBookingNo} 生成订舱订单异常,原因:{ex.Message}"); throw; } return id; } #endregion #region 为指定订舱记录引入舱位信息 /// /// 为指定订舱记录引入舱位信息 /// /// 引入的舱位请求参数 /// 返回回执 public async Task> ImportSlots(ImportSlotsDto model) { model.slots ??= new List(); var tenantDb = saasService.GetBizDbScopeById(user.TenantId); Monitor.Enter(ImportLockObj); try { if (model.isCheck) { (bool isExists, bool isEnough, string message) checkResult = await CheckImportSlots(model.slots, model.bookingOrderId); if (checkResult.isExists || !checkResult.isEnough) return DataResult.FailedData(checkResult.message); } var slotIdList = model.slots.Select(s => s.Id).ToList(); List latestSlotList = await tenantDb.Queryable().Where(b => slotIdList.Contains(b.Id)).ToListAsync(); foreach (var inSlotItem in model.slots) { var latestSlot = latestSlotList.First(b => b.Id == inSlotItem.Id); // 保存关联信息 var config = new TypeAdapterConfig(); config.ForType() .Ignore(dest => dest.CreateTime) .Ignore(dest => dest.UpdateTime) .Ignore(dest => dest.CreateBy) .Ignore(dest => dest.UpdateBy); //.Ignore(dest => dest.CreatedUserName) //.Ignore(dest => dest.UpdatedUserName) //.Ignore(dest => dest.TenantId) //.Ignore(dest => dest.TenantName); var newSlotAllocation = latestSlot.Adapt(config); newSlotAllocation.Id = 0; newSlotAllocation.BookingSlotId = latestSlot.Id; newSlotAllocation.BookingId = model.bookingOrderId; newSlotAllocation.AlloBillNo = latestSlot.SlotBookingNo; newSlotAllocation.FinalBillNo = latestSlot.SlotBookingNo; if (model.generateModel != null) { newSlotAllocation.CustomerId = model.generateModel.CustomerId; newSlotAllocation.CustomerName = model.generateModel.CustomerName; newSlotAllocation.CustServiceId = model.generateModel.CustServiceId?.ToString(); newSlotAllocation.CustService = model.generateModel.CustServiceName; newSlotAllocation.SaleId = model.generateModel.SaleId?.ToString(); newSlotAllocation.Sale = model.generateModel.SaleName; newSlotAllocation.DocId = model.generateModel.DocId?.ToString(); newSlotAllocation.Doc = model.generateModel.DocName; newSlotAllocation.OpId = model.generateModel.OpId?.ToString(); newSlotAllocation.Op = model.generateModel.OpName; newSlotAllocation.Business = model.generateModel.BUSINESS; newSlotAllocation.BusinessId = model.generateModel.BUSINESSID; newSlotAllocation.SaleTime = model.generateModel.SALE_TIME; newSlotAllocation.Shipper = model.generateModel.SHIPPER; newSlotAllocation.GoodsName = model.generateModel.GOODSNAME; newSlotAllocation.SellingPrice = model.generateModel.SELLING_PRICE; newSlotAllocation.SplitOrMergeFlag = model.generateModel.SplitOrMerge; if (model.generateModel.SplitOrMerge == 1 || model.generateModel.SplitOrMerge == 2) { newSlotAllocation.AlloBillNo = model.generateModel.NewMBlNo; } } await tenantDb.Insertable(newSlotAllocation).ExecuteReturnEntityAsync(); // 保存关联的箱信息 var insertCtnList = inSlotItem.CtnList.Select(c => new BookingSlotAllocationCtn() { SlotAllocId = newSlotAllocation.Id, CtnCode = c.CtnCode, CtnAll = c.CtnAll, CtnNum = c.CtnNum }); await tenantDb.Insertable(insertCtnList).ExecuteCommandAsync(); // 为订舱保存附件信息 var lastestBcFile = await tenantDb.Queryable().Where(x => (x.TypeCode == "bc" || x.TypeCode == "bc_notice") && x.LinkId == latestSlot.Id) .OrderByDescending(x => x.Id) .FirstAsync(); if (lastestBcFile != null) { var file = lastestBcFile.Adapt(); file.Id = 0; file.LinkId = model.bookingOrderId; await tenantDb.Insertable(file).ExecuteCommandAsync(); } await _bookingSlotStockService.BookingSlotStock(new BookingSlotStockUpdateModel { BookingSlotType = latestSlot.BookingSlotType, CarrierId = latestSlot.CarrierId.HasValue ? latestSlot.CarrierId.Value : 0, ContractNo = latestSlot.ContractNo, Vessel = latestSlot.Vessel, Voyno = latestSlot.Voyno, PortLoadId = latestSlot.PortLoadCode, PortDischargeId = latestSlot.PortLoadCode, }); } } finally { Monitor.Exit(ImportLockObj); } return DataResult.Success("引入成功"); } #endregion #region 检索舱位对应的订舱订单(BY 舱位主键) /// /// 检索舱位对应的订舱订单(BY 舱位主键) /// /// 舱位ID /// 返回回执 public async Task> SearchBookingSlotWithOrderById(long id) { BookingSlotWithOrderDto dto = null; var tenantDb = saasService.GetBizDbScopeById(user.TenantId); var slotInfo = await tenantDb.Queryable().FirstAsync(a => a.Id == id); if (slotInfo == null) { Logger.Log(NLog.LogLevel.Info, $"id={id} 获取舱位失败,舱位不存在或已作废"); return DataResult.FailedData(dto); } var list = tenantDb.Queryable().Where(a => a.BookingSlotId == id).ToList(); dto = new BookingSlotWithOrderDto { BookingSlotId = slotInfo.Id, }; if (list.Count > 0) { dto.HasBookingOrder = true; dto.BookingOrderList = list.Select(x => x.Id).ToList(); } return DataResult.Success(dto); } #endregion #region 刷新库存 /// /// 刷新库存 /// /// 请求参数 /// 返回回执 public async Task> RefreshStock(BookingSlotStockUpdateModel input) { return await _bookingSlotStockService.BookingSlotStock(input); } #endregion #region 订舱编号检索舱位信息 /// /// 订舱编号检索舱位信息 /// /// 订舱编号 /// 船公司ID /// public async Task QueryBookingSlot(string slotBookingNo, string CarrierId) { long id = 0; try { var tenantDb = saasService.GetBizDbScopeById(user.TenantId); var model = tenantDb.Queryable().First(x => x.SlotBookingNo == slotBookingNo && x.CarrierCode == CarrierId); //if (model == null) //throw Oops.Bah("舱位信息不存在"); id = model.Id; } catch (Exception ex) { //_logger.LogInformation($"订舱编号检索舱位信息失败,原因:{ex.Message}"); } return id; } #endregion #region 库存台账查询 /// /// 库存台账查询 /// /// /// public async Task>> GetPageStockAsync(PageRequest querySearch) { var tenantDb = saasService.GetBizDbScopeById(user.TenantId); //序列化查询条件 var whereList = db.ConfigQuery.Context.Utilities.JsonToConditionalModels(querySearch.QueryCondition); var result = tenantDb.Queryable() .Select() .Where(whereList); var list = result.ToList(); return await result.ToQueryPageAsync(querySearch.PageCondition); } #endregion /// /// 生成合票订舱订单 /// /// 生成订舱订单请求 /// 返回回执 public async Task MergeCreateBookingOrder(BookingGenerateDto model) { return null; } /// /// 估算差异重要提醒 /// /// 原舱位详情 /// 新舱位详情 /// 舱位ID /// public async Task MeasureDiffCautionTask(ParserBCInfoDto bcSrcDto, ParserBCInfoDto bcTargetDto, long slotId) { return; } /// /// 检索舱位对应的订舱订单(BY 订舱编号) /// /// 订舱编号 /// 租户ID /// 返回回执 public async Task SearchBookingSlotWithOrderByNo(string slotBookingNo, long tenantId) { return null; } /// /// 导入舱位 /// /// 导入舱位文件 /// 返回回执 public async Task ImportSlotFromFile(IFormFile file) { return null; } Task>> IBookingSlotService.GetAvailableSlots(BookingSlotBaseDto slotInput, List slotIdListInput, PageWithTotal pageInfo) { throw new NotImplementedException(); } /// /// 舱位台账查询 /// /// 查询条件 /// public async Task>> GetPageAsync(PageRequest querySearch) { var tenantDb = saasService.GetBizDbScopeById(user.TenantId); //序列化查询条件 var whereList = db.ConfigQuery.Context.Utilities.JsonToConditionalModels(querySearch.QueryCondition); var result = tenantDb.Queryable() .InnerJoin((a, b) => a.Id == b.SlotId) .Select() .Where(whereList); //.ToQueryPageAsync(request.PageCondition); var list = result.ToList(); var data = await result.ToQueryPageAsync(querySearch.PageCondition); return data; } Task IBookingSlotService.InnerApiReceive(BookingSlotBaseApiDto dto, DynameFileInfo file, DynameFileInfo modifyFile) { throw new NotImplementedException(); } Task> IBookingSlotService.GetSlotCompareResult(long id, string batchNo) { throw new NotImplementedException(); } #region 获取舱位详情列表 /// /// 获取舱位详情列表 /// /// 舱位ID组 /// 返回舱位详情 public async Task>> GetSlotList(long[] ids) { var tenantDb = saasService.GetBizDbScopeById(user.TenantId); var slotList = await tenantDb.Queryable().Where(u => ids.Contains(u.Id) && u.Deleted == false).ToListAsync(); if (slotList.Count == 0) { //未查询到此舱位信息,可能已被删除,请重新查询后重试 throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotBaseInfoNull))); } var data = slotList.Select(a => a.Adapt()).ToList(); if (data.Count > 0) return DataResult>.Success(data); return DataResult>.FailedData(data); } #endregion } public static class LetterIndexUtil { /// /// 根据当前字母获取它在26个英文字母中的下一个字母 /// public static char GetNextLetter(char currentLetter) { if (currentLetter == 'z') return 'a'; if (currentLetter == 'Z') return 'A'; return (char)(currentLetter + 1); } } }