track/src/TrackCenter.Application/Rabbit/TrackRefreshEventHandler.cs
2025-07-14 15:01:31 +08:00

334 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
}
}