using Furion; using Furion.FriendlyException; using Furion.Logging; using Furion.RemoteRequest.Extensions; using Myshipping.Application.Entity; using Myshipping.Application.Helper; using Myshipping.Core; using Myshipping.Core.Entity; using Myshipping.Core.Service; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics.Contracts; using System.Diagnostics.Metrics; using System.Linq; using System.Text; using System.Text.Encodings.Web; using System.Text.Json; 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 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 == "BookingPostApiServerAddr" && x.GroupCode == "DJY_CONST"); if (sCfgSpiderUrl == null) { return new KeyValuePair(false, "订舱API的爬虫URL地址未配置,请联系管理员"); } var sCfgUserKey = sysConfigList.FirstOrDefault(x => x.Code == "BookingPostApiKey" && x.GroupCode == "DJY_CONST"); var sCfgUserSecret = sysConfigList.FirstOrDefault(x => x.Code == "BookingPostApiSecret" && x.GroupCode == "DJY_CONST"); if (sCfgUserKey == null || sCfgUserSecret == null) { return new KeyValuePair(false, "订舱API的KEY和密钥未配置,请联系管理员"); } BookingSoTemplate template = null; DjyCustomerContact custContact = null; var postModel = new EMCSoApiModel(); postModel.webAccount = custOrder.BookingAccount; postModel.webPassword = custOrder.BookingPassword; //查找模板: //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.IsDeleted == false && x.CarrierId == custOrder.CARRIERID && x.UserId == custContact.Id && 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 mapPlaceReceipt = mappingPortLoad.FirstOrDefault(x => x.Module == "DjyCustBooking" && x.CarrierCode == "EMC" && x.Code == custOrder.PLACERECEIPTCODE); if (mapPlaceReceipt == null) { throw Oops.Bah($"未找到收货地映射信息:{custOrder.PLACERECEIPTCODE}"); } //起运港 var mapPortLoad = mappingPortLoad.FirstOrDefault(x => x.Module == "DjyCustBooking" && x.CarrierCode == "EMC" && x.Code == custOrder.PORTLOADCODE); if (mapPortLoad == null) { throw Oops.Bah($"未找到起运港映射信息:{custOrder.PORTLOADCODE}"); } //卸货港 var mapPort = mappingPort.FirstOrDefault(x => x.Module == "DjyCustBooking" && x.CarrierCode == "EMC" && x.Code == custOrder.PORTDISCHARGECODE); if (mapPort == null) { throw Oops.Bah($"未找到卸货港映射信息:{custOrder.PORTDISCHARGECODE}"); } //目的地 var mapDestination = mappingPort.FirstOrDefault(x => x.Module == "DjyCustBooking" && x.CarrierCode == "EMC" && x.Code == custOrder.DESTINATIONCODE); if (mapPort == null) { throw Oops.Bah($"未找到目的地映射信息:{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('-'); postModel.routes = new EMCSoApiRoute() { searchConditionDate = custOrder.ETD.Value.ToString("yyyy-MM-dd"), originName = mapPlaceReceipt.MapCode, destinationName = mapDestination.MapCode, polPortName = mapPortLoad.MapCode, podPortName = mapPort.MapCode, serviceType = custOrder.ServiceType, serviceMode = custOrder.ServiceMode, }; //货物信息(箱信息) 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", commodityName = custOrder.DESCRIPTION }; postModel.cargoInfos.Add(apiBox); } //合约信息 postModel.contractInfo = new EMCSoApiContractInfo() { bookingOffice = custOrder.BookingAddr, issuePlace = custOrder.BillSignLoc, billNum = custOrder.BillCount, contractNo = custOrder.CONTRACTNO, contractSelect = custOrder.ContractType, contractParty = custOrder.SignType }; //船期数据 if (string.IsNullOrEmpty(custOrder.ExtendData)) { return new KeyValuePair(false, $"船期数据信息不存在"); } var extData = JObject.Parse(custOrder.ExtendData); postModel.shipInfo = extData.GetJObjectValue("shipInfo"); //订舱公司详情 postModel.companyInfo = new EMCSoApiCompanyInfo() { receiveBookingNotice = custOrder.AcceptNotify ? "Yes" : "No", referenceNO = custOrder.CustomerInnerCode }; #region 收发通及货代 var companyName = ""; var telCountryCode = ""; var telAreaCode = ""; var telNumber = ""; //发货人 companyName = custOrder.ShipperName; telCountryCode = custOrder.ShipperPhoneCountryCode; telAreaCode = custOrder.ShipperPhoneCode; telNumber = custOrder.ShipperPhone; var bcMail = ""; if (!string.IsNullOrEmpty(custOrder.OpMail)) //优先使用东胜上传的邮箱 { bcMail = custOrder.OpMail; } else if (!string.IsNullOrEmpty(custContact.Email)) { bcMail = custContact.Email; } else { bcMail = template.BcReceiveEmail; } postModel.shipperInfo = new EMCSoApiSFTInfo() { companyName = companyName, contactTitle = custOrder.ShipperSex, contactLName = custOrder.ShipperFirstName, contactFName = custOrder.ShipperLastName, tel1 = telCountryCode, tel2 = telAreaCode, tel3 = telNumber, referenceNO = custOrder.ShipperInnerCode, email = bcMail }; //收货人 companyName = custOrder.ConsigneeName; telCountryCode = custOrder.ConsigneePhoneCountryCode; telAreaCode = custOrder.ConsigneePhoneCode; telNumber = custOrder.ConsigneePhone; if (!string.IsNullOrEmpty(companyName)) { postModel.consigneeInfo = new EMCSoApiSFTInfo() { companyName = companyName, contactTitle = custOrder.ConsigneeSex, contactLName = custOrder.ConsigneeFirstName, contactFName = custOrder.ConsigneeLastName, tel1 = telCountryCode, tel2 = telAreaCode, tel3 = telNumber, referenceNO = custOrder.ConsigneeInnerCode }; } //通知人 companyName = custOrder.NotifypartName; telCountryCode = custOrder.NotifypartPhoneCountryCode; telAreaCode = custOrder.NotifypartPhoneCode; telNumber = custOrder.NotifypartPhone; if (!string.IsNullOrEmpty(companyName)) { postModel.notifyInfo = new EMCSoApiSFTInfo() { companyName = companyName, contactTitle = custOrder.NotifypartSex, contactLName = custOrder.NotifypartFirstName, contactFName = custOrder.NotifypartLastName, tel1 = telCountryCode, tel2 = telAreaCode, tel3 = telNumber, referenceNO = custOrder.NotifypartInnerCode }; } //货代 companyName = custOrder.BookingName; telCountryCode = custOrder.BookingPhoneCountryCode; telAreaCode = custOrder.BookingPhoneCode; telNumber = custOrder.BookingPhone; var djyBookMail = sysConfigList.FirstOrDefault(x => x.Code == "DjyCustomerBookReceiveBcMail"); postModel.forwarderInfo = new EMCSoApiSFTInfo() { companyName = companyName, contactTitle = custOrder.BookingSex, contactLName = custOrder.BookingFirstName, contactFName = custOrder.BookingLastName, tel1 = telCountryCode, tel2 = telAreaCode, tel3 = telNumber, referenceNO = custOrder.BookingInnerCode, email = djyBookMail.Value }; #endregion postModel.remark = custOrder.SOREMARK; postModel.copyNum = custOrder.CopyNum; var apiUrl = sCfgSpiderUrl.Value; if (!apiUrl.EndsWith("/")) { apiUrl += "/"; } apiUrl += "v1/emc/booking/auto"; var jsonStr = JsonConvert.SerializeObject(postModel, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }); Log.Information($"发送API数据给爬虫({apiUrl}):{jsonStr}"); var rtn = await apiUrl.SetBody(jsonStr, "application/json") .PostAsStringAsync(); Log.Information($"爬虫返回:{rtn}"); var jobjRtn = JObject.Parse(rtn); if (jobjRtn.GetIntValue("code") == 200) { var jdata = jobjRtn.GetJObjectValue("data"); if (jdata != null) { JObject extObj = null; if (!string.IsNullOrEmpty(custOrder.ExtendData)) { extObj = JObject.Parse(custOrder.ExtendData); } else { extObj = new JObject(); } var bookingNoArr = jdata.GetJArrayValue("bookingNo"); extObj["CustNO"] = bookingNoArr; custOrder.ExtendData = extObj.ToJsonString(); await repCustOrder.AsUpdateable(custOrder).UpdateColumns(x => new { x.ExtendData }).ExecuteCommandAsync(); Log.Information($"回写CC号 {custOrder.Id} {custOrder.BOOKINGNO}"); } 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 dynamic 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; } /// /// 合约类型 /// 当类型为TR时,合约号不生效 /// SC, TR /// public string contractType { get; set; } = "SC"; /// /// 合约号 /// 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 List 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; } } }