diff --git a/Myshipping.Application/Service/BookingOrder/BookingValueAddedService.cs b/Myshipping.Application/Service/BookingOrder/BookingValueAddedService.cs index 75e6b575..c23c396b 100644 --- a/Myshipping.Application/Service/BookingOrder/BookingValueAddedService.cs +++ b/Myshipping.Application/Service/BookingOrder/BookingValueAddedService.cs @@ -1,15 +1,29 @@ -using Furion.DependencyInjection; +using Furion; +using Furion.DependencyInjection; +using Furion.DistributedIDGenerator; using Furion.DynamicApiController; +using Furion.FriendlyException; +using Furion.JsonSerialization; +using Furion.RemoteRequest.Extensions; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Myshipping.Application.ConfigOption; using Myshipping.Application.Entity; using Myshipping.Core; using Myshipping.Core.Service; +using Org.BouncyCastle.Crypto; using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Net.Http; +using System.Net.NetworkInformation; +using System.Runtime.InteropServices; using System.Text; +using System.Text.Json; using System.Threading.Tasks; +using Yitter.IdGenerator; namespace Myshipping.Application { @@ -20,16 +34,28 @@ namespace Myshipping.Application public class BookingValueAddedService : IBookingValueAddedService, IDynamicApiController, ITransient { private readonly ISysCacheService _cache; + private readonly IDjyWebsiteAccountConfigService _webAccountConfig; private readonly ILogger _logger; private readonly SqlSugarRepository _bookingOrderRepository; + private readonly SqlSugarRepository _bookingfile; - public BookingValueAddedService(ISysCacheService cache, ILogger logger, - SqlSugarRepository bookingOrderRepository) + const string CONST_MAPPING_CARRIER_MODULE_ROUTE = "BC_OR_DRAFT_RT"; + const string CONST_FORMAT_BC_URL = "{0}_bc_down_url"; + const string CONST_FORMAT_DRAFT_URL = "{0}_draft_down_url"; + + const string CONST_TSL_BC_WEB = "TslWeb"; + + public BookingValueAddedService(ISysCacheService cache, ILogger logger, + SqlSugarRepository bookingOrderRepository, SqlSugarRepository bookingfile, + IDjyWebsiteAccountConfigService webAccountConfig) { _cache = cache; _logger = logger; _bookingOrderRepository = bookingOrderRepository; + _bookingfile = bookingfile; + + _webAccountConfig = webAccountConfig; } @@ -38,13 +64,37 @@ namespace Myshipping.Application /// /// 订舱主键数组 /// - public async Task DownloadBookingConfirm(long[] bookingIds) + [HttpPost("/BookingValueAdded/DownloadBookingConfirm")] + public async Task DownloadBookingConfirm([FromBody]long[] bookingIds) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); + List> taskList = new List>(); + + string batchNo = IDGen.NextID().ToString(); try { - //var id = await InnerSave(info); + /* + 1、订舱主键提取提单号、订舱编号,优先去提单号,其次订舱编号 + 2、根据不同的船公司调取单独的配置账户。 + 3、不同船公司对应不同的接口。 + 4、轮询异步调取接口等待返回接口。 + 5、成功后将文件链接存入附件表。 + 6、等待所有请求完成返回统计结果。 + */ + + var list = _bookingOrderRepository.AsQueryable() + .Where(a => bookingIds.Contains(a.Id)).ToList(); + + if (list.Count != bookingIds.Length) + throw Oops.Oh($"订舱信息获取失败,订舱信息不存在或已作废"); + + foreach (var bk in list) + { + taskList.Add(InnerDownloadBookingConfirm(bk, batchNo)); + } + + Task.WaitAll(taskList.ToArray()); result.succ = true; result.msg = "批量下载BC成功"; @@ -59,6 +109,136 @@ namespace Myshipping.Application return result; } + private async Task InnerDownloadBookingConfirm(BookingOrder bookingOrder,string batchNo) + { + TaskManageOrderResultDto result = new TaskManageOrderResultDto(); + + try + { + /* + 1、根据船公司代码匹配船公司映射。 + 2、BC和DRAFT是分别2个请求地址。 + 3、生成请求报文。 + 4、请求相应的链接。 + 5、返回成功写入附件。 + */ + var bcOrDraftRouteCfg = _cache.GetAllMappingCarrier().GetAwaiter().GetResult() + .FirstOrDefault(t => t.Module.Equals(CONST_MAPPING_CARRIER_MODULE_ROUTE, StringComparison.OrdinalIgnoreCase) + && t.Code.Equals(bookingOrder.CARRIERID?.Trim(), StringComparison.OrdinalIgnoreCase)); + + _logger.LogInformation("提单号【{mbl}】根据订舱的船公司代码{ca} 提取船公司映射完成,结果={rlt}", + bookingOrder.MBLNO, bookingOrder.CARRIERID, bcOrDraftRouteCfg); + + if (bcOrDraftRouteCfg == null) + { + _logger.LogInformation("提单号【{mbl}】根据订舱的船公司代码{ca} 提取船公司映射失败", + bookingOrder.MBLNO, bookingOrder.CARRIERID); + + throw Oops.Bah("提单号={mbl} 船公司={ca} 未配置BC和DRAFT下载路由请联系官员配置", bookingOrder.MBLNO, bookingOrder.CARRIERID); + } + + string urlKey = string.Format(CONST_FORMAT_BC_URL, bcOrDraftRouteCfg.MapCode.ToLower()); + + var bcUrl = _cache.GetAllDictData().GetAwaiter().GetResult() + .FirstOrDefault(x => x.TypeCode == "url_set" && x.Code.Equals(urlKey, StringComparison.OrdinalIgnoreCase))?.Value; + + _logger.LogInformation("提单号【{mbl}】根据订舱的船公司代码{ca} 提取BC下载URL完成,结果={rlt}", + bookingOrder.MBLNO, bookingOrder.CARRIERID, bcUrl); + + if (string.IsNullOrWhiteSpace(bcUrl)) + { + _logger.LogInformation("提单号【{mbl}】根据订舱的船公司代码{ca} 提取BC下载URL失败,未取到配置key={key}", + bookingOrder.MBLNO, bookingOrder.CARRIERID, urlKey); + } + + //获取个人对应的账户,这里GetAccountConfig逻辑优先取个人,个人没有配置取公司对应配置 + var userWebAccountConfig = _webAccountConfig.GetAccountConfig(CONST_TSL_BC_WEB, UserManager.UserId).GetAwaiter() + .GetResult(); + + _logger.LogInformation("批次={no} 获取获取网站的账户完成,result={Num}", batchNo, JSON.Serialize(userWebAccountConfig)); + + if (userWebAccountConfig == null) + throw Oops.Bah($"个人/公司网站【{CONST_TSL_BC_WEB}】获取失败,请维护个人/公司网站账户信息"); + + BCOrDraftRequestDto requestDto = new BCOrDraftRequestDto + { + user_key = App.Configuration["BCOrDraftUserKey"], + user_secret = App.Configuration["BCOrDraftUserSecret"], + web_user = userWebAccountConfig.Account?.Trim(), + web_psw = userWebAccountConfig.Password?.Trim(), + bno = bookingOrder.MBLNO, + is_parse = false + }; + + //开始请求BC + + var rlt = await ExcuteBCDownload(bcUrl, requestDto, batchNo); + + if (rlt.code == 200) + { + string currFilePath = rlt.data.path; + + string fileTypeCode = "bc"; + string fileTypeName = "Booking Confirmation"; + + //读取文件配置 + var fileCfg = App.GetOptions(); + + string relativePath = $"{fileCfg.relativePath}\\bcfiles\\{bookingOrder.Id}"; + string filePath = $"{(!string.IsNullOrWhiteSpace(fileCfg.basePath) ? fileCfg.basePath : App.WebHostEnvironment.WebRootPath)}\\{relativePath}"; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + relativePath = relativePath.Replace("\\", "/"); + filePath = filePath.Replace("\\", "/"); + } + + _logger.LogInformation("批次={no} 生成文件保存路径完成 路由={filePath} 服务器系统={system}", batchNo, filePath, RuntimeInformation.OSDescription); + //预先创建目录 + if (!Directory.Exists(filePath)) + { + Directory.CreateDirectory(filePath); + } + + var bcStream = await currFilePath.GetAsStreamAsync(); + + //这里先写入附件表 + await SaveEDIFile(bookingOrder.Id, currFilePath, new System.IO.FileInfo(currFilePath).Name, + fileTypeCode, fileTypeName); + + } + + result.succ = true; + result.msg = "下载BC成功"; + } + catch (Exception ex) + { + result.succ = false; + result.msg = $"下载BC失败,原因:{ex.Message}"; + } + + return result; + } + + [NonAction] + private async Task SaveEDIFile(long boookId, string FilePath, string fileName, string fileTypeCode = "edi", string fileTypeName = "EDI文件") + { + /* + 直接将附件信息写入附件表 + */ + //EDI文件 + var bookFile = new BookingFile + { + Id = YitIdHelper.NextId(), + FileName = fileName, + FilePath = FilePath, + TypeCode = fileTypeCode, + TypeName = fileTypeName, + BookingId = boookId, + }; + + await _bookingfile.InsertAsync(bookFile); + } /// /// 批量下载Draft @@ -71,7 +251,14 @@ namespace Myshipping.Application try { - //var id = await InnerSave(info); + /* + 1、订舱主键提取提单号、订舱编号,优先去提单号,其次订舱编号 + 2、根据不同的船公司调取单独的配置账户。 + 3、不同船公司对应不同的接口。 + 4、轮询异步调取接口等待返回接口。 + 5、成功后将文件链接存入附件表。 + 6、等待所有请求完成返回统计结果。 + */ result.succ = true; result.msg = "批量下载Draft成功"; @@ -85,5 +272,78 @@ namespace Myshipping.Application return result; } + + + #region 请求规则平台 + /// + /// 请求规则平台 + /// + /// + /// + [NonAction] + private async Task> ExcuteBCDownload(string url, BCOrDraftRequestDto info, string batchNo) + { + BCAPIBaseResult model = null; + /* + 1、填充请求的类,并生成JSON报文 + 2、POST请求接口,并记录回执。 + 3、返回信息。 + */ + try + { + _logger.LogInformation("批次={no} 对应请求报文 request={res}", batchNo, JSON.Serialize(info)); + + var res = await url.SetHttpMethod(HttpMethod.Post) + .SetBody(JSON.Serialize(info), "application/json") + .SetContentEncoding(Encoding.UTF8) + .PostAsync(); + + _logger.LogInformation("批次={no} 对应请求报文完成 res={res}", batchNo, JSON.Serialize(res)); + + if (res.StatusCode == System.Net.HttpStatusCode.OK) + { + var userResult = await res.Content.ReadAsStringAsync(); + + System.Text.Json.JsonSerializerOptions jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new DateTimeJsonConverter("yyyy-MM-dd HH:mm:ss")); + + model = JSON.Deserialize>(userResult, jsonOptions); + } + } + catch (Exception ex) + { + //写日志 + if (ex is HttpRequestException) + throw Oops.Oh(10000002); + } + + return model; + } + #endregion + } + + public class DateTimeJsonConverter : System.Text.Json.Serialization.JsonConverter + { + private readonly string _dateFormatString; + public DateTimeJsonConverter() + { + _dateFormatString = "yyyy-MM-dd HH:mm:ss"; + } + + public DateTimeJsonConverter(string dateFormatString) + { + _dateFormatString = dateFormatString; + } + + public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return DateTime.Parse(reader.GetString()); + } + + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToUniversalTime().ToString(_dateFormatString)); + } } } + diff --git a/Myshipping.Application/Service/BookingOrder/Dto/BCAPIBaseResult.cs b/Myshipping.Application/Service/BookingOrder/Dto/BCAPIBaseResult.cs new file mode 100644 index 00000000..5e46e194 --- /dev/null +++ b/Myshipping.Application/Service/BookingOrder/Dto/BCAPIBaseResult.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Myshipping.Application +{ + public class BCAPIBaseResult + { + /// + /// 回执代码 + /// + public int code { get; set; } + + /// + /// 提示信息 + /// + public string msg { get; set; } + + /// + /// 数据集 + /// + public BCAPIBaseData data { get; set; } + } + + public class BCAPIBaseData + { + /// + /// 文件下载路径 + /// + public string path { get; set; } + + /// + /// 解析详情 + /// + public T parse { get; set; } + } + + public class BCAPIBaseDataParse + { + /// + /// 解析回执代码 + /// + public int status { get; set; } + + /// + /// 解析提示信息 + /// + public string message { get; set; } + + /// + /// 解析提示信息 + /// + public BCAPIBaseDataParseModel data { get; set; } + } + + public class BCAPIBaseDataParseModel + { + /// + /// 模板ID + /// + public string ModelID { get; set; } + + /// + /// 模板名称 + /// + public string ModelName { get; set; } + + /// + /// BC解析明细 + /// + public List BCList { get; set; } + } + + +} diff --git a/Myshipping.Application/Service/BookingOrder/Dto/BCOrDraftRequestDto.cs b/Myshipping.Application/Service/BookingOrder/Dto/BCOrDraftRequestDto.cs new file mode 100644 index 00000000..05f7efc6 --- /dev/null +++ b/Myshipping.Application/Service/BookingOrder/Dto/BCOrDraftRequestDto.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Myshipping.Application +{ + /// + /// BC或DRAFT下载请求 + /// + public class BCOrDraftRequestDto + { + /// + /// 用户key + /// + public string user_key { get; set; } + /// + /// 用户secret + /// + public string user_secret { get; set; } + /// + /// 网站账号 + /// + public string web_user { get; set; } + /// + /// 网站密码 + /// + public string web_psw { get; set; } + /// + /// 单号(订舱编号) + /// + public string bno { get; set; } + /// + /// 是否解析,默认否 + /// + public bool is_parse { get; set; } = false; + } +} diff --git a/Myshipping.Application/Service/BookingOrder/Dto/BCReadModel.cs b/Myshipping.Application/Service/BookingOrder/Dto/BCReadModel.cs new file mode 100644 index 00000000..2e521999 --- /dev/null +++ b/Myshipping.Application/Service/BookingOrder/Dto/BCReadModel.cs @@ -0,0 +1,265 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Myshipping.Application +{ + /// + /// BC解析主信息 + /// + public class BCReadModel + { + /// + /// 订舱单位 + /// + public string BookingParty { get; set; } + + /// + /// 发货人 + /// + public string Shipper { get; set; } + + /// + /// 收货人 + /// + public string Consigner { get; set; } + + /// + /// 通知人 + /// + public string NotifyParty { get; set; } + + /// + /// BC更新次数 + /// + public Nullable BCUpdateTimes { get; set; } + + /// + /// BC更新时间 + /// + public Nullable BCUpdateTime { get; set; } + + /// + /// 提单号 + /// + public string BLNo { get; set; } + + /// + /// 船名 + /// + public string Vessel { get; set; } + + /// + /// 航次 + /// + public string Voyage { get; set; } + + /// + /// 船公司 + /// + public string ShippingCompany { get; set; } + + /// + /// 收货地 + /// + public string PlaceOfReceipt { get; set; } + + /// + /// 发货人 + /// + public string LoadingPort { get; set; } + + /// + /// 截关时间 + /// + public Nullable ClosingDate { get; set; } + + /// + /// 截VGM时间 + /// + public Nullable VGMCutOffTime { get; set; } + + /// + /// ETA + /// + public Nullable ETA { get; set; } + + /// + /// ETD + /// + public Nullable ETD { get; set; } + + /// + /// 目的港ETA + /// + public Nullable PortOfDestinationETA { get; set; } + + /// + /// 预付地点 + /// + public Nullable CutSingleTime { get; set; } + + /// + /// 截港时间 + /// + public Nullable CYCutOffTime { get; set; } + + /// + /// 卸货港 + /// + public string DischargingPort { get; set; } + + /// + /// 交货地 + /// + public string DeliveryPlace { get; set; } + + /// + /// 装运方式 + /// + public string ShippingWay { get; set; } + + /// + /// 运输条款 + /// + public string ShippingTerms { get; set; } + + /// + /// 港前运输形态 + /// + public string PreportTransportationMode { get; set; } + + /// + /// 品名 + /// + public string OfTheGoods { get; set; } + + /// + /// 签单地点 + /// + public string SignTheBillLocation { get; set; } + + /// + /// 集港码头 + /// + public string CollectionTerminal { get; set; } + + /// + /// 约号 + /// + public string AboutNo { get; set; } + + /// + /// 预付地点 + /// + public string PlaceInAdvance { get; set; } + + /// + /// 船代 + /// + public string ShipAgency { get; set; } + + /// + /// 场站 + /// + public string Station { get; set; } + + /// + /// 场站联系人 + /// + public string StationContact { get; set; } + + /// + /// 场站联系电话 + /// + public string StationContactNumber { get; set; } + + /// + /// 一代客服姓名 + /// + public string FirstCustomerServiceName { get; set; } + + /// + /// 一代客服电话 + /// + public string FirstCustomerServiceNumber { get; set; } + + /// + /// 一代客服邮箱 + /// + public string FirstCustomerServiceEmail { get; set; } + + /// + /// 备注1 + /// + public string Remark { get; set; } + + /// + /// 集装箱列表 + /// + public List Containers { get; set; } + } + + /// + /// 集装箱详情 + /// + public class BCReadContaModel + { + /// + /// 箱量 + /// + public Nullable CartonQuantity { get; set; } + + /// + /// 箱型 + /// + public string BoxPile { get; set; } + + /// + /// 件数 + /// + public Nullable Pieces { get; set; } + + /// + /// 尺寸 + /// + public Nullable Size { get; set; } + + /// + /// 毛重 + /// + public Nullable GrossWeight { get; set; } + + /// + /// 箱皮重 + /// + public Nullable TareWeight { get; set; } + + /// + /// 危品票标示 + /// + public string IODGT { get; set; } + + /// + /// 特殊装载需求 + /// + public string SpecialLoadingRequirement { get; set; } + + /// + /// 提箱场站 + /// + public string SuitcaseTterminal { get; set; } + + /// + /// 提箱时间 + /// + public Nullable SuitcaseTime { get; set; } + + /// + /// 还箱场站 + /// + public string ReturnDepot { get; set; } + } +} diff --git a/Myshipping.Web.Core/applicationconfig.json b/Myshipping.Web.Core/applicationconfig.json index 28298932..22be0293 100644 --- a/Myshipping.Web.Core/applicationconfig.json +++ b/Myshipping.Web.Core/applicationconfig.json @@ -119,5 +119,7 @@ "Path": "TempFiles", "RemainHours": 2 }, - "ShippingOrderCompareUrl": "http://60.209.125.238:35210/api/TaskShippingOrderCompare/ExcuteShippingOrderCompare" + "ShippingOrderCompareUrl": "http://60.209.125.238:35210/api/TaskShippingOrderCompare/ExcuteShippingOrderCompare", + "BCOrDraftUserKey": "BookingOrderPlat", + "BCOrDraftUserSecret": "228b2db5952d13291f228d441018c1b6" } \ No newline at end of file