using Amazon.Runtime.Internal.Util; using DS.Module.Core; using DS.Module.Core.Constants; using DS.Module.Core.Data; using DS.Module.Core.Helpers; using DS.Module.DjyServiceStatus; using DS.Module.SqlSugar; using DS.Module.UserModule; using DS.WMS.Core.Code.Dtos; using DS.WMS.Core.Code.Interface; using DS.WMS.Core.Info.Dtos; using DS.WMS.Core.Info.Interface; using DS.WMS.Core.Map.Dtos; using DS.WMS.Core.Map.Interface; using DS.WMS.Core.Op.Dtos; using DS.WMS.Core.Op.Dtos.TaskInteraction; using DS.WMS.Core.Op.EDI; using DS.WMS.Core.Op.Entity; using DS.WMS.Core.Op.Entity.TaskInteraction; using DS.WMS.Core.Op.Interface; using DS.WMS.Core.Op.Interface.TaskInteraction; using DS.WMS.Core.Op.Method.TaskInteraction; using DS.WMS.Core.Sys.Entity; using DS.WMS.Core.Sys.Interface; using DS.WMS.Core.TaskPlat.Dtos; using DS.WMS.Core.TaskPlat.Dtos.BC; using DS.WMS.Core.TaskPlat.Entity; using DS.WMS.Core.TaskPlat.Interface; using HtmlAgilityPack; using Mapster; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using NLog; using SqlSugar; using System.Net.Http.Headers; using System.Text; using System.Text.RegularExpressions; using System.Web; namespace DS.WMS.Core.TaskPlat.Method { /// /// 任务台-BC子任务 /// public class TaskManageBCService : TaskManageBaseService, ITaskManageBCService { // 实例化时构建 private readonly ISqlSugarClient db; private readonly IWebHostEnvironment _environment; private readonly IConfigService _configService; private readonly IClientInfoService _clientInfoService; private readonly IMappingCarrierService _mappingCarrierService; private readonly IOpFileService _opFileService; private readonly ICodeCtnService _codeCtnService; private readonly IUserService _userService; private readonly IUserEmailService _userEmailService; private readonly ICodePortService _codePortService; private readonly IMappingPortService _mappingPortService; private readonly ICodeCountryService _codeCountryService; private readonly string bcCompareUrl; private readonly string bcMSKReadUrl; private readonly string bcMSKModifyFileUrl; private readonly string bcCMAReadUrl; // 按需构建 private Lazy bookingSlotService; private Lazy seaExportService; private Lazy seaExportCommonService; private Lazy djyServiceStatusService; private Lazy taskAllocationService; private Lazy codeVesselService; private Lazy _taskMailService; private Lazy _logService; private static readonly NLog.Logger Logger = LogManager.GetCurrentClassLogger(); private readonly SqlSugarScopeProvider tenantDb; //租户AMENDMENT默认转发KEY const string CONST_AMENDMENT_DEFAULT_PARAM = "AMENDMENT_TASK_AUTO_TRANSMIT"; //自动转发是否默认抄送操作 const string CONST_AUTO_TRANS_EMAIL_OP_CCTO = "AUTO_TRANS_EMAIL_OP_CCTO"; public TaskManageBCService(IUser user, ILogger logger, ISaasDbService saasDbService, IServiceProvider serviceProvider, IWebHostEnvironment environment, IConfigService configService, IClientInfoService clientInfoService, IMappingCarrierService mappingCarrierService, IOpFileService opFileService, ICodeCtnService codeCtnService, IUserService userService, IUserEmailService userEmailService, ICodePortService codePortService, IMappingPortService mappingPortService, ICodeCountryService codeCountryService) : base(user, logger, saasDbService, serviceProvider, environment) { _userService = userService; _userEmailService = userEmailService; _environment = environment; _configService = configService; _clientInfoService = clientInfoService; _mappingCarrierService = mappingCarrierService; _opFileService = opFileService; _codeCtnService = codeCtnService; _codePortService = codePortService; _mappingPortService = mappingPortService; db = serviceProvider.GetRequiredService(); bookingSlotService = new Lazy(serviceProvider.GetRequiredService); seaExportService = new Lazy(serviceProvider.GetRequiredService); djyServiceStatusService = new Lazy(serviceProvider.GetRequiredService); taskAllocationService = new Lazy(serviceProvider.GetRequiredService); seaExportCommonService = new Lazy(serviceProvider.GetRequiredService); codeVesselService = new Lazy(serviceProvider.GetRequiredService); _taskMailService = new Lazy(serviceProvider.GetRequiredService); _logService = new Lazy(serviceProvider.GetRequiredService); tenantDb = saasDbService.GetBizDbScopeById(user.TenantId); tenantDb.QueryFilter.Clear(); bcCompareUrl = AppSetting.app(new string[] { "BCCompare", "Url" }); bcMSKReadUrl = AppSetting.app(new string[] { "BCCompare", "MSKBCReadUrl" }); bcMSKModifyFileUrl = AppSetting.app(new string[] { "BCCompare", "MSKBCModifyFileUrl" }); bcCMAReadUrl = AppSetting.app(new string[] { "BCCompare", "CMABCReadUrl" }); _codeCountryService = codeCountryService; } /// /// 通过任务信息(BC)生成订舱或舱位 /// /// 生成订舱或者舱位请求 /// 返回回执 public async Task> CreateBookingAndSlot(BookingOrSlotGenerateDto model) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); logger.LogInformation($"接收生成订舱或舱位请求,参数{JsonConvert.SerializeObject(model)}"); try { /* 1、GEN_BOOKING_SLOT-生成舱位和订舱 (1)生成舱位 (2)生成海运订舱 (3)更新舱位库存 (4)更新BC任务 2、GEN_BOOKING-只生成订舱 (1)生成海运订舱 (4)更新BC任务 3、GEN_SLOT-只生成舱位 (1)生成舱位 (4)更新BC任务 3、GEN_SLOT-只生成舱位 (1)查找订舱记录 (3)更新舱位库存 (4)更新BC任务 */ if (model.BCTaskId == 0) throw new Exception($"BC任务主键不能为空"); //生成方式(GEN_BOOKING_SLOT-生成舱位和订舱;GEN_BOOKING-只生成订舱;GEN_SLOT-只生成舱位;GEN_EXIST_BOOKING-匹配指定的订舱) if (string.IsNullOrWhiteSpace(model.GenerateMethod)) throw new Exception($"生成方式不能为空,需要指定一种生成方式"); var tenantDb = saasDbService.GetBizDbScopeById(user.TenantId); //任务不考虑OrgId,这里去掉 tenantDb.QueryFilter.Clear(); var bcTaskInfo = await tenantDb.Queryable().Where(u => u.Id == model.BCTaskId).FirstAsync(); if (bcTaskInfo == null) { throw new Exception($"任务主键{model.BCTaskId}无法获取业务信息"); } var bcOrder = await tenantDb.Queryable().Where(a => a.TASK_ID == bcTaskInfo.Id).FirstAsync(); if (bcOrder == null) throw new Exception($"任务主键{model.BCTaskId}无法获取BC业务信息"); var bcCtnList = await tenantDb.Queryable().Where(a => a.P_ID == bcOrder.Id).ToListAsync(); var fileList = await tenantDb.Queryable().Where(a => a.TASK_PKID == bcTaskInfo.Id).ToListAsync(); if (model.GenerateMethod != "UPD_BOOKING") { if (bcOrder.BOOKING_ORDER_ID.HasValue && bcOrder.BOOKING_ORDER_ID.Value > 0) { throw new Exception($"当前BC任务已生成订舱订单,不能重复生成"); } } if (model.GenerateMethod == "GEN_BOOKING_SLOT") { #region 推送舱位、推送订舱 //推送舱位 //long bookingSlotId = 0; BookingSlotBase? bookingSlot = null; if (bcOrder.BOOKING_SLOT_ID.HasValue && bcOrder.BOOKING_SLOT_ID.Value > 0) { //bookingSlotId = bcOrder.BOOKING_SLOT_ID.Value; throw new Exception($"生成舱位失败,舱位已存在"); } //这里增加委托单位联系人的校验,重新从后台拉取了委托单位相关联系人,如果比对不一致跳出异常终止执行 ValidateContact(model); //触发生成舱位 bookingSlot = await GenerateBookingSlotByTaskBcInfo(bcOrder, bcCtnList, fileList); logger.LogInformation($"生成舱位完成,bookingSlotId={bookingSlot?.Id} taskid={bcOrder.TASK_ID}"); if (bookingSlot == null || bookingSlot.Id == 0) throw new Exception($"生成舱位失败"); var slotFileList = _opFileService.GetOpFileList(bookingSlot.Id.ToString()).Data; //if (bookingSlotService == null) //{ // bookingSlotService = serviceProvider.GetRequiredService(); //} //推送订舱订单 model.SlotId = bookingSlot.Id; var bookingOrderId = await bookingSlotService.Value.GenerateBookingOrder(bookingSlot, slotFileList, model, null); logger.LogInformation($"生成订舱订单完成,bookingOrderId={bookingOrderId} taskid={bcOrder.TASK_ID}"); if (bookingOrderId < 1) throw new Exception($"生成订舱订单失败"); List slots = new List(); //检索舱位信息 var slotInfo = (await bookingSlotService.Value.Detail(bookingSlot.Id)).Data; BookingSlotBaseWithCtnDto baseInfo = slotInfo.Adapt(); baseInfo.Id = bookingSlot.Id; baseInfo.CtnList = slotInfo.CtnList.Adapt>(); slots.Add(baseInfo); //对应订舱和舱位关系 ImportSlotsDto importSlotsDto = new() { bookingOrderId = bookingOrderId, slots = slots, isCheck = false }; var allocRlt = await bookingSlotService.Value.ImportSlots(importSlotsDto); logger.LogInformation($"生成订舱和舱位关系完成,allocRlt={JsonConvert.SerializeObject(allocRlt)} taskid={bcOrder.TASK_ID}"); if (!allocRlt.Succeeded) { throw new Exception($"生成订舱和舱位关系失败"); } var bcEntity = await tenantDb.Queryable().Where(a => a.Id == bcOrder.Id).FirstAsync(); if (bcEntity == null) { throw new Exception($"未获取有效任务BC失败,更新失败"); } bcEntity.BOOKING_ORDER_ID = bookingOrderId; bcEntity.BOOKING_SLOT_ID = slotInfo.Id; bcEntity.UpdateTime = DateTime.Now; bcEntity.UpdateBy = long.Parse(user.UserId); bcEntity.UpdateUserName = user.UserName; //更新任务BC await tenantDb.Updateable(bcEntity).UpdateColumns(it => new { it.BOOKING_ORDER_ID, it.BOOKING_SLOT_ID, it.UpdateTime, it.UpdateBy, it.UpdateUserName }).ExecuteCommandAsync(); var taskEntity = await tenantDb.Queryable().FirstAsync(u => u.Id == bcEntity.TASK_ID); if (taskEntity == null) { throw new Exception($"未获取有效任务记录,更新失败"); } //var currBCOrder = await tenantDb.Queryable().Where(a => a.Id == bcEntity.Id).FirstAsync(); //if (currBCOrder != null && model.IsDirectSend) //{ // //异步推送邮件 // var mailRlt = await GenerateSendEmail(currBCOrder, bcTaskInfo, model.usePersonalEmailSend); // if (!mailRlt.succ) // { // throw Oops.Oh($"邮件推送失败,原因:{mailRlt.msg},可以任务编辑页面重新发送邮件"); // } //} #endregion } else if (model.GenerateMethod == "GEN_BOOKING") { #region 推送订舱 //bookingSlot = null; if (bcOrder.BOOKING_ORDER_ID.HasValue && bcOrder.BOOKING_ORDER_ID.Value > 0) { //bookingSlotId = bcOrder.BOOKING_SLOT_ID.Value; throw new Exception($"生成订舱订单失败,订舱订单已存在"); } if (!bcOrder.BOOKING_SLOT_ID.HasValue || bcOrder.BOOKING_SLOT_ID.Value < 1) { //bookingSlotId = bcOrder.BOOKING_SLOT_ID.Value; throw new Exception($"生成订舱订单失败,舱位未生成不能直接生成订舱订单"); } long bookingSlotId = bcOrder.BOOKING_SLOT_ID.Value; //这里增加委托单位联系人的校验,重新从后台拉取了委托单位相关联系人,如果比对不一致跳出异常终止执行 ValidateContact(model); BookingSlotBase? bookingSlot = await tenantDb.Queryable().Where(x => x.Id == bookingSlotId).FirstAsync(); if (bookingSlot == null) { throw new Exception($"生成订舱订单失败,舱位未生成不能直接生成订舱订单"); } var slotFileList = _opFileService.GetOpFileList(bookingSlotId.ToString()).Data; //推送订舱订单 //if (bookingSlotService == null) //{ // bookingSlotService = serviceProvider.GetRequiredService(); //} model.SlotId = bookingSlot.Id; var bookingOrderId = await bookingSlotService.Value.GenerateBookingOrder(bookingSlot, slotFileList, model, null); logger.LogInformation($"生成订舱订单完成,bookingOrderId={bookingOrderId} taskid={bcOrder.TASK_ID}"); if (bookingOrderId < 1) throw new Exception($"生成订舱订单失败"); List slots = new List(); //检索舱位信息 var slotInfo = (await bookingSlotService.Value.Detail(bookingSlot.Id)).Data; BookingSlotBaseWithCtnDto baseInfo = slotInfo.Adapt(); baseInfo.Id = bookingSlot.Id; baseInfo.CtnList = slotInfo.CtnList.Adapt>(); slots.Add(baseInfo); //对应订舱和舱位关系 ImportSlotsDto importSlotsDto = new() { bookingOrderId = bookingOrderId, slots = slots, isCheck = false }; var allocRlt = await bookingSlotService.Value.ImportSlots(importSlotsDto); logger.LogInformation($"生成订舱和舱位关系完成,allocRlt={JsonConvert.SerializeObject(allocRlt)} taskid={bcOrder.TASK_ID}"); if (!allocRlt.Succeeded) { throw new Exception($"生成订舱和舱位关系失败"); } var bcEntity = await tenantDb.Queryable().Where(a => a.Id == bcOrder.Id).FirstAsync(); if (bcEntity == null) { throw new Exception($"未获取有效任务BC失败,更新失败"); } bcEntity.BOOKING_ORDER_ID = bookingOrderId; bcEntity.BOOKING_SLOT_ID = slotInfo.Id; bcEntity.UpdateTime = DateTime.Now; bcEntity.UpdateBy = long.Parse(user.UserId); bcEntity.UpdateUserName = user.UserName; //更新任务BC await tenantDb.Updateable(bcEntity).UpdateColumns(it => new { it.BOOKING_ORDER_ID, it.BOOKING_SLOT_ID, it.UpdateTime, it.UpdateBy, it.UpdateUserName }).ExecuteCommandAsync(); var taskEntity = await tenantDb.Queryable().FirstAsync(u => u.Id == bcEntity.TASK_ID); if (taskEntity == null) { throw new Exception($"未获取有效任务记录,更新失败"); } //var currBCOrder = _taskBCInfoRepository.AsQueryable().First(a => a.PK_ID == bcEntity.PK_ID); //if (currBCOrder != null && model.IsDirectSend) //{ // //异步推送邮件 // var mailRlt = await GenerateSendEmail(currBCOrder, bcTaskInfo, model.usePersonalEmailSend); // if (!mailRlt.succ) // { // throw Oops.Oh($"邮件推送失败,原因:{mailRlt.msg},可以任务编辑页面重新发送邮件"); // } //} #endregion } else if (model.GenerateMethod == "GEN_SLOT") { #region 推送舱位 BookingSlotBase? bookingSlot = null; if (bcOrder.BOOKING_SLOT_ID.HasValue && bcOrder.BOOKING_SLOT_ID.Value > 0) { //bookingSlotId = bcOrder.BOOKING_SLOT_ID.Value; throw new Exception($"生成舱位失败,舱位已存在"); } //触发生成舱位 bookingSlot = await GenerateBookingSlotByTaskBcInfo(bcOrder, bcCtnList, fileList); logger.LogInformation($"生成舱位完成,bookingSlotId={bookingSlot?.Id} taskid={bcOrder.TASK_ID}"); if (bookingSlot == null || bookingSlot.Id == 0) throw new Exception($"生成舱位失败"); var bcEntity = await tenantDb.Queryable().Where(a => a.Id == bcOrder.Id).FirstAsync(); if (bcEntity == null) { throw new Exception($"未获取有效任务BC失败,更新失败"); } bcEntity.BOOKING_SLOT_ID = bookingSlot.Id; bcEntity.UpdateTime = DateTime.Now; bcEntity.UpdateBy = long.Parse(user.UserId); bcEntity.UpdateUserName = user.UserName; //更新任务BC await tenantDb.Updateable(bcEntity).UpdateColumns(it => new { it.BOOKING_SLOT_ID, it.UpdateTime, it.UpdateBy, it.UpdateUserName }).ExecuteCommandAsync(); #endregion } else if (model.GenerateMethod == "UPD_BOOKING") { #region 更新订舱 if (bcOrder.BOOKING_ORDER_ID == null || bcOrder.BOOKING_ORDER_ID.Value == 0) { throw new Exception($"更新订舱订单失败,舱位未关联到订单信息"); } //这里增加委托单位联系人的校验,重新从后台拉取了委托单位相关联系人,如果比对不一致跳出异常终止执行 ValidateContact(model); long bookingSlotId = bcOrder.BOOKING_SLOT_ID.Value; //这里增加委托单位联系人的校验,重新从后台拉取了委托单位相关联系人,如果比对不一致跳出异常终止执行 ValidateContact(model); BookingSlotBase? bookingSlot = await tenantDb.Queryable().Where(x => x.Id == bookingSlotId).FirstAsync(); if (bookingSlot == null) { throw new Exception($"更新订舱订单失败,未找到id为{bookingSlotId}的舱位信息"); } List bookingSlotCtnList = await tenantDb.Queryable().Where(x => x.Id == bookingSlotId).ToListAsync(); //推送订舱订单 var bookingOrderId = await UpdateBookingOrder((long)bcOrder.BOOKING_ORDER_ID, bookingSlot, bookingSlotCtnList, model, tenantDb); var taskEntity = await tenantDb.Queryable().FirstAsync(u => u.Id == bcOrder.TASK_ID); if (taskEntity == null) { throw new Exception($"未获取有效任务记录,更新失败"); } //var currBCOrder = await tenantDb.Queryable().Where(a => a.Id == bcEntity.Id).FirstAsync(); //if (currBCOrder != null && model.IsDirectSend) //{ // //异步推送邮件 // var mailRlt = await GenerateSendEmail(currBCOrder, bcTaskInfo, model.usePersonalEmailSend); // if (!mailRlt.succ) // { // throw Oops.Oh($"邮件推送失败,原因:{mailRlt.msg},可以任务编辑页面重新发送邮件"); // } //} #endregion } #region 更新任务 //var taskEntity = await tenantDb.Queryable().FirstAsync(u => u.Id == bcEntity.TASK_ID); //if (taskEntity == null) //{ // throw new Exception($"未获取有效任务记录,更新失败"); //} ////如果是公共任务,需要变成个人任务 RealUserId = 当前操作人 //if (taskEntity.IS_PUBLIC == 1) //{ // taskEntity.IS_PUBLIC = 0; // taskEntity.RealUserId = long.Parse(user.UserId); // taskEntity.RealUserName = user.UserName; // taskEntity.UpdateTime = DateTime.Now; // taskEntity.UpdateBy = taskEntity.RealUserId ?? 0; // taskEntity.UpdateUserName = user.UserName; // taskEntity.IS_COMPLETE = 1; // taskEntity.COMPLETE_DATE = DateTime.Now; // taskEntity.COMPLETE_DEAL = "MANUAL"; // taskEntity.COMPLETE_DEAL = "手工"; // await tenantDb.Updateable(taskEntity).UpdateColumns(it => new // { // it.IS_PUBLIC, // it.UpdateTime, // it.UpdateBy, // it.UpdateUserName, // it.RealUserId, // it.RealUserName, // it.IS_COMPLETE, // it.COMPLETE_DATE, // it.COMPLETE_DEAL, // it.COMPLETE_DEAL_NAME // }).ExecuteCommandAsync(); //} //else //{ // taskEntity.UpdateTime = DateTime.Now; // taskEntity.UpdateBy = long.Parse(user.UserId); // taskEntity.UpdateUserName = user.UserName; // taskEntity.IS_COMPLETE = 1; // taskEntity.COMPLETE_DATE = DateTime.Now; // taskEntity.COMPLETE_DEAL = "MANUAL"; // taskEntity.COMPLETE_DEAL = "手工"; // await tenantDb.Updateable(taskEntity).UpdateColumns(it => new // { // it.UpdateTime, // it.UpdateBy, // it.UpdateUserName, // it.IS_COMPLETE, // it.COMPLETE_DATE, // it.COMPLETE_DEAL, // it.COMPLETE_DEAL_NAME // }).ExecuteCommandAsync(); //} #endregion result.succ = true; result.msg = "成功"; return DataResult.Success(result); } catch (Exception ex) { result.succ = false; result.msg = $"生成订舱或舱位失败,原因:{ex.Message}"; return DataResult.FailedData(result); } } private void ValidateContact(BookingOrSlotGenerateDto model) { var paramConfig = _configService.GetConfig(TenantParamCode.CONST_CREATE_BOOKING_NEED_CONTACT, long.Parse(user.TenantId), false).GetAwaiter().GetResult()?.Data?.Value; if (model.CustomerContactList != null && model.CustomerContactList.Count > 0) { //取委托客户下面所有的联系人列表 var djyCustomerInfo = _clientInfoService.GetClientInfoWithContact(new Info.Dtos.QueryClientInfo { ClientId = model.CustomerId.Value, IsController = true }).GetAwaiter().GetResult().Data; if (djyCustomerInfo == null) { logger.LogInformation($"委托单位{model.CustomerName} 获取失败,委托单位不存在或已作废 SlotId={model.SlotId.Value}"); //委托单位{0} 获取失败,委托单位不存在或已作废 throw new Exception(string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotCreateContaNull)), model.CustomerName)); } if (djyCustomerInfo.ClientContactList == null && djyCustomerInfo.ClientContactList.Count < 1) { logger.LogInformation($"委托单位{model.CustomerName} 获取相关联系人失败,委托单位相关联系人为空 SlotId={model.SlotId.Value}"); //委托单位{0} 获取相关联系人失败,委托单位相关联系人为空 throw new Exception(string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotCreateCustomerContractInfoNull)), model.CustomerName)); } model.CustomerContactList.ForEach(contact => { var djyCustomerContactMan = djyCustomerInfo.ClientContactList .FirstOrDefault(a => a.Id == contact.Id); if (djyCustomerContactMan == null) { logger.LogInformation($"委托单位{model.CustomerName} 联系人 {contact.Name}获取失败,联系人不存在或已作废 SlotId={model.SlotId}"); //委托单位 {0} 联系人 {1} 获取失败,联系人不存在或已作废 throw new Exception(string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotCreateCustomerContractDeletedOrNoExists)), model.CustomerName, contact.Name)); } }); } else { if (paramConfig != null && paramConfig.Equals("ENABLE", StringComparison.OrdinalIgnoreCase)) { //生成订舱时往来单位联系人必填,请修改 throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BookingSlotCreateCustomerContractNotNull))); } } } /// /// 通过任务主键获取BC详情 /// /// BC任务主键 public async Task> GetInfoByTaskId(long taskId) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); try { var tenantDb = saasDbService.GetBizDbScopeById(user.TenantId); //任务不考虑OrgId,这里去掉 tenantDb.QueryFilter.Clear(); var taskBase = await tenantDb.Queryable().FirstAsync(a => a.Id == taskId); if (taskBase == null) throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.TaskBaseEmpty))); var bcOrder = await tenantDb.Queryable().FirstAsync(a => a.TASK_ID == taskId); if (bcOrder == null) throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.TaskBCInfoEmpty))); var bcCtnList = await tenantDb.Queryable().Where(a => a.P_ID == bcOrder.Id).ToListAsync(); TaskBCShowBaseDto model = bcOrder.Adapt(); if (bcCtnList.Count > 0) model.CtnList = bcCtnList.Adapt>(); var fileList = await tenantDb.Queryable().Where(a => a.TASK_PKID == bcOrder.TASK_ID).ToListAsync(); if (fileList.Count > 0) model.FileList = fileList.Adapt>(); model.taskStatus = taskBase.STATUS; #region 生成关键信息 model.Keywords = new List(); if (bcOrder.CARRIAGE_TYPE == "DIRECT_SHIP") { model.Keywords.Add(new TaskBCShowBaseKeywordDto() { Name = $"承运方式:{bcOrder.CARRIAGE_TYPE_NAME}", Background = "#FFFF80", Icon = "icon-yunshu1" }); } else if (bcOrder.CARRIAGE_TYPE == "TRANSFER_SHIP") { model.Keywords.Add(new TaskBCShowBaseKeywordDto() { Name = $"承运方式:{bcOrder.CARRIAGE_TYPE_NAME}", Background = "#CAF982", Icon = "icon-shuaxin" }); } if (bcOrder.BOOKING_SLOT_TYPE == "CONTRACT_ORDER") { model.Keywords.Add(new TaskBCShowBaseKeywordDto() { Name = $"订舱方式:{bcOrder.BOOKING_SLOT_TYPE_NAME}", Background = "#81D3F8", Icon = "icon-touzijilu" }); } else if (bcOrder.BOOKING_SLOT_TYPE == "SPOT_ORDER") { model.Keywords.Add(new TaskBCShowBaseKeywordDto() { Name = $"订舱方式:{bcOrder.BOOKING_SLOT_TYPE_NAME}", Background = "#FACD91", Icon = "icon-beizhu1" }); } #endregion //await SetTaskStatus([taskId], x => x.IS_PUBLIC == 1, x => x.IS_EXCEPT == 0); //await SetTaskOwner([taskId], new List() //{ // new RecvUserInfo(111,"231312321"), // new RecvUserInfo(222,"affsdfdsf"), //}); result.succ = true; result.ext = model; //如果当前BC有对应记录,则读取订舱详情 //0726:经确认页面上没有用到订舱详情,所以暂时不返回 //if (bcOrder.BOOKING_ORDER_ID.HasValue) //{ // var bkOrder = await _bookingOrderRepository.AsQueryable(). // FirstAsync(a => a.Id == bcOrder.BOOKING_ORDER_ID.Value); // if (bkOrder != null) // { // var showBKOrder = bkOrder.Adapt(); // var ctnList = await _bookingCtnRepository.AsQueryable(). // Where(a => a.BILLID == bkOrder.Id).ToListAsync(); // if (ctnList.Count > 0) // showBKOrder.ctnInputs = ctnList.Adapt>(); // result.ext2 = showBKOrder; // } //} } catch (Exception ex) { result.succ = false; result.msg = $"获取BC详情异常,原因:{ex.Message}"; } return DataResult.Success(result); } #region 根据BC任务信息生成舱位 /// /// 根据BC任务信息,生成舱位(调用的) /// /// BC任务详情 /// BC任务集装箱列表 /// BC任务附件列表 /// 返回舱位ID private async Task GenerateBookingSlotByTaskBcInfo(TaskBCInfo taskBCInfo, List taskBCCtnList, List taskFileList, string opType = "add") { //long id = 0; try { var allMapCarrierList = await _mappingCarrierService.GetAllList(); MappingCarrierRes? carrierInfo = null; if (allMapCarrierList.Succeeded) { carrierInfo = allMapCarrierList.Data.Where(t => t.MapCode.Equals(taskBCInfo.CARRIERID, StringComparison.OrdinalIgnoreCase) && t.Module == MappingModuleConst.CONST_MAPPING_CARRIER_MODULE).FirstOrDefault(); } BookingSlotBaseApiDto slotModel = new BookingSlotBaseApiDto { DataObj = new BookingSlotBaseApiSaveDto { CarrierId = carrierInfo?.LinkId, CarrierCode = carrierInfo?.MapCode, Carrier = carrierInfo?.MapName, SlotBookingNo = taskBCInfo.MBL_NO, BookingParty = taskBCInfo.BOOKING_PARTY, BookingSlotType = taskBCInfo.BOOKING_SLOT_TYPE, BookingSlotTypeName = taskBCInfo.BOOKING_SLOT_TYPE_NAME, Vessel = taskBCInfo.VESSEL, Voyno = taskBCInfo.VOYNO, VGMSubmissionCutDate = taskBCInfo.VGM_CUTOFF_TIME, //WeekAt = taskBCInfoDto.WEEK_AT, CarriageType = taskBCInfo.CARRIAGE_TYPE, CarriageTypeName = taskBCInfo.CARRIAGE_TYPE_NAME, ContractNo = taskBCInfo.CONTRACTNO, CtnStat = taskBCInfo.CTN_STAT, CYCutDate = taskBCInfo.CY_CUTOFF_TIME, DetensionFreeDays = taskBCInfo.DETENSION_FREE_DAYS, ETD = taskBCInfo.ETD, ETA = taskBCInfo.ETA, LaneCode = taskBCInfo.LANECODE, LaneName = taskBCInfo.LANENAME, ManifestCutDate = taskBCInfo.MANIFEST_CUT_DATE, MDGFCutDate = taskBCInfo.MDGF_CUT_DATE, PlaceDelivery = taskBCInfo.PLACEDELIVERY, PlaceReceipt = taskBCInfo.PLACERECEIPT, PortDischarge = taskBCInfo.PORTDISCHARGE, PortLoad = taskBCInfo.PORTLOAD, SICutDate = taskBCInfo.SI_CUT_DATE, CustomSICutDate = taskBCInfo.CUSTOM_SI_CUT_DATE, TransferPort1 = taskBCInfo.TRANSFER_PORT_1, TransferPort2 = taskBCInfo.TRANSFER_PORT_2, PriceCalculationDate = taskBCInfo.PRICE_CALCULATION_DATE, CtnList = new List() }, OpType = "add" }; if (int.TryParse(taskBCInfo.WEEK_AT, out int _weekat)) { slotModel.DataObj.WeekAt = _weekat; } var ctnCodeList = (await _codeCtnService.GetAllList()).Data ?? new List(); if (taskBCCtnList.Count > 0) { taskBCCtnList.ForEach(t => { if (string.IsNullOrEmpty(t.CTNCODE) && !string.IsNullOrEmpty(t.CTNALL)) { var ctnCode = ctnCodeList.FirstOrDefault(a => !string.IsNullOrWhiteSpace(a.CtnName) && a.CtnName.Equals(t.CTNALL, StringComparison.OrdinalIgnoreCase)); t.CTNCODE = ctnCode != null ? $"{ctnCode.CtnSize}{ctnCode.CtnType}" : "(箱型未收录)"; } BookingSlotCtnSaveInput ctn = new BookingSlotCtnSaveInput { CtnCode = t.CTNCODE, CtnAll = t.CTNALL, CtnNum = t.CTNNUM.HasValue ? t.CTNNUM.Value : 1 }; slotModel.DataObj.CtnList.Add(ctn); }); } var basePath = AppSetting.app(new string[] { "FileSettings", "BasePath" }); DynameFileInfo dynameFile = null; DynameFileInfo dynameNoticeFile = null; if (taskFileList.Any(t => t.FILE_CATEGORY == TaskFileCategoryEnum.BC.ToString())) { var fileInfo = taskFileList.FirstOrDefault(t => t.FILE_CATEGORY == TaskFileCategoryEnum.BC.ToString()); string fileFullPath; if (string.IsNullOrEmpty(basePath)) { fileFullPath = Path.Combine(environment.WebRootPath ?? "", fileInfo.FILE_PATH); } else { fileFullPath = Path.Combine(basePath, fileInfo.FILE_PATH); } dynameFile = new DynameFileInfo { FileBytes = File.ReadAllBytes(fileFullPath), FileName = Path.GetFileName(fileFullPath) }; } if (taskFileList.Any(t => t.FILE_CATEGORY == TaskFileCategoryEnum.BC_NOTICE.ToString())) { var fileInfo = taskFileList.FirstOrDefault(t => t.FILE_CATEGORY == TaskFileCategoryEnum.BC.ToString()); string fileFullPath; if (string.IsNullOrEmpty(basePath)) { fileFullPath = Path.Combine(environment.WebRootPath ?? "", fileInfo.FILE_PATH); } else { fileFullPath = Path.Combine(basePath, fileInfo.FILE_PATH); } dynameNoticeFile = new DynameFileInfo { FileBytes = File.ReadAllBytes(fileFullPath), FileName = Path.GetFileName(fileFullPath) }; } var result = await bookingSlotService.Value.InnerApiReceive(slotModel, dynameFile, dynameNoticeFile); if (result.Succeeded && result.Data != null) { var tenantDb = saasDbService.GetBizDbScopeById(user.TenantId); var taskBcInfo = await tenantDb.Queryable().Where(x => x.Id == taskBCInfo.Id).FirstAsync(); if (taskBcInfo != null && taskBcInfo.BOOKING_SLOT_ID == null) { taskBcInfo.BOOKING_SLOT_ID = result.Data.Id; await tenantDb.Updateable(taskBcInfo).UpdateColumns(x => new { x.BOOKING_SLOT_ID }).ExecuteCommandAsync(); } } return result.Data; } catch (Exception ex) { logger.LogError($"任务BC MBLNO:{taskBCInfo.MBL_NO} 生成舱位异常,原因:{ex.Message}"); throw new Exception($"MBLNO:{taskBCInfo.MBL_NO} 生成舱位异常,原因:{ex.Message}"); } } #endregion #region 更新订舱 /// /// 更新订舱 /// /// 返回订舱ID private async Task UpdateBookingOrder( //TaskBCInfo taskBCInfo, //List taskBCCtnList, long bookingOrderId, BookingSlotBase bookingSlotBase, List bookingSlotCtnList, BookingOrSlotGenerateDto generateModel, SqlSugarScopeProvider? tenantDb = null) { //long id = 0; if (tenantDb == null) { tenantDb = saasDbService.GetBizDbScopeById(user.TenantId); } //任务不考虑OrgId,这里去掉 tenantDb.QueryFilter.Clear(); try { /* 1、新增订舱 2、推送服务项目 3、推送附件 */ var allMapCarrierList = await _mappingCarrierService.GetAllList(); MappingCarrierRes carrierInfo = null; if (allMapCarrierList.Succeeded) { carrierInfo = allMapCarrierList.Data.FirstOrDefault(t => t.LinkId == bookingSlotBase.CarrierId.Value && t.Module == MappingModuleConst.CONST_MAPPING_CARRIER_MODULE); } SeaExportReq bkModel = new SeaExportReq { Id = bookingOrderId, CustomerId = generateModel.CustomerId.Value, CustomerName = generateModel.CustomerName, Carrier = carrierInfo?.MapName?.Trim(), CarrierId = bookingSlotBase.CarrierId.Value, BookingNo = bookingSlotBase.SlotBookingNo.Trim(), MBLNO = bookingSlotBase.SlotBookingNo.Trim(), ContractNo = !string.IsNullOrWhiteSpace(bookingSlotBase.ContractNo) ? bookingSlotBase.ContractNo : "", Vessel = bookingSlotBase.Vessel?.ToUpper()?.Trim(), Voyno = bookingSlotBase.Voyno?.ToUpper()?.Trim(), InnerVoyno = bookingSlotBase.Voyno?.ToUpper()?.Trim(), ETD = bookingSlotBase.ETD, ETA = bookingSlotBase.ETA, SaleId = generateModel.SaleId.HasValue ? generateModel.SaleId.Value : 0, Sale = generateModel.SaleName, OperatorId = generateModel.OpId.HasValue ? generateModel.OpId.Value : 0, OperatorName = generateModel.OpName, Doc = generateModel.DocId.HasValue ? generateModel.DocId.Value : 0, DocName = generateModel.DocName, //ROUTEID = generateModel.RouteID?.ToString(), //ROUTE = generateModel.Route, //CZRemark = generateModel.CZRemark, //ShenQingXiangShi = generateModel.ShenQingXiangShi, //LineManageID = generateModel.LineManageID?.ToString(), //LineName = generateModel.LineManage, VGMCloseDate = bookingSlotBase.VGMSubmissionCutDate, ClosingDate = bookingSlotBase.CYCutDate, CloseDocDate = bookingSlotBase.SICutDate, CustomerService = generateModel.CustServiceId.HasValue ? generateModel.CustServiceId.Value : 0, CustomerServiceName = generateModel.CustServiceName, LoadPort = bookingSlotBase.PortLoad, LoadPortId = bookingSlotBase.PortLoadId.HasValue ? bookingSlotBase.PortLoadId.Value : 0, DischargePortId = bookingSlotBase.PortDischargeId.HasValue ? bookingSlotBase.PortLoadId.Value : 0, DischargePort = bookingSlotBase.PortDischarge, ReceiptPlace = bookingSlotBase.PlaceReceipt, ReceiptPlaceId = bookingSlotBase.PlaceReceiptId.HasValue ? bookingSlotBase.PlaceReceiptId.Value : 0, DeliveryPlace = bookingSlotBase.PlaceDelivery, DeliveryPlaceId = bookingSlotBase.PlaceDeliveryId.HasValue ? bookingSlotBase.PlaceDeliveryId.Value : 0, BLType = "整箱", StlName = "票结", CtnInfo = new List() }; //var ctnCodeList = _cache.GetAllCodeCtn().GetAwaiter().GetResult().ToList(); if (bookingSlotCtnList != null && bookingSlotCtnList.Count > 0) { bookingSlotCtnList.Select(t => { OpCtnReq ctn = new OpCtnReq { CtnCode = t.CtnCode, CtnAll = t.CtnAll, CtnNum = t.CtnNum }; return ctn; }); } var bkRlt = await seaExportService.Value.EditSeaExport(bkModel); if (!bkRlt.Succeeded) { throw new Exception(MultiLanguageConst.GetDescription(MultiLanguageConst.DataUpdateFailed) + "," + bkRlt.Message); } string batchNo = Guid.NewGuid().ToString(); //var hisList = _bookingOrderContactRepository.AsQueryable().Where(a => a.BookingId == id && a.IsDeleted == false).ToList(); //if (hisList.Count > 0) //{ // //批量作废 // hisList.ForEach(async a => // { // a.IsDeleted = true; // a.UpdatedTime = DateTime.Now; // a.UpdatedUserId = UserManager.UserId; // a.UpdatedUserName = UserManager.Name; // await _bookingOrderContactRepository.UpdateAsync(a); // }); //} //这里如果指定了委托单位的邮件联系人,则推送订舱联系人 if (generateModel.CustomerContactList != null && generateModel.CustomerContactList.Count > 0) { var bookingContactList = await tenantDb.Queryable() .Where(a => a.BusinessId == bookingOrderId && a.Deleted == false).ToListAsync(); var djyCustomerInfo = _clientInfoService.GetClientInfoWithContact(new Info.Dtos.QueryClientInfo { ClientId = generateModel.CustomerId.Value, IsController = true }) .GetAwaiter().GetResult().Data; generateModel.CustomerContactList.ForEach(contact => { ClientContactRes djyCustomerContactMan = null; if (djyCustomerInfo.ClientContactList != null && djyCustomerInfo.ClientContactList.Count > 0) { djyCustomerContactMan = djyCustomerInfo.ClientContactList.FirstOrDefault(a => a.Id == contact.Id); } if (djyCustomerContactMan != null) { var bookingContact = bookingContactList .FirstOrDefault(x => x.Email.Equals(djyCustomerContactMan.Email, StringComparison.OrdinalIgnoreCase)); if (bookingContact == null) { bookingContact = new BusinessOrderContact { Name = djyCustomerContactMan.Name, BusinessId = bookingOrderId, Email = djyCustomerContactMan.Email, Note = djyCustomerContactMan.Note, CreateTime = DateTime.Now, CreateBy = long.Parse(user.UserId), CreateUserName = user.UserName }; tenantDb.Insertable(bookingContact).ExecuteCommand(); //_bookingOrderContactRepository.Insert(bookingContact); } else { bookingContact.Name = djyCustomerContactMan.Name; bookingContact.Email = djyCustomerContactMan.Email; bookingContact.Note = djyCustomerContactMan.Note; bookingContact.UpdateTime = DateTime.Now; bookingContact.UpdateBy = long.Parse(user.UserId); bookingContact.UpdateUserName = user.UserName; tenantDb.Updateable(bookingContact).UpdateColumns(it => new { it.Name, it.Email, it.Note, it.UpdateTime, it.UpdateBy, it.UpdateUserName }).ExecuteCommand(); } } }); } if (generateModel.ProjectList != null && generateModel.ProjectList.Count > 0) { //写入服务项目 var prjRlt = djyServiceStatusService.Value.SaveServiceProject(new EmbedServiceProjectDto { BusinessId = bookingOrderId.ToString(), ProjectCodes = generateModel.ProjectList.Distinct().ToArray(), }); logger.LogInformation($"推送订舱的服务项目完成 id={bookingOrderId} rlt={JsonConvert.SerializeObject(prjRlt)}"); } logger.LogInformation($"任务BC MBLNO:{bookingSlotBase.SlotBookingNo} 生成订舱成功 id={bookingOrderId}"); } catch (Exception ex) { logger.LogError($"任务BC MBLNO:{bookingSlotBase.SlotBookingNo} 生成订舱异常,原因:{ex.Message}"); } return bookingOrderId; } #endregion #region 获取当前比对结果 /// /// 获取当前比对结果 /// /// BC任务主键 /// 返回回执 public async Task>> GetCompareResult(long taskId) { var tenantDb = saasDbService.GetBizDbScopeById(user.TenantId); //任务不考虑OrgId,这里去掉 tenantDb.QueryFilter.Clear(); var queryList = await tenantDb.Queryable() .InnerJoin((a, b) => a.Id == b.TASK_ID) .Where((a, b) => a.Id == taskId) .Select((a, b) => new { Base = a, BC = b }) .ToListAsync(); //任务主键{taskPkId}无法获取业务信息 if (queryList.Count == 0) throw new Exception(string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.TaskBaseInfoFromTaskIdNull)), taskId)); var taskBCInfo = queryList.FirstOrDefault().BC; if (taskBCInfo.BOOKING_SLOT_ID.HasValue) { return await bookingSlotService.Value.GetSlotCompareResult(taskBCInfo.BOOKING_SLOT_ID.Value, taskBCInfo.BATCH_NO); } else { return DataResult>.Success(new List()); } } #endregion /// /// 对比BC与海运出口订单(任务台使用) /// public async Task> CompareBcWithSeaExportTask(TaskFlowDataContext dataContext) { var taskBcInfo = dataContext.Get(TaskFlowDataNameConst.TaskBCInfo) ?? throw new ArgumentException($"缺少参数:{nameof(TaskFlowDataNameConst.TaskBCInfo)}"); var taskBcCtnList = dataContext.Get?>(TaskFlowDataNameConst.TaskBCCtnList) ?? throw new ArgumentException($"缺少参数:{nameof(TaskFlowDataNameConst.TaskBCCtnList)}"); var seaExport = await tenantDb.Queryable().Where(x => x.Id == taskBcInfo.BOOKING_ORDER_ID).Select(x => new SeaExport() { Id = x.Id, MBLNO = x.MBLNO, ContractNo = x.ContractNo, ETD = x.ETD, Vessel = x.Vessel, Voyno = x.Voyno, DischargePortId = x.DischargePortId, DischargePortCode = x.DischargePortCode, DischargePort = x.DischargePort, LoadPortId = x.LoadPortId, LoadPortCode = x.LoadPortCode, LoadPort = x.LoadPort, ReeferQuantity = x.ReeferQuantity, TemperatureMin = x.TemperatureMin, TemperatureMax = x.TemperatureMax, TemperatureSet = x.TemperatureSet, Humidity = x.Humidity, }).FirstAsync(); if (seaExport == null) { return DataResult.Failed($"根据订单Id:【{taskBcInfo.BOOKING_ORDER_ID}】未查询到海运出口订单信息"); } var seaExportCtnList = await tenantDb.Queryable().Where(x => x.BSNO == seaExport.Id.ToString()).Select(x => new OpCtn() { Id = x.Id, CtnAll = x.CtnAll, CtnNum = x.CtnNum }).ToListAsync(); var compareDto = new CompareBcWithSeaExportDto() { TaskBCInfo = taskBcInfo, TaskBCCtnList = taskBcCtnList, SeaExport = seaExport, OpCtnList = seaExportCtnList }; CompareResultInfo result = await CompareBcWithSeaExport(compareDto); if (result != null && result.ShowDetailList?.Count > 0) { // 将对比结果里的“收货地”替换为“装货港”,因为对比接口没有开启“装货港”对比 var c1 = result.ShowDetailList.FirstOrDefault(x => x.FieldCode == "placeReceipt"); if (c1 != null) { c1.FieldCode = "portLoad"; c1.FieldName = "装货港"; c1.Msg = c1.Msg.Replace("收货地", "装货港"); } } dataContext.Set(TaskFlowDataNameConst.BcCompareWithSeaExportResult, result); return DataResult.Success(result); } /// /// 对比BC与海运出口订单 /// public async Task CompareBcWithSeaExport(CompareBcWithSeaExportDto compareDto) { var taskBcInfo = compareDto.TaskBCInfo; var taskBcCtnList = compareDto.TaskBCCtnList; var seaExport = compareDto.SeaExport; var seaExportCtnList = compareDto.OpCtnList; var taskBcId = taskBcInfo.Id; string batchNo = GuidHelper.GetSnowflakeId(); // BC信息 ParserBCInfoDto bcSrcDto = new ParserBCInfoDto() { ContractNo = taskBcInfo.CONTRACTNO, Vessel = taskBcInfo.VESSEL, VoyNo = taskBcInfo.VOYNO, ETD = taskBcInfo.ETD?.ToString("yyyy-MM-dd"), //Portload = taskBcInfo.PORTLOAD?.Split(',').FirstOrDefault()?.Trim(), PlaceReceipt = taskBcInfo.PORTLOAD_CODE?.Split(',')?.FirstOrDefault()?.Trim(), // 对比接口没开启Portload这个字段,所以用收货地代替 PortDischarge = taskBcInfo.PORTDISCHARGE_CODE?.Split(',')?.FirstOrDefault()?.Trim(), CtnList = taskBcCtnList.GroupBy(x => x.CTNALL).Select(x => new ParserBCCTNInfoDto { CtnALL = x.Key, CtnNum = x.ToList().Sum(a => a.CTNNUM) }).ToList() }; // 订单信息 ParserBCInfoDto bcTargetDto = new ParserBCInfoDto() { ContractNo = seaExport.ContractNo, Vessel = seaExport.Vessel, VoyNo = seaExport.Voyno, ETD = seaExport.ETD?.ToString("yyyy-MM-dd"), //Portload = seaExport.LoadPort?.Split(',').FirstOrDefault()?.Trim(), PlaceReceipt = seaExport.LoadPortCode?.Split(',')?.FirstOrDefault()?.Trim(), // 对比接口没开启Portload这个字段,所以用收货地代替 PortDischarge = seaExport.DischargePortCode?.Split(',')?.FirstOrDefault()?.Trim(), CtnList = seaExportCtnList.GroupBy(x => x.CtnAll).Select(x => new ParserBCCTNInfoDto { CtnALL = x.Key, CtnNum = x.ToList().Sum(a => a.CtnNum) }).ToList() }; //2021-11-23,备注中含有NOR,不比较温度湿度等信息 //2023 - 02 - 02 这里补充了一下EDIREMARK的判断,用正则匹配NOR信息。NOR(冻代干集装箱) if (seaExport.CargoId == "R" && (string.IsNullOrWhiteSpace(seaExport.SORemark) || !Regex.IsMatch(seaExport.SORemark, "\\bNOR\\b"))) { bcSrcDto.TemperatureMin = taskBcInfo.TEMPERATURE_MIN; bcSrcDto.TemperatureMax = taskBcInfo.TEMPERATURE_MAX; bcSrcDto.TemperatureSet = taskBcInfo.TEMPERATURE_SET; bcSrcDto.Humidity = taskBcInfo.HUMIDITY; bcSrcDto.ReeferQuantity = taskBcInfo.REEFER_QUANTITY; bcTargetDto.TemperatureMin = seaExport.TemperatureMin; bcTargetDto.TemperatureMax = seaExport.TemperatureMax; bcTargetDto.TemperatureSet = seaExport.TemperatureSet; bcTargetDto.Humidity = seaExport.Humidity; bcTargetDto.ReeferQuantity = seaExport.ReeferQuantity; } DateTime bDate = DateTime.Now; logger.LogInformation($"批次={batchNo} taskBcInfo.id={taskBcInfo.Id} seaExport.id={seaExport.Id} 开始请求BC对比订单结果"); var compareResult = (await bookingSlotService.Value.ExcuteCompare(bcSrcDto, bcTargetDto)).Data; DateTime eDate = DateTime.Now; TimeSpan ts = eDate.Subtract(bDate); var timeDiff = ts.TotalMilliseconds; logger.LogInformation($"批次={batchNo} taskBcInfo.id={taskBcInfo.Id} seaExport.id={seaExport.Id} 请求BC对比订单结果完成,耗时:{timeDiff}ms,是否成功:{(compareResult?.succ == true ? "成功" : "失败")} "); if (compareResult != null && compareResult?.succ == true) { logger.LogInformation($"批次={batchNo} BC对比订单结果={JsonConvert.SerializeObject(compareResult)}"); DateTime nowDate = DateTime.Now; var hisInfo = await tenantDb.Queryable().FirstAsync(a => a.BUSI_ID == seaExport.Id.ToString() && a.TASK_ID == taskBcId); if (hisInfo == null) { BusinessCompareDiffRecord entity = new BusinessCompareDiffRecord { BUSI_ID = seaExport.Id.ToString(), TASK_ID = taskBcId, BUSI_TYPE = "SEAEXPORT", COMPARE_DIFF_NUM = compareResult.extra.IsExistsDiff ? compareResult.extra.ShowDetailList.Count : 0, CreateTime = nowDate, UpdateTime = nowDate, CreateBy = long.Parse(user.UserId), CreateUserName = user.UserName, UpdateBy = long.Parse(user.UserId), UpdateUserName = user.UserName, COMPARE_TYPE = "BC-SEAEXPORT", COMPARE_RLT = JsonConvert.SerializeObject(compareResult.extra.ShowDetailList), }; await tenantDb.Insertable(entity).ExecuteCommandAsync(); } else { hisInfo.COMPARE_DIFF_NUM = compareResult.extra.IsExistsDiff ? compareResult.extra.ShowDetailList.Count : 0; hisInfo.UpdateTime = nowDate; hisInfo.UpdateBy = long.Parse(user.UserId); hisInfo.UpdateUserName = user.UserName; hisInfo.COMPARE_RLT = JsonConvert.SerializeObject(compareResult.extra.ShowDetailList); await tenantDb.Updateable(hisInfo).UpdateColumns(it => new { it.COMPARE_DIFF_NUM, it.COMPARE_RLT, it.UpdateTime, it.UpdateBy, it.UpdateUserName }).ExecuteCommandAsync(); } return new CompareResultInfo() { IsExistsDiff = compareResult.extra.IsExistsDiff, ShowDetailList = compareResult.extra.ShowDetailList, DetailList = compareResult.extra.DetailList, IsSuccess = true }; //return (compareResult.extra.IsExistsDiff, compareResult.extra.ShowDetailList); } else { logger.LogError($"批次={batchNo} BC对比订单结果为空"); return new CompareResultInfo() { IsSuccess = false }; } } /// /// 通过BC任务匹配订单(任务台使用)(如果订单提单号为空,则更新上提单号) /// public async Task> BcMatchSeaExportTask(TaskFlowDataContext dataContext) { var taskBcInfo = dataContext.Get(TaskFlowDataNameConst.TaskBCInfo) ?? throw new ArgumentException($"缺少参数:{nameof(TaskFlowDataNameConst.TaskBCInfo)}"); SeaExportRes? seaExport = null; // 先从数据上下文中取,如果为null,再从库里查询 seaExport = dataContext.Get(TaskFlowDataNameConst.Business); if (seaExport == null) { var result = await BcMatchSeaExport(taskBcInfo); if (result.Succeeded && result.Data != null && result.Data.Id != 0) { seaExport = result.Data; } } if (seaExport != null) { // BC是否匹配到了海运出口订单=>true dataContext.Set(TaskFlowDataNameConst.BcIsMatchSeaExport, true); // 订单Id dataContext.Set(TaskFlowDataNameConst.BusinessId, seaExport.Id); await tenantDb.Updateable() .SetColumns(x => x.BOOKING_ORDER_ID == seaExport.Id) .Where(x => x.Id == taskBcInfo.Id) .ExecuteCommandAsync(); await tenantDb.Updateable() .SetColumns(x => x.OUT_BS_NO == seaExport.Id) .Where(x => x.Id == taskBcInfo.TASK_ID) .ExecuteCommandAsync(); return DataResult.Success(seaExport, "订单已匹配"); } else { return DataResult.Failed("匹配订单失败", MultiLanguageConst.BcMatchSeaExportFailed); } } /// /// 通过BC任务匹配订单 /// private async Task> BcMatchSeaExport(TaskBCInfo taskBcInfo) { var queryable = tenantDb.Queryable(); if (taskBcInfo.BOOKING_ORDER_ID is not null or 0) { queryable.Where(x => x.Id == taskBcInfo.BOOKING_ORDER_ID); } else if (!string.IsNullOrWhiteSpace(taskBcInfo.MBL_NO)) { queryable.Where(x => x.ParentId == 0 && (taskBcInfo.MBL_NO == x.MBLNO || taskBcInfo.MBL_NO == x.BookingNo)); } else { return DataResult.Failed("匹配订单失败", string.Format(MultiLanguageConst.BcMatchSeaExportFailed, ",订单Id及提单号都为空,无法匹配")); } List seaExportList = await queryable.Select(x => new SeaExport() { Id = x.Id, MBLNO = x.MBLNO, SplitOrMergeFlag = x.SplitOrMergeFlag, BookingNo = x.BookingNo, OperatorId = x.OperatorId, OperatorName = x.OperatorName, Doc = x.Doc, DocName = x.DocName, SaleId = x.SaleId, Sale = x.Sale, Vessel = x.Vessel, VesselId = x.VesselId, Vessel2N = x.Vessel2N, VesselId2N = x.VesselId2N, Voyno = x.Voyno, Voyno2N = x.Voyno2N, InnerVoyno = x.InnerVoyno, CloseDocDate = x.CloseDocDate, CustomerService = x.CustomerService, CustomerServiceName = x.CustomerServiceName, ForeignCustomerService = x.ForeignCustomerService, ForeignCustomerServiceName = x.ForeignCustomerServiceName }).ToListAsync(); logger.LogInformation($"通过BC任务匹配订单(任务台使用):根据taskBcInfo.MBL_NO【{taskBcInfo.MBL_NO}】查询订单后,seaExportList结果为:{JsonConvert.SerializeObject(seaExportList)}"); var seaExport = seaExportList.FirstOrDefault(); if (seaExport != null) { taskBcInfo.BOOKING_ORDER_ID = seaExport.Id; var result = seaExport.Adapt(); return DataResult.Success("匹配订单成功", result, MultiLanguageConst.OperationSuccess); } else { return DataResult.Failed("匹配订单失败", MultiLanguageConst.BcMatchSeaExportFailed); } } /// /// BC更新海运出口订单信息 /// public async Task BCUpdateOrderInfo(TaskFlowDataContext dataContext) { var seaExportRes = dataContext.Get(TaskFlowDataNameConst.Business) ?? throw new ArgumentException($"缺少参数:{nameof(TaskFlowDataNameConst.Business)}"); var taskBcInfo = dataContext.Get(TaskFlowDataNameConst.TaskBCInfo) ?? throw new ArgumentException($"缺少参数:{nameof(TaskFlowDataNameConst.TaskBCInfo)}"); var taskBaseInfo = dataContext.Get(TaskFlowDataNameConst.TaskBaseInfo) ?? throw new ArgumentException($"缺少参数:{nameof(TaskFlowDataNameConst.TaskBaseInfo)}"); var oldSeaExport = seaExportRes.Adapt(); var seaExport = seaExportRes.Adapt(); tenantDb.Tracking(seaExport); var codeVesselList = await codeVesselService.Value.GetAllList(); if (!string.IsNullOrEmpty(taskBcInfo.MBL_NO) && seaExport.SplitOrMergeFlag == 0) { seaExport.MBLNO = taskBcInfo.MBL_NO; } if (!string.IsNullOrEmpty(taskBcInfo.VOYNO)) { seaExport.Voyno = taskBcInfo.VOYNO; } if (!string.IsNullOrEmpty(taskBcInfo.CARRIER_VOYNO)) { seaExport.InnerVoyno = taskBcInfo.CARRIER_VOYNO; } if (!string.IsNullOrEmpty(taskBcInfo.VESSEL)) { long vesselId = 0; if (codeVesselList.Succeeded && codeVesselList.Data?.Count > 0) { vesselId = codeVesselList.Data.FirstOrDefault(x => x.VesselName == taskBcInfo.VESSEL)?.Id ?? 0; } seaExport.Vessel = taskBcInfo.VESSEL; seaExport.VesselId = vesselId; } if (!string.IsNullOrEmpty(taskBcInfo.SECOND_VESSEL)) { long vessel2NId = 0; if (codeVesselList.Succeeded && codeVesselList.Data?.Count > 0) { vessel2NId = codeVesselList.Data.FirstOrDefault(x => x.VesselName == taskBcInfo.SECOND_VESSEL)?.Id ?? 0; } seaExport.Vessel2N = taskBcInfo.SECOND_VESSEL; seaExport.VesselId2N = vessel2NId; } if (taskBcInfo.SI_CUT_DATE != null) { seaExport.CloseDocDate = taskBcInfo.SI_CUT_DATE; } await tenantDb.Updateable(seaExport).ExecuteCommandAsync(); // 记录日志 await seaExportCommonService.Value.SaveSeaExportLogAsync(new SeaExportSaveLog() { OperateType = "Update", OldOrder = oldSeaExport, NewOrder = seaExport, SourceCode = taskBaseInfo.TASK_SOURCE, SourceName = taskBaseInfo.TASK_SOURCE_NAME, }, tenantDb); return DataResult.Successed("BC更新订单信息成功"); } #region 生成并推送邮件 /// /// 生成并推送邮件 /// /// BC任务详情 /// 主任务详情 /// 检索订舱相关 /// 是否默认使用用户个人邮箱发送 /// 返回回执 private async Task GenerateSendEmail(TaskBCInfo taskBCInfo, TaskBaseInfo bcTaskInfo, SeaExportOrderExtension orderInfo, SeaExport currSeaExportOrder, bool saveBookingId = false, bool usePersonalEmailSend = false) { bool result = false; string msg = string.Empty; try { /* 1、提取邮件接收人、通过订舱的委托客户获取联系人信息(提取联系人中备注是BCNotice的邮箱) 2、提取当票订舱对应的操作人邮箱、通过订舱的委托客户获取操作OP的邮箱 3、读取用户邮箱配置,主要提取显示名称BCNotice的邮箱,用来作为公共邮箱来发送邮件。 4、读取邮件模板,填充详情。 5、推送邮件给邮件接收人 */ //if (bookingOrderEntity == null) //{ // var checkInfo = _bookingOrderRepository.AsQueryable().Filter(null, true) // .First(a => a.MBLNO == taskBCInfo.MBL_NO && a.IsDeleted == false && (a.ParentId == null || a.ParentId == 0)); // if (checkInfo != null) // { // throw Oops.Oh($"订舱详情获取失败,用提单号能检索到,但是没有ID关系,需要人工看看问题"); // } // else // { // throw Oops.Oh($"订舱详情获取失败,请确认订舱是否存在或已删除"); // } //} var tenantDb = saasDbService.GetBizDbScopeById(user.TenantId); //任务不考虑OrgId,这里去掉 tenantDb.QueryFilter.Clear(); var bookingOrderEntity = currSeaExportOrder; Logger.Log(NLog.LogLevel.Info, $"获取订舱详情完成,bookid={bookingOrderEntity.Id}"); if (saveBookingId && taskBCInfo.BOOKING_ORDER_ID != currSeaExportOrder.Id) { taskBCInfo.BOOKING_ORDER_ID = currSeaExportOrder.Id; taskBCInfo.UpdateTime = DateTime.Now; taskBCInfo.UpdateBy = long.Parse(user.UserId); taskBCInfo.UpdateUserName = user.UserName; await tenantDb.Updateable(taskBCInfo).UpdateColumns(x => new { x.BOOKING_ORDER_ID, x.UpdateTime, x.UpdateBy, x.UpdateUserName }).ExecuteCommandAsync(); } if (bookingOrderEntity.CustomerId == 0) { throw new Exception($"订舱的委托客户不能为空"); } var djyCustomerInfo = _clientInfoService.GetClientInfoWithContact(new Info.Dtos.QueryClientInfo { ClientId = bookingOrderEntity.CustomerId }) .GetAwaiter().GetResult().Data; if (djyCustomerInfo == null) { throw new Exception($"委托单位详情获取失败,请确认委托单位是否存在或已删除"); } Logger.Log(NLog.LogLevel.Info, $"获取委托单位详情完成,djyCustomerId={djyCustomerInfo.Id}"); //DjyCustomerContactOutput djyCustomerContactMan = null; //TO 邮件接收人 string toEmail = string.Empty; //订舱OP的邮箱 string opEmail = string.Empty; var bookingContactList = tenantDb.Queryable() .Where(a => a.BusinessId == currSeaExportOrder.Id).ToList(); if (bookingContactList == null || bookingContactList.Count == 0) { Logger.Log(NLog.LogLevel.Info, $"当前订舱未指定的联系人,toEmail={toEmail}"); } toEmail = string.Join(";", bookingContactList.Select(x => x.Email.Trim()).Distinct().ToArray()); //获取操作OP的邮箱 if (bookingOrderEntity.OperatorId > 0) { var opId = bookingOrderEntity.OperatorId; var opUser = db.Queryable().Filter(null, true).First(x => x.Id == bookingOrderEntity.OperatorId && x.TenantId == long.Parse(user.TenantId)); //var opUser = _userService.GetUserInfo(opId.ToString()).Data; if (opUser != null && !string.IsNullOrWhiteSpace(opUser.Email)) { opEmail = opUser.Email.Trim(); Logger.Log(NLog.LogLevel.Info, $"获取操作OP的邮箱,opEmail={opEmail} id={opId} name={opUser.UserName}"); } } //提取当前公共邮箱的配置 CodeUserEmailRes publicMailAccount = null; if (usePersonalEmailSend) { publicMailAccount = _userEmailService.GetEmailSettings().GetAwaiter().GetResult().Data; //&& x.SmtpPort > 0 && x.SmtpServer != null && x.SmtpServer != ""); } else { //这个是公共邮箱配置 publicMailAccount = _userEmailService.GetEmailSettings("PublicSend").GetAwaiter().GetResult().Data; } if (publicMailAccount == null) { throw new Exception($"提取公共邮箱配置失败,请在用户邮箱账号管理增加配置显示名为PublicSend或者配置个人邮箱"); } Logger.Log(NLog.LogLevel.Info, $"提取当前公共邮箱的配置完成,id={publicMailAccount.Id}"); string emailTitle = $"Booking Confirmation : {taskBCInfo.MBL_NO}"; string filePath = string.Empty; var opUserInfo = db.Queryable().Filter(null, true).First(x => x.Id == bookingOrderEntity.OperatorId && x.TenantId == long.Parse(user.TenantId)); if (taskBCInfo.BUSI_TYPE == "BookingAmendment") { emailTitle = $"【变更】Booking Amendment : {taskBCInfo.MBL_NO}"; } //读取邮件模板并填充数据 string emailHtml = string.Empty; if (taskBCInfo.BUSI_TYPE == "BookingAmendment") { emailHtml = GenerateSendEmailHtmlAmendment(taskBCInfo, opUserInfo, user.TenantName).GetAwaiter().GetResult(); } else { emailHtml = GenerateSendEmailHtml(taskBCInfo, opUserInfo, user.TenantName).GetAwaiter().GetResult(); } Logger.Log(NLog.LogLevel.Info, $"生成邮件BODY,结果:{emailHtml}"); TaskFileInfo fileInfo = null; if (bcTaskInfo.TASK_TYPE == TaskBaseTypeEnum.BC.ToString()) { fileInfo = tenantDb.Queryable().Where(a => a.TASK_PKID == taskBCInfo.TASK_ID && a.FILE_CATEGORY.Contains("BC_NOTICE")) .OrderByDescending(a => a.CreateTime).First(); } else if (bcTaskInfo.TASK_TYPE == TaskBaseTypeEnum.BC_MODIFY.ToString()) { //CMA没有变更附件,所以转发邮件时默认用原文件转发 if (bcTaskInfo.CARRIER_CODE.Equals("CMA", StringComparison.OrdinalIgnoreCase)) { fileInfo = tenantDb.Queryable().Where(a => a.TASK_PKID == taskBCInfo.TASK_ID && a.FILE_CATEGORY.Contains("BC_MODIFY")) .OrderByDescending(a => a.CreateTime).First(); } else { if (orderInfo.splitOrMergeFlag == 1) { //这里需要按照箱量重新修改变更附件 } else { fileInfo = tenantDb.Queryable().Where(a => a.TASK_PKID == taskBCInfo.TASK_ID && a.FILE_CATEGORY.Contains("BC_MODIFY_NOTICE")) .OrderByDescending(a => a.CreateTime).First(); } } } if (fileInfo == null) { throw new Exception($"提取变更文件失败,不能发送邮件"); } Logger.Log(NLog.LogLevel.Info, $"获取订舱附件地址,结果:{fileInfo.FILE_PATH}"); string fileRoot = AppSetting.app(new string[] { "FileSettings", "BasePath" }); string? dirAbs; if (string.IsNullOrEmpty(fileRoot)) { dirAbs = Path.Combine(_environment.WebRootPath ?? ""); } else { dirAbs = Path.Combine(fileRoot); } filePath = Path.Combine(dirAbs, fileInfo.FILE_PATH); EmailApiUserDefinedDto emailApiUserDefinedDto = new EmailApiUserDefinedDto { SendTo = toEmail, //CCTo = opEmail, Title = emailTitle, Body = emailHtml, Account = publicMailAccount.MailAccount?.Trim(), Password = publicMailAccount.Password?.Trim(), Server = publicMailAccount.SmtpServer?.Trim(), Port = publicMailAccount.SmtpPort.HasValue ? publicMailAccount.SmtpPort.Value : 465, UseSSL = publicMailAccount.SmtpSSL.HasValue ? publicMailAccount.SmtpSSL.Value : true, Attaches = new List() }; //如果配置了租户参数(AUTO_TRANS_EMAIL_OP_CCTO-自动转发是否默认抄送操作=ENABLE)发送邮件时自动抄送操作 var paramConfig = _configService.GetConfig(CONST_AMENDMENT_DEFAULT_PARAM, long.Parse(user.TenantId), false).GetAwaiter().GetResult()?.Data?.Value; if (paramConfig != null && paramConfig.Equals("ENABLE", StringComparison.OrdinalIgnoreCase)) { emailApiUserDefinedDto.CCTo = opEmail; } Logger.Log(NLog.LogLevel.Info, $"生成请求邮件参数,结果:{JsonConvert.SerializeObject(emailApiUserDefinedDto)}"); //推送邮件 var emailRlt = await PushEmail(emailApiUserDefinedDto, filePath); Logger.Log(NLog.LogLevel.Info, $"推送邮件完成,结果:{JsonConvert.SerializeObject(emailRlt)}"); //var taskBcInfo = _taskBCInfoRepository.AsQueryable().Filter(null, true) //.First(a => a.PK_ID == taskBCInfo.PK_ID); if (emailRlt.Succeeded) { result = true; msg = "成功"; } else { result = false; msg = emailRlt.Message; //new EmailNoticeHelper().SendEmailNotice($"MBLNO={taskBCInfo.MBL_NO} 转发通知邮件失败", $"MBLNO={taskBCInfo.MBL_NO} 转发通知邮件失败,原因:{emailRlt.msg}", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList()); } } catch (Exception ex) { Logger.Log(NLog.LogLevel.Info, $"推送邮件失败,异常:{ex.Message}"); result = false; msg = $"推送邮件失败,{ex.Message}"; //new EmailNoticeHelper().SendEmailNotice($"MBLNO={taskBCInfo.MBL_NO} 转发通知邮件失败", $"MBLNO={taskBCInfo.MBL_NO} 转发通知邮件失败,原因:{ex.Message}", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList()); } if (!result) return DataResult.Failed(msg); return DataResult.Successed(msg); } #endregion #region 通过邮件模板生成HTML /// /// 通过邮件模板生成HTML /// /// BC任务详情 /// 订舱OP详情 /// 当前租户全称 /// 返回生成的HTML public async Task GenerateSendEmailHtml(TaskBCInfo taskBCInfo, SysUser opUserInfo, string tenantName) { string result = string.Empty; /* 1、加载模板文件,读取HTML 2、读取main、conta的tr行,替换业务数据 3、返回HTML的文本信息。 */ try { var basePath = AppSetting.app(new string[] { "EmailTemplate", "BasePath" }); var relativePath = AppSetting.app(new string[] { "EmailTemplate", "RelativePath" }); string dirAbs = string.Empty; if (string.IsNullOrEmpty(basePath)) { dirAbs = Path.Combine(_environment.WebRootPath, relativePath); } else { dirAbs = Path.Combine(basePath, relativePath); } string templatePath = $"{dirAbs}\\BCEmailTemplate.html"; string baseHtml = File.ReadAllText(templatePath); if (string.IsNullOrWhiteSpace(baseHtml)) throw new Exception($"读取邮件模板失败"); if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.UserName)) { baseHtml = baseHtml.Replace("#opname#", opUserInfo.UserName); } else { baseHtml = baseHtml.Replace("#opname#", "操作"); } if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.Email)) { baseHtml = baseHtml.Replace("#opemail#", opUserInfo.Email); } else { baseHtml = baseHtml.Replace("#opemail#", ""); } if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.Phone)) { baseHtml = baseHtml.Replace("#optel#", opUserInfo.Phone); } else if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.Tel)) { baseHtml = baseHtml.Replace("#optel#", opUserInfo.Tel); } else { baseHtml = baseHtml.Replace("#optel#", ""); } HtmlDocument html = new HtmlDocument(); html.LoadHtml(baseHtml); HtmlNode baseTable = html.DocumentNode.SelectNodes("//table[@class='base-table']").FirstOrDefault(); if (baseTable == null) throw new Exception($"读取邮件模板格式错误,定位base-table失败"); var baseTrList = baseTable.SelectNodes(".//tr"); foreach (var baseTr in baseTrList) { var tdList = baseTr.SelectNodes(".//td"); foreach (var baseTd in tdList) { if (baseTd.Attributes["class"].Value == "billno-val") { baseTd.InnerHtml = taskBCInfo.MBL_NO; } else if (baseTd.Attributes["class"].Value == "takebillno-val") { baseTd.InnerHtml = taskBCInfo.MBL_NO; } else if (baseTd.Attributes["class"].Value == "pol-val") { baseTd.InnerHtml = taskBCInfo.PLACERECEIPT; } else if (baseTd.Attributes["class"].Value == "pod-val") { baseTd.InnerHtml = taskBCInfo.PLACEDELIVERY; } else if (baseTd.Attributes["class"].Value == "ctn-val") { baseTd.InnerHtml = taskBCInfo.CTN_STAT; } else if (baseTd.Attributes["class"].Value == "etd-val") { if (taskBCInfo.ETD.HasValue) { baseTd.InnerHtml = taskBCInfo.ETD.Value.ToString("yyyy-MM-dd"); } } else if (baseTd.Attributes["class"].Value == "eta-val") { if (taskBCInfo.ETA.HasValue) { baseTd.InnerHtml = taskBCInfo.ETA.Value.ToString("yyyy-MM-dd"); } } } } var noreplyTr = html.DocumentNode.SelectNodes("//tr[@class='email-noreply']").FirstOrDefault(); if (noreplyTr != null) { var currTd = noreplyTr.SelectNodes(".//td").FirstOrDefault(); if (currTd != null) { var currPList = currTd.SelectNodes(".//p"); foreach (var baseP in currPList) { if (baseP.Attributes["class"].Value == "notice-comp-val") { baseP.InnerHtml = tenantName; } } } } result = html.DocumentNode.OuterHtml; } catch (Exception ex) { Logger.Log(NLog.LogLevel.Info, $"通过邮件模板生成HTML异常,原因={ex.Message}"); throw ex; } return result; } #endregion #region 通过邮件模板生成HTML /// /// 通过邮件模板生成HTML /// /// BC任务详情 /// 订舱OP详情 /// 当前租户全称 /// 返回生成的HTML public async Task GenerateSendEmailHtmlAmendment(TaskBCInfo taskBCInfo, SysUser opUserInfo, string tenantName) { string result = string.Empty; /* 1、加载模板文件,读取HTML 2、读取main、conta的tr行,替换业务数据 3、返回HTML的文本信息。 */ try { var basePath = AppSetting.app(new string[] { "EmailTemplate", "BasePath" }); var relativePath = AppSetting.app(new string[] { "EmailTemplate", "RelativePath" }); string dirAbs = string.Empty; if (string.IsNullOrEmpty(basePath)) { dirAbs = Path.Combine(_environment.WebRootPath ?? "", relativePath); } else { dirAbs = Path.Combine(basePath, relativePath); } string templatePath = $"{dirAbs}\\BCModifyEmailTemplate.html"; string baseHtml = File.ReadAllText(templatePath); if (string.IsNullOrWhiteSpace(baseHtml)) throw new Exception($"读取邮件模板失败"); List compareList = GetCompareResult(taskBCInfo.TASK_ID).GetAwaiter().GetResult().Data; if (taskBCInfo.CARRIERID.Equals("CMA", StringComparison.OrdinalIgnoreCase)) { compareList = new List(); } else { if (compareList == null || compareList.Count == 0) throw new Exception($"读取变更数据失败,没有差异数据"); } if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.UserName)) { baseHtml = baseHtml.Replace("#opname#", opUserInfo.UserName); } else { baseHtml = baseHtml.Replace("#opname#", "操作"); } if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.Email)) { baseHtml = baseHtml.Replace("#opemail#", opUserInfo.Email); } else { baseHtml = baseHtml.Replace("#opemail#", ""); } if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.Phone)) { baseHtml = baseHtml.Replace("#optel#", opUserInfo.Phone); } else if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.Tel)) { baseHtml = baseHtml.Replace("#optel#", opUserInfo.Tel); } else { baseHtml = baseHtml.Replace("#optel#", ""); } HtmlDocument html = new HtmlDocument(); html.LoadHtml(baseHtml); HtmlNode baseTable = html.DocumentNode.SelectNodes("//table[@class='base-table']").FirstOrDefault(); if (baseTable == null) throw new Exception($"读取邮件模板格式错误,定位base-table失败"); var baseTrList = baseTable.SelectNodes(".//tr"); foreach (var baseTr in baseTrList) { var tdList = baseTr.SelectNodes(".//td"); foreach (var baseTd in tdList) { if (baseTd.Attributes["class"].Value == "billno-val") { baseTd.InnerHtml = taskBCInfo.MBL_NO; } else if (baseTd.Attributes["class"].Value == "takebillno-val") { baseTd.InnerHtml = taskBCInfo.MBL_NO; } else if (baseTd.Attributes["class"].Value == "pol-val") { baseTd.InnerHtml = taskBCInfo.PLACERECEIPT; } else if (baseTd.Attributes["class"].Value == "pod-val") { baseTd.InnerHtml = taskBCInfo.PLACEDELIVERY; } else if (baseTd.Attributes["class"].Value == "ctn-val") { baseTd.InnerHtml = taskBCInfo.CTN_STAT; } /* else if (baseTd.Attributes["class"].Value == "etd-val") { if (taskBCInfo.ETD.HasValue) { baseTd.InnerHtml = taskBCInfo.ETD.Value.ToString("yyyy-MM-dd"); } } else if (baseTd.Attributes["class"].Value == "eta-val") { if (taskBCInfo.ETA.HasValue) { baseTd.InnerHtml = taskBCInfo.ETA.Value.ToString("yyyy-MM-dd"); } }*/ } } bool isOnlyChangeOne = false; List nameList = new List(); //船名 if (compareList.Any(x => x.FieldCode.Equals("vessel", StringComparison.OrdinalIgnoreCase))) { var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("vessel", StringComparison.OrdinalIgnoreCase)).TargetVal; baseTable.AppendChild(HtmlNode.CreateNode("船名 变更为:" + name + "")); nameList.Add("船名"); } //航次号 if (compareList.Any(x => x.FieldCode.Equals("voyNo", StringComparison.OrdinalIgnoreCase))) { var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("voyNo", StringComparison.OrdinalIgnoreCase)).TargetVal; baseTable.AppendChild(HtmlNode.CreateNode("航次号 变更为:" + name + "")); nameList.Add("航次号"); } //ETD if (compareList.Any(x => x.FieldCode.Equals("ETD", StringComparison.OrdinalIgnoreCase))) { var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("ETD", StringComparison.OrdinalIgnoreCase)).TargetVal; baseTable.AppendChild(HtmlNode.CreateNode("ETD 变更为:" + name + "")); nameList.Add("ETD"); } //ETA if (compareList.Any(x => x.FieldCode.Equals("ETA", StringComparison.OrdinalIgnoreCase))) { var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("ETA", StringComparison.OrdinalIgnoreCase)).TargetVal; baseTable.AppendChild(HtmlNode.CreateNode("ETA 变更为:" + name + "")); nameList.Add("ETA"); } //CustomSICutDate 客户样单截止日期 if (compareList.Any(x => x.FieldCode.Equals("CustomSICutDate", StringComparison.OrdinalIgnoreCase))) { var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("CustomSICutDate", StringComparison.OrdinalIgnoreCase)).TargetVal; baseTable.AppendChild(HtmlNode.CreateNode("样单截止日期 变更为:" + name + "")); nameList.Add("样单截止日期"); } //CYCutoffTime 截港时间 if (compareList.Any(x => x.FieldCode.Equals("CYCutoffTime", StringComparison.OrdinalIgnoreCase))) { var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("CYCutoffTime", StringComparison.OrdinalIgnoreCase)).TargetVal; baseTable.AppendChild(HtmlNode.CreateNode("截港时间 变更为:" + name + "")); nameList.Add("截港时间"); } //ManifestCutDate 舱单截止时间 if (compareList.Any(x => x.FieldCode.Equals("ManifestCutDate", StringComparison.OrdinalIgnoreCase))) { var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("ManifestCutDate", StringComparison.OrdinalIgnoreCase)).TargetVal; baseTable.AppendChild(HtmlNode.CreateNode("舱单截止时间 变更为:" + name + "")); nameList.Add("舱单截止时间"); } //VGMCutoffTime MDGF提交截止时间 if (compareList.Any(x => x.FieldCode.Equals("VGMCutoffTime", StringComparison.OrdinalIgnoreCase))) { var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("VGMCutoffTime", StringComparison.OrdinalIgnoreCase)).TargetVal; baseTable.AppendChild(HtmlNode.CreateNode("截VGM时间 变更为:" + name + "")); nameList.Add("截VGM时间"); } //MDGFCutDate MDGF提交截止时间 if (compareList.Any(x => x.FieldCode.Equals("MDGFCutDate", StringComparison.OrdinalIgnoreCase))) { var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("MDGFCutDate", StringComparison.OrdinalIgnoreCase)).TargetVal; baseTable.AppendChild(HtmlNode.CreateNode("MDGF提交截止时间 变更为:" + name + "")); nameList.Add("MDGF提交截止时间"); } //PlaceReceipt 收货地 if (compareList.Any(x => x.FieldCode.Equals("PlaceReceipt", StringComparison.OrdinalIgnoreCase))) { var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("PlaceReceipt", StringComparison.OrdinalIgnoreCase)).TargetVal; baseTable.AppendChild(HtmlNode.CreateNode("收货地 变更为:" + name + "")); nameList.Add("收货地"); } //PlaceDelivery 交货地 if (compareList.Any(x => x.FieldCode.Equals("PlaceDelivery", StringComparison.OrdinalIgnoreCase))) { var name = compareList.FirstOrDefault(x => x.FieldCode.Equals("PlaceDelivery", StringComparison.OrdinalIgnoreCase)).TargetVal; baseTable.AppendChild(HtmlNode.CreateNode("交货地 变更为:" + name + "")); nameList.Add("交货地"); } var noreplyTr = html.DocumentNode.SelectNodes("//tr[@class='email-noreply']").FirstOrDefault(); if (noreplyTr != null) { var currTd = noreplyTr.SelectNodes(".//td").FirstOrDefault(); if (currTd != null) { var currPList = currTd.SelectNodes(".//p"); foreach (var baseP in currPList) { if (baseP.Attributes["class"].Value == "notice-comp-val") { baseP.InnerHtml = tenantName; } else if (baseP.Attributes["class"].Value == "dynamic-val") { if (nameList.Count > 0) { baseP.InnerHtml = $"请注意{(string.Join("、", nameList.ToArray()))} 变更,请您按相关的更新安排操作,谢谢!"; } } } } } result = html.DocumentNode.OuterHtml; } catch (Exception ex) { Logger.Log(NLog.LogLevel.Info, $"通过邮件模板生成HTML异常,原因={ex.Message}"); throw ex; } return result; } #endregion #region 推送邮件 /// /// 推送邮件 /// /// 自定义邮件详情 /// 文件路径 /// 返回回执 private async Task PushEmail(EmailApiUserDefinedDto emailApiUserDefinedDto, string filePath) { List emailList = new List(); bool result = false; string msg = string.Empty; var emailUrl = db.Queryable().Filter(null, true).Where(x => x.Code == "email_api_url" && x.TenantId == 1288018625843826688).First().Value; if (emailUrl == null) throw new Exception("字典未配置 url_set->email_api_url 请联系管理员"); System.IO.FileStream file = new System.IO.FileStream(filePath, FileMode.Open, FileAccess.Read); int SplitSize = 5242880;//5M分片长度 int index = 1; //序号 第几片 long StartPosition = 5242880 * (index - 1); long lastLens = file.Length - StartPosition;//真不知道怎么起命了,就这样吧 if (lastLens < 5242880) { SplitSize = (int)lastLens; } byte[] heByte = new byte[SplitSize]; file.Seek(StartPosition, SeekOrigin.Begin); //第一个参数是 起始位置 file.Read(heByte, 0, SplitSize); //第三个参数是 读取长度(剩余长度) file.Close(); string base64Str = Convert.ToBase64String(heByte); emailApiUserDefinedDto.Attaches.Add(new AttachesInfo { AttachName = Path.GetFileName(filePath).Replace("_MODIFY", ""), AttachContent = base64Str }); emailList.Add(emailApiUserDefinedDto); //string strJoin = System.IO.File.ReadAllText(filePath); DateTime bDate = DateTime.Now; string res = string.Empty; try { var jsonBody = JsonConvert.SerializeObject(emailList); res = RequestHelper.Post(jsonBody, emailUrl); //res = await emailUrl.SetBody(emailList, "application/json").PostAsync(); } catch (Exception ex) { Logger.Log(NLog.LogLevel.Info, $"发送邮件异常:{ex.Message}"); } DateTime eDate = DateTime.Now; TimeSpan ts = eDate.Subtract(bDate); var timeDiff = ts.TotalMilliseconds; Logger.Log(NLog.LogLevel.Info, $"邮件上传完成 上传文件大小:{heByte.Length} 用时:{timeDiff}ms.,"); Logger.Log(NLog.LogLevel.Info, $"发送邮件返回:{res}"); if (!string.IsNullOrWhiteSpace(res)) { //var userResult = await res.Content.ReadAsStringAsync(); var respObj = JsonConvert.DeserializeAnonymousType(res, new { Success = false, Message = string.Empty, Code = -9999, }); result = respObj.Success; msg = respObj.Message; } return DataResult.Successed(""); } #endregion #region 发送邮件 /// /// 发送邮件 /// /// BC任务主键 /// 是否默认使用用户个人邮箱发送 /// 返回回执 public async Task SendEmail(string taskPKId, bool usePersonalEmailSend = false) { return null; } #endregion #region 同步舱位变更 /// /// 同步舱位变更 /// /// BC任务主键 /// 返回回执 public async Task SyncBookingSlotChange(long taskPKId) { /* * Amendment 1、需要先匹配舱位,如果没有舱位不能执行后续的步骤。 2、判断当前租户是否配置了自动转发客户邮件。 3、如果配置了自动转发,需要判断订舱是否已有,没有不能执行自动转发。 4、转发需要提取差异数据,如果没有差异数据也不能转发邮件。 5、默认通过钉钉通知操作收到了变更通知。 * Cancellation 1、需要先匹配舱位,如果没有舱位不能执行后续的步骤。 2、判断当前租户是否配置了自动转发客户邮件。 3、如果配置了自动转发,需要判断订舱是否已有,没有不能执行自动转发。 4、转发需要提取差异数据,如果没有差异数据也不能转发邮件。 */ try { var tenantDb = saasDbService.GetBizDbScopeById(user.TenantId); //任务不考虑OrgId,这里去掉 tenantDb.QueryFilter.Clear(); var queryList = await tenantDb.Queryable() .InnerJoin((a, b) => a.Id == b.TASK_ID) .Where((a, b) => a.Id == taskPKId) .Select((a, b) => new { Base = a, BC = b }) .ToListAsync(); //任务主键{taskPkId}无法获取业务信息 if (queryList.Count == 0) throw new Exception(string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.TaskBaseInfoFromTaskIdNull)), taskPKId)); var paramConfig = _configService.GetConfig(CONST_AMENDMENT_DEFAULT_PARAM, long.Parse(user.TenantId), false).GetAwaiter().GetResult()?.Data?.Value; Logger.Log(NLog.LogLevel.Info, $"taskPKId={taskPKId} 判断是否默认转发邮件 paramConfig={paramConfig}"); var taskBcInfo = queryList.FirstOrDefault().BC; var taskBaseInfo = queryList.FirstOrDefault().Base; var orderInfo = seaExportService.Value.SearchOrderInfo(taskBcInfo.MBL_NO).GetAwaiter().GetResult().Data; if (orderInfo != null && orderInfo.currOrder != null) { //更新订舱相关内容 if (!string.IsNullOrWhiteSpace(paramConfig) && paramConfig.Equals("ENABLE", StringComparison.OrdinalIgnoreCase)) { //如果是拆票需要按照分票明细单个发送邮件 if (orderInfo.splitOrMergeFlag == 1) { var splitOrderIds = new List(); splitOrderIds.Add(orderInfo.currOrder.Id); splitOrderIds.Append(orderInfo.otherOrderList.Select(b => b.Id).ToList()); var bookList = tenantDb.Queryable().Where(b => splitOrderIds.Contains(b.Id)).ToList(); foreach (var seaExportId in splitOrderIds) { var currSeaExport = bookList.FirstOrDefault(b => b.Id == seaExportId); bool saveBookingId = currSeaExport.Id == orderInfo.currOrder.Id; await GenerateSendEmail(taskBcInfo, taskBaseInfo, orderInfo, currSeaExport, saveBookingId); } } else { var currSeaExport = tenantDb.Queryable().First(b => b.Id == orderInfo.currOrder.Id); //普通和合票都按单票处理 await GenerateSendEmail(taskBcInfo, taskBaseInfo, orderInfo, currSeaExport, true); } } else { //这里没有取到订舱信息,终止,并发出邮件通知 Logger.Log(NLog.LogLevel.Info, $"taskPKId={taskPKId} mblno={taskBcInfo.MBL_NO} 当前租户未配置自动转发邮件"); } } else { //这里没有取到订舱信息,终止,并发出邮件通知 Logger.Log(NLog.LogLevel.Info, $"taskPKId={taskPKId} mblno={taskBcInfo.MBL_NO} 没有找到订单"); } } catch (Exception ex) { Logger.Log(NLog.LogLevel.Info, $"执行SyncBookingSlotChange时,异常{ex.Message}"); return DataResult.Failed($"执行SyncBookingSlotChange时,异常{ex.Message}"); } return DataResult.Successed(MultiLanguageConst.DataCreateSuccess); } #endregion /// /// 同步舱位变更(任务自动) /// /// 上下文 /// 返回回执 public async Task SyncBookingSlotChangeTask(TaskFlowDataContext dataContext) { var taskBaseInfo = dataContext.Get(TaskFlowDataNameConst.TaskBCInfo); if (taskBaseInfo == null) { logger.LogInformation($"执行ApiReceiveTask时,未获取到{TaskFlowDataNameConst.TaskBaseInfo}"); } return await SyncBookingSlotChange(taskBaseInfo.Id); } #region 读取BC详情 /// /// 读取BC详情 /// /// 船公司代码 /// BC文件 /// 返回解析详情 public async Task> GetReadBC(string carrierCode, IFormFile file) { DateTime nowDate = DateTime.Now; string msg = string.Empty; var messageInfo = new { Head = new { GID = SnowFlakeSingle.Instance.NextId().ToString(), MessageType = "BOOKING_GAMENDMENT", SenderId = AppSetting.app(new string[] { "ExcuteRuleService", "RulesEngineSender" }), SenderName = AppSetting.app(new string[] { "ExcuteRuleService", "RulesEngineSenderName" }), ReceiverId = "RulesEngine", ReceiverName = "大简云规则引擎", Version = "1.0", RequestDate = nowDate.ToString("yyyy-MM-dd HH:mm:ss"), RequestAction = "ReadFile", }, Main = new { TenantId = user.TenantId.ToString() } }; using (var httpClient = new HttpClient()) { try { using (var reduceAttach = new MultipartFormDataContent()) { var dataContent = new ByteArrayContent(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(messageInfo))); dataContent.Headers.ContentDisposition = new ContentDispositionHeaderValue($"form-data") { Name = "jsonData" }; reduceAttach.Add(dataContent); #region 文件参数 if (file != null) { var Content = new ByteArrayContent(file.ToByteArray()); Content.Headers.Add("Content-Type", "application/json"); reduceAttach.Add(Content, "file", HttpUtility.UrlEncode(file.FileName)); } #endregion var url = string.Empty; if (carrierCode.Equals("MSK", StringComparison.OrdinalIgnoreCase)) url = bcMSKReadUrl; if (carrierCode.Equals("CMA", StringComparison.OrdinalIgnoreCase)) url = bcCMAReadUrl; //请求 var response = httpClient.PostAsync(url, reduceAttach).Result; string result = response.Content.ReadAsStringAsync().Result; var model = JsonConvert.DeserializeObject(result); if (model != null && model.succ) { var bcInfo = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(model.extra)); return DataResult.Success(bcInfo); } else { msg = model.msg; } } } catch (Exception ex) { Logger.Log(NLog.LogLevel.Info, "请求BC读取详情异常,原因:{error}", ex.Message); msg = $"请求BC读取详情异常,原因:{ex.Message}"; } return DataResult.Failed(msg); } } #endregion #region 读取BC擦写后文件流 /// /// 读取BC擦写后文件流 /// /// 船公司代码 /// BC文件 /// 返回解析详情 public async Task> GetModifyBCFile(string carrierCode, IFormFile file) { byte[] bytes; var result = string.Empty; using (var httpClient = new HttpClient()) { try { using (var reduceAttach = new MultipartFormDataContent()) { var dataContent = new ByteArrayContent(Encoding.UTF8.GetBytes("")); dataContent.Headers.ContentDisposition = new ContentDispositionHeaderValue($"form-data") { Name = "jsonData" }; reduceAttach.Add(dataContent); #region 文件参数 if (file != null) { var Content = new ByteArrayContent(file.ToByteArray()); Content.Headers.Add("Content-Type", "application/json"); reduceAttach.Add(Content, "file", HttpUtility.UrlEncode(file.FileName)); } #endregion //请求 var response = httpClient.PostAsync(bcMSKModifyFileUrl, reduceAttach).Result; bytes = response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult(); return DataResult.Success(bytes); } } catch (Exception ex) { Logger.Log(NLog.LogLevel.Info, "请求自动变更文件内容异常,原因:{error}", ex.Message); result = $"请求自动变更文件内容异常,原因:{ex.Message}"; } } return DataResult.Failed(result); } #endregion /// /// 上传BC文件并触发执行BC任务 /// /// 船公司代码 /// 任务主键 /// BC文件 public async Task UploadBcThenRunTask(string carrierCode, long taskId, IFormFile file) { //return DataResult.Failed("开发中...", MultiLanguageConst.Operation_Failed); /* 解析BC文件,生成BC任务 生成Base任务 生成擦写后的文件 保存文件和擦写后的文件到硬盘 保存任务对象 文件信息存库 触发BC任务 */ string batchNo = Guid.NewGuid().ToString(); logger.LogInformation("批次={no} 接收到上传BC请求", batchNo); List supportCarrier = ["MSK", "CMA"]; if (!supportCarrier.Contains(carrierCode)) { return DataResult.Failed(string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BCParserNotSupportedCarrier)), carrierCode)); } // 解析BC附件 var parserBcInfo = await GetReadBC(carrierCode, file); if (!parserBcInfo.Succeeded || parserBcInfo.Data == null) { return DataResult.Failed(parserBcInfo.Message); } // 解析出BC任务 var taskBcInfo = parserBcInfo.Data.Adapt(); taskBcInfo.Id = SnowFlakeSingle.Instance.NextId(); var allPortCodeList = (await _codePortService.GetAllList()).Data ?? []; // 解析收货地,得到装货港名称及五字码 if (string.IsNullOrWhiteSpace(taskBcInfo.PLACERECEIPT)) { logger.LogInformation("手动上传BC后,通过收货地城市名称匹配港口信息时,收货地为空"); } else { var portEnName = taskBcInfo.PLACERECEIPT.Split(',')[0].Trim(); //这里CMA的收货地全称放在了括号里面 if (taskBcInfo.PLACERECEIPT.Equals("CMA", StringComparison.OrdinalIgnoreCase)) { taskBcInfo.PLACERECEIPT = taskBcInfo.PLACERECEIPT.Replace("(", "(").Replace(")", ")"); if (taskBcInfo.PLACERECEIPT.IndexOf("(") >= 0) { string? currStr = Regex.Match(taskBcInfo.PLACERECEIPT, "(?<=\\().*(?=\\))").Value?.Trim(); if (!string.IsNullOrEmpty(currStr)) { portEnName = currStr.Split(',')[0].Trim(); } } } var portInfo = await PlaceDeliveryToPort(portEnName, allPortCodeList, () => _mappingPortService.GetAllList()); if (!portInfo.Succeeded || portInfo.Data == null) { logger.LogInformation($"手动上传BC后,通过收货地城市名称未匹配到港口信息,BC对象主键:{taskBcInfo.Id}"); } else { taskBcInfo.PORTLOAD = portInfo.Data.PortName; taskBcInfo.PORTLOAD_CODE = portInfo.Data.EdiCode; taskBcInfo.PORTLOAD_ID = portInfo.Data.Id; } } // 解析交货地,得到为卸货港名称及五字码, 以及国家信息 if (string.IsNullOrWhiteSpace(taskBcInfo.PLACEDELIVERY)) { logger.LogInformation("手动上传BC后,通过交货地城市名称匹配港口信息时,交货地为空"); } else { var portEnName = taskBcInfo.PLACEDELIVERY.Split(',')[0].Trim(); //这里CMA的收货地全称放在了括号里面 if (taskBcInfo.CARRIER?.Equals("CMA", StringComparison.OrdinalIgnoreCase) == true) { taskBcInfo.PLACEDELIVERY = taskBcInfo.PLACEDELIVERY.Replace("(", "(").Replace(")", ")"); if (taskBcInfo.PLACEDELIVERY.IndexOf("(") >= 0) { string? currStr = Regex.Match(taskBcInfo.PLACEDELIVERY, "(?<=\\().*(?=\\))")?.Value?.Trim(); if (!string.IsNullOrEmpty(currStr)) { portEnName = currStr.Split(',')[0].Trim(); } } } var portInfo = await PlaceDeliveryToPort(portEnName, allPortCodeList, () => _mappingPortService.GetAllList()); if (!portInfo.Succeeded || portInfo.Data == null) { logger.LogInformation($"手动上传BC后,通过交货地城市名称未匹配到港口信息,BC对象主键:{taskBcInfo.Id}"); } else { taskBcInfo.PORTDISCHARGE = portInfo.Data.PortName; taskBcInfo.PORTDISCHARGE_CODE = portInfo.Data.EdiCode; taskBcInfo.PORTDISCHARGE_ID = portInfo.Data.Id; var allCountryCodeList = (await _codeCountryService.GetAllList()).Data ?? []; var countryInfo = allCountryCodeList.FirstOrDefault(p => p.Id == portInfo.Data.CountryId || p.CountryName == portInfo.Data.CountryName); if (countryInfo != null) { taskBcInfo.PORTDISCHARGE_COUNTRY = countryInfo.CountryEnName; taskBcInfo.PORTDISCHARGE_COUNTRY_CODE = countryInfo.CountryCode; taskBcInfo.PORTDISCHARGE_COUNTRY_ID = countryInfo.Id; } } } // 获取待收BC任务中的订单主键 var waitBcTask = await tenantDb.Queryable() .Where(x => x.Id == taskId) .Select(x => new { x.Id, x.OUT_BS_NO, x.STATUS }).FirstAsync(); if (waitBcTask == null) { return DataResult.Failed("未执行:当前任务不存在,请刷新页面"); } //if (waitBcTask.STATUS == TaskStatusEnum.Complete.ToString()) //{ // return DataResult.Failed("未执行:当前任务状态为已完成,请重新查询"); //} if (waitBcTask.OUT_BS_NO == null) { return DataResult.Failed("未执行:当前任务的业务主键为null"); } taskBcInfo.BOOKING_ORDER_ID = waitBcTask.OUT_BS_NO; // 获取订单信息 var seaExport = await BcMatchSeaExport(taskBcInfo); if (!seaExport.Succeeded || seaExport.Data == null) { return DataResult.Failed("未执行:" + seaExport.Message); } //if (seaExport.Data.MBLNO != taskBcInfo.MBL_NO && seaExport.Data.BookingNo != taskBcInfo.MBL_NO) //{ // return DataResult.Failed(string.Format(MultiLanguageConst.GetDescription(MultiLanguageConst.BCMblnoNotMatchSeaexport), seaExport.Data.MBLNO, taskBcInfo.MBL_NO)); //} TaskFlowDataContext dataContext = new((TaskFlowDataNameConst.Business, seaExport.Data)); // 构建Base任务 var taskInfo = new TaskBaseInfo { Id = SnowFlakeSingle.Instance.NextId(), STATUS = TaskStatusEnum.Create.ToString(), STATUS_NAME = TaskStatusEnum.Create.EnumDescription(), IS_EXCEPT = 0, IS_COMPLETE = 0, MBL_NO = taskBcInfo.MBL_NO, TASK_TYPE = TaskBaseTypeEnum.BC.ToString(), TASK_TYPE_NAME = TaskBaseTypeEnum.BC.EnumDescription(), //TASK_BASE_TYPE = info.Main.TaskType.ToString(), //CARRIER_ID = info.Main.CarrierId?.Trim(), //IS_PUBLIC = string.IsNullOrWhiteSpace(info.Main.TaskUserId) ? 1 : 0, IS_PUBLIC = 1, //OUT_BUSI_NO = $"{info.Head.SenderId}_{info.Head.GID}", TASK_TITLE = $"手动上传-BC {taskBcInfo.VESSEL}/{taskBcInfo.VOYNO} ETD:{taskBcInfo.ETD} BLNo:{taskBcInfo.MBL_NO}", //TASK_DESP = info.Main.TaskDesp, TASK_SOURCE = "ManualUploadBC", TASK_SOURCE_NAME = "手动上传BC", VESSEL_VOYNO = $"{taskBcInfo.VESSEL}/{taskBcInfo.VOYNO}"?.Trim(), CONTA_INFO = taskBcInfo.CTN_STAT, TASK_REQ_USERNAME = user.UserName, YARD_NAME = taskBcInfo.YARD, ETD = taskBcInfo.ETD, BATCH_STATIC = batchNo, //DJYUserId = info.Head.DJYUserId, CreateTime = DateTime.Now, CreateBy = long.Parse(user.UserId), CreateUserName = user.UserName, OUT_BS_NO = seaExport.Data.Id, BOOK_ORDER_NO = seaExport.Data.Id.ToString(), CARRIER_ID = seaExport.Data.CarrierId, CARRIER_NAME = seaExport.Data.Carrier, CUSTOMER_ID = seaExport.Data.CustomerId, CUSTOMER_NAME = seaExport.Data.CustomerName, }; taskInfo.TASK_DESP = taskInfo.TASK_TITLE; taskBcInfo.TASK_ID = taskInfo.Id; // 构建箱信息 var ctnCodeList = (await _codeCtnService.GetAllList()).Data ?? new List(); var ctnList = parserBcInfo.Data.CtnList?.Select(ctn => { var bcCtnInfo = ctn.Adapt(); bcCtnInfo.Id = SnowFlakeSingle.Instance.NextId(); bcCtnInfo.P_ID = taskBcInfo.Id; if (string.IsNullOrEmpty(bcCtnInfo.CTNCODE)) { var ctnCode = ctnCodeList.FirstOrDefault(a => !string.IsNullOrWhiteSpace(a.CtnName) && a.CtnName.Equals(bcCtnInfo.CTNALL, StringComparison.OrdinalIgnoreCase)); bcCtnInfo.CTNCODE = ctnCode != null ? ctnCode.EdiCode : ""; } bcCtnInfo.CreateBy = taskInfo.CreateBy; bcCtnInfo.CreateTime = taskInfo.CreateTime; return bcCtnInfo; }).ToList(); // 获取任务分配关系 TaskFlowDataContext allotDataContext = new TaskFlowDataContext((TaskFlowDataNameConst.Business, seaExport.Data)); var allotUserList = await taskAllocationService.Value.GetAllotUserBySeaExportId(TaskBaseTypeEnum.BC, seaExport.Data.Id, allotDataContext); List allocationList = []; if (allotUserList.Succeeded && allotUserList.Data?.Count > 0) { allocationList = allotUserList.Data.Select(x => new TaskBaseAllocation { TaskId = taskInfo.Id, UserId = x.RecvUserId, UserName = x.RecvUserName, Status = TaskStatusEnum.Complete.ToString(), StatusName = TaskStatusEnum.Complete.EnumDescription(), StatusTime = DateTime.Now }).ToList(); taskInfo.IS_PUBLIC = 0; } // 保存BC文件 string bcFileName = file.FileName; var bcFileBytes = file.ToByteArray(); var bcNoExtensionFileName = Path.GetFileNameWithoutExtension(file.FileName); var bcFileFullName = await SaveFile(taskInfo.Id.ToString()!, bcFileBytes, batchNo, bcNoExtensionFileName, GetFileType(file.FileName), "bcfiles"); // 构建BC文件索引信息 var bcFileInfo = new TaskFileInfo { Id = SnowFlakeSingle.Instance.NextId(), TASK_PKID = taskInfo.Id, CreateBy = taskInfo.CreateBy, CreateTime = taskInfo.CreateTime, FILE_PATH = bcFileFullName, FILE_NAME = bcFileName, FILE_TYPE = Path.GetExtension(bcFileName).ToLower(), FILE_CATEGORY = TaskFileCategoryEnum.BC.ToString(), FILE_CATEGORY_NAME = TaskFileCategoryEnum.BC.EnumDescription() }; // 保存BC擦写文件 string bcNotifyFileName = $"{bcNoExtensionFileName}_MODIFY{Path.GetExtension(bcFileName).ToLower()}"; var bcNotifyFileBytes = await GetModifyBCFile(carrierCode, file); if (!bcNotifyFileBytes.Succeeded || bcNotifyFileBytes.Data == null || bcNotifyFileBytes.Data.Length == 0) { return DataResult.Failed(bcNotifyFileBytes.Message); } var bcNotifyNoExtensionFileName = Path.GetFileNameWithoutExtension(bcNotifyFileName); var bcNotifyFileFullName = await SaveFile(taskInfo.Id.ToString()!, bcNotifyFileBytes.Data, batchNo, bcNotifyNoExtensionFileName, GetFileType(bcNotifyFileName), "bcnoticefiles"); // 构建BC文件擦写文件索引信息 var bcNotifyFileInfo = new TaskFileInfo { Id = SnowFlakeSingle.Instance.NextId(), TASK_PKID = taskInfo.Id, CreateBy = taskInfo.CreateBy, CreateTime = taskInfo.CreateTime, FILE_PATH = bcNotifyFileFullName, FILE_NAME = bcNotifyFileName, FILE_TYPE = Path.GetExtension(bcNotifyFileName).ToLower(), FILE_CATEGORY = TaskFileCategoryEnum.BC.ToString(), FILE_CATEGORY_NAME = TaskFileCategoryEnum.BC.EnumDescription() }; // 用于挂载到订单的文件信息 var orderBcFileInfo = new OpFile { FileName = bcFileInfo.FILE_NAME, FilePath = bcFileInfo.FILE_PATH, TypeCode = bcFileInfo.FILE_CATEGORY, TypeName = bcFileInfo.FILE_CATEGORY_NAME, LinkId = seaExport.Data.Id, FileSize = bcFileBytes.Length, FileType = Path.GetExtension(bcFileName), Extension = Path.GetExtension(bcFileName), OrgId = user.OrgId }; var orderBcNotifyFileInfo = new OpFile { FileName = bcNotifyFileInfo.FILE_NAME, FilePath = bcNotifyFileInfo.FILE_PATH, TypeCode = bcNotifyFileInfo.FILE_CATEGORY, TypeName = bcNotifyFileInfo.FILE_CATEGORY_NAME, LinkId = seaExport.Data.Id, FileSize = bcNotifyFileBytes.Data.Length, FileType = Path.GetExtension(bcNotifyFileName), Extension = Path.GetExtension(bcNotifyFileName), OrgId = user.OrgId }; // 存库 try { await tenantDb.Ado.BeginTranAsync(); await tenantDb.Insertable(taskInfo).ExecuteCommandAsync(); await tenantDb.Insertable(taskBcInfo).ExecuteCommandAsync(); await tenantDb.Insertable(ctnList).ExecuteCommandAsync(); await tenantDb.Insertable(allocationList).ExecuteCommandAsync(); await tenantDb.Insertable(bcFileInfo).ExecuteCommandAsync(); await tenantDb.Insertable(bcNotifyFileInfo).ExecuteCommandAsync(); await tenantDb.Insertable(orderBcFileInfo).ExecuteCommandAsync(); await tenantDb.Insertable(orderBcNotifyFileInfo).ExecuteCommandAsync(); await tenantDb.Ado.CommitTranAsync(); } catch (Exception ex) { await tenantDb.Ado.RollbackTranAsync(); logger.LogError(ex, "手动上传BC时,在存库的过程中发生异常"); throw; } var taskService = serviceProvider.GetRequiredService(); var rlt = await taskService.SetTaskStatusAsync(new TaskUpdateRequest() { AutoCreateNext = false, BusinessId = (long)waitBcTask.OUT_BS_NO, BusinessType = BusinessType.OceanShippingExport, TaskStatus = TaskStatusEnum.Complete, TaskTypeName = TaskBaseTypeEnum.WAIT_BC.ToString() }); if (!rlt.Succeeded) { logger.LogError("手动上传BC:设置收BC任务完成时失败,message:" + rlt.Message); } // 触发BC任务,执行自动化操作 TaskManageOrderBCInfo messageBcInfo = taskBcInfo.Adapt(); messageBcInfo.CtnList = ctnList.Adapt>(); TaskManageOrderMessageInfo messageInfo = new TaskManageOrderMessageInfo() { Main = new TaskManageOrderMessageMainInfo() { BCInfo = messageBcInfo, TaskType = TaskBaseTypeEnum.BC, TaskBatchNo = batchNo, }, Head = new TaskManageOrderMessageHeadInfo() { RequestAction = "add" } }; // 固定 dataContext.Set(TaskFlowDataNameConst.TaskBaseInfo, taskInfo); dataContext.Set(TaskFlowDataNameConst.TaskManageOrderMessageInfo, messageInfo); // BC任务特有 dataContext.Set(TaskFlowDataNameConst.BCFileInfo, new DynameFileInfo { FileBytes = file.ToByteArray(), FileName = file.FileName }); dataContext.Set(TaskFlowDataNameConst.BCNotifyFileInfo, new DynameFileInfo { FileBytes = bcFileBytes, FileName = bcNotifyFileName }); dataContext.Set(TaskFlowDataNameConst.TaskBCInfo, taskBcInfo); dataContext.Set(TaskFlowDataNameConst.TaskBCCtnList, ctnList); TaskFlowRuner taskFlow = new TaskFlowRuner(tenantDb, serviceProvider); await taskFlow.Run(TaskBaseTypeEnum.BC, taskInfo.Id, dataContext); return DataResult.Successed("上传完成,请查看相关任务状态", MultiLanguageConst.UploadBcThenRunTaskSuccess); } /// /// 结束BC任务并创建下一步任务 /// /// 数据上下文 public async Task BCTaskCompleteThenNext(TaskFlowDataContext dataContext) { // 这里获取到的是获取BC任务的基础任务 var taskBaseInfo = dataContext.Get(TaskFlowDataNameConst.TaskBaseInfo); if (taskBaseInfo == null) { return DataResult.Failed("未获取到BC任务对象TaskBaseInfo"); } await SetTaskStatus(taskBaseInfo, TaskStatusEnum.Complete, DateTime.Now); if (taskBaseInfo.OUT_BS_NO is null or 0) { logger.LogError("BCTaskCompleteThenNext:BC任务对象TaskBaseInfo的OUT_BS_NO为空或0"); return DataResult.Failed("任务对象的业务主键为空"); } else { var req = new TaskCreationRequest { BusinessId = (long)taskBaseInfo.OUT_BS_NO, BusinessType = BusinessType.OceanShippingExport, TaskTypeName = TaskBaseTypeEnum.WAIT_SPACE_RELEASE.ToString(), }; CompareResultInfo? compareResult = dataContext.Get(TaskFlowDataNameConst.BcCompareWithSeaExportResult); if (compareResult == null) { req.TaskDescription = $"未放舱原因:BC对比结果不存在"; } else { if (compareResult.IsExistsDiff) { StringBuilder builder = new(); builder.AppendLine("未放舱原因:BC对比订单后存在差异,差异项:"); foreach (var item in compareResult.ShowDetailList) { builder.AppendLine($"{item.FieldName}: BC【{item.ReqVal}】订单【{item.TargetVal}】"); } req.TaskDescription = builder.ToString(); } } var taskService = serviceProvider.GetRequiredService(); var result = await taskService.CreateTaskAsync(req, false); if (!result.Succeeded) return DataResult.Failed("创建下一任务时返回错误:" + result.Message, result.MultiCode); } return DataResult.Successed("BC任务已完成,并创建下一步任务"); } #region 发送邮件通知给客户 /// /// 发送邮件通知给客户 /// /// 起运港未提箱主键 /// 邮件模板主键 /// 返回回执 public async Task> InnerSendEmailToCustomer(long taskPKId, long businessTaskMailId) { /* 1、先匹配订单。这里可能关联的是拆票订单,如果是拆票订单需要所有拆票记录都要发邮件通知) 2、有订单,调取任务规则补全相关人(如果当票已经有了人员表记录 task_base_allocation则不再处理人员) 3、调取发送模板。 4、填充模板数据。 5、发送邮件。 6、成功后置任务为完成状态。 */ var tenantDb = saasDbService.GetBizDbScopeById(user.TenantId); var entity = tenantDb.Queryable().Filter(null, true).First(a => a.TASK_ID == taskPKId); var baseInfo = tenantDb.Queryable().Filter(null, true).First(a => a.Id == taskPKId); var queryRlt = await seaExportService.Value.SearchOrderInfo(entity.MBL_NO); if (!queryRlt.Succeeded) { logger.LogInformation($"匹配订单信息失败 mblno={entity.MBL_NO},原因:{queryRlt.Message}"); return DataResult.Failed($"匹配订单信息失败 mblno={entity.MBL_NO},原因:{queryRlt.Message}"); } var taskInfo = entity.Adapt(); BusinessTaskMail? mailConfig = _taskMailService.Value.GetAsync(businessTaskMailId).GetAwaiter().GetResult().Data; if (mailConfig == null) { await _logService.Value.WriteLogAsync(new Op.Dtos.TaskInteraction.TaskUpdateRequest { BusinessId = taskPKId, BusinessType = BusinessType.OceanShippingExport, AutoCreateNext = true, TaskTypeName = TaskBaseTypeEnum.POL_CTN_NOT_PICKUP.ToString(), }, $"未能根据任务配置值获取邮件模板设置"); return DataResult.Failed("未能根据任务配置值获取邮件模板设置"); } var orderInfo = queryRlt.Data; logger.LogInformation($"获取订单详情成功 bookid={orderInfo.currOrder.Id}"); DateTime nowDate = DateTime.Now; List orderIdList = new List { orderInfo.currOrder.Id }; if (orderInfo.otherOrderList != null && orderInfo.otherOrderList.Count > 0) orderIdList.AddRange(orderInfo.otherOrderList.Select(t => t.Id).ToList()); //如果是拆票需要处理,处理所以分票记录 if (orderInfo.currOrder.SplitOrMergeFlag == 1) { bool isHasAlloc = false; foreach (var id in orderIdList) { var taskAllocList = tenantDb.Queryable().Where(a => a.TaskId == taskPKId).ToList(); var searchAllotUserRlt = taskAllocationService.Value.GetAllotUserBySeaExportId(TaskBaseTypeEnum.POL_CTN_NOT_PICKUP, id, new TaskFlowDataContext()).GetAwaiter().GetResult(); if (searchAllotUserRlt.Succeeded && searchAllotUserRlt.Data?.Count > 0) { isHasAlloc = true; var addUserList = searchAllotUserRlt.Data.GroupJoin(taskAllocList, l => l.RecvUserId, r => r.UserId, (l, r) => { if (r.ToList().Count == 0) return new { add = true, obl = l }; return new { add = false, obl = l }; }).Where(a => a.add).Select(a => a.obl).ToList(); if (addUserList.Count > 0) { //写入 addUserList.ForEach(b => { var alloc = new TaskBaseAllocation { Status = baseInfo.STATUS, StatusName = baseInfo.STATUS_NAME, TaskId = taskPKId, StatusTime = nowDate, UserId = b.RecvUserId, UserName = b.RecvUserName }; tenantDb.Insertable(alloc).ExecuteCommand(); }); } } } if (isHasAlloc && baseInfo.IS_PUBLIC == 1) { await tenantDb.Updateable(baseInfo).UpdateColumns(x => new { x.IS_PUBLIC }).ExecuteCommandAsync(); } } else { var taskAllocList = tenantDb.Queryable().Where(a => a.TaskId == taskPKId).ToList(); if (taskAllocList.Count == 0) { var searchAllotUserRlt = taskAllocationService.Value.GetAllotUserBySeaExportId(TaskBaseTypeEnum.POL_CTN_NOT_PICKUP, orderInfo.currOrder.Id, new TaskFlowDataContext()).GetAwaiter().GetResult(); if (searchAllotUserRlt.Succeeded && searchAllotUserRlt.Data?.Count > 0) { //写入 searchAllotUserRlt.Data.ForEach(b => { var alloc = new TaskBaseAllocation { Status = baseInfo.STATUS, StatusName = baseInfo.STATUS_NAME, TaskId = taskPKId, StatusTime = nowDate, UserId = b.RecvUserId, UserName = b.RecvUserName }; tenantDb.Insertable(alloc).ExecuteCommand(); }); if (baseInfo.IS_PUBLIC == 1) { baseInfo.IS_PUBLIC = 0; await tenantDb.Updateable(baseInfo).UpdateColumns(x => new { x.IS_PUBLIC }).ExecuteCommandAsync(); } } } } TaskTransferMsgDto resultDto = new TaskTransferMsgDto { ExcuteDate = nowDate, Detail = new List() }; Dictionary> dict = new Dictionary>(); foreach (var id in orderIdList) { TaskTransferMsgDataDto detail = new TaskTransferMsgDataDto { BusinessId = id, }; var model = new MailTemplateModel(taskInfo) { BusinessId = id, BusinessType = BusinessType.OceanShippingExport, }; MailService mailService = new MailService(serviceProvider); var result = await mailService.SendAsync(mailConfig, model); if (!result.Succeeded) { await _logService.Value.WriteLogAsync(new Op.Dtos.TaskInteraction.TaskUpdateRequest { BusinessId = taskPKId, BusinessType = BusinessType.OceanShippingExport, AutoCreateNext = true, TaskTypeName = TaskBaseTypeEnum.POL_CTN_NOT_PICKUP.ToString(), }, result.Message); //return DataResult.Failed(result.Message); } detail.Status = result.Succeeded ? "SUCC" : "FAILURE"; detail.Message = result.Message; detail.MBlNo = entity.MBL_NO; resultDto.Detail.Add(detail); dict.Add(id, new Tuple(result.Succeeded, result.Message, entity.MBL_NO)); } //如果存在失败记录,不能置完成状态 if (!dict.Any(t => !t.Value.Item1)) { entity.IS_TRANSFER_USER = true; entity.LST_TRANSFER_USER_DATE = DateTime.Now; entity.LST_STATUS = "SUCC"; entity.LST_STATUS_NAME = "发送成功"; await tenantDb.Updateable(entity).UpdateColumns(x => new { x.IS_TRANSFER_USER, x.LST_TRANSFER_USER_DATE, x.LST_STATUS, x.LST_STATUS_NAME }).ExecuteCommandAsync(); //发送完邮件,自动标记任务状态为完成 await SetTaskStatus(taskPKId, TaskBaseTypeEnum.POL_CTN_NOT_PICKUP, TaskStatusEnum.Complete, DateTime.Now, null); } return DataResult.Success(resultDto); } #endregion #region 发送邮件通知给客户(任务自动机调取) /// /// 发送邮件通知给客户(任务自动机调取) /// /// 数据上下文 /// 返回回执 public async Task> SendEmailToCustomerTask(TaskFlowDataContext dataContext) { var taskPKId = dataContext.Get(TaskFlowDataNameConst.TaskPKId); if (taskPKId == 0) throw new ArgumentException($"缺少参数:{nameof(TaskFlowDataNameConst.TaskPKId)}"); var businessTaskMailId = dataContext.Get($"{nameof(BusinessTaskMail)}.{nameof(BusinessTaskMail.Id)}"); if (businessTaskMailId == 0) throw new ArgumentException($"缺少参数:{nameof(BusinessTaskMail)}.{nameof(BusinessTaskMail.Id)}"); if (businessTaskMailId == 0) { await _logService.Value.WriteLogAsync(new Op.Dtos.TaskInteraction.TaskUpdateRequest { BusinessId = taskPKId, BusinessType = BusinessType.OceanShippingExport, AutoCreateNext = true, TaskTypeName = TaskBaseTypeEnum.POL_CTN_NOT_PICKUP.ToString(), }, $"缺少参数:{nameof(BusinessTaskMail)}.{nameof(BusinessTaskMail.Id)}"); return DataResult.Failed($"缺少参数:{nameof(BusinessTaskMail)}.{nameof(BusinessTaskMail.Id)}"); } return await InnerSendEmailToCustomer(taskPKId, businessTaskMailId); } #endregion #region 手工发送邮件通知给客户 /// /// 手工发送邮件通知给客户 /// /// 起运港未提箱任务主键 /// 返回回执 public async Task> ManualSendEmailToCustomer(long taskPKId) { var paramConfig = _configService.GetConfig("BookingAmendMSKTransferEmailTemplateID", long.Parse(user.TenantId), false).GetAwaiter().GetResult()?.Data?.Value; long businessTaskMailId = 0; if (!string.IsNullOrWhiteSpace(paramConfig)) { businessTaskMailId = long.Parse(paramConfig); } else { return DataResult.Failed($"缺少系统参数参数:订舱变更自动转发邮件模板ID-BookingAmendMSKTransferEmailTemplateID"); } return await InnerSendEmailToCustomer(taskPKId, businessTaskMailId); } #endregion } }