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 IDjyTenantParamService _djyTenantParamService; 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, IDjyTenantParamService djyTenantParamService) { _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; _djyTenantParamService = djyTenantParamService; _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 && x.TenantId == UserManager.TENANT_ID); 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 && a.IsDeleted == false).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 && x.TenantId == UserManager.TENANT_ID); 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() }; //如果配置了租户参数(AUTO_TRANS_EMAIL_OP_CCTO-自动转发是否默认抄送操作=ENABLE)发送邮件时自动抄送操作 DjyTenantParamValueOutput paramConfig = _djyTenantParamService.GetParaCodeWithValue(new[] { "AUTO_TRANS_EMAIL_OP_CCTO" }).GetAwaiter().GetResult().FirstOrDefault(); if (paramConfig != null && paramConfig.ParaValue.Equals("ENABLE", StringComparison.OrdinalIgnoreCase)) { emailApiUserDefinedDto.CCTo = opEmail; } _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 } }