using Furion; using Furion.DatabaseAccessor; using Furion.DependencyInjection; 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; 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; using System.Text; using System.Text.RegularExpressions; using System.Web; using static System.Net.Mime.MediaTypeNames; Serve.RunGeneric(additional: services => { services.AddRemoteRequest(); services.AddSqlsugarSetup(App.Configuration); }, true, true); Console.WriteLine("开始准备同步历史服务状态数据"); var service1 = App.GetService(); //service1.SyncServiceProjectRecord(); service1.SyncServiceProjectRecord5(); Console.ReadKey(); public interface ISyncHisRecord { void SyncServiceProjectRecord4(); void SyncServiceProjectRecord5(); //void SyncServiceProjectRecord2(); //void SyncServiceProjectRecord3(); } public class SyncHisRecord: ISyncHisRecord,ITransient { SqlSugar.ISqlSugarClient _db; private readonly ILogger _logger; public SyncHisRecord(ISqlSugarClient db, ILogger logger) { _db = db; _logger = logger; } public void SyncServiceProjectRecord() { int totalSyncNum = 0; int succSyncNum = 0; long maxId = 0; //349708986646597/中集世联达领鲜物流科技(山东)有限公司 long tenantId = 349708986646597; string batchNo = Guid.NewGuid().ToString(); _logger.LogInformation("批次={no} 触发同步货物状态 tenantId={tenantId}", batchNo, tenantId); try { var tenantInfo = _db.Queryable().First(a => a.Id == tenantId); /* 1、按批次读取服务状态数据。(去掉没有完成时间的、并且订舱数据是已删除的记录) 2、写入历史记录表。 3、单票生成触发报文,并推送状态。 4、更新历史记录,标记同步状态 */ maxId = _db.Queryable().Max(a => a.ORG_STATUS_ID); _logger.LogInformation("批次={no} 获取最后同步服务状态的ID={maxId}", batchNo, maxId); int takeNum = 1000; while (true) { var takeList = _db.Queryable() .InnerJoin((gs, cfg) => gs.ConfigId == cfg.Id) .InnerJoin((gs, cfg, bk) => gs.bookingId == bk.Id) .Where((gs, cfg, bk) => gs.FinishTime.HasValue && gs.TenantId == tenantId && !bk.IsDeleted && gs.FinishUserId != 142307070910551 && (maxId == 0 || gs.Id > maxId)) .OrderBy((gs, cfg, bk) => gs.Id) .Select((gs, cfg, bk) => new { GS = gs, CFG = cfg, BK = bk }) .Take(takeNum).ToList(); totalSyncNum += takeList.Count; Console.WriteLine($"批次={batchNo} 同步待处理任务 totalSyncNum={totalSyncNum}"); _logger.LogInformation("批次={no} 同步待处理任务 totalSyncNum={totalSyncNum}", batchNo, totalSyncNum); //没有记录跳出循环 if (takeList.Count == 0) { Console.WriteLine($"没有记录跳出循环 total={succSyncNum}"); break; } maxId = takeList.Max(a => a.GS.Id); _logger.LogInformation("批次={no} 获取最后同步服务状态的 maxId={maxId}", batchNo, maxId); //写入记录表 takeList.ForEach(async record => { var entity = new ServiceStatusBookingSyncHisInfo { PK_ID = Guid.NewGuid().ToString(), ORG_STATUS_ID = record.GS.Id, BOOKING_ID = record.BK.Id, FINISH_TIME = record.GS.FinishTime.Value, FINISH_USER_ID = record.GS.FinishUserId.Value, FINISH_USER_NAME = record.GS.FinishUser, MBL_NO = record.BK.MBLNO, SORT_NO = record.CFG.Sort, STATUS_SKU_CODE = record.CFG.SystemCode, STATUS_SKU_NAME = record.CFG.StatusName, STATUS_REMARK = record.GS.Remark, STATUS_VAL = record.GS.ExtData, TENANT_ID = record.GS.TenantId.Value, TENANT_NAME = tenantInfo.Name, VESSEL = record.BK.VESSEL, VOYNO = record.BK.VOYNO, }; _db.Insertable(entity).ExecuteCommand(); //await _serviceStatusBookingSyncHisInfoRepository.InsertAsync(entity); succSyncNum++; Console.WriteLine($"ORG_STATUS_ID={record.GS.Id} 写入成功 total={succSyncNum}"); Thread.Sleep(300); }); Console.WriteLine($"等待500毫秒"); Thread.Sleep(500); } //停用的状态(接受委托、放箱指令、已发账单、账单确认、账单已回传) string[] deletedStatusCodeArg = new string[] { "JSWTUO", "FXZLING", "YFZD", "ZDQR", "ZDYHC" }; /* 单票触发推送状态 1、取状态是null的,并且每次取前100个订舱记录。 2、按订舱记录取所有的状态记录。 3、生成触发脚本推送PUSH状态。(部分状态自动不执行) 4、执行成功后,更新对应的状态。 */ Console.WriteLine($"开始推送记录 succ={succSyncNum}"); while (true) { var bookingList = _db.Queryable() .Where(a => a.TENANT_ID == tenantId && a.STATUS == null) .Take(100).Select(a => a.BOOKING_ID).Distinct().ToList(); //无数据跳出 if (bookingList.Count == 0) { Console.WriteLine($"没有待推送记录跳出循环 succ={succSyncNum}"); break; } var taskList = _db.Queryable() .Where(a => a.TENANT_ID == tenantId && bookingList.Contains(a.BOOKING_ID)).ToList(); taskList.GroupBy(a => a.BOOKING_ID).ToList().ForEach(a => { DateTime currDate = DateTime.Now; var currStatusList = a.ToList(); var groupCheckList = currStatusList .Where(b => !deletedStatusCodeArg.Contains(b.STATUS_SKU_CODE)) .GroupBy(b => b.STATUS_SKU_CODE).Select(b => { var currList = b.ToList(); if (currList.Count == 1) return currList.FirstOrDefault(); return currList.OrderByDescending(c => c.ORG_STATUS_ID).FirstOrDefault(); }).ToList(); if (groupCheckList.Count > 0) { var firstInfo = groupCheckList.FirstOrDefault(); TrackingMessageInfo msgInfo = new TrackingMessageInfo { Head = new TrackingMessageHeadInfo { GID = IDGen.NextID().ToString(), MessageType = "PROJECT", ReceiverId = "ServiceProjectStatus", ReceiverName = "服务项目和状态", SenderId = "BookingOrder", SenderName = "海运订舱", RequestDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"), Version = "2.0", RequestAction = "AddOrModify", }, Main = new TrackingMessageMainInfo { BusiId = firstInfo.BOOKING_ID.ToString(), BusiSystemCode = "BOOKING_ORDER", MBlNo = firstInfo.MBL_NO, VesselVoyno = $"{firstInfo.VESSEL}/{firstInfo.VOYNO}", OrderNo = "", PushType = TrackingPushTypeEnum.Status, OperTenantId = firstInfo.TENANT_ID, OperTenantName = firstInfo.TENANT_NAME, OpertType = TrackingOperTypeEnum.AUTO, OperUserId = firstInfo.FINISH_USER_ID.ToString(), OperUserName = firstInfo.FINISH_USER_NAME, SourceType = TrackingSourceTypeEnum.AUTO, StatusList = groupCheckList.Select(a => new TrackingMessageMainStatusInfo { StatusCode = a.STATUS_SKU_CODE, StatusDate = a.FINISH_TIME, StatusVal = a.STATUS_VAL, Remark = a.STATUS_REMARK }).ToList() } }; Console.WriteLine($"准备PUSH状态 msg={JSON.Serialize(msgInfo.Main)}"); //推送状态 var pushRlt = PushStatus(msgInfo).GetAwaiter().GetResult(); Console.WriteLine($"PUSH返回结果 rlt={JSON.Serialize(pushRlt)}"); groupCheckList.ForEach(async t => { t.STATUS = pushRlt.succ ? "SUCC" : "FAILURE"; t.SYNC_TIME = currDate; t.SYNC_RESULT = pushRlt.msg; if (deletedStatusCodeArg.Contains(t.STATUS_SKU_CODE)) { t.STATUS = "FAILURE"; t.SYNC_RESULT = "状态已取消不再同步"; } await _db.Updateable(t) .UpdateColumns(it => new { it.STATUS, it.SYNC_TIME, it.SYNC_RESULT }).ExecuteCommandAsync(); }); var undoList = currStatusList.GroupJoin(groupCheckList, l => l.ORG_STATUS_ID, r => r.ORG_STATUS_ID, (l, r) => { var currList = r.ToList(); if (currList.Count == 0) return new { IsUpdate = true, Obj = l }; return new { IsUpdate = false, Obj = l }; }).Where(c => c.IsUpdate) .Select(c => c.Obj).ToList(); if (undoList.Count > 0) { undoList.ForEach(async t => { t.STATUS = "FAILURE"; t.SYNC_RESULT = "状态已取最后触发记录"; t.SYNC_TIME = currDate; if (deletedStatusCodeArg.Contains(t.STATUS_SKU_CODE)) { t.SYNC_RESULT = "状态已取消不再同步"; } await _db.Updateable(t) .UpdateColumns(it => new { it.STATUS, it.SYNC_TIME, it.SYNC_RESULT }).ExecuteCommandAsync(); }); } Console.WriteLine($"更新表结束"); } else { Console.WriteLine($"没有可用记录"); } Thread.Sleep(300); }); Thread.Sleep(500); } } catch(Exception ex) { _logger.LogInformation("批次={no} 同步异常ex={ex}", batchNo, maxId); } } public async Task PushStatus(TrackingMessageInfo info) { TaskManageOrderResultDto model = null; /* 1、读取配置文件中的规则引擎URL 2、填充请求的类,并生成JSON报文 3、POST请求接口,并记录回执。 4、返回信息。 */ var url = App.Configuration["ServiceStatusPushUrl"]; try { var res = await url.SetHttpMethod(HttpMethod.Post) .SetBody(JSON.Serialize(info), "application/json") .SetContentEncoding(Encoding.UTF8) .PostAsync(); _logger.LogInformation("批次={no} 对应请求报文完成 res={res}", info.Head.GID, JSON.Serialize(res)); if (res.StatusCode == System.Net.HttpStatusCode.OK) { var userResult = await res.Content.ReadAsStringAsync(); var cmRlt = JSON.Deserialize(userResult); if(cmRlt.success) model = JSON.Deserialize(JSON.Serialize(cmRlt.data)); } } catch (Exception ex) { //写日志 if (ex is HttpRequestException) throw Oops.Oh(10000002); } return model; } public void SyncServiceProjectRecord2() { /* string fmt = "(16)PortofDischarge(17)PlaceofDelivery"; var fmtList = fmt.Select(a => a).ToList(); string name = "(1P6)E PNorAt oNf GDi,scMhaArgLeA YSIA P(1E7)N PAlaNceG o,f DMeAlivLerAy YSIA"; List array = new List(); List array2 = new List(); for (var i=0;i< name.Length;i++) { bool isExists = false; if(fmtList.Count > 0) { if (name[i] == fmtList.First()) { array.Add(name[i]); fmtList.Remove(fmtList[0]); isExists = true; } } if (!isExists) array2.Add(name[i]); } string s1 = string.Join("", array); string s2 = string.Join("", array2); */ string Consignee = "LOT 13/3,KAWASAN PERINDUSTRIAN, K AMPUNG KOLAM PADANG BESAR,02100 P ADANG BESAR,PERLIS. T EL;04-949 0507 FAX 94-949 2888"; string NotifyParty = "SHUN EE TRADING SDN BHD (1183208 U) LOT 13/3,KAWASAN PERINDUSTRIAN, K AMPUNG KOLAM PADANG BESAR,02100 P ADANG BESAR,PERLIS. T EL;04-949 0507 FAX 94-949 2888"; if (Regex.IsMatch(Consignee, "\\b[a-zA-Z]{1}\\b\\s+\\b[a-zA-Z]+\\b")) { Consignee = Regex.Replace(Consignee, "\\b[a-zA-Z]{1}\\b\\s+\\b[a-zA-Z]+\\b", m => Regex.Replace(m.Value, "\\s+", "")); } if (Regex.IsMatch(NotifyParty, "\\b[a-zA-Z]{1}\\b\\s+\\b[a-zA-Z]+\\b")) { NotifyParty = Regex.Replace(NotifyParty, "\\b[a-zA-Z]{1}\\b\\s+\\b[a-zA-Z]+\\b", m => Regex.Replace(m.Value, "\\s+", "")); } decimal similarity = 100; for (int i = 0; i < NotifyParty.Length; i++) { } } public void SyncServiceProjectRecord4() { /* 批量更新SI 截止时间 1、更新任务台 2、更新舱位的截止时间记录 */ var taskList = _db.Queryable().Where(a => (a.BUSI_TYPE == "BookingConfirmation" || a.BUSI_TYPE == "BookingAmendment")) .OrderBy(t=>t.CreatedTime) .ToList(); string batchNo = Guid.NewGuid().ToString(); _logger.LogInformation($"批次={batchNo} 提取待处理任务 num={taskList.Count}"); string bcReadUrl = "http://47.104.73.97:7115/api/TaskBCParser/ExcuteBCFileRead"; string amendReadUrl = "http://47.104.73.97:7115/api/TaskBookingAmendmentParser/ExcuteBookingAmendmentRead"; //foreach (var task in taskList) for(int i=0;i().Where(a => a.TASK_PKID == task.TASK_ID && a.FILE_CATEGORY == "BC").First(); if (fileInfo != null) { _logger.LogInformation($"批次={batchNo} 提取待处理任务 MBLNO={task.MBL_NO} 取到文件"); string bcFileFullPath = $"D:\\djy\\backend\\wwwroot\\{fileInfo.FILE_PATH}"; var bcFileName = Path.GetFileName(bcFileFullPath); if(!File.Exists(bcFileFullPath)) { _logger.LogInformation($"批次={batchNo} 提取待处理任务 MBLNO={task.MBL_NO} 取到文件不存在 path={bcFileFullPath}"); continue; } TaskBCInfoReadDto BCReadInfo = GetBCReaderInfo(bcFileFullPath, bcFileName, task.TenantId.Value, task.TASK_ID, bcReadUrl).GetAwaiter().GetResult(); _logger.LogInformation($"批次={batchNo} 提取待处理任务 MBLNO={task.MBL_NO} 识别完文件 BCReadInfo={JSON.Serialize(BCReadInfo)}"); StringBuilder msgBuilder = new StringBuilder(); if (BCReadInfo != null) { //SI_CUT_DATE if (BCReadInfo.SICutDate.HasValue) { if (!task.SI_CUT_DATE.HasValue || task.SI_CUT_DATE.Value != BCReadInfo.SICutDate.Value) { msgBuilder.Append($"SI_CUT_DATE org={task.SI_CUT_DATE} tar={BCReadInfo.SICutDate.Value}##"); task.SI_CUT_DATE = BCReadInfo.SICutDate.Value; } } else { msgBuilder.Append($"SI_CUT_DATE org={task.SI_CUT_DATE} tar=null"); task.SI_CUT_DATE = null; } //VGM_CUTOFF_TIME if (BCReadInfo.VGMCutoffTime.HasValue) { if (!task.VGM_CUTOFF_TIME.HasValue || task.VGM_CUTOFF_TIME.Value != BCReadInfo.VGMCutoffTime.Value) { msgBuilder.Append($"VGM_CUTOFF_TIME org={task.VGM_CUTOFF_TIME} tar={BCReadInfo.VGMCutoffTime.Value}##"); task.VGM_CUTOFF_TIME = BCReadInfo.VGMCutoffTime.Value; } } else { msgBuilder.Append($"VGM_CUTOFF_TIME org={task.VGM_CUTOFF_TIME} tar=null##"); task.VGM_CUTOFF_TIME = null; } //MANIFEST_CUT_DATE if (BCReadInfo.ManifestCutDate.HasValue) { if (!task.MANIFEST_CUT_DATE.HasValue || task.MANIFEST_CUT_DATE.Value != BCReadInfo.ManifestCutDate.Value) { msgBuilder.Append($"MANIFEST_CUT_DATE org={task.MANIFEST_CUT_DATE} tar={BCReadInfo.ManifestCutDate.Value}##"); task.MANIFEST_CUT_DATE = BCReadInfo.ManifestCutDate.Value; } } else { msgBuilder.Append($"MANIFEST_CUT_DATE org={task.MANIFEST_CUT_DATE} tar=null##"); task.MANIFEST_CUT_DATE = null; } //CY_CUTOFF_TIME if (BCReadInfo.CYCutoffTime.HasValue) { if (!task.MANIFEST_CUT_DATE.HasValue || task.MANIFEST_CUT_DATE.Value != BCReadInfo.CYCutoffTime.Value) { msgBuilder.Append($"CY_CUTOFF_TIME org={task.CY_CUTOFF_TIME} tar={BCReadInfo.CYCutoffTime.Value}##"); task.CY_CUTOFF_TIME = BCReadInfo.CYCutoffTime.Value; } } else { msgBuilder.Append($"CY_CUTOFF_TIME org={task.CY_CUTOFF_TIME} tar=null##"); task.CY_CUTOFF_TIME = null; } //MDGF_CUT_DATE if (BCReadInfo.MDGFCutDate.HasValue) { if (!task.MDGF_CUT_DATE.HasValue || task.MDGF_CUT_DATE.Value != BCReadInfo.MDGFCutDate.Value) { msgBuilder.Append($"MDGF_CUT_DATE org={task.MDGF_CUT_DATE} tar={BCReadInfo.MDGFCutDate.Value}##"); task.MDGF_CUT_DATE = BCReadInfo.MDGFCutDate.Value; } } else { msgBuilder.Append($"MDGF_CUT_DATE org={task.MDGF_CUT_DATE} tar=null##"); task.MDGF_CUT_DATE = null; } //CLOSING_DATE if (BCReadInfo.ClosingDate.HasValue) { if (!task.CLOSING_DATE.HasValue || task.CLOSING_DATE.Value != BCReadInfo.ClosingDate.Value) { msgBuilder.Append($"CLOSING_DATE org={task.CLOSING_DATE} tar={BCReadInfo.ClosingDate.Value}##"); task.CLOSING_DATE = BCReadInfo.ClosingDate.Value; } } else { msgBuilder.Append($"CLOSING_DATE org={task.CLOSING_DATE} tar=null##"); task.CLOSING_DATE = null; } //CLOSING_DATE if (BCReadInfo.CustomSICutDate.HasValue) { if (!task.CUSTOM_SI_CUT_DATE.HasValue || task.CUSTOM_SI_CUT_DATE.Value != BCReadInfo.CustomSICutDate.Value) { msgBuilder.Append($"CUSTOM_SI_CUT_DATE org={task.CUSTOM_SI_CUT_DATE} tar={BCReadInfo.CustomSICutDate.Value}##"); task.CUSTOM_SI_CUT_DATE = BCReadInfo.CustomSICutDate.Value; } } else { msgBuilder.Append($"CUSTOM_SI_CUT_DATE org={task.CUSTOM_SI_CUT_DATE} tar=null##"); task.CUSTOM_SI_CUT_DATE = null; } _db.Updateable(task).UpdateColumns(it => new { it.SI_CUT_DATE, it.CUSTOM_SI_CUT_DATE, it.VGM_CUTOFF_TIME, it.MANIFEST_CUT_DATE, it.CY_CUTOFF_TIME, it.MDGF_CUT_DATE, it.CLOSING_DATE }).ExecuteCommand(); } _logger.LogInformation($"批次={batchNo} 提取待处理任务 MBLNO={task.MBL_NO} 识别完文件 有变更 结果={msgBuilder.ToString()}"); } } else if (task.BUSI_TYPE == "BookingAmendment") { var fileInfo = _db.Queryable().Where(a => a.TASK_PKID == task.TASK_ID && a.FILE_CATEGORY == "BC_MODIFY").First(); if (fileInfo != null) { string bcFileFullPath = $"D:\\djy\\backend\\wwwroot\\{fileInfo.FILE_PATH}"; var bcFileName = Path.GetFileName(bcFileFullPath); if (!File.Exists(bcFileFullPath)) { _logger.LogInformation($"批次={batchNo} 提取待处理任务 MBLNO={task.MBL_NO} 取到文件不存在 path={bcFileFullPath}"); continue; } TaskBCInfoReadDto BCReadInfo = GetBCReaderInfo(bcFileFullPath, bcFileName, task.TenantId.Value, task.TASK_ID, amendReadUrl).GetAwaiter().GetResult(); StringBuilder msgBuilder = new StringBuilder(); if (BCReadInfo != null) { //SI_CUT_DATE if (BCReadInfo.SICutDate.HasValue) { if (!task.SI_CUT_DATE.HasValue || task.SI_CUT_DATE.Value != BCReadInfo.SICutDate.Value) { msgBuilder.Append($"SI_CUT_DATE org={task.SI_CUT_DATE} tar={BCReadInfo.SICutDate.Value}##"); task.SI_CUT_DATE = BCReadInfo.SICutDate.Value; } } else { msgBuilder.Append($"SI_CUT_DATE org={task.SI_CUT_DATE} tar=null"); task.SI_CUT_DATE = null; } //VGM_CUTOFF_TIME if (BCReadInfo.VGMCutoffTime.HasValue) { if (!task.VGM_CUTOFF_TIME.HasValue || task.VGM_CUTOFF_TIME.Value != BCReadInfo.VGMCutoffTime.Value) { msgBuilder.Append($"VGM_CUTOFF_TIME org={task.VGM_CUTOFF_TIME} tar={BCReadInfo.VGMCutoffTime.Value}##"); task.VGM_CUTOFF_TIME = BCReadInfo.VGMCutoffTime.Value; } } else { msgBuilder.Append($"VGM_CUTOFF_TIME org={task.VGM_CUTOFF_TIME} tar=null##"); task.VGM_CUTOFF_TIME = null; } //MANIFEST_CUT_DATE if (BCReadInfo.ManifestCutDate.HasValue) { if (!task.MANIFEST_CUT_DATE.HasValue || task.MANIFEST_CUT_DATE.Value != BCReadInfo.ManifestCutDate.Value) { msgBuilder.Append($"MANIFEST_CUT_DATE org={task.MANIFEST_CUT_DATE} tar={BCReadInfo.ManifestCutDate.Value}##"); task.MANIFEST_CUT_DATE = BCReadInfo.ManifestCutDate.Value; } } else { msgBuilder.Append($"MANIFEST_CUT_DATE org={task.MANIFEST_CUT_DATE} tar=null##"); task.MANIFEST_CUT_DATE = null; } //CY_CUTOFF_TIME if (BCReadInfo.CYCutoffTime.HasValue) { if (!task.MANIFEST_CUT_DATE.HasValue || task.MANIFEST_CUT_DATE.Value != BCReadInfo.CYCutoffTime.Value) { msgBuilder.Append($"CY_CUTOFF_TIME org={task.CY_CUTOFF_TIME} tar={BCReadInfo.CYCutoffTime.Value}##"); task.CY_CUTOFF_TIME = BCReadInfo.CYCutoffTime.Value; } } else { msgBuilder.Append($"CY_CUTOFF_TIME org={task.CY_CUTOFF_TIME} tar=null##"); task.CY_CUTOFF_TIME = null; } //MDGF_CUT_DATE if (BCReadInfo.MDGFCutDate.HasValue) { if (!task.MDGF_CUT_DATE.HasValue || task.MDGF_CUT_DATE.Value != BCReadInfo.MDGFCutDate.Value) { msgBuilder.Append($"MDGF_CUT_DATE org={task.MDGF_CUT_DATE} tar={BCReadInfo.MDGFCutDate.Value}##"); task.MDGF_CUT_DATE = BCReadInfo.MDGFCutDate.Value; } } else { msgBuilder.Append($"MDGF_CUT_DATE org={task.MDGF_CUT_DATE} tar=null##"); task.MDGF_CUT_DATE = null; } //CLOSING_DATE if (BCReadInfo.ClosingDate.HasValue) { if (!task.CLOSING_DATE.HasValue || task.CLOSING_DATE.Value != BCReadInfo.ClosingDate.Value) { msgBuilder.Append($"CLOSING_DATE org={task.CLOSING_DATE} tar={BCReadInfo.ClosingDate.Value}##"); task.CLOSING_DATE = BCReadInfo.ClosingDate.Value; } } else { msgBuilder.Append($"CLOSING_DATE org={task.CLOSING_DATE} tar=null##"); task.CLOSING_DATE = null; } //CLOSING_DATE if (BCReadInfo.CustomSICutDate.HasValue) { if (!task.CUSTOM_SI_CUT_DATE.HasValue || task.CUSTOM_SI_CUT_DATE.Value != BCReadInfo.CustomSICutDate.Value) { msgBuilder.Append($"CUSTOM_SI_CUT_DATE org={task.CUSTOM_SI_CUT_DATE} tar={BCReadInfo.CustomSICutDate.Value}##"); task.CUSTOM_SI_CUT_DATE = BCReadInfo.CustomSICutDate.Value; } } else { msgBuilder.Append($"CUSTOM_SI_CUT_DATE org={task.CUSTOM_SI_CUT_DATE} tar=null##"); task.CUSTOM_SI_CUT_DATE = null; } _db.Updateable(task).UpdateColumns(it => new { it.SI_CUT_DATE, it.CUSTOM_SI_CUT_DATE, it.VGM_CUTOFF_TIME, it.MANIFEST_CUT_DATE, it.CY_CUTOFF_TIME, it.MDGF_CUT_DATE, it.CLOSING_DATE }).ExecuteCommand(); } _logger.LogInformation($"批次={batchNo} 提取待处理任务 MBLNO={task.MBL_NO} 识别完文件 有变更 结果={msgBuilder.ToString()}"); } } Thread.Sleep(500); } } 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详情详情 /// /// 文件完整路径 /// 文件名称 /// 所属租户 /// 任务ID /// 请求URL /// private async Task GetBCReaderInfo(string attachFullName, string fileName, long tenantId, string taskPKId, string url) { TaskBCInfoReadDto taskBCInfoReadDto = null; try { DateTime nowDate = DateTime.Now; EmailBCReadMessageInfo messageInfo = new EmailBCReadMessageInfo { Head = new TaskMessageHead { GID = IDGen.NextID().ToString(), MessageType = "BOOKING_AMENDMENT", SenderId = "DJY", SenderName = "新大简云", ReceiverId = "RulesEngine", ReceiverName = "大简云规则引擎", Version = "1.0", RequestDate = nowDate.ToString("yyyy-MM-dd HH:mm:ss"), RequestAction = "ReadFile", }, Main = new EmailBCReadMessageMainInfo { TenantId = tenantId > 0 ? tenantId.ToString() : "" } }; NameValueCollection par = new NameValueCollection(); par.Add("jsonData", JSON.Serialize(messageInfo)); //解析BookingAmendment var compareRlt = await ExcuteReadFile(par, url, new { file = "file", fileName = fileName, fileBytes = File.ReadAllBytes(attachFullName) }); _logger.LogInformation($"读取BC附件详情 taskPKId={taskPKId},compareRlt={JSON.Serialize(compareRlt)}"); if (compareRlt.succ) { taskBCInfoReadDto = JSON.Deserialize(JSON.Serialize(compareRlt.extra)); } } catch (Exception ex) { _logger.LogError($"读取BC附件详情异常,原因:{ex.Message}"); } return taskBCInfoReadDto; } #endregion #region 请求BookingAmendment解析 /// /// 请求BookingAmendment解析 /// /// 请求参数 /// 请求url /// 文件 /// 请求类型 /// 返回回执 [NonAction] private async Task ExcuteReadFile(NameValueCollection nameValueCollection, string url, dynamic fileInfo, string contentType = "application/json") { ParserReaderExcuteResultDto model = null; var result = string.Empty; using (var httpClient = new HttpClient()) { try { using (var reduceAttach = new MultipartFormDataContent()) { string[] allKeys = nameValueCollection.AllKeys; foreach (string key in allKeys) { var dataContent = new ByteArrayContent(Encoding.UTF8.GetBytes(nameValueCollection[key])); dataContent.Headers.ContentDisposition = new ContentDispositionHeaderValue($"form-data") { Name = key }; reduceAttach.Add(dataContent); } #region 文件参数 if (fileInfo != null) { var Content = new ByteArrayContent(fileInfo.fileBytes); //Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") //{ // Name = fileInfo.file.ToString(), // FileName = fileInfo.fileName.ToString(), //}; Content.Headers.Add("Content-Type", contentType); reduceAttach.Add(Content, fileInfo.file.ToString(), HttpUtility.UrlEncode(fileInfo.fileName.ToString())); } #endregion //httpClient.DefaultRequestHeaders.Add("USER_KEY", App.Configuration["ApiUserKey"]); //httpClient.DefaultRequestHeaders.Add("USER_SECRET", App.Configuration["ApiUserSecret"]); //请求 var response = httpClient.PostAsync(url, reduceAttach).Result; result = response.Content.ReadAsStringAsync().Result; model = JSON.Deserialize(result); } } catch (Exception ex) { _logger.LogInformation("请求读取BC附件详情读取详情异常,原因:{error}", ex.Message); throw Oops.Oh($"请求读取BC附件详情读取详情异常,原因:{ex.Message}"); } } return model; } #endregion public class EmailBCReadMessageInfo { /// /// 表头 /// public TaskMessageHead Head { get; set; } /// /// 表体 /// public EmailBCReadMessageMainInfo Main { get; set; } } public class EmailBCReadMessageMainInfo { /// /// 所属租户ID /// public string TenantId { get; set; } } public class TaskMessageHead : WebAPIHeadBase { } public class WebAPIHeadBase { /// /// 报文惟一主键 /// /// 08dab66c-96a1-4f90-8606-2626e06202ad [Required(ErrorMessage = "必填")] public string GID { get; set; } /// /// 报文类型 BUSI_RULE-业务规则校验 /// /// BUSI_RULE [Required(ErrorMessage = "必填")] public string MessageType { get; set; } /// /// 发送方代码 /// /// CUSTOMER1 [Required(ErrorMessage = "必填")] public string SenderId { get; set; } /// /// 发送方名称 /// /// 企业A [Required(ErrorMessage = "必填")] public string SenderName { get; set; } /// /// 接收方代码 /// /// RulesEngine [Required(ErrorMessage = "必填")] public string ReceiverId { get; set; } /// /// 接收方名称 /// /// 大简云规则引擎 [Required(ErrorMessage = "必填")] public string ReceiverName { get; set; } /// /// 请求方登录TOKEN(可以是真实的登录人TOKEN或者是服务模拟登录人TOKEN) /// /// eyJhbGciOiJSUzI1NiIsImtpZCI6IkQ1RTkxMDI5OUU0RURFNUZEM0EwNTJBMEFDRDUzMUQzIiwidHlwIjoiYXQrand0In0 public string Token { get; set; } /// /// 版本号 默认1.0 /// /// 1.0 [Required(ErrorMessage = "必填")] public string Version { get; set; } = "1.0"; /// /// 请求时间 /// /// 2022-10-10 10:00:00 public string RequestDate { get; set; } /// /// 请求操作类型 /// /// Add [Required(ErrorMessage = "必填")] public string RequestAction { get; set; } = "Add"; } } public class ParserReaderExcuteResultDto { /// /// 是否成功 true=成功 false=失败 /// public bool succ { get; set; } = false; /// /// 状态 0-成功 /// public int status { get; set; } = 0; /// /// 返回消息 /// public string msg { get; set; } /// /// 总记录数 /// public int total { get; set; } /// /// 当前页列表数据 /// public object rows { get; set; } /// /// 合计信息 /// public object summary { get; set; } /// /// 扩展信息 /// public object extra { get; set; } /// /// 扩展信息2 /// public object extra2 { get; set; } /// /// 扩展信息场站统计 /// public object yardStatInfo { get; set; } /// /// 是否异常 /// public bool exceptionflag { get; set; } /// /// 生成HTML /// public string ResultHtml { get; set; } } /// /// /// public class TaskBCInfoReadDto { /// /// 订舱单位 /// public string BookingParty { get; set; } /// /// 发货人 /// public string Shipper { get; set; } /// /// 收货人 /// public string Consignee { get; set; } /// /// 通知人 /// public string NotifyParty { get; set; } /// /// BC更新次数 /// public Nullable BCModifyTimes { get; set; } /// /// BC更新时间 /// public Nullable BCModifyDate { get; set; } /// /// 主单号 /// public string MBLNo { get; set; } /// /// 船名 /// public string Vessel { get; set; } /// /// 航次 /// public string VoyNo { get; set; } /// /// 船公司 /// public string Carrier { get; set; } /// /// 收货地 /// public string PlaceReceipt { get; set; } /// /// 装货港 /// public string Portload { get; set; } /// /// 截关时间 /// public Nullable ClosingDate { get; set; } /// /// 截VGM时间 /// public Nullable VGMCutoffTime { get; set; } /// /// ETA(预计到港时间) /// public Nullable ETA { get; set; } /// /// ETD(预计离港时间) /// public Nullable ETD { get; set; } /// /// 目的港ETA /// public Nullable PODETA { get; set; } /// /// 截单时间 /// public Nullable CutSingleTime { get; set; } /// /// 卸货港 /// public string PortDischarge { get; set; } /// /// 交货地 /// public string PlaceDelivery { get; set; } /// /// 装运方式 /// public string ShippingMethod { get; set; } /// /// 运输条款 /// public string Service { get; set; } /// /// 港前运输形态 /// public string PreTransMode { get; set; } /// /// 品名 /// public string Description { get; set; } /// /// 签单地点 /// public string IssuePlace { get; set; } /// /// 集港码头 /// public string CollectionTerminal { get; set; } /// /// 约号 /// public string ContractNo { get; set; } /// /// 预付地点 /// public string PrepardAT { get; set; } /// /// 船代 /// public string ShipAgent { get; set; } /// /// 场站 /// public string Yard { get; set; } /// /// 场站联系人 /// public string YardContactUserName { get; set; } /// /// 场站联系电话 /// public string YardContactTel { get; set; } /// /// 一代客服姓名 /// public string FstCustomerSerUserName { get; set; } /// /// 一代客服电话 /// public string FstCustomerSerUserTel { get; set; } /// /// 一代客服邮箱 /// public string FstCustomerSerUserEmail { get; set; } /// /// 备注1 /// public string Remark1 { get; set; } /// /// 截港时间 /// public Nullable CYCutoffTime { get; set; } /// /// 状态 TEMP-暂存 SUCC-已对应 ERROR-异常 /// public string Status { get; set; } /// /// 文件MD5 /// public string FileMD5 { get; set; } /// /// 最后对应时间,最后关联到订舱日期 /// public Nullable LastToBookingDate { get; set; } /// /// 来源邮箱 /// public string FromEmail { get; set; } /// /// 接收邮箱 /// public string RecvEmail { get; set; } /// /// 订舱ID,对应成功后,订舱ID写入 /// public Nullable BookingOrderId { get; set; } /// /// 集装箱列表 /// public List CtnList { get; set; } /// /// 顺序号 /// public int Indx { get; set; } /// /// 对应订舱序号 /// public int BKOrderIndx { get; set; } /// /// 舱位主键 /// public Nullable BookingSlotId { get; set; } /// /// 船公司代号 /// public string CarrierId { get; set; } /// /// 航线代码(船公司) /// public string LaneCode { get; set; } /// /// 航线名称(船公司) /// public string LaneName { get; set; } /// /// 承运方式 DIRECT_SHIP-直达;TRANSFER_SHIP-中转 /// public string CarriageType { get; set; } /// /// 承运方式名称 DIRECT_SHIP-直达;TRANSFER_SHIP-中转 /// public string CarriageTypeName { get; set; } /// /// 订舱方式 CONTRACT_ORDER-合约订舱;SPOT_ORDER-SPOT订舱 /// public string BookingSlotType { get; set; } /// /// 订舱方式名称 CONTRACT_ORDER-合约订舱;SPOT_ORDER-SPOT订舱 /// public string BookingSlotTypeName { get; set; } /// /// 箱型箱量 /// public string CtnStat { get; set; } /// /// 所在周数 /// public string WeekAt { get; set; } /// /// 箱使天数 /// public int DetensionFreeDays { get; set; } /// /// 样单截止日期 /// public Nullable SICutDate { get; set; } /// /// 舱单截止时间 /// public Nullable ManifestCutDate { get; set; } /// /// MDGF提交截止时间 /// public Nullable MDGFCutDate { get; set; } /// /// 中转港1 /// public string TransferPort1 { get; set; } /// /// 中转港2 /// public string TransferPort2 { get; set; } /// /// 二程船名 /// public string SecondVessel { get; set; } /// /// 二程航次 /// public string SecondVoyno { get; set; } /// /// 二程ETD /// public Nullable SecondETD { get; set; } /// /// 二程ETA /// public Nullable SecondETA { get; set; } /// /// 订舱确认时间 /// public Nullable BookingConfirmDate { get; set; } /// /// 客户样单截止日期 /// public Nullable CustomSICutDate { get; set; } } /// /// 任务BC集装箱 /// public class TaskBCInfoReadCtnDto { /// /// 箱型代码 /// public string CtnCode { get; set; } /// /// 箱型 /// public string CtnALL { get; set; } /// /// 箱量 /// public Nullable CTNNUM { get; set; } /// /// 件数 /// public Nullable PKGS { get; set; } /// /// 尺码 /// public Nullable CBM { get; set; } /// /// 毛重 /// public Nullable KGS { get; set; } /// /// 皮重 /// public Nullable TareWeight { get; set; } /// /// 危品票标示 /// public string IODGT { get; set; } /// /// 特殊装载需求 /// public string SpecialLoadingRequire { get; set; } /// /// 提箱场站 /// public string TakeCTNYard { get; set; } /// /// 提箱时间 /// public Nullable TakeCTNTime { get; set; } /// /// 还箱场站 /// 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; } }