You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
BookingHeChuan/Myshipping.Application/Service/BookingOrder/BookingValueAddedService.cs

712 lines
29 KiB
C#

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.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.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_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_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($"订舱信息获取失败,订舱信息不存在或已作废");
foreach (var bk in list)
{
taskList.Add(InnerDownloadBookingConfirm(bk, batchNo));
}
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>
/// <returns>返回回执</returns>
private async Task<TaskManageOrderResultDto> InnerDownloadBookingConfirm(BookingOrder bookingOrder,string batchNo)
{
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
result.bno = bookingOrder.MBLNO;
try
{
/*
1
2BCDRAFT2
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_FORMAT_WEB, UserManager.UserId).GetAwaiter()
.GetResult();
_logger.LogInformation("批次={no} 获取获取网站的账户完成result={Num}", batchNo, JSON.Serialize(userWebAccountConfig));
if (userWebAccountConfig == null)
throw Oops.Bah($"个人/公司网站【{CONST_FORMAT_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
};
_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.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
/// <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);
}
/// <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($"订舱信息获取失败,订舱信息不存在或已作废");
foreach (var bk in list)
{
taskList.Add(InnerDownloadBookingConfirm(bk, batchNo));
}
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 = $"批量下载Draft异常原因{ex.Message}";
}
return result;
}
#region 单票下载BC
/// <summary>
/// 单票下载BC
/// </summary>
/// <param name="bookingOrder">订舱详情</param>
/// <param name="batchNo">批次号</param>
/// <returns>返回回执</returns>
private async Task<TaskManageOrderResultDto> InnerDownloadDraft(BookingOrder bookingOrder, string batchNo)
{
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
result.bno = bookingOrder.MBLNO;
try
{
/*
1
2BCDRAFT2
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_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("提单号【{mbl}】根据订舱的船公司代码{ca} 提取BC下载URL失败未取到配置key={key}",
bookingOrder.MBLNO, bookingOrder.CARRIERID, urlKey);
}
string accountTypeCode = string.Empty;
//获取个人对应的账户,这里GetAccountConfig逻辑优先取个人个人没有配置取公司对应配置
var userWebAccountConfig = _webAccountConfig.GetAccountConfig(CONST_FORMAT_WEB, UserManager.UserId).GetAwaiter()
.GetResult();
_logger.LogInformation("批次={no} 获取获取网站的账户完成result={Num}", batchNo, JSON.Serialize(userWebAccountConfig));
if (userWebAccountConfig == null)
throw Oops.Bah($"个人/公司网站【{CONST_FORMAT_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
};
_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.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="url">请求URL</param>
/// <param name="info">请求详情</param>
/// <param name="batchNo">批次号</param>
/// <returns>返回结果</returns>
[NonAction]
private async Task<BCAPIBaseResult<BCAPIBaseDataParse>> ExcuteBCDownload(string url, BCOrDraftRequestDto info, string batchNo)
{
BCAPIBaseResult<BCAPIBaseDataParse> model = null;
/*
1JSON
2POST
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<BCAPIBaseResult<BCAPIBaseDataParse>>(userResult, jsonOptions);
}
}
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();
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;
}
}
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));
}
}
}