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.

381 lines
15 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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
{
/// <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_TSL_BC_WEB = "TslWeb";
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);
if (downResultList.Any(x => !x.succ))
{
result.succ = false;
result.msg = "BC下载失败";
//result.ext =
}
//result.ext = id;
}
catch (Exception ex)
{
result.succ = false;
result.msg = $"批量下载BC异常原因{ex.Message}";
}
return result;
}
private async Task<TaskManageOrderResultDto> 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<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);
}
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成功";
}
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 = "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();
try
{
/*
1、订舱主键提取提单号、订舱编号优先去提单号其次订舱编号
2、根据不同的船公司调取单独的配置账户。
3、不同船公司对应不同的接口。
4、轮询异步调取接口等待返回接口。
5、成功后将文件链接存入附件表。
6、等待所有请求完成返回统计结果。
*/
result.succ = true;
result.msg = "批量下载Draft成功";
//result.ext = id;
}
catch (Exception ex)
{
result.succ = false;
result.msg = $"批量下载Draft异常原因{ex.Message}";
}
return result;
}
#region 请求规则平台
/// <summary>
/// 请求规则平台
/// </summary>
/// <param name="BusinessMsg"></param>
/// <returns></returns>
[NonAction]
private async Task<BCAPIBaseResult<BCAPIBaseDataParse>> ExcuteBCDownload(string url, BCOrDraftRequestDto info, string batchNo)
{
BCAPIBaseResult<BCAPIBaseDataParse> 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<BCAPIBaseResult<BCAPIBaseDataParse>>(userResult, jsonOptions);
}
}
catch (Exception ex)
{
//写日志
if (ex is HttpRequestException)
throw Oops.Oh(10000002);
}
return model;
}
#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));
}
}
}