334 lines
13 KiB
C#
334 lines
13 KiB
C#
using System.Diagnostics;
|
||
using System.Text;
|
||
using DotNetCore.CAP;
|
||
using Fake.DependencyInjection;
|
||
using InterfaceForward.Domain.Shared.Helpers;
|
||
using Microsoft.Extensions.Logging;
|
||
using Newtonsoft.Json;
|
||
using Newtonsoft.Json.Linq;
|
||
using RabbitMQ.Client.Events;
|
||
using TrackCenter.Application.Dtos.TrackPull;
|
||
using TrackCenter.Application.Events;
|
||
using TrackCenter.Application.Helpers;
|
||
using TrackCenter.Application.Services.TrackPull;
|
||
using TrackCenter.Domain.Aggregates.OrderAggregate;
|
||
using TrackCenter.Domain.Aggregates.TrackDetailsAggregate;
|
||
using TrackCenter.Domain.Enums;
|
||
|
||
namespace TrackCenter.Application.Rabbit;
|
||
|
||
public class TrackRefreshEventHandler : ITransientDependency, IRabbitHandler
|
||
{
|
||
private readonly ICapPublisher _capPublisher;
|
||
private readonly ILogger<TrackRefreshEventHandler> _logger;
|
||
private readonly IOrderRepository _orderRepository;
|
||
private readonly ITrackDetailsEsRepository _trackDetailsEsRepository;
|
||
private readonly TrackPullCacheService _trackPullCacheService;
|
||
|
||
public TrackRefreshEventHandler(IOrderRepository orderRepository,
|
||
ICapPublisher capPublisher, TrackPullCacheService trackPullCacheService,
|
||
ITrackDetailsEsRepository trackDetailsEsRepository,
|
||
ILogger<TrackRefreshEventHandler> logger)
|
||
{
|
||
_orderRepository = orderRepository;
|
||
_capPublisher = capPublisher;
|
||
_trackPullCacheService = trackPullCacheService;
|
||
_trackDetailsEsRepository = trackDetailsEsRepository;
|
||
_logger = logger;
|
||
}
|
||
|
||
public ConsumeOptions Options()
|
||
{
|
||
return new ConsumeOptions()
|
||
{
|
||
Queue = GlobalConst.TrackRefreshSubscribe,
|
||
};
|
||
}
|
||
|
||
public async Task Handle(IServiceProvider sp, BasicDeliverEventArgs args, ConsumeOptions options)
|
||
{
|
||
var msg = Encoding.UTF8.GetString(args.Body.ToArray());
|
||
var @event = System.Text.Json.JsonSerializer.Deserialize<InterfaceRelayForwardReplayEvent>(msg);
|
||
if (@event == null)
|
||
{
|
||
_logger.LogWarning(
|
||
$"rabbit on queue({options.Queue}) received msg can't convert to {nameof(InterfaceRelayForwardReplayEvent)}");
|
||
return;
|
||
}
|
||
|
||
await ConsumerAsync(@event);
|
||
}
|
||
|
||
public async Task ConsumerAsync(InterfaceRelayForwardReplayEvent @event)
|
||
{
|
||
if (!@event.IsSuccess)
|
||
{
|
||
_logger.LogWarning("请求id: {@RequestId},轨迹刷新失败", @event.RequestId);
|
||
return;
|
||
}
|
||
|
||
var attach = Base64Helper.DeSerialize<TrackRefreshAttach>(@event.Attach);
|
||
if (attach is null || @event.Data == null)
|
||
{
|
||
_logger.LogWarning("请求id: {@RequestId},轨迹消费失败,attach或data解析为空", @event.RequestId);
|
||
return;
|
||
}
|
||
|
||
var config = await _trackPullCacheService.GetPullParamsWithContentAsync(attach.SubSystemId,
|
||
attach.ServiceProviderCode);
|
||
if (config == null)
|
||
{
|
||
_logger.LogWarning("请求id: {@RequestId},轨迹消费失败,找不到拉取配置:{@Attach}", attach.ToJson(), @event.RequestId);
|
||
return;
|
||
}
|
||
|
||
var data = JObject.Parse(@event.Data);
|
||
|
||
var orderToken = data.SelectToken(config.ParamConfig.OrderPath);
|
||
if (orderToken is JArray orderTokens)
|
||
{
|
||
foreach (var token in orderTokens)
|
||
{
|
||
await HandleOrderAsync(token, config, attach);
|
||
}
|
||
}
|
||
else if (orderToken is JObject)
|
||
{
|
||
await HandleOrderAsync(orderToken, config, attach);
|
||
}
|
||
}
|
||
|
||
private async Task HandleOrderAsync(JToken orderToken, TrackPullParamsWithContentDto paramsWithContent,
|
||
TrackRefreshAttach attach)
|
||
{
|
||
var paramConfig = paramsWithContent.ParamConfig;
|
||
var orderNo = orderToken[paramConfig.OrderNoName]?.Value<string>();
|
||
if (orderNo.IsNullOrWhiteSpace()) return;
|
||
|
||
var trackToken = orderToken.SelectToken(paramConfig.TrackDetailsPath);
|
||
if (trackToken is null) return;
|
||
|
||
//回传单号可能是主单也可能是外部单号
|
||
//通过平台code和(外部单号/主单号)查询主单
|
||
var order = await _orderRepository.GetTrackOrderByProviderAndOrderNo(attach.ServiceProviderCode, orderNo!);
|
||
if (order == default)
|
||
{
|
||
_logger.LogError($"请求id: {attach.ToJson()},轨迹消费失败,找不到主单,平台code:{attach.ServiceProviderCode},单号:{orderNo}");
|
||
return;
|
||
}
|
||
|
||
var context = new TrackRefreshContext
|
||
{
|
||
OrderId = order.MainOrderId,
|
||
OrderNo = order.OrderNo,
|
||
CustomId = order.CustomId,
|
||
OrderType = attach.OrderType,
|
||
SubSystemId = attach.SubSystemId,
|
||
ServiceProviderCode = attach.ServiceProviderCode
|
||
};
|
||
|
||
|
||
if (trackToken is JArray trackTokens)
|
||
{
|
||
foreach (var item in trackTokens)
|
||
{
|
||
await HandleTrackAsync(item, paramsWithContent, context);
|
||
}
|
||
}
|
||
else if (trackToken is JObject)
|
||
{
|
||
await HandleTrackAsync(trackToken, paramsWithContent, context);
|
||
}
|
||
}
|
||
|
||
private async Task HandleTrackAsync(JToken trackToken, TrackPullParamsWithContentDto paramsWithContent,
|
||
TrackRefreshContext context)
|
||
{
|
||
var originTrackDataHash = StringHelper.GetHash(JsonConvert.SerializeObject(trackToken));
|
||
// 如果已经存在一模一样的报文了说明处理过了,直接return
|
||
if (await _trackDetailsEsRepository.AnyAsync(context.OrderId, originTrackDataHash))
|
||
{
|
||
return;
|
||
}
|
||
|
||
var paramConfig = paramsWithContent.ParamConfig;
|
||
var contentConfigs = paramsWithContent.ContentConfigs;
|
||
TrackPullContentConfigDto? matchedContentConfig = null;
|
||
|
||
// 轨迹内容
|
||
var trackContent = paramConfig.TrackContentName == null
|
||
? null
|
||
: trackToken[paramConfig.TrackContentName]?.Value<string>();
|
||
|
||
// 1、匹配轨迹代码
|
||
|
||
// 2、匹配轨迹状态
|
||
if (matchedContentConfig == null && !paramConfig.TrackStatusName.IsNullOrWhiteSpace())
|
||
{
|
||
var trackStatus = trackToken[paramConfig.TrackStatusName!]?.Value<string>();
|
||
if (!trackStatus.IsNullOrWhiteSpace())
|
||
{
|
||
matchedContentConfig = contentConfigs.FirstOrDefault(x => x.TrackStatusValues.Contains(trackStatus!));
|
||
}
|
||
}
|
||
|
||
// 3、匹配轨迹内容
|
||
if (matchedContentConfig == null && !paramConfig.TrackContentName.IsNullOrWhiteSpace())
|
||
{
|
||
if (!trackContent.IsNullOrWhiteSpace())
|
||
{
|
||
Debug.Assert(trackContent != null, nameof(trackContent) + " != null");
|
||
// 3.1 按内容直接匹配
|
||
|
||
// 3.2 按关键字分析轨迹内容
|
||
foreach (var contentConfig in contentConfigs)
|
||
{
|
||
foreach (var keyword in contentConfig.TrackKeywords)
|
||
{
|
||
if (trackContent.Contains(keyword, StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
matchedContentConfig = contentConfig;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (matchedContentConfig is null) continue;
|
||
|
||
foreach (var keyword in contentConfig.TrackKeywordReverses)
|
||
{
|
||
if (trackContent.Contains(keyword, StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
matchedContentConfig = null;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (matchedContentConfig is not null) break; // 找到匹配的,跳出循环
|
||
}
|
||
}
|
||
}
|
||
|
||
// 轨迹发生ISO时间
|
||
var trackIsoTime = paramConfig.TrackIsoTimeName == null
|
||
? null
|
||
: trackToken[paramConfig.TrackIsoTimeName]?.ToObject<DateTimeOffset>();
|
||
// 轨迹发生时间
|
||
var trackTime = trackIsoTime?.DateTime ?? (paramConfig.TrackTimeName == null
|
||
? null
|
||
: trackToken[paramConfig.TrackTimeName]?.ToObject<DateTime>());
|
||
// 轨迹发生时区
|
||
var trackTimeZone = (float?) trackIsoTime?.Offset.TotalHours ?? (paramConfig.TrackTimeZoneName == null
|
||
? null
|
||
: trackToken[paramConfig.TrackTimeZoneName]?.ToObject<float>());
|
||
// 轨迹发生地
|
||
var trackPlace = paramConfig.TrackPlaceName == null
|
||
? null
|
||
: trackToken[paramConfig.TrackPlaceName]?.ToObject<string>();
|
||
|
||
|
||
if (matchedContentConfig is null)
|
||
{
|
||
_logger.LogDebug("订单号:{OrderNo},未匹配到轨迹内容,轨迹报文:{TrackContent}",
|
||
context.OrderNo, trackToken.ToString());
|
||
await InsertNotMappedTrackAsync(trackToken, context, trackPlace, trackTime, trackTimeZone, trackContent);
|
||
return;
|
||
}
|
||
|
||
// 新增/更新轨迹
|
||
var trackUpsertEvent = new TrackUpsertEvent
|
||
{
|
||
OrderIdentifiers =
|
||
new[]
|
||
{
|
||
new OrderIdentifier
|
||
{
|
||
OrderId = context.OrderId,
|
||
OrderNo = context.OrderNo,
|
||
OrderType = context.OrderType,
|
||
}
|
||
},
|
||
TrackRefreshData = new TrackRefreshStruct
|
||
{
|
||
ServiceProviderCode = context.ServiceProviderCode,
|
||
OriginTrackDataHash = originTrackDataHash,
|
||
OriginTrackContent = trackContent ?? GlobalConst.EmptyString,
|
||
},
|
||
SubSystemId = context.SubSystemId,
|
||
CustomId = context.CustomId,
|
||
TrackCode = matchedContentConfig.TrackContentCode,
|
||
TrackPlace = trackPlace ?? GlobalConst.EmptyString,
|
||
TrackTime = trackTime ?? DateTime.Now,
|
||
TrackTimeZone = trackTimeZone ?? GlobalConst.NoneTrackTimeZone,
|
||
IsAdd = true,
|
||
//服务商轨迹默认不对外
|
||
IsExternal = false,
|
||
Source = (int) TrackSourceEnum.ExternalServiceProvide,
|
||
Remarks = null,
|
||
Files = null,
|
||
Content = null,
|
||
EnStatus = null,
|
||
TrackPlaceCity = null,
|
||
TrackPlaceCityId = null,
|
||
TrackPlaceProvince = null,
|
||
TrackPlaceProvinceId = null,
|
||
TrackPlaceNation = null,
|
||
TrackPlaceNationId = null,
|
||
TrackPlacePostcode = null,
|
||
DestinationTimeZone = null,
|
||
EstimatedDeliveryDate = null,
|
||
};
|
||
|
||
await _capPublisher.PublishAsync(GlobalConst.TrackUpsertForRefreshSubscribe, trackUpsertEvent);
|
||
}
|
||
|
||
private async Task InsertNotMappedTrackAsync(JToken trackToken, TrackRefreshContext context, string? trackPlace,
|
||
DateTime? trackTime, float? trackTimeZone, string? trackContent)
|
||
{
|
||
// 新增服务商轨迹到es
|
||
var track = new TrackDetailsIndex
|
||
{
|
||
IndexName = $"{GlobalConst.TrackDetailsIndex}-{DateTime.Now:yyyy.MM}",
|
||
SubSystemId = context.SubSystemId,
|
||
OrderId = context.OrderId,
|
||
OrderType = context.OrderType,
|
||
OrderNo = context.OrderNo,
|
||
TrackPlace = trackPlace ?? GlobalConst.EmptyString,
|
||
TrackTime = trackTime ?? DateTime.Now,
|
||
TrackTimeZone = trackTimeZone ?? GlobalConst.NoneTrackTimeZone,
|
||
ServiceProviderCode = context.ServiceProviderCode,
|
||
OriginTrackDataHash = StringHelper.GetHash(JsonConvert.SerializeObject(trackToken)),
|
||
OriginTrackContent = trackContent ?? GlobalConst.EmptyString,
|
||
CreateUser = GlobalConst.SystemUser,
|
||
CreateTime = DateTime.Now,
|
||
//来源轨迹内容,第一次新增的时候使用轨迹内容表提供的
|
||
TrackStatusId = GlobalConst.NotExistId,
|
||
TrackStatus = GlobalConst.EmptyString,
|
||
TrackStatusCode = GlobalConst.EmptyString,
|
||
TrackContentId = GlobalConst.NotExistId,
|
||
TrackContent = GlobalConst.EmptyString,
|
||
TrackEnContent = GlobalConst.EmptyString,
|
||
//服务商轨迹默认不对外
|
||
IsExternal = false,
|
||
Source = (int) TrackSourceEnum.ExternalServiceProvide,
|
||
Remarks = null,
|
||
Files = null,
|
||
TrackPlaceCity = null,
|
||
TrackPlaceCityId = null,
|
||
TrackPlaceProvince = null,
|
||
TrackPlaceProvinceId = null,
|
||
TrackPlaceNation = null,
|
||
TrackPlaceNationId = null,
|
||
TrackPlacePostcode = null,
|
||
DestinationTimeZone = null,
|
||
EstimatedDeliveryDate = null,
|
||
Extra = null
|
||
};
|
||
|
||
track.ZeroTrackTime = Math.Abs(track.TrackTimeZone - GlobalConst.NoneTrackTimeZone) < 1
|
||
? track.TrackTime
|
||
: track.TrackTime.Subtract(TimeSpan.FromHours(track.TrackTimeZone));
|
||
|
||
await _trackDetailsEsRepository.AddAsync(track);
|
||
}
|
||
}
|