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.

483 lines
20 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.FriendlyException;
using Furion.JsonSerialization;
using Furion;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Furion.RemoteRequest.Extensions;
using Furion.DependencyInjection;
using Microsoft.AspNetCore.Mvc;
using System.Xml.Linq;
using Furion.DynamicApiController;
using Microsoft.AspNetCore.Authorization;
using Myshipping.Application.Entity;
using Myshipping.Core;
using Furion.DistributedIDGenerator;
using Mapster;
using Furion.DataValidation;
using Newtonsoft.Json.Linq;
using Myshipping.Core.Service;
using Myshipping.Core.Entity;
using Microsoft.Extensions.Logging;
namespace Myshipping.Application
{
/// <summary>
/// 请求规则平台
/// </summary>
[ApiDescriptionSettings("Application", Name = "RulesEngineClient", Order = 9)]
public class RulesEngineClientService: IRulesEngineClientService, IDynamicApiController, ITransient
{
private readonly SqlSugarRepository<BookingOrder> _bookingOrderRepository;
private readonly SqlSugarRepository<BookingCtn> _bookingOrderContaRepository;
private readonly SqlSugarRepository<BookingCtnDetail> _bookingOrderContaCargoRepository;
private readonly ISysCacheService _cache;
private readonly SqlSugarRepository<RelaPortCarrierLane> _relaPortCarrierLaneRep;
private readonly ILogger<BookingOrderService> _logger;
const string CONST_MAPPING_MODULE = "RULE_ENGINE_CHECK";
/// <summary>
///
/// </summary>
public RulesEngineClientService(SqlSugarRepository<BookingOrder> bookingOrderRepository,
SqlSugarRepository<BookingCtn> bookingOrderContaRepository,
SqlSugarRepository<BookingCtnDetail> bookingOrderContaCargoRepository,
ISysCacheService cache,
SqlSugarRepository<RelaPortCarrierLane> relaPortCarrierLaneRep,
ILogger<BookingOrderService> logger)
{
_bookingOrderRepository = bookingOrderRepository;
_bookingOrderContaRepository = bookingOrderContaRepository;
_bookingOrderContaCargoRepository = bookingOrderContaCargoRepository;
_cache = cache;
_relaPortCarrierLaneRep = relaPortCarrierLaneRep;
_logger = logger;
}
#region 海运订舱请求规则引擎校验
/// <summary>
/// 海运订舱请求规则引擎校验
/// </summary>
/// <param name="model">海运订舱请求业务</param>
/// <returns>返回用户信息</returns>
[HttpPost("/RulesEngineClient/ExcuteRulesOceanBookingByMsg")]
public async Task<RulesEngineExcuteResultDto> ExcuteRulesOceanBookingByMsg(RulesEngineOrderBookingMessageInfo model)
{
RulesEngineExcuteResultDto result = new RulesEngineExcuteResultDto();
var ruleResult = await ExcuteRulesEngine(model);
if (ruleResult == null)
throw Oops.Oh($"订舱请求规则失败,返回为空");
var innerRlt = JSON.Deserialize<RemoteRulesEngineExcuteResultDto>(ruleResult.extra.ToString());
var ruleDetailList = innerRlt.DetailList;
result.succ = ruleResult.succ;
result.msg = ruleResult.msg;
if (ruleDetailList.Count > 0)
{
result.rows = ruleDetailList;
}
return result;
}
#endregion
#region 海运订舱请求规则引擎校验
/// <summary>
/// 海运订舱请求规则引擎校验
/// </summary>
/// <param name="bookingId">海运订舱主键</param>
/// <returns>返回用户信息</returns>
[HttpGet("/RulesEngineClient/ExcuteRulesOceanBooking")]
public async Task<RulesEngineExcuteResultDto> ExcuteRulesOceanBooking(string bookingId)
{
string batchNo = IDGen.NextID().ToString();
_logger.LogInformation("批次={no}获取订舱数据请求规则 {id}", batchNo, bookingId);
/*
处理逻辑
1、订单保存后触发调取此方法传入海运订舱主键。
2、调取订舱的详情。
3、对应请求报文。
4、请求规则接口。
5、返回回执。
*/
RulesEngineExcuteResultDto result = new RulesEngineExcuteResultDto();
try
{
DateTime nowDate = DateTime.Now;
var model = _bookingOrderRepository.AsQueryable().InSingle(long.Parse(bookingId));
if (model == null)
throw Oops.Oh($"订舱主键{bookingId}无法获取业务信息");
_logger.LogInformation("批次={no}获取订舱数据完成", batchNo);
var mainInfo = model.Adapt<RulesEngineOrderBookingMainBusinessInfo>();
var contaList = await _bookingOrderContaRepository.AsQueryable().Where(x => x.BILLID == model.Id).ToListAsync();
_logger.LogInformation("批次={no} 提取箱完成 数量={total}", batchNo, contaList.Count);
if (contaList.Count > 0)
{
mainInfo.ContaList = contaList.Adapt<List<RulesEngineOrderBookingContaInfo>>();
var ctnArg = contaList.Select(t => t.Id).ToArray();
var cargoList = await _bookingOrderContaCargoRepository.AsQueryable()
.Where(x => ctnArg.Contains(x.CTNID.Value)).ToListAsync();
_logger.LogInformation("批次={no} 提取货物明细完成 数量={total}", batchNo, cargoList.Count);
if (cargoList.Count > 0)
{
mainInfo.ContaList = contaList.GroupJoin(cargoList, l => l.Id, r => r.CTNID, (l, r) => {
var currList = r.ToList();
if (currList.Count > 0)
{
var info = l.Adapt<RulesEngineOrderBookingContaInfo>();
info.CargoList = currList.Adapt<List<RulesEngineOrderBookingContaCargoInfo>>();
return info;
}
return l.Adapt<RulesEngineOrderBookingContaInfo>();
}).ToList();
}
else
{
mainInfo.ContaList = contaList.Adapt<List<RulesEngineOrderBookingContaInfo>>();
}
}
var msgModel = GetMessageInfo(batchNo, mainInfo);
_logger.LogInformation("批次={no} 对应请求报文完成 msg={msg}", batchNo, JSON.Serialize(msgModel));
DateTime bDate = DateTime.Now;
var ruleResult = await ExcuteRulesEngine(msgModel);
DateTime eDate = DateTime.Now;
TimeSpan ts = eDate.Subtract(bDate);
var timeDiff = ts.TotalMilliseconds;
_logger.LogInformation("批次={no} 请求完成,耗时:{timeDiff}ms. 结果{msg}", batchNo, timeDiff, ruleResult.succ ? "成功" : "失败");
if (ruleResult == null)
throw Oops.Oh($"订舱主键{bookingId}请求规则失败,返回为空");
var innerRlt = JSON.Deserialize<RemoteRulesEngineExcuteResultDto>(ruleResult.extra.ToString());
var ruleDetailList = innerRlt.DetailList;
result.succ = ruleResult.succ;
result.msg = ruleResult.msg;
if (ruleDetailList != null && ruleDetailList.Count > 0)
{
result.rows = ruleDetailList.OrderBy(t=>t.ErrorType).ToList();
}
_logger.LogInformation("批次={no} 返回结果{msg}", batchNo,JSON.Serialize(result));
}
catch(Exception ex)
{
result.succ = false;
result.msg = $"请求规则异常,{ex.Message}";
}
return result;
}
#endregion
#region 海运订舱请求规则引擎校验
/// <summary>
/// 海运订舱请求规则引擎校验
/// </summary>
/// <param name="model">海运订舱报文类</param>
/// <returns>返回回执</returns>
[HttpPost("/RulesEngineClient/ExcuteRulesOceanBookingByModel")]
public async Task<RulesEngineExcuteResultDto> ExcuteRulesOceanBookingByModel(BookingOrderDto model)
{
string batchNo = IDGen.NextID().ToString();
/*
处理逻辑
1、前台将保存报文推送过来。
2、如果有订舱主键调取数据库内的订舱的详情已推送的数据为准。
3、对应请求报文。
4、请求规则接口。
5、返回回执。
*/
RulesEngineExcuteResultDto result = new RulesEngineExcuteResultDto();
try
{
DateTime nowDate = DateTime.Now;
var mainInfo = model.Adapt<RulesEngineOrderBookingMainBusinessInfo>();
RulesEngineOrderBookingMessageInfo msgModel = GetMessageInfo(batchNo, mainInfo);
var ruleResult = await ExcuteRulesEngine(msgModel);
if (ruleResult == null)
throw Oops.Oh($"订舱主键{model.BOOKINGNO}请求规则失败,返回为空");
var innerRlt = JSON.Deserialize<RemoteRulesEngineExcuteResultDto>(ruleResult.extra.ToString());
var ruleDetailList = innerRlt.DetailList;
result.succ = ruleResult.succ;
result.msg = ruleResult.msg;
if (ruleDetailList.Count > 0)
{
result.rows = ruleDetailList;
}
}
catch (Exception ex)
{
result.succ = false;
result.msg = "请求规则异常";
}
return result;
}
#endregion
/// <summary>
/// 生成请求规则报文
/// </summary>
/// <param name="batchNo">批次号</param>
/// <param name="mainInfo">订舱主业务信息</param>
/// <returns>返回请求报文类</returns>
[NonAction]
private RulesEngineOrderBookingMessageInfo GetMessageInfo(string batchNo,RulesEngineOrderBookingMainBusinessInfo mainInfo)
{
DateTime nowDate = DateTime.Now;
RulesEngineOrderBookingMessageInfo msgModel = new RulesEngineOrderBookingMessageInfo();
msgModel.Head = new RulesEngineWebAPIHeadBase
{
GID = batchNo,
MessageType = "BUSI_RULE",
SenderId = App.Configuration["RulesEngineSender"],
SenderName = App.Configuration["RulesEngineSenderName"],
SenderKey = App.Configuration["RulesEngineAuthKey"],
ReceiverId = "RulesEngine",
ReceiverName = "大简云规则引擎",
Version = "1.0",
RequestDate = nowDate.ToString("yyyy-MM-dd HH:mm:ss"),
RequestAction = "CheckRule",
};
msgModel.Main = new RulesEngineOrderBookingMainInfo
{
ProjectCode = App.Configuration["RulesEngineProjects"].Split(new char[] { ',' }).ToArray(),
};
msgModel.Main.BusinessInfo = mainInfo;
List<CodePort> codePortList = new List<CodePort>();
//根据卸货港翻译航线信息
if (!string.IsNullOrWhiteSpace(mainInfo.PortDischargeId))
{
var laneList = _cache.GetAllRelaPortCarrierLane().GetAwaiter().GetResult();
var laneInfo = laneList
.FirstOrDefault(p => !string.IsNullOrWhiteSpace(mainInfo.CarrierId) && !string.IsNullOrWhiteSpace(p.CarrierCode)
&& !string.IsNullOrWhiteSpace(p.PortCode)
&& p.CarrierCode.Equals(mainInfo.CarrierId, StringComparison.OrdinalIgnoreCase) && p.PortCode.Equals(mainInfo.PortDischargeId, StringComparison.OrdinalIgnoreCase));
if (laneInfo == null)
laneInfo = laneList.FirstOrDefault(p => string.IsNullOrWhiteSpace(p.CarrierCode)
&& !string.IsNullOrWhiteSpace(p.PortCode)
&& p.PortCode.Equals(mainInfo.PortDischargeId, StringComparison.OrdinalIgnoreCase));
_logger.LogInformation("批次={no} 港口对应航线完成 port={port} msg={msg}", batchNo, mainInfo.PortDischargeId, JSON.Serialize(laneInfo));
if (laneInfo != null)
{
var lineModel = _cache.GetAllCodeLane().GetAwaiter().GetResult().FirstOrDefault(t =>
!string.IsNullOrWhiteSpace(t.Code)
&& t.Code.Equals(laneInfo.LaneCode, StringComparison.OrdinalIgnoreCase));
_logger.LogInformation("批次={no} 检索航线完成 lane={lane} msg={msg}", batchNo, laneInfo.LaneCode, JSON.Serialize(lineModel));
msgModel.Main.BusinessInfo.LaneCode = laneInfo.LaneCode;
msgModel.Main.BusinessInfo.LaneName = lineModel?.CnName;
}
codePortList = _cache.GetAllCodePort().GetAwaiter().GetResult();
//翻译卸货港对应的国家
var portInfo = codePortList.FirstOrDefault(t =>
!string.IsNullOrWhiteSpace(t.Code)
&& t.EdiCode.Equals(mainInfo.PortDischargeId, StringComparison.OrdinalIgnoreCase));
_logger.LogInformation("批次={no} 检索港口完成 lane={lane} msg={msg}", batchNo, mainInfo.PortDischargeId, JSON.Serialize(portInfo));
if (portInfo == null || string.IsNullOrWhiteSpace(portInfo.CountryCode))
throw Oops.Oh($"卸货港口代码{mainInfo.PortDischargeId}获取港口基础数据中国家代码失败");
var countryInfo = _cache.GetAllCodeCountry().GetAwaiter().GetResult().FirstOrDefault(t =>
!string.IsNullOrWhiteSpace(t.Code)
&& t.Code.Equals(portInfo.CountryCode, StringComparison.OrdinalIgnoreCase));
if (countryInfo == null || string.IsNullOrWhiteSpace(portInfo.EnName))
throw Oops.Oh($"国家代码{portInfo.CountryCode}获取国家基础数据失败");
msgModel.Main.BusinessInfo.PortDischargeCountryNo = countryInfo.Code;
msgModel.Main.BusinessInfo.PortDischargeEN = countryInfo.EnName;
msgModel.Main.BusinessInfo.PortDischargeCN = countryInfo.CnName;
}
//中转港
if (!string.IsNullOrWhiteSpace(mainInfo.TransportId))
{
if(codePortList.Count == 0)
{
codePortList = _cache.GetAllCodePort().GetAwaiter().GetResult();
}
//翻译中转港对应的国家
var portInfo = codePortList.FirstOrDefault(t =>
!string.IsNullOrWhiteSpace(t.Code)
&& t.EdiCode.Equals(mainInfo.TransportId, StringComparison.OrdinalIgnoreCase));
_logger.LogInformation("批次={no} 检索港口完成 port={lane} msg={msg}", batchNo, mainInfo.TransportId, JSON.Serialize(portInfo));
if (portInfo == null || string.IsNullOrWhiteSpace(portInfo.CountryCode))
throw Oops.Oh($"中转港口代码{mainInfo.TransportId}获取港口基础数据失败");
var countryInfo = _cache.GetAllCodeCountry().GetAwaiter().GetResult().FirstOrDefault(t =>
!string.IsNullOrWhiteSpace(t.Code)
&& t.Code.Equals(portInfo.CountryCode, StringComparison.OrdinalIgnoreCase));
if (countryInfo == null || string.IsNullOrWhiteSpace(portInfo.EnName))
throw Oops.Oh($"国家代码{portInfo.CountryCode}获取国家基础数据失败");
msgModel.Main.BusinessInfo.TransportCountryNo = countryInfo.Code;
msgModel.Main.BusinessInfo.TransportEN = countryInfo.EnName;
msgModel.Main.BusinessInfo.TransportCN = countryInfo.CnName;
}
//对应签单方式
if (!string.IsNullOrWhiteSpace(mainInfo.IssueType))
{
var issueType = _cache.GetAllCodeIssueType().GetAwaiter().GetResult().FirstOrDefault(p =>
!string.IsNullOrWhiteSpace(p.EnName)
&& p.EnName.Equals(mainInfo.IssueType, StringComparison.OrdinalIgnoreCase));
_logger.LogInformation("批次={no} 提取签单方式完成 IssueType={issue} msg={msg}", batchNo, mainInfo.IssueType, JSON.Serialize(issueType));
//这里规则约定用了签单方式的代码大写
if (issueType != null)
msgModel.Main.BusinessInfo.IssueType = issueType.Code.ToUpper();
}
if (mainInfo.ContaList != null && mainInfo.ContaList.Count > 0)
{
//var contaList = _cache.GetAllMappingCtn().GetAwaiter().GetResult();
//mainInfo.ContaList.ForEach(t =>
//{
// var currContaType = contaList.FirstOrDefault(x => x.Module.Equals(CONST_MAPPING_MODULE, StringComparison.OrdinalIgnoreCase)
// && x.Code.Equals(t.ContaType, StringComparison.OrdinalIgnoreCase));
// if (currContaType != null)
// {
// t.ContaType = currContaType.MapCode?.ToUpper();
// }
//});
mainInfo.ContaList.ForEach(t =>
{
if (!string.IsNullOrWhiteSpace(t.ContaType))
{
var contaInfo = _cache.GetAllCodeCtn().GetAwaiter().GetResult().FirstOrDefault(p =>
!string.IsNullOrWhiteSpace(p.Code)
&& p.Code.Equals(t.ContaType, StringComparison.OrdinalIgnoreCase));
if (contaInfo == null)
throw Oops.Oh($"箱型{t.ContaType} 基础数据没有指定大小箱");
t.ContaCategory = contaInfo.CtnCategory;
}
});
}
return msgModel;
}
#region 请求规则平台
/// <summary>
/// 请求规则平台
/// </summary>
/// <param name="BusinessMsg"></param>
/// <returns></returns>
[NonAction]
private async Task<RulesEngineWebApiResult> ExcuteRulesEngine(RulesEngineOrderBookingMessageInfo info)
{
RulesEngineWebApiResult model = null;
/*
1、读取配置文件中的规则引擎URL
2、填充请求的类并生成JSON报文
3、POST请求接口并记录回执。
4、返回信息。
*/
var url = App.Configuration["RulesEngineUrl"];
try
{
var res = await url.SetHttpMethod(HttpMethod.Post)
.SetBody(JSON.Serialize(info), "application/json")
.SetContentEncoding(Encoding.UTF8)
.PostAsync();
_logger.LogInformation("批次={no} 对应请求报文完成 res={res}", info.Head.GID, JSON.Serialize(res));
if (res.StatusCode == System.Net.HttpStatusCode.OK)
{
var userResult = await res.Content.ReadAsStringAsync();
model = JSON.Deserialize<RulesEngineWebApiResult>(userResult);
}
}
catch (Exception ex)
{
//写日志
if (ex is HttpRequestException)
throw Oops.Oh(10000002);
}
return model;
}
#endregion
}
}