admin/src/Oms.Application/Services/Orders/OrderService.BatchJob.cs
zzh fa9777bd08
All checks were successful
Build and Deploy / deploy (push) Successful in 59s
fix: SqlSugar 与 EF Outbox 连接冲突时回落短事务
发布集成事件前先挂 Outbox 上下文;连接不匹配时 RequiresNew 写 Outbox,避免预报等按钮报 transaction not associated。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-10 19:01:15 +08:00

236 lines
9.1 KiB
C#

using System.Data;
using Fake.Application;
using Fake.Security.Claims;
using Fake.UnitOfWork;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Oms.Application.Dtos.Orders;
using Oms.Application.Dtos.Shared;
using Oms.Application.Services.Orders.Batch;
using Oms.Application.Messaging;
using Oms.Application.Shared;
using Oms.Domain.CustomersAggregate.Repositories;
using Oms.Domain.OrdersAggregate.Consts;
namespace Oms.Application.Services.Orders;
public partial class OrderService
{
private OrderBatchJobCoordinator BatchJobCoordinator =>
LazyServiceProvider.GetRequiredService<OrderBatchJobCoordinator>();
/// <summary>
/// 创建订单批量异步任务并入队 RabbitMQ。
/// </summary>
[HttpPost("/order/batch/jobs")]
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
public Task<OrderBatchJobOutput> CreateBatchJobAsync([FromBody] OrderBatchJobCreateInput input)
{
return BatchJobCoordinator.CreateAndEnqueueAsync(input, CurrentUser.GetId<int>());
}
/// <summary>
/// 查询批量任务进度。
/// </summary>
[HttpGet("/order/batch/jobs/{id:int}")]
public async Task<OrderBatchJobOutput> GetBatchJobAsync([FromRoute] int id)
{
var job = await BatchJobCoordinator.GetJobAsync(id);
if (job == null)
{
throw new BusinessException("批量任务不存在");
}
return job;
}
/// <summary>
/// 分页查询批量任务列表。
/// </summary>
[HttpGet("/order/batch/jobs")]
public async Task<OmsPageResult<OrderBatchJobOutput>> GetBatchJobListAsync([FromQuery] OrderBatchJobListInput input)
{
var customerId = await OmsSessionHelper.ResolveListCustomerIdAsync(
LazyServiceProvider.GetRequiredService<ICustomerRepository>(),
CurrentUser,
null);
return await BatchJobCoordinator.GetJobListAsync(input, customerId);
}
/// <summary>
/// 查询批量任务子项明细。
/// </summary>
[HttpGet("/order/batch/jobs/{id:int}/items")]
public async Task<List<OrderBatchJobItemOutput>> GetBatchJobItemsAsync([FromRoute] int id)
{
var job = await BatchJobCoordinator.GetJobEntityAsync(id);
if (job == null)
{
throw new BusinessException("批量任务不存在");
}
return await BatchJobCoordinator.GetJobItemsAsync(id);
}
/// <summary>
/// 异步批量创建轨迹订单(替代前端逐条调用)。
/// </summary>
[HttpPost("/order/batch/track-upsert")]
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
public Task<OrderBatchJobOutput> BatchTrackUpsertAsync([FromBody] OrderBatchJobCreateInput input)
{
input.Action = OrderBatchActionCodes.TrackUpsertSmallBag;
var authorization = LazyServiceProvider
.GetRequiredService<IHttpContextAccessor>()
.HttpContext?
.Request
.Headers
.Authorization
.ToString();
return BatchJobCoordinator.CreateAndEnqueueAsync(
input,
CurrentUser.GetId<int>(),
string.IsNullOrWhiteSpace(authorization) ? null : authorization);
}
/// <summary>
/// 异步批量预报下游订单。
/// </summary>
[HttpPost("/order/batch/downstream-forward")]
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
public Task<OrderBatchJobOutput> BatchDownstreamForwardAsync([FromBody] OrderBatchJobCreateInput input)
{
input.Action = OrderBatchActionCodes.DownstreamForwardCreateOne;
var authorization = LazyServiceProvider
.GetRequiredService<IHttpContextAccessor>()
.HttpContext?
.Request
.Headers
.Authorization
.ToString();
return BatchJobCoordinator.CreateAndEnqueueAsync(
input,
CurrentUser.GetId<int>(),
string.IsNullOrWhiteSpace(authorization) ? null : authorization);
}
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
private Task<OrderBatchJobOutput> EnqueueOrderBatchAsync(string action, OrderBatchJobCreateInput input)
{
input.Action = action;
return BatchJobCoordinator.CreateAndEnqueueAsync(input, CurrentUser.GetId<int>());
}
/// <summary>
/// 异步批量支付订单。
/// </summary>
[HttpPost("/order/batch/pay")]
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
public Task<OrderBatchJobOutput> BatchPayAsync([FromBody] OrderBatchJobCreateInput input) =>
EnqueueOrderBatchAsync(OrderBatchActionCodes.Pay, input);
/// <summary>
/// 异步批量预报订单。
/// </summary>
[HttpPost("/order/batch/pre-declare")]
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
public Task<OrderBatchJobOutput> BatchPreDeclareAsync([FromBody] OrderBatchJobCreateInput input) =>
EnqueueOrderBatchAsync(OrderBatchActionCodes.PreDeclare, input);
/// <summary>
/// 异步批量拦截订单。
/// </summary>
[HttpPost("/order/batch/intercept")]
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
public Task<OrderBatchJobOutput> BatchInterceptAsync([FromBody] OrderBatchJobCreateInput input) =>
EnqueueOrderBatchAsync(OrderBatchActionCodes.Intercept, input);
/// <summary>
/// 异步批量取消拦截。
/// </summary>
[HttpPost("/order/batch/cancel-intercept")]
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
public Task<OrderBatchJobOutput> BatchCancelInterceptAsync([FromBody] OrderBatchJobCreateInput input) =>
EnqueueOrderBatchAsync(OrderBatchActionCodes.CancelIntercept, input);
/// <summary>
/// 异步批量删除订单。
/// </summary>
[HttpPost("/order/batch/delete")]
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
public Task<OrderBatchJobOutput> BatchDeleteAsync([FromBody] OrderBatchJobCreateInput input) =>
EnqueueOrderBatchAsync(OrderBatchActionCodes.Delete, input);
/// <summary>
/// 异步批量打印面单。
/// </summary>
[HttpPost("/order/batch/print")]
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
public Task<OrderBatchJobOutput> BatchPrintAsync([FromBody] OrderBatchJobCreateInput input) =>
EnqueueOrderBatchAsync(OrderBatchActionCodes.Print, input);
/// <summary>
/// 异步批量标记问题件。
/// </summary>
[HttpPost("/order/batch/mark-problematic")]
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
public Task<OrderBatchJobOutput> BatchMarkProblematicAsync([FromBody] OrderBatchJobCreateInput input) =>
EnqueueOrderBatchAsync(OrderBatchActionCodes.MarkProblematic, input);
/// <summary>
/// 异步批量重新排货。
/// </summary>
[HttpPost("/order/batch/reallocate")]
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
public Task<OrderBatchJobOutput> BatchReallocateAsync([FromBody] OrderBatchJobCreateInput input) =>
EnqueueOrderBatchAsync(OrderBatchActionCodes.Reallocate, input);
/// <summary>
/// 异步批量标记已认领。
/// </summary>
[HttpPost("/order/batch/mark-claimed")]
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
public Task<OrderBatchJobOutput> BatchMarkClaimedAsync([FromBody] OrderBatchJobCreateInput input) =>
EnqueueOrderBatchAsync(OrderBatchActionCodes.MarkClaimed, input);
/// <summary>
/// 异步批量更新处理状态。
/// </summary>
[HttpPost("/order/batch/handle-status-update")]
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
public Task<OrderBatchJobOutput> BatchHandleStatusUpdateAsync([FromBody] OrderBatchJobCreateInput input) =>
EnqueueOrderBatchAsync(OrderBatchActionCodes.HandleStatusUpdate, input);
/// <summary>
/// 查询 RabbitMQ 队列与 DLX 堆积情况。
/// </summary>
[HttpGet("/order/batch/dlx/stats")]
public Task<OrderBatchDlxStatsOutput> GetBatchDlxStatsAsync()
{
var inspector = LazyServiceProvider.GetRequiredService<IOrderBatchQueueInspector>();
return inspector.GetStatsAsync();
}
/// <summary>
/// 重新投递失败子项。
/// </summary>
[HttpPost("/order/batch/jobs/{id:int}/retry-failed")]
public Task<OrderBatchJobManageOutput> RetryFailedBatchJobAsync(
[FromRoute] int id,
[FromBody] OrderBatchJobRetryFailedInput? input)
{
return BatchJobCoordinator.RetryFailedAsync(id, input, CurrentUser.GetId<int>());
}
/// <summary>
/// 取消尚未执行的 pending 子项。
/// </summary>
[HttpPost("/order/batch/jobs/{id:int}/cancel")]
public Task<OrderBatchJobManageOutput> CancelBatchJobAsync([FromRoute] int id)
{
return BatchJobCoordinator.CancelPendingAsync(id, CurrentUser.GetId<int>());
}
}