admin/src/Oms.Application/Messaging/OrderSideEffects/OrderSideEffectEventPublisher.cs
zzh dcd985bca4 feat: 创单/出入库后置副作用接入 Fake.EventBus,预报计费排货走 MQ
将轨迹、面单、9610、揽收、自动触发与 ForecastBilling 异步化,缩短扫码与下单 RT;队列未启用时同步回落。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-10 10:54:24 +08:00

113 lines
3.4 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 Fake.EventBus.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Oms.Application.Services.Orders.Issues;
namespace Oms.Application.Messaging.OrderSideEffects;
/// <summary>
/// 优先 Fake.EventBus.RabbitMQ;发布失败则同进程同步执行 Handler。
/// </summary>
public sealed class OrderSideEffectEventPublisher(
IDistributedEventBus eventBus,
IServiceScopeFactory scopeFactory,
ILogger<OrderSideEffectEventPublisher> logger) : IOrderSideEffectEventPublisher
{
public async Task PublishLifecycleTrackAppendAsync(
int orderId,
string orderNo,
string trackCode,
string? authorization = null,
CancellationToken cancellationToken = default)
{
if (orderId <= 0 || string.IsNullOrWhiteSpace(orderNo) || string.IsNullOrWhiteSpace(trackCode))
{
return;
}
var @event = new OrderLifecycleTrackAppendIntegrationEvent
{
OrderId = orderId,
OrderNo = orderNo.Trim(),
TrackCode = trackCode.Trim(),
Authorization = string.IsNullOrWhiteSpace(authorization) ? null : authorization.Trim()
};
if (await TryPublishAsync(@event, cancellationToken))
{
return;
}
await ExecuteInScopeAsync<OrderLifecycleTrackAppendEventHandler>(
h => h.HandleAsync(@event, cancellationToken),
cancellationToken);
}
public async Task PublishAutoTriggerFireAsync(
int orderId,
AutoTriggerTiming timing,
int operatorUserId,
CancellationToken cancellationToken = default)
{
if (orderId <= 0)
{
return;
}
var @event = new OrderAutoTriggerFireIntegrationEvent
{
OrderId = orderId,
Timing = timing.ToString(),
OperatorUserId = operatorUserId
};
if (await TryPublishAsync(@event, cancellationToken))
{
return;
}
await ExecuteInScopeAsync<OrderAutoTriggerFireEventHandler>(
h => h.HandleAsync(@event, cancellationToken),
cancellationToken);
}
private async Task<bool> TryPublishAsync(IntegrationEvent @event, CancellationToken cancellationToken)
{
try
{
await eventBus.PublishAsync(@event, cancellationToken);
logger.LogDebug(
"Published side-effect event {EventName} Id={EventId}",
@event.GetType().Name,
@event.Id);
return true;
}
catch (Exception ex)
{
logger.LogWarning(
ex,
"Publish side-effect event {EventName} failed, fallback to sync. EventId={EventId}",
@event.GetType().Name,
@event.Id);
return false;
}
}
private async Task ExecuteInScopeAsync<THandler>(
Func<THandler, Task> action,
CancellationToken cancellationToken)
where THandler : notnull
{
try
{
using var scope = scopeFactory.CreateScope();
var handler = scope.ServiceProvider.GetRequiredService<THandler>();
await action(handler);
}
catch (Exception ex)
{
logger.LogWarning(ex, "Sync fallback for {Handler} failed", typeof(THandler).Name);
}
}
}