|
|
|
|
using Furion;
|
|
|
|
|
using Furion.DependencyInjection;
|
|
|
|
|
using Furion.DistributedIDGenerator;
|
|
|
|
|
using Furion.DynamicApiController;
|
|
|
|
|
using Furion.FriendlyException;
|
|
|
|
|
using Furion.JsonSerialization;
|
|
|
|
|
using Furion.RemoteRequest.Extensions;
|
|
|
|
|
using Mapster;
|
|
|
|
|
using MathNet.Numerics.Distributions;
|
|
|
|
|
using Microsoft.AspNetCore.Authorization;
|
|
|
|
|
using Microsoft.AspNetCore.Http;
|
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
using Myshipping.Core;
|
|
|
|
|
using Myshipping.Core.Service;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Net.Http;
|
|
|
|
|
using System.Reflection.Emit;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using Yitter.IdGenerator;
|
|
|
|
|
|
|
|
|
|
namespace Myshipping.Application
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 内嵌运踪港后数据查询
|
|
|
|
|
/// </summary>
|
|
|
|
|
[AllowAnonymous, ApiDescriptionSettings("Application", Name = "EmbedTraceProduct", Order = 20)]
|
|
|
|
|
public class EmbedTraceProductService : IEmbedTraceProductService, IDynamicApiController, ITransient
|
|
|
|
|
{
|
|
|
|
|
private readonly ILogger<EmbedTraceProductService> _logger;
|
|
|
|
|
private const string CONST_TRACE_API_URL = "embed_trace_flow_url";
|
|
|
|
|
private readonly ISysCacheService _cache;
|
|
|
|
|
private readonly IDjyWebsiteAccountConfigService _webAccountConfig;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
///
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="logger"></param>
|
|
|
|
|
public EmbedTraceProductService(ILogger<EmbedTraceProductService> logger, ISysCacheService cache, IDjyWebsiteAccountConfigService webAccountConfig)
|
|
|
|
|
{
|
|
|
|
|
_logger = logger;
|
|
|
|
|
_cache = cache;
|
|
|
|
|
_webAccountConfig = webAccountConfig;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region 获取单票运踪流程详情
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 获取单票运踪流程详情
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="model">查询单票运踪流程详情</param>
|
|
|
|
|
/// <returns>返回回执</returns>
|
|
|
|
|
[AllowAnonymous, HttpPost("/EmbedTraceProduct/GetTraceFlowInfo"), ApiUser(ApiCode = "EmbedServiceTraceShow")]
|
|
|
|
|
public async Task<EmbedTraceFlowResultDto> GetTraceFlowInfo(EmbedQueryTraceFlowDto model)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
1、调取运踪接口。 embed_trace_flow_url
|
|
|
|
|
2、根据返回结果组织暂时的JSON结果。
|
|
|
|
|
3、返回结果
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
EmbedTraceFlowResultDto result = null;
|
|
|
|
|
|
|
|
|
|
string batchNo = IDGen.NextID().ToString();
|
|
|
|
|
|
|
|
|
|
string sendUrl = _cache.GetAllDictData().GetAwaiter().GetResult()
|
|
|
|
|
.FirstOrDefault(x => x.TypeCode == "url_set" && x.Code == CONST_TRACE_API_URL)?.Value;
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(sendUrl))
|
|
|
|
|
throw Oops.Oh("未配置发送订舱请求接口地址,请联系管理员");
|
|
|
|
|
|
|
|
|
|
var webAccountConfig = _webAccountConfig
|
|
|
|
|
.GetAccountConfigByTenantId("seae_billtraceurl", UserManager.UserId, UserManager.TENANT_ID).GetAwaiter().GetResult();
|
|
|
|
|
|
|
|
|
|
if (webAccountConfig == null)
|
|
|
|
|
throw Oops.Oh("未配置账户,请先配置公司或者个人账户 类型-运踪新增调用");
|
|
|
|
|
|
|
|
|
|
QueryTraceAfterPortDto queryDto = new QueryTraceAfterPortDto
|
|
|
|
|
{
|
|
|
|
|
user_key = webAccountConfig.Account,
|
|
|
|
|
user_secret = webAccountConfig.Password,
|
|
|
|
|
carriercd = model.carrier,
|
|
|
|
|
referenceno = model.billNo,
|
|
|
|
|
ctnrno = model.ctnNo
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var queryRlt = await QueryTraceAsync(queryDto, sendUrl);
|
|
|
|
|
|
|
|
|
|
var statusDict = _cache.GetAllDictData().GetAwaiter().GetResult()
|
|
|
|
|
.Where(t => t.TypeCode.Equals("after_port_trace_ctn_status", StringComparison.OrdinalIgnoreCase)).ToList();
|
|
|
|
|
|
|
|
|
|
//按顺序取状态配置字典
|
|
|
|
|
Dictionary<string, Tuple<string, string, int>> statusEnumDict = statusDict.Select(a => new { Key = a.Code, Val = new Tuple<string, string, int>(a.Value, a.Remark, a.Sort), Sort = a.Sort })
|
|
|
|
|
.OrderBy(a => a.Sort).ToDictionary(a => a.Key, b => b.Val);
|
|
|
|
|
|
|
|
|
|
string[] skipStatus = new string[] { "GIOI", "LOFV", "FVD", "FVA", "DFFV", "LOR", "DFR", "RA", "DIFR", "LOT", "DIFT", "ADI", "DIDI", "CGRL", "RFP", "ETD", "ETA", "LDI", "CDPOD", "BPOD" };
|
|
|
|
|
string[] transferStatus = new string[] { "AIP", "BIP", "ETDIP", "DIIP", "GOIP", "GIIP", "LIP", "EDIP", "DEIP", "EAIP" };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (queryRlt.code == 200)
|
|
|
|
|
{
|
|
|
|
|
var queryResult = queryRlt.data.FirstOrDefault();
|
|
|
|
|
|
|
|
|
|
result = queryResult.Adapt<EmbedTraceFlowResultDto>();
|
|
|
|
|
|
|
|
|
|
if (result != null && result.resultData != null)
|
|
|
|
|
{
|
|
|
|
|
//这里需要翻译一下箱型
|
|
|
|
|
var ctnCodeMappingList = _cache.GetAllMappingCtn().GetAwaiter().GetResult().ToList();
|
|
|
|
|
var ctnCodeList = _cache.GetAllCodeCtn().GetAwaiter().GetResult().ToList();
|
|
|
|
|
|
|
|
|
|
Dictionary<string, int> referToCtnDict = new Dictionary<string, int>();
|
|
|
|
|
|
|
|
|
|
bool isTransfer = false;
|
|
|
|
|
|
|
|
|
|
if (result.resultData.containerInfoList != null && result.resultData.containerInfoList.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
result.resultData.containerInfoList.ForEach(s =>
|
|
|
|
|
{
|
|
|
|
|
string ctnSize = s.size;
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(s.size))
|
|
|
|
|
{
|
|
|
|
|
ctnSize = $"{Regex.Match(s.size, "[0-9]{2}")}{Regex.Match(s.size, "[a-zA-Z]{1,3}([0-9]{1})?")}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var ctnCode = ctnCodeList.FirstOrDefault(t => !string.IsNullOrWhiteSpace(t.EdiCode) && t.EdiCode.Equals(ctnSize));
|
|
|
|
|
|
|
|
|
|
if (ctnCode != null)
|
|
|
|
|
{
|
|
|
|
|
s.sizeName = ctnCode.Name?.Trim();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var ctnMapping = ctnCodeMappingList.FirstOrDefault(t => !string.IsNullOrWhiteSpace(t.MapCode) && t.MapCode.Equals(ctnSize));
|
|
|
|
|
|
|
|
|
|
if (ctnMapping != null)
|
|
|
|
|
{
|
|
|
|
|
s.sizeName = ctnMapping.MapName?.Trim();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (s.containerStatusInfoList != null && s.containerStatusInfoList.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
referToCtnDict.Add(s.containerNo, s.containerStatusInfoList.Count);
|
|
|
|
|
|
|
|
|
|
s.containerStatusInfoList = s.containerStatusInfoList.Select((a, idx) =>
|
|
|
|
|
{
|
|
|
|
|
a.sortNo = idx + 1;
|
|
|
|
|
|
|
|
|
|
if (statusEnumDict.Any(p => p.Key.Equals(a.statusCd, StringComparison.OrdinalIgnoreCase)))
|
|
|
|
|
{
|
|
|
|
|
a.statusCnName = statusEnumDict[a.statusCd].Item1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
a.statusCnName = a.statusCd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return a;
|
|
|
|
|
}).ToList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//legType = "T" 标识当票有中转
|
|
|
|
|
if (result.resultData.routingInfoList.Any(t => t.legType.Equals("T", StringComparison.OrdinalIgnoreCase)))
|
|
|
|
|
{
|
|
|
|
|
isTransfer = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.embedTraceFlowStatusList = new List<EmbedTraceFlowStatusInfo>();
|
|
|
|
|
|
|
|
|
|
var ctnNo = referToCtnDict.OrderByDescending(a => a.Value).FirstOrDefault().Key;
|
|
|
|
|
|
|
|
|
|
var longContainerStatus = result.resultData.containerInfoList.FirstOrDefault(a => a.containerNo.Equals(ctnNo, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(longContainerStatus != null)
|
|
|
|
|
{
|
|
|
|
|
var longContainerStatusList = longContainerStatus.containerStatusInfoList;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var takeList = result.resultData.containerInfoList.SelectMany(p => p.containerStatusInfoList).GroupBy(a => a.statusCd).Select(a =>
|
|
|
|
|
{
|
|
|
|
|
var currArg = a.ToList();
|
|
|
|
|
|
|
|
|
|
EmbedTraceFlowStatusInfo currStatus = new EmbedTraceFlowStatusInfo
|
|
|
|
|
{
|
|
|
|
|
voy = a.FirstOrDefault().voy,
|
|
|
|
|
statusDescription = a.FirstOrDefault().statusDescription,
|
|
|
|
|
statusTime = a.FirstOrDefault().statusTime,
|
|
|
|
|
vslName = a.FirstOrDefault().vslName,
|
|
|
|
|
statusCd = a.FirstOrDefault().statusCd,
|
|
|
|
|
statusPlace = a.FirstOrDefault().statusPlace,
|
|
|
|
|
statusTerminal = a.FirstOrDefault().statusTerminal,
|
|
|
|
|
statusCnName = a.FirstOrDefault().statusCnName,
|
|
|
|
|
sortNo = a.FirstOrDefault().sortNo,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!currArg.Any(a => !a.isEst.Equals("N", StringComparison.OrdinalIgnoreCase)))
|
|
|
|
|
{
|
|
|
|
|
currStatus.isComplete = true;
|
|
|
|
|
currStatus.hasStatusCtnNum = currArg.Count;
|
|
|
|
|
currStatus.noStatusCtnNum = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
currStatus.isComplete = false;
|
|
|
|
|
currStatus.hasStatusCtnNum = currArg.Count(a => a.isEst.Equals("N", StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
currStatus.noStatusCtnNum = currArg.Count(a => !a.isEst.Equals("N", StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currStatus.statusCtnStatic = $"{currStatus.hasStatusCtnNum}/{(currStatus.hasStatusCtnNum + currStatus.noStatusCtnNum)}";
|
|
|
|
|
|
|
|
|
|
return currStatus;
|
|
|
|
|
}).ToList();
|
|
|
|
|
|
|
|
|
|
var APODtotal = takeList.Where(p => p.statusCd.Equals("APOD", StringComparison.OrdinalIgnoreCase)).ToList();
|
|
|
|
|
|
|
|
|
|
if (APODtotal.Count > 1 && APODtotal.Any(p => p.isComplete))
|
|
|
|
|
{
|
|
|
|
|
takeList = takeList.Select(a =>
|
|
|
|
|
{
|
|
|
|
|
if (a.statusCd.Equals("APOD", StringComparison.OrdinalIgnoreCase) && !a.isComplete)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
return a;
|
|
|
|
|
}).Where(a => a != null).ToList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (KeyValuePair<string, Tuple<string, string, int>> kvp in statusEnumDict)
|
|
|
|
|
{
|
|
|
|
|
if (!skipStatus.Contains(kvp.Key))
|
|
|
|
|
{
|
|
|
|
|
//不是中转的需要把状态节点去掉
|
|
|
|
|
if (isTransfer || (!isTransfer && !transferStatus.Contains(kvp.Key)))
|
|
|
|
|
{
|
|
|
|
|
var currStatus = takeList.FirstOrDefault(p => p.statusCd.Equals(kvp.Key, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
|
|
|
|
if (currStatus == null)
|
|
|
|
|
{
|
|
|
|
|
currStatus = new EmbedTraceFlowStatusInfo
|
|
|
|
|
{
|
|
|
|
|
statusCd = kvp.Key,
|
|
|
|
|
statusCnName = !string.IsNullOrWhiteSpace(kvp.Value.Item2) ? kvp.Value.Item2 : kvp.Value.Item1,
|
|
|
|
|
sortNo = kvp.Value.Item3,
|
|
|
|
|
statusCtnStatic = $"0/{referToCtnDict.Count}",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(kvp.Value.Item2))
|
|
|
|
|
currStatus.statusCnName = kvp.Value.Item2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (currStatus.statusCd.Equals("DPOL", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
currStatus.isSplitStart = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (currStatus.statusCd.Equals("APOD", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
currStatus.isSplitEnd = true;
|
|
|
|
|
|
|
|
|
|
if (longContainerStatus != null && !string.IsNullOrWhiteSpace(longContainerStatus.currentNodeCd) && longContainerStatus.currentNodeCd.Equals(kvp.Key, StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
currStatus.isCurrentStatus = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.embedTraceFlowStatusList.Add(currStatus);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
result.resultData.containerInfoList.ForEach(b =>
|
|
|
|
|
{
|
|
|
|
|
if (statusEnumDict.Any(k => !string.IsNullOrWhiteSpace(b.currentNodeCd) && k.Key.Equals(b.currentNodeCd, StringComparison.OrdinalIgnoreCase)))
|
|
|
|
|
{
|
|
|
|
|
b.currentNodeCnName = !string.IsNullOrWhiteSpace(statusEnumDict[b.currentNodeCd].Item2) ? statusEnumDict[b.currentNodeCd].Item2 : statusEnumDict[b.currentNodeCd].Item1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b.containerStatusInfoList = b.containerStatusInfoList.Select(c =>
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(b.currentNodeCd) && b.currentNodeCd.Equals(c.statusCd, StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
c.isCurrentStatus = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(c.statusPlace))
|
|
|
|
|
c.statusPlace = c.statusPlace.ToUpper();
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(c.statusCd) && c.statusCd.Equals("ETA", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(c.statusCd) && c.statusCd.Equals("APOD", StringComparison.OrdinalIgnoreCase) && !c.isEst.Equals("N", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return c;
|
|
|
|
|
|
|
|
|
|
}).Where(a => a != null).ToList();
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation($"{JSON.Serialize(queryDto)} 查询成功");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation($"{JSON.Serialize(queryDto)} 查询失败,{result.resultMessage}");
|
|
|
|
|
|
|
|
|
|
throw Oops.Oh(result.resultMessage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation($"{JSON.Serialize(queryDto)} 查询失败,{queryRlt.msg}");
|
|
|
|
|
|
|
|
|
|
throw Oops.Oh((result != null && result.resultMessage != null && result.resultMessage.Contains("官网查询无数据")) ? "无查询结果" : queryRlt.msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region 查询运踪
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 查询运踪
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="query">查询参数</param>
|
|
|
|
|
/// <param name="url">请求URL</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public async Task<QueryTraceAfterPortResultDto> QueryTraceAsync(QueryTraceAfterPortDto query,string url)
|
|
|
|
|
{
|
|
|
|
|
QueryTraceAfterPortResultDto model = null;
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
var res = await url.OnClientCreating(client => {
|
|
|
|
|
// client 为 HttpClient 对象
|
|
|
|
|
client.Timeout = TimeSpan.FromMinutes(15); // 设置超时时间 15分钟
|
|
|
|
|
}).SetHttpMethod(HttpMethod.Post)
|
|
|
|
|
.SetBody(JSON.Serialize(query), "application/json")
|
|
|
|
|
.SetContentEncoding(Encoding.UTF8)
|
|
|
|
|
.PostAsync();
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("单号={no} 对应请求报文完成 post={post} res={res}", query.referenceno, JSON.Serialize(query), JSON.Serialize(res));
|
|
|
|
|
|
|
|
|
|
if (res.StatusCode == System.Net.HttpStatusCode.OK)
|
|
|
|
|
{
|
|
|
|
|
var userResult = await res.Content.ReadAsStringAsync();
|
|
|
|
|
|
|
|
|
|
model = JSON.Deserialize<QueryTraceAfterPortResultDto>(userResult);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogInformation($"请求运踪查询异常,原因:{ex.Message}");
|
|
|
|
|
|
|
|
|
|
throw Oops.Oh($"请求运踪查询异常,原因:{ex.Message}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return model;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|