using Furion; using Furion.DependencyInjection; using Furion.DistributedIDGenerator; using Furion.DynamicApiController; using Furion.EventBus; using Furion.Extensions; using Furion.FriendlyException; using Furion.JsonSerialization; using Mapster; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Myshipping.Application.ConfigOption; using Myshipping.Application.Entity; using Myshipping.Application.Event; using Myshipping.Application.Helper; using Myshipping.Application.Service.BookingLabel.Dto; using Myshipping.Application.Service.BookingOrder.Dto; using Myshipping.Application.Service.BookingSlot.Dto; using Myshipping.Core; using Myshipping.Core.Const; using Myshipping.Core.Entity; using Myshipping.Core.Service; using NPOI.XSSF.UserModel; using SqlSugar; using StackExchange.Profiling.Internal; using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Yitter.IdGenerator; namespace Myshipping.Application { /// /// 订舱舱位 /// [ApiDescriptionSettings("Application", Name = "BookingSlot", Order = 1)] public class BookingSlotService : IDynamicApiController, ITransient, IBookingSlotService { private readonly SqlSugarRepository _repBase; private readonly SqlSugarRepository _repCtn; private readonly SqlSugarRepository _repStock; private readonly SqlSugarRepository _repAllocation; private readonly SqlSugarRepository _repLabelAllocation; private readonly SqlSugarRepository _repAllocationCtn; private readonly SqlSugarRepository _bookingFileRepository; private readonly SqlSugarRepository _bookingSlotCompareRepository; private readonly SqlSugarRepository _bookingOrderContactRepository; private readonly SqlSugarRepository _repBookingOrder; private readonly SqlSugarRepository _repBookingLog; private readonly SqlSugarRepository _repBookingLogDetail; private readonly SqlSugarRepository _bookingfile; private readonly SqlSugarRepository _repPrintTemplate; private readonly SqlSugarRepository _sysUserRepository; private readonly ILogger _logger; private readonly ISysCacheService _cache; private readonly IEventPublisher _publisher; private readonly INamedServiceProvider _namedBookingOrderServiceProvider; private readonly INamedServiceProvider _namedTaskManageServiceProvider; private readonly IDjyCustomerService _djyCustomerService; private readonly IBookingValueAddedService _bookingValueAddedService; private readonly IBookingLabelService _bookingLabelService; private readonly IDjyTenantParamService _djyTenantParamService; private readonly ISysDataUserMenu _sysDataUserMenuService; 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_moidfynotice"; const string CONST_BC_MODIFY_NOTICE_FILE_NAME = "Booking Amendment Notice"; //租户默认参数管理(BC任务或舱位生成订舱客户联系人必填) const string CONST_CREATE_BOOKING_NEED_CONTACT = "BC_TASK_OR_SLOT_BOOKING_NEED_CONTACT"; public BookingSlotService(SqlSugarRepository repBase, SqlSugarRepository repCtn, SqlSugarRepository repStock, SqlSugarRepository repBookingLog, SqlSugarRepository repBookingLogDetail, SqlSugarRepository bookingfile, ILogger logger, ISysCacheService cache, IEventPublisher publisher, IDjyCustomerService djyCustomerService, SqlSugarRepository repAllocation, SqlSugarRepository repAllocationCtn, SqlSugarRepository bookingFileRepository, SqlSugarRepository bookingSlotCompareRepository, SqlSugarRepository bookingOrderContactRepository, INamedServiceProvider namedBookingOrderServiceProvider, IBookingValueAddedService bookingValueAddedService, SqlSugarRepository repBookingOrder, SqlSugarRepository repLabelAllocation, IBookingLabelService bookingLabelService, INamedServiceProvider namedTaskManageServiceProvider, SqlSugarRepository sysUserRepository, IDjyTenantParamService djyTenantParamService, SqlSugarRepository repPrintTemplate, ISysDataUserMenu sysDataUserMenuService) { _repBase = repBase; _repCtn = repCtn; _repStock = repStock; _repBookingLog = repBookingLog; _repBookingLogDetail = repBookingLogDetail; _repAllocation = repAllocation; _repAllocationCtn = repAllocationCtn; _djyCustomerService = djyCustomerService; _logger = logger; _cache = cache; _publisher = publisher; _bookingfile = bookingfile; _bookingFileRepository = bookingFileRepository; _bookingSlotCompareRepository = bookingSlotCompareRepository; _namedBookingOrderServiceProvider = namedBookingOrderServiceProvider; _bookingOrderContactRepository = bookingOrderContactRepository; _bookingValueAddedService = bookingValueAddedService; _repBookingOrder = repBookingOrder; _repLabelAllocation = repLabelAllocation; _bookingLabelService = bookingLabelService; _repPrintTemplate = repPrintTemplate; _sysUserRepository = sysUserRepository; _namedTaskManageServiceProvider = namedTaskManageServiceProvider; _djyTenantParamService = djyTenantParamService; _sysDataUserMenuService = sysDataUserMenuService; } #region 舱位 /// /// 保存订舱舱位 /// /// /// [HttpPost("/BookingSlot/save")] public async Task Save(BookingSlotBaseSaveInput input) { BookingSlotBase model = null; if (input.Id > 0) //修改 { var c = _repBase.AsQueryable().Where(x => x.SLOT_BOOKING_NO == input.SLOT_BOOKING_NO && input.Id != input.Id).Count(); if (c > 0) { throw Oops.Bah("订舱编号已存在"); } model = _repBase.FirstOrDefault(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.BOOKING_SLOT_TYPE != model.BOOKING_SLOT_TYPE || oldObj.CARRIERID != model.CARRIERID || oldObj.PORTLOADID != model.PORTLOADID || oldObj.PORTDISCHARGEID != model.PORTDISCHARGEID) { isNeedUpdateOldStock = true; } await _repBase.UpdateAsync(model); if (isNeedUpdateOldStock) { //更新库存 await _publisher.PublishAsync(new ChannelEventSource("BookingSlotStock:Update", new BookingSlotStockUpdateModel { BOOKING_SLOT_TYPE = oldObj.BOOKING_SLOT_TYPE, CARRIERID = oldObj.CARRIERID, CONTRACT_NO = oldObj.CONTRACT_NO, VESSEL = oldObj.VESSEL, VOYNO = oldObj.VOYNO, PORTLOADID = oldObj.PORTLOADID, PORTDISCHARGEID = oldObj.PORTDISCHARGEID, TenantId = model.TenantId })); } await _repCtn.DeleteAsync(x => x.SLOT_ID == model.Id); if (input.CtnList != null) { foreach (var ctn in input.CtnList) { var newCtn = ctn.Adapt(); newCtn.SLOT_ID = model.Id; await _repCtn.InsertAsync(newCtn); } } #region 关联订舱信息修改 if (input.BookingSlotSaleInfoList != null) { var bookingOrderService = App.GetService(); foreach (var item in input.BookingSlotSaleInfoList) { if (!item.UpdateFlag) { continue; } var allocation = await _repAllocation.FirstOrDefaultAsync(x => x.Id == item.Id); if (allocation == null) { throw Oops.Oh($"保存失败,原因:更新关联订舱时未查询到订舱(关联表Id:{item.Id})"); } // 更新关联表 item.Adapt(allocation); _repAllocation.CurrentBeginTran(); try { await _repAllocation.AsUpdateable(allocation).UpdateColumns(x => new { x.CUSTOMERID, x.CUSTOMERNAME, x.CUSTSERVICE, x.CUSTSERVICEID, x.SALE, x.SALEID, x.OP, x.OPID, x.DOC, x.DOCID, x.BUSINESSID, x.BUSINESS, x.SHIPPER, x.SALE_TIME, x.GOODSNAME, x.SELLING_PRICE, x.UpdatedUserId, x.UpdatedUserName, x.UpdatedTime, }).ExecuteCommandAsync(); // 更新订舱表 var bookingOrder = await _repBookingOrder.FirstOrDefaultAsync(x => x.Id == allocation.BOOKING_ID); 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 }); }); } _repAllocation.CurrentCommitTran(); } catch (Exception) { _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 = _repBase.AsQueryable().Where(x => x.SLOT_BOOKING_NO == input.SLOT_BOOKING_NO).Count(); if (c > 0) { throw Oops.Bah("订舱编号已存在"); } model = input.Adapt(); await _repBase.InsertAsync(model); foreach (var ctn in input.CtnList) { var newCtn = ctn.Adapt(); newCtn.SLOT_ID = model.Id; await _repCtn.InsertAsync(newCtn); } await InsLog("Add", model.Id, "新增舱位"); } //更新库存 await _publisher.PublishAsync(new ChannelEventSource("BookingSlotStock:Update", new BookingSlotStockUpdateModel { BOOKING_SLOT_TYPE = model.BOOKING_SLOT_TYPE, CARRIERID = model.CARRIERID, CONTRACT_NO = model.CONTRACT_NO, VESSEL = model.VESSEL, VOYNO = model.VOYNO, PORTLOADID = model.PORTLOADID, PORTDISCHARGEID = model.PORTDISCHARGEID, TenantId = model.TenantId })); return await Detail(model.Id); } /// /// 直接在台账页数据列表上进行保存 /// /// [HttpPost("/BookingSlot/SaveDataInList")] public async Task SaveDataInList(BookingSlotBaseSaveDataInListInputDto input) { long slotBaseId = 0; if (input.SlotBase != null) { slotBaseId = input.SlotBase.GetLongValue("Id"); if (slotBaseId == 0) { throw Oops.Bah("修改舱位信息需要提供参数:Id"); } var model = await _repBase.FirstOrDefaultAsync(x => x.Id == slotBaseId); if (model == null) { throw Oops.Bah("未查询到舱位信息,请刷新页面"); } var type = typeof(BookingSlotBase); var properties = type.GetProperties(); List updateColumns = new(input.SlotBase.Count + 3) { "UpdatedTime","UpdatedUserId","UpdatedUserName" }; foreach (var item in input.SlotBase) { if (item.Key == "Id") { continue; } updateColumns.Add(item.Key); var property = properties.FirstOrDefault(x => x.Name.Equals(item.Key, StringComparison.OrdinalIgnoreCase)) ?? throw Oops.Bah($"参数有误,该属性不存在:{item.Key}"); if (property.PropertyType == typeof(string) || property.PropertyType == typeof(string)) { property.SetValue(model, input.SlotBase.GetStringValue(item.Key)); } else if (property.PropertyType == typeof(int) || property.PropertyType == typeof(int?)) { property.SetValue(model, input.SlotBase.GetIntValue(item.Key)); } else if (property.PropertyType == typeof(long) || property.PropertyType == typeof(long?)) { property.SetValue(model, input.SlotBase.GetLongValue(item.Key)); } else if (property.PropertyType == typeof(DateTime) || property.PropertyType == typeof(DateTime?)) { property.SetValue(model, input.SlotBase.GetDateTimeValue(item.Key)); } else if (property.PropertyType == typeof(bool) || property.PropertyType == typeof(bool?)) { property.SetValue(model, input.SlotBase.GetBooleanValue(item.Key)); } else if (property.PropertyType == typeof(decimal) || property.PropertyType == typeof(decimal?)) { property.SetValue(model, input.SlotBase.GetDecimalValue(item.Key)); } else { throw new Exception($"需要维护此类型:{property.PropertyType},请联系管理员"); } } await _repBase.AsUpdateable(model).UpdateColumns(updateColumns.ToArray()).ExecuteCommandAsync(); } if (input.SlotSaleInfo != null) { var allocationId = input.SlotSaleInfo.GetLongValue("Id"); if (allocationId == 0) { throw Oops.Bah("修改订舱信息需要提供参数:Id"); } var model = await _repAllocation.FirstOrDefaultAsync(x => x.Id == allocationId); if (model == null) { throw Oops.Bah("未查询到关联的订舱信息,请刷新页面"); } slotBaseId = model.BOOKING_SLOT_ID; var update1 = _repAllocation.Context.Updateable(); var update2 = _repBookingOrder.Context.Updateable(); foreach (var item in input.SlotSaleInfo) { if (item.Key == "Id") { continue; } if (item.Key.Equals("CUSTOMERNAME", StringComparison.OrdinalIgnoreCase)) { var value = input.SlotSaleInfo.GetStringValue(item.Key); update1.SetColumns(x => x.CUSTOMERNAME == value); update2.SetColumns(x => x.CUSTOMERNAME == value); } else if (item.Key.Equals("CUSTOMERID", StringComparison.OrdinalIgnoreCase)) { var value = input.SlotSaleInfo.GetLongValue(item.Key); update1.SetColumns(x => x.CUSTOMERID == value); update2.SetColumns(x => x.CUSTOMERID == value); } else { throw Oops.Bah($"需要维护此字段:{item.Key},请联系管理员"); } } try { _repAllocation.CurrentBeginTran(); await update1.Where(x => x.Id == model.Id).ExecuteCommandAsync(); await update2.Where(x => x.Id == model.BOOKING_ID).ExecuteCommandAsync(); _repAllocation.CurrentCommitTran(); var bookingOrderService = App.GetService(); await bookingOrderService.SendBookingOrder(new long[] { model.BOOKING_ID }); } catch (Exception) { _repAllocation.CurrentRollbackTran(); throw; } } //return await Detail(slotBaseId); } /// /// 获取订舱舱位 /// /// /// [HttpGet("/BookingSlot/detail")] public async Task Detail(long id) { var slotBase = await _repBase.FirstOrDefaultAsync(u => u.Id == id); if (slotBase == null) { throw Oops.Oh("未查询到此舱位信息,可能已被删除,请重新查询后重试"); } var ctns = await _repCtn.Where(x => x.SLOT_ID == id).ToListAsync(); var rtn = slotBase.Adapt(); rtn.CtnList = ctns.Adapt>(); List list = new List(); var main = await _repBookingLog.AsQueryable().Where(u => u.BookingId == slotBase.Id).ToListAsync(); var mailidlist = main.Select(x => x.Id).ToList(); list = main.Adapt>(); if (list != null) { var bookinglogdetail = await _repBookingLogDetail.AsQueryable().Where(x => mailidlist.Contains(x.PId)).ToListAsync(); foreach (var item in list) { var details = bookinglogdetail.Where(x => x.PId == item.Id).ToList(); item.details = details; } } rtn.LogList = list; // 赋值关联的订舱列表 // 查询舱位绑定的销售信息,赋值到舱位对象中 List saleInfoList = await _repAllocation.AsQueryable() .Where(x => x.BOOKING_SLOT_ID == id) .Select(x => new BookingSlotSaleInfoDto { Id = x.Id, BOOKING_ID = x.BOOKING_ID, BOOKING_SLOT_ID = x.BOOKING_SLOT_ID, 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, SALE_TIME = x.SALE_TIME, SHIPPER = x.SHIPPER, GOODSNAME = x.GOODSNAME, SELLING_PRICE = x.SELLING_PRICE, CreatedUserId = x.CreatedUserId }).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 rtn; } #region 对外接口 /// /// 舱位接收保存、取消接口 /// /// [HttpPost("/BookingSlot/ApiReceive"), AllowAnonymous, ApiUser(ApiCode = "BCTaskManage")] public async Task ApiReceive(string jsonData, IFormFile file = null, IFormFile modifyFile = null) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); try { _logger.LogInformation($"jsonData={jsonData} 接收请求舱位报文"); BookingSlotBaseApiDto dto = JSON.Deserialize(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 id = InnerApiReceive(dto, bcFile, bcNoticeFile).GetAwaiter().GetResult(); result.succ = true; result.msg = "成功"; result.ext = id; } catch (Exception ex) { result.succ = false; result.msg = $"失败,原因:{ex.Message}"; } return result; } #endregion /// /// 舱位接收保存、取消接口 /// /// /// /// /// [HttpPost("/BookingSlot/InnerApiReceive")] public async Task InnerApiReceive(BookingSlotBaseApiDto dto, DynameFileInfo file = null, DynameFileInfo modifyFile = null) { long id = 0; //接口方法直接调用save、delete等方法会报错,可能因为非token授权登录导致,故重写一遍保存、删除代码 if (dto.OpType == "add" || dto.OpType == "update" || dto.OpType == "del" || dto.OpType == "cancellation") { //翻译船公司 if (!string.IsNullOrWhiteSpace(dto.DataObj.CARRIERID) && string.IsNullOrWhiteSpace(dto.DataObj.CARRIER)) { var carrierInfo = _cache.GetAllCodeCarrier().GetAwaiter().GetResult() .Where(t => t.Code.Equals(dto.DataObj.CARRIERID, StringComparison.OrdinalIgnoreCase) || t.EnName.Equals(dto.DataObj.CARRIERID, StringComparison.OrdinalIgnoreCase) || t.CnName.Equals(dto.DataObj.CARRIERID, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); if (carrierInfo != null) { dto.DataObj.CARRIER = carrierInfo.CnName?.Trim(); } } //翻译箱型代码 if (dto.DataObj.CtnList != null && dto.DataObj.CtnList.Count > 0 && dto.DataObj.CtnList.Any(t => string.IsNullOrWhiteSpace(t.CTNCODE))) { var ctnCodeList = _cache.GetAllCodeCtn().GetAwaiter().GetResult().ToList(); dto.DataObj.CtnList.ForEach(t => { if (!string.IsNullOrWhiteSpace(t.CTNALL) && string.IsNullOrWhiteSpace(t.CTNCODE)) { var ctnCode = ctnCodeList.FirstOrDefault(a => !string.IsNullOrWhiteSpace(a.Name) && a.Name.Equals(t.CTNALL, StringComparison.OrdinalIgnoreCase)); if (ctnCode != null) t.CTNCODE = ctnCode?.Code; } }); } // 解析收货地,得到装货港名称及五字码 if (!string.IsNullOrWhiteSpace(dto.DataObj.PLACERECEIPT)) { var portEnName = dto.DataObj.PLACERECEIPT.Split(',')[0]?.Trim(); //这里CMA的收货地全称放在了括号里面 if (dto.DataObj.CARRIERID.Equals("CMA", StringComparison.OrdinalIgnoreCase)) { 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 cachePortLoad = await _cache.GetAllCodePortLoad(); var portInfo = await PlaceToPortHelper.PlaceReceiptToPortload(portEnName, cachePortLoad, () => _cache.GetAllMappingPortLoad()); if (portInfo == null) { _logger.LogInformation("通过收货地城市名称未匹配到港口信息,订舱编号:{SLOT_BOOKING_NO}", dto.DataObj.SLOT_BOOKING_NO); } else { dto.DataObj.PORTLOAD = portInfo.EnName; dto.DataObj.PORTLOADID = portInfo.EdiCode; } } else { _logger.LogInformation("收货地分割后得到的城市名称为空,订舱编号:{SLOT_BOOKING_NO}", dto.DataObj.SLOT_BOOKING_NO); } } else { _logger.LogInformation("收货地为空,订舱编号:{SLOT_BOOKING_NO}", dto.DataObj.SLOT_BOOKING_NO); } // 解析交货地,得到为卸货港名称及五字码, 以及国家信息 if (!string.IsNullOrWhiteSpace(dto.DataObj.PLACEDELIVERY)) { var portEnName = dto.DataObj.PLACEDELIVERY.Split(',')[0]?.Trim(); //这里CMA的收货地全称放在了括号里面 if (dto.DataObj.CARRIERID.Equals("CMA", StringComparison.OrdinalIgnoreCase)) { if (dto.DataObj.PLACEDELIVERY.IndexOf("(") >= 0) { string currStr = Regex.Match(dto.DataObj.PLACEDELIVERY, "(?<=\\().*(?=\\))").Value?.Trim(); portEnName = currStr.Split(',')[0]?.Trim(); } } if (!string.IsNullOrWhiteSpace(portEnName)) { var cachePort = await _cache.GetAllCodePort(); var portInfo = await PlaceToPortHelper.PlaceDeliveryToPort(portEnName, cachePort, () => _cache.GetAllMappingPort()); if (portInfo == null) { _logger.LogInformation("通过交货地城市名称未匹配到港口信息,订舱编号:{SLOT_BOOKING_NO}", dto.DataObj.SLOT_BOOKING_NO); } else { dto.DataObj.PORTDISCHARGE = portInfo.EnName; dto.DataObj.PORTDISCHARGEID = portInfo.EdiCode; dto.DataObj.PORTDISCHARGE_COUNTRY = portInfo.Country; dto.DataObj.PORTDISCHARGE_COUNTRY_CODE = portInfo.CountryCode; } } else { _logger.LogInformation("交货地分割后得到的城市名称为空,订舱编号:{SLOT_BOOKING_NO}", dto.DataObj.SLOT_BOOKING_NO); } } else { _logger.LogInformation("交货地为空,订舱编号:{SLOT_BOOKING_NO}", dto.DataObj.SLOT_BOOKING_NO); } if (string.IsNullOrWhiteSpace(dto.DataObj.CTN_STAT)) { if(dto.DataObj.CtnList != null && dto.DataObj.CtnList.Count > 0) { dto.DataObj.CTN_STAT = string.Join(",",dto.DataObj.CtnList.GroupBy(a => a.CTNALL).Select(a => $"{a.Key}*{a.Select(b => b.CTNNUM).Sum()}").ToArray()); } } BookingSlotBase model = null; if (dto.OpType == "add") { var c = _repBase.AsQueryable().Filter(null, true).Where(x => x.IsDeleted == false && x.TenantId == UserManager.TENANT_ID && x.SLOT_BOOKING_NO == dto.DataObj.SLOT_BOOKING_NO).Count(); if (c > 0) { throw Oops.Bah("订舱编号已存在"); } model = dto.DataObj.Adapt(); await _repBase.InsertAsync(model); id = model.Id; foreach (var ctn in dto.DataObj.CtnList) { var newCtn = ctn.Adapt(); newCtn.SLOT_ID = model.Id; await _repCtn.InsertAsync(newCtn); } await InsLog("Add", model.Id, "新增舱位"); string batchNo = IDGen.NextID().ToString(); //处理附件 if (file != null) { _logger.LogInformation($"请求文件名:{file.FileName}"); var fileFullPath = await FileAttachHelper.SaveFileDirect(model.Id.ToString(), file.FileBytes, batchNo, file.FileName, "bcfiles"); _logger.LogInformation($"保存文件路径:{fileFullPath}"); if (!string.IsNullOrWhiteSpace(fileFullPath)) { //将格式单附件写入订舱的附件 SaveEDIFile(id, fileFullPath, file.FileName, UserManager.TENANT_ID, CONST_BC_FILE_CODE, CONST_BC_FILE_NAME).GetAwaiter(); } } if (modifyFile != null) { _logger.LogInformation($"请求文件名(变更文件):{modifyFile.FileName}"); var fileFullPath = await FileAttachHelper.SaveFileDirect(model.Id.ToString(), modifyFile.FileBytes, batchNo, modifyFile.FileName, "bcnoticefiles"); _logger.LogInformation($"保存文件路径(变更文件):{fileFullPath}"); if (!string.IsNullOrWhiteSpace(fileFullPath)) { //将格式单附件写入订舱的附件 SaveEDIFile(id, fileFullPath, modifyFile.FileName, UserManager.TENANT_ID, CONST_BC_NOTICE_FILE_CODE, CONST_BC_NOTICE_FILE_NAME).GetAwaiter(); } } } else if (dto.OpType == "update") { model = await _repBase.AsQueryable().Filter(null, true).FirstAsync(x => x.IsDeleted == false && x.TenantId == UserManager.TENANT_ID && x.SLOT_BOOKING_NO == dto.DataObj.SLOT_BOOKING_NO); if (model == null) { throw Oops.Bah($"未找到订舱编号为 {dto.DataObj.SLOT_BOOKING_NO} 的数据"); } id = model.Id; //生成待比对详情 TaskBCInfoDto bcSrcDto = new TaskBCInfoDto(); try { bcSrcDto = model.Adapt(); } catch (Exception ex) { _logger.LogInformation($"slotId={model.Id} 映射数据库对象请求对应异常,原因:{ex.Message}"); throw Oops.Bah($"slotId={model.Id} 映射数据库对象请求对应异常,原因:{ex.Message}"); } TaskBCInfoDto bcTargetDto = new TaskBCInfoDto(); try { bcTargetDto = dto.DataObj.Adapt(); } catch (Exception ex) { _logger.LogInformation($"slotId={model.Id} 映射推送的舱位请求对应异常,原因:{ex.Message}"); throw Oops.Bah($"slotId={model.Id} 映射推送的舱位请求对应异常,原因:{ex.Message}"); } _logger.LogInformation($"slotId={model.Id} 开始处理重要提醒"); //执行差异重要提醒 await MeasureDiffCautionTask(bcSrcDto, bcTargetDto, model.Id); _logger.LogInformation($"slotId={model.Id} 处理重要提醒结束"); //提取箱信息 var ctnList = _repCtn.AsQueryable().Filter(null, true) .Where(x => x.IsDeleted == false && x.TenantId.Value == UserManager.TENANT_ID && x.SLOT_ID == model.Id).ToList(); if (ctnList != null) { bcSrcDto.CtnList = ctnList.GroupBy(x => x.CTNALL) .Select(x => { return new TaskBCCTNInfoDto { 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 TaskBCCTNInfoDto { 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.BOOKING_SLOT_TYPE != model.BOOKING_SLOT_TYPE || oldObj.CARRIERID != model.CARRIERID || oldObj.PORTLOADID != model.PORTLOADID || oldObj.PORTDISCHARGEID != model.PORTDISCHARGEID) { isNeedUpdateOldStock = true; } await _repBase.UpdateAsync(model); if (isNeedUpdateOldStock) { //更新库存 await _publisher.PublishAsync(new ChannelEventSource("BookingSlotStock:Update", new BookingSlotStockUpdateModel { BOOKING_SLOT_TYPE = oldObj.BOOKING_SLOT_TYPE, CARRIERID = oldObj.CARRIERID, CONTRACT_NO = oldObj.CONTRACT_NO, VESSEL = oldObj.VESSEL, VOYNO = oldObj.VOYNO, PORTLOADID = oldObj.PORTLOADID, PORTDISCHARGEID = oldObj.PORTDISCHARGEID, TenantId = model.TenantId })); } await _repCtn.DeleteAsync(x => x.SLOT_ID == model.Id); foreach (var ctn in dto.DataObj.CtnList) { var newCtn = ctn.Adapt(); newCtn.SLOT_ID = model.Id; await _repCtn.InsertAsync(newCtn); } await InsLog("Update", model.Id, typeof(BookingSlotBaseApiSaveDto), oldObj, dto.DataObj, nameof(BookingSlotBaseApiSaveDto.CtnList), nameof(BookingSlotBaseApiSaveDto.BookingSlotSaleInfoList)); string batchNo = IDGen.NextID().ToString(); //处理附件 if (file != null) { _logger.LogInformation($"请求文件名:{file.FileName}"); var fileFullPath = await FileAttachHelper.SaveFileDirect(model.Id.ToString(), file.FileBytes, batchNo, file.FileName, "bcmoidfyfiles"); _logger.LogInformation($"保存文件路径:{fileFullPath}"); if (!string.IsNullOrWhiteSpace(fileFullPath)) { //将格式单附件写入订舱的附件 SaveEDIFile(id, fileFullPath, file.FileName, UserManager.TENANT_ID, CONST_BC_MODIFY_FILE_CODE, CONST_BC_MODIFY_FILE_NAME).GetAwaiter(); } } if (modifyFile != null) { _logger.LogInformation($"请求文件名(变更文件):{modifyFile.FileName}"); var fileFullPath = await FileAttachHelper.SaveFileDirect(model.Id.ToString(), modifyFile.FileBytes, batchNo, modifyFile.FileName, "bcmoidfynoticefiles"); _logger.LogInformation($"保存文件路径(变更文件):{fileFullPath}"); if (!string.IsNullOrWhiteSpace(fileFullPath)) { //将格式单附件写入订舱的附件 SaveEDIFile(id, fileFullPath, modifyFile.FileName, UserManager.TENANT_ID, CONST_BC_MODIFY_NOTICE_FILE_CODE, CONST_BC_MODIFY_NOTICE_FILE_NAME).GetAwaiter(); } } //一般更新数据指的是Booking Amendment,需要与舱位进行数据比对 await PushCompareBCInfo(bcSrcDto, bcTargetDto, id, dto.BatchNo); } else if (dto.OpType == "del") { var slotNO = dto.DataObj.SLOT_BOOKING_NO; model = await _repBase.AsQueryable().Filter(null, true).FirstAsync(x => x.IsDeleted == false && x.TenantId == UserManager.TENANT_ID && x.SLOT_BOOKING_NO == slotNO); if (model == null) { throw Oops.Bah($"未找到订舱编号为 {slotNO} 的数据"); } id = model.Id; model.IsDeleted = true; await _repBase.UpdateAsync(model); var ctns = await _repCtn.AsQueryable().Filter(null, true).Where(x => x.IsDeleted == false && x.TenantId == UserManager.TENANT_ID && x.SLOT_ID == model.Id).ToListAsync(); foreach (var ctn in ctns) { ctn.IsDeleted = true; await _repCtn.UpdateAsync(ctn); } await InsLog("Del", model.Id, "取消舱位"); } else if (dto.OpType == "cancellation") { // 更新标志 var slotNO = dto.DataObj.SLOT_BOOKING_NO; model = await _repBase.AsQueryable().Filter(null, true).FirstAsync(x => x.IsDeleted == false && x.TenantId == UserManager.TENANT_ID && x.SLOT_BOOKING_NO == slotNO); if (model == null) { throw Oops.Bah($"未找到订舱编号为 {slotNO} 的数据"); } id = model.Id; model.IS_CANCELLATION = true; model.CANCELLATION_DATE = DateTime.Now; await _repBase.UpdateAsync(model); // 删除该舱位相关的订舱关联关系 var slotList = await _repAllocation.AsQueryable().Where(a => a.BOOKING_SLOT_ID == id).ToListAsync(); var slotIdList = slotList.Select(s => s.Id); await _repAllocation.Context.Updateable() .SetColumns(a => a.IsDeleted == true) .SetColumns(a => a.UpdatedTime == DateTime.Now) .SetColumns(a => a.UpdatedUserId == UserManager.UserId) .SetColumns(a => a.UpdatedUserName == UserManager.Name) .Where(a => slotIdList.Contains(a.Id)) .ExecuteCommandAsync(); await _repAllocationCtn.Context.Updateable() .SetColumns(a => a.IsDeleted == true) .SetColumns(a => a.UpdatedTime == DateTime.Now) .SetColumns(a => a.UpdatedUserId == UserManager.UserId) .SetColumns(a => a.UpdatedUserName == UserManager.Name) .Where(a => slotIdList.Contains(a.SLOT_ALLOC_ID)) .ExecuteCommandAsync(); await InsLog("Cancellation", model.Id, "取消舱位"); } //更新库存 await _publisher.PublishAsync(new ChannelEventSource("BookingSlotStock:Update", new BookingSlotStockUpdateModel { BOOKING_SLOT_TYPE = model.BOOKING_SLOT_TYPE, CARRIERID = model.CARRIERID, CONTRACT_NO = model.CONTRACT_NO, VESSEL = model.VESSEL, VOYNO = model.VOYNO, PORTLOADID = model.PORTLOADID, PORTDISCHARGEID = model.PORTDISCHARGEID, TenantId = model.TenantId })); } else { throw Oops.Bah("操作类型参数有误"); } return id; } /// /// 插入日志(仅显示一条文本信息) /// /// /// /// /// [NonAction] private async Task InsLog(string type, long slotId, string status) { var bid = await _repBookingLog.InsertReturnSnowflakeIdAsync(new BookingLog { Type = type, BookingId = slotId, TenantId = Convert.ToInt64(UserManager.TENANT_ID), CreatedTime = DateTime.Now, CreatedUserId = UserManager.UserId, CreatedUserName = UserManager.Name, Module = "Slot" }); if (!string.IsNullOrEmpty(status)) { await _repBookingLogDetail.InsertReturnSnowflakeIdAsync(new BookingLogDetail { PId = bid, Field = "", OldValue = "", NewValue = status, }); } } /// /// 插入日志(比对修改内容) /// /// /// /// /// /// /// /// [NonAction] private async Task InsLog(string type, long id, Type objType, object objOld, object objNew, params string[] excepProp) { var bid = await _repBookingLog.InsertReturnSnowflakeIdAsync(new BookingLog { Type = type, BookingId = id, TenantId = Convert.ToInt64(UserManager.TENANT_ID), CreatedTime = DateTime.Now, CreatedUserId = UserManager.UserId, CreatedUserName = UserManager.Name, Module = "Slot" }); List waitInsertLogList = new(); foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(objType)) { if (excepProp.Contains(descriptor.Name)) { continue; } var oldValue = descriptor.GetValue(objOld)?.ToString(); var newValue = descriptor.GetValue(objNew)?.ToString(); if (oldValue != newValue) { var fieldName = descriptor.Description; if (string.IsNullOrWhiteSpace(fieldName)) { fieldName = descriptor.Name; } waitInsertLogList.Add(new BookingLogDetail { PId = bid, Field = fieldName, OldValue = oldValue switch { "True" => "是", "False" => "否", _ => oldValue }, NewValue = newValue switch { "True" => "是", "False" => "否", _ => newValue }, }); } } if (waitInsertLogList.Any()) { await _repBookingLogDetail.InsertAsync(waitInsertLogList); } } #endregion #region 库存 /// /// 库存查询 /// /// /// [HttpPost("/BookingSlot/pageStock")] public async Task PageStock(BookingSlotStockPageInput input) { var entities = await _repStock.AsQueryable() .WhereIF(!string.IsNullOrEmpty(input.VESSEL), u => u.VESSEL.Contains(input.VESSEL)) .WhereIF(!string.IsNullOrEmpty(input.VOYNO), u => u.VOYNO.Contains(input.VOYNO)) .WhereIF(!string.IsNullOrEmpty(input.CARRIER), u => u.CARRIER.Contains(input.CARRIER)) .WhereIF(!string.IsNullOrEmpty(input.CTN_STAT), u => u.CTN_STAT.Contains(input.CTN_STAT)) .WhereIF(!string.IsNullOrEmpty(input.PORTLOADID), u => u.PORTLOADID == input.PORTLOADID) .WhereIF(!string.IsNullOrEmpty(input.PORTDISCHARGEID), u => u.PORTDISCHARGEID == input.PORTDISCHARGEID) .WhereIF(!string.IsNullOrEmpty(input.PORTDISCHARGE_COUNTRY_CODE), u => u.PORTDISCHARGE_COUNTRY_CODE == input.PORTDISCHARGE_COUNTRY_CODE) .WhereIF(input.ETD_START.HasValue, u => u.ETD >= input.ETD_START.Value) .WhereIF(input.ETD_END.HasValue, u => u.ETD < input.ETD_END.Value.AddDays(1)) .WhereIF(input.ETA_START.HasValue, u => u.ETA >= input.ETA_START.Value) .WhereIF(input.ETA_END.HasValue, u => u.ETA < input.ETA_END.Value.AddDays(1)) .ToPagedListAsync(input.PageNo, input.PageSize); var result = entities.Adapt>(); return result.XnPagedResult(); } /// /// 刷新库存统计 /// /// [HttpPost("/BookingSlot/refreshStock")] public async Task RefreshStock(BookingSlotStockUpdateModel input) { input.TenantId = UserManager.TENANT_ID; //更新库存 await _publisher.PublishAsync(new ChannelEventSource("BookingSlotStock:Update", input)); } /// /// 重新计算某租户下面所有的库存 /// /// [HttpPost("/BookingSlot/refreshAllStock")] public async Task RefreshAllStock([FromQuery] long telentId) { var n = await _repStock.DeleteAsync(x => x.TenantId == telentId); var logger = App.GetService>(); BookingSlotStockSubscriber subscriber = new BookingSlotStockSubscriber(logger); var group = await _repBase.AsQueryable().Filter(null, true) .Where(x => x.TenantId == telentId && x.IsDeleted == false) .GroupBy(x => new { x.VESSEL, x.VOYNO, x.CARRIERID, x.CONTRACT_NO, x.BOOKING_SLOT_TYPE, x.PORTLOADID, x.PORTDISCHARGEID, x.TenantId }).Select(x => new { x.VESSEL, x.VOYNO, x.CARRIERID, x.CONTRACT_NO, x.BOOKING_SLOT_TYPE, x.PORTLOADID, x.PORTDISCHARGEID, x.TenantId }).ToListAsync(); foreach (var item in group) { BookingSlotStockUpdateModel model = item.Adapt(); await subscriber.BookingSlotStock(model); } } /// /// 根据某租户下面所有收货地、交货地、计算出装货港、卸货港 /// [HttpPost("/BookingSlot/refreshAllPort")] public async Task RefreshAllPort([FromQuery] long? telentId, [FromQuery] int? type) { var cachePort = await _cache.GetAllCodePort(); var cachePortLoad = await _cache.GetAllCodePortLoad(); var list = await _repBase.AsQueryable().Filter(null, true) .Where(x => x.IsDeleted == false && x.IS_CANCELLATION == false) .WhereIF(telentId != null, x => x.TenantId == telentId) .WhereIF(type == 1, x => !string.IsNullOrEmpty(x.PLACERECEIPT) && (string.IsNullOrEmpty(x.PORTLOADID) || string.IsNullOrEmpty(x.PORTLOAD))) .WhereIF(type == 2, x => !string.IsNullOrEmpty(x.PLACEDELIVERY) && (string.IsNullOrEmpty(x.PORTDISCHARGEID) || string.IsNullOrEmpty(x.PORTDISCHARGE))) .WhereIF(type == null, x => (!string.IsNullOrEmpty(x.PLACERECEIPT) || !string.IsNullOrEmpty(x.PLACEDELIVERY)) && (string.IsNullOrEmpty(x.PORTDISCHARGE) || string.IsNullOrEmpty(x.PORTDISCHARGEID) || string.IsNullOrEmpty(x.PORTLOAD) || string.IsNullOrEmpty(x.PORTLOADID))) .Select(x => new BookingSlotBase() { Id = x.Id, VESSEL = x.VESSEL, VOYNO = x.VOYNO, CARRIERID = x.CARRIERID, CONTRACT_NO = x.CONTRACT_NO, BOOKING_SLOT_TYPE = x.BOOKING_SLOT_TYPE, SLOT_BOOKING_NO = x.SLOT_BOOKING_NO, PLACERECEIPT = x.PLACERECEIPT, PLACEDELIVERY = x.PLACEDELIVERY, PORTLOADID = x.PORTLOADID, PORTDISCHARGEID = x.PORTDISCHARGEID, PORTLOAD = x.PORTLOAD, PORTDISCHARGE = x.PORTDISCHARGE, PORTDISCHARGE_COUNTRY_CODE = x.PORTDISCHARGE_COUNTRY_CODE, PORTDISCHARGE_COUNTRY = x.PORTDISCHARGE_COUNTRY, TenantId = x.TenantId }) .ToListAsync(); HashSet updateIdSet = new HashSet(); foreach (var item in list) { // 解析收货地,得到装货港名称及五字码 if (!string.IsNullOrEmpty(item.PLACERECEIPT)) { var portEnName = item.PLACERECEIPT.Split(',')[0]?.Trim(); if (!string.IsNullOrWhiteSpace(portEnName) && (string.IsNullOrEmpty(item.PORTLOADID) || string.IsNullOrEmpty(item.PORTLOAD))) { var portInfo = await PlaceToPortHelper.PlaceReceiptToPortload(portEnName, cachePortLoad, () => _cache.GetAllMappingPortLoad()); if (portInfo != null) { //if (string.IsNullOrEmpty(item.PORTLOADID)) { updateIdSet.Add(item.Id); item.PORTLOADID = portInfo.EdiCode; } //if (string.IsNullOrEmpty(item.PORTLOAD)) { //updateIdSet.Add(item.Id); item.PORTLOAD = portInfo.EnName; } } else { _logger.LogInformation("通过收货地城市名称未匹配到港口信息,订舱编号:{SLOT_BOOKING_NO}", item.SLOT_BOOKING_NO); } } } // 解析交货地,得到为卸货港名称及五字码, 以及国家信息 if (!string.IsNullOrEmpty(item.PLACEDELIVERY)) { var portEnName2 = item.PLACEDELIVERY.Split(',')[0]?.Trim(); if (!string.IsNullOrWhiteSpace(portEnName2)) { var portInfo = await PlaceToPortHelper.PlaceDeliveryToPort(portEnName2, cachePort, () => _cache.GetAllMappingPort()); if (portInfo != null && (string.IsNullOrEmpty(item.PORTDISCHARGEID) || string.IsNullOrEmpty(item.PORTDISCHARGE) || string.IsNullOrEmpty(item.PORTDISCHARGE_COUNTRY_CODE) || string.IsNullOrEmpty(item.PORTDISCHARGE_COUNTRY))) { //if (string.IsNullOrEmpty(item.PORTDISCHARGEID)) { updateIdSet.Add(item.Id); item.PORTDISCHARGEID = portInfo.EdiCode; } //if (string.IsNullOrEmpty(item.PORTDISCHARGE)) { //updateIdSet.Add(item.Id); item.PORTDISCHARGE = portInfo.EnName; } //if (string.IsNullOrEmpty(item.PORTDISCHARGE_COUNTRY_CODE)) { //updateIdSet.Add(item.Id); item.PORTDISCHARGE_COUNTRY_CODE = portInfo.CountryCode; } //if (string.IsNullOrEmpty(item.PORTDISCHARGE_COUNTRY)) { //updateIdSet.Add(item.Id); item.PORTDISCHARGE_COUNTRY = portInfo.Country; } } else { _logger.LogInformation("通过交货地城市名称未匹配到港口信息,订舱编号:{SLOT_BOOKING_NO}", item.SLOT_BOOKING_NO); } } } } if (updateIdSet.Count > 0) { var updateList = list.Where(x => updateIdSet.Contains(x.Id)).ToList(); await _repBase.AsUpdateable(updateList) .UpdateColumnsIF(type == 1, x => new { x.PORTLOADID, x.PORTLOAD, }) .UpdateColumnsIF(type == 2, x => new { x.PORTDISCHARGEID, x.PORTDISCHARGE, x.PORTDISCHARGE_COUNTRY_CODE, x.PORTDISCHARGE_COUNTRY, }) .UpdateColumnsIF(type == null, x => new { x.PORTLOADID, x.PORTDISCHARGEID, x.PORTLOAD, x.PORTDISCHARGE, x.PORTDISCHARGE_COUNTRY_CODE, x.PORTDISCHARGE_COUNTRY, }).ExecuteCommandAsync(); var group = updateList.Where(x => !string.IsNullOrEmpty(x.VESSEL) && !string.IsNullOrEmpty(x.VOYNO) && !string.IsNullOrEmpty(x.CONTRACT_NO) && !string.IsNullOrEmpty(x.BOOKING_SLOT_TYPE) && !string.IsNullOrEmpty(x.CARRIERID) && !string.IsNullOrEmpty(x.PORTLOADID) && !string.IsNullOrEmpty(x.PORTDISCHARGEID)) .GroupBy(x => new { x.VESSEL, x.VOYNO, x.CARRIERID, x.BOOKING_SLOT_TYPE, x.PORTDISCHARGEID, x.PORTLOADID, x.CONTRACT_NO, x.TenantId }).ToList(); _ = Task.Run(async () => { foreach (var item in group) { await Task.Delay(1000); await _publisher.PublishAsync(new ChannelEventSource("BookingSlotStock:Update", new BookingSlotStockUpdateModel { BOOKING_SLOT_TYPE = item.Key.BOOKING_SLOT_TYPE, CARRIERID = item.Key.CARRIERID, CONTRACT_NO = item.Key.CONTRACT_NO, VESSEL = item.Key.VESSEL, VOYNO = item.Key.VOYNO, PORTLOADID = item.Key.PORTLOADID, PORTDISCHARGEID = item.Key.PORTDISCHARGEID, TenantId = item.Key.TenantId })); } }); } } #endregion #region 舱位引入 /// /// 分页查询可用的舱位及箱子列表 /// /// 可选:舱位查询条件 /// 可选:分页信息 [HttpGet("/BookingSlot/getAvailableSlots")] public async Task> GetAvailableSlots([FromQuery] BookingSlotBaseDto input, [FromQuery] PageWithTotal pageInfo) { var result = await GetAvailableSlots(input, null, pageInfo); SqlSugarPagedList pageResult = new() { PageIndex = pageInfo.PageNo, PageSize = pageInfo.PageSize, TotalCount = pageInfo.Total, Items = result, }; return pageResult; } /// /// 查询可用的舱位及箱子列表 /// /// 筛选条件1:舱位信息、箱型 /// 筛选条件2:舱位主键列表 /// 筛选条件3:分页 /// 可用的舱位列表(含可用的箱子列表) [NonAction] public async Task> GetAvailableSlots(BookingSlotBaseDto slotInput = null, List slotIdListInput = null, PageWithTotal pageInfo = null) { slotInput ??= new(); slotIdListInput ??= new(); string[] ctnCodeList = null; if (!string.IsNullOrEmpty(slotInput.CTN_STAT)) { ctnCodeList = slotInput.CTN_STAT.Split(','); } // 1. 【舱位基础表】与【箱子表】做关联,并根据【舱位主键】、【箱型】做分组,统计出【总的箱量】,作为queryable1 var queryable1 = _repBase.Context.Queryable((bas, ctn) => bas.Id == ctn.SLOT_ID) .Where(bas => bas.IS_CANCELLATION == false) .WhereIF(!string.IsNullOrEmpty(slotInput.SLOT_BOOKING_NO), bas => bas.SLOT_BOOKING_NO == slotInput.SLOT_BOOKING_NO) .WhereIF(!string.IsNullOrEmpty(slotInput.PORTLOAD), bas => bas.PORTLOAD.Contains(slotInput.PORTLOAD)) .WhereIF(!string.IsNullOrEmpty(slotInput.PORTDISCHARGE), bas => bas.PORTLOAD.Contains(slotInput.PORTLOAD)) .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.CARRIAGE_TYPE), bas => bas.CARRIAGE_TYPE == slotInput.CARRIAGE_TYPE) .WhereIF(!string.IsNullOrEmpty(slotInput.BOOKING_SLOT_TYPE), bas => bas.BOOKING_SLOT_TYPE == slotInput.BOOKING_SLOT_TYPE) .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 = _repBase.Context.Queryable((alc, ctn) => alc.Id == ctn.SLOT_ALLOC_ID) .WhereIF(slotIdListInput.Any(), (alc, ctn) => slotIdListInput.Contains(alc.BOOKING_SLOT_ID)) .GroupBy((alc, ctn) => new { alc.BOOKING_SLOT_ID, ctn.CTNCODE }) .Select((alc, ctn) => new { id = alc.BOOKING_SLOT_ID, 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(),得到可用的【舱位主键】、【箱型】、【箱量】列表 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 _repBase.AsQueryable() .Where(u => baseIdList.Contains(u.Id)) .ToListAsync(); List ctnCodeCache = await _cache.GetAllCodeCtn(); // 构建结果 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, CTNALL = ctnCodeCache.FirstOrDefault(e => e.Code == c.ctnCode)?.Name ?? throw new Exception($"舱位信息中存在未收录的箱型:{c.ctnCode},需要在箱型字典中补充"), }).ToList(); } } return result; } /// /// 查询指定舱位可用的箱子列表 /// /// 舱位主键 /// 可用的箱子列表 [HttpGet("/BookingSlot/getAvailableCtnsBySlot")] public async Task> GetAvailableCtnsBySlot(long slotId) { if (await _repBase.IsExistsAsync(x => x.Id == slotId && x.IS_CANCELLATION == false) == false) { throw Oops.Oh($"获取舱位失败,舱位不存在或已作废"); } // 1. 【舱位基础表】与【箱子表】做关联,并根据【舱位主键】、【箱型】做分组,统计出【总的箱量】,作为queryable1 var queryable1 = _repBase.Context.Queryable((bas, ctn) => bas.Id == ctn.SLOT_ID) .Where(bas => bas.IS_CANCELLATION == 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 = _repBase.Context.Queryable((alc, ctn) => alc.Id == ctn.SLOT_ALLOC_ID) .Where((alc, ctn) => alc.BOOKING_SLOT_ID == slotId) .GroupBy((alc, ctn) => new { alc.BOOKING_SLOT_ID, ctn.CTNCODE }) .Select((alc, ctn) => new { id = alc.BOOKING_SLOT_ID, 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 = await _cache.GetAllCodeCtn(); List result = canUselist.Select(c => new BookingSlotCtnDto() { CTNCODE = c.ctnCode, CTNNUM = c.numResidue, CTNALL = ctnCodeCache.FirstOrDefault(e => e.Code == c.ctnCode)?.Name ?? throw new Exception($"舱位信息中存在未收录的箱型:{c.ctnCode},需要在箱型字典中补充"), }).ToList(); return result; } /// /// 检查指定订舱记录,是否可以引入舱位列表 /// /// 待引入的舱位列表 /// 待关联的订舱记录 /// isExists:指定订舱记录是否已经引入过舱位数据,isEnough:现有舱位及箱子是否满足需求,message:提示信息 [NonAction] public async Task<(bool isExists, bool isEnough, string message)> CheckImportSlots(List slots, long bookingOrderId) { slots ??= new List(); // 判断是否已存在引用关系 if (bookingOrderId != 0 && await _repAllocation.IsExistsAsync(a => a.BOOKING_ID == bookingOrderId)) { return (true, false, $"订舱主键{bookingOrderId}已引用舱位"); } var slotIdList = slots.Select(s => s.Id).ToList(); // 查询可用舱位及箱子列表 var latestSlotList = await GetAvailableSlots(null, slotIdList); // 判断余量是否满足需求 foreach (var inSlotItem in slots) { var latestSlot = latestSlotList.FirstOrDefault(b => b.Id == inSlotItem.Id); if (latestSlot == null) { return (false, false, $"订舱编号为{inSlotItem.SLOT_BOOKING_NO}的舱位已被占用或取消,请重新引入"); } if (inSlotItem.CtnList?.Any() == false) { return (false, false, $"每个舱位至少选择一个箱子,订舱编号:{inSlotItem.SLOT_BOOKING_NO}"); } foreach (var inCtnItem in inSlotItem.CtnList) { var latestCtn = latestSlot.CtnList.FirstOrDefault(c => c.CTNCODE == inCtnItem.CTNCODE); if (latestCtn == null) { return (false, false, $"订舱编号为{latestSlot.SLOT_BOOKING_NO}的舱位中,箱型为{inCtnItem.CTNALL}的箱子已被占用或取消,请重新引入"); } if (latestCtn.CTNNUM < inCtnItem.CTNNUM) { return (false, false, $"订舱编号为{latestSlot.SLOT_BOOKING_NO}的舱位中,箱型为{inCtnItem.CTNALL}的箱子当前剩余{latestCtn.CTNNUM}个,少于所需的{inCtnItem.CTNNUM}个,请重新引入"); } } } return (false, true, $"可以引入"); } public static object ImportLockObj = new object(); /// /// 为指定订舱记录引入舱位信息 /// /// 待引入的舱位列表 /// 待关联的订舱记录 /// 是否进行剩余量检查 /// 额外的用于生成管理记录的信息 /// isSuccess:检查(余量及已引用检查)是否成功通过,message:提示信息 [NonAction] public async Task<(bool isSuccess, string message)> ImportSlots(List slots, long bookingOrderId, bool isCheck, BookingGenerateDto generateModel = null) { slots ??= new List(); Monitor.Enter(ImportLockObj); try { if (isCheck) { (bool isExists, bool isEnough, string message) checkResult = await CheckImportSlots(slots, bookingOrderId); if (checkResult.isExists || !checkResult.isEnough) return (false, checkResult.message); } var slotIdList = slots.Select(s => s.Id).ToList(); List latestSlotList = await _repBase.AsQueryable().Where(b => slotIdList.Contains(b.Id)).ToListAsync(); foreach (var inSlotItem in slots) { var latestSlot = latestSlotList.First(b => b.Id == inSlotItem.Id); // 保存关联信息 var config = new TypeAdapterConfig(); config.ForType() .Ignore(dest => dest.CreatedTime) .Ignore(dest => dest.UpdatedTime) .Ignore(dest => dest.CreatedUserId) .Ignore(dest => dest.UpdatedUserId) .Ignore(dest => dest.CreatedUserName) .Ignore(dest => dest.UpdatedUserName) .Ignore(dest => dest.TenantId) .Ignore(dest => dest.TenantName); var newSlotAllocation = latestSlot.Adapt(config); newSlotAllocation.Id = 0; newSlotAllocation.BOOKING_SLOT_ID = latestSlot.Id; newSlotAllocation.BOOKING_ID = bookingOrderId; newSlotAllocation.ALLO_BILL_NO = latestSlot.SLOT_BOOKING_NO; newSlotAllocation.FINAL_BILL_NO = latestSlot.SLOT_BOOKING_NO; if (generateModel != null) { newSlotAllocation.CUSTOMERID = generateModel.CustomerId; newSlotAllocation.CUSTOMERNAME = generateModel.CustomerName; newSlotAllocation.CUSTSERVICEID = generateModel.CustServiceId?.ToString(); newSlotAllocation.CUSTSERVICE = generateModel.CustServiceName; newSlotAllocation.SALEID = generateModel.SaleId?.ToString(); newSlotAllocation.SALE = generateModel.SaleName; newSlotAllocation.DOCID = generateModel.DocId?.ToString(); newSlotAllocation.DOC = generateModel.DocName; newSlotAllocation.OPID = generateModel.OpId?.ToString(); newSlotAllocation.OP = generateModel.OpName; newSlotAllocation.BUSINESS = generateModel.BUSINESS; newSlotAllocation.BUSINESSID = generateModel.BUSINESSID; newSlotAllocation.SALE_TIME = generateModel.SALE_TIME; newSlotAllocation.SHIPPER = generateModel.SHIPPER; newSlotAllocation.GOODSNAME = generateModel.GOODSNAME; newSlotAllocation.SELLING_PRICE = generateModel.SELLING_PRICE; } await _repAllocation.InsertAsync(newSlotAllocation); // 保存关联的箱信息 var insertCtnList = inSlotItem.CtnList.Select(c => new BookingSlotAllocationCtn() { SLOT_ALLOC_ID = newSlotAllocation.Id, CTNCODE = c.CTNCODE, CTNALL = c.CTNALL, CTNNUM = c.CTNNUM }); await _repAllocationCtn.InsertAsync(insertCtnList); // 为订舱保存附件信息 var lastestBcFile = await _bookingfile.Where(x => (x.TypeCode == "bc" || x.TypeCode == "bc_notice") && x.BookingId == latestSlot.Id) .OrderByDescending(x => x.Id) .FirstAsync(); if (lastestBcFile != null) { var file = lastestBcFile.Adapt(); file.Id = 0; file.BookingId = bookingOrderId; await _bookingfile.InsertAsync(file); } // 更新库存 await _publisher.PublishAsync(new ChannelEventSource("BookingSlotStock:Update", new Event.BookingSlotStockUpdateModel { BOOKING_SLOT_TYPE = latestSlot.BOOKING_SLOT_TYPE, CARRIERID = latestSlot.CARRIERID, CONTRACT_NO = latestSlot.CONTRACT_NO, VESSEL = latestSlot.VESSEL, VOYNO = latestSlot.VOYNO, PORTLOADID = latestSlot.PORTLOADID, PORTDISCHARGEID = latestSlot.PORTDISCHARGEID, TenantId = latestSlot.TenantId, })); } } finally { Monitor.Exit(ImportLockObj); } return (true, "引入成功"); } #endregion #region 获取附件 /// /// 获取附件 /// /// 舱位主键 /// 返回附件列表 [HttpGet("/BookingSlot/GetFile")] public async Task> GetFile(long id) { var list = await _bookingfile.AsQueryable().Filter(null, true) .Where(u => u.BookingId == id && u.Moudle == "BookingSlot").ToListAsync(); return list; } #endregion #region 舱位 [HttpPost("/BookingSlot/delete")] public async Task Delete([FromQuery] string Ids) { var idArr = Ids.Split(','); foreach (var idStr in idArr) { var id = Convert.ToInt64(idStr); var slot = await _repBase.FirstOrDefaultAsync(x => x.Id == id); if (slot == null) { throw Oops.Oh("舱位信息不存在"); } await _repBase.UpdateAsync(x => x.Id == id, x => new BookingSlotBase() { IsDeleted = true, UpdatedTime = DateTime.Now, UpdatedUserId = UserManager.UserId, UpdatedUserName = UserManager.Name }); await _repCtn.UpdateAsync(x => x.SLOT_ID == id, x => new BookingSlotCtn() { IsDeleted = true, UpdatedTime = DateTime.Now, UpdatedUserId = UserManager.UserId, UpdatedUserName = UserManager.Name }); await _publisher.PublishAsync(new ChannelEventSource("BookingSlotStock:Update", new BookingSlotStockUpdateModel { BOOKING_SLOT_TYPE = slot.BOOKING_SLOT_TYPE, CARRIERID = slot.CARRIERID, CONTRACT_NO = slot.CONTRACT_NO, VESSEL = slot.VESSEL, VOYNO = slot.VOYNO, PORTLOADID = slot.PORTLOADID, PORTDISCHARGEID = slot.PORTDISCHARGEID, TenantId = UserManager.TENANT_ID })); } } /// /// 分页查询订舱舱位 /// [HttpPost("/BookingSlot/page")] public async Task Page(BookingSlotBasePageInput input) { ISugarQueryable select = null; // 箱型筛选 string[] ctnCodeArr = null; if (!string.IsNullOrEmpty(input.CTN_STAT)) { ctnCodeArr = input.CTN_STAT.Split(','); select = _repBase.AsQueryable().InnerJoin((u, c) => u.Id == c.SLOT_ID); } else { select = _repBase.AsQueryable(); } select = select.WhereIF(!string.IsNullOrEmpty(input.SLOT_BOOKING_NO), u => u.SLOT_BOOKING_NO.Contains(input.SLOT_BOOKING_NO)) .WhereIF(!string.IsNullOrEmpty(input.SLOT_BOOKING_NO), u => u.SLOT_BOOKING_NO.Contains(input.SLOT_BOOKING_NO)) .WhereIF(input.STATUS == 1, u => !u.IS_CANCELLATION) .WhereIF(input.STATUS == 2, u => u.IS_CANCELLATION) .WhereIF(!string.IsNullOrEmpty(input.VESSEL), u => u.VESSEL.Contains(input.VESSEL)) .WhereIF(!string.IsNullOrEmpty(input.VOYNO), u => u.VOYNO.Contains(input.VOYNO)) .WhereIF(!string.IsNullOrEmpty(input.PORTLOAD), u => u.PORTLOAD.Contains(input.PORTLOAD)) .WhereIF(!string.IsNullOrEmpty(input.PORTDISCHARGE), u => u.PORTDISCHARGE.Contains(input.PORTDISCHARGE)) .WhereIF(input.ETD_START.HasValue, u => u.ETD >= input.ETD_START.Value) .WhereIF(input.ETD_END.HasValue, u => u.ETD < input.ETD_END.Value.AddDays(1)) .WhereIF(input.ETA_START.HasValue, u => u.ETA >= input.ETA_START.Value) .WhereIF(input.ETA_END.HasValue, u => u.ETA < input.ETA_END.Value.AddDays(1)) .WhereIF(!string.IsNullOrEmpty(input.CreatedUserName), u => u.CreatedUserName.Contains(input.CreatedUserName)) .WhereIF(!string.IsNullOrEmpty(input.UpdatedUserName), u => u.UpdatedUserName.Contains(input.UpdatedUserName)) .WhereIF(!string.IsNullOrEmpty(input.CARRIER), u => u.CARRIER.Contains(input.CARRIER)) .WhereIF(!string.IsNullOrEmpty(input.CONTRACT_NO), u => u.CONTRACT_NO.Contains(input.CONTRACT_NO)) .WhereIF(!string.IsNullOrEmpty(input.SI_RLT_STAT), u => u.SI_RLT_STAT == input.SI_RLT_STAT) .WhereIF(!string.IsNullOrEmpty(input.VGM_RLT_STAT), u => u.VGM_RLT_STAT == input.VGM_RLT_STAT) .WhereIF(!string.IsNullOrEmpty(input.CARRIAGE_TYPE), u => u.CARRIAGE_TYPE == input.CARRIAGE_TYPE) .WhereIF(!string.IsNullOrEmpty(input.BOOKING_SLOT_TYPE), u => u.BOOKING_SLOT_TYPE == input.BOOKING_SLOT_TYPE) .WhereIF(!string.IsNullOrEmpty(input.LANENAME), u => u.LANENAME.Contains(input.LANENAME)) .WhereIF(input.WEEK_AT != null, u => u.WEEK_AT == input.WEEK_AT) //.WhereIF(isEnableCustomerAuthority, u => SqlFunc.Subqueryable() // .Where(x => x.BOOKING_SLOT_ID == u.Id) // .NotAny() // || SqlFunc.Subqueryable() // .Where(x => x.BOOKING_SLOT_ID == u.Id // && userList.Contains((long)x.CreatedUserId) // || userListStr.Contains(x.OPID) // || userListStr.Contains(x.SALEID) // || userListStr.Contains(x.DOCID) // || userListStr.Contains(x.CUSTSERVICEID)) // .Any()) // 标签筛选 .WhereIF(input.LabelIdArray != null && input.LabelIdArray.Length > 0, u => SqlFunc.Subqueryable() .Where(x => x.BusinessId == u.Id && input.LabelIdArray.Contains(x.LabelId)) .Any()); if (ctnCodeArr != null && ctnCodeArr.Length > 0) { var tempSelect = select as ISugarQueryable; tempSelect.Where((u, c) => ctnCodeArr.Contains(c.CTNCODE)); } //var sql = select.OrderByDescending(u => u.CreatedTime).ToSqlString(); var entities = await select.OrderByDescending(u => u.Id) .ToPagedListAsync(input.PageNo, input.PageSize); var result = entities.Adapt>(); var slotIds = result.Items.Select(x => x.Id); if (slotIds.Any()) { // 查询舱位绑定的销售信息,赋值到舱位对象中 List allocationInfoList = await _repAllocation.AsQueryable() .Where(x => slotIds.Contains(x.BOOKING_SLOT_ID)) .Select(x => new BookingSlotSaleInfoDto { Id = x.Id, BOOKING_ID = x.BOOKING_ID, BOOKING_SLOT_ID = x.BOOKING_SLOT_ID, 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, SALE_TIME = x.SALE_TIME, SHIPPER = x.SHIPPER, GOODSNAME = x.GOODSNAME, SELLING_PRICE = x.SELLING_PRICE, CreatedUserId = x.CreatedUserId }).ToListAsync(); if (allocationInfoList.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) { isEnableCustomerAuthority = false; } else { userListStr = userList.Select(x => x.ToString()).ToList(); } } var saleInfoGroup = allocationInfoList.GroupBy(x => x.BOOKING_SLOT_ID); foreach (var item in saleInfoGroup) { if (isEnableCustomerAuthority) { // 遍历销售信息,如果销售信息中的“销售、操作、单证、客服、创建人”中不在当前登陆人的权限范围,则隐藏客户信息 foreach (BookingSlotSaleInfoDto saleInfoItem in item) { 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 = "--"; } } } var slot = result.Items.FirstOrDefault(x => x.Id == item.Key); if (slot != null) { slot.BookingSlotSaleInfoList = item.ToList(); } } } // 查询舱位绑定的标签信息,赋值到舱位对象中 var labelCacheList = await _bookingLabelService.List(1); var labelAllocationList = await _repLabelAllocation.AsQueryable() .Where(x => slotIds.Contains(x.BusinessId)) .ToListAsync(); if (labelAllocationList.Any()) { var labelInfoGroup = labelAllocationList.GroupBy(x => x.BusinessId); foreach (var item in labelInfoGroup) { var slot = result.Items.FirstOrDefault(x => x.Id == item.Key); if (slot != null) { slot.LabelList = item.Select(x => { var labelCache = labelCacheList.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); } } } } return result.XnPagedResult(); } #endregion #region 异步写入附件表 /// /// 异步写入附件表 /// /// 订舱ID /// 文件路径 /// 文件名 /// 租户ID /// 附件类型代码 /// 附件类型名称 /// 附件模块代码 /// [NonAction] private async Task SaveEDIFile(long boookId, string FilePath, string fileName, long tenantId, string fileTypeCode = "bc", string fileTypeName = "Booking Confirmation", string moudle = "BookingSlot") { /* 直接将附件信息写入附件表 */ //EDI文件 var bookFile = new BookingFile { Id = YitIdHelper.NextId(), FileName = fileName, FilePath = FilePath, TypeCode = fileTypeCode, TypeName = fileTypeName, BookingId = boookId, TenantId = tenantId, Moudle = moudle }; await _bookingFileRepository.InsertAsync(bookFile); } #endregion #region 推送BC变更比对 /// /// 推送BC变更比对 /// /// 原舱位详情 /// 变更后舱位详情 /// 舱位主键 /// 请求批次号用来区分对应的哪个批次任务 /// [NonAction] public async Task PushCompareBCInfo(TaskBCInfoDto bcSrcDto, TaskBCInfoDto bcTargetDto, long slotId, string reqBatchNo) { string batchNo = IDGen.NextID().ToString(); DateTime bDate = DateTime.Now; _logger.LogInformation($"批次={batchNo} slotId={slotId} 开始请求比对结果"); var compareResult = await ExcuteCompare(bcSrcDto, bcTargetDto); _logger.LogInformation($"批次={batchNo} slotId={slotId} 请求比对结果完成,结果={JSON.Serialize(compareResult)}"); DateTime eDate = DateTime.Now; TimeSpan ts = eDate.Subtract(bDate); var timeDiff = ts.TotalMilliseconds; if (compareResult != null) { _logger.LogInformation("批次={no} 请求完成,耗时:{timeDiff}ms. 结果{msg}", batchNo, timeDiff, compareResult.succ ? "成功" : "失败"); } if (compareResult != null) { DateTime nowDate = DateTime.Now; var hisInfo = _bookingSlotCompareRepository.AsQueryable().Filter(null, true).First(a => a.COMPARE_BATCHNO == reqBatchNo); if (hisInfo == null) { BookingSlotCompare entity = new BookingSlotCompare { SLOT_ID = slotId, COMPARE_BATCHNO = reqBatchNo, COMPARE_DIFF_NUM = compareResult.extra.IsExistsDiff ? compareResult.extra.ShowDetailList.Count : 0, CreatedTime = nowDate, UpdatedTime = nowDate, CreatedUserId = UserManager.UserId, CreatedUserName = UserManager.Name, UpdatedUserId = UserManager.UserId, UpdatedUserName = UserManager.Name, COMPARE_TYPE = "BC_MODIFY", COMPARE_RLT = JSON.Serialize(compareResult.extra.ShowDetailList), }; await _bookingSlotCompareRepository.InsertAsync(entity); } else { hisInfo.COMPARE_DIFF_NUM = compareResult.extra.IsExistsDiff ? compareResult.extra.ShowDetailList.Count : 0; hisInfo.UpdatedTime = nowDate; hisInfo.UpdatedUserId = UserManager.UserId; hisInfo.UpdatedUserName = UserManager.Name; hisInfo.COMPARE_RLT = JSON.Serialize(compareResult.extra.ShowDetailList); await _bookingSlotCompareRepository.AsUpdateable(hisInfo).UpdateColumns(it => new { it.COMPARE_DIFF_NUM, it.COMPARE_RLT, it.UpdatedTime, it.UpdatedUserId, it.UpdatedUserName }).ExecuteCommandAsync(); } //throw Oops.Oh($"舱位主键{slotId}请求BC比对失败,返回为空"); if (compareResult.extra.ShowDetailList == null || compareResult.extra.ShowDetailList.Count == 0) { new EmailNoticeHelper().SendEmailNotice($"MBLNO={bcSrcDto.MBLNo} 与舱位比对差异失败,比对结果为0", $"MBLNO={bcSrcDto.MBLNo} 与舱位比对差异失败,比对结果为0", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList()); } } else { new EmailNoticeHelper().SendEmailNotice($"MBLNO={bcSrcDto.MBLNo} 与舱位比对差异失败,未获取到比对结果", $"MBLNO={bcSrcDto.MBLNo} 与舱位比对差异失败,未获取到比对结果", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList()); } } #endregion #region 请求BC比对 /// /// 请求BC比对 /// /// BC详情 /// BC变更后详情 /// 返回回执 [NonAction] public async Task ExcuteCompare(TaskBCInfoDto bcSrcDto, TaskBCInfoDto bcTargetDto) { TaskManageExcuteResultDto model = null; /* 1、读取配置文件中的规则引擎URL 2、填充请求的类,并生成JSON报文 3、POST请求接口,并记录回执。 4、返回信息。 */ var url = App.Configuration["BCCompareUrl"]; using (var httpClient = new HttpClient()) { try { using (var reduceAttach = new MultipartFormDataContent()) { var dataContent = new ByteArrayContent(Encoding.UTF8.GetBytes(JSON.Serialize(bcSrcDto))); dataContent.Headers.ContentDisposition = new ContentDispositionHeaderValue($"form-data") { Name = "srcJson" }; reduceAttach.Add(dataContent); var dataContent2 = new ByteArrayContent(Encoding.UTF8.GetBytes(JSON.Serialize(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 = JSON.Deserialize(result); _logger.LogInformation($"推送BC比返回结果:{JSON.Serialize(model)}"); } } catch (Exception ex) { _logger.LogInformation("推送BC比对异常,原因:{error}", ex.Message); // throw Oops.Oh($"推送BC比对异常,原因:{ex.Message}"); } } return model; } #endregion #region 获取舱位变更比对结果 /// /// 获取舱位变更比对结果 /// /// 舱位主键 /// 批次号 /// 返回舱位变更比对结果 [HttpGet("/BookingSlot/GetSlotCompareResult")] public async Task> GetSlotCompareResult([FromQuery] long id, [FromQuery] string batchNo) { var compareInfo = await _bookingSlotCompareRepository.AsQueryable().Filter(null, true) .FirstAsync(t => t.SLOT_ID == id && t.COMPARE_BATCHNO == batchNo); if (compareInfo == null) { throw Oops.Oh($"舱位变更比对结果不存在"); } if (string.IsNullOrWhiteSpace(compareInfo.COMPARE_RLT)) { throw Oops.Oh($"获取舱位变更比对结果错误,比对内容不存在"); } if (!string.IsNullOrWhiteSpace(compareInfo.COMPARE_RLT)) { return JSON.Deserialize>(compareInfo.COMPARE_RLT); } return new List(); } #endregion #region 导入导出 /// /// 导入舱位 /// /// 导入舱位文件 /// 返回回执 [HttpPost("/BookingSlot/ImportSlotFromFile")] public async Task ImportSlotFromFile(IFormFile file) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); try { result.succ = false; if (file == null) { result.msg = "附件不能为空"; return result; } FileInfo fileInfo = new FileInfo(file.FileName); if (fileInfo.Extension != ".xlsx") { result.msg = "请上传指定模板文件"; return result; } var tempDic = Path.Combine(App.WebHostEnvironment.WebRootPath, App.GetOptions().Path, 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) { result.msg = "内容为空"; return result; } var rowCount = sheet.LastRowNum; if (rowCount <= 1) { result.msg = "内容为空"; return result; } Dictionary> data = new(rowCount); //List data = new(rowCount); var cacheCarrier = await _cache.GetAllCodeCarrier(); var cachePortLoad = await _cache.GetAllCodePortLoad(); var cachePort = await _cache.GetAllCodePort(); var cacheLane = await _cache.GetAllCodeLane(); var cacheCtnCode = await _cache.GetAllCodeCtn(); 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.Name == value)?.Code; 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.SLOT_BOOKING_NO = value; break; case 1: slot.CARRIERID = value; break; case 2: slot.VESSEL = value; break; case 3: slot.VOYNO = value; break; case 4: slot.CONTRACT_NO = value; break; case 5: slot.BOOKING_SLOT_TYPE_NAME = 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.BOOKING_PARTY = value; break; case 9: slot.PLACERECEIPT = value; break; case 10: slot.PLACEDELIVERY = value; break; case 11: slot.PORTLOADID = value; break; case 12: slot.PORTDISCHARGEID = value; break; case 13: { if (int.TryParse(value, out int temp)) slot.WEEK_AT = temp; break; } case 14: { //if (DateTime.TryParse(value, out DateTime temp)) slot.SI_CUT_DATE = temp; break; slot.SI_CUT_DATE = cell.DateCellValue; break; } case 15: { //if (DateTime.TryParse(value, out DateTime temp)) slot.VGM_SUBMISSION_CUT_DATE = temp; break; slot.VGM_SUBMISSION_CUT_DATE = cell.DateCellValue; break; } case 16: { //if (DateTime.TryParse(value, out DateTime temp)) slot.CY_CUT_DATE = temp; break; slot.CY_CUT_DATE = cell.DateCellValue; break; } case 17: { //if (DateTime.TryParse(value, out DateTime temp)) slot.MANIFEST_CUT_DATE = temp; break; slot.MANIFEST_CUT_DATE = cell.DateCellValue; break; } case 18: { //if (DateTime.TryParse(value, out DateTime temp)) slot.MDGF_CUT_DATE = temp; break; slot.MDGF_CUT_DATE = cell.DateCellValue; break; } case 19: slot.LANENAME = value; break; // case 20: { if (int.TryParse(value, out int temp)) slot.DETENSION_FREE_DAYS = temp; break; } case 21: { //if (DateTime.TryParse(value, out DateTime temp)) slot.CreatedTime = temp; break; slot.CreatedTime = cell.DateCellValue; break; } case 22: { //if (DateTime.TryParse(value, out DateTime temp)) slot.PRICE_CALCULATION_DATE = temp; break; slot.PRICE_CALCULATION_DATE = cell.DateCellValue; break; } default: break; } } // 特殊处理 if (!string.IsNullOrWhiteSpace(slot.CARRIERID)) slot.CARRIER = cacheCarrier.FirstOrDefault(x => x.EnName == slot.CARRIERID)?.CnName; if (!string.IsNullOrWhiteSpace(slot.PORTLOADID)) slot.PORTLOAD = cachePortLoad.FirstOrDefault(x => x.EdiCode == slot.PORTLOADID)?.EnName; if (!string.IsNullOrWhiteSpace(slot.PORTDISCHARGEID)) slot.PORTDISCHARGE = cachePort.FirstOrDefault(x => x.EdiCode == slot.PORTDISCHARGEID)?.EnName; if (!string.IsNullOrWhiteSpace(slot.LANENAME)) slot.LANECODE = cacheLane.FirstOrDefault(x => x.CnName == slot.LANENAME)?.Code; if (!string.IsNullOrWhiteSpace(slot.BOOKING_SLOT_TYPE_NAME)) slot.BOOKING_SLOT_TYPE = slot.BOOKING_SLOT_TYPE_NAME 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.Name == value)?.Code; 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.SLOT_BOOKING_NO).ToList(); var existsNoList = await _repBase.AsQueryable() .Where(x => noList.Contains(x.SLOT_BOOKING_NO)) .Select(x => x.SLOT_BOOKING_NO) .ToListAsync(); List list = new List(); result.ext = list; foreach (var item in data) { if (existsNoList.Contains(item.Key.SLOT_BOOKING_NO)) { list.Add(new { IsSuccess = false, FailReason = "此订舱编号已存在", SlotBookingNo = item.Key.SLOT_BOOKING_NO }); continue; } await _repBase.InsertAsync(item.Key); var id = item.Key.Id; if (item.Value.Any()) { item.Value.ForEach(x => { x.SLOT_ID = id; }); await _repCtn.InsertAsync(item.Value); } list.Add(new { IsSuccess = true, SlotBookingNo = item.Key.SLOT_BOOKING_NO }); } result.succ = true; var group = data.Keys.Where(x => !existsNoList.Contains(x.SLOT_BOOKING_NO) && !string.IsNullOrEmpty(x.VESSEL) && !string.IsNullOrEmpty(x.VOYNO) && !string.IsNullOrEmpty(x.CONTRACT_NO) && !string.IsNullOrEmpty(x.BOOKING_SLOT_TYPE) && !string.IsNullOrEmpty(x.CARRIERID) && !string.IsNullOrEmpty(x.PORTLOADID) && !string.IsNullOrEmpty(x.PORTDISCHARGEID)) .GroupBy(x => new { x.VESSEL, x.VOYNO, x.CARRIERID, x.BOOKING_SLOT_TYPE, x.PORTDISCHARGEID, x.PORTLOADID, x.CONTRACT_NO }).ToList(); foreach (var item in group) { await _publisher.PublishAsync(new ChannelEventSource("BookingSlotStock:Update", new Event.BookingSlotStockUpdateModel { BOOKING_SLOT_TYPE = item.Key.BOOKING_SLOT_TYPE, CARRIERID = item.Key.CARRIERID, CONTRACT_NO = item.Key.CONTRACT_NO, VESSEL = item.Key.VESSEL, VOYNO = item.Key.VOYNO, PORTLOADID = item.Key.PORTLOADID, PORTDISCHARGEID = item.Key.PORTDISCHARGEID, TenantId = UserManager.TENANT_ID })); } } catch (Exception ex) { _logger.LogError($"导入舱位异常,原因:{ex.Message}"); result.succ = false; result.msg = $"导入舱位异常,原因:{ex.Message}"; } return result; } /// /// 导出舱位为Excel /// [HttpPost("/BookingSlot/PrintOrder")] public async Task PrintOrder(BookingSlotBasePageInput input) { ISugarQueryable select = null; // 箱型筛选 string[] ctnCodeArr = null; if (!string.IsNullOrEmpty(input.CTN_STAT)) { ctnCodeArr = input.CTN_STAT.Split(','); select = _repBase.AsQueryable().InnerJoin((u, c) => u.Id == c.SLOT_ID); } else { select = _repBase.AsQueryable(); } select = select.WhereIF(!string.IsNullOrEmpty(input.SLOT_BOOKING_NO), u => u.SLOT_BOOKING_NO.Contains(input.SLOT_BOOKING_NO)) .WhereIF(!string.IsNullOrEmpty(input.SLOT_BOOKING_NO), u => u.SLOT_BOOKING_NO.Contains(input.SLOT_BOOKING_NO)) .WhereIF(input.STATUS == 1, u => !u.IS_CANCELLATION) .WhereIF(input.STATUS == 2, u => u.IS_CANCELLATION) .WhereIF(!string.IsNullOrEmpty(input.VESSEL), u => u.VESSEL.Contains(input.VESSEL)) .WhereIF(!string.IsNullOrEmpty(input.VOYNO), u => u.VOYNO.Contains(input.VOYNO)) .WhereIF(!string.IsNullOrEmpty(input.PORTLOAD), u => u.PORTLOAD.Contains(input.PORTLOAD)) .WhereIF(!string.IsNullOrEmpty(input.PORTDISCHARGE), u => u.PORTDISCHARGE.Contains(input.PORTDISCHARGE)) .WhereIF(input.ETD_START.HasValue, u => u.ETD >= input.ETD_START.Value) .WhereIF(input.ETD_END.HasValue, u => u.ETD < input.ETD_END.Value.AddDays(1)) .WhereIF(input.ETA_START.HasValue, u => u.ETA >= input.ETA_START.Value) .WhereIF(input.ETA_END.HasValue, u => u.ETA < input.ETA_END.Value.AddDays(1)) .WhereIF(!string.IsNullOrEmpty(input.CreatedUserName), u => u.CreatedUserName.Contains(input.CreatedUserName)) .WhereIF(!string.IsNullOrEmpty(input.UpdatedUserName), u => u.UpdatedUserName.Contains(input.UpdatedUserName)) .WhereIF(!string.IsNullOrEmpty(input.CARRIER), u => u.CARRIER.Contains(input.CARRIER)) .WhereIF(!string.IsNullOrEmpty(input.CONTRACT_NO), u => u.CONTRACT_NO.Contains(input.CONTRACT_NO)) .WhereIF(!string.IsNullOrEmpty(input.SI_RLT_STAT), u => u.SI_RLT_STAT == input.SI_RLT_STAT) .WhereIF(!string.IsNullOrEmpty(input.VGM_RLT_STAT), u => u.VGM_RLT_STAT == input.VGM_RLT_STAT) .WhereIF(!string.IsNullOrEmpty(input.CARRIAGE_TYPE), u => u.CARRIAGE_TYPE == input.CARRIAGE_TYPE) .WhereIF(!string.IsNullOrEmpty(input.BOOKING_SLOT_TYPE), u => u.BOOKING_SLOT_TYPE == input.BOOKING_SLOT_TYPE) .WhereIF(!string.IsNullOrEmpty(input.LANENAME), u => u.LANENAME.Contains(input.LANENAME)) .WhereIF(input.WEEK_AT != null, u => u.WEEK_AT == input.WEEK_AT) //.WhereIF(isEnableCustomerAuthority, u => SqlFunc.Subqueryable() // .Where(x => x.BOOKING_SLOT_ID == u.Id) // .NotAny() // || SqlFunc.Subqueryable() // .Where(x => x.BOOKING_SLOT_ID == u.Id // && userList.Contains((long)x.CreatedUserId) // || userListStr.Contains(x.OPID) // || userListStr.Contains(x.SALEID) // || userListStr.Contains(x.DOCID) // || userListStr.Contains(x.CUSTSERVICEID)) // .Any()) // 标签筛选 .WhereIF(input.LabelIdArray != null && input.LabelIdArray.Length > 0, u => SqlFunc.Subqueryable() .Where(x => x.BusinessId == u.Id && input.LabelIdArray.Contains(x.LabelId)) .Any()); if (ctnCodeArr != null && ctnCodeArr.Length > 0) { var tempSelect = select as ISugarQueryable; tempSelect.Where((u, c) => ctnCodeArr.Contains(c.CTNCODE)); } //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 _repAllocation.AsQueryable() .Where(x => slotIds.Contains(x.BOOKING_SLOT_ID)) .Select(x => new BookingSlotSaleInfoDto { Id = x.Id, BOOKING_ID = x.BOOKING_ID, BOOKING_SLOT_ID = x.BOOKING_SLOT_ID, 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, SALE_TIME = x.SALE_TIME, SHIPPER = x.SHIPPER, GOODSNAME = x.GOODSNAME, SELLING_PRICE = x.SELLING_PRICE }).ToListAsync(); if (allocationInfoList.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) { isEnableCustomerAuthority = false; } else { userListStr = userList.Select(x => x.ToString()).ToList(); } } var saleInfoGroup = allocationInfoList.GroupBy(x => x.BOOKING_SLOT_ID); foreach (var item in saleInfoGroup) { if (isEnableCustomerAuthority) { // 遍历销售信息,如果销售信息中的“销售、操作、单证、客服、创建人”中不在当前登陆人的权限范围,则隐藏客户信息 foreach (BookingSlotSaleInfoDto saleInfoItem in item) { 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 = "--"; } } } 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 _repLabelAllocation.AsQueryable() .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.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); } } } } 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 Oops.Bah("舱位导出模板文件不存在"); } 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 (BookingSlotBaseListOutput 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.SLOT_BOOKING_NO, 1 => item.CARRIER, 2 => item.VESSEL, 3 => item.VOYNO, 4 => item.CONTRACT_NO, 5 => item.BOOKING_SLOT_TYPE_NAME, 6 => item.ETD?.ToString(dateTimeStr), 7 => item.ETA?.ToString(dateTimeStr), 8 => item.BOOKING_PARTY, 9 => item.IS_CANCELLATION ? "是" : "", 10 => item.PLACERECEIPT, 11 => item.PLACEDELIVERY, 12 => item.PORTLOAD, 13 => item.PORTDISCHARGE, 14 => item.CTN_STAT, 15 => item.WEEK_AT?.ToString(), 16 => item.SI_CUT_DATE?.ToString(dateTimeStr), 17 => item.VGM_SUBMISSION_CUT_DATE?.ToString(dateTimeStr), 18 => item.CY_CUT_DATE?.ToString(dateTimeStr), 19 => item.MANIFEST_CUT_DATE?.ToString(dateTimeStr), 20 => item.MDGF_CUT_DATE?.ToString(dateTimeStr), 21 => item.LANENAME, 22 => item.DETENSION_FREE_DAYS?.ToString(), 23 => item.CreatedTime?.ToString(dateTimeStr), 24 => item.VGM_RLT_STAT, 25 => item.SI_RLT_STAT, 26 => item.TAKE_CTN_RLT_STAT, 27 => item.RETURN_CTN_RLT_STAT, 28 => item.NOMINATION_RLT_STAT, 29 => item.AMENDMENT_RLT_STAT, 30 => item.CANCELLATION_RLT_STAT, 31 => item.DISCHARGE_FULL_RLT_STAT, 32 => item.GATE_OUTFULL_RLT_STAT, 33 => "", 34 => item.REMARK, 35 => item.PRICE_CALCULATION_DATE?.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.SELLING_PRICE?.ToString(), 42 => saleItem.SALE_TIME?.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(fileFullPath, fileName), FileMode.OpenOrCreate, FileAccess.ReadWrite); excelwork.Write(filestream); await Task.Delay(2000); //return HttpUtility.UrlEncode(fileName, Encoding.GetEncoding("UTF-8")); return fileName; } #endregion #region 生成订舱订单 /// /// 生成订舱订单 /// /// 生成订舱订单请求 /// 返回回执 [HttpPost("/BookingSlot/CreateBookingOrder")] public async Task CreateBookingOrder(BookingGenerateDto model) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); if (model.CustomerId == null || string.IsNullOrWhiteSpace(model.CustomerName)) { throw Oops.Oh($"请选择委托单位"); } if (model.CtnList == null || !model.CtnList.Any()) { throw Oops.Oh($"请选择要使用的箱信息"); } if (!model.SlotId.HasValue || model.SlotId.Value < 1) throw Oops.Oh($"舱位ID不能为空"); var slotInfo = await _repBase.AsQueryable() .FirstAsync(a => a.Id == model.SlotId.Value); if (slotInfo == null) throw Oops.Oh($"舱位信息不存在或已作废"); // 判断是否已存在引用关系 //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 = await _bookingfile.AsQueryable().Filter(null, true) .Where(u => u.BookingId == slotInfo.Id && u.Moudle == "BookingSlot").ToListAsync(); DjyTenantParamValueOutput paramConfig = null; var paramList = _djyTenantParamService.GetParaCodeWithValue(new[] { CONST_CREATE_BOOKING_NEED_CONTACT }).GetAwaiter().GetResult(); if (paramList != null && paramList.Count > 0) { paramConfig = paramList.FirstOrDefault(a => a.ParaCode.Equals(CONST_CREATE_BOOKING_NEED_CONTACT, StringComparison.OrdinalIgnoreCase)); } if (model.CustomerContactList != null && model.CustomerContactList.Count > 0) { //取委托客户下面所有的联系人列表 var djyCustomerInfo = _djyCustomerService.Detail(new GetDjyCustomerInput { Id = model.CustomerId.Value }) .GetAwaiter().GetResult(); if (djyCustomerInfo == null) { _logger.LogInformation($"委托单位{model.CustomerName} 获取失败,委托单位不存在或已作废 SlotId={model.SlotId.Value}"); throw Oops.Oh($"委托单位{model.CustomerName} 获取失败,委托单位不存在或已作废"); } if (djyCustomerInfo.Contacts == null && djyCustomerInfo.Contacts.Count < 1) { _logger.LogInformation($"委托单位{model.CustomerName} 获取相关联系人失败,委托单位相关联系人为空 SlotId={model.SlotId.Value}"); throw Oops.Oh($"委托单位{model.CustomerName} 获取相关联系人失败,委托单位相关联系人为空"); } model.CustomerContactList.ForEach(contact => { DjyCustomerContactOutput djyCustomerContactMan = djyCustomerInfo.Contacts .FirstOrDefault(a => a.Id == contact.CustomerContactId); if (djyCustomerContactMan == null) { _logger.LogInformation($"委托单位{model.CustomerName} 联系人 {contact.CustomerContactName}获取失败,联系人不存在或已作废 SlotId={model.SlotId.Value}"); throw Oops.Oh($"委托单位{model.CustomerName} 联系人 {contact.CustomerContactName}获取失败,联系人不存在或已作废"); } }); } else { if (paramConfig != null && paramConfig.ParaValue.Equals("ENABLE", StringComparison.OrdinalIgnoreCase)) { throw Oops.Oh($"生成订舱时往来单位联系必填,请修改"); } } var bookingOrderId = await GenerateBookingOrder(slotInfo, fileList, model); result.succ = true; result.msg = "成功"; return result; } #endregion #region 生成订舱 /// /// 生成订舱 /// /// 舱位详情 /// 舱位附件列表 /// 订舱请求详情 /// 返回订舱ID private async Task GenerateBookingOrder(BookingSlotBase bookingSlotBase, List bookingSlotFileList, BookingGenerateDto generateModel) { long id = 0; try { /* 1、新增订舱 2、推送服务项目 3、推送附件 */ var carrierInfo = _cache.GetAllCodeCarrier().GetAwaiter().GetResult() .Where(t => t.Code.Equals(bookingSlotBase.CARRIERID, StringComparison.OrdinalIgnoreCase) || t.EnName.Equals(bookingSlotBase.CARRIERID, StringComparison.OrdinalIgnoreCase) || t.CnName.Equals(bookingSlotBase.CARRIERID, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); var custNo = bookingSlotBase.SLOT_BOOKING_NO.Trim(); SaveBookingOrderInput bkModel = new SaveBookingOrderInput { CUSTOMERID = generateModel.CustomerId, CUSTOMERNAME = generateModel.CustomerName, CARRIERID = carrierInfo?.Code?.Trim(), CARRIER = carrierInfo?.CnName?.Trim(), CUSTNO = custNo, //MBLNO = bookingSlotBase.SLOT_BOOKING_NO.Trim(), CONTRACTNO = !string.IsNullOrWhiteSpace(bookingSlotBase.CONTRACT_NO) ? bookingSlotBase.CONTRACT_NO : "", VESSEL = bookingSlotBase.VESSEL?.ToUpper()?.Trim(), VOYNO = bookingSlotBase.VOYNO?.ToUpper()?.Trim(), VOYNOINNER = bookingSlotBase.VOYNO?.ToUpper()?.Trim(), ETD = bookingSlotBase.ETD, ETA = bookingSlotBase.ETA, SALEID = generateModel.SaleId?.ToString(), SALE = generateModel.SaleName, OPID = generateModel.OpId?.ToString(), OP = generateModel.OpName, DOCID = generateModel.DocId?.ToString(), DOC = generateModel.DocName, ROUTEID = generateModel.RouteID?.ToString(), ROUTE = generateModel.Route, CZRemark = generateModel.CZRemark, ShenQingXiangShi = generateModel.ShenQingXiangShi, LineManageID = generateModel.LineManageID?.ToString(), LineName = generateModel.LineManage, CLOSEVGMDATE = bookingSlotBase.VGM_SUBMISSION_CUT_DATE, CLOSINGDATE = bookingSlotBase.CY_CUT_DATE, CLOSEDOCDATE = bookingSlotBase.SI_CUT_DATE, CUSTSERVICEID = generateModel.CustServiceId?.ToString(), CUSTSERVICE = generateModel.CustServiceName, PORTLOAD = bookingSlotBase.PORTLOAD, PORTLOADID = bookingSlotBase.PORTLOADID, PORTDISCHARGE = bookingSlotBase.PORTDISCHARGE, PORTDISCHARGEID = bookingSlotBase.PORTDISCHARGEID, PLACEDELIVERY = bookingSlotBase.PLACEDELIVERY, PLACEDELIVERYID = bookingSlotBase.PLACEDELIVERYID, PLACERECEIPT = bookingSlotBase.PLACERECEIPT, PLACERECEIPTID = bookingSlotBase.PLACERECEIPTID, ctnInputs = new List() }; // 判断是否为拆票的舱位,如果为拆票,提单号需要加上ABCD... var selectNum = generateModel.CtnList.Sum(x => x.CTNNUM); _logger.LogInformation("根据舱位生成订舱,selectNum:{selectNum}", selectNum); var allNum = await _repCtn.AsQueryable().Where(x => x.SLOT_ID == generateModel.SlotId).SumAsync(x => x.CTNNUM); _logger.LogInformation("根据舱位生成订舱,allNum:{allNum}", allNum); bkModel.IsSplit = selectNum != allNum; if (bkModel.IsSplit) { //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(); 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 { bkModel.MBLNO = bookingSlotBase.SLOT_BOOKING_NO.Trim(); } _logger.LogInformation("根据舱位生成订舱,得到MBLNO:{MBLNO}", bkModel.MBLNO); var ctnCodeList = _cache.GetAllCodeCtn().GetAwaiter().GetResult().ToList(); if (generateModel.CtnList != null && generateModel.CtnList.Count > 0) { generateModel.CtnList.ForEach(t => { var ctnCode = ctnCodeList.FirstOrDefault(a => !string.IsNullOrWhiteSpace(a.Name) && a.Name.Equals(t.CTNALL, StringComparison.OrdinalIgnoreCase)); BookingCtnDto ctn = new BookingCtnDto { CTNCODE = ctnCode?.Code, CTNALL = t.CTNALL, CTNNUM = t.CTNNUM }; bkModel.ctnInputs.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 Oops.Oh(checkResult.message); } var bookingOrderService = _namedBookingOrderServiceProvider.GetService(nameof(BookingOrderService)); _logger.LogInformation("根据舱位生成订舱,开始调用Save保存订舱"); var bkRlt = await bookingOrderService.Save(bkModel); _logger.LogInformation("根据舱位生成订舱,调用Save保存订舱完成,id:{id}", bkRlt.Id); id = bkRlt.Id; string batchNo = IDGen.NextID().ToString(); if (id > 0) { ////对应订舱和舱位关系 var allocRlt = await ImportSlots(importSlots, id, false, generateModel); _logger.LogInformation("根据舱位生成订舱,引入订舱关系完成"); //这里如果指定了委托单位的邮件联系人,则推送订舱联系人 if (generateModel.CustomerContactList != null && generateModel.CustomerContactList.Count > 0) { var bookingContactList = _bookingOrderContactRepository.AsQueryable() .Where(a => a.BookingId == id && !a.IsDeleted).ToList(); var djyCustomerInfo = _djyCustomerService.Detail(new GetDjyCustomerInput { Id = generateModel.CustomerId.Value }) .GetAwaiter().GetResult(); generateModel.CustomerContactList.ForEach(contact => { DjyCustomerContactOutput djyCustomerContactMan = null; if (djyCustomerInfo.Contacts != null && djyCustomerInfo.Contacts.Count > 0) { djyCustomerContactMan = djyCustomerInfo.Contacts.FirstOrDefault(a => a.Id == contact.CustomerContactId); } if (djyCustomerContactMan != null) { var bookingContact = bookingContactList .FirstOrDefault(x => x.Email.Equals(djyCustomerContactMan.Email, StringComparison.OrdinalIgnoreCase)); if (bookingContact == null) { bookingContact = new BookingOrderContact { Name = djyCustomerContactMan.Name, BookingId = id, Email = djyCustomerContactMan.Email, Remark = djyCustomerContactMan.Remark, CreatedTime = DateTime.Now, CreatedUserId = UserManager.UserId, CreatedUserName = UserManager.Name }; _bookingOrderContactRepository.Insert(bookingContact); } else { bookingContact.Name = djyCustomerContactMan.Name; bookingContact.Email = djyCustomerContactMan.Email; bookingContact.Remark = djyCustomerContactMan.Remark; bookingContact.UpdatedTime = DateTime.Now; bookingContact.UpdatedUserId = UserManager.UserId; bookingContact.UpdatedUserName = UserManager.Name; _bookingOrderContactRepository.AsUpdateable(bookingContact).UpdateColumns(it => new { it.Name, it.Email, it.Remark, it.UpdatedTime, it.UpdatedUserId, it.UpdatedUserName }).ExecuteCommand(); } } }); } if (generateModel.ProjectList != null && generateModel.ProjectList.Count > 0) { ModifyServiceProjectDto projectDto = new ModifyServiceProjectDto { BookingId = id, ProjectCodes = generateModel.ProjectList.Distinct().ToArray(), }; //写入服务项目 var prjRlt = await _bookingValueAddedService.SaveServiceProject(projectDto); _logger.LogInformation($"推送订舱的服务项目完成 id={id} rlt={JSON.Serialize(prjRlt)}"); } var opt = App.GetOptions(); var dirAbs = opt.basePath; if (string.IsNullOrEmpty(dirAbs)) { dirAbs = App.WebHostEnvironment.WebRootPath; } if (bookingSlotFileList.Any(a => a.TypeCode.Equals("bc", StringComparison.OrdinalIgnoreCase))) { var file = bookingSlotFileList.OrderByDescending(a => a.CreatedTime) .FirstOrDefault(a => a.TypeCode.Equals("bc", StringComparison.OrdinalIgnoreCase)); var fileFullPath = Path.Combine(dirAbs, file.FilePath); if (File.Exists(fileFullPath)) { //如果确认文件读取成功 var bookFilePath = FileAttachHelper.MoveFile(id.ToString(), fileFullPath, batchNo , false, null, true).GetAwaiter().GetResult(); //将格式单附件写入订舱的附件 SaveEDIFile(id, bookFilePath, new System.IO.FileInfo(bookFilePath).Name, file.TenantId.Value, CONST_BC_FILE_CODE, CONST_BC_FILE_NAME).GetAwaiter(); } } if (bookingSlotFileList.Any(a => a.TypeCode.Equals("bc_notice", StringComparison.OrdinalIgnoreCase))) { var file = bookingSlotFileList.OrderByDescending(a => a.CreatedTime) .FirstOrDefault(a => a.TypeCode.Equals("bc_notice", StringComparison.OrdinalIgnoreCase)); var fileFullPath = Path.Combine(dirAbs, file.FilePath); if (File.Exists(fileFullPath)) { //如果确认文件读取成功 var bookFilePath = FileAttachHelper.MoveFile(id.ToString(), fileFullPath, batchNo , false, "bcnoticefile", true).GetAwaiter().GetResult(); //将格式单附件写入订舱的附件 SaveEDIFile(id, bookFilePath, new System.IO.FileInfo(bookFilePath).Name, file.TenantId.Value, CONST_BC_FILE_CODE, CONST_BC_FILE_NAME).GetAwaiter(); } } } _logger.LogInformation($"MBLNO:{bookingSlotBase.SLOT_BOOKING_NO} 生成订舱订单成功 id={id}"); } catch (Exception ex) { _logger.LogError($"MBLNO:{bookingSlotBase.SLOT_BOOKING_NO} 生成订舱订单异常,原因:{ex.Message}"); throw; } return id; } #endregion #region 检索舱位对应的订舱订单(BY 舱位主键) /// /// 检索舱位对应的订舱订单(BY 舱位主键) /// /// /// 返回回执 [HttpGet("/BookingSlot/SearchBookingSlotWithOrderById")] public async Task SearchBookingSlotWithOrderById(long id) { BookingSlotWithOrderDto dto = null; var slotInfo = await _repBase.AsQueryable().FirstAsync(a => a.Id == id); if (slotInfo == null) { _logger.LogInformation($"id={id} 获取舱位失败,舱位不存在或已作废"); return dto; } var list = _repAllocation.AsQueryable().Where(a => a.BOOKING_SLOT_ID == id).ToList(); dto = new BookingSlotWithOrderDto { BookingSlotId = slotInfo.Id, }; if (list.Count > 0) { dto.HasBookingOrder = true; dto.BookingOrderList = list.Select(x => x.Id).ToList(); } return dto; } #endregion #region 检索舱位对应的订舱订单(BY 订舱编号) /// /// 检索舱位对应的订舱订单(BY 订舱编号) /// /// 订舱编号 /// 租户ID /// 返回回执 [HttpGet("/BookingSlot/SearchBookingSlotWithOrderByNo")] public async Task SearchBookingSlotWithOrderByNo(string slotBookingNo, long tenantId) { BookingSlotWithOrderDto dto = null; var slotInfo = await _repBase.AsQueryable().Filter(null, true).FirstAsync(a => a.SLOT_BOOKING_NO == slotBookingNo && a.TenantId == tenantId && a.IsDeleted == false); if (slotInfo == null) { _logger.LogInformation($"slotBookingNo={slotBookingNo} 获取舱位失败,舱位不存在或已作废"); return dto; } var list = _repAllocation.AsQueryable().Filter(null, true).Where(a => a.BOOKING_SLOT_ID == slotInfo.Id && a.TenantId == tenantId && a.IsDeleted == false).ToList(); dto = new BookingSlotWithOrderDto { BookingSlotId = slotInfo.Id, }; if (list.Count > 0) { dto.HasBookingOrder = true; dto.BookingOrderList = list.Select(x => x.BOOKING_ID).ToList(); } return dto; } #endregion #region 估算差异重要提醒 /// /// 估算差异重要提醒 /// /// 原舱位详情 /// 新舱位详情 /// 舱位ID /// [NonAction] public async Task MeasureDiffCautionTask(TaskBCInfoDto bcSrcDto, TaskBCInfoDto bcTargetDto, long slotId) { try { if (bcSrcDto.CarrierId.Equals("MSK", StringComparison.OrdinalIgnoreCase)) { var bookingSlotAllocList = _repBase.AsQueryable().Filter(null, true) .LeftJoin((slot, alloc) => slot.Id == alloc.BOOKING_SLOT_ID) .Where((slot, alloc) => slot.Id == slotId && slot.IsDeleted == false && slot.TenantId == UserManager.TENANT_ID && alloc.IsDeleted == false) .Select((slot, alloc) => new { Slot = slot, Alloc = alloc }).ToList(); List bookingOrderList = new List(); List userList = new List(); List userIds = new List(); if (bookingSlotAllocList.Any(t => t.Alloc != null)) { var bookingNoList = bookingSlotAllocList.Select(a => a.Alloc.BOOKING_ID).ToList(); bookingOrderList = _repBookingOrder.AsQueryable().Filter(null, true) .Where(x => bookingNoList.Contains(x.Id) && x.IsDeleted == false && x.TenantId == UserManager.TENANT_ID).ToList(); if (bookingOrderList.Count > 0) { bookingOrderList.ForEach(bk => { if (!string.IsNullOrWhiteSpace(bk.OPID)) userIds.Add(long.Parse(bk.OPID)); //if (!string.IsNullOrWhiteSpace(bk.SALEID)) // userIds.Add(long.Parse(bk.SALEID)); if (!string.IsNullOrWhiteSpace(bk.CUSTSERVICEID)) userIds.Add(long.Parse(bk.CUSTSERVICEID)); userIds.Add(bk.CreatedUserId.Value); }); } } BookingSlotBase slotInfo = null; if (bookingSlotAllocList.Count > 0) { slotInfo = bookingSlotAllocList.FirstOrDefault().Slot; } else { slotInfo = _repBase.AsQueryable().Filter(null, true).First(t => t.Id == slotId); } if (slotInfo != null) { userIds.Add(slotInfo.CreatedUserId.Value); } userIds = userIds.Distinct().ToList(); userList = _sysUserRepository.AsQueryable().Filter(null, true) .Where(x => userIds.Contains(x.Id) && x.IsDeleted == false && x.TenantId == UserManager.TENANT_ID).ToList(); string srcWeek = bcSrcDto.WeekAt ?? ""; string targetWeek = bcTargetDto.WeekAt ?? ""; if (!srcWeek.Equals(targetWeek, StringComparison.OrdinalIgnoreCase)) { if (bookingSlotAllocList.Count > 0) { bookingSlotAllocList.ForEach(async ca => { var bookingInfo = _repBookingOrder.AsQueryable().Filter(null, true).First(x => x.Id == ca.Alloc.BOOKING_ID && x.IsDeleted == false && x.TenantId == UserManager.TENANT_ID); CreateTask(CautionNoticeTaskEnum.WeekAt, bcSrcDto, userList, bookingInfo, slotInfo, srcWeek, targetWeek, $"提单号:{bcSrcDto.MBLNo} \r\n计费周变更了 \r\n原:{srcWeek} \r\n新:{targetWeek}"); }); } else if (slotInfo != null) { CreateTask(CautionNoticeTaskEnum.WeekAt, bcSrcDto, userList, null, slotInfo, srcWeek, targetWeek, $"提单号:{bcSrcDto.MBLNo} \r\n计费周变更了 \r\n原:{srcWeek} \r\n新:{targetWeek}"); } } string srcPriceDate = bcSrcDto.PriceCalculationDate.HasValue ? bcSrcDto.PriceCalculationDate.Value.ToString("yyyy-MM-dd") : ""; string targetPriceDate = bcTargetDto.PriceCalculationDate.HasValue ? bcTargetDto.PriceCalculationDate.Value.ToString("yyyy-MM-dd") : ""; //如果原始的没有解析计费日期,就不做提醒了 if (!string.IsNullOrWhiteSpace(srcPriceDate) && !srcPriceDate.Equals(targetPriceDate, StringComparison.OrdinalIgnoreCase)) { if (bookingSlotAllocList.Count > 0) { bookingSlotAllocList.ForEach(async ca => { var bookingInfo = _repBookingOrder.AsQueryable().Filter(null, true).First(x => x.Id == ca.Alloc.BOOKING_ID && x.IsDeleted == false && x.TenantId == UserManager.TENANT_ID); CreateTask(CautionNoticeTaskEnum.PriceCalcDate, bcSrcDto, userList, bookingInfo, slotInfo, srcPriceDate, targetPriceDate, $"提单号:{bcSrcDto.MBLNo} \r\n计费日期变更了 \r\n原:{srcPriceDate} \r\n新:{targetPriceDate}"); }); } else if (slotInfo != null) { CreateTask(CautionNoticeTaskEnum.PriceCalcDate, bcSrcDto, userList, null, slotInfo, srcPriceDate, targetPriceDate, $"提单号:{bcSrcDto.MBLNo} \r\n计费日期变更了 \r\n原:{srcPriceDate} \r\n新:{targetPriceDate}"); } } string srcVesselVoyno = $"{bcSrcDto.Vessel}/{bcSrcDto.VoyNo}"; string targetVesselVoyno = $"{bcTargetDto.Vessel}/{bcTargetDto.VoyNo}"; if (!srcVesselVoyno.Equals(targetVesselVoyno, StringComparison.OrdinalIgnoreCase)) { if (bookingSlotAllocList.Count > 0) { bookingSlotAllocList.ForEach(async ca => { var bookingInfo = _repBookingOrder.AsQueryable().Filter(null, true).First(x => x.Id == ca.Alloc.BOOKING_ID && x.IsDeleted == false && x.TenantId == UserManager.TENANT_ID); CreateTask(CautionNoticeTaskEnum.ChangeVesselVoyno, bcSrcDto, userList, bookingInfo, slotInfo, srcVesselVoyno, targetVesselVoyno, $"提单号:{bcSrcDto.MBLNo} \r\n船名航次变更了 \r\n原:{srcVesselVoyno} \r\n新:{targetVesselVoyno}"); }); } else if (slotInfo != null) { CreateTask(CautionNoticeTaskEnum.ChangeVesselVoyno, bcSrcDto, userList, null, slotInfo, srcVesselVoyno, targetVesselVoyno, $"提单号:{bcSrcDto.MBLNo} \r\n船名航次变更了 \r\n原:{srcVesselVoyno} \r\n新:{targetVesselVoyno}"); } } string srcCarrierType = bcSrcDto.CarriageType ?? ""; string targetCarrierType = bcTargetDto.CarriageType ?? ""; //如果原来是直航现在变成了中转需要做重要提醒 if (!srcCarrierType.Equals(targetCarrierType, StringComparison.OrdinalIgnoreCase) && srcCarrierType.Equals("DIRECT_SHIP", StringComparison.OrdinalIgnoreCase) && targetCarrierType.Equals("TRANSFER_SHIP", StringComparison.OrdinalIgnoreCase)) { if (bookingSlotAllocList.Count > 0) { bookingSlotAllocList.ForEach(async ca => { var bookingInfo = _repBookingOrder.AsQueryable().Filter(null, true).First(x => x.Id == ca.Alloc.BOOKING_ID && x.IsDeleted == false && x.TenantId == UserManager.TENANT_ID); CreateTask(CautionNoticeTaskEnum.ChangeTransfer, bcSrcDto, userList, bookingInfo, slotInfo, "直达", "中转", $"提单号:{bcSrcDto.MBLNo} \r\n直达变成中转了"); }); } else if (slotInfo != null) { CreateTask(CautionNoticeTaskEnum.ChangeTransfer, bcSrcDto, userList, null, slotInfo, "直达", "中转", $"提单号:{bcSrcDto.MBLNo} \r\n直达变成中转了"); } } string srcVGMCut = bcSrcDto.VGMSubmissionCutDate; string targeVGMCut = bcTargetDto.VGMSubmissionCutDate; if (!string.IsNullOrWhiteSpace(srcVGMCut) && !string.IsNullOrWhiteSpace(targeVGMCut) && !srcVGMCut.Equals(targeVGMCut)) { DateTime srcVGMCutDate = DateTime.Parse(srcVGMCut); DateTime targeVGMCutDate = DateTime.Parse(targeVGMCut); if (srcVGMCutDate > targeVGMCutDate) { if (bookingSlotAllocList.Count > 0) { bookingSlotAllocList.ForEach(async ca => { var bookingInfo = _repBookingOrder.AsQueryable().Filter(null, true).First(x => x.Id == ca.Alloc.BOOKING_ID && x.IsDeleted == false && x.TenantId == UserManager.TENANT_ID); CreateTask(CautionNoticeTaskEnum.VGMCutDateAdvanced, bcSrcDto, userList, bookingInfo, slotInfo, srcVGMCut, targeVGMCut, $"提单号:{bcSrcDto.MBLNo} \r\nVGM截单时间提前了 \r\n原:{srcVGMCut} \r\n新:{targeVGMCut}"); }); } else if (slotInfo != null) { CreateTask(CautionNoticeTaskEnum.VGMCutDateAdvanced, bcSrcDto, userList, null, slotInfo, srcVGMCut, targeVGMCut, $"提单号:{bcSrcDto.MBLNo} \r\nVGM截单时间提前了 \r\n原:{srcVGMCut} \r\n新:{targeVGMCut}"); } } } string srcSICut = bcSrcDto.SICutDate; string targeSICut = bcTargetDto.SICutDate; if (!string.IsNullOrWhiteSpace(srcSICut) && !string.IsNullOrWhiteSpace(targeSICut) && !srcSICut.Equals(targeSICut)) { DateTime srcSICutDate = DateTime.Parse(srcSICut); DateTime targeSICutDate = DateTime.Parse(targeSICut); //如果新给的SI截止时间,需要推送通知 if (srcSICutDate > targeSICutDate) { if (bookingSlotAllocList.Count > 0) { bookingSlotAllocList.ForEach(async ca => { var bookingInfo = _repBookingOrder.AsQueryable().Filter(null, true).First(x => x.Id == ca.Alloc.BOOKING_ID && x.IsDeleted == false && x.TenantId == UserManager.TENANT_ID); CreateTask(CautionNoticeTaskEnum.SICutDateAdvanced, bcSrcDto, userList, bookingInfo, slotInfo, srcSICut, targeSICut, $"提单号:{bcSrcDto.MBLNo} \r\n样单截止时间提前了 \r\n原:{srcSICut} \r\n新:{targeSICut}"); }); } else if (slotInfo != null) { CreateTask(CautionNoticeTaskEnum.SICutDateAdvanced, bcSrcDto, userList, null, slotInfo, srcSICut, targeSICut, $"提单号:{bcSrcDto.MBLNo} \r\n样单截止时间提前了 \r\n原:{srcSICut} \r\n新:{targeSICut}"); } } } } } catch (Exception ex) { _logger.LogInformation($"提单号:{bcSrcDto.MBLNo} slotId={slotId} 估算差异重要提醒发生异常,原因:{ex.Message}"); new EmailNoticeHelper().SendEmailNotice($"MBLNO={bcSrcDto.MBLNo} slotId={slotId} 估算差异重要提醒发生异常", $"MBLNO={bcSrcDto.MBLNo} slotId={slotId} 估算差异重要提醒发生异常,原因:{ex.Message}", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList()); } } #endregion #region 生成联系人 /// /// 生成联系人 /// /// 联系人列表 /// 已检索用户详情列表 /// 联系用户ID /// 提单号 /// 重要通知类型 private void CreateContact(List notifyList, List userList, long contactId, string mblNo, CautionNoticeTaskEnum cautionNoticeType) { if (!notifyList.Any(p => p.notifyUserId == contactId)) { var userInfo = userList.FirstOrDefault(x => x.Id == contactId); if (userInfo != null) { var contact = new CautionNoticeTaskNoitfyDto { notifyUserId = userInfo.Id, notifyUserName = userInfo.Name, notifyMethod = new CautionNoticeMethodEnum[] { CautionNoticeMethodEnum.Email, CautionNoticeMethodEnum.DingDing }, notifyMobile = userInfo.Phone?.Trim(), notifyEmail = userInfo.Email?.Trim(), }; if (string.IsNullOrWhiteSpace(userInfo.Phone)) { _logger.LogInformation($"MBLNO={mblNo} 比对舱位{cautionNoticeType.ToString()} 有变化,通知人{userInfo.Name}没有手机号"); new EmailNoticeHelper().SendEmailNotice($"MBLNO={mblNo} 比对舱位{cautionNoticeType.ToString()} 有变化,通知人{userInfo.Name}没有手机号", $"MBLNO={mblNo} 比对舱位{cautionNoticeType.ToString()} 有变化,通知人{userInfo.Name}没有手机号", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList()); } if (string.IsNullOrWhiteSpace(userInfo.Email)) { _logger.LogInformation($"MBLNO={mblNo} 比对舱位{cautionNoticeType.ToString()} 有变化,通知人{userInfo.Name}没有邮箱"); new EmailNoticeHelper().SendEmailNotice($"MBLNO={mblNo} 比对舱位{cautionNoticeType.ToString()} 有变化,通知人{userInfo.Name}没有邮箱", $"MBLNO={mblNo} 比对舱位{cautionNoticeType.ToString()} 有变化,通知人{userInfo.Name}没有邮箱", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList()); } notifyList.Add(contact); } } } #endregion private async Task SendTask(CautionNoticeTaskDto dto) { TaskManageOrderMessageInfo messageInfo = new TaskManageOrderMessageInfo { Head = new TaskManageOrderMessageHeadInfo { GID = IDGen.NextID().ToString(), MessageType = "CAUTION_TASK", SenderId = "CautionNoticeTask", SenderName = "重要提醒任务生成", ReceiverId = "TaskManage", ReceiverName = "任务台", RequestDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"), Version = "1.0", RequestAction = "Add" }, Main = new TaskManageOrderMessageMainInfo { TaskType = TaskBaseTypeEnum.CAUTION_NOTICE, } }; messageInfo.Main.TaskTenatId = dto.tenentId; messageInfo.Main.TaskTenatName = dto.tenentName; messageInfo.Main.VesselVoyno = $"{dto.vessel}/{dto.voyno}"; messageInfo.Main.TaskUserId = dto.userId.ToString(); messageInfo.Main.TaskUserName = dto.userName; messageInfo.Main.CarrierId = dto.carrier; messageInfo.Main.CustomerId = dto.customerId; messageInfo.Main.CustomerName = dto.customerName; messageInfo.Main.MBlNo = dto.mblNo; messageInfo.Main.ETD = dto.etd; messageInfo.Main.TaskTitle = $"重要提醒-{dto.cautionNoticeType.GetDescription()} BLNo:{dto.mblNo} {dto.vessel}/{dto.voyno} {(dto.etd.HasValue ? dto.etd.Value.ToString("yyyy-MM-dd") : "")} "; messageInfo.Main.TaskDesp = $"重要提醒-{dto.cautionNoticeType.GetDescription()} BLNo:{dto.mblNo} {dto.vessel}/{dto.voyno} {(dto.etd.HasValue ? dto.etd.Value.ToString("yyyy-MM-dd") : "")}"; messageInfo.Main.CautionNoticeInfo = new TaskManageOrderCautionNoticeInfo { BookingId = dto.bookingId, BookingSlotId = dto.bookingSlotId, CautionNoticeType = dto.cautionNoticeType, Carrier = dto.carrier, MBlNo = dto.mblNo, OrigVal = dto.origVal, NewVal = dto.newVal, NotifyContent = dto.notifyContent, SourceSystem = dto.sourceSystem, SourceBusiType = dto.sourceBusiType, SourceBusiTypeName = dto.sourceBusiTypeName, CreateTime = DateTime.Now, NoticeList = new List() }; if (dto.notifyList != null && dto.notifyList.Count > 0) { dto.notifyList.ForEach(p => { foreach (var item in p.notifyMethod) { var notifyInfo = new TaskManageOrderCautionNoticeDetailInfo { CautionNoticeType = item, Email = p.notifyEmail, UserId = p.notifyUserId, UserName = p.notifyUserName, Mobile = p.notifyMobile, }; messageInfo.Main.CautionNoticeInfo.NoticeList.Add(notifyInfo); } }); } var service = _namedTaskManageServiceProvider.GetService(nameof(TaskManageService)); var rlt = await service.CreateTaskJob(messageInfo); } #region 生成重要提醒任务 /// /// 生成重要提醒任务 /// /// 重要提醒任务类型 /// 原数据详情 /// 提取的用户详情列表 /// 订舱详情 /// 舱位详情 /// 原值 /// 变更值 /// 提示信息 private void CreateTask(CautionNoticeTaskEnum cautionNoticeType, TaskBCInfoDto bcSrcDto, List userList, BookingOrder bookingInfo, BookingSlotBase bookingSlotInfo, string srcVal, string targetVal, string notifyContent) { List userIdList = new List(); if (bookingInfo != null) { if (!string.IsNullOrWhiteSpace(bookingInfo.OPID)) { userIdList.Add(long.Parse(bookingInfo.OPID)); } //if (!string.IsNullOrWhiteSpace(bookingInfo.SALEID)) //{ // userIdList.Add(long.Parse(bookingInfo.SALEID)); //} if (!string.IsNullOrWhiteSpace(bookingInfo.CUSTSERVICEID)) { userIdList.Add(long.Parse(bookingInfo.CUSTSERVICEID)); } userIdList.Add(bookingInfo.CreatedUserId.Value); } if (bookingSlotInfo != null) { userIdList.Add(bookingSlotInfo.CreatedUserId.Value); } userIdList = userIdList.Distinct().ToList(); userIdList.ForEach(x => { var userInfo = userList.FirstOrDefault(p => p.Id == x); var notice = new CautionNoticeTaskDto { cautionNoticeType = cautionNoticeType, createTime = DateTime.Now, origVal = srcVal, newVal = targetVal, tenentId = userInfo.TenantId.Value, userId = userInfo.Id, userName = userInfo.Name, mblNo = bcSrcDto.MBLNo, carrier = bcSrcDto.CarrierId, sourceSystem = "DjyBooking", sourceBusiType = "BookingSlot", sourceBusiTypeName = "舱位管理", vessel = bcSrcDto.Vessel, voyno = bcSrcDto.VoyNo, tenentName = UserManager.TENANT_NAME, notifyContent = notifyContent, notifyList = new List() }; if (!string.IsNullOrWhiteSpace(bcSrcDto.ETD)) { DateTime etd = DateTime.MinValue; if (DateTime.TryParse(bcSrcDto.ETD, out etd)) { notice.etd = etd; } } if (bookingInfo != null) { notice.bookingId = bookingInfo.Id; notice.customerId = bookingInfo.CUSTOMERID; notice.customerName = bookingInfo.CUSTOMERNAME; } if (bookingSlotInfo != null) { notice.bookingSlotId = bookingSlotInfo.Id; } CreateContact(notice.notifyList, userList, x, bcSrcDto.MBLNo, notice.cautionNoticeType); SendTask(notice).GetAwaiter().GetResult(); }); } #endregion #region 订舱编号检索舱位信息 /// /// 订舱编号检索舱位信息 /// /// 订舱编号 /// 船公司ID /// [HttpGet("/BookingSlot/QueryBookingSlot"), AllowAnonymous, ApiUser(ApiCode = "BCTaskManage")] public async Task QueryBookingSlot([FromQuery] string slotBookingNo, [FromQuery] string CarrierId) { long id = 0; try { var model =_repBase.AsQueryable().Filter(null, true) .First(x => x.IsDeleted == false && x.TenantId == UserManager.TENANT_ID && x.SLOT_BOOKING_NO == slotBookingNo && x.CARRIERID == CarrierId); if(model == null) throw Oops.Bah("舱位信息不存在"); id = model.Id; } catch(Exception ex) { _logger.LogInformation($"订舱编号检索舱位信息失败,原因:{ex.Message}"); } return id; } #endregion #region 获取舱位详情 /// /// 获取舱位详情 /// /// 舱位ID /// 返回舱位详情 public async Task GetInfo(long id) { var slotBase = await _repBase.FirstOrDefaultAsync(u => u.Id == id); if (slotBase == null) { throw Oops.Oh("未查询到此舱位信息,可能已被删除,请重新查询后重试"); } var ctns = await _repCtn.Where(x => x.SLOT_ID == id).ToListAsync(); var rtn = slotBase.Adapt(); rtn.CtnList = ctns.Adapt>(); return rtn; } #endregion } public class DynameFileInfo { /// /// 文件名称 /// public string FileName { get; set; } /// /// 文件二进制流 /// public byte[] FileBytes { get; set; } } }