|
|
|
|
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 StackExchange.Profiling.Internal;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
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.Text.RegularExpressions;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using Yitter.IdGenerator;
|
|
|
|
|
|
|
|
|
|
namespace Myshipping.Application
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 订舱增值类服务
|
|
|
|
|
/// </summary>
|
|
|
|
|
[ApiDescriptionSettings("Application", Name = "BookingValueAdded", Order = 9)]
|
|
|
|
|
public class BookingValueAddedService : IBookingValueAddedService, IDynamicApiController, ITransient
|
|
|
|
|
{
|
|
|
|
|
private readonly ISysCacheService _cache;
|
|
|
|
|
private readonly IDjyWebsiteAccountConfigService _webAccountConfig;
|
|
|
|
|
private readonly ILogger<BookingTruckService> _logger;
|
|
|
|
|
private readonly SqlSugarRepository<BookingOrder> _bookingOrderRepository;
|
|
|
|
|
private readonly SqlSugarRepository<BookingFile> _bookingfile;
|
|
|
|
|
|
|
|
|
|
const string CONST_MAPPING_BC_MODULE_ROUTE = "BC_DOWN_RT";
|
|
|
|
|
const string CONST_MAPPING_DRAFT_MODULE_ROUTE = "DRAFT_DOWN_RT";
|
|
|
|
|
const string CONST_MAPPING_MANIALLO_CHK_MODULE_ROUTE = "MANI_ALLO_CHK_RT";
|
|
|
|
|
|
|
|
|
|
const string CONST_FORMAT_BC_URL = "{0}_bc_down_url";
|
|
|
|
|
const string CONST_FORMAT_DRAFT_URL = "{0}_draft_down_url";
|
|
|
|
|
const string CONST_FORMAT_MANIALLO_CHK_URL = "{0}_maniallo_chk_url";
|
|
|
|
|
|
|
|
|
|
const string CONST_FORMAT_WEB = "{0}_Web";
|
|
|
|
|
|
|
|
|
|
public BookingValueAddedService(ISysCacheService cache, ILogger<BookingTruckService> logger,
|
|
|
|
|
SqlSugarRepository<BookingOrder> bookingOrderRepository, SqlSugarRepository<BookingFile> bookingfile,
|
|
|
|
|
IDjyWebsiteAccountConfigService webAccountConfig)
|
|
|
|
|
{
|
|
|
|
|
_cache = cache;
|
|
|
|
|
_logger = logger;
|
|
|
|
|
|
|
|
|
|
_bookingOrderRepository = bookingOrderRepository;
|
|
|
|
|
_bookingfile = bookingfile;
|
|
|
|
|
|
|
|
|
|
_webAccountConfig = webAccountConfig;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 批量BC下载
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="bookingIds">订舱主键数组</param>
|
|
|
|
|
/// <returns>返回回执</returns>
|
|
|
|
|
[HttpPost("/BookingValueAdded/DownloadBookingConfirm")]
|
|
|
|
|
public async Task<TaskManageOrderResultDto> DownloadBookingConfirm([FromBody]long[] bookingIds)
|
|
|
|
|
{
|
|
|
|
|
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
|
|
|
|
|
|
|
|
|
|
List<Task<TaskManageOrderResultDto>> taskList = new List<Task<TaskManageOrderResultDto>>();
|
|
|
|
|
|
|
|
|
|
string batchNo = IDGen.NextID().ToString();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
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($"订舱信息获取失败,订舱信息不存在或已作废");
|
|
|
|
|
|
|
|
|
|
var noList = bookingIds.Select((a, idx) => new { no = idx + 1, id = a }).ToList();
|
|
|
|
|
|
|
|
|
|
foreach (var bk in list)
|
|
|
|
|
{
|
|
|
|
|
var sortNo = noList.FirstOrDefault(a=>a.id == bk.Id).no;
|
|
|
|
|
taskList.Add(Task.Run(() => InnerDownloadBookingConfirm(bk, batchNo, sortNo)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Task.WaitAll(taskList.ToArray());
|
|
|
|
|
|
|
|
|
|
result.succ = true;
|
|
|
|
|
result.msg = "批量下载BC成功";
|
|
|
|
|
|
|
|
|
|
var downResultList = taskList.Select(x => x.Result).ToList();
|
|
|
|
|
|
|
|
|
|
if (downResultList.Any(x => !x.succ))
|
|
|
|
|
{
|
|
|
|
|
result.succ = false;
|
|
|
|
|
result.msg = "BC下载失败";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
result.succ = true;
|
|
|
|
|
result.msg = downResultList.FirstOrDefault().msg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.ext = downResultList;
|
|
|
|
|
|
|
|
|
|
var succ = downResultList.Count(x => x.succ);
|
|
|
|
|
var fail = downResultList.Count(x => !x.succ);
|
|
|
|
|
|
|
|
|
|
if (succ > 0)
|
|
|
|
|
result.batchTotal = succ.ToString();
|
|
|
|
|
|
|
|
|
|
if(fail > 0)
|
|
|
|
|
{
|
|
|
|
|
if (succ > 0)
|
|
|
|
|
result.batchTotal = result.batchTotal+"/"+ fail.ToString();
|
|
|
|
|
|
|
|
|
|
result.batchTotal = "/" + fail.ToString();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
result.succ = false;
|
|
|
|
|
result.msg = $"批量BC下载异常,原因:{ex.Message}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region 单票下载BC
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 单票下载BC
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="bookingOrder">订舱详情</param>
|
|
|
|
|
/// <param name="batchNo">批次号</param>
|
|
|
|
|
/// <param name="sortNo">请求顺序号</param>
|
|
|
|
|
/// <returns>返回回执</returns>
|
|
|
|
|
private async Task<TaskManageOrderResultDto> InnerDownloadBookingConfirm(BookingOrder bookingOrder,string batchNo, int sortNo)
|
|
|
|
|
{
|
|
|
|
|
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
|
|
|
|
|
|
|
|
|
|
result.bno = bookingOrder.MBLNO;
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
1、根据船公司代码匹配船公司映射。
|
|
|
|
|
2、BC和DRAFT是分别2个请求地址。
|
|
|
|
|
3、生成请求报文。
|
|
|
|
|
4、请求相应的链接。
|
|
|
|
|
5、返回成功写入附件。
|
|
|
|
|
*/
|
|
|
|
|
if (string.IsNullOrWhiteSpace(bookingOrder.MBLNO))
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(bookingOrder.CUSTNO))
|
|
|
|
|
{
|
|
|
|
|
result.bno = $"订 {bookingOrder.CUSTNO}";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
result.bno = $"NO.{sortNo}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw Oops.Bah($"主提单号不能为空");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var bcOrDraftRouteCfg = _cache.GetAllMappingCarrier().GetAwaiter().GetResult()
|
|
|
|
|
.FirstOrDefault(t => t.Module.Equals(CONST_MAPPING_BC_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($"船公司={bookingOrder.CARRIERID} 暂不支持BC下载");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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} 提取Draft下载URL完成,结果={rlt}",
|
|
|
|
|
bookingOrder.MBLNO, bookingOrder.CARRIERID, bcUrl);
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(bcUrl))
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("提单号{0} 根据订舱的船公司代码{1} 提取BC下载URL失败,未取到配置key={key}",
|
|
|
|
|
bookingOrder.MBLNO, bookingOrder.CARRIERID, urlKey);
|
|
|
|
|
|
|
|
|
|
throw Oops.Bah($"船公司={bookingOrder.CARRIERID} 未配置请求地址{urlKey} 请联系管理员");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string webKey = string.Format(CONST_FORMAT_WEB, bcOrDraftRouteCfg.MapCode.ToLower());
|
|
|
|
|
|
|
|
|
|
//获取个人对应的账户,这里GetAccountConfig逻辑优先取个人,个人没有配置取公司对应配置
|
|
|
|
|
var userWebAccountConfig = _webAccountConfig.GetAccountConfig(webKey, UserManager.UserId).GetAwaiter()
|
|
|
|
|
.GetResult();
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("批次={no} 获取获取网站的账户完成,result={Num}", batchNo, JSON.Serialize(userWebAccountConfig));
|
|
|
|
|
|
|
|
|
|
if (userWebAccountConfig == null)
|
|
|
|
|
throw Oops.Oh($" 未配置个人或公司网站账户,网站{webKey}");
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("批次={no} json={json} 请求BC远端下载开始", batchNo, JSON.Serialize(requestDto));
|
|
|
|
|
|
|
|
|
|
DateTime bDate = DateTime.Now;
|
|
|
|
|
//开始请求BC
|
|
|
|
|
var rlt = await ExcuteBCDownload(bcUrl, requestDto, batchNo);
|
|
|
|
|
|
|
|
|
|
DateTime eDate = DateTime.Now;
|
|
|
|
|
TimeSpan ts = eDate.Subtract(bDate);
|
|
|
|
|
var timeDiff = ts.TotalMilliseconds;
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("批次={no} result={result} 请求BC远端下载结束 耗时:{timeDiff}ms. ", batchNo,
|
|
|
|
|
JSON.Serialize(rlt),timeDiff);
|
|
|
|
|
|
|
|
|
|
if (rlt.code == 200)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("批次={no} 下载文件成功,转存本地", batchNo);
|
|
|
|
|
|
|
|
|
|
string currFilePath = rlt.data.FirstOrDefault().path;
|
|
|
|
|
|
|
|
|
|
string fileTypeCode = "bc";
|
|
|
|
|
string fileTypeName = "Booking Confirmation";
|
|
|
|
|
|
|
|
|
|
//读取文件配置
|
|
|
|
|
var fileCfg = App.GetOptions<BookingAttachOptions>();
|
|
|
|
|
|
|
|
|
|
string relativePath = $"{fileCfg.relativePath}\\bcfiles\\{bookingOrder.Id}";
|
|
|
|
|
string filePath = $"{(!string.IsNullOrWhiteSpace(fileCfg.basePath) ? fileCfg.basePath : App.WebHostEnvironment.WebRootPath)}\\{relativePath}";
|
|
|
|
|
|
|
|
|
|
string fileFullName = $"{filePath}\\{new System.IO.FileInfo(currFilePath).Name}";
|
|
|
|
|
|
|
|
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
|
|
|
|
{
|
|
|
|
|
relativePath = relativePath.Replace("\\", "/");
|
|
|
|
|
filePath = filePath.Replace("\\", "/");
|
|
|
|
|
|
|
|
|
|
fileFullName = fileFullName.Replace("\\", "/");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("批次={no} 生成文件保存路径完成 路由={filePath} 服务器系统={system}", batchNo, filePath, RuntimeInformation.OSDescription);
|
|
|
|
|
//预先创建目录
|
|
|
|
|
if (!Directory.Exists(filePath))
|
|
|
|
|
{
|
|
|
|
|
Directory.CreateDirectory(filePath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var bcStream = await currFilePath.GetAsStreamAsync();
|
|
|
|
|
|
|
|
|
|
using (var fileStream = File.Create(fileFullName))
|
|
|
|
|
{
|
|
|
|
|
await bcStream.CopyToAsync(fileStream);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("批次={no} 完成文件保存 filepath={path}", batchNo, fileFullName);
|
|
|
|
|
|
|
|
|
|
string bookFilePath = string.Empty;
|
|
|
|
|
|
|
|
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
|
|
|
|
{
|
|
|
|
|
bookFilePath = System.Text.RegularExpressions.Regex.Match(fileFullName, relativePath.Replace("/", "\\/") + ".*").Value;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
bookFilePath = System.Text.RegularExpressions.Regex.Match(fileFullName, relativePath.Replace("\\", "\\\\") + ".*").Value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//这里先写入附件表
|
|
|
|
|
await SaveEDIFile(bookingOrder.Id, bookFilePath, new System.IO.FileInfo(currFilePath).Name,
|
|
|
|
|
fileTypeCode, fileTypeName);
|
|
|
|
|
|
|
|
|
|
result.succ = true;
|
|
|
|
|
result.msg = "BC下载成功";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
result.succ = false;
|
|
|
|
|
result.msg = $"BC下载失败,{rlt.msg}";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
result.succ = false;
|
|
|
|
|
result.msg = $"BC下载失败,{ex.Message}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region 异步写入订舱附件表
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 异步写入订舱附件表
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="boookId">订舱ID</param>
|
|
|
|
|
/// <param name="FilePath">文件路径</param>
|
|
|
|
|
/// <param name="fileName">文件名</param>
|
|
|
|
|
/// <param name="fileTypeCode">附件类型代码</param>
|
|
|
|
|
/// <param name="fileTypeName">附件类型名称</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
[NonAction]
|
|
|
|
|
private async Task SaveEDIFile(long boookId, string FilePath, string fileName, string fileTypeCode = "bc", string fileTypeName = "Booking Confirmation")
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
直接将附件信息写入附件表
|
|
|
|
|
*/
|
|
|
|
|
//EDI文件
|
|
|
|
|
var bookFile = new BookingFile
|
|
|
|
|
{
|
|
|
|
|
Id = YitIdHelper.NextId(),
|
|
|
|
|
FileName = fileName,
|
|
|
|
|
FilePath = FilePath,
|
|
|
|
|
TypeCode = fileTypeCode,
|
|
|
|
|
TypeName = fileTypeName,
|
|
|
|
|
BookingId = boookId,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
await _bookingfile.InsertAsync(bookFile);
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 批量Draft下载
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="bookingIds">订舱主键数组</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
[HttpPost("/BookingValueAdded/DownloadDraft")]
|
|
|
|
|
public async Task<TaskManageOrderResultDto> DownloadDraft([FromBody]long[] bookingIds)
|
|
|
|
|
{
|
|
|
|
|
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
|
|
|
|
|
List<Task<TaskManageOrderResultDto>> taskList = new List<Task<TaskManageOrderResultDto>>();
|
|
|
|
|
|
|
|
|
|
string batchNo = IDGen.NextID().ToString();
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
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($"订舱信息获取失败,订舱信息不存在或已作废");
|
|
|
|
|
|
|
|
|
|
var noList = bookingIds.Select((a, idx) => new { no = idx + 1, id = a }).ToList();
|
|
|
|
|
foreach (var bk in list)
|
|
|
|
|
{
|
|
|
|
|
var sortNo = noList.FirstOrDefault(a=>a.id == bk.Id).no;
|
|
|
|
|
taskList.Add(Task.Run(() => InnerDownloadDraft(bk, batchNo, sortNo)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Task.WaitAll(taskList.ToArray());
|
|
|
|
|
|
|
|
|
|
result.succ = true;
|
|
|
|
|
result.msg = "下载Draft成功";
|
|
|
|
|
|
|
|
|
|
var downResultList = taskList.Select(x => x.Result).ToList();
|
|
|
|
|
|
|
|
|
|
if (downResultList.Any(x => !x.succ))
|
|
|
|
|
{
|
|
|
|
|
result.succ = false;
|
|
|
|
|
result.msg = "Draft下载失败";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
result.succ = true;
|
|
|
|
|
result.msg = downResultList.FirstOrDefault().msg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.ext = downResultList;
|
|
|
|
|
|
|
|
|
|
var succ = downResultList.Count(x => x.succ);
|
|
|
|
|
var fail = downResultList.Count(x => !x.succ);
|
|
|
|
|
|
|
|
|
|
if (succ > 0)
|
|
|
|
|
result.batchTotal = succ.ToString();
|
|
|
|
|
|
|
|
|
|
if (fail > 0)
|
|
|
|
|
{
|
|
|
|
|
if (succ > 0)
|
|
|
|
|
result.batchTotal = result.batchTotal + "/" + fail.ToString();
|
|
|
|
|
|
|
|
|
|
result.batchTotal = "/" + fail.ToString();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
result.succ = false;
|
|
|
|
|
result.msg = $"下载Draft异常,原因:{ex.Message}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region 单票下载BC
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 单票下载BC
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="bookingOrder">订舱详情</param>
|
|
|
|
|
/// <param name="batchNo">批次号</param>
|
|
|
|
|
/// <param name="sortNo">请求顺序号</param>
|
|
|
|
|
/// <returns>返回回执</returns>
|
|
|
|
|
private async Task<TaskManageOrderResultDto> InnerDownloadDraft(BookingOrder bookingOrder, string batchNo,int sortNo)
|
|
|
|
|
{
|
|
|
|
|
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
|
|
|
|
|
|
|
|
|
|
result.bno = bookingOrder.MBLNO;
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
1、根据船公司代码匹配船公司映射。
|
|
|
|
|
2、BC和DRAFT是分别2个请求地址。
|
|
|
|
|
3、生成请求报文。
|
|
|
|
|
4、请求相应的链接。
|
|
|
|
|
5、返回成功写入附件。
|
|
|
|
|
*/
|
|
|
|
|
if(bookingOrder.CARRIERID.Equals("ESL", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
if(string.IsNullOrWhiteSpace(bookingOrder.TMBLNO))
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(bookingOrder.CUSTNO))
|
|
|
|
|
{
|
|
|
|
|
result.bno = $"订 {bookingOrder.CUSTNO}";
|
|
|
|
|
}
|
|
|
|
|
else if (string.IsNullOrWhiteSpace(bookingOrder.MBLNO))
|
|
|
|
|
{
|
|
|
|
|
result.bno = $"NO.{sortNo}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw Oops.Bah($"EP号不能为空");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(bookingOrder.MBLNO))
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(bookingOrder.CUSTNO))
|
|
|
|
|
{
|
|
|
|
|
result.bno = $"订 {bookingOrder.CUSTNO}";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
result.bno = $"NO.{sortNo}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw Oops.Bah($"主提单号不能为空");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var bcOrDraftRouteCfg = _cache.GetAllMappingCarrier().GetAwaiter().GetResult()
|
|
|
|
|
.FirstOrDefault(t => t.Module.Equals(CONST_MAPPING_DRAFT_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("提单号{0} 根据订舱的船公司代码{1} 提取船公司映射失败",
|
|
|
|
|
bookingOrder.MBLNO, bookingOrder.CARRIERID);
|
|
|
|
|
|
|
|
|
|
throw Oops.Bah($"船公司={bookingOrder.CARRIERID} 暂不支持Draft下载");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string urlKey = string.Format(CONST_FORMAT_DRAFT_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} 提取DRAFT下载URL完成,结果={rlt}",
|
|
|
|
|
bookingOrder.MBLNO, bookingOrder.CARRIERID, bcUrl);
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(bcUrl))
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("提单号{0} 根据订舱的船公司代码{1} 提取BC下载URL失败,未取到配置key={key}",
|
|
|
|
|
bookingOrder.MBLNO, bookingOrder.CARRIERID, urlKey);
|
|
|
|
|
|
|
|
|
|
throw Oops.Bah($"船公司={bookingOrder.CARRIERID} 未配置请求地址{urlKey} 请联系管理员");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string webKey = string.Format(CONST_FORMAT_WEB, bcOrDraftRouteCfg.MapCode.ToLower());
|
|
|
|
|
|
|
|
|
|
//获取个人对应的账户,这里GetAccountConfig逻辑优先取个人,个人没有配置取公司对应配置
|
|
|
|
|
var userWebAccountConfig = _webAccountConfig.GetAccountConfig(webKey, UserManager.UserId).GetAwaiter()
|
|
|
|
|
.GetResult();
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("批次={no} 获取获取网站的账户完成,result={Num}", batchNo, JSON.Serialize(userWebAccountConfig));
|
|
|
|
|
|
|
|
|
|
if (userWebAccountConfig == null)
|
|
|
|
|
throw Oops.Oh($" 未配置个人或公司网站账户,网站{webKey}");
|
|
|
|
|
|
|
|
|
|
string downloadFilePathRlt = string.Empty;
|
|
|
|
|
string erroMsg = string.Empty;
|
|
|
|
|
|
|
|
|
|
if(bcOrDraftRouteCfg.MapCode.Trim().Equals("ESL", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
ESLDraftRequestDto requestDto = new ESLDraftRequestDto
|
|
|
|
|
{
|
|
|
|
|
u = userWebAccountConfig.Account?.Trim(),
|
|
|
|
|
p = userWebAccountConfig.Password?.Trim(),
|
|
|
|
|
ep_code = bookingOrder.TMBLNO?.Trim().ToUpper(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("批次={no} json={json} 请求Draft远端下载开始", batchNo, JSON.Serialize(requestDto));
|
|
|
|
|
|
|
|
|
|
DateTime bDate = DateTime.Now;
|
|
|
|
|
//开始请求BC
|
|
|
|
|
var rlt = await ExcuteESLDraftDownload(bcUrl, requestDto, batchNo);
|
|
|
|
|
|
|
|
|
|
DateTime eDate = DateTime.Now;
|
|
|
|
|
TimeSpan ts = eDate.Subtract(bDate);
|
|
|
|
|
var timeDiff = ts.TotalMilliseconds;
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("批次={no} result={result} 请求Draft远端下载结束 耗时:{timeDiff}ms. ", batchNo,
|
|
|
|
|
JSON.Serialize(rlt), timeDiff);
|
|
|
|
|
|
|
|
|
|
if (rlt.status == 1)
|
|
|
|
|
{
|
|
|
|
|
downloadFilePathRlt = rlt.data.api_path;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
erroMsg = rlt.message;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
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
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("批次={no} json={json} 请求Draft远端下载开始", batchNo, JSON.Serialize(requestDto));
|
|
|
|
|
|
|
|
|
|
DateTime bDate = DateTime.Now;
|
|
|
|
|
//开始请求BC
|
|
|
|
|
var rlt = await ExcuteDraftDownload(bcUrl, requestDto, batchNo);
|
|
|
|
|
|
|
|
|
|
DateTime eDate = DateTime.Now;
|
|
|
|
|
TimeSpan ts = eDate.Subtract(bDate);
|
|
|
|
|
var timeDiff = ts.TotalMilliseconds;
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("批次={no} result={result} 请求Draft远端下载结束 耗时:{timeDiff}ms. ", batchNo,
|
|
|
|
|
JSON.Serialize(rlt), timeDiff);
|
|
|
|
|
|
|
|
|
|
if (rlt.code == 200)
|
|
|
|
|
{
|
|
|
|
|
downloadFilePathRlt = rlt.data.FirstOrDefault().path;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
erroMsg = rlt.msg;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(downloadFilePathRlt))
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("批次={no} 下载文件成功,转存本地", batchNo);
|
|
|
|
|
|
|
|
|
|
string currFilePath = downloadFilePathRlt;
|
|
|
|
|
|
|
|
|
|
string fileTypeCode = "draft";
|
|
|
|
|
string fileTypeName = "Draft";
|
|
|
|
|
|
|
|
|
|
//读取文件配置
|
|
|
|
|
var fileCfg = App.GetOptions<BookingAttachOptions>();
|
|
|
|
|
|
|
|
|
|
string relativePath = $"{fileCfg.relativePath}\\draftfiles\\{bookingOrder.Id}";
|
|
|
|
|
string filePath = $"{(!string.IsNullOrWhiteSpace(fileCfg.basePath) ? fileCfg.basePath : App.WebHostEnvironment.WebRootPath)}\\{relativePath}";
|
|
|
|
|
|
|
|
|
|
string fileFullName = $"{filePath}\\{new System.IO.FileInfo(currFilePath).Name}";
|
|
|
|
|
|
|
|
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
|
|
|
|
{
|
|
|
|
|
relativePath = relativePath.Replace("\\", "/");
|
|
|
|
|
filePath = filePath.Replace("\\", "/");
|
|
|
|
|
|
|
|
|
|
fileFullName = fileFullName.Replace("\\", "/");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("批次={no} 生成文件保存路径完成 路由={filePath} 服务器系统={system}", batchNo, filePath, RuntimeInformation.OSDescription);
|
|
|
|
|
//预先创建目录
|
|
|
|
|
if (!Directory.Exists(filePath))
|
|
|
|
|
{
|
|
|
|
|
Directory.CreateDirectory(filePath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var bcStream = await currFilePath.GetAsStreamAsync();
|
|
|
|
|
|
|
|
|
|
using (var fileStream = File.Create(fileFullName))
|
|
|
|
|
{
|
|
|
|
|
await bcStream.CopyToAsync(fileStream);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("批次={no} 完成文件保存 filepath={path}", batchNo, fileFullName);
|
|
|
|
|
|
|
|
|
|
string bookFilePath = string.Empty;
|
|
|
|
|
|
|
|
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
|
|
|
|
{
|
|
|
|
|
bookFilePath = System.Text.RegularExpressions.Regex.Match(fileFullName, relativePath.Replace("/", "\\/") + ".*").Value;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
bookFilePath = System.Text.RegularExpressions.Regex.Match(fileFullName, relativePath.Replace("\\", "\\\\") + ".*").Value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//这里先写入附件表
|
|
|
|
|
await SaveEDIFile(bookingOrder.Id, bookFilePath, new System.IO.FileInfo(currFilePath).Name,
|
|
|
|
|
fileTypeCode, fileTypeName);
|
|
|
|
|
|
|
|
|
|
result.succ = true;
|
|
|
|
|
result.msg = "Draft下载成功";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
result.succ = false;
|
|
|
|
|
result.msg = $"Draft下载失败,原因={erroMsg}";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
result.succ = false;
|
|
|
|
|
result.msg = $"Draft下载失败,原因:{ex.Message}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region BC请求远端下载
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// BC请求远端下载
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="url">请求URL</param>
|
|
|
|
|
/// <param name="info">请求详情</param>
|
|
|
|
|
/// <param name="batchNo">批次号</param>
|
|
|
|
|
/// <returns>返回结果</returns>
|
|
|
|
|
[NonAction]
|
|
|
|
|
private async Task<BCAPIBaseResult> 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.OnClientCreating(client => {
|
|
|
|
|
// client 为 HttpClient 对象
|
|
|
|
|
client.Timeout = TimeSpan.FromMinutes(15); // 设置超时时间 15分钟
|
|
|
|
|
}).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<BCAPIBaseResult>(userResult, jsonOptions);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
//写日志
|
|
|
|
|
if (ex is HttpRequestException)
|
|
|
|
|
throw Oops.Oh(10000002);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return model;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Draft请求远端下载
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Draft请求远端下载
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="url">请求URL</param>
|
|
|
|
|
/// <param name="info">请求详情</param>
|
|
|
|
|
/// <param name="batchNo">批次号</param>
|
|
|
|
|
/// <returns>返回结果</returns>
|
|
|
|
|
[NonAction]
|
|
|
|
|
private async Task<DraftAPIBaseResult> ExcuteDraftDownload(string url, BCOrDraftRequestDto info, string batchNo)
|
|
|
|
|
{
|
|
|
|
|
DraftAPIBaseResult model = null;
|
|
|
|
|
/*
|
|
|
|
|
1、填充请求的类,并生成JSON报文
|
|
|
|
|
2、POST请求接口,并记录回执。
|
|
|
|
|
3、返回信息。
|
|
|
|
|
*/
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("批次={no} 对应请求报文 request={res}", batchNo, JSON.Serialize(info));
|
|
|
|
|
|
|
|
|
|
var res = await url.OnClientCreating(client => {
|
|
|
|
|
// client 为 HttpClient 对象
|
|
|
|
|
client.Timeout = TimeSpan.FromMinutes(15); // 设置超时时间 15分钟
|
|
|
|
|
}).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<DraftAPIBaseResult>(userResult, jsonOptions);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
//写日志
|
|
|
|
|
if (ex is HttpRequestException)
|
|
|
|
|
throw Oops.Oh(10000002);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return model;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ESL Draft请求远端下载
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// ESL Draft请求远端下载
|
|
|
|
|
/// 由于现有的ESL和TSL接口不一致,需要提供单独的POST方法
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="url">请求URL</param>
|
|
|
|
|
/// <param name="info">请求详情</param>
|
|
|
|
|
/// <param name="batchNo">批次号</param>
|
|
|
|
|
/// <returns>返回结果</returns>
|
|
|
|
|
[NonAction]
|
|
|
|
|
private async Task<ESLDraftAPIBaseResult> ExcuteESLDraftDownload(string url, ESLDraftRequestDto info, string batchNo)
|
|
|
|
|
{
|
|
|
|
|
ESLDraftAPIBaseResult model = null;
|
|
|
|
|
/*
|
|
|
|
|
1、填充请求的类,并生成JSON报文
|
|
|
|
|
2、POST请求接口,并记录回执。
|
|
|
|
|
3、返回信息。
|
|
|
|
|
*/
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("批次={no} 对应请求报文 request={res}", batchNo, JSON.Serialize(info));
|
|
|
|
|
|
|
|
|
|
var res = await url.OnClientCreating(client => {
|
|
|
|
|
// client 为 HttpClient 对象
|
|
|
|
|
client.Timeout = TimeSpan.FromMinutes(15); // 设置超时时间 15分钟
|
|
|
|
|
}).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();
|
|
|
|
|
|
|
|
|
|
model = JSON.Deserialize<ESLDraftAPIBaseResult>(userResult);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
//写日志
|
|
|
|
|
if (ex is HttpRequestException)
|
|
|
|
|
throw Oops.Oh(10000002);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return model;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ESL SO Ref.No搜索ESL号查询
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// ESL SO Ref.No搜索ESL号查询
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="url">请求URL</param>
|
|
|
|
|
/// <param name="info">请求详情</param>
|
|
|
|
|
/// <param name="batchNo">批次号</param>
|
|
|
|
|
/// <returns>返回结果</returns>
|
|
|
|
|
[NonAction]
|
|
|
|
|
private async Task<ESLManiAlloChkAPIBaseResult> ExcuteManiAllocCheckDownload(string url, ESLManiAlloChkRequestDto info, string batchNo)
|
|
|
|
|
{
|
|
|
|
|
ESLManiAlloChkAPIBaseResult model = null;
|
|
|
|
|
/*
|
|
|
|
|
1、填充请求的类,并生成JSON报文
|
|
|
|
|
2、POST请求接口,并记录回执。
|
|
|
|
|
3、返回信息。
|
|
|
|
|
*/
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("批次={no} 对应请求报文 request={res}", batchNo, JSON.Serialize(info));
|
|
|
|
|
|
|
|
|
|
var res = await url.OnClientCreating(client => {
|
|
|
|
|
// client 为 HttpClient 对象
|
|
|
|
|
client.Timeout = TimeSpan.FromMinutes(15); // 设置超时时间 15分钟
|
|
|
|
|
}).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();
|
|
|
|
|
model = JSON.Deserialize<ESLManiAlloChkAPIBaseResult>(userResult);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
//写日志
|
|
|
|
|
if (ex is HttpRequestException)
|
|
|
|
|
throw Oops.Oh(10000002);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return model;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 到港时间更新
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="bookingIds">订舱主键数组</param>
|
|
|
|
|
/// <returns>返回回执</returns>
|
|
|
|
|
[HttpPost("/BookingValueAdded/CheckUpdateETA")]
|
|
|
|
|
public async Task<TaskManageOrderResultDto> CheckUpdateETA([FromBody] long[] bookingIds)
|
|
|
|
|
{
|
|
|
|
|
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
1、订舱主键提取提单号、订舱编号,优先去提单号,其次订舱编号
|
|
|
|
|
2、根据不同的船公司调取单独的配置账户。
|
|
|
|
|
3、不同船公司对应不同的接口。
|
|
|
|
|
4、轮询异步调取接口等待返回接口。
|
|
|
|
|
5、成功后将文件链接存入附件表。
|
|
|
|
|
6、等待所有请求完成返回统计结果。
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
result.succ = true;
|
|
|
|
|
result.msg = "批量执行成功";
|
|
|
|
|
//result.ext = id;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
result.succ = false;
|
|
|
|
|
result.msg = $"批量下载Draft异常,原因:{ex.Message}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 舱位分配查询
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="bookingIds">订舱主键数组</param>
|
|
|
|
|
/// <returns>返回回执</returns>
|
|
|
|
|
[HttpPost("/BookingValueAdded/CheckUpdateManifestNo")]
|
|
|
|
|
public async Task<TaskManageOrderResultDto> CheckUpdateManifestNo([FromBody] long[] bookingIds)
|
|
|
|
|
{
|
|
|
|
|
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
|
|
|
|
|
List<Task<TaskManageOrderResultDto>> taskList = new List<Task<TaskManageOrderResultDto>>();
|
|
|
|
|
|
|
|
|
|
string batchNo = IDGen.NextID().ToString();
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
现在只有ESL支持舱位分配查询
|
|
|
|
|
|
|
|
|
|
1、订舱主键提取提单号,判断提单号如果不是ESL开头的,需要启动流程
|
|
|
|
|
2、异步调取ESL接口。
|
|
|
|
|
3、返回结果后提取esl_no号
|
|
|
|
|
4、将参考号写入订舱编号。
|
|
|
|
|
5、将esl_no号写入主提单号。
|
|
|
|
|
6、等待所有请求完成返回统计结果。
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
var list = _bookingOrderRepository.AsQueryable()
|
|
|
|
|
.Where(a => bookingIds.Contains(a.Id)).ToList();
|
|
|
|
|
|
|
|
|
|
if (list.Count != bookingIds.Length)
|
|
|
|
|
throw Oops.Oh($"订舱信息获取失败,订舱信息不存在或已作废");
|
|
|
|
|
|
|
|
|
|
var noList = bookingIds.Select((a, idx) => new { no = idx + 1, id = a }).ToList();
|
|
|
|
|
foreach (var bk in list)
|
|
|
|
|
{
|
|
|
|
|
var sortNo = noList.FirstOrDefault(a => a.id == bk.Id).no;
|
|
|
|
|
taskList.Add(Task.Run(() => InnerCheckUpdateManifestNo(bk, batchNo, sortNo)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Task.WaitAll(taskList.ToArray());
|
|
|
|
|
|
|
|
|
|
result.succ = true;
|
|
|
|
|
result.msg = "批量执行成功";
|
|
|
|
|
|
|
|
|
|
var downResultList = taskList.Select(x => x.Result).ToList();
|
|
|
|
|
|
|
|
|
|
if (downResultList.Any(x => !x.succ))
|
|
|
|
|
{
|
|
|
|
|
result.succ = false;
|
|
|
|
|
result.msg = "批量执行失败";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
result.succ = true;
|
|
|
|
|
result.msg = downResultList.FirstOrDefault().msg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.ext = downResultList;
|
|
|
|
|
|
|
|
|
|
var succ = downResultList.Count(x => x.succ);
|
|
|
|
|
var fail = downResultList.Count(x => !x.succ);
|
|
|
|
|
|
|
|
|
|
if (succ > 0)
|
|
|
|
|
result.batchTotal = succ.ToString();
|
|
|
|
|
|
|
|
|
|
if (fail > 0)
|
|
|
|
|
{
|
|
|
|
|
if (succ > 0)
|
|
|
|
|
result.batchTotal = result.batchTotal + "/" + fail.ToString();
|
|
|
|
|
|
|
|
|
|
result.batchTotal = "/" + fail.ToString();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
result.succ = false;
|
|
|
|
|
result.msg = $"批量执行失败,原因:{ex.Message}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region 单票舱位分配查询
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 单票舱位分配查询
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="bookingOrder">订舱详情</param>
|
|
|
|
|
/// <param name="batchNo">批次号</param>
|
|
|
|
|
/// <param name="sortNo">请求顺序号</param>
|
|
|
|
|
/// <returns>返回回执</returns>
|
|
|
|
|
private async Task<TaskManageOrderResultDto> InnerCheckUpdateManifestNo(BookingOrder bookingOrder, string batchNo, int sortNo)
|
|
|
|
|
{
|
|
|
|
|
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
|
|
|
|
|
|
|
|
|
|
result.bno = bookingOrder.MBLNO;
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
1、根据船公司代码匹配船公司映射。
|
|
|
|
|
2、BC和DRAFT是分别2个请求地址。
|
|
|
|
|
3、生成请求报文。
|
|
|
|
|
4、请求相应的链接。
|
|
|
|
|
5、返回成功写入附件。
|
|
|
|
|
*/
|
|
|
|
|
if (string.IsNullOrWhiteSpace(bookingOrder.MBLNO))
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(bookingOrder.CUSTNO))
|
|
|
|
|
{
|
|
|
|
|
result.bno = $"订 {bookingOrder.CUSTNO}";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
result.bno = $"NO.{sortNo}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw Oops.Bah($"主提单号不能为空");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var bcOrDraftRouteCfg = _cache.GetAllMappingCarrier().GetAwaiter().GetResult()
|
|
|
|
|
.FirstOrDefault(t => t.Module.Equals(CONST_MAPPING_MANIALLO_CHK_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($"船公司={bookingOrder.CARRIERID} 暂不支持舱位分配查询");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string urlKey = string.Format(CONST_FORMAT_MANIALLO_CHK_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} 提取舱位分配查询URL完成,结果={rlt}",
|
|
|
|
|
bookingOrder.MBLNO, bookingOrder.CARRIERID, bcUrl);
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(bcUrl))
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("提单号{0} 根据订舱的船公司代码{1} 提取舱位分配查询URL失败,未取到配置key={key}",
|
|
|
|
|
bookingOrder.MBLNO, bookingOrder.CARRIERID, urlKey);
|
|
|
|
|
|
|
|
|
|
throw Oops.Bah($"船公司={bookingOrder.CARRIERID} 未配置请求地址{urlKey} 请联系管理员");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string webKey = string.Format(CONST_FORMAT_WEB, bcOrDraftRouteCfg.MapCode.ToLower());
|
|
|
|
|
|
|
|
|
|
//获取个人对应的账户,这里GetAccountConfig逻辑优先取个人,个人没有配置取公司对应配置
|
|
|
|
|
var userWebAccountConfig = _webAccountConfig.GetAccountConfig(webKey, UserManager.UserId).GetAwaiter()
|
|
|
|
|
.GetResult();
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("批次={no} 获取获取网站的账户完成,result={Num}", batchNo, JSON.Serialize(userWebAccountConfig));
|
|
|
|
|
|
|
|
|
|
if (userWebAccountConfig == null)
|
|
|
|
|
throw Oops.Oh($" 未配置个人或公司网站账户,网站{webKey}");
|
|
|
|
|
|
|
|
|
|
ESLManiAlloChkRequestDto requestDto = new ESLManiAlloChkRequestDto
|
|
|
|
|
{
|
|
|
|
|
u = userWebAccountConfig.Account?.Trim(),
|
|
|
|
|
p = userWebAccountConfig.Password?.Trim(),
|
|
|
|
|
so_no = bookingOrder.MBLNO,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("批次={no} json={json} 请求舱位分配查询远端下载开始", batchNo, JSON.Serialize(requestDto));
|
|
|
|
|
|
|
|
|
|
DateTime bDate = DateTime.Now;
|
|
|
|
|
//开始请求BC
|
|
|
|
|
var rlt = await ExcuteManiAllocCheckDownload(bcUrl, requestDto, batchNo);
|
|
|
|
|
|
|
|
|
|
DateTime eDate = DateTime.Now;
|
|
|
|
|
TimeSpan ts = eDate.Subtract(bDate);
|
|
|
|
|
var timeDiff = ts.TotalMilliseconds;
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("批次={no} result={result} 请求舱位分配查询远端下载结束 耗时:{timeDiff}ms. ", batchNo,
|
|
|
|
|
JSON.Serialize(rlt), timeDiff);
|
|
|
|
|
|
|
|
|
|
if (rlt.status == 1)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("批次={no} 舱位分配查询成功", batchNo);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
提单号写入订舱编号,并把esl_no写入主提单号
|
|
|
|
|
*/
|
|
|
|
|
var bkInfo = _bookingOrderRepository.AsQueryable().First(a => a.Id == bookingOrder.Id);
|
|
|
|
|
|
|
|
|
|
if(bkInfo == null)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("批次={no} id={id} 订舱信息获取失败,无法更新", batchNo, bookingOrder.Id);
|
|
|
|
|
|
|
|
|
|
throw Oops.Oh($"订舱信息获取失败,无法更新");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(Regex.IsMatch(bkInfo.MBLNO,"\\bESL\\w+"))
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation("批次={no} id={id} 主提单号已变更,无法更新", batchNo, bookingOrder.Id);
|
|
|
|
|
|
|
|
|
|
throw Oops.Oh($"主提单号已变更,无法更新");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("批次={no} 变更前记录 id={id} MBLNO={MBLNO} CUSTNO={CUSTNO} esl_no={eslno}",
|
|
|
|
|
batchNo, bookingOrder.Id, bkInfo.MBLNO, bkInfo.CUSTNO, rlt.data.esl_no);
|
|
|
|
|
|
|
|
|
|
bkInfo.CUSTNO = bkInfo.MBLNO;
|
|
|
|
|
bkInfo.MBLNO = rlt.data.esl_no.Trim().ToUpper();
|
|
|
|
|
bkInfo.UpdatedTime = DateTime.Now;
|
|
|
|
|
bkInfo.UpdatedUserId = UserManager.UserId;
|
|
|
|
|
bkInfo.UpdatedUserName = UserManager.Name;
|
|
|
|
|
|
|
|
|
|
await _bookingOrderRepository.AsUpdateable(bkInfo).UpdateColumns(it => new
|
|
|
|
|
{
|
|
|
|
|
it.CUSTNO,
|
|
|
|
|
it.MBLNO,
|
|
|
|
|
it.UpdatedTime,
|
|
|
|
|
it.UpdatedUserId,
|
|
|
|
|
it.UpdatedUserName
|
|
|
|
|
}).ExecuteCommandAsync();
|
|
|
|
|
|
|
|
|
|
result.succ = true;
|
|
|
|
|
result.msg = "舱位分配查询成功";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
result.succ = false;
|
|
|
|
|
result.msg = $"舱位分配查询失败,{rlt.message}";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
result.succ = false;
|
|
|
|
|
result.msg = $"舱位分配查询失败,{ex.Message}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class DateTimeJsonConverter : System.Text.Json.Serialization.JsonConverter<DateTime>
|
|
|
|
|
{
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|