using Furion; using Furion.DependencyInjection; using Furion.DistributedIDGenerator; using Furion.DynamicApiController; using Furion.FriendlyException; using Furion.JsonSerialization; using Furion.RemoteRequest.Extensions; using Mapster; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Myshipping.Application.ConfigOption; using Myshipping.Application.Entity; using Myshipping.Application.Enum; using Myshipping.Application.Helper; using Myshipping.Application.Service.TaskManagePlat; using Myshipping.Core; using Myshipping.Core.Entity; using Myshipping.Core.Service; using MySqlX.XDevAPI.Common; using Newtonsoft.Json; using NPOI.XWPF.UserModel; using StackExchange.Profiling.Internal; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Myshipping.Application { /// /// 预甩货通知 /// [ApiDescriptionSettings("Application", Name = "TaskManageRollingNomination", Order = 10)] public class TaskManageRollingNominationService : ITaskManageRollingNominationService, IDynamicApiController, ITransient { private readonly ISysCacheService _cache; private readonly ILogger _logger; private readonly SqlSugarRepository _taskBaseRepository; private readonly SqlSugarRepository _taskRollingNominationInfoRepository; private readonly SqlSugarRepository _taskRollingNominationShipInfoRepository; private readonly SqlSugarRepository _taskRollingNominationDetailInfoRepository; private readonly SqlSugarRepository _taskRollingNominationDispatchInfoRepository; private readonly SqlSugarRepository _taskShareLinkInfoRepository; private readonly SqlSugarRepository _bookingOrderRepository; private readonly SqlSugarRepository _bookingCtnRepository; private readonly SqlSugarRepository _repPrintTemplate; private readonly SqlSugarRepository _sysUserRepository; private readonly SqlSugarRepository _djyUserMailAccount; private readonly SqlSugarRepository _bookingOrderContactRepository; private readonly INamedServiceProvider _namedServiceProvider; public TaskManageRollingNominationService(SqlSugarRepository taskBaseRepository, SqlSugarRepository taskRollingNominationInfoRepository, SqlSugarRepository taskRollingNominationDetailInfoRepository, SqlSugarRepository taskRollingNominationDispatchInfoRepository, SqlSugarRepository taskRollingNominationShipInfoRepository, SqlSugarRepository taskShareLinkInfoRepository, SqlSugarRepository bookingOrderRepository, SqlSugarRepository repPrintTemplate, SqlSugarRepository sysUserRepository, SqlSugarRepository djyUserMailAccount, SqlSugarRepository bookingOrderContactRepository, INamedServiceProvider namedServiceProvider, SqlSugarRepository bookingCtnRepository, ISysCacheService cache, ILogger logger) { _taskBaseRepository = taskBaseRepository; _taskRollingNominationInfoRepository = taskRollingNominationInfoRepository; _taskRollingNominationDetailInfoRepository = taskRollingNominationDetailInfoRepository; _taskRollingNominationDispatchInfoRepository = taskRollingNominationDispatchInfoRepository; _taskRollingNominationShipInfoRepository = taskRollingNominationShipInfoRepository; _taskShareLinkInfoRepository = taskShareLinkInfoRepository; _bookingOrderRepository = bookingOrderRepository; _bookingCtnRepository = bookingCtnRepository; _repPrintTemplate = repPrintTemplate; _sysUserRepository = sysUserRepository; _djyUserMailAccount = djyUserMailAccount; _bookingOrderContactRepository = bookingOrderContactRepository; _namedServiceProvider = namedServiceProvider; _cache = cache; _logger = logger; } #region 获取预甩详情 /// /// 获取预甩详情 /// /// 预甩主键 /// 返回回执 [HttpGet("/TaskManageRollingNomination/GetInfo")] public async Task GetInfo(string pkId) { TaskRollingNominationShowDto model = null; try { var rollModel = _taskRollingNominationInfoRepository.AsQueryable() .First(a => a.PK_ID == pkId && !a.IsDeleted); if (rollModel == null) throw Oops.Oh($"预甩货主键{pkId}无法获取业务信息"); model = await InnerGetInfo(rollModel); } catch (Exception ex) { _logger.LogError($"获取预甩详情异常,原因:{ex.Message}"); throw ex; } return model; } #endregion #region 通过任务主键获取预甩详情 /// /// 通过任务主键获取预甩详情 /// /// 预甩任务主键 /// 返回回执 [HttpGet("/TaskManageRollingNomination/GetInfoByTaskId")] public async Task GetInfoByTaskId(string taskPkId) { TaskRollingNominationShowDto model = null; try { var taskBase = _taskBaseRepository.AsQueryable().First(a => a.PK_ID == taskPkId && !a.IsDeleted); if (taskBase == null) throw Oops.Oh($"预甩货任务主键{taskPkId}无法获取任务信息"); var rollModel = _taskRollingNominationInfoRepository.AsQueryable() .First(a => a.TASK_ID == taskBase.PK_ID && !a.IsDeleted); if (rollModel == null) throw Oops.Oh($"预甩货任务主键{taskBase.PK_ID}无法获取业务信息"); model = await InnerGetInfo(rollModel); } catch(Exception ex) { _logger.LogError($"获取预甩详情异常,原因:{ex.Message}"); throw ex; } return model; } #endregion #region 获取预甩详情 /// /// 获取预甩详情 /// /// 预甩货详情实体类 /// 返回详情 private async Task InnerGetInfo(TaskRollingNominationInfo rollModel) { TaskRollingNominationShowDto model = null; try { List rollingPlanList = new List(); if (!string.IsNullOrWhiteSpace(rollModel.PLAN_TXT)) { rollingPlanList = rollModel.PLAN_TXT.Split("\\n").ToList(); } model = new TaskRollingNominationShowDto { PlanType = rollModel.PLAN_TYPE, CarrierId = rollModel.CARRIERID, Carrier = rollModel.CARRIER, ConfirmDeadLine = rollModel.CONFIRM_DEAD_LINE, CreateTime = rollModel.READ_CREATE_TIME, RollingTouchDoubleRollRemark = rollModel.ROLL_DOUBLE_REMARK, LoadDetailList = new List(), RollingPlanList = rollingPlanList, PreBillList = new List(), NominationId = rollModel.PK_ID }; var shipList = _taskRollingNominationShipInfoRepository.AsQueryable() .Where(a => a.NOM_ID == rollModel.PK_ID && a.IsDeleted == false).ToList(); var fromEntity = shipList.Where(a => a.SHIP_TYPE.Equals("From", StringComparison.OrdinalIgnoreCase)).ToList(); if (fromEntity.Count > 0) model.From = fromEntity.Select(p=>p.Adapt()).ToList(); var toEntity = shipList.Where(a => Regex.IsMatch(a.SHIP_TYPE,"To(\\s+[0-9]+)?" , RegexOptions.IgnoreCase)).ToList(); if (toEntity.Count > 0) model.To = toEntity.Select(p => p.Adapt()).ToList(); //优先PORT关联FROM和TO,也有PORT为空的情况,这种就需要用Terminal来关联 if (model.From.Any(t => string.IsNullOrWhiteSpace(t.Port))) { //这里为了前端原船和换船做了根据Terminal对应 model.FromToList = model.From.GroupJoin(model.To, l => l.Terminal?.Trim(), r => r.Terminal?.Trim(), (l, r) => { TaskRollingNominationShipFromToDto dto = new TaskRollingNominationShipFromToDto(); dto.FromShip = l; var currArg = r.ToList(); if (currArg.Count > 0) { dto.ToShipList = currArg.Select((p, idx) => { if (Regex.IsMatch(p.ShipType, "[0-9]+")) return new { SortNo = int.Parse(Regex.Match(p.ShipType, "[0-9]+").Value), Obj = p }; return new { SortNo = idx + 1, Obj = p }; }).OrderBy(p => p.SortNo).Select(p => p.Obj).ToList(); } return dto; }).ToList(); } else { //这里为了前端原船和换船做了根据PORT对应 model.FromToList = model.From.GroupJoin(model.To, l => l.Port?.Trim(), r => r.Port?.Trim(), (l, r) => { TaskRollingNominationShipFromToDto dto = new TaskRollingNominationShipFromToDto(); dto.FromShip = l; var currArg = r.ToList(); if (currArg.Count > 0) { dto.ToShipList = currArg.Select((p, idx) => { if (Regex.IsMatch(p.ShipType, "[0-9]+")) return new { SortNo = int.Parse(Regex.Match(p.ShipType, "[0-9]+").Value), Obj = p }; return new { SortNo = idx + 1, Obj = p }; }).OrderBy(p => p.SortNo).Select(p => p.Obj).ToList(); } return dto; }).ToList(); } List> tuples = new List>(); var withDispatchList = _taskRollingNominationDetailInfoRepository.AsQueryable().Filter(null, true) .LeftJoin((detail, dispatch) => detail.PK_ID == dispatch.DETAIL_ID) .Where((detail, dispatch) => detail.NOM_ID == rollModel.PK_ID && detail.IsDeleted == false && (string.IsNullOrWhiteSpace(dispatch.PK_ID) || (!string.IsNullOrWhiteSpace(dispatch.PK_ID) && dispatch.IsDeleted == false))) .Select((detail, dispatch) => new { Detail = detail, Dispatch = dispatch }).ToList(); if (withDispatchList.Count > 0) { model.PreBillList = withDispatchList.Where(a => !a.Detail.NOM_STATUS_NOTE.Equals("Load",StringComparison.OrdinalIgnoreCase)) .Select((a, idx) => { TaskRollingNominationShipPreBillShowDto preBillInfo = new TaskRollingNominationShipPreBillShowDto { Bookedby = a.Detail.BOOKED_BY, ConfirmDate = a.Dispatch?.CONFIRM_DATE, ConfirmDeadLine = a.Dispatch?.CONFIRM_DEAD_LINE, CtnStat = $"{a.Detail.CTNALL}*{a.Detail.CTNNUM}", CustomerId = a.Detail.CUSTOMERID, CustomerName = a.Detail.CUSTOMERNAME, ContractualName = a.Detail.CONTRACTUAL_NAME, CreateShareLinkDate = a.Dispatch?.CREATE_SHARE_LINK_DATE, DischargePortName = a.Detail.DISCHARGEPORT_NAME, IsSend = a.Dispatch != null ? a.Dispatch.IS_SEND : false, IsUserManual = a.Dispatch != null ? a.Dispatch.IS_USER_MANUAL : false, LoadPortName = a.Detail.LOADPORT_NAME, PlaceOfDelivery = a.Detail.PLACEOF_DELIVERY, PlaceOfReceipt = a.Detail.PLACEOF_RECEIPT, Shipment = a.Detail.SHIPMENT, Status = a.Dispatch?.STATUS, ShareLinkKey = a.Dispatch?.SHARE_LINK_KEY, UserOpinion = a.Dispatch?.USER_OPINION, UserOpinionTxt = a.Dispatch?.USER_OPINION_TXT, BatchId = a.Dispatch?.BATCH_ID, BookingId = a.Detail.BOOKING_ID, GroupName = a.Detail.CUSTOMERID.HasValue ? $"CUST_{idx + 1}" : "", CtnNote = !string.IsNullOrWhiteSpace(a.Detail.CTNNOTE) ? a.Detail.CTNNOTE : "" }; return preBillInfo; }).ToList(); tuples = withDispatchList.Where(a => !a.Detail.NOM_STATUS_NOTE.Equals("Load", StringComparison.OrdinalIgnoreCase)) .GroupBy(t => t.Detail.CTNALL) .Select(t => new { Key = t.Key, Num = t.ToList().Sum(x => x.Detail.CTNNUM) }).Select(x => new Tuple(x.Key, x.Num)).ToList(); } if (withDispatchList.Any(a => a.Detail.NOM_STATUS_NOTE.Equals("Load", StringComparison.OrdinalIgnoreCase))) { model.LoadDetailList.AddRange(withDispatchList.Where(a => a.Detail.NOM_STATUS_NOTE.Equals("Load", StringComparison.OrdinalIgnoreCase)) .Select((a, idx) => { return a.Detail.Adapt(); }).ToList()); } model.TotalPreBillCtnStat = string.Join(",", tuples.GroupBy(x => x.Item1) .Select(x => $"{x.Key}*{x.Sum(t => t.Item2)}").ToArray()); if (model.LoadDetailList.Count > 0) { model.TotalLoadCtnStat = string.Join(",", model.LoadDetailList.GroupBy(x => x.CtnAll) .Select(x => $"{x.Key}*{x.Sum(t => t.CtnNum)}").ToArray()); } } catch (Exception ex) { _logger.LogError($"获取预甩详情异常,原因:{ex.Message}"); throw ex; } return model; } #endregion #region 推送预甩货客户访问链接 /// /// 推送预甩货客户访问链接 /// /// 预甩货任务主键组 /// 返回回执 [HttpPost("/TaskManageRollingNomination/PushShareLink")] public async Task PushShareLink([FromBody] string[] nominationDispatchId) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); try { } catch (Exception ex) { result.succ = false; result.msg = $"推送预甩货客户访问链接异常,原因:{ex.Message}"; } return result; } #endregion #region 生成预甩货调度 /// /// 生成预甩货调度 /// /// 生成预甩货调度请求 /// 返回回执 [HttpPost("/TaskManageRollingNomination/DispatchRollingNomination")] public async Task DispatchRollingNomination([FromBody] RollingNominationDispatchRequestDto model) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); try { /* 1、如果调度ID不为空,标识选择新的明细替换原有的调度。 (1)首先匹配所选的箱型箱量是否一致,装货港、卸货港必需一致,不满足不能进行生成调度。 (2)删除原调度,生成新调度。 2、如果调度ID为空,标识是新增调度。 3、没有订舱ID或委托单位ID的数据不能生成调度。 */ DateTime nowDate = DateTime.Now; if (!model.isAdd) { if(string.IsNullOrWhiteSpace(model.nominationBatchId)) { throw Oops.Oh($"改配调度需要提供预甩货调度批次ID"); } if (model.loadDetailIds == null || model.loadDetailIds.Length == 0) { throw Oops.Oh($"预甩货可Load明细信息不能为空"); } var detailList = _taskRollingNominationDetailInfoRepository.AsQueryable() .Where(a => model.loadDetailIds.Contains(a.PK_ID) && a.IsDeleted == false).ToList(); if (detailList.Count != model.loadDetailIds.Length) { throw Oops.Oh($"预甩货明细部分获取失败,请重新获取预甩货明细"); } if (detailList.Any(a => !a.BOOKING_ID.HasValue || !a.CUSTOMERID.HasValue)) { throw Oops.Oh($"预甩货明细存在未对应订舱的记录,不能生成调度"); } var nomId = detailList.FirstOrDefault().NOM_ID; var nomModel = _taskRollingNominationInfoRepository.AsQueryable() .First(a => a.PK_ID == nomId); var shipmentList = detailList.Select(a => a.SHIPMENT).Distinct().ToList(); Dictionary batchDict = shipmentList.Select(a => new { Key = a, BatchId = IDGen.NextID().ToString() }) .ToDictionary(a => a.Key, b => b.BatchId); List dispatchList = new List(); List withDispatchDetailList = new List(); var withDispatchList = _taskRollingNominationDetailInfoRepository.AsQueryable().Filter(null, true) .InnerJoin((detail, dispatch) => detail.PK_ID == dispatch.DETAIL_ID) .Where((detail, dispatch) => detail.PK_ID == dispatch.DETAIL_ID && detail.IsDeleted == false && dispatch.IsDeleted == false && dispatch.BATCH_ID == model.nominationBatchId) .Select((detail, dispatch) => new { Detail = detail, Dispatch = dispatch }).ToList(); if (withDispatchList.Count > 0) { withDispatchDetailList = withDispatchList.Select(a => a.Detail) .ToList(); } if (dispatchList.Count == 0) { throw Oops.Oh($"预甩货调度获取失败,无法改配"); } //如果原调度对应的明细含有本次提交的明细,不允许改配调度 if (withDispatchDetailList.Any(a => model.loadDetailIds.Any(b => b == a.PK_ID))) { throw Oops.Oh($"预甩货调度获取失败,提交明细已生成调度,无法改配"); } //需要匹配箱型箱量 var origCtnStat = string.Join(",", withDispatchDetailList .GroupBy(a => a.CTNALL).OrderBy(a => a.Key) .Select(a => $"{a.Key}*{a.Sum(b => b.CTNNUM)}").ToArray()); detailList = _taskRollingNominationDetailInfoRepository.AsQueryable() .Where(a => shipmentList.Contains(a.SHIPMENT) && a.IsDeleted == false && a.NOM_ID == nomId).ToList(); var targetCtnStat = string.Join(",", detailList .GroupBy(a => a.CTNALL).OrderBy(a => a.Key) .Select(a => $"{a.Key}*{a.Sum(b => b.CTNNUM)}").ToArray()); //如果箱型箱量不一致不能改配 if (!origCtnStat.Equals(targetCtnStat)) { throw Oops.Oh($"预甩货调度获取失败,提交明细箱型箱量于被改配调度不一致,无法改配"); } var origShip = withDispatchDetailList.FirstOrDefault(); var targetShip = detailList.FirstOrDefault(); var origKey = $"{origShip.PLACEOF_RECEIPT}_{origShip.LOADPORT_NAME}_{origShip.DISCHARGEPORT_NAME}_{origShip.PLACEOF_DELIVERY}"; var targeKey = $"{targetShip.PLACEOF_RECEIPT}_{targetShip.LOADPORT_NAME}_{targetShip.DISCHARGEPORT_NAME}_{targetShip.PLACEOF_DELIVERY}"; //收货地、装货港、卸货港、交货地必需一致 if (!origKey.Equals(targeKey)) { throw Oops.Oh($"预甩货调度获取失败,提交明细收货地、装货港、卸货港、交货地被改配调度不一致,无法改配"); } //删除原调度 withDispatchList.ForEach(async a => { a.Dispatch.IsDeleted = true; a.Dispatch.UpdatedTime = nowDate; a.Dispatch.UpdatedUserId = UserManager.UserId; a.Dispatch.UpdatedUserName = UserManager.Name; await _taskRollingNominationDispatchInfoRepository.AsUpdateable(a.Dispatch) .UpdateColumns(it => new { it.IsDeleted, it.UpdatedTime, it.UpdatedUserId, it.UpdatedUserName }).ExecuteCommandAsync(); }); //写入新调度 detailList.ForEach(a => { TaskRollingNominationDispatchInfo dispatchInfo = new TaskRollingNominationDispatchInfo { PK_ID = IDGen.NextID().ToString(), CreatedTime = nowDate, UpdatedTime = nowDate, CreatedUserId = UserManager.UserId, CreatedUserName = UserManager.Name, DETAIL_ID = a.PK_ID, NOM_ID = a.NOM_ID, NOM_SHIP_ID = a.NOM_SHIP_ID, IsDeleted = false, STATUS = "WAIT", TASK_ID = nomModel.TASK_ID, TenantId = UserManager.TENANT_ID, TenantName = UserManager.TENANT_NAME, BATCH_ID = batchDict[a.SHIPMENT], }; _taskRollingNominationDispatchInfoRepository.Insert(dispatchInfo); }); } else { var withDispatchList = _taskRollingNominationInfoRepository.AsQueryable().Filter(null, true) .InnerJoin((nom, detail) => nom.PK_ID == detail.NOM_ID) .LeftJoin((nom, detail, dispatch) => detail.PK_ID == dispatch.DETAIL_ID && dispatch.IsDeleted == false) .Where((nom, detail, dispatch) => detail.IsDeleted == false && detail.NOM_ID == model.nominationId) .Select((nom, detail, dispatch) => new { Nom = nom, Detail = detail, Dispatch = dispatch }).ToList(); if (withDispatchList.Any(p => p.Dispatch != null && !string.IsNullOrWhiteSpace(p.Dispatch.PK_ID))) { throw Oops.Oh($"预甩货明细已生成调度不能重复"); } var nomiInfo = withDispatchList.FirstOrDefault().Nom; var shipmentList = withDispatchList .Select(a => a.Detail.SHIPMENT) .Distinct().ToList(); Dictionary batchDict = shipmentList.Select(a => new { Key = a, BatchId = IDGen.NextID().ToString() }) .ToDictionary(a => a.Key, b => b.BatchId); DateTime deadLine = nomiInfo.CONFIRM_DEAD_LINE.Value.AddHours(2); //开始写入调度 withDispatchList.ForEach(info => { var a = info.Detail; if (!a.NOM_STATUS_NOTE.Equals("Load", StringComparison.OrdinalIgnoreCase)) { TaskRollingNominationDispatchInfo dispatchInfo = new TaskRollingNominationDispatchInfo { PK_ID = IDGen.NextID().ToString(), CreatedTime = nowDate, UpdatedTime = nowDate, CreatedUserId = UserManager.UserId, CreatedUserName = UserManager.Name, DETAIL_ID = a.PK_ID, NOM_ID = a.NOM_ID, NOM_SHIP_ID = a.NOM_SHIP_ID, IsDeleted = false, STATUS = RollingNominationDispatchStatusEnum.WAIT.ToString(), TASK_ID = nomiInfo.TASK_ID, TenantId = UserManager.TENANT_ID, TenantName = UserManager.TENANT_NAME, BATCH_ID = batchDict[a.SHIPMENT], CONFIRM_DEAD_LINE = deadLine }; _taskRollingNominationDispatchInfoRepository.Insert(dispatchInfo); } }); } result.succ = true; result.msg = "成功"; } catch (Exception ex) { result.succ = false; result.msg = $"生成预甩货调度异常,原因:{ex.Message}"; } return result; } #endregion #region 刷新预甩货对应订舱 /// /// 刷新预甩货对应订舱 /// /// 预甩货主键 /// 返回回执 [HttpGet("/TaskManageRollingNomination/RefreshBookingOrder")] public async Task RefreshBookingOrder(string taskPkId) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); try { /* 获取所有当票预甩的所有没有BOOKING_ID的明细记录,并用SHIPMENT(提单号),检索订舱数据,能匹配的更新 */ var list = _taskRollingNominationInfoRepository.AsQueryable().Filter(null, true) .InnerJoin((nom, detail) => nom.PK_ID == detail.NOM_ID) .Where((nom, detail) => nom.TASK_ID == taskPkId && detail.IsDeleted == false && !detail.BOOKING_ID.HasValue) .Select((nom, detail) => detail).ToList(); if (list.Count > 0) { var mblNoArg = list.Select(a => a.SHIPMENT?.Trim()).Distinct().ToList(); var orderList = _bookingOrderRepository.AsQueryable() .Where(a => mblNoArg.Contains(a.MBLNO) && (a.ParentId == null || a.ParentId == 0) && a.IsDeleted == false).ToList(); DateTime nowDate = DateTime.Now; if (orderList.Count > 0) { orderList.ForEach(ord => { var dlist = list.Where(b => b.SHIPMENT.Equals(ord.MBLNO)).ToList(); if (dlist.Count > 0) { dlist.ForEach(dt => { dt.BOOKING_ID = ord.Id; dt.UpdatedTime = nowDate; dt.UpdatedUserId = UserManager.UserId; dt.UpdatedUserName = UserManager.Name; _taskRollingNominationDetailInfoRepository.AsUpdateable(dt) .UpdateColumns(it => new { it.BOOKING_ID, it.UpdatedTime, it.UpdatedUserId, it.UpdatedUserName }).ExecuteCommand(); }); } }); } } result.succ = true; result.msg = "对应订舱完成"; } catch (Exception ex) { result.succ = false; result.msg = $"刷新预甩货对应订舱异常,原因:{ex.Message}"; } return result; } #endregion #region 查看分享链接 /// /// 查看分享链接 /// /// 预甩调度批次号 /// 返回回执 [HttpGet("/TaskManageRollingNomination/GetUrl")] public async Task GetUrl(string dispatchBatchId) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); try { /* 用预甩调度的dispatchBatchId关联分享表的BUSI_ID获取分享KEY */ if (string.IsNullOrWhiteSpace(dispatchBatchId)) throw Oops.Oh($"预甩调度批次号不能为空"); var dispatchInfo = _taskRollingNominationDispatchInfoRepository.AsQueryable() .First(a => a.BATCH_ID == dispatchBatchId); if (dispatchInfo == null) { throw Oops.Oh($"获取预甩调度信息失败,不存在或已作废"); } if(string.IsNullOrWhiteSpace(dispatchInfo.SHARE_LINK_KEY)) throw Oops.Oh($"链接访问KEY不存在,请生成分享链接"); string[] statusArg = new string[] { RollingNominationDispatchStatusEnum.CANCEL.ToString(), RollingNominationDispatchStatusEnum.EXPIRE.ToString() }; if (statusArg.Any(a => a.Equals(dispatchInfo.STATUS, StringComparison.OrdinalIgnoreCase))) throw Oops.Oh($"链接访问KEY状态不可用"); result.succ = true; result.ext = dispatchInfo.SHARE_LINK_KEY; } catch (Exception ex) { result.succ = false; result.msg = $"推送预甩货客户访问链接异常,原因:{ex.Message}"; } return result; } #endregion #region 获取Status是load的可配载的列表 /// /// 获取Status是load的可配载的列表 /// /// 预甩货任务主键 /// 返回回执 [HttpGet("/TaskManageRollingNomination/GetLoadStatusDetailList")] public async Task GetLoadStatusDetailList(string taskPkId) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); try { var withDispatchList = _taskRollingNominationInfoRepository.AsQueryable().Filter(null, true) .InnerJoin((nom,detail)=> nom.PK_ID == detail.NOM_ID) .LeftJoin((nom,detail, dispatch) => detail.PK_ID == dispatch.DETAIL_ID && dispatch.IsDeleted == false) .Where((nom,detail, dispatch) => nom.TASK_ID == taskPkId && detail.IsDeleted == false && string.IsNullOrWhiteSpace(dispatch.TASK_ID)) .Select((nom, detail, dispatch) => new { Detail = detail}).ToList(); if (withDispatchList.Count > 0) { result.ext = withDispatchList.Select(a => a.Detail.Adapt()).ToList(); } result.succ = true; } catch (Exception ex) { result.succ = false; result.msg = $"推送预甩货客户访问链接异常,原因:{ex.Message}"; } return result; } #endregion #region 获取提单号下预甩货的单票明细 /// /// 获取提单号下预甩货的单票明细 /// /// 预甩货主键 /// 提单号 /// 返回回执 [HttpGet("/TaskManageRollingNomination/GetPreBillDetailList")] public async Task GetPreBillDetailList([FromQuery] string nominationId, [FromQuery] string shipmentNo) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); try { var withDispatchList = _taskRollingNominationInfoRepository.AsQueryable() .InnerJoin((nom, detail) => nom.PK_ID == detail.NOM_ID) .Where((nom, detail) => nom.PK_ID == nominationId && detail.IsDeleted == false && detail.SHIPMENT == shipmentNo) .Select((nom, detail) => new { Detail = detail }).ToList(); if (withDispatchList.Count > 0) { result.ext = withDispatchList.Select(a => a.Detail.Adapt()).ToList(); } result.succ = true; } catch (Exception ex) { result.succ = false; result.msg = $"获取提单号下预甩货的单票明细异常,原因:{ex.Message}"; } return result; } #endregion #region 保存预甩货明细箱型信息 /// /// 保存预甩货明细箱型信息(处理没有给箱型高度或者未能翻译的箱型,进行人工修正) /// /// 保存预甩货明细箱型请求 /// 返回回执 [HttpPost("/TaskManageRollingNomination/SaveDetailContainer")] public async Task SaveDetailContainer(SaveDetailContainerDto model) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); try { if (string.IsNullOrWhiteSpace(model.ctnCode)) { throw Oops.Oh($"请求的箱型代码不能为空"); } var detailInfo = _taskRollingNominationDetailInfoRepository.AsQueryable() .First(a => a.PK_ID == model.detailPKId && a.IsDeleted == false); if (detailInfo == null) throw Oops.Oh($"预甩货明细主键{model.detailPKId}无法获取业务信息"); var ctnCodeList = _cache.GetAllCodeCtn().GetAwaiter().GetResult().ToList(); var ctnCodeModel = ctnCodeList.FirstOrDefault(a => !string.IsNullOrWhiteSpace(a.Name) && a.Code.Equals(model.ctnCode, StringComparison.OrdinalIgnoreCase)); if (ctnCodeModel == null) throw Oops.Oh($"请求的箱型代码基础数据不存在,请修改"); detailInfo.CTNCODE = ctnCodeModel.Code; detailInfo.CTNALL = ctnCodeModel.Name; detailInfo.UpdatedTime = DateTime.Now; detailInfo.UpdatedUserId = UserManager.UserId; detailInfo.UpdatedUserName = UserManager.Name; await _taskRollingNominationDetailInfoRepository.AsUpdateable(detailInfo).UpdateColumns(it => new { it.CTNCODE, it.CTNALL, it.UpdatedTime, it.UpdatedUserId, it.UpdatedUserName }).ExecuteCommandAsync(); result.succ = true; result.msg = "成功"; } catch (Exception ex) { result.succ = false; result.msg = $"保存预甩货明细箱型信息异常,原因:{ex.Message}"; } return result; } #endregion #region 生成访问链接 /// /// 生成访问链接 /// /// 创建分享链接请求 /// 返回回执 [HttpPost("/TaskManageRollingNomination/GenShareLink")] public async Task GenShareLink(RollingNominationGenShareLinkDto model) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); try { if (string.IsNullOrWhiteSpace(model.dispatchBatchId)) { throw Oops.Oh($"预甩调度批次ID不能为空"); } var list = _taskRollingNominationDispatchInfoRepository.AsQueryable() .Where(a => a.BATCH_ID == model.dispatchBatchId).ToList(); if(list.Count == 0) throw Oops.Oh($"预甩调度批次ID无法获取业务信息"); var taskInfo = _taskBaseRepository.AsQueryable().First(a => a.PK_ID == list.FirstOrDefault().TASK_ID); ShareLinkRequestDto reqDto = new ShareLinkRequestDto { businessId = model.dispatchBatchId, businessType = "NOMI_DISPATCH", expireDate = list.FirstOrDefault().CONFIRM_DEAD_LINE.Value.ToString("yyyy-MM-dd HH:mm:ss"), isUserFeedBack = true, taskType = taskInfo.TASK_BASE_TYPE, isRenew = model.isRenew }; var service = _namedServiceProvider.GetService(nameof(TaskManageShareLinkService)); result = await service.CreateShareLink(reqDto); if(result.succ) { DateTime nowDate = DateTime.Now; string shareKey = result.ext.ToString(); list.ForEach(p => { p.SHARE_LINK_KEY = shareKey; p.CREATE_SHARE_LINK_DATE = nowDate; p.UpdatedTime = nowDate; p.UpdatedUserId = UserManager.UserId; p.UpdatedUserName = UserManager.Name; _taskRollingNominationDispatchInfoRepository.AsUpdateable(p) .UpdateColumns(it => new { it.SHARE_LINK_KEY, it.CREATE_SHARE_LINK_DATE, it.UpdatedTime, it.UpdatedUserId, it.UpdatedUserName }).ExecuteCommandAsync(); }); } } catch(Exception ex) { result.succ = false; result.msg = $"生成访问链接异常,原因:{ex.Message}"; } return result; } #endregion #region 取消访问链接 /// /// 取消访问链接 /// /// 预甩调度批次ID /// 返回回执 [HttpPost("/TaskManageRollingNomination/CancelShareLink")] public async Task CancelShareLink(string dispatchBatchId) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); try { if (string.IsNullOrWhiteSpace(dispatchBatchId)) { throw Oops.Oh($"预甩调度批次ID不能为空"); } var list = _taskRollingNominationDispatchInfoRepository.AsQueryable() .Where(a => a.BATCH_ID == dispatchBatchId).ToList(); if (list.Count == 0) throw Oops.Oh($"预甩调度批次ID无法获取业务信息"); var service = _namedServiceProvider.GetService(nameof(TaskManageShareLinkService)); result = await service.CancelShareLink(dispatchBatchId); } catch (Exception ex) { result.succ = false; result.msg = $"取消访问链接异常,原因:{ex.Message}"; } return result; } #endregion #region 获取用户反馈信息 /// /// 获取用户反馈信息 /// /// 预甩调度批次ID /// 返回回执 [HttpPost("/TaskManageRollingNomination/GetUserFeedBack")] public async Task GetUserFeedBack(string dispatchBatchId) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); try { if (string.IsNullOrWhiteSpace(dispatchBatchId)) { throw Oops.Oh($"预甩调度批次ID不能为空"); } var list = _taskRollingNominationDispatchInfoRepository.AsQueryable() .Where(a => a.BATCH_ID == dispatchBatchId).ToList(); if (list.Count == 0) throw Oops.Oh($"预甩调度批次ID无法获取业务信息"); result.succ = true; result.ext = new { userOpinion = list.FirstOrDefault().USER_OPINION, userOpinionTxt = list.FirstOrDefault().USER_OPINION_TXT }; } catch (Exception ex) { result.succ = false; result.msg = $"获取用户反馈信息异常,原因:{ex.Message}"; } return result; } #endregion /// /// 发送预甩的通知 /// /// 请求详情 /// [HttpPost("/TaskManageRollingNomination/SendRollingNominationNotice")] public async Task SendRollingNominationNotice(RollingNominationNoticeDto model) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); try { if (string.IsNullOrWhiteSpace(model.taskId)) { throw Oops.Oh($"预甩任务ID不能为空"); } var printTemplate = await _repPrintTemplate.AsQueryable().Filter(null, true).FirstAsync(x => x.Id == model.templateId); if (printTemplate == null) { throw Oops.Bah(BookingErrorCode.BOOK115); } if (string.IsNullOrWhiteSpace(printTemplate.CateCode) || !printTemplate.CateCode.Contains("rolling_nomination_notice_template")) { throw Oops.Bah("模板类型分类错误,当前只支持预甩邮件通知"); } string html = string.Empty; var list = _taskRollingNominationDetailInfoRepository.AsQueryable().Filter(null, true) .InnerJoin((detail, norm) => detail.NOM_ID == norm.PK_ID) .Where((detail, norm) => norm.TASK_ID == model.taskId && detail.IsDeleted == false && norm.IsDeleted == false) .Select((detail, norm) => detail).ToList(); var shipList = _taskRollingNominationShipInfoRepository.AsQueryable().Filter(null, true).Where(p => (p.PK_ID.Equals(model.fromShipPKId) || p.PK_ID.Equals(model.toShipPKId)) && p.IsDeleted == false).ToList(); var fromShipInfo = shipList.FirstOrDefault(p => p.PK_ID == model.fromShipPKId); if (fromShipInfo == null) throw Oops.Bah("当前预甩原船信息获取失败"); var toShipInfo = shipList.FirstOrDefault(p => p.PK_ID == model.toShipPKId); if (fromShipInfo == null) throw Oops.Bah("当前预甩换船信息获取失败"); BookingOrder bookingOrderEntity = null; if (list.Any(p => p.BOOKING_ID.HasValue)) { var bookingId = list.FirstOrDefault().BOOKING_ID.Value; //读取订舱数据 bookingOrderEntity = _bookingOrderRepository.AsQueryable().Filter(null, true) .First(a => a.Id == bookingId); } if (bookingOrderEntity == null) throw Oops.Bah("当前预甩没有对应的订舱信息,请先生成订舱订单才能转发邮件"); SysUser opUserInfo = null; if (!string.IsNullOrWhiteSpace(bookingOrderEntity.OPID) && Regex.IsMatch(bookingOrderEntity.OPID, "[0-9]+")) { opUserInfo = _sysUserRepository.AsQueryable().Filter(null, true).First(u => u.Id == long.Parse(bookingOrderEntity.OPID)); } if (opUserInfo == null) throw Oops.Bah("当前预甩对应的订舱信息未指定操作OP,不能转发邮件"); html = GenerateTemplateHtml(printTemplate.FilePath, fromShipInfo, toShipInfo, opUserInfo, UserManager.TENANT_NAME); if(string.IsNullOrWhiteSpace(html)) throw Oops.Bah("生成预甩邮件通知正文失败,未生成有效信息"); //TO 邮件接收人 string toEmail = string.Empty; //订舱OP的邮箱 string opEmail = string.Empty; var bookingContactList = _bookingOrderContactRepository.AsQueryable().Filter(null, true) .Where(a => a.BookingId == bookingOrderEntity.Id).ToList(); if (bookingContactList == null || bookingContactList.Count == 0) { _logger.LogInformation($"当前订舱未指定的联系人,toEmail={toEmail}"); } toEmail = string.Join(";", bookingContactList.Select(x => x.Email.Trim()).Distinct().ToArray()); //获取操作OP的邮箱 if (!string.IsNullOrWhiteSpace(bookingOrderEntity.OPID)) { var opId = long.Parse(bookingOrderEntity.OPID); var opUser = _sysUserRepository.AsQueryable().Filter(null, true).First(a => a.Id == opId); if (opUser != null && !string.IsNullOrWhiteSpace(opUser.Email)) { opEmail = opUser.Email.Trim(); _logger.LogInformation($"获取操作OP的邮箱,opEmail={opEmail} id={opId} name={opUser.Name}"); } } //推送邮件 var opt = App.GetOptions(); var dirAbs = opt.basePath; if (string.IsNullOrEmpty(dirAbs)) { dirAbs = App.WebHostEnvironment.WebRootPath; } string emailTitle = $"【预甩通知】{list.FirstOrDefault().SHIPMENT} 原船名/航次 {fromShipInfo.VESSEL}/{fromShipInfo.VOYNO}"; var sendRlt = await SendEmail(emailTitle, html, toEmail, opEmail, list.FirstOrDefault().SHIPMENT); if (sendRlt.succ) { result.succ = true; result.msg = "成功"; } else { result.succ = false; result.msg = sendRlt.msg; new EmailNoticeHelper().SendEmailNotice($"MBLNO={list.FirstOrDefault().SHIPMENT} 发送预甩的通知失败", $"MBLNO={list.FirstOrDefault().SHIPMENT} 发送预甩的通知失败,原因:{sendRlt.msg}", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList()); } result.succ = true; } catch (Exception ex) { result.succ = false; result.msg = $"发送预甩的通知失败,原因:{ex.Message}"; } return result; } #region 获取预甩的通知预览 /// /// 获取预甩的通知预览 /// /// 请求详情 /// [HttpPost("/TaskManageRollingNomination/GetRollingNominationNoticeReview")] public async Task GetRollingNominationNoticeReview(RollingNominationNoticeDto model) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); try { if (string.IsNullOrWhiteSpace(model.taskId)) { throw Oops.Oh($"预甩任务ID不能为空"); } var printTemplate = await _repPrintTemplate.AsQueryable().Filter(null, true).FirstAsync(x => x.Id == model.templateId); if (printTemplate == null) { throw Oops.Bah(BookingErrorCode.BOOK115); } if (string.IsNullOrWhiteSpace(printTemplate.CateCode) || !printTemplate.CateCode.Contains("rolling_nomination_notice_template")) { throw Oops.Bah("模板类型分类错误,当前只支持预甩邮件通知"); } string html = string.Empty; var list = _taskRollingNominationDetailInfoRepository.AsQueryable().Filter(null, true) .InnerJoin((detail, norm) => detail.NOM_ID == norm.PK_ID) .Where((detail, norm) => norm.TASK_ID == model.taskId && detail.IsDeleted == false && norm.IsDeleted == false) .Select((detail, norm) => detail).ToList(); var shipList = _taskRollingNominationShipInfoRepository.AsQueryable().Filter(null, true).Where(p => (p.PK_ID.Equals(model.fromShipPKId) || p.PK_ID.Equals(model.toShipPKId)) && p.IsDeleted == false).ToList(); var fromShipInfo = shipList.FirstOrDefault(p => p.PK_ID == model.fromShipPKId); if (fromShipInfo == null) throw Oops.Bah("当前预甩原船信息获取失败"); var toShipInfo = shipList.FirstOrDefault(p => p.PK_ID == model.toShipPKId); if (fromShipInfo == null) throw Oops.Bah("当前预甩换船信息获取失败"); BookingOrder bookingOrderEntity = null; if (list.Any(p => p.BOOKING_ID.HasValue)) { var bookingId = list.FirstOrDefault().BOOKING_ID.Value; //读取订舱数据 bookingOrderEntity = _bookingOrderRepository.AsQueryable().Filter(null, true) .First(a => a.Id == bookingId); } if (bookingOrderEntity == null) throw Oops.Bah("当前预甩没有对应的订舱信息,请先生成订舱订单才能转发邮件"); SysUser opUserInfo = null; if (!string.IsNullOrWhiteSpace(bookingOrderEntity.OPID) && Regex.IsMatch(bookingOrderEntity.OPID, "[0-9]+")) { opUserInfo = _sysUserRepository.AsQueryable().Filter(null, true).First(u => u.Id == long.Parse(bookingOrderEntity.OPID)); } if (opUserInfo == null) throw Oops.Bah("当前预甩对应的订舱信息未指定操作OP,不能转发邮件"); html = GenerateTemplateHtml(printTemplate.FilePath, fromShipInfo, toShipInfo, opUserInfo, UserManager.TENANT_NAME); result.succ = true; result.ext = html; } catch (Exception ex) { result.succ = false; result.msg = $"取消获取预甩的通知预览异常,原因:{ex.Message}"; } return result; } #endregion #region 获取换船选择列表 /// /// 获取换船选择列表 /// /// 任务ID /// [HttpGet("/TaskManageRollingNomination/GetToShipSelect")] public async Task GetToShipSelect(string taskPkId) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); List fromToList = new List(); try { var list = _taskRollingNominationDetailInfoRepository.AsQueryable().Filter(null, true) .InnerJoin((detail, norm) => detail.NOM_ID == norm.PK_ID) .Where((detail, norm) => norm.TASK_ID == taskPkId && detail.IsDeleted == false && norm.IsDeleted == false) .Select((detail, norm) => detail).ToList(); if (list.Count == 0) throw Oops.Oh($"预甩货任务主键{taskPkId}无法获取业务信息"); TaskRollingNominationShowDto model = new TaskRollingNominationShowDto(); var shipList = _taskRollingNominationShipInfoRepository.AsQueryable() .Where(a => a.NOM_ID == list.FirstOrDefault().NOM_ID && a.IsDeleted == false).ToList(); var fromEntity = shipList.Where(a => a.SHIP_TYPE.Equals("From", StringComparison.OrdinalIgnoreCase)).ToList(); if (fromEntity.Count > 0) model.From = fromEntity.Select(p => p.Adapt()).ToList(); model.LoadDetailList = list.Select(a => a.Adapt()).ToList(); if (model.From != null && model.From.Count > 1) { var firstLoadDetail = model.LoadDetailList.FirstOrDefault(); if (model.From.Any(t => string.IsNullOrWhiteSpace(t.Port))) { model.From = model.From.Where(a => a.Terminal.Contains(firstLoadDetail.LoadPortName?.Trim())).ToList(); } else { model.From = model.From.Where(a => a.Port.Equals(firstLoadDetail.LoadPortName?.Trim(),StringComparison.OrdinalIgnoreCase)).ToList(); } } var toEntity = shipList.Where(a => Regex.IsMatch(a.SHIP_TYPE, "To(\\s+[0-9]+)?" , RegexOptions.IgnoreCase)).ToList(); if (toEntity.Count > 0) model.To = toEntity.Select(p => p.Adapt()).ToList(); //优先PORT关联FROM和TO,也有PORT为空的情况,这种就需要用Terminal来关联 if (model.From.Any(t => string.IsNullOrWhiteSpace(t.Port))) { //这里为了前端原船和换船做了根据Terminal对应 fromToList = model.From.GroupJoin(model.To, l => l.Terminal?.Trim(), r => r.Terminal?.Trim(), (l, r) => { TaskRollingNominationShipFromToDto dto = new TaskRollingNominationShipFromToDto(); dto.FromShip = l; var currArg = r.ToList(); if (currArg.Count > 0) { dto.ToShipList = currArg.Select((p, idx) => { if (Regex.IsMatch(p.ShipType, "[0-9]+")) return new { SortNo = int.Parse(Regex.Match(p.ShipType, "[0-9]+").Value), Obj = p }; return new { SortNo = idx + 1, Obj = p }; }).OrderBy(p => p.SortNo).Select(p => p.Obj).ToList(); } return dto; }).ToList(); } else { //这里为了前端原船和换船做了根据PORT对应 fromToList = model.From.GroupJoin(model.To, l => l.Port?.Trim(), r => r.Port?.Trim(), (l, r) => { TaskRollingNominationShipFromToDto dto = new TaskRollingNominationShipFromToDto(); dto.FromShip = l; var currArg = r.ToList(); if (currArg.Count > 0) { dto.ToShipList = currArg.Select((p, idx) => { if (Regex.IsMatch(p.ShipType, "[0-9]+")) return new { SortNo = int.Parse(Regex.Match(p.ShipType, "[0-9]+").Value), Obj = p }; return new { SortNo = idx + 1, Obj = p }; }).OrderBy(p => p.SortNo).Select(p => p.Obj).ToList(); } return dto; }).ToList(); } List> tuples = new List>(); model.FromToList = new List(); if (model.LoadDetailList.Count > 0) { model.TotalLoadCtnStat = string.Join(",", model.LoadDetailList.GroupBy(x => x.CtnAll) .Select(x => $"{x.Key}*{x.Sum(t => t.CtnNum)}").ToArray()); string rollRegex = "Roll\\s+to\\s+\\w+\\s+\\w{3,}\\/\\w+,\\s?if\\s+cannot\\s+catch\\s?,\\s+roll\\s+to\\s+\\w{3,}\\s+next\\s+sailing"; string rollontoRegex = "Roll\\s+onto\\s+\\w{3,}\\s+\\w+,\\s+[0-9]+\\s+days\\s+delay\\."; bool isSelectRoll = false; bool isRoll = false; bool isRollOnto = false; model.LoadDetailList.ForEach(p => { string vslCode = string.Empty; string voyNo = string.Empty; string strCode = string.Empty; string strCode2 = string.Empty; if (Regex.IsMatch(p.Status, rollRegex, RegexOptions.IgnoreCase)) { string currS = Regex.Match(p.Status, "(?<=Roll\\sto\\s\\w+\\s)\\w{3,}\\/\\w+(?=,)").Value; vslCode = Regex.Split(currS, "\\/")[0]?.Trim(); voyNo = Regex.Split(currS, "\\/")[1]?.Trim(); strCode = Regex.Match(p.Status, "(?<=if\\scannot\\scatch\\s?,\\sroll\\sto\\s)\\w{3,}(?=\\s+next\\s+sailing)").Value?.Trim(); isSelectRoll = true; } else if (p.Status.Trim().Equals("roll", StringComparison.OrdinalIgnoreCase)) { isRoll = true; } else if (Regex.IsMatch(p.Status, rollontoRegex, RegexOptions.IgnoreCase)) { vslCode = Regex.Match(p.Status, "(?<=Roll\\sonto\\s)\\w{3,}(?=\\s+\\w+,)").Value?.Trim(); isRollOnto = true; } for (int i = 0; i < fromToList.Count; i++) { if (!string.IsNullOrWhiteSpace(fromToList[i].FromShip.Port)) { if (fromToList[i].FromShip.Port.Equals(p.PlaceOfReceipt, StringComparison.OrdinalIgnoreCase)) { var fromInfo = fromToList[i].FromShip.Adapt(); List toShipList = new List(); if (isSelectRoll) { for (int j = 0; j < fromToList[i].ToShipList.Count; j++) { if (fromToList[i].ToShipList[j].VslCode.Equals(vslCode, StringComparison.OrdinalIgnoreCase)) { toShipList.Add(fromToList[i].ToShipList[j].Adapt()); } else if (fromToList[i].ToShipList[j].ShipString.Equals(strCode, StringComparison.OrdinalIgnoreCase)) { toShipList.Add(fromToList[i].ToShipList[j].Adapt()); } } } else if (isRoll) { for (int j = 0; j < fromToList[i].ToShipList.Count; j++) { if (fromToList[i].ToShipList[j].Port.Equals(p.PlaceOfDelivery?.Trim(), StringComparison.OrdinalIgnoreCase)) { toShipList.Add(fromToList[i].ToShipList[j].Adapt()); } } } if (toShipList.Count == 0) { if (fromToList[i].ToShipList.Count == 1) { toShipList.Add(fromToList[i].ToShipList.FirstOrDefault().Adapt()); } } model.FromToList.Add(new TaskRollingNominationShipFromToDto { FromShip = fromInfo, ToShipList = toShipList }); } } else { if (fromToList[i].FromShip.Terminal.Contains(p.PlaceOfReceipt, StringComparison.OrdinalIgnoreCase)) { var fromInfo = fromToList[i].FromShip.Adapt(); List toShipList = new List(); if (isRollOnto) { for (int j = 0; j < fromToList[i].ToShipList.Count; j++) { if (fromToList[i].ToShipList[j].VslCode.Equals(vslCode, StringComparison.OrdinalIgnoreCase)) { toShipList.Add(fromToList[i].ToShipList[j].Adapt()); } } } model.FromToList.Add(new TaskRollingNominationShipFromToDto { FromShip = fromInfo, ToShipList = toShipList }); } } } }); } result.succ = true; result.ext = fromToList; } catch (Exception ex) { result.succ = false; result.msg = $"获取换船选择列表异常,原因:{ex.Message}"; } return result; } #endregion #region 通过邮件模板生成预甩邮件通知内容 /// /// 通过邮件模板生成预甩邮件通知内容 /// /// 文件路径 /// 原船详情 /// 换船详情 /// 操作OP详情 /// 租户名称 /// 返回生成后邮件正文 private string GenerateTemplateHtml(string filePath, TaskRollingNominationShipInfo fromShipInfo, TaskRollingNominationShipInfo toShipInfo, SysUser opUserInfo, string tenantName) { string result = string.Empty; string baseHtml = string.Empty; try { var opt = App.GetOptions(); var dirAbs = opt.basePath; if (string.IsNullOrEmpty(dirAbs)) { dirAbs = App.WebHostEnvironment.WebRootPath; } var fileAbsPath = Path.Combine(dirAbs, filePath); _logger.LogInformation($"查找模板文件:{fileAbsPath}"); if (!File.Exists(fileAbsPath)) { throw Oops.Bah(BookingErrorCode.BOOK115); } baseHtml = File.ReadAllText(fileAbsPath); if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.Name)) { baseHtml = baseHtml.Replace("#opname#", opUserInfo.Name); } 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#", ""); } //原船 if (fromShipInfo.ETD.HasValue && fromShipInfo.ETD.Value != DateTime.MinValue) { baseHtml = baseHtml.Replace("#ETD#", fromShipInfo.ETD.Value.ToString("yyyy-MM-dd")); } else { baseHtml = baseHtml.Replace("#ETD#", ""); } //原船 if (!string.IsNullOrWhiteSpace(fromShipInfo.VESSEL) && !string.IsNullOrWhiteSpace(fromShipInfo.VOYNO)) { baseHtml = baseHtml.Replace("#VesselVoyno#", $"{fromShipInfo.VESSEL}/{fromShipInfo.VOYNO}"); } else { baseHtml = baseHtml.Replace("#VesselVoyno#", ""); } //换船 if (toShipInfo.ETD.HasValue && toShipInfo.ETD.Value != DateTime.MinValue) { baseHtml = baseHtml.Replace("#newETD#", toShipInfo.ETD.Value.ToString("yyyy-MM-dd")); } else { baseHtml = baseHtml.Replace("#newETD#", ""); } //换船 if (!string.IsNullOrWhiteSpace(toShipInfo.VESSEL) && !string.IsNullOrWhiteSpace(toShipInfo.VOYNO)) { baseHtml = baseHtml.Replace("#newVesselVoyno#", $"{toShipInfo.VESSEL}/{toShipInfo.VOYNO}"); } else { baseHtml = baseHtml.Replace("#newVesselVoyno#", ""); } //换船 if (toShipInfo.SI_CUT_DATE.HasValue && toShipInfo.SI_CUT_DATE.Value != DateTime.MinValue) { baseHtml = baseHtml.Replace("#CutSingleTime#", toShipInfo.SI_CUT_DATE.Value.ToString("yyyy-MM-dd")); } else { baseHtml = baseHtml.Replace("#CutSingleTime#", ""); } if (!string.IsNullOrWhiteSpace(tenantName)) { baseHtml = baseHtml.Replace("#TenantCompanyName#", tenantName); } else { baseHtml = baseHtml.Replace("#TenantCompanyName#", ""); } } catch (Exception ex) { _logger.LogInformation($"生成预甩通知邮件正文失败,原因:{ex.Message}"); throw Oops.Bah($"生成预甩通知邮件正文失败,原因:{ex.Message}"); } result = baseHtml; return result; } #endregion #region 发送邮件 /// /// 发送邮件 /// /// 邮件标题 /// 邮件正文 /// 邮件接收人 /// 邮件抄送人 /// 提单号 /// 是否使用个人邮箱 /// 返回发送回执 private async Task SendEmail(string emailTitle,string emailHtml,string toEmail,string opEmail,string mblNo,bool usePersonalEmailSend = false) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); try { //提取当前公共邮箱的配置 DjyUserMailAccount publicMailAccount = null; if (usePersonalEmailSend) { publicMailAccount = _djyUserMailAccount.AsQueryable().Filter(null, true).First(x => x.CreatedUserId == UserManager.UserId && x.SmtpPort > 0 && x.SmtpServer != null && x.SmtpServer != ""); } else { //这个是公共邮箱配置 publicMailAccount = _djyUserMailAccount.AsQueryable().Filter(null, true).First(x => x.TenantId == UserManager.TENANT_ID && x.ShowName == "PublicSend" && x.SmtpPort > 0 && x.SmtpServer != null && x.SmtpServer != ""); } if (publicMailAccount == null) { throw Oops.Oh($"提取公共邮箱配置失败,请在用户邮箱账号管理增加配置显示名为PublicSend或者配置个人邮箱"); } _logger.LogInformation($"提取当前公共邮箱的配置完成,id={publicMailAccount.Id}"); 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() }; _logger.LogInformation($"生成请求邮件参数,结果:{JSON.Serialize(emailApiUserDefinedDto)}"); string filePath = string.Empty; //推送邮件 var emailRlt = await PushEmail(emailApiUserDefinedDto, filePath); _logger.LogInformation($"推送邮件完成,结果:{JSON.Serialize(emailRlt)}"); if (emailRlt.succ) { result.succ = true; result.msg = "成功"; } else { result.succ = false; result.msg = emailRlt.msg; new EmailNoticeHelper().SendEmailNotice($"MBLNO={mblNo} 转发预甩通知邮件失败", $"MBLNO={mblNo} 转发预甩通知邮件失败,原因:{emailRlt.msg}", App.Configuration["EmailNoticeDefaultUser"].GetUserEmailList()); } } catch(Exception ex) { result.succ = false; result.msg = $"推送邮件失败,原因:{ex.Message}"; } return result; } #endregion #region 推送邮件 /// /// 推送邮件 /// /// 自定义邮件详情 /// 文件路径 /// 返回回执 private async Task PushEmail(EmailApiUserDefinedDto emailApiUserDefinedDto, string filePath) { CommonWebApiResult result = new CommonWebApiResult { succ = true }; List emailList = new List(); var emailUrl = _cache.GetAllDictData().GetAwaiter().GetResult() .FirstOrDefault(x => x.TypeCode == "url_set" && x.Code == "email_api_url")?.Value; if (emailUrl == null) throw Oops.Bah("字典未配置 url_set->email_api_url 请联系管理员"); if (!string.IsNullOrWhiteSpace(filePath)) { 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; HttpResponseMessage res = null; try { res = await emailUrl.SetBody(emailList, "application/json").PostAsync(); } catch (Exception ex) { _logger.LogInformation($"发送邮件异常:{ex.Message}"); } DateTime eDate = DateTime.Now; TimeSpan ts = eDate.Subtract(bDate); var timeDiff = ts.TotalMilliseconds; //_logger.LogInformation($"邮件上传完成 上传文件大小:{heByte.Length} 用时:{timeDiff}ms.,"); _logger.LogInformation($"发送邮件返回:{JSON.Serialize(res)}"); if (res != null && res.StatusCode == System.Net.HttpStatusCode.OK) { var userResult = await res.Content.ReadAsStringAsync(); var respObj = JsonConvert.DeserializeAnonymousType(userResult, new { Success = false, Message = string.Empty, Code = -9999, }); result.succ = respObj.Success; result.msg = respObj.Message; } return result; } #endregion } }