using Microsoft.Extensions.Logging; using Myshipping.Application.Entity; using Myshipping.Core.Service; using Myshipping.Core; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Furion.FriendlyException; using Furion.JsonSerialization; using Furion.RemoteRequest.Extensions; using Furion; using Mapster; using Microsoft.AspNetCore.Mvc; using Furion.DependencyInjection; using Furion.DynamicApiController; using System.Text.RegularExpressions; using Myshipping.Application.Helper; using Myshipping.Core.Const; using Yitter.IdGenerator; using System.Diagnostics; using Newtonsoft.Json; using StackExchange.Profiling.Internal; namespace Myshipping.Application.Service.BookingOrder { /// /// 马士基 SPOT /// [ApiDescriptionSettings("Application", Name = "BookingMSKSPOTAPI", Order = 10)] public class BookingMSKSPOTAPIService: IBookingMSKSPOTAPIService, IDynamicApiController, ITransient { private readonly SqlSugarRepository _bookingDeliveryRecordRep; private readonly SqlSugarRepository _bookingDeliveryRecordCtnRep; private readonly SqlSugarRepository _bookingDeliveryRecordShipScheduleRep; private readonly ISysCacheService _cache; private readonly IDjyWebsiteAccountConfigService _webAccountConfig; private readonly ILogger _logger; private readonly ISysDataUserMenu _sysDataUserMenuService; private readonly IDjyTenantParamService _djyTenantParamService; const string CONST_MSK_API_COMMODITY_URL = "MSKApiCommodity"; const string CONST_MSK_API_BOOKING_URL = "MSKApiBooking"; const string CONST_MSK_SPOT_API_QUERY_SCHEDULE_RATE_URL = "MSKSPOTScheduleRate"; const string CONST_MSK_SPOT_API_LOCATION_URL = "MSKSPOTApilocation"; public BookingMSKSPOTAPIService(ILogger logger, ISysCacheService cache, IDjyWebsiteAccountConfigService webAccountConfig, SqlSugarRepository bookingDeliveryRecordRep, ISysDataUserMenu sysDataUserMenuService, IDjyTenantParamService djyTenantParamService, SqlSugarRepository bookingDeliveryRecordShipScheduleRep, SqlSugarRepository bookingDeliveryRecordCtnRep) { _logger = logger; _cache = cache; _webAccountConfig = webAccountConfig; _bookingDeliveryRecordRep = bookingDeliveryRecordRep; _bookingDeliveryRecordCtnRep = bookingDeliveryRecordCtnRep; _sysDataUserMenuService = sysDataUserMenuService; _djyTenantParamService = djyTenantParamService; _bookingDeliveryRecordShipScheduleRep = bookingDeliveryRecordShipScheduleRep; } #region 检索始发地、目的港口信息 /// /// 检索始发地、目的港口信息 /// /// 请求详情 /// 返回检索结果 [HttpPost("/BookingMSKSPOTAPI/SearchLocations")] public async Task> SearchLocations(QuerySPOTLocationsDto model) { List list = new List(); /* MSKApiCommodity */ try { if (string.IsNullOrWhiteSpace(model.cityName)) throw Oops.Oh("港口或城市名称不能为空"); if (string.IsNullOrWhiteSpace(model.carrierCode)) throw Oops.Oh("服务船公司不能为空"); if (string.IsNullOrWhiteSpace(model.carrierId)) throw Oops.Oh("船公司代码不能为空"); if (model.cityName.Length < 3) throw Oops.Oh("港口或城市名称至少输入3个以上字符"); string queryUrl = string.Empty; if (model.carrierId.Equals("MSK", StringComparison.OrdinalIgnoreCase)) { queryUrl = _cache.GetAllDictData().GetAwaiter().GetResult() .FirstOrDefault(x => x.TypeCode == "url_set" && x.Code == CONST_MSK_SPOT_API_LOCATION_URL)?.Value; } else { throw Oops.Oh($"当前船公司 {model.carrierId} 未配置相应的请求接口"); } if (string.IsNullOrWhiteSpace(queryUrl)) throw Oops.Oh("未配置商品请求接口地址,请联系管理员"); var webAccountConfig = _webAccountConfig .GetAccountConfig("MSKSpotApi", UserManager.UserId).GetAwaiter().GetResult(); if (webAccountConfig == null) throw Oops.Oh("未配检索始发地、目的港口,请先配置个人账户 类型-MSKApi"); MSKAPISPOTSearchLocationDto queryInfo = new MSKAPISPOTSearchLocationDto { userKey = App.Configuration["MSKAPIDjyUserKey"], userSecret = App.Configuration["MSKAPIDjyUserSecret"], operatingEnvironment = "PRODUCTION",//App.Configuration["MSKAPIOPEnvironment"], spot_api_key = webAccountConfig.Account, brandScac = model.carrierCode, originPrefix = model.cityName }; MSKAPISPOTSearchLocationResultDto resultInfo = null; var rlt = await queryUrl.SetBody(queryInfo) .PostAsStringAsync(); if (!string.IsNullOrWhiteSpace(rlt)) { try { resultInfo = JSON.Deserialize(rlt); } catch (Exception ex) { _logger.LogInformation($"请求MSK API检索始发地、目的港口异常,原因:{ex.Message}"); throw Oops.Bah($"请求MSK API检索始发地、目的港口异常,原因:{ex.Message}"); } } if (resultInfo != null && resultInfo.code == 200 && resultInfo.data != null && resultInfo.data.Count > 0) { list = resultInfo.data.Select(a => { return new QueryLocationsResultDto { carrierGeoID = a.geoId, cityName = a.cityName, countryCode = a.countryCode, countryName = a.countryName, locationName = a.locationName, UNRegionName = a.regionName, UNLocationCode = a.unLocCode, }; }).ToList(); } } catch (Exception ex) { _logger.LogError($"检索始发地、目的港口异常,req={JSON.Serialize(model)} 原因:{ex.Message}"); throw Oops.Bah($"检索始发地、目的港口失败,{ex.Message}"); } return list; } #endregion #region 检索海运船期详情 /// /// 检索海运船期详情 /// /// 请求船期详情 /// 返回船期结果 [HttpPost("/BookingMSKSPOTAPI/SearchShipSailingSchedule")] public async Task> SearchShipSailingSchedule(QueryMSKSPOTShipDateDto query) { List list = new List(); try { var model = query.Adapt(); if (string.IsNullOrWhiteSpace(model.originGeoId)) throw Oops.Oh($"起运地不能为空"); if (string.IsNullOrWhiteSpace(model.destinationGeoId)) throw Oops.Oh($"目的地不能为空"); if (string.IsNullOrWhiteSpace(model.brandScac)) throw Oops.Oh($"服务船公司不能为空"); if (string.IsNullOrWhiteSpace(model.carrierId)) throw Oops.Oh("船公司代码不能为空"); if (string.IsNullOrWhiteSpace(model.departureDate)) throw Oops.Oh($"预计离港日期不能为空"); DateTime etd = DateTime.MinValue; if (!DateTime.TryParse(model.departureDate, out etd)) throw Oops.Oh($"预计离港日期格式错误"); if (string.IsNullOrWhiteSpace(model.containerType)) throw Oops.Oh($"箱型不能为空"); if (!model.containerCount.HasValue || model.containerCount.Value <= 0) throw Oops.Oh($"箱型不能为空"); if (!model.containerWeight.HasValue || model.containerWeight.Value <= 0) throw Oops.Oh($"箱重不能为空"); if (model.containerWeight.HasValue) { model.containerWeight = model.containerWeight.Value / 1000; } string queryUrl = string.Empty; if (model.carrierId.Equals("MSK", StringComparison.OrdinalIgnoreCase)) { queryUrl = _cache.GetAllDictData().GetAwaiter().GetResult() .FirstOrDefault(x => x.TypeCode == "url_set" && x.Code == CONST_MSK_SPOT_API_QUERY_SCHEDULE_RATE_URL)?.Value; } else { throw Oops.Oh($"当前船公司 {model.carrierId} 未配置相应的请求接口"); } if (string.IsNullOrWhiteSpace(queryUrl)) throw Oops.Oh("未配置查询船期请求接口地址,请联系管理员"); var webAccountConfig = _webAccountConfig .GetAccountConfig("MSKSpotApi", UserManager.UserId).GetAwaiter().GetResult(); if (webAccountConfig == null) throw Oops.Oh("未配置个人账户,请先配置个人账户 类型-MSKApi"); MSKAPISPOTSearchScheduleRateDto queryInfo = new MSKAPISPOTSearchScheduleRateDto { userKey = App.Configuration["MSKAPIDjyUserKey"], userSecret = App.Configuration["MSKAPIDjyUserSecret"], operatingEnvironment = "PRODUCTION",//App.Configuration["MSKAPIOPEnvironment"], spot_api_key = webAccountConfig.Account, originServiceMode = model.originServiceMode, destinationServiceMode = model.destinationServiceMode, originGeoId = model.originGeoId, destinationGeoId = model.destinationGeoId, brandScac = model.brandScac, departureDate = etd.ToString("yyyy-MM-dd"), }; //有时候船期需要带上箱型检索 if (!string.IsNullOrWhiteSpace(model.containerType)) { //这里需要翻译一下箱型 var ctnCodeMappingList = _cache.GetAllMappingCtn().GetAwaiter().GetResult().ToList(); if (ctnCodeMappingList.Count > 0) ctnCodeMappingList = ctnCodeMappingList.Where(x => x.CarrierCode == "MSK" && x.Module == "BOOK_MSK_SPOT_API").ToList(); var ctnMapping = ctnCodeMappingList.FirstOrDefault(t => t.Code.Equals(model.containerType)); if (ctnMapping == null) { queryInfo.containerType = model.containerType; } else { queryInfo.containerType = ctnMapping.MapCode; } } if (model.containerCount.HasValue) { queryInfo.containerCount = model.containerCount.Value; } if (model.containerWeight.HasValue) { queryInfo.containerWeight = model.containerWeight.Value; } MSKAPISPOTSearchScheduleRateResultDto resultInfo = null; var jsonBody = Newtonsoft.Json.JsonConvert.SerializeObject(queryInfo, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); var rlt = await queryUrl.SetBody(jsonBody) .PostAsStringAsync(); _logger.LogInformation($"请求MSK SPOT API查询船期请求,{jsonBody}"); if (!string.IsNullOrWhiteSpace(rlt)) { try { _logger.LogInformation($"请求MSK SPOT API查询船期结果,{rlt}"); resultInfo = JSON.Deserialize(rlt); } catch (Exception ex) { _logger.LogInformation($"请求MSK SPOT API查询船期异常,原因:{ex.Message}"); throw Oops.Bah($"请求MSK API查询船期异常,原因:{ex.Message}"); } } if (resultInfo != null && resultInfo.code == 200 && resultInfo.data != null && resultInfo.data.Count > 0) { foreach (var scheduleModel in resultInfo.data) { CacheShipSailingSchedule(scheduleModel).GetAwaiter().GetResult(); } list = resultInfo.data.Select(a => { MSKAPISPOTScheduleRateResultShowDto showDto = new MSKAPISPOTScheduleRateResultShowDto { priceID = a.priceID, PId = a.PId, MD5 = a.MD5, totalAmount = a.totalAmount.HasValue ? (decimal)a.totalAmount.Value : 0, TotalCurrency = a.totalCurrency, orignCarrierCityGeoID = a.originGeoId, orignUNLocationCode = a.originUnLocCode, orignCityName = a.originCityName, originRegionName = a.originRegionName, originCountryName = a.originCountryName, deliveryCarrierCityGeoID = a.destinationGeoId, deliveryUNLocationCode = a.destinationUnLocCode, deliveryCityName = a.destinationCityName, deliveryRegionName = a.destinationRegionName, deliveryCountryName = a.destinationCountryName, vesselName = a.vesselName, carrierDepartureVoyageNumber = a.voyageNumber, Legs = new List() }; if (a.totalAmount.HasValue) showDto.totalAmountShow = string.Format("{0:N}", a.totalAmount.Value); //ETD if (!string.IsNullOrWhiteSpace(a.departureDate)) { DateTime currDate = DateTime.MinValue; if (DateTime.TryParse(a.departureDate, out currDate)) { showDto.ETD = currDate; } else { throw Oops.Bah($"查询船期错误,priceID={a.priceID} pid={a.PId} 预计离港日期departureDate={a.departureDate} 格式解析错误"); } } //ETA if (!string.IsNullOrWhiteSpace(a.arrivalDate)) { DateTime currDate = DateTime.MinValue; if (DateTime.TryParse(a.arrivalDate, out currDate)) { showDto.ETA = currDate; } else { throw Oops.Bah($"查询船期错误,priceID={a.priceID} pid={a.PId} 预计到达日期 arrivalDate={a.arrivalDate} 格式解析错误"); } } //计算预计天数 if (showDto.ETD.HasValue && showDto.ETA.HasValue) { TimeSpan ts = showDto.ETA.Value.Subtract(showDto.ETD.Value); var timeDiff = ts.TotalHours; showDto.days = (int)Math.Ceiling(timeDiff / 24.0); } if (a.isTransfer.Equals("true", StringComparison.OrdinalIgnoreCase)) showDto.isTransfer = true; //Legs if (a.scheduleDetails != null && a.scheduleDetails.Count > 0) { var legs = a.scheduleDetails.Select(b => { MSKAPISPOTScheduleRateResultShowLegsDto leg = new MSKAPISPOTScheduleRateResultShowLegsDto { vesselName = b.transport.vessel.longName, VoyageNo = b.transport.voyageNumber, From = new MSKAPISPOTScheduleRateResultShowLegsLocationDto { CityGeoId = b.fromLocation.cityGeoId, CityName = b.fromLocation.cityName, CountryCode = b.fromLocation.countryCode, CountryName = b.fromLocation.countryName, rkstCode = b.fromLocation.rkstCode, SiteGeoId = b.fromLocation.siteGeoId, LocationType = "From", RegionName = b.fromLocation.regionName, SiteName = b.fromLocation.siteName, UnLocCode = b.fromLocation.unLocCode, }, To = new MSKAPISPOTScheduleRateResultShowLegsLocationDto { CityGeoId = b.toLocation.cityGeoId, CityName = b.toLocation.cityName, CountryCode = b.toLocation.countryCode, CountryName = b.toLocation.countryName, rkstCode = b.toLocation.rkstCode, SiteGeoId = b.toLocation.siteGeoId, LocationType = "To", RegionName = b.toLocation.regionName, SiteName = b.toLocation.siteName, UnLocCode = b.toLocation.unLocCode, } }; //ETD if (!string.IsNullOrWhiteSpace(b.fromLocation.date)) { DateTime currDate = DateTime.MinValue; if (DateTime.TryParse($"{b.fromLocation.date} {b.fromLocation.time}", out currDate)) { leg.ETD = currDate; } else { throw Oops.Bah($"查询船期错误,航程明细 priceID={a.priceID} pid={a.PId} 预计离港日期fromLocation.date={b.fromLocation.date} {b.fromLocation.time} 格式解析错误"); } } //ETA if (!string.IsNullOrWhiteSpace(b.toLocation.date)) { DateTime currDate = DateTime.MinValue; if (DateTime.TryParse(a.arrivalDate, out currDate)) { leg.ETA = currDate; } else { throw Oops.Bah($"查询船期错误,航程明细 priceID={a.priceID} pid={a.PId} 预计到达日期 toLocation.date={b.toLocation.date} {b.toLocation.time} 格式解析错误"); } } return leg; }).ToList(); showDto.Legs = legs.OrderBy(t=>t.ETD).Select((t,idx)=> { t.SortNo = idx + 1; return t; }).ToList(); } return showDto; }).ToList(); } else { throw Oops.Bah($"检索海运船期详情失败,{resultInfo.msg}"); } } catch (Exception ex) { _logger.LogError($"检索海运船期详情异常,req={JSON.Serialize(query)} 原因:{ex.Message}"); throw Oops.Bah($"检索海运船期详情失败,{ex.Message}"); } return list; } #endregion #region 缓存船期数据 /// /// 缓存船期数据 /// /// 船期查询结果明细 /// 船期类型 MSKSPOT-马士基即期 /// 返回主键ID private async Task CacheShipSailingSchedule(MSKAPISPOTSearchScheduleRateResultDataDto model,string busiType = "MSKSPOT") { /* 1、按照船期明细缓存,并把ID作为缓存的KEY 2、对数据进行JSON串行化的文本提取MD5 3、写入缓存,并返回ID */ var newModel = model.Adapt(); newModel.priceID = null; var json = Newtonsoft.Json.JsonConvert.SerializeObject(newModel); model.PId = YitIdHelper.NextId(); string md5 = json.ToMd5(); model.MD5 = md5; var shareKey = model.PId.ToString(); DateTime nowDate = DateTime.Now; DateTime expireDateTime = DateTime.Now.AddHours(1); var expireTimeSpan = expireDateTime.Subtract(nowDate).Duration(); if (!_cache.Exists($"{shareKey}_{busiType}")) { await _cache.SetTimeoutAsync($"{shareKey}_{busiType}", Newtonsoft.Json.JsonConvert.SerializeObject(model), expireTimeSpan); } } #endregion private MSKAPISPOTSearchScheduleRateResultDataDto GetCacheShipSailingSchedule(long pid, string busiType = "MSKSPOT") { if (_cache.Exists($"{pid}_{busiType}")) { return _cache.Get($"{pid}_{busiType}"); } return null; } #region 发送马士基订舱请求 /// /// 发送马士基订舱请求 /// /// 请求订舱详情 /// [HttpPost("/BookingMSKSPOTAPI/SendMSKBooking")] public async Task SendMSKBooking(MSKSPOTBookingDto model) { MSKBookingResultDto result = await InnerSendMSKBooking(model, 0); return result; } #endregion #region 发送马士基订舱请求(内部方法) /// /// 发送马士基订舱请求(内部方法) /// /// 请求订舱详情 /// 当前马士基订舱主键 /// 是否默认保存 /// private async Task InnerSendMSKBooking(MSKSPOTBookingDto model, long currId, bool isDefaultSave = true) { MSKBookingResultDto result = new MSKBookingResultDto(); /* MSKApiBooking */ try { if (string.IsNullOrWhiteSpace(model.carrierCode)) throw Oops.Oh($"服务船公司不能为空"); if (string.IsNullOrWhiteSpace(model.carrierId)) throw Oops.Oh("船公司代码不能为空"); string sendUrl = string.Empty; if (model.carrierId.Equals("MSK", StringComparison.OrdinalIgnoreCase)) { sendUrl = _cache.GetAllDictData().GetAwaiter().GetResult() .FirstOrDefault(x => x.TypeCode == "url_set" && x.Code == CONST_MSK_API_BOOKING_URL)?.Value; } else { throw Oops.Oh($"当前船公司 {model.carrierId} 未配置相应的请求接口"); } if (string.IsNullOrWhiteSpace(sendUrl)) throw Oops.Oh("未配置发送订舱请求接口地址,请联系管理员"); var webAccountConfig = _webAccountConfig .GetAccountConfig("MSKApi", UserManager.UserId).GetAwaiter().GetResult(); if (webAccountConfig == null) throw Oops.Oh("未配置个人账户,请先配置个人账户 类型-MSKApi"); //这里是校验必填项 ValidateMSKAPIData(model); MSKAPISPOTSearchScheduleRateResultDataDto selectedShipSchedule = null; BookingDeliveryRecordShipSchedule shipScheduleRecord = null; if (model.PId == 0) throw Oops.Oh("船期信息不能为空,请重新检索船期"); if (model.PId > 0) { if (model.id.HasValue && model.id.Value > 0) { var recordId = model.id.Value; shipScheduleRecord = await _bookingDeliveryRecordShipScheduleRep.AsQueryable() .FirstAsync(a => a.RECORD_ID == recordId && a.SHIP_RATE_PID != null && a.SHIP_RATE_PID.Value == model.PId && a.IsDeleted == false); } if (shipScheduleRecord == null) { selectedShipSchedule = GetCacheShipSailingSchedule(model.PId); } else { selectedShipSchedule = JSON.Deserialize(shipScheduleRecord.SHIP_JSON); } if (selectedShipSchedule == null) throw Oops.Oh("船期数据校验失败,请重新查询船期信息"); if (selectedShipSchedule.priceID.Equals(model.priceID)) { _logger.LogInformation($"请求当前船期请求参数错误 priceID={model.priceID} 缓存priceID={selectedShipSchedule.priceID} PId={model.PId}"); throw Oops.Oh("船期数据校验失败,当前船期请求参数错误,请重新查询船期信息"); } if (selectedShipSchedule.totalAmount.Value != model.ShipRateTotalAmount.Value) { _logger.LogInformation($"请求当前船期请求参数错误 totalAmount={selectedShipSchedule.totalAmount.Value} 缓存totalAmount={model.ShipRateTotalAmount.Value} PId={model.PId}"); throw Oops.Oh("船期数据校验失败,当前船期请求参数错误,价格发生差异,请重新查询船期信息"); } } DateTime nowDate = DateTime.Now; var recordInfo = model.Adapt(); var recordCtnList = model.ctns.Adapt>(); var ctnCodeMappingList = _cache.GetAllMappingCtn().GetAwaiter().GetResult().ToList(); if (ctnCodeMappingList.Count > 0) ctnCodeMappingList = ctnCodeMappingList.Where(x => x.CarrierCode == "MSK" && x.Module == "BOOK_MSK_API").ToList(); MSKAPIBookingDto bookingDto = new MSKAPIBookingDto { userKey = App.Configuration["MSKAPIDjyUserKey"], userSecret = App.Configuration["MSKAPIDjyUserSecret"], operatingEnvironment = App.Configuration["MSKAPIOPEnvironment"], mskAppKey = webAccountConfig.Account, mskAppSecret = webAccountConfig.Password, bookingBody = new MSKAPIBookingBodyDto() }; bookingDto.bookingBody.references = new MSKAPIBookingReferenceDto { priceReference = model.priceReference, productCode = model.productCode, sender = model.sender, bookingOfficeUNLocationCode = "CNTAO", isSpotTermsAndConditionsAccepted = true, }; bookingDto.bookingBody.mandatoryParties = new MSKAPIBookingMandatoryParties { bookedByCompanyName = model.bookedByCompanyName, bookedByMaerskPartyCode = model.bookedByMaerskPartyCode, bookedByPartyContact = new MSKAPIBookingMandatoryPartyContact { name = model.bookedByCompanyContactName, email = model.bookedByCompanyContactEmail }, priceOwnerCompanyName = model.priceOwnerCompanyName, priceOwnerMaerskPartyCode = model.priceOwnerMaerskPartyCode, priceOwnerPartyContact = new MSKAPIBookingMandatoryPartyContact { name = model.priceOwnerContactName, email = model.priceOwnerContactEmail }, }; bookingDto.bookingBody.transport = new MSKAPIBookingTransport { carrierCode = model.carrierCode, earliestDepartureDate = model.earliestDepartureDate.Value.ToString("yyyy-MM-ddTHH:mm:ss"), exportServiceMode = model.exportServiceMode, importServiceMode = model.importServiceMode, routeDetails = new MSKAPIBookingRouteDetails { placeOfReceipt = new MSKAPIBookingRouteDetailsBase { UNLocationCode = model.userPlaceOfReceiptUnLocCode, maerskCityGeoId = selectedShipSchedule.originGeoId, }, placeOfDelivery = new MSKAPIBookingRouteDetailsBase { UNLocationCode = model.userPlaceOfDeliveryUnLocCode, maerskCityGeoId = selectedShipSchedule.destinationGeoId, }, selectedRoute = new MSKAPIBookingRoute { bookingSchedules = new List(), }, } }; foreach (var detail in selectedShipSchedule.scheduleDetails) { var currDto = new MSKAPIBookingSchedules(); //ETD if (!string.IsNullOrWhiteSpace(detail.fromLocation.date)) { DateTime currDate = DateTime.MinValue; if (DateTime.TryParse($"{detail.fromLocation.date} {detail.fromLocation.time}", out currDate)) { currDto.originDepartureDateTimeLocal = currDate.ToString("yyyy-MM-ddTHH:mm:ss"); } else { throw Oops.Bah($"查询船期错误,priceID={model.priceID} pid={model.PId} 预计离港日期departureDate={detail.fromLocation.date} {detail.fromLocation.time} 格式解析错误"); } } //ETD if (!string.IsNullOrWhiteSpace(detail.toLocation.date)) { DateTime currDate = DateTime.MinValue; if (DateTime.TryParse($"{detail.toLocation.date} {detail.toLocation.time}", out currDate)) { currDto.destinationArrivalDateTimeLocal = currDate.ToString("yyyy-MM-ddTHH:mm:ss"); } else { throw Oops.Bah($"查询船期错误,priceID={model.priceID} pid={model.PId} 预计离港日期ArrivalDate={detail.toLocation.date} {detail.toLocation.time} 格式解析错误"); } } currDto.transportMode = new MSKAPIBookingTransportMode { vessel = new MSKAPIBookingTransportModeVessel { name = detail.transport.vessel.name, maerskVesselCode = detail.transport.vessel.code, vesselIMONumber = detail.transport.vessel.imoNumber }, exportVoyageNumber = detail.transport.voyageNumber, }; currDto.serviceCode = detail.serviceCode; currDto.startLocation = new MSKAPIBookingRouteDetailsBase { cityName = selectedShipSchedule.scheduleDetails.FirstOrDefault().fromLocation.cityName, ISOcountryCode = selectedShipSchedule.scheduleDetails.FirstOrDefault().fromLocation.countryCode, UNLocationCode = selectedShipSchedule.scheduleDetails.FirstOrDefault().fromLocation.unLocCode, maerskCityGeoId = selectedShipSchedule.scheduleDetails.FirstOrDefault().fromLocation.cityGeoId, }; currDto.endLocation = new MSKAPIBookingRouteDetailsBase { cityName = selectedShipSchedule.scheduleDetails.FirstOrDefault().toLocation.cityName, ISOcountryCode = selectedShipSchedule.scheduleDetails.FirstOrDefault().toLocation.countryCode, UNLocationCode = selectedShipSchedule.scheduleDetails.FirstOrDefault().toLocation.unLocCode, maerskCityGeoId = selectedShipSchedule.scheduleDetails.FirstOrDefault().toLocation.cityGeoId, }; bookingDto.bookingBody.transport.routeDetails.selectedRoute.bookingSchedules.Add(currDto); } bookingDto.bookingBody.cargo = new MSKAPIBookingCargo { commodityCode = model.commodityCode, commodityCodeType = "MaerskCode", cargoType = model.cargoType, totalCargoWeight = model.totalCargoWeight }; //是否冷冻处理 if (model.isReefer) { bookingDto.bookingBody.cargo.reeferSettings = new MSKAPIBookingCargoReeferSettings(); bookingDto.bookingBody.cargo.reeferSettings.temperatureDetails = model.temperature; bookingDto.bookingBody.cargo.reeferSettings.temperatureMeasureUnit = "C"; bookingDto.bookingBody.cargo.reeferSettings.noOfProbes = model.noOfProbes; //每小时需要的通风量,单位为立方米(0-285) bookingDto.bookingBody.cargo.reeferSettings.ventilation = model.ventilation; bookingDto.bookingBody.cargo.reeferSettings.humidity = model.humidity; } bookingDto.bookingBody.equipmentAndHaulage = new List(); if (model.ctns != null && model.ctns.Count > 0) { model.ctns.ForEach(ctn => { var ctnMapping = ctnCodeMappingList.FirstOrDefault(t => t.Code.Equals(ctn.ctnCode)); if (ctnMapping == null) throw Oops.Oh($"未配置相应的箱型对应{ctn.ctnName},请联系管理员"); MSKAPIBookingEquipmentAndHaulage haulage = new MSKAPIBookingEquipmentAndHaulage { equipmentDetails = new MSKAPIBookingEquipmentAndHaulageItem(), stuffingDetails = new List() }; if (model.isShipperOwned) haulage.equipmentDetails.isShipperOwned = model.isShipperOwned; if (model.isImportReturned) haulage.equipmentDetails.isImportReturned = model.isImportReturned; haulage.equipmentDetails.ISOEquipmentCode = ctnMapping.MapCode; haulage.equipmentDetails.equipmentQuantity = ctn.ctnNum; haulage.stuffingDetails.Add(new MSKAPIBookingStuffingdetails { stuffingValue = (int)ctn.ctnSufferWeight.Value, stuffingMeasurementType = "WEIGHT", stuffingMeasurementUnit = "KGS" }); bookingDto.bookingBody.equipmentAndHaulage.Add(haulage); }); } long id = 0; if (isDefaultSave) { id = InnerSave(model, isSendApi: true).GetAwaiter().GetResult(); } else { id = currId; } result.id = id; MSKAPIBookingResultDto resultInfo = null; _logger.LogInformation($"开始请求MSK API订舱,JSON={JSON.Serialize(bookingDto)}"); var jsonBody = Newtonsoft.Json.JsonConvert.SerializeObject(bookingDto); var rlt = await sendUrl.SetBody(jsonBody) .PostAsStringAsync(); _logger.LogInformation($"开始请求MSK API订舱,返回结果 JSON={JSON.Serialize(rlt)}"); if (!string.IsNullOrWhiteSpace(rlt)) { try { resultInfo = JSON.Deserialize(rlt); } catch (Exception ex) { _logger.LogInformation($"请求MSK API订舱异常,原因:{ex.Message}"); throw Oops.Bah($"请求MSK API订舱异常,原因:{ex.Message}"); } } MSKAPIBookingResultDataDto resultData = null; if (resultInfo.code == 200) { resultData = JSON.Deserialize(JSON.Serialize(resultInfo.data)); } var entity = _bookingDeliveryRecordRep .FirstOrDefault(a => a.Id == id); if (resultInfo != null && resultInfo.code == 200 && resultData != null) { entity.REQUEST_ACKNOWLEDGEMENT_ID = resultData.requestAcknowledgementId; entity.BOOKING_REFERENCE = resultData.bookingReference; entity.STATUS = "SUCC"; entity.STATUS_NAME = "发送成功"; await _bookingDeliveryRecordRep.AsUpdateable(entity).UpdateColumns(x => new { x.REQUEST_ACKNOWLEDGEMENT_ID, x.BOOKING_REFERENCE, x.STATUS, x.STATUS_NAME }).ExecuteCommandAsync(); } else { entity.STATUS = "FAILURE"; entity.STATUS_NAME = "发送失败"; entity.NOTES = resultInfo.msg.Length > 500 ? resultInfo.msg.Substring(0, 500) : resultInfo.msg; await _bookingDeliveryRecordRep.AsUpdateable(entity).UpdateColumns(x => new { x.STATUS, x.STATUS_NAME, x.NOTES }).ExecuteCommandAsync(); throw Oops.Bah(resultInfo.msg); } result.succ = true; } catch (Exception ex) { _logger.LogError($"MSK API订舱异常,req={JSON.Serialize(model)} 原因:{ex.Message}"); //throw Oops.Bah($"MSK API订舱失败,{ex.Message}"); result.succ = false; result.msg = $"MSK API订舱失败,{ex.Message}"; throw Oops.Bah($"MSK API订舱失败,{ex.Message}"); } return result; } #endregion #region 校验马士基API订舱必填项 /// /// 校验马士基API订舱必填项 /// /// private void ValidateMSKAPIData(MSKSPOTBookingDto model) { /* 马士基API订舱内容校验 1、合约号必填 2、请求类别必填 3、服务船公司必填 4、订舱公司名称、代码、联系人、邮箱必填 5、持约方公司名称、代码、联系、邮箱必填 6、始发地城市名称、UNLOC、国家代码、服务模式 7、目的地城市名称、UNLOC、国家代码、服务模式 8、船名、航次、ETD、ETA 9、商品名称、代码必填 10、总重必填 11、货物标志必填 12、如果选择了冷冻处理,至少要填个温度 13、箱型、箱量、重量必填 14、总重=箱量*单箱重量 15、预计离港日期必填 */ if (string.IsNullOrWhiteSpace(model.priceReference)) throw Oops.Bah($"订舱合同惟一ID必填"); if (!Regex.IsMatch(model.priceReference, "[a-zA-Z0-9_/,-]{1,50}")) throw Oops.Bah($"订舱合同惟一ID格式错误 [a-zA-Z0-9_/,-]{{1,50}}"); if (string.IsNullOrWhiteSpace(model.sender)) throw Oops.Bah($"请求类别必填"); if (string.IsNullOrWhiteSpace(model.carrierCode)) throw Oops.Bah($"服务船公司必填"); if (string.IsNullOrWhiteSpace(model.bookedByCompanyName)) throw Oops.Bah($"订舱公司名称必填"); if (string.IsNullOrWhiteSpace(model.bookedByMaerskPartyCode)) throw Oops.Bah($"订舱方ID必填"); if (string.IsNullOrWhiteSpace(model.bookedByCompanyContactName)) throw Oops.Bah($"订舱方公司联系人必填"); if (string.IsNullOrWhiteSpace(model.bookedByCompanyContactEmail)) throw Oops.Bah($"订舱方公司邮箱必填"); if (string.IsNullOrWhiteSpace(model.priceOwnerContactName)) throw Oops.Bah($"持约方公司名称必填"); if (string.IsNullOrWhiteSpace(model.priceOwnerMaerskPartyCode)) throw Oops.Bah($"持约方ID必填"); if (string.IsNullOrWhiteSpace(model.priceOwnerContactName)) throw Oops.Bah($"持约方公司联系人必填"); if (string.IsNullOrWhiteSpace(model.priceOwnerContactEmail)) throw Oops.Bah($"持约方公司邮箱必填"); if (string.IsNullOrWhiteSpace(model.placeOfReceiptCityName)) throw Oops.Bah($"始发地城市名称必填"); if (string.IsNullOrWhiteSpace(model.placeOfReceiptUnLocCode)) throw Oops.Bah($"始发地UN CODE必填"); if (string.IsNullOrWhiteSpace(model.placeOfReceiptCountryCode)) throw Oops.Bah($"始发地国家代码必填"); if (string.IsNullOrWhiteSpace(model.exportServiceMode)) throw Oops.Bah($"始发地服务模式必填"); if (string.IsNullOrWhiteSpace(model.placeOfDeliveryCityName)) throw Oops.Bah($"目的地城市名称必填"); if (string.IsNullOrWhiteSpace(model.placeOfDeliveryUnLocCode)) throw Oops.Bah($"目的地UN CODE必填"); if (string.IsNullOrWhiteSpace(model.placeOfDeliveryCountryCode)) throw Oops.Bah($"目的地国家代码必填"); if (string.IsNullOrWhiteSpace(model.importServiceMode)) throw Oops.Bah($"目的地服务模式必填"); if (string.IsNullOrWhiteSpace(model.vesselName)) throw Oops.Bah($"船名必填,请确认正确选择了船期"); if (string.IsNullOrWhiteSpace(model.exportVoyageNumber)) throw Oops.Bah($"航次号必填,请确认正确选择了船期"); if (!model.originDepartureDateTimeLocal.HasValue) throw Oops.Bah($"ETD必填,请确认正确选择了船期"); if (!model.destinationArrivalDateTimeLocal.HasValue) throw Oops.Bah($"ETA必填,请确认正确选择了船期"); if (string.IsNullOrWhiteSpace(model.commodityCode)) throw Oops.Bah($"商品代码必填,请确认正确选择了商品"); if (string.IsNullOrWhiteSpace(model.commodityName)) throw Oops.Bah($"商品名称必填,请确认正确选择了商品"); if (!model.totalCargoWeight.HasValue || model.totalCargoWeight.Value < 1) throw Oops.Bah($"总重必填"); if (string.IsNullOrWhiteSpace(model.cargoType)) throw Oops.Bah($"货物标志必填"); if (!model.earliestDepartureDate.HasValue) throw Oops.Bah($"预计离港日期必填"); if (string.IsNullOrWhiteSpace(model.userPlaceOfReceiptUnLocCode)) throw Oops.Bah($"始发地必填"); if (string.IsNullOrWhiteSpace(model.userPlaceOfDeliveryUnLocCode)) throw Oops.Bah($"目的地必填"); //if (!model.isSendNoSchedule && string.IsNullOrWhiteSpace(model.carrierProductId)) // throw Oops.Bah($"船期信息不能为空,请查询船期信息"); if (model.isReefer) { if (!model.temperature.HasValue) throw Oops.Bah($"选择了冷冻处理,温度必填"); } if (model.ctns.Count == 0) throw Oops.Bah($"箱型箱量信息必填"); if (model.ctns.Any(a => string.IsNullOrWhiteSpace(a.ctnCode))) throw Oops.Bah($"箱型不能为空"); if (model.ctns.Any(a => !a.ctnNum.HasValue || a.ctnNum.Value < 1)) throw Oops.Bah($"箱量不能为空,并且不能小于1"); if (model.ctns.Any(a => !a.ctnSufferWeight.HasValue || a.ctnSufferWeight.Value < 1)) throw Oops.Bah($"箱内重量不能为空,并且不能小于1"); if (model.totalCargoWeight.Value != model.ctns.Sum(b => b.ctnNum.Value * b.ctnSufferWeight.Value)) { throw Oops.Bah($"箱内重量合计不等于总重,请修改"); } } #endregion #region 检索商品名称 /// /// 检索商品名称 /// /// 请求详情 /// 返回检索结果 [HttpPost("/BookingMSKSPOTAPI/SearchCommodities")] public async Task> SearchCommodities(QueryCommoditiesDto model) { List list = new List(); /* MSKApiCommodity */ try { if (string.IsNullOrWhiteSpace(model.commodityName)) throw Oops.Oh("商品名称不能为空"); if (string.IsNullOrWhiteSpace(model.carrierId)) throw Oops.Oh("船公司代码不能为空"); if (model.commodityName.Length < 3) throw Oops.Oh("商品名称至少输入3个以上字符"); string queryUrl = string.Empty; if (model.carrierId.Equals("MSK", StringComparison.OrdinalIgnoreCase)) { queryUrl = _cache.GetAllDictData().GetAwaiter().GetResult() .FirstOrDefault(x => x.TypeCode == "url_set" && x.Code == CONST_MSK_API_COMMODITY_URL)?.Value; } else { throw Oops.Oh($"当前船公司 {model.carrierId} 未配置相应的请求接口"); } if (string.IsNullOrWhiteSpace(queryUrl)) throw Oops.Oh("未配置商品请求接口地址,请联系管理员"); var webAccountConfig = _webAccountConfig .GetAccountConfig("MSKApi", UserManager.UserId).GetAwaiter().GetResult(); if (webAccountConfig == null) throw Oops.Oh("未配置个人账户,请先配置个人账户 类型-MSKApi"); MSKAPISearchCommodityDto queryInfo = new MSKAPISearchCommodityDto { userKey = App.Configuration["MSKAPIDjyUserKey"], userSecret = App.Configuration["MSKAPIDjyUserSecret"], operatingEnvironment = App.Configuration["MSKAPIOPEnvironment"], commodityName = model.commodityName, mskAppKey = webAccountConfig.Account }; MSKAPISearchCommodityResultDto resultInfo = null; var rlt = await queryUrl.SetBody(queryInfo) .PostAsStringAsync(); if (!string.IsNullOrWhiteSpace(rlt)) { try { resultInfo = JSON.Deserialize(rlt); } catch (Exception ex) { _logger.LogInformation($"请求MSK API检索商品异常,原因:{ex.Message}"); throw Oops.Bah($"请求MSK API检索商品异常,原因:{ex.Message}"); } } if (resultInfo != null && resultInfo.code == 200 && resultInfo.data != null && resultInfo.data.Count > 0) { list = resultInfo.data.Adapt>(); } } catch (Exception ex) { _logger.LogError($"检索商品名称异常,req={JSON.Serialize(model)} 原因:{ex.Message}"); throw Oops.Bah($"检索商品名称失败,{ex.Message}"); } return list; } #endregion #region 获取马士基API订舱详情 /// /// 获取马士基API订舱详情 /// /// 马士基API订舱ID /// 返回详情 [HttpGet("/BookingMSKSPOTAPI/GetInfo")] public async Task GetInfo(long id) { MSKSPOTBookingDto model = null; var entity = await _bookingDeliveryRecordRep.AsQueryable() .FirstAsync(a => a.Id == id); if (entity == null) throw Oops.Oh($"获取马士基API订舱详情失败,不存在或已作废"); model = entity.Adapt(); var ctnList = _bookingDeliveryRecordCtnRep.AsQueryable() .Where(a => a.RECORD_ID == id && a.IsDeleted == false).ToList(); if (ctnList.Count > 0) { model.ctns = ctnList.Select(a => new MSKBookingCtnInfo { id = a.Id, ctnCode = a.CTN_CODE, carrierCtnCode = a.CARRIER_CTN_CODE, ctnNum = a.CTN_NUM, ctnName = a.CTN_NAME, ctnSufferWeight = a.CTN_SUFFER_WEIGHT, stuffingMeasurementType = a.STUFFING_MEASUREMENT_TYPE, stuffingMeasurementUnit = a.STUFFING_MEASUREMENT_UNIT }).ToList(); } MSKAPISPOTSearchScheduleRateResultDataDto selectedShipSchedule = null; if (model.PId > 0) { var shipScheduleRecord = await _bookingDeliveryRecordShipScheduleRep.AsQueryable() .FirstAsync(a => a.SHIP_RATE_PID != null && a.SHIP_RATE_PID.Value == model.PId); if (shipScheduleRecord != null) { selectedShipSchedule = JSON.Deserialize(shipScheduleRecord.SHIP_JSON); } } return model; } #endregion #region 保存 /// /// 保存 /// /// 请求订舱详情 /// 返回ID [HttpPost("/BookingMSKSPOTAPI/Save")] public async Task Save([FromBody] MSKSPOTBookingDto model) { return await InnerSave(model); } #endregion #region 保存内部方法 /// /// 保存内部方法 /// /// API订舱详情 /// 是否发送API,true-发送需要校验状态;false-不校验状态 /// private async Task InnerSave(MSKSPOTBookingDto model, bool isSendApi = false) { DateTime nowDate = DateTime.Now; string ctnStat = string.Empty; _logger.LogInformation($"获取请求马士基API订舱报文,JSON={JSON.Serialize(model)}"); MSKAPISPOTSearchScheduleRateResultDataDto selectedShipSchedule = null; BookingDeliveryRecordShipSchedule shipScheduleRecord = null; if (model.PId > 0) { if (model.id.HasValue && model.id.Value > 0) { var recordId = model.id.Value; shipScheduleRecord = await _bookingDeliveryRecordShipScheduleRep.AsQueryable() .FirstAsync(a => a.RECORD_ID == recordId && a.SHIP_RATE_PID != null && a.SHIP_RATE_PID.Value == model.PId && a.IsDeleted == false); } if (shipScheduleRecord == null) { selectedShipSchedule = GetCacheShipSailingSchedule(model.PId); } else { selectedShipSchedule = JSON.Deserialize(shipScheduleRecord.SHIP_JSON); } if (selectedShipSchedule == null) throw Oops.Oh("船期数据校验失败,请重新查询船期信息"); if (selectedShipSchedule.priceID.Equals(model.priceID)) { _logger.LogInformation($"请求当前船期请求参数错误 priceID={model.priceID} 缓存priceID={selectedShipSchedule.priceID} PId={model.PId}"); throw Oops.Oh("船期数据校验失败,当前船期请求参数错误,请重新查询船期信息"); } if (selectedShipSchedule.totalAmount.Value != model.ShipRateTotalAmount.Value) { _logger.LogInformation($"请求当前船期请求参数错误 totalAmount={selectedShipSchedule.totalAmount.Value} 缓存totalAmount={model.ShipRateTotalAmount.Value} PId={model.PId}"); throw Oops.Oh("船期数据校验失败,当前船期请求参数错误,价格发生差异,请重新查询船期信息"); } model.placeOfReceiptCountryName = selectedShipSchedule.originCountryName; model.placeOfReceiptRegionName = selectedShipSchedule.originRegionName; model.placeOfReceiptGeoId = selectedShipSchedule.originGeoId; model.placeOfDeliveryRegionName = selectedShipSchedule.destinationCountryName; model.placeOfDeliveryCountryName = selectedShipSchedule.destinationCountryName; model.placeOfReceiptRegionName = selectedShipSchedule.destinationGeoId; } if (model.ctns != null && model.ctns.Count > 0) { ctnStat = string.Join(",", model.ctns.GroupBy(a => a.ctnName) .Select(a => $"{a.Key}*{a.ToList().Sum(b => b.ctnNum.HasValue ? b.ctnNum.Value : 0)}").ToArray()); } if (model.id.HasValue && model.id.Value > 0) { var oldInfo = _bookingDeliveryRecordRep.AsQueryable() .First(a => a.Id == model.id); if (oldInfo == null) throw Oops.Oh("订舱数据不存在或已作废"); if (oldInfo.STATUS == "SUCC") { if (isSendApi) throw Oops.Oh("订舱数据已发送成功,不能重复发送"); throw Oops.Oh("订舱数据已发送成功,不能修改"); } BookingDeliveryRecord entity = model.Adapt(); entity.Id = model.id.Value; entity.UpdatedTime = nowDate; entity.UpdatedUserId = UserManager.UserId; entity.UpdatedUserName = UserManager.Name; entity.CTN_STAT = ctnStat; entity.BOOKING_CHANNEL_TYPE = "SPOT_API"; entity.BOOKING_CHANNEL_TYPE_NAME = "SPOT订舱"; await _bookingDeliveryRecordRep.AsUpdateable(entity).IgnoreColumns(x => new { x.CreatedTime, x.CreatedUserId, x.CreatedUserName, x.BOOKING_REFERENCE, x.BookingId, x.REQUEST_ACKNOWLEDGEMENT_ID, x.STATUS, x.TenantId, x.IS_RECV_BC, x.LST_RECV_BC_DATE, x.IS_RECV_BK_CANCEL, x.LST_RECV_BK_CANCEL_DATE }).ExecuteCommandAsync(); if (model.ctns != null && model.ctns.Count > 0) { var ctnEntityList = _bookingDeliveryRecordCtnRep.AsQueryable() .Where(a => a.RECORD_ID == entity.Id && a.IsDeleted == false).ToList(); model.ctns.ForEach(ctn => { if (ctn.id.HasValue && ctn.id.Value > 0) { if (ctnEntityList.Any(x => x.Id == ctn.id.Value)) { ctnEntityList.Remove(ctnEntityList.FirstOrDefault(x => x.Id == ctn.id.Value)); } //更新 var ctnEntity = ctn.Adapt(); ctnEntity.Id = ctn.id.Value; ctnEntity.RECORD_ID = entity.Id; ctnEntity.UpdatedTime = nowDate; ctnEntity.UpdatedUserId = UserManager.UserId; ctnEntity.UpdatedUserName = UserManager.Name; _bookingDeliveryRecordCtnRep.AsUpdateable(ctnEntity).IgnoreColumns(x => new { x.CreatedUserId, x.CreatedUserName, x.CreatedTime, x.TenantId, x.STUFFING_MEASUREMENT_TYPE, x.STUFFING_MEASUREMENT_UNIT, x.CTN_SUFFER_WEIGHT }).ExecuteCommand(); } else { var ctnEntity = new BookingDeliveryRecordCtn { CTN_CODE = ctn.ctnCode, CTN_NAME = ctn.ctnName, CTN_NUM = ctn.ctnNum.Value, CTN_SUFFER_WEIGHT = (int)ctn.ctnSufferWeight.Value, }; ctnEntity.RECORD_ID = entity.Id; ctnEntity.CreatedTime = nowDate; ctnEntity.UpdatedTime = nowDate; ctnEntity.CreatedUserId = UserManager.UserId; ctnEntity.CreatedUserName = UserManager.Name; ctnEntity.STUFFING_MEASUREMENT_TYPE = "WEIGHT"; ctnEntity.STUFFING_MEASUREMENT_UNIT = "KGS"; _bookingDeliveryRecordCtnRep.Insert(ctnEntity); } }); if (ctnEntityList.Count > 0) { ctnEntityList.ForEach(async ctn => { ctn.IsDeleted = true; ctn.UpdatedTime = nowDate; ctn.UpdatedUserId = UserManager.UserId; ctn.UpdatedUserName = UserManager.Name; await _bookingDeliveryRecordCtnRep.AsUpdateable(ctn).UpdateColumns(x => new { x.IsDeleted, x.UpdatedTime, x.UpdatedUserId, x.UpdatedUserName }).ExecuteCommandAsync(); }); } } if(shipScheduleRecord == null) { if(model.PId > 0 && selectedShipSchedule != null) { shipScheduleRecord = new BookingDeliveryRecordShipSchedule { Id = YitIdHelper.NextId(), RECORD_ID = model.id.Value, PRICE_ID = model.priceID, SHIP_RATE_PID = selectedShipSchedule.PId, SHIP_RATE_MD5 = selectedShipSchedule.MD5, SHIP_RATE_TOTAL_AMOUNT = selectedShipSchedule.totalAmount.HasValue ? selectedShipSchedule.totalAmount.Value : 0, SHIP_RATE_TOTAL_CURRENCY = selectedShipSchedule.totalCurrency, SHIP_JSON = Newtonsoft.Json.JsonConvert.SerializeObject(selectedShipSchedule), CreatedTime = nowDate, UpdatedTime = nowDate, CreatedUserId = UserManager.UserId, CreatedUserName = UserManager.Name }; await _bookingDeliveryRecordShipScheduleRep.InsertAsync(shipScheduleRecord); } } return model.id.Value; } else { BookingDeliveryRecord entity = model.Adapt(); entity.CreatedTime = nowDate; entity.UpdatedTime = nowDate; entity.CreatedUserId = UserManager.UserId; entity.CreatedUserName = UserManager.Name; entity.STATUS = "TEMP"; entity.STATUS_NAME = "暂存"; entity.CTN_STAT = ctnStat; entity.BOOKING_CHANNEL_TYPE = "SPOT_API"; entity.BOOKING_CHANNEL_TYPE_NAME = "SPOT订舱"; await _bookingDeliveryRecordRep.InsertAsync(entity); if (model.ctns != null && model.ctns.Count > 0) { model.ctns.ForEach(ctn => { var ctnEntity = new BookingDeliveryRecordCtn { CTN_CODE = ctn.ctnCode, CTN_NAME = ctn.ctnName, CTN_NUM = ctn.ctnNum.Value, CTN_SUFFER_WEIGHT = (int)ctn.ctnSufferWeight.Value, }; ctnEntity.RECORD_ID = entity.Id; ctnEntity.CreatedTime = nowDate; ctnEntity.UpdatedTime = nowDate; ctnEntity.CreatedUserId = UserManager.UserId; ctnEntity.CreatedUserName = UserManager.Name; ctnEntity.STUFFING_MEASUREMENT_TYPE = "WEIGHT"; ctnEntity.STUFFING_MEASUREMENT_UNIT = "KGS"; _bookingDeliveryRecordCtnRep.Insert(ctnEntity); }); } if (shipScheduleRecord == null) { if (model.PId > 0 && selectedShipSchedule != null) { shipScheduleRecord = new BookingDeliveryRecordShipSchedule { Id = YitIdHelper.NextId(), RECORD_ID = model.id.Value, PRICE_ID = model.priceID, SHIP_RATE_PID = selectedShipSchedule.PId, SHIP_RATE_MD5 = selectedShipSchedule.MD5, SHIP_RATE_TOTAL_AMOUNT = selectedShipSchedule.totalAmount.HasValue ? selectedShipSchedule.totalAmount.Value : 0, SHIP_RATE_TOTAL_CURRENCY = selectedShipSchedule.totalCurrency, SHIP_JSON = Newtonsoft.Json.JsonConvert.SerializeObject(selectedShipSchedule), CreatedTime = nowDate, UpdatedTime = nowDate, CreatedUserId = UserManager.UserId, CreatedUserName = UserManager.Name }; await _bookingDeliveryRecordShipScheduleRep.InsertAsync(shipScheduleRecord); } } else { //只有船期发生变化时才重新写入新的船期记录 if (model.PId != shipScheduleRecord.SHIP_RATE_PID.Value) { _logger.LogInformation($"MSK SPOT id={model.id} 换了船期 原记录:{selectedShipSchedule.PId} 新记录:{model.PId} 作废原船期,插入新船期"); shipScheduleRecord.IsDeleted = true; shipScheduleRecord.UpdatedTime = nowDate; shipScheduleRecord.UpdatedUserId = UserManager.UserId; shipScheduleRecord.UpdatedUserName = UserManager.Name; await _bookingDeliveryRecordShipScheduleRep.AsUpdateable(shipScheduleRecord).UpdateColumns(x=>new { x.IsDeleted, x.UpdatedUserId, x.UpdatedUserName, x.UpdatedTime }).ExecuteCommandAsync(); shipScheduleRecord = new BookingDeliveryRecordShipSchedule { Id = YitIdHelper.NextId(), RECORD_ID = model.id.Value, PRICE_ID = model.priceID, SHIP_RATE_PID = selectedShipSchedule.PId, SHIP_RATE_MD5 = selectedShipSchedule.MD5, SHIP_RATE_TOTAL_AMOUNT = selectedShipSchedule.totalAmount.HasValue ? selectedShipSchedule.totalAmount.Value : 0, SHIP_RATE_TOTAL_CURRENCY = selectedShipSchedule.totalCurrency, SHIP_JSON = Newtonsoft.Json.JsonConvert.SerializeObject(selectedShipSchedule), CreatedTime = nowDate, UpdatedTime = nowDate, CreatedUserId = UserManager.UserId, CreatedUserName = UserManager.Name }; await _bookingDeliveryRecordShipScheduleRep.InsertAsync(shipScheduleRecord); } else { _logger.LogInformation($"MSK SPOT id={model.id} 船期没变化 model.PId={model.PId} shipScheduleRecord.SHIP_RATE_PID={shipScheduleRecord.SHIP_RATE_PID.Value}"); } } return entity.Id; } } #endregion #region 删除 /// /// 删除 /// /// 请求订舱ID /// [HttpGet("/BookingMSKSPOTAPI/Delete")] public async Task Delete(long id) { var info = _bookingDeliveryRecordRep.AsQueryable().First(a => a.Id == id); if (info == null) throw Oops.Bah($"删除失败,业务信息不存在或已作废"); info.IsDeleted = true; info.UpdatedTime = DateTime.Now; info.UpdatedUserId = UserManager.UserId; info.UpdatedUserName = UserManager.Name; await _bookingDeliveryRecordRep.AsUpdateable(info).UpdateColumns(x => new { x.IsDeleted, x.UpdatedTime, x.UpdatedUserId, x.UpdatedUserName }).ExecuteCommandAsync(); } #endregion #region 批量发送 /// /// 批量发送 /// /// 马士基API订舱ID组 /// 返回执行结果消息 [HttpPost("/BookingMSKSPOTAPI/BatchSend")] public async Task BatchSend([FromBody] long[] ids) { var list = await _bookingDeliveryRecordRep.AsQueryable() .Where(a => ids.Contains(a.Id)).ToListAsync(); if (list.Count != ids.Length) throw Oops.Oh("订舱数据获取失败,请确认选中的记录是否存在"); List msgList = new List(); int totalNum = list.Count; int succNum = 0; int failNum = 0; if (list.Count > 1) { list.ForEach(entity => { MSKBookingResultDto result = null; try { var model = GetInfo(entity.Id).GetAwaiter().GetResult(); result = InnerSendMSKBooking(model, entity.Id, false).GetAwaiter().GetResult(); succNum++; } catch (Exception ex) { msgList.Add(ex.Message); failNum++; } }); msgList.Add($"共计{totalNum} 条 成功{succNum}条 失败:{failNum}"); } else { MSKBookingResultDto result = null; try { var entity = list.FirstOrDefault(); var model = GetInfo(entity.Id).GetAwaiter().GetResult(); result = InnerSendMSKBooking(model, entity.Id, false).GetAwaiter().GetResult(); if (result.succ) return string.Empty; } catch (Exception ex) { msgList.Add(ex.Message); } } return string.Join("#", msgList.ToArray()); } #endregion #region 批量复制 /// /// 批量复制 /// /// 马士基API批量复制指定数据 /// 返回执行结果消息 [HttpPost("/BookingMSKSPOTAPI/BatchCopy")] public async Task BatchCopy(MSKAPISPOTBookingCopyDto model) { if (model.copyNum < 1) throw Oops.Oh($"复制数量不能小于1"); if (model.copyNum > 1000) throw Oops.Oh($"复制数量不能大于1000"); if (string.IsNullOrWhiteSpace(model.opType)) throw Oops.Oh($"请求的操作类型不能为空"); var entity = await _bookingDeliveryRecordRep.AsQueryable() .FirstAsync(a => a.Id == model.originalId); if (entity == null) throw Oops.Oh($"获取马士基API订舱详情失败,不存在或已作废"); var ctnList = _bookingDeliveryRecordCtnRep.AsQueryable() .Where(a => a.RECORD_ID == model.originalId && a.IsDeleted == false).ToList(); var numArg = "".PadLeft(model.copyNum, '0').Select((a, idx) => idx + 1).ToList(); DateTime nowDate = DateTime.Now; _logger.LogInformation("获取到批量复制功能"); //如果是指定的编辑信息需要先把编辑数据同步到实体类,再进行复制 if (model.opType == "copy_edit") { string ctnStat = string.Empty; if (model.bookingDto.ctns != null && model.bookingDto.ctns.Count > 0) { ctnStat = string.Join(",", model.bookingDto.ctns.GroupBy(a => a.ctnName) .Select(a => $"{a.Key}*{a.ToList().Sum(b => b.ctnNum.HasValue ? b.ctnNum.Value : 0)}").ToArray()); ctnList = new List(); model.bookingDto.ctns.ForEach(ctn => { var ctnEntity = new BookingDeliveryRecordCtn { CTN_CODE = ctn.ctnCode, CTN_NAME = ctn.ctnName, CTN_NUM = ctn.ctnNum.Value, CTN_SUFFER_WEIGHT = (int)ctn.ctnSufferWeight.Value, }; ctnEntity.CreatedTime = nowDate; ctnEntity.UpdatedTime = nowDate; ctnEntity.CreatedUserId = UserManager.UserId; ctnEntity.CreatedUserName = UserManager.Name; ctnEntity.STUFFING_MEASUREMENT_TYPE = "WEIGHT"; ctnEntity.STUFFING_MEASUREMENT_UNIT = "KGS"; ctnList.Add(ctnEntity); }); } entity = model.bookingDto.Adapt(); entity.CreatedTime = nowDate; entity.UpdatedTime = nowDate; entity.CreatedUserId = UserManager.UserId; entity.CreatedUserName = UserManager.Name; entity.STATUS = "TEMP"; entity.STATUS_NAME = "暂存"; entity.CTN_STAT = ctnStat; } int totalNum = 0; foreach (var a in numArg) { var copyEntity = entity.Adapt(); copyEntity.Id = 0; copyEntity.CreatedTime = nowDate; copyEntity.CreatedUserId = UserManager.UserId; copyEntity.CreatedUserName = UserManager.Name; copyEntity.UpdatedTime = nowDate; copyEntity.STATUS = "TEMP"; copyEntity.STATUS_NAME = "暂存"; copyEntity.IS_RECV_BC = false; copyEntity.LST_RECV_BC_DATE = null; copyEntity.IS_RECV_BK_CANCEL = false; copyEntity.LST_RECV_BK_CANCEL_DATE = null; copyEntity.REQUEST_ACKNOWLEDGEMENT_ID = null; copyEntity.BOOKING_REFERENCE = null; copyEntity.NOTES = $"COPY NO.{a} BY={model.originalId}"; await _bookingDeliveryRecordRep.InsertAsync(copyEntity); totalNum++; if (ctnList.Count > 0) { ctnList.ForEach(async ctn => { var ctnEntity = ctn.Adapt(); ctnEntity.Id = 0; ctnEntity.RECORD_ID = copyEntity.Id; ctnEntity.CreatedTime = nowDate; ctnEntity.UpdatedTime = nowDate; ctnEntity.CreatedUserId = UserManager.UserId; ctnEntity.CreatedUserName = UserManager.Name; await _bookingDeliveryRecordCtnRep.InsertAsync(ctnEntity); }); } } return JSON.Serialize(new { Total = totalNum }); } #endregion #region 同步BC状态(BC,Cancellation) /// /// 同步BC状态(BC,Cancellation) /// /// 提单号 /// 租户ID /// 操作类型 BC-同步BC状态 Cancellation-同步取消状态 /// 返回回执 [HttpGet("/BookingMSKSPOTAPI/SyncBCInfo")] public async Task SyncBCInfo(string mblno, long tenantId, string opTypeName = "BookingConfirmation") { MSKBookingResultDto result = new MSKBookingResultDto(); try { var model = await _bookingDeliveryRecordRep.AsQueryable().Filter(null, true) .FirstAsync(a => a.BOOKING_REFERENCE == mblno && a.IsDeleted == false && a.TenantId == tenantId); if (model == null) throw Oops.Oh("订舱数据不存在或已作废"); if (opTypeName == "BookingConfirmation") { model.IS_RECV_BC = true; model.LST_RECV_BC_DATE = DateTime.Now; model.UpdatedTime = model.LST_RECV_BC_DATE; model.UpdatedUserId = UserManager.UserId; model.UpdatedUserName = UserManager.Name; await _bookingDeliveryRecordRep.AsUpdateable(model).UpdateColumns(x => new { x.IS_RECV_BC, x.LST_RECV_BC_DATE, x.UpdatedTime, x.UpdatedUserId, x.UpdatedUserName }).ExecuteCommandAsync(); } else if (opTypeName == "Cancellation") { model.IS_RECV_BK_CANCEL = true; model.LST_RECV_BK_CANCEL_DATE = DateTime.Now; model.UpdatedTime = model.LST_RECV_BC_DATE; model.UpdatedUserId = UserManager.UserId; model.UpdatedUserName = UserManager.Name; await _bookingDeliveryRecordRep.AsUpdateable(model).UpdateColumns(x => new { x.IS_RECV_BK_CANCEL, x.LST_RECV_BK_CANCEL_DATE, x.UpdatedTime, x.UpdatedUserId, x.UpdatedUserName }).ExecuteCommandAsync(); } result.succ = true; } catch (Exception ex) { result.succ = false; result.msg = $"同步BC状态异常,原因:{ex.Message}"; } return result; } #endregion #region 批量复制前调取校验预警 /// /// 批量复制前调取校验预警 /// /// 马士基API批量复制指定数据 /// 返回提示信息 [HttpPost("/BookingMSKSPOTAPI/CheckWarningBatchCopy")] public async Task CheckWarningBatchCopy(MSKAPISPOTBookingCopyDto model) { /* 主要校验内容 1、如果待复制的单子里还有“托运人自己的集装箱”或“进口退货集装箱或者其他三角集运”需要做提醒是否继续复制。 2、如果是opType=copy_edit-复制使用提交的编辑信息,需要判断原始货物标志变化是否继续。 3、如果是冷冻处理的提示是否继续。 */ var entity = await _bookingDeliveryRecordRep.AsQueryable() .FirstAsync(a => a.Id == model.originalId); if (entity != null) { List msgList = new List(); if (entity.IS_SHIPPER_OWNED || (model.bookingDto != null && model.bookingDto.isShipperOwned)) { msgList.Add("含有指定托运人自己的集装箱"); } if (entity.IS_IMPORT_RETURNED || (model.bookingDto != null && model.bookingDto.isImportReturned)) { msgList.Add("含有指定进口退货集装箱或者其他三角集运"); } if (entity.IS_REEFER || (model.bookingDto != null && model.bookingDto.isReefer)) { msgList.Add("含有是冷冻处理"); } if (model.bookingDto != null && model.bookingDto.cargoType != entity.CARGO_TYPE) { msgList.Add("货物标志与被复制货物标志不一致"); } if (msgList.Count > 0) throw Oops.Oh(string.Join(",", msgList.ToArray())); } else { return string.Empty; } return "校验成功"; } #endregion #region 批量发送API前调取校验预警 /// /// 批量发送API前调取校验预警 /// /// 马士基API订舱ID组 /// 返回提示信息 [HttpPost("/BookingMSKSPOTAPI/CheckWarningBatchSend")] public async Task CheckWarningBatchSend([FromBody] long[] ids) { var list = await _bookingDeliveryRecordRep.AsQueryable().Where(a => ids.Contains(a.Id)).ToListAsync(); if (list.Count > 0) { List msgList = new List(); if (list.Any(a => a.IS_SHIPPER_OWNED)) { msgList.Add("含有指定托运人自己的集装箱"); } if (list.Any(a => a.IS_IMPORT_RETURNED)) { msgList.Add("含有指定进口退货集装箱或者其他三角集运"); } if (list.Any(a => a.IS_REEFER)) { msgList.Add("含有是冷冻处理"); } if (msgList.Count > 0) throw Oops.Oh(string.Join("#", msgList.ToArray())); } return "校验成功"; } #endregion #region 获取初始化页面默认值 /// /// 获取初始化页面默认值 /// /// 返回详情 [HttpGet("/BookingMSKSPOTAPI/GetInitInfo")] public async Task GetInitInfo() { MSKSPOTBookingInitDto dto = null; var webAccountConfig = await _webAccountConfig.GetAccountConfigByTenantId("MSKSpotApi", UserManager.UserId, UserManager.TENANT_ID); if (webAccountConfig == null) throw Oops.Oh("未配置公司账户维护-MSK即期,请联系管理员"); if (string.IsNullOrWhiteSpace(webAccountConfig.RegistPartyCode) || string.IsNullOrWhiteSpace(webAccountConfig.RegistPartyName) || string.IsNullOrWhiteSpace(webAccountConfig.RegistContractName) || string.IsNullOrWhiteSpace(webAccountConfig.RegistContractEmail)) { throw Oops.Oh("账户维护-MSK即期,未配置完整注册信息(备案代码、备案全称、联系人、邮箱必填),请联系管理员"); } dto = new MSKSPOTBookingInitDto { bookedMaerskByPartyCode = webAccountConfig.RegistPartyCode, bookedByCompanyName = webAccountConfig.RegistPartyName, priceOwnerMaerskPartyCode = webAccountConfig.RegistPartyCode, priceOwnerCompanyName = webAccountConfig.RegistPartyName, bookedByPartyContactName = webAccountConfig.RegistContractName, bookedByPartyContactEmail = webAccountConfig.RegistContractEmail, priceOwnerContactName = webAccountConfig.RegistContractName, priceOwnerContactEmail = webAccountConfig.RegistContractEmail, }; return dto; } #endregion } }