diff --git a/Myshipping.Application/Service/TaskManagePlat/Interface/ITaskManagePODDischargeGateoutFullService.cs b/Myshipping.Application/Service/TaskManagePlat/Interface/ITaskManagePODDischargeGateoutFullService.cs index b73d70bd..5e7562cd 100644 --- a/Myshipping.Application/Service/TaskManagePlat/Interface/ITaskManagePODDischargeGateoutFullService.cs +++ b/Myshipping.Application/Service/TaskManagePlat/Interface/ITaskManagePODDischargeGateoutFullService.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.AspNetCore.Mvc; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -53,5 +54,12 @@ namespace Myshipping.Application.Service.TaskManagePlat.Interface /// 目的港未提货未返箱任务主键 /// 返回回执 Task SendEmailToCustomer(string taskPKId); + + /// + /// 自动转发目的港未提货未返箱 + /// + /// 目的港未提货未返箱任务主键 + /// 返回回执 + Task AutoTransferNotice(string taskPKId); } } diff --git a/Myshipping.Application/Service/TaskManagePlat/TaskManagePODDischargeGateoutFullService.cs b/Myshipping.Application/Service/TaskManagePlat/TaskManagePODDischargeGateoutFullService.cs index 5ff14f76..24ae57d6 100644 --- a/Myshipping.Application/Service/TaskManagePlat/TaskManagePODDischargeGateoutFullService.cs +++ b/Myshipping.Application/Service/TaskManagePlat/TaskManagePODDischargeGateoutFullService.cs @@ -1,4 +1,5 @@ using Furion; +using Furion.DependencyInjection; using Furion.DynamicApiController; using Furion.FriendlyException; using Furion.JsonSerialization; @@ -32,7 +33,7 @@ namespace Myshipping.Application /// 任务目的港未提货未返箱 /// [ApiDescriptionSettings("Application", Name = "PODDischargeGateoutFull", Order = 10)] - public class TaskManagePODDischargeGateoutFullService: ITaskManagePODDischargeGateoutFullService, IDynamicApiController + public class TaskManagePODDischargeGateoutFullService: ITaskManagePODDischargeGateoutFullService, IDynamicApiController, ITransient { private readonly SqlSugarRepository _taskPODDischargeGateoutFullInfoRep; private readonly SqlSugarRepository _taskPODDischargeGateoutFullDetailInfoRep; @@ -521,7 +522,12 @@ namespace Myshipping.Application if (opEmailList.Count > 0) opEmail = string.Join(";", opEmailList.Distinct().ToArray()); - string emailTitle = $"{model.MBL_NO}-/ 目的港未提箱"; + string emailTitle = $"{model.MBL_NO}-/ 卸船未提货"; + + if(model.NOTICE_TYPE == "GATEOUT_FULL") + { + emailTitle = $"{model.MBL_NO}-/ 提箱未返空箱"; + } //提取当前公共邮箱的配置 DjyUserMailAccount publicMailAccount = _djyUserMailAccount.AsQueryable().Filter(null, true).First(x => x.TenantId == UserManager.TENANT_ID && x.ShowName == "PublicSend" @@ -765,5 +771,43 @@ namespace Myshipping.Application return result; } #endregion + + #region 自动转发目的港未提货未返箱 + /// + /// 自动转发目的港未提货未返箱 + /// + /// 目的港未提货未返箱任务主键 + /// 返回回执 + [HttpGet("/PODDischargeGateoutFull/AutoTransferNotice")] + public async Task AutoTransferNotice(string taskPKId) + { + TaskManageOrderResultDto result = new TaskManageOrderResultDto(); + + try + { + var queryRlt = QueryBookingOrder(taskPKId); + + _logger.LogInformation($"taskPKId={taskPKId} 检索对应的订舱记录完成,结果:{JSON.Serialize(queryRlt)}"); + + + //_logger.LogInformation($"taskPKId={taskPKId} 当前租户未开启货物运输计划已变更是否同批次同客户合并邮件通知,按照单票推送邮件处理"); + + //如果没有配置批量,则按单票发送邮件 + var rlt = SendEmailToCustomer(taskPKId); + + _logger.LogInformation($"taskPKId={taskPKId} 推送邮件完成,结果:{JSON.Serialize(rlt)}"); + + } + catch (Exception ex) + { + result.succ = false; + result.msg = $"自动转发目的港未提货未返箱失败,原因:{ex.Message}"; + + _logger.LogInformation($"taskPKId={taskPKId} 自动转发目的港未提货未返箱失败,原因:{ex.Message}"); + } + + return result; + } + #endregion } } diff --git a/Myshipping.Application/Service/TaskManagePlat/TaskManageService.cs b/Myshipping.Application/Service/TaskManagePlat/TaskManageService.cs index b89cbe5f..1ccd24e8 100644 --- a/Myshipping.Application/Service/TaskManagePlat/TaskManageService.cs +++ b/Myshipping.Application/Service/TaskManagePlat/TaskManageService.cs @@ -155,6 +155,7 @@ namespace Myshipping.Application private readonly INamedServiceProvider _namedTaskManageCutDateChangeServiceProvider; private readonly INamedServiceProvider _namedRouteChangeAdvisoryServiceServiceProvider; private readonly INamedServiceProvider _namedBookingLabelServiceServiceProvider; + private readonly INamedServiceProvider _namedPODDischargeGateoutFullProvider; private readonly IBookingValueAddedService _bookingValueAddedService; @@ -229,6 +230,7 @@ namespace Myshipping.Application INamedServiceProvider namedTaskManageCutDateChangeServiceProvider, INamedServiceProvider namedRouteChangeAdvisoryServiceServiceProvider, INamedServiceProvider namedBookingLabelServiceServiceProvider, + INamedServiceProvider namedPODDischargeGateoutFullProvider, ILogger logger, BookingOrderAutoService bookingOrderAuto, SqlSugarRepository taskFlowTenant) { @@ -303,6 +305,7 @@ namespace Myshipping.Application _taskFlowTenant = taskFlowTenant; _namedBookingLabelServiceServiceProvider = namedBookingLabelServiceServiceProvider; + _namedPODDischargeGateoutFullProvider = namedPODDischargeGateoutFullProvider; } #region 创建任务 @@ -1521,6 +1524,12 @@ namespace Myshipping.Application await _taskPODDischargeGateoutFullDetailInfoRepository.InsertAsync(detailInfo); }); } + + //触发推送消息 + var name = _namedPODDischargeGateoutFullProvider + .GetService(nameof(TaskManagePODDischargeGateoutFullService)); + + await name.AutoTransferNotice(taskInfo.PK_ID); } #endregion diff --git a/Myshipping.Application/Service/TaskManagePlat/TaskManageVGMService.cs b/Myshipping.Application/Service/TaskManagePlat/TaskManageVGMService.cs index 35b3bdd4..046e6914 100644 --- a/Myshipping.Application/Service/TaskManagePlat/TaskManageVGMService.cs +++ b/Myshipping.Application/Service/TaskManagePlat/TaskManageVGMService.cs @@ -10,6 +10,7 @@ using MySqlX.XDevAPI.Common; using System; using System.Collections.Generic; using System.Linq; +using System.Reflection.Metadata.Ecma335; using System.Text; using System.Threading.Tasks; @@ -210,8 +211,17 @@ namespace Myshipping.Application [HttpGet("/TaskManageVGM/SendVGMMissingNotice")] public async Task SendVGMMissingNotice(string taskPkId) { + /* + 检索对应的订舱订单,提取相关人推送消息和 + */ //查询所有 return null; } + + + public async Task SyncBookingOrder() + { + return null; + } } } diff --git a/ServiceProjectSyncWin/Program.cs b/ServiceProjectSyncWin/Program.cs index a3551901..c30261f2 100644 --- a/ServiceProjectSyncWin/Program.cs +++ b/ServiceProjectSyncWin/Program.cs @@ -5,6 +5,7 @@ using Furion.DistributedIDGenerator; using Furion.FriendlyException; using Furion.JsonSerialization; using Furion.RemoteRequest.Extensions; +using HtmlAgilityPack; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.Extensions.DependencyInjection; @@ -12,6 +13,7 @@ using Microsoft.Extensions.Logging; using ServiceProjectSyncWin; using ServiceProjectSyncWin.Entities; using SqlSugar; +using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel.DataAnnotations; using System.Net.Http.Headers; @@ -32,7 +34,7 @@ Console.WriteLine("开始准备同步历史服务状态数据"); var service1 = App.GetService(); //service1.SyncServiceProjectRecord(); -service1.SyncServiceProjectRecord4(); +service1.SyncServiceProjectRecord5(); Console.ReadKey(); @@ -40,6 +42,7 @@ public interface ISyncHisRecord { void SyncServiceProjectRecord4(); + void SyncServiceProjectRecord5(); //void SyncServiceProjectRecord2(); //void SyncServiceProjectRecord3(); @@ -779,6 +782,1021 @@ public class SyncHisRecord: ISyncHisRecord,ITransient } + + public void SyncServiceProjectRecord5() + { + string strBody = File.ReadAllText("C:\\Users\\Administrator\\Desktop\\测试HTML.txt", Encoding.UTF8); + //FileStream f = new FileStream("C:\\Users\\Administrator\\Desktop\\测试HTML.txt", FileMode.Create); + //StreamWriter r = new StreamWriter(f, Encoding.Default); + + //r.WriteLine(strBody); + + HtmlDocument html = new HtmlDocument(); + html.LoadHtml(strBody); + + List list = new List(); + + var divTopList = html.DocumentNode.SelectSingleNode("//div[@class='data__top']"); + + if (divTopList == null) + return; + + //先获取第一层的div + var rootDivNode = divTopList.ParentNode.ChildNodes.Where(a => a.Name.Equals("div", StringComparison.OrdinalIgnoreCase) + && a.Attributes.Contains("class") && (a.Attributes["class"].Value.Equals("data__top") || a.Attributes["class"].Value.Equals("data"))) + .ToList(); + + //先遍历第一层子节点 + for (int i = 0; i < rootDivNode.Count; i++) + { + //先取top + if (rootDivNode[i].Attributes["class"].Value.Equals("data__top", StringComparison.OrdinalIgnoreCase)) + { + TransPlanHasChangeDto dto = new TransPlanHasChangeDto + { + From = new TransPlanHasChangeDetailDto + { + portList = new List(), + dateList = new List(), + vesselList = new List() + }, + To = new TransPlanHasChangeDetailDto + { + portList = new List(), + dateList = new List(), + vesselList = new List() + }, + Carrier = "MSK", + ContaNoList = new List() + }; + + if (i == rootDivNode.Count - 1) + { + var innerList = GetChildList(rootDivNode[i]); + + if (innerList != null && innerList.Count > 0) + { + list.AddRange(innerList); + } + } + else + { + #region 解析TOP + var ctnNode = rootDivNode[i].SelectSingleNode("./table[not(contains(@class,'reason'))]/tbody/tr[1]/th[contains(@class,'last')]"); + + if (ctnNode != null) + { + string s = ctnNode.SelectSingleNode("./table/tr/th/table/tbody/tr/th/table/tr/th").InnerText.ReplaceHtmlStr(); + + if (!string.IsNullOrWhiteSpace(s) && (Regex.IsMatch(s, "集装箱号码") || Regex.IsMatch(s, "Container\\(s\\)"))) + { + string ctnStr = Regex.Match(s, "(?<=集装箱号码).*").Value?.Trim(); + + if (string.IsNullOrWhiteSpace(ctnStr)) + { + ctnStr = Regex.Match(s, "(?<=Container\\(s\\)).*").Value?.Trim(); + } + + if (ctnStr.IndexOf(",") >= 0) + { + dto.ContaNoList = ctnStr.Split(new char[] { ',' }).Select(a => a.Trim()).ToList(); + } + else + { + if (!string.IsNullOrWhiteSpace(ctnStr)) + dto.ContaNoList.Add(ctnStr.Trim()); + } + } + } + + Dictionary labelDict = new Dictionary(); + Dictionary valDict = new Dictionary(); + Dictionary> reasonDict = new Dictionary>(); + + string lableXpath = "./table[not(contains(@class,'reason'))]/tbody/tr[1]/th[1]/table/tr[1]/"; + + var currLabel1 = rootDivNode[i].SelectSingleNode($"{lableXpath}/th[1]/table/tbody/tr[1]/th[1]/table/tr[1]/th[1]").InnerText.ReplaceHtmlStr(); + + if (!string.IsNullOrWhiteSpace(currLabel1)) + { + labelDict.Add(1, currLabel1); + } + + var currLabel2Node = rootDivNode[i].SelectSingleNode($"{lableXpath}/th[1]/table/tbody/tr[1]/th[1]/table/tr[2]/th[1]"); + + if (currLabel2Node != null) + { + var currLabel2 = currLabel2Node.InnerText.ReplaceHtmlStr(); + + if (!string.IsNullOrWhiteSpace(currLabel2)) + { + labelDict.Add(2, currLabel2); + } + } + + var currValue1 = rootDivNode[i].SelectSingleNode($"{lableXpath}/th[2]/table/tr[1]/th[1]").InnerText.ReplaceHtmlStr(); + + if (!string.IsNullOrWhiteSpace(currValue1)) + { + valDict.Add(1, currValue1); + } + + var currValue2Node = rootDivNode[i].SelectSingleNode($"{lableXpath}/th[2]/table/tr[2]/th[1]"); + + if (currValue2Node != null) + { + var currValue2 = currValue2Node.InnerText.ReplaceHtmlStr(); + + if (!string.IsNullOrWhiteSpace(currValue2)) + { + valDict.Add(2, currValue2); + } + } + + foreach (KeyValuePair kvp in labelDict) + { + if (kvp.Value.Equals("订舱号") || Regex.IsMatch(kvp.Value, "Booking\\s+Number", RegexOptions.IgnoreCase)) + { + dto.BookingNo = valDict[kvp.Key]; + } + else if (kvp.Value.Equals("提单号码") || Regex.IsMatch(kvp.Value, "Bill\\s+of\\s+Lading", RegexOptions.IgnoreCase)) + { + dto.BillNo = valDict[kvp.Key]; + } + } + + string reason1 = string.Empty; + string reason2 = string.Empty; + + bool isReasonNote = false; + + var reasonTables = rootDivNode[i].SelectNodes("./table[contains(@class,'reason')]"); + + if (reasonTables.Count > 0) + { + for (int k = 0; k < reasonTables.Count; k++) + { + var thFirstNode = rootDivNode[i].SelectSingleNode("./table[contains(@class,'reason')][" + (k + 1) + "]/tbody/tr/th[contains(@class,'columns first')]"); + + if (thFirstNode != null) + { + isReasonNote = true; + } + + if (!isReasonNote) + { + string s = rootDivNode[i].SelectSingleNode("./table[contains(@class,'reason')][" + (k + 1) + "]/tr/th").InnerText.ReplaceHtmlStr(); + + if (!string.IsNullOrWhiteSpace(s) && s.IndexOf("此订舱号派生自原单号") >= 0) + { + reasonDict.Add(k + 1, new Tuple(s, Regex.Match(s, "(?<=此订舱号派生自原单号\\:)\\?\\w+").Value?.Trim())); + } + } + else + { + var labelNode = rootDivNode[i].SelectSingleNode("./table[contains(@class,'reason')][" + (k + 1) + "]/tbody/tr/th[contains(@class,'columns first')]"); + var notesNode = rootDivNode[i].SelectSingleNode("./table[contains(@class,'reason')][" + (k + 1) + "]/tbody/tr/th[contains(@class,'columns last')]"); + + string label = string.Empty; + string notes = string.Empty; + + if (labelNode != null) + { + label = labelNode.InnerText.ReplaceHtmlStr(); + } + + if (notesNode != null) + { + notes = notesNode.InnerText.ReplaceHtmlStr(); + } + + if (!string.IsNullOrWhiteSpace(notes)) + { + reasonDict.Add(k + 1, new Tuple(label, notes)); + } + } + } + } + + foreach (KeyValuePair> kvp in reasonDict) + { + if (Regex.IsMatch(kvp.Value.Item1, "此订舱号派生自原单号", RegexOptions.IgnoreCase)) + { + dto.OrigBillNo = kvp.Value.Item2; + } + else if (Regex.IsMatch(kvp.Value.Item1, "((变更原因)|(Reason))", RegexOptions.IgnoreCase)) + { + dto.ChangeReasonNotes = kvp.Value.Item2; + } + } + + #endregion + + #region 解析DATA + //如果下一个节点是data表示后面是详情 + if (rootDivNode[i + 1].Attributes["class"].Value.Equals("data", StringComparison.OrdinalIgnoreCase)) + { + var title = rootDivNode[i + 1].SelectSingleNode("./table[1]/tbody/tr/th[1]/table/tr/th").InnerText.ReplaceHtmlStr(); + var title2 = rootDivNode[i + 1].SelectSingleNode("./table[2]/tbody/tr/th[1]/table/tr/th").InnerText.ReplaceHtmlStr(); + + var title3 = rootDivNode[i + 1].SelectSingleNode(".//b[1]"); + + if (title3 != null) + { + dto.PleaseNotes = title3.InnerText.ReplaceHtmlStr(); + } + //From + if (Regex.IsMatch(title, "((出运计划)|(From))", RegexOptions.IgnoreCase)) + { + //port + var portNodes = rootDivNode[i + 1].SelectNodes("./table[1]/tbody/tr/th[2]/table/tr/th//div[contains(@class,'remove') or contains(@class,'updated') or contains(@class,'no-change')]"); + + if (portNodes.Count == 1) + { + string s = portNodes.FirstOrDefault().InnerText; + + var currArg = s.Split(new char[] { ',' }); + + dto.From.portList.Add(new TransPlanHasChangePortDto + { + Indx = 1, + PortName = currArg[0].Trim(), + IsRemoved = portNodes[0].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + CountryCode = currArg[1].Trim(), + TerminalName = currArg[2].Trim(), + }); + } + else if (portNodes.Count > 1) + { + for (int t = 0; t < portNodes.Count; t++) + { + string s = portNodes[t].InnerText; + + var currArg = s.Split(new char[] { ',' }); + + dto.From.portList.Add(new TransPlanHasChangePortDto + { + Indx = t + 1, + PortName = currArg[0].Trim(), + IsRemoved = portNodes[t].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + CountryCode = currArg[1].Trim(), + TerminalName = currArg[2].Trim(), + }); + } + + } + + //date + var dateNodes = rootDivNode[i + 1].SelectNodes("./table[1]/tbody/tr/th[3]/table/tr/th//div[contains(@class,'remove') or contains(@class,'updated') or contains(@class,'no-change')]"); + + if (dateNodes.Count == 1) + { + string s = dateNodes.FirstOrDefault().InnerText; + + var currInfo = new TransPlanHasChangeDateDto + { + Indx = 1, + OrigDateTxt = s, + IsRemoved = dateNodes[0].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + }; + + DateTime currDate = DateTime.MinValue; + + if (!string.IsNullOrWhiteSpace(s)) + { + if (DateTime.TryParse(s, out currDate)) + currInfo.DateVal = currDate; + } + + dto.From.dateList.Add(currInfo); + } + else if (dateNodes.Count > 1) + { + for (int t = 0; t < dateNodes.Count; t++) + { + string s = dateNodes[t].InnerText; + + var currInfo = new TransPlanHasChangeDateDto + { + Indx = t + 1, + OrigDateTxt = s, + IsRemoved = dateNodes[t].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + }; + + DateTime currDate = DateTime.MinValue; + + if (!string.IsNullOrWhiteSpace(s)) + { + if (DateTime.TryParse(s, out currDate)) + currInfo.DateVal = currDate; + } + + dto.From.dateList.Add(currInfo); + } + } + + //vessel + var vesselNodes = rootDivNode[i + 1].SelectNodes("./table[1]/tbody/tr/th[4]/table/tr/th//div[contains(@class,'remove') or contains(@class,'updated') or contains(@class,'no-change')]"); + + if (vesselNodes.Count == 1) + { + string s = vesselNodes.FirstOrDefault().InnerText; + + var currArg = s.Split(new char[] { ',' }); + + dto.From.vesselList.Add(new TransPlanHasChangeVesselVoynoDto + { + Indx = 1, + Vessel = currArg[0].Trim(), + IsRemoved = vesselNodes[0].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + Voyno = currArg[1].Trim(), + Flag = currArg[2].Trim(), + }); + } + else if (vesselNodes.Count > 1) + { + for (int t = 0; t < vesselNodes.Count; t++) + { + string s = vesselNodes[t].InnerText; + + var currArg = s.Split(new char[] { ',' }); + + dto.From.vesselList.Add(new TransPlanHasChangeVesselVoynoDto + { + Indx = t + 1, + Vessel = currArg[0].Trim(), + IsRemoved = vesselNodes[t].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + Voyno = currArg[1].Trim(), + Flag = currArg[2].Trim(), + }); + } + + } + } + + if (Regex.IsMatch(title2, "((到达计划)|(To))", RegexOptions.IgnoreCase)) + { + //to + //port + var portNodes = rootDivNode[i + 1].SelectNodes("./table[2]/tbody/tr/th[2]/table/tr/th//div[contains(@class,'remove') or contains(@class,'updated') or contains(@class,'no-change')]"); + + if (portNodes.Count == 1) + { + string s = portNodes.FirstOrDefault().InnerText; + + var currArg = s.Split(new char[] { ',' }); + + dto.To.portList.Add(new TransPlanHasChangePortDto + { + Indx = 1, + PortName = currArg[0].Trim(), + IsRemoved = portNodes[0].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + CountryCode = currArg[1].Trim(), + TerminalName = currArg[2].Trim(), + }); + } + else if (portNodes.Count > 1) + { + for (int t = 0; t < portNodes.Count; t++) + { + string s = portNodes[t].InnerText; + + var currArg = s.Split(new char[] { ',' }); + + dto.To.portList.Add(new TransPlanHasChangePortDto + { + Indx = t + 1, + PortName = currArg[0].Trim(), + IsRemoved = portNodes[t].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + CountryCode = currArg[1].Trim(), + TerminalName = currArg[2].Trim(), + }); + } + + } + + //date + var dateNodes = rootDivNode[i + 1].SelectNodes("./table[2]/tbody/tr/th[3]/table/tr/th//div[contains(@class,'remove') or contains(@class,'updated') or contains(@class,'no-change')]"); + + if (dateNodes.Count == 1) + { + string s = dateNodes.FirstOrDefault().InnerText; + + var currInfo = new TransPlanHasChangeDateDto + { + Indx = 1, + OrigDateTxt = s, + IsRemoved = dateNodes[0].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + }; + + DateTime currDate = DateTime.MinValue; + + if (!string.IsNullOrWhiteSpace(s)) + { + if (DateTime.TryParse(s, out currDate)) + currInfo.DateVal = currDate; + } + + dto.To.dateList.Add(currInfo); + } + else if (dateNodes.Count > 1) + { + for (int t = 0; t < dateNodes.Count; t++) + { + string s = dateNodes[t].InnerText; + + var currInfo = new TransPlanHasChangeDateDto + { + Indx = t + 1, + OrigDateTxt = s, + IsRemoved = dateNodes[t].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + }; + + DateTime currDate = DateTime.MinValue; + + if (!string.IsNullOrWhiteSpace(s)) + { + if (DateTime.TryParse(s, out currDate)) + currInfo.DateVal = currDate; + } + + dto.To.dateList.Add(currInfo); + } + } + + //vessel + var vesselNodes = rootDivNode[i + 1].SelectNodes("./table[2]/tbody/tr/th[4]/table/tr/th//div[contains(@class,'remove') or contains(@class,'updated') or contains(@class,'no-change')]"); + + if (vesselNodes.Count == 1) + { + string s = vesselNodes.FirstOrDefault().InnerText; + + var currArg = s.Split(new char[] { ',' }); + + dto.To.vesselList.Add(new TransPlanHasChangeVesselVoynoDto + { + Indx = 1, + Vessel = currArg[0].Trim(), + IsRemoved = vesselNodes[0].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + Voyno = currArg[1].Trim(), + Flag = currArg[2].Trim(), + }); + } + else if (vesselNodes.Count > 1) + { + for (int t = 0; t < vesselNodes.Count; t++) + { + string s = vesselNodes[t].InnerText; + + var currArg = s.Split(new char[] { ',' }); + + dto.To.vesselList.Add(new TransPlanHasChangeVesselVoynoDto + { + Indx = t + 1, + Vessel = currArg[0].Trim(), + IsRemoved = vesselNodes[t].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + Voyno = currArg[1].Trim(), + Flag = currArg[2].Trim(), + }); + } + } + } + + list.Add(dto); + } + #endregion + } + } + } + } + + private List GetChildList(HtmlNode node) + { + List list = new List(); + + string firstBillNo = string.Empty; + string firstBookingNo = string.Empty; + //string firstCtnNo = string.Empty; + + bool isDiv = false; + + for (int i = 0; i < node.ChildNodes.Count; i++) + { + if (node.ChildNodes[i].Name.Equals("table", StringComparison.OrdinalIgnoreCase)) + { + var currLabel1 = node.ChildNodes[i].SelectSingleNode($"./tbody/tr[1]/th[1]/table/tr[1]/th[1]/table/tbody/tr[1]/th[1]").InnerText.ReplaceHtmlStr(); + + var currValue2Node = node.ChildNodes[i].SelectSingleNode("./tbody/tr[1]/th[1]/table/tr[1]/th[1]/table/tbody/tr[1]/th[2]"); + + if (!string.IsNullOrWhiteSpace(currLabel1)) + { + if (currLabel1.Equals("订舱号") || Regex.IsMatch(currLabel1, "Booking\\s+Number", RegexOptions.IgnoreCase)) + { + if(currValue2Node != null) + { + firstBookingNo = currValue2Node.InnerText.ReplaceHtmlStr(); + } + } + else if (currLabel1.Equals("提单号码") || Regex.IsMatch(currLabel1, "Bill\\s+of\\s+Lading", RegexOptions.IgnoreCase)) + { + if (currValue2Node != null) + { + firstBillNo = currValue2Node.InnerText.ReplaceHtmlStr(); + } + } + } + break; + } + else + { + if (node.ChildNodes[i].Name.Equals("div", StringComparison.OrdinalIgnoreCase)) + isDiv = true; + } + } + + + //先获取第一层的div + var rootDivNode = node.ChildNodes.Where(a => a.Name.Equals("div", StringComparison.OrdinalIgnoreCase) + && a.Attributes.Contains("class") && (a.Attributes["class"].Value.Equals("data__top") || a.Attributes["class"].Value.Equals("data"))) + .ToList(); + + for (int i = 0; i < rootDivNode.Count; i++) + { + //先取top + if (rootDivNode[i].Attributes["class"].Value.Equals("data__top", StringComparison.OrdinalIgnoreCase)) + { + TransPlanHasChangeDto dto = new TransPlanHasChangeDto + { + From = new TransPlanHasChangeDetailDto + { + portList = new List(), + dateList = new List(), + vesselList = new List() + }, + To = new TransPlanHasChangeDetailDto + { + portList = new List(), + dateList = new List(), + vesselList = new List() + }, + Carrier = "MSK", + ContaNoList = new List() + }; + + if (i == rootDivNode.Count - 1) + { + var innerList = GetChildList(rootDivNode[i]); + + if (innerList != null && innerList.Count > 0) + { + list.AddRange(innerList); + } + } + else + { + #region 解析TOP + var ctnNode = rootDivNode[i].SelectSingleNode("./table[not(contains(@class,'reason'))]/tbody/tr[1]/th[contains(@class,'last')]"); + + if (ctnNode != null) + { + string s = ctnNode.SelectSingleNode("./table/tr/th/table/tbody/tr/th/table/tr/th").InnerText.ReplaceHtmlStr(); + + if (!string.IsNullOrWhiteSpace(s) && (Regex.IsMatch(s, "集装箱号码") || Regex.IsMatch(s, "Container\\(s\\)"))) + { + string ctnStr = Regex.Match(s, "(?<=集装箱号码).*").Value?.Trim(); + + if (string.IsNullOrWhiteSpace(ctnStr)) + { + ctnStr = Regex.Match(s, "(?<=Container\\(s\\)).*").Value?.Trim(); + } + + if (ctnStr.IndexOf(",") >= 0) + { + dto.ContaNoList = ctnStr.Split(new char[] { ',' }).Select(a => a.Trim()).ToList(); + } + else + { + if (!string.IsNullOrWhiteSpace(ctnStr)) + dto.ContaNoList.Add(ctnStr.Trim()); + } + } + } + + Dictionary labelDict = new Dictionary(); + Dictionary valDict = new Dictionary(); + Dictionary> reasonDict = new Dictionary>(); + + string lableXpath = "./table[not(contains(@class,'reason'))]/tbody/tr[1]/th[1]/table/tr[1]/"; + + var currLabel1 = rootDivNode[i].SelectSingleNode($"{lableXpath}/th[1]/table/tbody/tr[1]/th[1]").InnerText.ReplaceHtmlStr(); + + if (!string.IsNullOrWhiteSpace(currLabel1)) + { + labelDict.Add(1, currLabel1); + } + + var currLabel2Node = rootDivNode[i].SelectSingleNode("./table[not(contains(@class,'reason'))]/tbody/tr[1]/th[1]/table/tbody/tr[1]/th[1]/table/tr[2]/th[1]"); + + if (currLabel2Node != null) + { + var currLabel2 = currLabel2Node.InnerText.ReplaceHtmlStr(); + + if (!string.IsNullOrWhiteSpace(currLabel2)) + { + labelDict.Add(2, currLabel2); + } + } + + var currValue1 = rootDivNode[i].SelectSingleNode($"{lableXpath}/th[2]/table/tr[1]/th[1]").InnerText.ReplaceHtmlStr(); + + if (!string.IsNullOrWhiteSpace(currValue1)) + { + valDict.Add(1, currValue1); + } + + var currValue2Node = rootDivNode[i].SelectSingleNode("./table[not(contains(@class,'reason'))]/tbody/tr[1]/th[1]/table/tbody/tr[1]/th[2]/table/tr[2]/th[1]"); + + if (currValue2Node != null) + { + var currValue2 = currValue2Node.InnerText.ReplaceHtmlStr(); + + if (!string.IsNullOrWhiteSpace(currValue2)) + { + valDict.Add(2, currValue2); + } + } + + foreach (KeyValuePair kvp in labelDict) + { + if (kvp.Value.Equals("订舱号") || Regex.IsMatch(kvp.Value, "Booking\\s+Number", RegexOptions.IgnoreCase)) + { + dto.BookingNo = valDict[kvp.Key]; + } + else if (kvp.Value.Equals("提单号码") || Regex.IsMatch(kvp.Value, "Bill\\s+of\\s+Lading", RegexOptions.IgnoreCase)) + { + if (Regex.IsMatch(valDict[kvp.Key], "BL\\s+number\\s+not\\s+yet\\s+generated", RegexOptions.IgnoreCase)) + { + dto.BillNotes = valDict[kvp.Key]; + + if(i ==0) + { + dto.BillNo = firstBillNo; + } + } + else + { + dto.BillNo = valDict[kvp.Key]; + } + } + } + + string reason1 = string.Empty; + string reason2 = string.Empty; + + bool isReasonNote = false; + + var reasonTables = rootDivNode[i].SelectNodes("./table[contains(@class,'reason')]"); + + if (reasonTables.Count > 0) + { + for (int k = 0; k < reasonTables.Count; k++) + { + var thFirstNode = rootDivNode[i].SelectSingleNode("./table[contains(@class,'reason')][" + (k + 1) + "]/tbody/tr/th[contains(@class,'columns first')]"); + + if (thFirstNode != null) + { + isReasonNote = true; + } + + if (!isReasonNote) + { + string s = rootDivNode[i].SelectSingleNode("./table[contains(@class,'reason')][" + (k + 1) + "]/tr/th").InnerText.ReplaceHtmlStr(); + + if (!string.IsNullOrWhiteSpace(s) && s.IndexOf("此订舱号派生自原单号") >= 0) + { + reasonDict.Add(k + 1, new Tuple(s, Regex.Match(s, "(?<=此订舱号派生自原单号\\:)\\?\\w+").Value?.Trim())); + } + } + else + { + var labelNode = rootDivNode[i].SelectSingleNode("./table[contains(@class,'reason')][" + (k + 1) + "]/tbody/tr/th[contains(@class,'columns first')]"); + var notesNode = rootDivNode[i].SelectSingleNode("./table[contains(@class,'reason')][" + (k + 1) + "]/tbody/tr/th[contains(@class,'columns last')]"); + + string label = string.Empty; + string notes = string.Empty; + + if (labelNode != null) + { + label = labelNode.InnerText.ReplaceHtmlStr(); + } + + if (notesNode != null) + { + notes = notesNode.InnerText.ReplaceHtmlStr(); + } + + if (!string.IsNullOrWhiteSpace(notes)) + { + reasonDict.Add(k + 1, new Tuple(label, notes)); + } + } + } + } + + foreach (KeyValuePair> kvp in reasonDict) + { + if (Regex.IsMatch(kvp.Value.Item1, "此订舱号派生自原单号", RegexOptions.IgnoreCase)) + { + dto.OrigBillNo = kvp.Value.Item2; + } + else if (Regex.IsMatch(kvp.Value.Item1, "((变更原因)|(Reason))", RegexOptions.IgnoreCase)) + { + dto.ChangeReasonNotes = kvp.Value.Item2; + } + } + + #endregion + + #region 解析DATA + //如果下一个节点是data表示后面是详情 + if (rootDivNode[i + 1].Attributes["class"].Value.Equals("data", StringComparison.OrdinalIgnoreCase)) + { + var title = rootDivNode[i + 1].SelectSingleNode("./table[1]/tbody/tr/th[1]/table/tr/th").InnerText.ReplaceHtmlStr(); + var title2 = rootDivNode[i + 1].SelectSingleNode("./table[2]/tbody/tr/th[1]/table/tr/th").InnerText.ReplaceHtmlStr(); + + var title3 = rootDivNode[i + 1].SelectSingleNode(".//b[1]"); + + if (title3 != null) + { + dto.PleaseNotes = title3.InnerText.ReplaceHtmlStr(); + } + //From + if (Regex.IsMatch(title, "((出运计划)|(From))", RegexOptions.IgnoreCase)) + { + //port + var portNodes = rootDivNode[i + 1].SelectNodes("./table[1]/tbody/tr/th[2]/table/tr/th//div[contains(@class,'remove') or contains(@class,'updated') or contains(@class,'no-change')]"); + + if (portNodes.Count == 1) + { + string s = portNodes.FirstOrDefault().InnerText; + + var currArg = s.Split(new char[] { ',' }); + + dto.From.portList.Add(new TransPlanHasChangePortDto + { + Indx = 1, + PortName = currArg[0].Trim(), + IsRemoved = portNodes[0].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + CountryCode = currArg[1].Trim(), + TerminalName = currArg[2].Trim(), + }); + } + else if (portNodes.Count > 1) + { + for (int t = 0; t < portNodes.Count; t++) + { + string s = portNodes[t].InnerText; + + var currArg = s.Split(new char[] { ',' }); + + dto.From.portList.Add(new TransPlanHasChangePortDto + { + Indx = t + 1, + PortName = currArg[0].Trim(), + IsRemoved = portNodes[t].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + CountryCode = currArg[1].Trim(), + TerminalName = currArg[2].Trim(), + }); + } + + } + + //date + var dateNodes = rootDivNode[i + 1].SelectNodes("./table[1]/tbody/tr/th[3]/table/tr/th//div[contains(@class,'remove') or contains(@class,'updated') or contains(@class,'no-change')]"); + + if (dateNodes.Count == 1) + { + string s = dateNodes.FirstOrDefault().InnerText; + + var currInfo = new TransPlanHasChangeDateDto + { + Indx = 1, + OrigDateTxt = s, + IsRemoved = dateNodes[0].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + }; + + DateTime currDate = DateTime.MinValue; + + if (!string.IsNullOrWhiteSpace(s)) + { + if (DateTime.TryParse(s, out currDate)) + currInfo.DateVal = currDate; + } + + dto.From.dateList.Add(currInfo); + } + else if (dateNodes.Count > 1) + { + for (int t = 0; t < dateNodes.Count; t++) + { + string s = dateNodes[t].InnerText; + + var currInfo = new TransPlanHasChangeDateDto + { + Indx = t + 1, + OrigDateTxt = s, + IsRemoved = dateNodes[t].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + }; + + DateTime currDate = DateTime.MinValue; + + if (!string.IsNullOrWhiteSpace(s)) + { + if (DateTime.TryParse(s, out currDate)) + currInfo.DateVal = currDate; + } + + dto.From.dateList.Add(currInfo); + } + } + + //vessel + var vesselNodes = rootDivNode[i + 1].SelectNodes("./table[1]/tbody/tr/th[4]/table/tr/th//div[contains(@class,'remove') or contains(@class,'updated') or contains(@class,'no-change')]"); + + if (vesselNodes.Count == 1) + { + string s = vesselNodes.FirstOrDefault().InnerText; + + var currArg = s.Split(new char[] { ',' }); + + dto.From.vesselList.Add(new TransPlanHasChangeVesselVoynoDto + { + Indx = 1, + Vessel = currArg[0].Trim(), + IsRemoved = vesselNodes[0].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + Voyno = currArg[1].Trim(), + Flag = currArg[2].Trim(), + }); + } + else if (vesselNodes.Count > 1) + { + for (int t = 0; t < vesselNodes.Count; t++) + { + string s = vesselNodes[t].InnerText; + + var currArg = s.Split(new char[] { ',' }); + + dto.From.vesselList.Add(new TransPlanHasChangeVesselVoynoDto + { + Indx = t + 1, + Vessel = currArg[0].Trim(), + IsRemoved = vesselNodes[t].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + Voyno = currArg[1].Trim(), + Flag = currArg[2].Trim(), + }); + } + + } + } + + if (Regex.IsMatch(title2, "((到达计划)|(To))", RegexOptions.IgnoreCase)) + { + //to + //port + var portNodes = rootDivNode[i + 1].SelectNodes("./table[2]/tbody/tr/th[2]/table/tr/th//div[contains(@class,'remove') or contains(@class,'updated') or contains(@class,'no-change')]"); + + if (portNodes.Count == 1) + { + string s = portNodes.FirstOrDefault().InnerText; + + var currArg = s.Split(new char[] { ',' }); + + dto.To.portList.Add(new TransPlanHasChangePortDto + { + Indx = 1, + PortName = currArg[0].Trim(), + IsRemoved = portNodes[0].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + CountryCode = currArg[1].Trim(), + TerminalName = currArg[2].Trim(), + }); + } + else if (portNodes.Count > 1) + { + for (int t = 0; t < portNodes.Count; t++) + { + string s = portNodes[t].InnerText; + + var currArg = s.Split(new char[] { ',' }); + + dto.To.portList.Add(new TransPlanHasChangePortDto + { + Indx = t + 1, + PortName = currArg[0].Trim(), + IsRemoved = portNodes[t].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + CountryCode = currArg[1].Trim(), + TerminalName = currArg[2].Trim(), + }); + } + + } + + //date + var dateNodes = rootDivNode[i + 1].SelectNodes("./table[2]/tbody/tr/th[3]/table/tr/th//div[contains(@class,'remove') or contains(@class,'updated') or contains(@class,'no-change')]"); + + if (dateNodes.Count == 1) + { + string s = dateNodes.FirstOrDefault().InnerText; + + var currInfo = new TransPlanHasChangeDateDto + { + Indx = 1, + OrigDateTxt = s, + IsRemoved = dateNodes[0].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + }; + + DateTime currDate = DateTime.MinValue; + + if (!string.IsNullOrWhiteSpace(s)) + { + if (DateTime.TryParse(s, out currDate)) + currInfo.DateVal = currDate; + } + + dto.To.dateList.Add(currInfo); + } + else if (dateNodes.Count > 1) + { + for (int t = 0; t < dateNodes.Count; t++) + { + string s = dateNodes[t].InnerText; + + var currInfo = new TransPlanHasChangeDateDto + { + Indx = t + 1, + OrigDateTxt = s, + IsRemoved = dateNodes[t].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + }; + + DateTime currDate = DateTime.MinValue; + + if (!string.IsNullOrWhiteSpace(s)) + { + if (DateTime.TryParse(s, out currDate)) + currInfo.DateVal = currDate; + } + + dto.To.dateList.Add(currInfo); + } + } + + //vessel + var vesselNodes = rootDivNode[i + 1].SelectNodes("./table[2]/tbody/tr/th[4]/table/tr/th//div[contains(@class,'remove') or contains(@class,'updated') or contains(@class,'no-change')]"); + + if (vesselNodes.Count == 1) + { + string s = vesselNodes.FirstOrDefault().InnerText; + + var currArg = s.Split(new char[] { ',' }); + + dto.To.vesselList.Add(new TransPlanHasChangeVesselVoynoDto + { + Indx = 1, + Vessel = currArg[0].Trim(), + IsRemoved = vesselNodes[0].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + Voyno = currArg[1].Trim(), + Flag = currArg[2].Trim(), + }); + } + else if (vesselNodes.Count > 1) + { + for (int t = 0; t < vesselNodes.Count; t++) + { + string s = vesselNodes[t].InnerText; + + var currArg = s.Split(new char[] { ',' }); + + dto.To.vesselList.Add(new TransPlanHasChangeVesselVoynoDto + { + Indx = t + 1, + Vessel = currArg[0].Trim(), + IsRemoved = vesselNodes[t].Attributes["class"].Value.Equals("remove", StringComparison.OrdinalIgnoreCase), + Voyno = currArg[1].Trim(), + Flag = currArg[2].Trim(), + }); + } + } + } + + list.Add(dto); + } + #endregion + } + } + } + + return list; + } + #region 读BC详情详情 /// /// 读BC详情详情 @@ -1486,4 +2504,197 @@ public class TaskBCInfoReadCtnDto /// 还箱场站 /// public string ReturnCTNYard { get; set; } +} + +/// +/// MSK 您的货物运输计划已变更 +/// +public class TransPlanHasChangeDto +{ + /// + /// 订舱编号 + /// + public string BookingNo { get; set; } + + /// + /// 船公司代码 + /// + public string Carrier { get; set; } + + /// + /// 提单号码 + /// + public string BillNo { get; set; } + + /// + /// 派生自原单号 + /// + public string OrigBillNo { get; set; } + + /// + /// 集装箱号列表 + /// + public List ContaNoList { get; set; } + + /// + /// 变更原因 + /// + public string ChangeReasonNotes { get; set; } + + /// + /// 出运计划 + /// + public TransPlanHasChangeDetailDto From { get; set; } + + /// + /// 到达计划 + /// + public TransPlanHasChangeDetailDto To { get; set; } + + /// + /// 特别提示 + /// + public string PleaseNotes { get; set; } + + /// + /// 提单号特殊提示 + /// + public string BillNotes { get; set; } +} + +/// +/// 您的货物运输计划已变更明细 +/// +public class TransPlanHasChangeDetailDto +{ + /// + /// 港口变更明细 + /// + public List portList { get; set; } + + /// + /// 日期变更明细(ETD或者ATD) + /// + public List dateList { get; set; } + + /// + /// 船名航次变更 + /// + public List vesselList { get; set; } +} + +/// +/// 您的货物运输计划已变更-港口 +/// +public class TransPlanHasChangePortDto +{ + /// + /// 顺序号 + /// + public int Indx { get; set; } + /// + /// 港口名 + /// + public string PortName { get; set; } + + /// + /// 国家 + /// + public string CountryCode { get; set; } + + /// + /// 码头 + /// + public string TerminalName { get; set; } + + /// + /// 是否被作废掉了 + /// + public bool IsRemoved { get; set; } = false; +} + +/// +/// 您的货物运输计划已变更-日期 +/// +public class TransPlanHasChangeDateDto +{ + /// + /// 顺序号 + /// + public int Indx { get; set; } + + /// + /// 提取日期文本 + /// + public string OrigDateTxt { get; set; } + + /// + /// 日期 + /// + public DateTime DateVal { get; set; } + + /// + /// 是否被作废掉了 + /// + public bool IsRemoved { get; set; } = false; +} + +/// +/// 您的货物运输计划已变更-船名航次 +/// +public class TransPlanHasChangeVesselVoynoDto +{ + /// + /// 顺序号 + /// + public int Indx { get; set; } + + /// + /// 船名 + /// + public string Vessel { get; set; } + + /// + /// 航次 + /// + public string Voyno { get; set; } + + /// + /// 船旗 + /// + public string Flag { get; set; } + + /// + /// 是否被作废掉了 + /// + public bool IsRemoved { get; set; } = false; +} + +public static class StringUtilsExtension +{ + /// + /// 格式化文件名,去掉非法字符 + /// + /// 请求参数 + /// 返回格式化表达式值 + public static string FormatFileName(this string inputVal) + { + if (!string.IsNullOrWhiteSpace(inputVal)) + return Regex.Replace(inputVal, "\\|\\/|\\:|\\*|\\?|\\\"|\\<|\\>|\\|", "_"); + + return inputVal; + } + + /// + /// 清理HTML字符 + /// + /// 请求参数 + /// 返回格式化表达式值 + public static string ReplaceHtmlStr(this string inputVal) + { + if (!string.IsNullOrWhiteSpace(inputVal)) + return Regex.Replace(Regex.Replace(inputVal, "\\ \\;", " "), "\\s{2,}", " ")?.Trim(); + + return inputVal; + } } \ No newline at end of file diff --git a/ServiceProjectSyncWin/ServiceProjectSyncWin.csproj b/ServiceProjectSyncWin/ServiceProjectSyncWin.csproj index 286dfa60..1f09d388 100644 --- a/ServiceProjectSyncWin/ServiceProjectSyncWin.csproj +++ b/ServiceProjectSyncWin/ServiceProjectSyncWin.csproj @@ -9,6 +9,7 @@ +