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; using NPOI.XSSF.UserModel; using AngleSharp.Dom; using DS.WMS.Core.TaskPlat.Entity; 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 ICodeLanesService _codeLanesService; 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 IS_ENABLE_CUSTOMER_AUTHORITY = "IS_ENABLE_CUSTOMER_AUTHORITY"; 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(); _codeLanesService = _serviceProvider.GetRequiredService(); _clientInfoService = _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.PortLoadCode != model.PortLoadCode || oldObj.PortDischargeCode != 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.PortLoadCode, PortDischargeId = oldObj.PortDischargeCode, 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 { PortDischargeCode = model.PortDischargeCode, PortDischarge = model.PortDischarge, PortLoadCode = model.PortLoadCode, PortLoad = model.PortLoad, PlaceDelivery = model.PlaceDelivery, PlaceDeliveryCode = model.PlaceDeliveryCode, PlaceReceipt = model.PlaceReceipt, PlaceReceiptCode = 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.PortLoadCode, 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.PortDischargeCode, 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, CreateBy = 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?.Id ?? 0; } 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 messageInfo = dataContext.Get(TaskFlowDataNameConst.TaskManageOrderMessageInfo); if (messageInfo == null) { throw new ArgumentNullException($"缺少参数:{TaskFlowDataNameConst.TaskManageOrderMessageInfo}"); } var taskBcId = dataContext.Get(TaskFlowDataNameConst.TaskBCId); if (taskBcId == null) { Logger.Log(NLog.LogLevel.Info, $"执行ApiReceiveTask时,未获取到{TaskFlowDataNameConst.TaskBCId}"); } 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 == MappingModuleConst.CONST_MAPPING_CARRIER_MODULE).FirstOrDefault(); } BookingSlotBaseApiDto slotModel = new BookingSlotBaseApiDto { DataObj = new BookingSlotBaseApiSaveDto { CarrierId = carrierInfo?.LinkId, CarrierCode = carrierInfo?.MapCode, Carrier = carrierInfo?.MapName, 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 = messageInfo.Head.RequestAction?.ToLower() }; if (int.TryParse(taskBCInfoDto.WeekAt, out int week)) { slotModel.DataObj.WeekAt = week; } if (taskBCInfoDto.CtnList.Count > 0) { var ctnCodeList = (await _codeCtnService.GetAllList()).Data ?? new List(); taskBCInfoDto.CtnList.ForEach(t => { if (string.IsNullOrEmpty(t.CtnCode)) { var ctnCode = ctnCodeList.FirstOrDefault(a => !string.IsNullOrWhiteSpace(a.CtnName) && a.CtnName.Equals(t.CtnALL, StringComparison.OrdinalIgnoreCase)); t.CtnCode = ctnCode != null ? ctnCode.EdiCode : "(箱型未收录)"; } BookingSlotCtnSaveInput ctn = new BookingSlotCtnSaveInput { CtnCode = t.CtnCode, CtnAll = t.CtnALL, CtnNum = t.CTNNUM.HasValue ? t.CTNNUM.Value : 1 }; slotModel.DataObj.CtnList.Add(ctn); }); } var rlt = await InnerApiReceive(slotModel, bcFileInfo, bcNoticeFileInfo); // 回写舱位主键到BC任务 if (rlt.Succeeded && rlt.Data != null && taskBcId != null) { var tenantDb = saasService.GetBizDbScopeById(user.TenantId); var taskBcInfo = await tenantDb.Queryable().Where(x => x.Id == taskBcId).FirstAsync(); if (taskBcInfo != null && taskBcInfo.BOOKING_SLOT_ID == null) { taskBcInfo.BOOKING_SLOT_ID = rlt.Data.Id; await tenantDb.Updateable(taskBcInfo).UpdateColumns(x => new { x.BOOKING_SLOT_ID }).ExecuteCommandAsync(); } } 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); BookingSlotBase model = null; //接口方法直接调用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)) || (!string.IsNullOrWhiteSpace(t.CtnAll) && t.CtnAll == 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.PortLoadCode = 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.PortDischargeCode = 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()); } } //自动转换对应标签 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.Item2; Logger.Log(NLog.LogLevel.Info, $"保存文件路径:{fileFullPath}"); if (!string.IsNullOrWhiteSpace(fileFullPath)) { //将格式单附件写入订舱的附件 await SaveEDIFile(id, fileFullPath, file.FileName, long.Parse(user.TenantId), file.FileBytes.Length, CONST_BC_FILE_CODE, CONST_BC_FILE_NAME); } } 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.Item2; Logger.Log(NLog.LogLevel.Info, $"保存文件路径(变更文件):{fileFullPath}"); if (!string.IsNullOrWhiteSpace(fileFullPath)) { //将格式单附件写入订舱的附件 await SaveEDIFile(id, fileFullPath, modifyFile.FileName, long.Parse(user.TenantId), modifyFile.FileBytes.Length, CONST_BC_NOTICE_FILE_CODE, CONST_BC_NOTICE_FILE_NAME); } } //触发标签自动绑定 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.PortLoadCode != model.PortLoadCode || oldObj.PortDischargeCode != 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.PortLoadCode, PortDischargeId = oldObj.PortDischargeCode, 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.Item2; Logger.Log(NLog.LogLevel.Info, $"保存文件路径:{fileFullPath}"); if (!string.IsNullOrWhiteSpace(fileFullPath)) { //将格式单附件写入订舱的附件 await SaveEDIFile(id, fileFullPath, file.FileName, long.Parse(user.TenantId), file.FileBytes.Length, CONST_BC_MODIFY_FILE_CODE, CONST_BC_MODIFY_FILE_NAME); } } 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.Item2; Logger.Log(NLog.LogLevel.Info, $"保存文件路径(变更文件):{fileFullPath}"); if (!string.IsNullOrWhiteSpace(fileFullPath)) { //将格式单附件写入订舱的附件 await SaveEDIFile(id, fileFullPath, modifyFile.FileName, long.Parse(user.TenantId), modifyFile.FileBytes.Length, CONST_BC_MODIFY_NOTICE_FILE_CODE, CONST_BC_MODIFY_NOTICE_FILE_NAME); } } //一般更新数据指的是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(model); return DataResult.Success(model); } #endregion #region 异步写入附件表 /// /// 异步写入附件表 /// /// 订舱ID /// 文件路径 /// 文件名 /// 租户ID /// 附件类型代码 /// 附件类型名称 /// 附件模块代码 /// private async Task> SaveEDIFile(long boookId, string FilePath, string fileName, long tenantId, int fileSize, 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, FileSize = fileSize, FileType = Path.GetExtension(fileName), Extension = Path.GetExtension(fileName), OrgId = long.Parse(user.OrgId) }; await tenantDb.Insertable(newFile).ExecuteCommandAsync(); return DataResult.Success(string.Empty); } #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()); } return DataResult.Success(string.Empty); } #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 getRlt = await GetAvailableSlots(null, model.MergeList, null); var list = getRlt.Data; //舱位合票失败,校验库存为不可用,请确认正确的库存 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 DataResult.Success(rlt); } #endregion #region 舱位引入 /// /// 分页查询可用的舱位及箱子列表 /// /// 可选:舱位查询条件 /// 可选:分页信息 public async Task>> GetAvailableSlots(BookingSlotBaseDto input, PageWithTotal pageInfo) { var rlt = await GetAvailableSlots(input, null, pageInfo); SqlSugarPagedList pageResult = new() { PageIndex = pageInfo.PageNo, PageSize = pageInfo.PageSize, TotalCount = pageInfo.Total, Items = rlt.Data, }; return DataResult>.Success(pageResult); } #endregion #region 查询可用的舱位及箱子列表 /// /// 查询可用的舱位及箱子列表 /// /// 筛选条件1:舱位信息、箱型 /// 筛选条件2:舱位主键列表 /// 筛选条件3:分页 /// 可用的舱位列表(含可用的箱子列表) 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.EdiCode == c.ctnCode)?.CtnName ?? throw new Exception($"舱位信息中存在未收录的箱型:{c.ctnCode},需要在箱型字典中补充"), }).ToList(); } } return DataResult>.Success(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 lsRlt = await GetAvailableSlots(null, slotIdList); // 查询可用舱位及箱子列表 var latestSlotList = lsRlt.Data; // 判断余量是否满足需求 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.Id); 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 public 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 == MappingModuleConst.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, OperatorName = generateModel.OpName, Doc = generateModel.DocId.HasValue ? generateModel.DocId.Value : 0, DocName = generateModel.DocName, //ROUTEID = generateModel.RouteID?.ToString(), //ROUTE = generateModel.Route, //CZRemark = generateModel.CZRemark, //ShenQingXiangShi = generateModel.ShenQingXiangShi, //LineManageID = generateModel.LineManageID?.ToString(), //LineName = generateModel.LineManage, VGMCloseDate = bookingSlotBase.VGMSubmissionCutDate, ClosingDate = bookingSlotBase.CYCutDate, CloseDocDate = bookingSlotBase.SICutDate, CustomerService = generateModel.CustServiceId.HasValue ? generateModel.CustServiceId.Value : 0, CustomerServiceName = 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, BLType = "整箱", StlName = "票结", CtnInfo = new List() }; if (bookingSlotBase.ETD.HasValue) { bkModel.AccountDate = bookingSlotBase.ETD.Value.ToString("yyyy-MM"); } else { bkModel.AccountDate = DateTime.Now.ToString("yyyy-MM"); } if (generateModel.CtnList == null) { var ctnList = (await GetAvailableCtnsBySlot(bookingSlotBase.Id)).Data; if (ctnList == null || ctnList.Count == 0) { throw new Exception("可用舱位为空"); } generateModel.CtnList = ctnList; } // 判断是否为拆票的舱位,如果为拆票,提单号需要加上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; bkModel.BLType = "拆票主票"; } 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; bkModel.BLType = "拆票分票"; } //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; if (custNo == masterBookingSlotNo) { bkModel.BLType = "合票主票"; } else { bkModel.BLType = "合票分票"; } } 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 => { if (string.IsNullOrEmpty(t.CtnCode) && !string.IsNullOrEmpty(t.CtnAll)) { var ctnCode = ctnCodeList.FirstOrDefault(a => !string.IsNullOrWhiteSpace(a.CtnName) && a.CtnName.Equals(t.CtnAll, StringComparison.OrdinalIgnoreCase)); t.CtnCode = ctnCode != null ? $"{ctnCode.CtnSize}{ctnCode.CtnType}" : "(箱型未收录)"; } OpCtnReq ctn = new OpCtnReq { CtnCode = t.CtnCode, 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); if (bkRlt.Data == null) { throw new Exception("根据舱位生成订舱后,订舱主键为空,messsage:" + bkRlt.Message); } 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.Id); } 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), CreateUserName = user.UserName }; 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.UpdateUserName = user.UserName; tenantDb.Updateable(bookingContact).UpdateColumns(it => new { it.Name, it.Email, it.Note, it.UpdateTime, it.UpdateBy, it.UpdateUserName }).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 fileRlt = _sysFileService.MoveFile(id.ToString(), fileFullPath, batchNo , false, null, true).GetAwaiter().GetResult().Data; //如果确认文件读取成功 var bookFilePath = fileRlt.Item2; //将格式单附件写入订舱的附件 await SaveEDIFile(id, bookFilePath, bookFilePath, long.Parse(user.TenantId), fileRlt.Item4, CONST_BC_FILE_CODE, CONST_BC_FILE_NAME); } } 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)) { Tuple? fileRtl = _sysFileService.MoveFile(id.ToString(), fileFullPath, batchNo , false, "bcnoticefile", true).GetAwaiter().GetResult().Data; //如果确认文件读取成功 var bookFilePath = fileRtl.Item2; //将格式单附件写入订舱的附件 await SaveEDIFile(id, bookFilePath, bookFilePath, long.Parse(user.TenantId), fileRtl.Item4, CONST_BC_FILE_CODE, CONST_BC_FILE_NAME); } } } 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(); Monitor.Enter(ImportLockObj); try { var tenantDb = saasService.GetBizDbScopeById(user.TenantId); if (model.isCheck) { (bool isExists, bool isEnough, string message) checkResult = CheckImportSlots(model.slots, model.bookingOrderId).GetAwaiter().GetResult(); if (checkResult.isExists || !checkResult.isEnough) return DataResult.FailedData(checkResult.message); } var slotIdList = model.slots.Select(s => s.Id).ToList(); List latestSlotList = tenantDb.Queryable().Where(b => slotIdList.Contains(b.Id)).ToList(); 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.CreateUserName) .Ignore(dest => dest.UpdateUserName); //.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; } } tenantDb.Insertable(newSlotAllocation).ExecuteReturnEntity(); // 保存关联的箱信息 var insertCtnList = inSlotItem.CtnList.Select(c => new BookingSlotAllocationCtn() { SlotAllocId = newSlotAllocation.Id, CtnCode = c.CtnCode, CtnAll = c.CtnAll, CtnNum = c.CtnNum }).ToList(); insertCtnList.ForEach(c => { tenantDb.Insertable(c).ExecuteCommand(); }); // 为订舱保存附件信息 var lastestBcFile = tenantDb.Queryable().Where(x => x.TypeCode == "bc" && x.LinkId == latestSlot.Id) .OrderByDescending(x => x.Id) .First(); if (lastestBcFile != null) { var file = lastestBcFile.Adapt(); file.Id = 0; file.LinkId = model.bookingOrderId; tenantDb.Insertable(file).ExecuteCommand(); } var lastestBcModFile = tenantDb.Queryable().Where(x => x.TypeCode == "bc_notice" && x.LinkId == latestSlot.Id) .OrderByDescending(x => x.Id) .First(); if (lastestBcFile != null) { var file = lastestBcFile.Adapt(); file.Id = 0; file.LinkId = model.bookingOrderId; tenantDb.Insertable(file).ExecuteCommand(); } if (lastestBcModFile != null) { var file = lastestBcModFile.Adapt(); file.Id = 0; file.LinkId = model.bookingOrderId; tenantDb.Insertable(file).ExecuteCommand(); } _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, }).GetAwaiter().GetResult(); } } 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 DataResult.Success(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 DataResult.Successed(string.Empty); } #region 检索舱位对应的订舱订单(BY 订舱编号) /// /// 检索舱位对应的订舱订单(BY 订舱编号) /// /// 订舱编号 /// 租户ID /// 返回回执 public async Task> SearchBookingSlotWithOrderByNo(string slotBookingNo, long tenantId) { BookingSlotWithOrderDto dto = null; var tenantDb = saasService.GetBizDbScopeById(user.TenantId); var slotInfo = await tenantDb.Queryable().FirstAsync(a => a.SlotBookingNo == slotBookingNo && a.Deleted == false); if (slotInfo == null) { Logger.Log(NLog.LogLevel.Info, $"slotBookingNo={slotBookingNo} 获取舱位失败,舱位不存在或已作废"); } var list = tenantDb.Queryable().Where(a => a.SlotBookingNo == slotBookingNo && a.Deleted == false).ToList(); dto = new BookingSlotWithOrderDto { BookingSlotId = slotInfo.Id, }; if (list.Count > 0) { dto.HasBookingOrder = true; var bkNoList = list.Select(x => x.BookingId).ToList(); var bkList = tenantDb.Queryable().Where(a => bkNoList.Contains(a.Id) && a.Deleted == false).ToList(); if (bkList.Count > 0) { dto.BookingOrderList = bkList.Select(x => x.Id).ToList(); } else { dto.BookingOrderList = new List(); } } return DataResult.Success(dto); } #endregion #region 导入舱位 /// /// 导入舱位 /// /// 导入舱位文件 /// 返回回执 public async Task>> ImportSlotFromFile(IFormFile file) { var tenantDb = saasService.GetBizDbScopeById(user.TenantId); bool succ = false; List list = new List(); try { if (file == null) { //附件不能为空 return DataResult>.Failed(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotImportFileNull))); } FileInfo fileInfo = new FileInfo(file.FileName); if (fileInfo.Extension != ".xlsx") { //请上传指定模板文件 return DataResult>.Failed(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotImportFileTypeError))); } string fileRoot = AppSetting.app(new string[] { "FileSettings", "BasePath" }); string relativePath = AppSetting.app(new string[] { "FileSettings", "RelativePath" }); var tempDic = Path.Combine(fileRoot, relativePath, DateTime.Now.Ticks.ToString()); Directory.CreateDirectory(tempDic); var filePath = Path.Combine(tempDic, file.FileName); using (var stream = File.Create(filePath)) { await file.CopyToAsync(stream); } using FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite); var readbook = new XSSFWorkbook(fs); var sheet = readbook.GetSheet("导入"); if (sheet == null) { //内容为空 return DataResult>.Failed(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotImportExcelEmpty))); } var rowCount = sheet.LastRowNum; if (rowCount <= 1) { //内容为空 return DataResult>.Failed(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotImportExcelEmpty))); } Dictionary> data = new(rowCount); //List data = new(rowCount); var cacheMappCarrier = _mappingCarrierService.GetAllList().GetAwaiter().GetResult().Data; var cachePort = _codePortService.GetAllList().GetAwaiter().GetResult().Data; var cacheLane = _codeLanesService.GetAllList().GetAwaiter().GetResult().Data; var cacheCtnCode = _codeCtnService.GetAllList().GetAwaiter().GetResult().Data; BookingSlotBase lastSlot = null; for (int i = 1; i < rowCount; i++) { var row = sheet.GetRow(i); if (row == null) { continue; } if (string.IsNullOrWhiteSpace(row.GetCell(0)?.StringCellValue)) { if (string.IsNullOrWhiteSpace(row.GetCell(23)?.ToString())) { continue; } BookingSlotCtn ctnExt = new(); // 解析箱信息 for (int n = 23; n <= 24; n++) { var cell = row.GetCell(n); if (cell == null) continue; var value = cell.ToString(); if (string.IsNullOrWhiteSpace(value)) continue; switch (n) { case 23: { ctnExt.CtnAll = value; ctnExt.CtnCode = cacheCtnCode.FirstOrDefault(x => x.CtnName == value)?.EdiCode; break; } case 24: { if (int.TryParse(value, out int temp)) ctnExt.CtnNum = temp; break; } } } if (!string.IsNullOrEmpty(ctnExt.CtnCode) && data.TryGetValue(lastSlot, out var ctnList)) { ctnList.Add(ctnExt); } continue; } var slot = new BookingSlotBase(); // 解析舱位信息 for (int n = 0; n <= 22; n++) { var cell = row.GetCell(n); if (cell == null) continue; //var value = cell.StringCellValue; var value = cell.ToString().Trim(); if (string.IsNullOrWhiteSpace(value)) continue; switch (n) { case 0: slot.SlotBookingNo = value; break; case 1: slot.CarrierCode = value; break; case 2: slot.Vessel = value; break; case 3: slot.Voyno = value; break; case 4: slot.ContractNo = value; break; case 5: slot.BookingSlotTypeName = value; break; case 6: slot.ETD = cell.DateCellValue; break; //{ // if (DateTime.TryParse(value, out DateTime temp)) slot.ETD = temp; break; //} case 7: slot.ETA = cell.DateCellValue; break; //{ // if (DateTime.TryParse(value, out DateTime temp)) slot.ETA = temp; break; //} case 8: slot.BookingParty = value; break; case 9: slot.PlaceReceipt = value; break; case 10: slot.PlaceDelivery = value; break; case 11: slot.PortLoadCode = value; break; case 12: slot.PortDischargeCode = value; break; case 13: { if (int.TryParse(value, out int temp)) slot.WeekAt = temp; break; } case 14: { //if (DateTime.TryParse(value, out DateTime temp)) slot.SI_CUT_DATE = temp; break; slot.SICutDate = cell.DateCellValue; break; } case 15: { //if (DateTime.TryParse(value, out DateTime temp)) slot.VGM_SUBMISSION_CUT_DATE = temp; break; slot.VGMSubmissionCutDate = cell.DateCellValue; break; } case 16: { //if (DateTime.TryParse(value, out DateTime temp)) slot.CY_CUT_DATE = temp; break; slot.CYCutDate = cell.DateCellValue; break; } case 17: { //if (DateTime.TryParse(value, out DateTime temp)) slot.MANIFEST_CUT_DATE = temp; break; slot.ManifestCutDate = cell.DateCellValue; break; } case 18: { //if (DateTime.TryParse(value, out DateTime temp)) slot.MDGF_CUT_DATE = temp; break; slot.MDGFCutDate = cell.DateCellValue; break; } case 19: slot.LaneName = value; break; // case 20: { if (int.TryParse(value, out int temp)) slot.DetensionFreeDays = temp; break; } case 21: { //if (DateTime.TryParse(value, out DateTime temp)) slot.CreatedTime = temp; break; slot.CreateTime = cell.DateCellValue.Value; break; } case 22: { //if (DateTime.TryParse(value, out DateTime temp)) slot.PRICE_CALCULATION_DATE = temp; break; slot.PriceCalculationDate = cell.DateCellValue; break; } default: break; } } // 特殊处理 if (!string.IsNullOrWhiteSpace(slot.CarrierCode)) slot.Carrier = cacheMappCarrier.FirstOrDefault(x => x.MapCode == slot.CarrierCode)?.MapName; if (!string.IsNullOrWhiteSpace(slot.PortLoadCode)) slot.PortLoad = cachePort.FirstOrDefault(x => x.EdiCode == slot.PortLoadCode)?.PortName; if (!string.IsNullOrWhiteSpace(slot.PortDischargeCode)) slot.PortDischarge = cachePort.FirstOrDefault(x => x.EdiCode == slot.PortDischargeCode)?.PortName; if (!string.IsNullOrWhiteSpace(slot.LaneName)) slot.LaneCode = cacheLane.FirstOrDefault(x => x.LaneName == slot.LaneName)?.LaneEnName; if (!string.IsNullOrWhiteSpace(slot.BookingSlotTypeName)) slot.BookingSlotType = slot.BookingSlotTypeName switch { "合约订舱" => "CONTRACT_ORDER", "SPOT订舱" => "SPOT_ORDER", _ => null }; var ctns = new List(); BookingSlotCtn ctn = new(); // 解析箱信息 for (int n = 23; n <= 24; n++) { var cell = row.GetCell(n); if (cell == null) continue; var value = cell.ToString(); if (string.IsNullOrWhiteSpace(value)) continue; switch (n) { case 23: { ctn.CtnAll = value; ctn.CtnCode = cacheCtnCode.FirstOrDefault(x => x.CtnName == value)?.EdiCode; break; } case 24: { if (int.TryParse(value, out int temp)) ctn.CtnNum = temp; break; } } } if (!string.IsNullOrEmpty(ctn.CtnCode)) { ctns.Add(ctn); } data.Add(slot, ctns); lastSlot = slot; } // 判断是否已经存在 var noList = data.Select(x => x.Key).Select(x => x.SlotBookingNo).ToList(); var existsNoList = await tenantDb.Queryable() .Where(x => noList.Contains(x.SlotBookingNo)) .Select(x => x.SlotBookingNo) .ToListAsync(); foreach (var item in data) { if (existsNoList.Contains(item.Key.SlotBookingNo)) { list.Add(new { IsSuccess = false, FailReason = "此订舱编号已存在", SlotBookingNo = item.Key.SlotBookingNo }); continue; } await tenantDb.Insertable(item.Key).ExecuteReturnEntityAsync(); var id = item.Key.Id; if (item.Value.Any()) { item.Value.ForEach(x => { x.SlotId = id; }); await tenantDb.Insertable(item.Value).ExecuteCommandAsync(); } list.Add(new { IsSuccess = true, SlotBookingNo = item.Key.SlotBookingNo }); } succ = true; var group = data.Keys.Where(x => !existsNoList.Contains(x.SlotBookingNo) && !string.IsNullOrEmpty(x.Vessel) && !string.IsNullOrEmpty(x.Voyno) && !string.IsNullOrEmpty(x.ContractNo) && !string.IsNullOrEmpty(x.BookingSlotType) && !string.IsNullOrEmpty(x.CarrierCode) && !string.IsNullOrEmpty(x.PortLoadCode) && !string.IsNullOrEmpty(x.PortDischargeCode)) .GroupBy(x => new { x.Vessel, x.Voyno, x.CarrierCode, x.BookingSlotType, x.PortDischargeCode, x.PortLoadCode, x.ContractNo }).ToList(); foreach (var item in group) { await _bookingSlotStockService.BookingSlotStock(new BookingSlotStockUpdateModel { BookingSlotType = item.Key.BookingSlotType, CarrierCode = item.Key.CarrierCode, ContractNo = item.Key.ContractNo, Vessel = item.Key.Vessel, Voyno = item.Key.Voyno, PortLoadId = item.Key.PortLoadCode, PortDischargeId = item.Key.PortDischargeCode, TenantId = long.Parse(user.TenantId) }); } } catch (Exception ex) { Logger.Log(NLog.LogLevel.Error, $"导入舱位异常,原因:{ex.Message}"); return DataResult>.Failed(string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotImportException)), ex.Message)); } if (succ) { return DataResult>.Success(list); } return DataResult>.Failed(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotImportFail))); } #endregion #region 导出舱位为Excel /// /// 导出舱位为Excel /// /// 请求参数 /// public async Task> ExportOrder(PageRequest querySearch) { var tenantDb = saasService.GetBizDbScopeById(user.TenantId); //序列化查询条件 var whereList = db.ConfigQuery.Context.Utilities.JsonToConditionalModels(querySearch.QueryCondition); List ctnCodeArr = new List(); List labelIdArray = new List(); if (whereList.Any(t => ((ConditionalModel)t).FieldName.Equals("ctnStat", StringComparison.OrdinalIgnoreCase))) { var codList = whereList.Where(t => ((ConditionalModel)t).FieldName.Equals("ctnStat", StringComparison.OrdinalIgnoreCase)).ToList(); ctnCodeArr = codList.Select(t => ((ConditionalModel)t).FieldValue).ToList(); codList.ForEach(t => { whereList.Remove(t); }); } if (whereList.Any(t => ((ConditionalModel)t).FieldName.Equals("labelIdArray", StringComparison.OrdinalIgnoreCase))) { var codList = whereList.Where(t => ((ConditionalModel)t).FieldName.Equals("labelIdArray", StringComparison.OrdinalIgnoreCase)).ToList(); labelIdArray = codList.Select(t => long.Parse(((ConditionalModel)t).FieldValue)).ToList(); codList.ForEach(t => { whereList.Remove(t); }); } var select = tenantDb.Queryable() .LeftJoin((a, b) => a.Id == b.SlotId) .Where(whereList) .WhereIF(ctnCodeArr != null && ctnCodeArr.Count > 0, (a, b) => b != null && !string.IsNullOrWhiteSpace(b.CtnCode) && ctnCodeArr.Contains(b.CtnCode) && b.Deleted == false) .WhereIF(labelIdArray != null && labelIdArray.Count > 0, a => SqlFunc.Subqueryable() .Where(x => x.BusinessId == a.Id && labelIdArray.Contains(x.LabelId)) .Any()) .Select().Distinct(); //var sql = select.OrderByDescending(u => u.CreatedTime).ToSqlString(); var entities = await select.OrderByDescending(u => u.Id).ToListAsync(); var data = entities.Adapt>(); var slotIds = entities.Select(x => x.Id); if (slotIds.Any()) { // 查询舱位绑定的销售信息,赋值到舱位对象中 List allocationInfoList = await tenantDb.Queryable() .Where(x => slotIds.Contains(x.BookingSlotId)) .Select(x => new BookingSlotSaleInfoDto { Id = x.Id, BookingId = x.BookingId, BookingSlotId = x.BookingSlotId, CustomerId = x.CustomerId, CustomerName = x.CustomerName, CustServiceId = x.CustService, CustService = x.CustService, SaleId = x.SaleId, Sale = x.Sale, OpId = x.OpId, Op = x.Op, DocId = x.DocId, Doc = x.Doc, Business = x.Business, BusinessId = x.BusinessId, SaleTime = x.SaleTime, Shipper = x.Shipper, GoodsName = x.GoodsName, SellingPrice = x.SellingPrice }).ToListAsync(); if (allocationInfoList.Any()) { // 判断是否启用了委托单位查看控制权限 var paramConfig = _configService.GetConfig(IS_ENABLE_CUSTOMER_AUTHORITY, long.Parse(user.TenantId), false).GetAwaiter().GetResult()?.Data?.Value; // 判断是否启用了委托单位查看控制权限 bool isEnableCustomerAuthority = false; if (paramConfig.Equals("ENABLE", StringComparison.OrdinalIgnoreCase)) { isEnableCustomerAuthority = true; } List userList = new List(); List userListStr = new List(); //if (isEnableCustomerAuthority) //{ // userList = await _sysDataUserMenuService.GetDataScopeList(MenuConst.MenuDjyCustomer); // if (userList == null || userList.Count == 0) // { // isEnableCustomerAuthority = false; // } // else // { // userListStr = userList.Select(x => x.ToString()).ToList(); // } //} var saleInfoGroup = allocationInfoList.GroupBy(x => x.BookingSlotId); foreach (var item in saleInfoGroup) { if (isEnableCustomerAuthority) { // 遍历销售信息,如果销售信息中的“销售、操作、单证、客服、创建人”中不在当前登陆人的权限范围,则隐藏客户信息 foreach (BookingSlotSaleInfoDto saleInfoItem in item) { if (!userList.Contains(saleInfoItem.CreateBy) && !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 = "--"; } } } var slot = data.FirstOrDefault(x => x.Id == item.Key); if (slot != null) { slot.BookingSlotSaleInfoList = item.ToList(); } } } // 查询舱位绑定的标签信息,赋值到舱位对象中 var labelCacheList = await _bookingLabelService.List(1); var labelAllocationList = await tenantDb.Queryable() .Where(x => slotIds.Contains(x.BusinessId)) .ToListAsync(); if (labelAllocationList.Any()) { var labelInfoGroup = labelAllocationList.GroupBy(x => x.BusinessId); foreach (var item in labelInfoGroup) { var slot = data.FirstOrDefault(x => x.Id == item.Key); if (slot != null) { slot.LabelList = item.Select(x => { var labelCache = labelCacheList.Data.FirstOrDefault(l => l.Id == x.LabelId); if (labelCache != null) { return new BookingLabelBaseDto { Id = x.LabelId, Name = labelCache.Name, Color = labelCache.Color, Scope = labelCache.Scope }; } return null; }).ToList(); slot.LabelList.RemoveAll(x => x == null); } } } } string fileRoot = AppSetting.app(new string[] { "FileSettings", "BasePath" }); string relativePath = AppSetting.app(new string[] { "FileSettings", "RelativePath" }); var tempDic = Path.Combine(fileRoot, relativePath, DateTime.Now.Ticks.ToString()); Directory.CreateDirectory(tempDic); var fileAbsPath = Path.Combine(tempDic, ""); //var opt = App.GetOptions(); //var dirAbs = opt.basePath; //if (string.IsNullOrEmpty(dirAbs)) //{ // dirAbs = App.WebHostEnvironment.WebRootPath; //} //var fileAbsPath = Path.Combine(dirAbs, "upload/printtemplate/舱位信息导出模板.xlsx"); if (!File.Exists(fileAbsPath)) { //舱位台账导出模板【舱位台账导出模板】文件不存在 throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotExportTemplateNull))); } var fs = new FileStream(fileAbsPath, FileMode.Open); var excelwork = new XSSFWorkbook(fs); var sheet = excelwork.GetSheet("导出"); var rowIndex = 1; var dateStr = "yyyy-MM-dd"; var dateTimeStr = "yyyy-MM-dd HH:mm:ss"; foreach (BookingSlotBaseDto item in data) { var row = sheet.GetRow(rowIndex); if (row == null) { row = sheet.CreateRow(rowIndex); } for (int i = 0; i <= 35; i++) { var cell = row.GetCell(i); if (cell == null) { cell = row.CreateCell(i); } var value = i switch { 0 => item.SlotBookingNo, 1 => item.CarrierCode, 2 => item.Vessel, 3 => item.Voyno, 4 => item.ContractNo, 5 => item.BookingSlotType, 6 => item.ETD?.ToString(dateTimeStr), 7 => item.ETA?.ToString(dateTimeStr), 8 => item.BookingParty, 9 => item.IsCancellation ? "是" : "", 10 => item.PlaceReceipt, 11 => item.PlaceDelivery, 12 => item.PortLoad, 13 => item.PortDischarge, 14 => item.CtnStat, 15 => item.WeekAt?.ToString(), 16 => item.SICutDate?.ToString(dateTimeStr), 17 => item.VGMSubmissionCutDate?.ToString(dateTimeStr), 18 => item.CYCutDate?.ToString(dateTimeStr), 19 => item.ManifestCutDate?.ToString(dateTimeStr), 20 => item.MDGFCutDate?.ToString(dateTimeStr), 21 => item.LaneName, 22 => item.DetensionFreeDays?.ToString(), 23 => item.CreateTime.ToString(dateTimeStr), 24 => item.VGMRltStat, 25 => item.SIRltStat, 26 => item.TakeCtnRltStat, 27 => item.ReturnCtnRltStat, 28 => item.NominationRltStat, 29 => item.AmendmentRltStat, 30 => item.CancellationRltStat, 31 => item.DischargeFullRltStat, 32 => item.GateOutFullRltStat, 33 => "", 34 => item.Remark, 35 => item.PriceCalculationDate?.ToString(dateStr), _ => "" }; cell.SetCellValue(value); } if (item.BookingSlotSaleInfoList != null && item.BookingSlotSaleInfoList.Count > 0) { for (int m = 0; m < item.BookingSlotSaleInfoList.Count; m++) { BookingSlotSaleInfoDto saleItem = item.BookingSlotSaleInfoList[m]; var row2 = sheet.GetRow(rowIndex); if (row2 == null) { row2 = sheet.CreateRow(rowIndex); } for (int i = 36; i <= 42; i++) { var cell2 = row2.GetCell(i); if (cell2 == null) { cell2 = row2.CreateCell(i); } var value2 = i switch { 36 => saleItem.CustomerName, 37 => saleItem.CustService, 38 => saleItem.Sale, 39 => saleItem.Shipper, 40 => saleItem.GoodsName, 41 => saleItem.SellingPrice?.ToString(), 42 => saleItem.SaleTime?.ToString(dateTimeStr), _ => "" }; cell2.SetCellValue(value2); } // 如果不是最后一条数据,创建新行 if (m < item.BookingSlotSaleInfoList.Count - 1) { rowIndex++; } } } rowIndex++; } //var fileFullPath = Path.Combine(App.WebHostEnvironment.WebRootPath, App.GetOptions().Path);//服务器路径 //if (!Directory.Exists(fileFullPath)) //{ // Directory.CreateDirectory(fileFullPath); //} var fileName = $"舱位导出_{DateTime.Now.ToString("yyyyMMdd-HHmmss")}.xlsx";//名称 var filestream = new FileStream(Path.Combine("", fileName), FileMode.OpenOrCreate, FileAccess.ReadWrite); excelwork.Write(filestream); await Task.Delay(2000); //return HttpUtility.UrlEncode(fileName, Encoding.GetEncoding("UTF-8")); return DataResult.Success(fileName); } #endregion /// /// 舱位台账查询 /// /// 查询条件 /// public async Task>> GetPageAsync(PageRequest querySearch) { var tenantDb = saasService.GetBizDbScopeById(user.TenantId); long batchNo = SnowFlakeSingle.Instance.NextId(); DateTime bDate = DateTime.Now; Logger.Log(NLog.LogLevel.Info, $"批次={batchNo} 开始查询台账"); //序列化查询条件 var whereList = db.ConfigQuery.Context.Utilities.JsonToConditionalModels(querySearch.QueryCondition); List ctnCodeArr = new List(); List labelIdArray = new List(); if (whereList.Any(t => ((ConditionalModel)t).FieldName.Equals("ctnStat", StringComparison.OrdinalIgnoreCase))) { var codList = whereList.Where(t => ((ConditionalModel)t).FieldName.Equals("ctnStat", StringComparison.OrdinalIgnoreCase)).ToList(); ctnCodeArr = codList.Select(t => ((ConditionalModel)t).FieldValue).ToList(); codList.ForEach(t => { whereList.Remove(t); }); } if (whereList.Any(t => ((ConditionalModel)t).FieldName.Equals("labelIdArray", StringComparison.OrdinalIgnoreCase))) { var codList = whereList.Where(t => ((ConditionalModel)t).FieldName.Equals("labelIdArray", StringComparison.OrdinalIgnoreCase)).ToList(); labelIdArray = codList.Select(t => long.Parse(((ConditionalModel)t).FieldValue)).ToList(); codList.ForEach(t => { whereList.Remove(t); }); } var result = tenantDb.Queryable() .LeftJoin((a, b) => a.Id == b.SlotId) .Where(whereList) .WhereIF(ctnCodeArr != null && ctnCodeArr.Count > 0, (a, b) => b != null && !string.IsNullOrWhiteSpace(b.CtnCode) && ctnCodeArr.Contains(b.CtnCode) && b.Deleted == false) .WhereIF(labelIdArray != null && labelIdArray.Count > 0, a => SqlFunc.Subqueryable() .Where(x => x.BusinessId == a.Id && labelIdArray.Contains(x.LabelId)) .Any()) .Select().Distinct(); var data = await result.ToQueryPageAsync(querySearch.PageCondition); var slotIds = data.Data.Select(x => x.Id); if (slotIds.Any()) { // 查询舱位绑定的销售信息,赋值到舱位对象中 List allocationInfoList = await tenantDb.Queryable() .Where(x => slotIds.Contains(x.BookingSlotId)) .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, Business = x.Business, BusinessId = x.BusinessId, SaleTime = x.SaleTime, Shipper = x.Shipper, GoodsName = x.GoodsName, SellingPrice = x.SellingPrice, CreateBy = x.CreateBy }).ToListAsync(); if (allocationInfoList.Any()) { var paramConfig = _configService.GetConfig(IS_ENABLE_CUSTOMER_AUTHORITY, long.Parse(user.TenantId), false).GetAwaiter().GetResult()?.Data?.Value; // 判断是否启用了委托单位查看控制权限 bool isEnableCustomerAuthority = false; if (paramConfig.Equals("ENABLE", StringComparison.OrdinalIgnoreCase)) { isEnableCustomerAuthority = true; } List userList = new List(); List userListStr = new List(); //if (isEnableCustomerAuthority) //{ // userList = await _sysDataUserMenuService.GetDataScopeList(MenuConst.MenuDjyCustomer); // if (userList == null || userList.Count == 0) // { // isEnableCustomerAuthority = false; // } // else // { // userListStr = userList.Select(x => x.ToString()).ToList(); // } //} var saleInfoGroup = allocationInfoList.GroupBy(x => x.BookingSlotId); foreach (var item in saleInfoGroup) { if (isEnableCustomerAuthority) { // 遍历销售信息,如果销售信息中的“销售、操作、单证、客服、创建人”中不在当前登陆人的权限范围,则隐藏客户信息 foreach (BookingSlotSaleInfoDto saleInfoItem in item) { if (!userList.Contains(saleInfoItem.CreateBy) && !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 = "--"; } } } var slot = data.Data.FirstOrDefault(x => x.Id == item.Key); if (slot != null) { slot.BookingSlotSaleInfoList = item.ToList(); } } } // 查询舱位绑定的标签信息,赋值到舱位对象中 var labelCacheList = await _bookingLabelService.List(1); var labelAllocationList = await tenantDb.Queryable() .Where(x => slotIds.Contains(x.BusinessId)) .ToListAsync(); if (labelAllocationList.Any()) { var labelInfoGroup = labelAllocationList.GroupBy(x => x.BusinessId); foreach (var item in labelInfoGroup) { var slot = data.Data.FirstOrDefault(x => x.Id == item.Key); if (slot != null) { slot.LabelList = item.Select(x => { var labelCache = labelCacheList.Data.FirstOrDefault(l => l.Id == x.LabelId); if (labelCache != null) { return new BookingLabelBaseDto { Id = x.LabelId, Name = labelCache.Name, Color = labelCache.Color, Scope = labelCache.Scope }; } return null; }).ToList(); slot.LabelList.RemoveAll(x => x == null); } } } } DateTime eDate = DateTime.Now; TimeSpan ts = eDate.Subtract(bDate); var timeDiff = ts.TotalMilliseconds; Logger.Log(NLog.LogLevel.Info, $"批次={batchNo} 请求完成,耗时:{timeDiff}ms."); return data; } #region 获取舱位变更比对结果 /// /// 获取舱位变更比对结果 /// /// 舱位主键 /// 批次号 /// 返回舱位变更比对结果 public async Task>> GetSlotCompareResult(long id, string batchNo) { var tenantDb = saasService.GetBizDbScopeById(user.TenantId); var compareInfo = await tenantDb.Queryable() .FirstAsync(t => t.SlotId == id && t.CompareBatchNo == batchNo); if (compareInfo == null) { //舱位变更比对结果不存在 throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotCompareNull))); } if (string.IsNullOrWhiteSpace(compareInfo.CompareRlt)) { //获取舱位变更比对结果错误,比对内容不存在 throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotCompareJsonNull))); } if (!string.IsNullOrWhiteSpace(compareInfo.CompareRlt)) { var data = JsonConvert.DeserializeObject>(compareInfo.CompareRlt); return DataResult>.Success(data); } return DataResult>.FailedData(new List()); } #endregion #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 #region 作废舱位(可以批量) /// /// 作废舱位(可以批量) /// /// 舱位主键数组 /// 返回回执 public async Task Delete(long[] ids) { var tenantDb = saasService.GetBizDbScopeById(user.TenantId); foreach (long id in ids) { var slot = await tenantDb.Queryable().FirstAsync(x => x.Id == id); if (slot == null) { throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotBaseInfoNull))); } slot.Deleted = true; slot.DeleteBy = long.Parse(user.UserId); slot.DeleteUserName = user.UserName; slot.DeleteTime = DateTime.Now; await tenantDb.Updateable(slot).ExecuteCommandAsync(); var ctnList = tenantDb.Queryable().Where(x => x.SlotId == id).ToList(); ctnList.ForEach(t => { t.Deleted = true; t.DeleteTime = DateTime.Now; t.DeleteBy = long.Parse(user.UserId); t.DeleteUserName = user.UserName; tenantDb.Updateable(t).ExecuteCommand(); }); var alloc = tenantDb.Queryable().Where(a => a.BookingSlotId == id && a.Deleted == false).ToList(); if (alloc.Count > 0) { alloc.ForEach(t => { t.Deleted = true; t.DeleteTime = DateTime.Now; t.DeleteBy = long.Parse(user.UserId); t.DeleteUserName = user.UserName; tenantDb.Updateable(t).ExecuteCommand(); }); } //更新库存 await _bookingSlotStockService.BookingSlotStock(new BookingSlotStockUpdateModel { BookingSlotType = slot.BookingSlotType, CarrierCode = slot.CarrierCode, ContractNo = slot.ContractNo, Vessel = slot.Vessel, Voyno = slot.Voyno, PortLoadId = slot.PortLoadCode, PortDischargeId = slot.PortDischargeCode, TenantId = long.Parse(user.TenantId) }); } return DataResult.Successed(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotDeleteSucc))); } #endregion /// /// 查询指定舱位可用的箱子列表 /// /// 舱位主键 /// 可用的箱子列表 public async Task>> GetAvailableCtnsBySlot(long slotId) { var tenantDb = saasService.GetBizDbScopeById(user.TenantId); if (await tenantDb.Queryable().AnyAsync(x => x.Id == slotId && x.IsCancellation == false) == false) { //获取舱位失败,舱位不存在或已作废 throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotCreateRecordDeletedOrNoExists))); } // 1. 【舱位基础表】与【箱子表】做关联,并根据【舱位主键】、【箱型】做分组,统计出【总的箱量】,作为queryable1 var queryable1 = tenantDb.Queryable((bas, ctn) => bas.Id == ctn.SlotId) .Where(bas => bas.IsCancellation == false && bas.Id == slotId) .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) .Where((alc, ctn) => alc.BookingSlotId == slotId) .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, numResidue = SqlFunc.IsNull(q1.numAll - q2.numUse, q1.numAll) }) .MergeTable() .Where(r => r.numResidue > 0); // 4. 执行ToList(),得到可用的【舱位主键】、【箱型】、【箱量】列表 var canUselist = await queryable3.ToListAsync(); List ctnCodeCache = new List(); var allCtnCodeList = await _codeCtnService.GetAllList(); if (allCtnCodeList.Succeeded) { ctnCodeCache = allCtnCodeList.Data; } List result = canUselist.Select(c => new BookingSlotCtnDto() { CtnCode = c.ctnCode, CtnNum = c.numResidue, CtnAll = ctnCodeCache.FirstOrDefault(e => e.EdiCode == c.ctnCode)?.CtnName ?? throw new Exception($"舱位信息中存在未收录的箱型:{c.ctnCode},需要在箱型字典中补充"), }).ToList(); return DataResult>.Success(result); } #region 获取现舱位查询 /// /// 获取现舱位查询 /// /// 查询条件 /// public async Task>> GetAvailableBookingSlots(PageRequest querySearch) { var tenantDb = saasService.GetBizDbScopeById(user.TenantId); //序列化查询条件 var whereList = db.ConfigQuery.Context.Utilities.JsonToConditionalModels(querySearch.QueryCondition); List ctnCodeArr = new List(); if (whereList.Any(t => ((ConditionalModel)t).FieldName.Equals("ctnStat", StringComparison.OrdinalIgnoreCase))) { var codList = whereList.Where(t => ((ConditionalModel)t).FieldName.Equals("ctnStat", StringComparison.OrdinalIgnoreCase)).ToList(); ctnCodeArr = codList.Select(t => ((ConditionalModel)t).FieldValue).ToList(); codList.ForEach(t => { whereList.Remove(t); }); } if (querySearch.PageCondition.SortConditions == null || querySearch.PageCondition.SortConditions.Length == 0) { querySearch.PageCondition.SortConditions = new SortCondition[]{new SortCondition { SortField = nameof(BookingSlotBase.CreateTime), ListSortDirection = System.ComponentModel.ListSortDirection.Descending }}; } // 1. 【舱位基础表】与【箱子表】做关联,并根据【舱位主键】、【箱型】做分组,统计出【总的箱量】,作为queryable1 var queryable1 = tenantDb.Queryable().InnerJoin((bas, ctn) => bas.Id == ctn.SlotId) .Where(whereList) .Where(bas => bas.IsCancellation == false) .WhereIF(ctnCodeArr != null && ctnCodeArr.Count > 0, (a, b) => b != null && !string.IsNullOrWhiteSpace(b.CtnCode) && ctnCodeArr.Contains(b.CtnCode) && b.Deleted == false) .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().InnerJoin((alc, ctn) => alc.Id == ctn.SlotAllocId) .Where((alc, ctn) => alc.Deleted == false) .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 CtnStatInfo { id = q1.id, ctnCode = q1.ctnCode, numAll = q1.numAll, numResidue = SqlFunc.IsNull(q1.numAll - q2.numUse, q1.numAll) }) .MergeTable() .Where(r => r.numResidue > 0); var canUselist = await queryable3.ToListAsync(); List ids = new List(); if (canUselist.Count > 0) { ids = canUselist.Select(a => a.id).Distinct().ToList(); } var baseList = await tenantDb.Queryable().Where(u => ids.Contains(u.Id)) .OrderBy(querySearch.PageCondition.SortConditions) .Select() .ToPageListAsync(querySearch.PageCondition.PageIndex, querySearch.PageCondition.PageSize); List ctnCodeCache = new List(); var allCtnist = await _codeCtnService.GetAllList(); if (allCtnist.Succeeded) { ctnCodeCache = allCtnist.Data; } // 构建结果 foreach (var item in baseList) { var ctnList = canUselist.Where(c => c.id == item.Id).ToList(); if (ctnList?.Any() == true) { item.CtnList = ctnList.Select(c => { var ctnCode = ctnCodeCache.FirstOrDefault(e => !string.IsNullOrWhiteSpace(e.EdiCode) && e.EdiCode == c.ctnCode); if (ctnCode == null) throw new Exception($"舱位信息中存在未收录的箱型:{c.ctnCode},需要在箱型字典中补充"); return new BookingSlotCtnDto() { CtnCode = c.ctnCode, CtnNum = c.numResidue, TotalNum = c.numAll, CtnAll = ctnCode.CtnName }; }).ToList(); } } if (baseList.Count > 0) return DataResult>.Success(baseList); return DataResult>.FailedData(baseList); } #endregion #region 引入现舱关联海运出口 /// /// 引入现舱关联海运出口 /// /// 请求参数 /// public async Task> BringInBookingSlotToOrder(BringInBookingSlotReq model) { /* 1、提取海运出口的订舱详情 2、触发舱位的关联订舱 */ if (model.seaExportId == 0) { //海运出口主键不能为空 throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.SeaExportIdNotNull))); } SeaExportRes orderInfo = null; var orderRlt = _seaExportService.GetSeaExportInfo(model.seaExportId.ToString()); if (orderRlt.Succeeded) { orderInfo = orderRlt.Data; } if (orderInfo == null) { //海运出口详情获取失败,已作废或数据不存在 throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.SeaExportInfoNotExists))); } var generateDto = new BookingGenerateDto() { CustomerId = orderInfo.CustomerId, CustomerName = orderInfo.CustomerName, CustServiceId = orderInfo.CustomerService, CustServiceName = orderInfo.CustomerServiceName, SaleId = orderInfo.SaleId, SaleName = orderInfo.Sale, GOODSNAME = orderInfo.GoodsName, }; if (model.slots.Any(k => k.SplitOrMerge == 1 || k.SplitOrMerge == 2)) { generateDto.BookingReferNo = orderInfo.OrderNo; generateDto.NewMBlNo = orderInfo.MBLNO; generateDto.NewSubBlNo = orderInfo.HBLNO; } return await ImportSlots(new ImportSlotsDto { slots = model.slots, isCheck = false, bookingOrderId = model.seaExportId, generateModel = generateDto }); } #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); } } public class CtnStatInfo { public long id { get; set; } public string ctnCode { get; set; } public int numAll { get; set; } public int numResidue { get; set; } } }