diff --git a/Myshipping.Application/EDI/EMCSoApiHelper.cs b/Myshipping.Application/EDI/EMCSoApiHelper.cs new file mode 100644 index 00000000..1230eb46 --- /dev/null +++ b/Myshipping.Application/EDI/EMCSoApiHelper.cs @@ -0,0 +1,785 @@ +using Furion; +using Furion.Logging; +using Furion.RemoteRequest.Extensions; +using Myshipping.Application.Entity; +using Myshipping.Core; +using Myshipping.Core.Entity; +using Myshipping.Core.Service; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.Metrics; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Myshipping.Application.EDI +{ + /// + /// EMCAPI订舱 + /// + public static class EMCSoApiHelper + { + public async static Task> DoPost(long custOrderId) + { + var repCustOrder = App.GetService>(); + var repOrder = App.GetService>(); + var repCtn = App.GetService>(); + var repCustomer = App.GetService>(); + var repContact = App.GetService>(); + var repTemplate = App.GetService>(); + var cache = App.GetService(); + + var cacheService = App.GetService(); + + //var order = await repOrder.AsQueryable().Filter(null, true).FirstAsync(o => o.Id == bookingId); + //if (order == null) + //{ + // return new KeyValuePair(false, "订舱信息未找到"); + //} + + var custOrder = await repCustOrder.AsQueryable().Filter(null, true).FirstAsync(x => x.Id == custOrderId); + if (custOrder == null) + { + return new KeyValuePair(false, "客户订舱信息未找到"); + } + + var sysConfigList = await cache.GetAllSysConfig(); + var sCfgSpiderUrl = sysConfigList.FirstOrDefault(x => x.Code == "EMCApiSpiderUrl" && x.GroupCode == "DJY_CONST"); + if (sCfgSpiderUrl == null) + { + return new KeyValuePair(false, "EMC订舱API的爬虫URL地址未配置,请联系管理员"); + } + + var sCfgUserKey = sysConfigList.FirstOrDefault(x => x.Code == "EMCApiSpiderKey" && x.GroupCode == "DJY_CONST"); + var sCfgUserSecret = sysConfigList.FirstOrDefault(x => x.Code == "EMCApiSpiderSecret" && x.GroupCode == "DJY_CONST"); + if (sCfgUserKey == null || sCfgUserSecret == null) + { + return new KeyValuePair(false, "EMC订舱API的KEY和密钥未配置,请联系管理员"); + } + + + BookingSoTemplate template = null; + DjyCustomerContact custContact = null; + var postModel = new EMCSoApiModel(); + + JObject extObj = null; + if (!string.IsNullOrEmpty(custOrder.ExtendData)) + { + extObj = JObject.Parse(custOrder.ExtendData); + postModel.webAccount = extObj.GetStringValue("Account"); + postModel.webPassword = extObj.GetStringValue("Password"); + } + else + { + return new KeyValuePair(false, "未找到订舱账号信息"); + } + + //查找模板: + //1.根据客户订舱信息中的BookingUserId和BookingTenantId,去客户信息中根据CustSysId查找客户(公司)及联系人(员工)信息 + //2.根据找到的客户及联系人信息,查找EMC订舱模板 + if (custOrder.BookingUserId > 0 && custOrder.BookingTenantId > 0) + { + custContact = await repCustomer.AsQueryable().Filter(null, true) + .InnerJoin((cust, contact) => cust.Id == contact.CustomerId) + .Where((cust, contact) => cust.CustSysId == custOrder.BookingTenantId && contact.CustSysId == custOrder.BookingUserId) + .Select((cust, contact) => contact) + .SingleAsync(); + if (custContact == null) + { + return new KeyValuePair(false, "未找到客户及联系人信息"); + } + + //根据:用户+船司+船司账号+约号,找到启用的模板 + template = await repTemplate.AsQueryable().Filter(null, true).FirstAsync(x => x.CarrierId == custOrder.CARRIERID && x.UserId == custContact.Id && x.ContractNO == custOrder.CONTRACTNO && x.BookingAccount == postModel.webAccount && x.IsEnable); + if (template == null) + { + return new KeyValuePair(false, "未找到订舱模板"); + } + } + else + { + return new KeyValuePair(false, "未找到客户端公司和用户ID"); + } + + var mappingCtn = await cache.GetAllMappingCtn(); + var mappingFrt = await cache.GetAllMappingFrt(); + var mappingPortLoad = await cache.GetAllMappingPortLoad(); + var mappingPort = await cache.GetAllMappingPort(); + + postModel.userKey = sCfgUserKey.Value; + postModel.userSecret = sCfgUserSecret.Value; + + + postModel.uploadType = template.Category; //DRAFT, TEMPLATE, BOOKING分别对应:草稿, 模板, 订舱 + + //起运港 + var mapPortLoad = mappingPortLoad.FirstOrDefault(x => x.Module == "DjyCustBooking" && x.CarrierCode == "EMC" && x.Code == custOrder.PORTLOADCODE); + if (mapPortLoad == null) + { + return new KeyValuePair(false, $"未找到起运港映射信息:{custOrder.PORTLOADCODE}"); + } + + //目的地 + var mapPort = mappingPort.FirstOrDefault(x => x.Module == "DjyCustBooking" && x.CarrierCode == "EMC" && x.Code == custOrder.DESTINATIONCODE); + if (mapPort == null) + { + return new KeyValuePair(false, $"未找到目的地映射信息:{custOrder.DESTINATIONCODE}"); + } + + ////运输条款 + //var mappingService = await cacheService.GetAllMappingService(); + //var mappService = mappingService.FirstOrDefault(x => x.Module == "DjyCustBooking" && x.CarrierCode == "EMC" && x.Code == custOrder.SERVICE); + //if (mappService == null) + //{ + // return new KeyValuePair(false, $"未找到运输条款映射信息:{custOrder.SERVICE}"); + //} + + //if (!Regex.IsMatch(mappService.MapCode, "^[A-Za-z]+-[A-Za-z]+$")) + //{ + // return new KeyValuePair(false, $"映射配置不正确:{mappService.MapCode}"); + //} + + //var mapServArr = mappService.MapCode.Split('-'); + + var startDay = template.StartDay.HasValue ? template.StartDay.Value : 1; + var endWeek = template.EndWeek.HasValue ? template.EndWeek.Value : 2; + + postModel.routes = new EMCSoApiRoute() + { + searchConditionDate = custOrder.ETD.Value.AddDays(startDay).ToString("yyyy-MM-dd"), + originName = mapPortLoad.MapCode, + destinationName = mapPort.MapCode, + polPortName = extObj?.GetStringValue("PolPortName"), + podPortName = extObj?.GetStringValue("PodPortName"), + serviceType = extObj?.GetStringValue("YSFS"), + serviceMode = extObj?.GetStringValue("YSXT"), + }; + + //货物信息(箱信息) + var ctns = await repCtn.AsQueryable().Filter(null, true).Where(x => x.BILLID == custOrder.Id).ToListAsync(); + postModel.cargoInfos = new List(); + foreach (var ctn in ctns) + { + if (!ctn.CTNNUM.HasValue || !ctn.KGS.HasValue) + { + return new KeyValuePair(false, $"所有箱的箱量和毛重都不能为空"); + } + + var mapCtn = mappingCtn.FirstOrDefault(x => x.Module == "DjyCustBooking" && x.CarrierCode == "EMC" && x.Code == ctn.CTNCODE); + if (mapCtn == null) + { + return new KeyValuePair(false, $"未找箱型映射信息:{ctn.CTNCODE}"); + } + + var apiBox = new EMCSoApiCargoInfo() + { + containerType = mapCtn.MapCode, + containerQuantity = ctn.CTNNUM.Value, + containerWeight = ctn.KGS.Value, + containerWeightUnit = "KGS", + number = ctn.PKGS, + packageUnit = ctn.KINDPKGS, + measurements = ctn.CBM, + measurementUnit = "CBM" + }; + + postModel.cargoInfos.Add(apiBox); + } + + #region 收发通及货代 + var telCountryCode = ""; + var telAreaCode = ""; + var telNumber = ""; + + //发货人 + if (string.IsNullOrEmpty(template.ShipperName)) + { + telCountryCode = custOrder.ShipperPhoneCountryCode; + telAreaCode = custOrder.ShipperPhoneCode; + telNumber = custOrder.ShipperPhone; + } + else + { + telCountryCode = template.ShipperPhoneCountryCode; + telAreaCode = template.ShipperPhoneCode; + telNumber = template.ShipperPhone; + } + + postModel.shipperInfo = new EMCSoApiSFTInfo() + { + companyName = "", + contactTitle = "", + contactLName = string.IsNullOrEmpty(template.ShipperName) ? custOrder.ShipperFirstName : template.ShipperFirstName, + contactFName = string.IsNullOrEmpty(template.ShipperLastName) ? custOrder.ShipperLastName : template.ShipperLastName, + tel1 = telCountryCode, + tel2 = telAreaCode, + tel3 = telNumber, + }; + + //收货人 + if (string.IsNullOrEmpty(template.ConsigneeName)) + { + telCountryCode = custOrder.ConsigneePhoneCountryCode; + telAreaCode = custOrder.ConsigneePhoneCode; + telNumber = custOrder.ConsigneePhone; + } + else + { + telCountryCode = template.ConsigneePhoneCountryCode; + telAreaCode = template.ConsigneePhoneCode; + telNumber = template.ConsigneePhone; + } + + postModel.consigneeInfo = new EMCSoApiSFTInfo() + { + companyName = "", + contactTitle = "", + contactLName = string.IsNullOrEmpty(template.ConsigneeFirstName) ? custOrder.ConsigneeFirstName : template.ConsigneeFirstName, + contactFName = string.IsNullOrEmpty(template.ConsigneeLastName) ? custOrder.ConsigneeLastName : template.ConsigneeLastName, + tel1 = telCountryCode, + tel2 = telAreaCode, + tel3 = telNumber, + }; + + //通知人 + if (string.IsNullOrEmpty(template.NotifypartName)) + { + telCountryCode = custOrder.NotifypartPhoneCountryCode; + telAreaCode = custOrder.NotifypartPhoneCode; + telNumber = custOrder.NotifypartPhone; + } + else + { + telCountryCode = template.NotifypartPhoneCountryCode; + telAreaCode = template.NotifypartPhoneCode; + telNumber = template.NotifypartPhone; + } + + postModel.notifyInfo = new EMCSoApiSFTInfo() + { + companyName = "", + contactTitle = "", + contactLName = string.IsNullOrEmpty(template.NotifypartFirstName) ? custOrder.NotifypartFirstName : template.NotifypartFirstName, + contactFName = string.IsNullOrEmpty(template.NotifypartLastName) ? custOrder.NotifypartLastName : template.NotifypartLastName, + tel1 = telCountryCode, + tel2 = telAreaCode, + tel3 = telNumber, + }; + + //货代 + if (string.IsNullOrEmpty(template.BookingName)) + { + telCountryCode = custOrder.BookingPhoneCountryCode; + telAreaCode = custOrder.BookingPhoneCode; + telNumber = custOrder.BookingPhone; + } + else + { + telCountryCode = template.BookingPhoneCountryCode; + telAreaCode = template.BookingPhoneCode; + telNumber = template.BookingPhone; + } + + postModel.forwarderInfo = new EMCSoApiSFTInfo() + { + companyName = "", + contactTitle = "", + contactLName = string.IsNullOrEmpty(template.BookingFirstName) ? custOrder.BookingFirstName : template.BookingFirstName, + contactFName = string.IsNullOrEmpty(template.BookingLastName) ? custOrder.BookingLastName : template.BookingLastName, + tel1 = telCountryCode, + tel2 = telAreaCode, + tel3 = telNumber, + }; + #endregion + + + + ////大简云客户订舱接收BC邮箱 + //var djyBookMail = sysConfigList.FirstOrDefault(x => x.Code == "DjyCustomerBookReceiveBcMail"); + //var bcMail = custContact.Email; + //if (extObj != null) //优先使用东胜上传的邮箱 + //{ + // var opMail = extObj.GetStringValue("OpMail"); + // if (!string.IsNullOrEmpty(opMail)) + // { + // bcMail = opMail; + // } + //} + + //if (djyBookMail != null) + //{ + // bcMail += ";" + djyBookMail.Value; + //} + + //postModel.special = new EMCSoApiSpecial() + //{ + // emailAddresses = bcMail, + // remarksForEntireBooking = custOrder.SOREMARK + //}; + + Log.Information($"发送API数据给爬虫({sCfgSpiderUrl.Value}):{postModel.ToJsonString()}"); + var rtn = await sCfgSpiderUrl.Value.SetBody(postModel) + .PostAsStringAsync(); + + Log.Information($"爬虫返回:{rtn}"); + var jobjRtn = JObject.Parse(rtn); + if (jobjRtn.GetIntValue("code") == 200) + { + return new KeyValuePair(true, "发送成功"); + } + else + { + return new KeyValuePair(false, jobjRtn.GetStringValue("msg")); + } + + + + } + + } + + + /// + /// EMCAPI订舱传输对象 + /// + public class EMCSoApiModel + { + /// + /// 用户key + /// + public string userKey { get; set; } + + /// + /// 用户secret + /// + public string userSecret { get; set; } + + /// + /// 网站账户 + /// + public string webAccount { get; set; } + + /// + /// 网站密码 + /// + public string webPassword { get; set; } + + /// + /// 上传类型 + /// + public string uploadType { get; set; } + + /// + /// 路线信息 + /// + public EMCSoApiRoute routes { get; set; } + + /// + /// 货物信息 + /// + public List cargoInfos { get; set; } + + /// + /// 合约信息 + /// + public EMCSoApiContractInfo contractInfo { get; set; } + + /// + /// 船期数据 + /// + public EMCSoApiShipInfo shipInfo { get; set; } + + /// + /// 订舱公司详情 + /// + public EMCSoApiCompanyInfo companyInfo { get; set; } + + /// + /// 发货人 + /// + public EMCSoApiSFTInfo shipperInfo { get; set; } + + /// + /// 货代信息 + /// + public EMCSoApiSFTInfo forwarderInfo { get; set; } + + /// + /// 收货人信息 + /// + public EMCSoApiSFTInfo consigneeInfo { get; set; } + + /// + /// 通知人信息 + /// + public EMCSoApiSFTInfo notifyInfo { get; set; } + + /// + /// 已进场货柜均可装船 + /// Y, N + /// + public string partialLoad { get; set; } = "Y"; + + /// + /// 复制的单数 + /// + public int copyNum { get; set; } + + /// + /// 是否同意: 在此提醒您,网络订舱需求仅为要约,本公司保留更新及修改权利,并以正式回复之"订舱确认通知"为准 + /// + public bool isAgree { get; set; } + + /// + /// 备注信息 + /// + public string remark { get; set; } + } + + /// + /// 路线信息 + /// + public class EMCSoApiRoute + { + /// + /// 查询日期 + /// + public string searchConditionDate { get; set; } + + /// + /// 收货地, 去网站上应一致 + /// + public string originName { get; set; } + + /// + /// 交货地 + /// + public string destinationName { get; set; } + + /// + /// 装货港 + /// + public string polPortName { get; set; } + + /// + /// 卸货港 + /// + public string podPortName { get; set; } + + /// + /// 运送方式 + /// + public string serviceType { get; set; } + + /// + /// 运送形态 + /// + public string serviceMode { get; set; } + + } + + /// + /// 货物信息 + /// + public class EMCSoApiCargoInfo + { + /// + /// 箱量 + /// + public int? containerQuantity { get; set; } = 1; + + /// + /// 箱型, 注意当isReefer为Y时, 应选择冻柜的数据 + /// + public string containerType { get; set; } + + /// + /// 称重方式 E: 每个, T: 总和 + /// + public string containerWeightMode { get; set; } + + /// + /// 重量 最多两位小数 + /// + public decimal? containerWeight { get; set; } + + /// + /// 重量单位 KGS, LBS + /// + public string containerWeightUnit { get; set; } = "KGS"; + + /// + /// 品名 + /// + public string commodityName { get; set; } + + /// + /// Harmonized Tariff Schedule Code + /// + public string htCode { get; set; } + + /// + /// 件数 + /// + public int? number { get; set; } + + /// + /// 包装单位 + /// + public string packageUnit { get; set; } + + /// + /// 体积 + /// + public decimal? measurements { get; set; } + + /// + /// 体积单位 + /// + public string measurementUnit { get; set; } = "CBM"; + + /// + /// 特殊要求 + /// + public string handling { get; set; } + + } + + /// + /// 合约信息 + /// + public class EMCSoApiContractInfo + { + /// + /// 合约方案选择 + /// 当合约类型为SC时, 这个参数生效,且合约号生效, 默认SC + /// SC, HT, LT, SQ, RS, HV, LM, HM, TV, LV, EM + /// + public string contractSelect { get; set; } = "SC"; + /// + /// 合约类型 + /// 当类型为TR时,合约号不生效 + /// SC, TR + /// + public string contractType { get; set; } + /// + /// 合约号 + /// + public string contractNo { get; set; } + /// + /// 签约方 + /// Shipper, Forwarder, Consignee, Notify Party + /// + public string contractParty { get; set; } + /// + /// 订舱网点 + /// + public string bookingOffice { get; set; } + /// + /// 提单签发地 + /// + public string issuePlace { get; set; } + /// + /// 付款方式 + /// P, C + /// + public string paymentMethod { get; set; } + /// + /// 付款地点 + /// + public string paymentPlace { get; set; } + /// + /// PO号码 + /// + public string purchase { get; set; } + /// + /// 提单数量 + /// + public int? billNum { get; set; } = 1; + } + + /// + /// 船期数据 + /// 船期查询的结果总选择的数据集, 原样上传 + /// + public class EMCSoApiShipInfo + { + /// + /// 收货地 + /// + public string originName { get; set; } + + /// + /// 交货地 + /// + public string destinationName { get; set; } + + /// + /// 本地截止时间 + /// + public string cutOffLocalDate { get; set; } + + /// + /// 开航时间 + /// + public string departureDate { get; set; } + + /// + /// 航线代码 + /// + public string serviceCode { get; set; } + + /// + /// 船名 + /// + public string vesselName { get; set; } + + /// + /// 航次 + /// + public string voyageNumber { get; set; } + + /// + /// 下一段航线代码 + /// + public string nextServiceCode { get; set; } + + /// + /// 总航程 + /// + public string estimatedTransitTimeInDays { get; set; } + + /// + /// 详情参数, 订舱时需要原样上传 + /// + public string paramsText { get; set; } + + /// + /// 后续请求需要使用, 原样上传即可 + /// + public string buttonText { get; set; } + + /// + /// 航线详情 + /// + public EMCSoApiShipInfoOceanLegs oceanLegs { get; set; } + } + + /// + /// 航线详情 + /// + public class EMCSoApiShipInfoOceanLegs + { + /// + /// 起运港 + /// + public string polPortName { get; set; } + + /// + /// 目的港 + /// + public string podPortName { get; set; } + + /// + /// ETD + /// + public string polETD { get; set; } + + /// + /// ETA + /// + public string podETA { get; set; } + + /// + /// 航线代码 + /// + public string serviceCode { get; set; } + + /// + /// 船名航线字符 + /// + public string vesselAndVoyage { get; set; } + + /// + /// 航程 + /// + public string days { get; set; } + } + + /// + /// 订舱公司详情 + /// + public class EMCSoApiCompanyInfo + { + /// + /// 是否收取订舱通知 + /// Yes, No + /// + public string receiveBookingNotice { get; set; } + + /// + /// 内参号码 + /// + public string referenceNO { get; set; } + } + + /// + /// 收发通信息 + /// + public class EMCSoApiSFTInfo + { + /// + /// 公司名称 + /// + public string companyName { get; set; } + + /// + /// 联系人性别 + /// MS, MR + /// + public string contactTitle { get; set; } + + /// + /// 姓 + /// + public string contactFName { get; set; } + + /// + /// 名 + /// + public string contactLName { get; set; } + + /// + /// 国家代码 + /// + public string tel1 { get; set; } + + /// + /// 地区代码 + /// + public string tel2 { get; set; } + + /// + /// 电话号码 + /// + public string tel3 { get; set; } + + /// + /// 分机号码 + /// + public string tel4 { get; set; } + + /// + /// 内参号码 + /// + public string referenceNO { get; set; } + + /// + /// 邮箱 + /// + public string email { get; set; } + } +} diff --git a/Myshipping.Application/Event/BookingAutoSubscriber.cs b/Myshipping.Application/Event/BookingAutoSubscriber.cs new file mode 100644 index 00000000..6123b775 --- /dev/null +++ b/Myshipping.Application/Event/BookingAutoSubscriber.cs @@ -0,0 +1,75 @@ +using Furion.EventBus; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Myshipping.Application.Entity; +using Myshipping.Application.MQ; +using Myshipping.Core; +using Myshipping.Core.Entity; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Myshipping.Application.Event +{ + /// + /// 订舱自动化事件处理 + /// + public class BookingAutoSubscriber : IEventSubscriber + { + private IServiceProvider _services { get; } + private readonly ILogger _logger; + + public BookingAutoSubscriber(IServiceProvider services, ILogger logger) + { + _services = services; + _logger = logger; + } + + + //收到BC + [EventSubscribe("BookingAuto:BC")] + public async Task BookingAutoBC(EventHandlerExecutingContext context) + { + _logger.LogInformation($"收到订舱自动化BC消息:{context.Source.Payload}"); + + var msgBC = context.Source.Payload as BookingAutoMessageBC; + + var scope = _services.CreateScope(); + + var repoBooking = scope.ServiceProvider.GetRequiredService>(); + var repoStatuslog = scope.ServiceProvider.GetRequiredService>(); + var bookingfile = scope.ServiceProvider.GetRequiredService>(); + var booking = await repoBooking.AsQueryable().Filter(null, true).FirstAsync(x => x.CUSTNO == msgBC.BookingNO && x.TenantId == msgBC.TenantId); + if (booking != null) + { + booking.MBLNO = msgBC.MBLNO; //回写提单号 + await repoBooking.AsUpdateable(booking).UpdateColumns(x => new { x.MBLNO }).ExecuteCommandAsync(); + + //货运动态 + var bsl = new BookingStatusLog(); + bsl.BookingId = booking.Id; + bsl.Status = $"收到BC"; + bsl.OpTime = DateTime.Now; + bsl.Category = "ship"; + bsl.MBLNO = booking.MBLNO; + await repoStatuslog.InsertAsync(bsl); + + //挂载附件 + var newFile = new BookingFile + { + FileName = Path.GetFileName(msgBC.AttachUrl), + FilePath = msgBC.AttachUrl, + TypeCode = "BC", + TypeName = ".pdf", + BookingId = booking.Id, + + }; + await bookingfile.InsertAsync(newFile); + } + + } + } +} diff --git a/Myshipping.Application/Helper/PlaceToPortHelper.cs b/Myshipping.Application/Helper/PlaceToPortHelper.cs new file mode 100644 index 00000000..0a291702 --- /dev/null +++ b/Myshipping.Application/Helper/PlaceToPortHelper.cs @@ -0,0 +1,78 @@ +using Myshipping.Core; +using Myshipping.Core.Entity; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Myshipping.Application.Helper +{ + public static class PlaceToPortHelper + { + public static async Task PlaceReceiptToPortload(string portEnName, List cachePortLoad, Func>> cacheMapPortLoadFunc) + { + if (string.IsNullOrEmpty(portEnName)) + { + return null; + } + + // 匹配方式1:精准匹配 + var portInfo = cachePortLoad.FirstOrDefault(x => x.EnName.Equals(portEnName, StringComparison.OrdinalIgnoreCase)); + if (portInfo != null) return portInfo; + + // 匹配方式2:起始模糊匹配 + portInfo = cachePortLoad.FirstOrDefault(x => x.EnName.StartsWith(portEnName, StringComparison.OrdinalIgnoreCase)); + if (portInfo != null) return portInfo; + + // 匹配方式3:完整模糊匹配 + portInfo = cachePortLoad.FirstOrDefault(x => x.EnName.Contains(portEnName, StringComparison.OrdinalIgnoreCase)); + if (portInfo != null) return portInfo; + + // 匹配方式4:精准映射匹配 + var mapCachePortLoad = await cacheMapPortLoadFunc(); + var map = mapCachePortLoad.FirstOrDefault(x => x.Module == CommonConst.RECEIPT_TO_PORTLOAD + && x.MapName.Equals(portEnName, StringComparison.OrdinalIgnoreCase)); + + if (map != null) + { + portInfo = cachePortLoad.FirstOrDefault(x => x.Code == map.Code); + + if (portInfo != null) return portInfo; + } + return null; + } + public static async Task PlaceDeliveryToPort(string portEnName, List cachePort, Func>> cacheMapPortFunc) + { + if (string.IsNullOrEmpty(portEnName)) + { + return null; + } + + // 匹配方式1:精准匹配 + var portInfo = cachePort.FirstOrDefault(x => x.EnName.Equals(portEnName, StringComparison.OrdinalIgnoreCase)); + if (portInfo != null) return portInfo; + + // 匹配方式2:起始模糊匹配 + portInfo = cachePort.FirstOrDefault(x => x.EnName.StartsWith(portEnName, StringComparison.OrdinalIgnoreCase)); + if (portInfo != null) return portInfo; + + // 匹配方式3:完整模糊匹配 + portInfo = cachePort.FirstOrDefault(x => x.EnName.Contains(portEnName, StringComparison.OrdinalIgnoreCase)); + if (portInfo != null) return portInfo; + + // 匹配方式4:精准映射匹配 + var mapCachePort = await cacheMapPortFunc(); + var map = mapCachePort.FirstOrDefault(x => x.Module == CommonConst.DELIVERY_TO_PORT + && x.MapName.Equals(portEnName, StringComparison.OrdinalIgnoreCase)); + + if (map != null) + { + portInfo = cachePort.FirstOrDefault(x => x.Code == map.Code); + + if (portInfo != null) return portInfo; + } + return null; + } + } +} diff --git a/Myshipping.Application/MQ/BookingAutoService.cs b/Myshipping.Application/MQ/BookingAutoService.cs new file mode 100644 index 00000000..fb72422e --- /dev/null +++ b/Myshipping.Application/MQ/BookingAutoService.cs @@ -0,0 +1,148 @@ +using Furion.EventBus; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Myshipping.Core.Entity; +using Myshipping.Core; +using RabbitMQ.Client; +using RabbitMQ.Client.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; + +namespace Myshipping.Application.MQ +{ + public class BookingAutoService : BackgroundService + { + private readonly IServiceScopeFactory _scopeFactory; + private readonly IServiceScope _serviceScope; + private readonly ILogger _logger; + private readonly IEventPublisher _publisher; + private IConnection mqConn; + private IModel model; + + public BookingAutoService(IServiceScopeFactory scopeFactory) + { + _scopeFactory = scopeFactory; + //通过这个注入DBContext + _serviceScope = _scopeFactory.CreateScope(); + _logger = _serviceScope.ServiceProvider.GetService>(); + _publisher = _serviceScope.ServiceProvider.GetService(); + } + + protected override Task ExecuteAsync(CancellationToken stoppingToken) + { + _logger.LogInformation("BookingAutoService ExecuteAsync"); + + return Task.Run(() => + { + _logger.LogInformation("BookingAutoService ExecuteAsync RunTask"); + + //绑定队列 + BindMQ(); + }); + } + + private void BindMQ() + { + string ExchangeName = "djy.booking.auto"; + string QueueName = $"djy.booking.auto.{DateTime.Now.Ticks}"; + + + ConnectionFactory factory = new ConnectionFactory(); + var repoSysCfg = _serviceScope.ServiceProvider.GetService>(); + var mqUrl = repoSysCfg.FirstOrDefault(x => x.Code == "DjyBookingAutoMQUrl")?.Value; + if (string.IsNullOrEmpty(mqUrl)) + { + _logger.LogError($"接收订舱自动化消息推送所需MQUrl未配置"); + } + else + { + _logger.LogInformation($"准备连接订舱自动化消息队列:{mqUrl}"); + factory.Uri = new Uri(mqUrl); + mqConn = factory.CreateConnection("大简云订舱平台"); + + model = mqConn.CreateModel(); + model.ExchangeDeclare(ExchangeName, ExchangeType.Topic); + model.QueueDeclare(QueueName, false, false, true, null); + model.QueueBind(QueueName, ExchangeName, "*", null); + + var consumer = new EventingBasicConsumer(model); + consumer.Received += (obj, arg) => + { + var body = arg.Body; + var strBody = Encoding.UTF8.GetString(body.ToArray()); + _logger.LogInformation($"收到订舱自动化消息队列:{strBody}"); + + if (arg.RoutingKey == "BC") //收到BC + { + var dto = JsonConvert.DeserializeObject(strBody); + _publisher.PublishAsync(new ChannelEventSource("BookingAuto:BC", dto)); + } + else if (arg.RoutingKey == "DraftDownloadOk") //DRAFT下载完成 + { + + } + }; + model.BasicConsume(QueueName, true, consumer); + } + } + + public override void Dispose() + { + base.Dispose(); + _serviceScope.Dispose(); + if (mqConn != null && mqConn.IsOpen) + mqConn.Close(); + + _logger.LogInformation("BookingAutoService Dispose"); + } + } + + /// + /// BC消息对象 + /// + public class BookingAutoMessageBC + { + /// + /// 租户ID + /// + public long TenantId { get; set; } + + /// + /// 大简云公司ID + /// + public string CompanyId { get; set; } + + /// + /// 船司 + /// + public string Carrier { get; set; } + + /// + /// 提单号 + /// + public string MBLNO { get; set; } + + /// + /// 订舱号 + /// + public string BookingNO { get; set; } + + /// + /// 链接地址 + /// + public string Url { get; set; } + + /// + /// 附件地址 + /// + public string AttachUrl { get; set; } + + } +} diff --git a/Myshipping.Application/Service/BookingCustomerOrder/BookingCustomerOrderService.cs b/Myshipping.Application/Service/BookingCustomerOrder/BookingCustomerOrderService.cs index 957ddc90..f9fb741f 100644 --- a/Myshipping.Application/Service/BookingCustomerOrder/BookingCustomerOrderService.cs +++ b/Myshipping.Application/Service/BookingCustomerOrder/BookingCustomerOrderService.cs @@ -1650,7 +1650,7 @@ namespace Myshipping.Application custOrder.DESCRIPTION = data.Rows[idx]["description of goods"].ToString(); var portLoadCode = data.Rows[idx]["POL"].ToString(); - var portLoad = portLoadList.FirstOrDefault(x => x.Code == portLoadCode); + var portLoad = portLoadList.FirstOrDefault(x => x.Code == portLoadCode || x.EnName == portLoadCode); if (portLoad == null) { errList.Add($"第{idx + 1}行,未找到起运港:{portLoadCode}"); @@ -1663,7 +1663,7 @@ namespace Myshipping.Application } var portCode = data.Rows[idx]["卸货港"].ToString(); - var portDest = portDestList.FirstOrDefault(x => x.Code == portCode); + var portDest = portDestList.FirstOrDefault(x => x.Code == portCode || x.EnName == portCode); if (portLoad == null) { errList.Add($"第{idx + 1}行,未找到卸货港:{portCode}"); @@ -1676,7 +1676,7 @@ namespace Myshipping.Application } var destina = data.Rows[idx]["交货地"].ToString(); - var destinaFind = portDestList.FirstOrDefault(x => x.Code == destina); + var destinaFind = portDestList.FirstOrDefault(x => x.Code == destina || x.EnName == destina); if (portLoad == null) { errList.Add($"第{idx + 1}行,未找到交货地:{destina}"); @@ -1712,7 +1712,7 @@ namespace Myshipping.Application var ctns = new List(); if (arrCtn.Length == 2) { - var ctn = ctnList.FirstOrDefault(x => x.Code == arrCtn[0]); + var ctn = ctnList.FirstOrDefault(x => x.Code == arrCtn[0] || x.Name == arrCtn[0]); if (ctn == null) { errList.Add($"第{idx + 1}行,未找到箱型:{arrCtn[0]}"); diff --git a/Myshipping.Application/Service/BookingSlot/BookingSlotService.cs b/Myshipping.Application/Service/BookingSlot/BookingSlotService.cs index b5c10776..12b72b40 100644 --- a/Myshipping.Application/Service/BookingSlot/BookingSlotService.cs +++ b/Myshipping.Application/Service/BookingSlot/BookingSlotService.cs @@ -629,13 +629,9 @@ namespace Myshipping.Application var portEnName = dto.DataObj.PLACERECEIPT.Split(',')[0]?.Trim(); if (!string.IsNullOrWhiteSpace(portEnName)) { - var cachePort = await _cache.GetAllCodePort(); + var cachePortLoad = await _cache.GetAllCodePortLoad(); + var portInfo = await PlaceToPortHelper.PlaceReceiptToPortload(portEnName, cachePortLoad, () => _cache.GetAllMappingPortLoad()); - var portInfo = cachePort.FirstOrDefault(x => x.EnName.StartsWith(portEnName, StringComparison.OrdinalIgnoreCase)); - if (portInfo == null) - { - portInfo = cachePort.FirstOrDefault(x => x.EnName.Contains(portEnName, StringComparison.OrdinalIgnoreCase)); - } if (portInfo == null) { _logger.LogInformation("通过收货地城市名称未匹配到港口信息,订舱编号:{SLOT_BOOKING_NO}", dto.DataObj.SLOT_BOOKING_NO); @@ -662,11 +658,8 @@ namespace Myshipping.Application if (!string.IsNullOrWhiteSpace(portEnName)) { var cachePort = await _cache.GetAllCodePort(); - var portInfo = cachePort.FirstOrDefault(x => x.EnName.StartsWith(portEnName + ',', StringComparison.OrdinalIgnoreCase)); - if (portInfo == null) - { - portInfo = cachePort.FirstOrDefault(x => x.EnName.Contains(portEnName, StringComparison.OrdinalIgnoreCase)); - } + + var portInfo = await PlaceToPortHelper.PlaceDeliveryToPort(portEnName, cachePort, () => _cache.GetAllMappingPort()); if (portInfo == null) { @@ -1146,6 +1139,7 @@ namespace Myshipping.Application var list = await _repBase.AsQueryable().Filter(null, true) .Where(x => x.TenantId == telentId && x.IsDeleted == false + && x.IS_CANCELLATION == false && !string.IsNullOrEmpty(x.PLACERECEIPT) && !string.IsNullOrEmpty(x.PLACEDELIVERY) && (string.IsNullOrEmpty(x.PORTLOADID) || string.IsNullOrEmpty(x.PORTDISCHARGEID))) @@ -1169,9 +1163,10 @@ namespace Myshipping.Application { // 解析收货地,得到装货港名称及五字码 var portEnName = item.PLACERECEIPT.Split(',')[0]?.Trim(); - if (!string.IsNullOrWhiteSpace(portEnName)) + if (!string.IsNullOrWhiteSpace(portEnName) + && (string.IsNullOrEmpty(item.PORTLOADID) || string.IsNullOrEmpty(item.PORTLOAD))) { - var portInfo = cachePortLoad.FirstOrDefault(x => x.EnName.Equals(portEnName, StringComparison.OrdinalIgnoreCase)); + var portInfo = await PlaceToPortHelper.PlaceReceiptToPortload(portEnName, cachePortLoad, () => _cache.GetAllMappingPortLoad()); if (portInfo != null) { if (string.IsNullOrEmpty(item.PORTLOADID)) @@ -1195,12 +1190,13 @@ namespace Myshipping.Application var portEnName2 = item.PLACEDELIVERY.Split(',')[0]?.Trim(); if (!string.IsNullOrWhiteSpace(portEnName2)) { - var portInfo = cachePort.FirstOrDefault(x => x.EnName.StartsWith(portEnName2, StringComparison.OrdinalIgnoreCase)); - if (portInfo == null) - { - portInfo = cachePort.FirstOrDefault(x => x.EnName.Contains(portEnName2, StringComparison.OrdinalIgnoreCase)); - } - if (portInfo != null) + var portInfo = await PlaceToPortHelper.PlaceDeliveryToPort(portEnName2, cachePort, () => _cache.GetAllMappingPort()); + + if (portInfo != null + && (string.IsNullOrEmpty(item.PORTDISCHARGEID) + || string.IsNullOrEmpty(item.PORTDISCHARGE) + || string.IsNullOrEmpty(item.PORTDISCHARGE_COUNTRY_CODE) + || string.IsNullOrEmpty(item.PORTDISCHARGE_COUNTRY))) { if (string.IsNullOrEmpty(item.PORTDISCHARGEID)) { @@ -1243,38 +1239,38 @@ namespace Myshipping.Application x.PORTDISCHARGE_COUNTRY, }).ExecuteCommandAsync(); - var group = updateList.Where(x => !string.IsNullOrEmpty(x.VESSEL) - && !string.IsNullOrEmpty(x.VOYNO) - && !string.IsNullOrEmpty(x.CONTRACT_NO) - && !string.IsNullOrEmpty(x.BOOKING_SLOT_TYPE) - && !string.IsNullOrEmpty(x.CARRIERID) - && !string.IsNullOrEmpty(x.PORTLOADID) - && !string.IsNullOrEmpty(x.PORTDISCHARGEID)) - .GroupBy(x => new - { - x.VESSEL, - x.VOYNO, - x.CARRIERID, - x.BOOKING_SLOT_TYPE, - x.PORTDISCHARGEID, - x.PORTLOADID, - x.CONTRACT_NO - }).ToList(); - - foreach (var item in group) - { - await _publisher.PublishAsync(new ChannelEventSource("BookingSlotStock:Update", new BookingSlotStockUpdateModel - { - BOOKING_SLOT_TYPE = item.Key.BOOKING_SLOT_TYPE, - CARRIERID = item.Key.CARRIERID, - CONTRACT_NO = item.Key.CONTRACT_NO, - VESSEL = item.Key.VESSEL, - VOYNO = item.Key.VOYNO, - PORTLOADID = item.Key.PORTLOADID, - PORTDISCHARGEID = item.Key.PORTDISCHARGEID, - TenantId = UserManager.TENANT_ID - })); - } + //var group = updateList.Where(x => !string.IsNullOrEmpty(x.VESSEL) + // && !string.IsNullOrEmpty(x.VOYNO) + // && !string.IsNullOrEmpty(x.CONTRACT_NO) + // && !string.IsNullOrEmpty(x.BOOKING_SLOT_TYPE) + // && !string.IsNullOrEmpty(x.CARRIERID) + // && !string.IsNullOrEmpty(x.PORTLOADID) + // && !string.IsNullOrEmpty(x.PORTDISCHARGEID)) + // .GroupBy(x => new + // { + // x.VESSEL, + // x.VOYNO, + // x.CARRIERID, + // x.BOOKING_SLOT_TYPE, + // x.PORTDISCHARGEID, + // x.PORTLOADID, + // x.CONTRACT_NO + // }).ToList(); + + //foreach (var item in group) + //{ + // await _publisher.PublishAsync(new ChannelEventSource("BookingSlotStock:Update", new BookingSlotStockUpdateModel + // { + // BOOKING_SLOT_TYPE = item.Key.BOOKING_SLOT_TYPE, + // CARRIERID = item.Key.CARRIERID, + // CONTRACT_NO = item.Key.CONTRACT_NO, + // VESSEL = item.Key.VESSEL, + // VOYNO = item.Key.VOYNO, + // PORTLOADID = item.Key.PORTLOADID, + // PORTDISCHARGEID = item.Key.PORTDISCHARGEID, + // TenantId = UserManager.TENANT_ID + // })); + //} } } #endregion @@ -3162,7 +3158,7 @@ namespace Myshipping.Application BookingSlotBase slotInfo = null; - if(bookingSlotAllocList.Count > 0) + if (bookingSlotAllocList.Count > 0) { slotInfo = bookingSlotAllocList.FirstOrDefault().Slot; } @@ -3170,7 +3166,7 @@ namespace Myshipping.Application { slotInfo = _repBase.AsQueryable().Filter(null, true).First(t => t.Id == slotId); } - + if (slotInfo != null) { @@ -3203,7 +3199,7 @@ namespace Myshipping.Application } } - string srcPriceDate = bcSrcDto.PriceCalculationDate.HasValue? bcSrcDto.PriceCalculationDate.Value.ToString("yyyy-MM-dd"): ""; + string srcPriceDate = bcSrcDto.PriceCalculationDate.HasValue ? bcSrcDto.PriceCalculationDate.Value.ToString("yyyy-MM-dd") : ""; string targetPriceDate = bcTargetDto.PriceCalculationDate.HasValue ? bcTargetDto.PriceCalculationDate.Value.ToString("yyyy-MM-dd") : ""; //如果原始的没有解析计费日期,就不做提醒了 @@ -3415,7 +3411,7 @@ namespace Myshipping.Application messageInfo.Main.MBlNo = dto.mblNo; messageInfo.Main.ETD = dto.etd; - messageInfo.Main.TaskTitle = $"重要提醒-{dto.cautionNoticeType.GetDescription()} BLNo:{dto.mblNo} {dto.vessel}/{dto.voyno} {(dto.etd.HasValue? dto.etd.Value.ToString("yyyy-MM-dd"):"")} "; + messageInfo.Main.TaskTitle = $"重要提醒-{dto.cautionNoticeType.GetDescription()} BLNo:{dto.mblNo} {dto.vessel}/{dto.voyno} {(dto.etd.HasValue ? dto.etd.Value.ToString("yyyy-MM-dd") : "")} "; messageInfo.Main.TaskDesp = $"重要提醒-{dto.cautionNoticeType.GetDescription()} BLNo:{dto.mblNo} {dto.vessel}/{dto.voyno} {(dto.etd.HasValue ? dto.etd.Value.ToString("yyyy-MM-dd") : "")}"; messageInfo.Main.CautionNoticeInfo = new TaskManageOrderCautionNoticeInfo @@ -3529,11 +3525,11 @@ namespace Myshipping.Application notifyList = new List() }; - if(!string.IsNullOrWhiteSpace(bcSrcDto.ETD)) + if (!string.IsNullOrWhiteSpace(bcSrcDto.ETD)) { DateTime etd = DateTime.MinValue; - if(DateTime.TryParse(bcSrcDto.ETD,out etd)) + if (DateTime.TryParse(bcSrcDto.ETD, out etd)) { notice.etd = etd; } diff --git a/Myshipping.Core/Const/CommonConst.cs b/Myshipping.Core/Const/CommonConst.cs index c97f7a8a..9a874018 100644 --- a/Myshipping.Core/Const/CommonConst.cs +++ b/Myshipping.Core/Const/CommonConst.cs @@ -260,6 +260,17 @@ public class CommonConst public const string CACHE_KEY_BOOKING_LABEL = "BookingLabelList"; #endregion + #region 映射模块 + /// + /// 收货地名称解析装货港港口 + /// + public const string RECEIPT_TO_PORTLOAD = "ReceiptToPortLoad"; + + /// + /// 交货地名称解析卸货港港口 + /// + public const string DELIVERY_TO_PORT = "DeliveryToPort"; + #endregion #region 系统运行方式 /// diff --git a/Myshipping.Core/Myshipping.Core.xml b/Myshipping.Core/Myshipping.Core.xml index 842327b7..beb29ecb 100644 --- a/Myshipping.Core/Myshipping.Core.xml +++ b/Myshipping.Core/Myshipping.Core.xml @@ -822,6 +822,16 @@ 标签缓存键 + + + 收货地名称解析装货港港口 + + + + + 交货地名称解析卸货港港口 + + 和川(会执行一些和川特有的逻辑) @@ -7340,6 +7350,16 @@ 用户Id + + + 权限范围 + + + + + 权限类型(1用户,2公司) + + 根据授权key 换取token diff --git a/Myshipping.Web.Core/Startup.cs b/Myshipping.Web.Core/Startup.cs index 9b8fdd99..ac13646d 100644 --- a/Myshipping.Web.Core/Startup.cs +++ b/Myshipping.Web.Core/Startup.cs @@ -24,6 +24,7 @@ using Newtonsoft.Json; using Myshipping.Application.ConfigOption; using Myshipping.Application.Event; using Myshipping.Core.MQ; +using Myshipping.Application.MQ; namespace Myshipping.Web.Core; @@ -147,6 +148,12 @@ public class Startup : AppStartup builder.AddSubscriber(); } + //自用订舱:处理订舱自动化 + if (runType is CommonConst.RUN_TYPE_NORMAL) + { + builder.AddSubscriber(); + } + //扣费 builder.AddSubscriber(); }); @@ -157,6 +164,13 @@ public class Startup : AppStartup // 公司员工变动消息队列服务 services.AddHostedService(); } + + //自用订舱:处理订舱自动化 + if (runType is CommonConst.RUN_TYPE_NORMAL) + { + // 订舱自动化消息队列服务 + services.AddHostedService(); + } } public void Configure(IApplicationBuilder app, IWebHostEnvironment env)