admin/src/Oms.Application/Services/Orders/OrderService.BatchWorker.cs
zzh 03f11a276f Add order attachment files table and move system labels off Order.
Persist generated/printed/downstream labels in t_order_file, drop Order.LabelUrl generate-status fields, and expose file list/upload/delete APIs with customer read-only systemLabel access.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-28 16:32:55 +08:00

177 lines
6.7 KiB
C#

using Fake.Application;
using Newtonsoft.Json;
using Oms.Application.Dtos.Orders;
using Oms.Application.Labeling;
using Oms.Application.Services.Orders.Issues;
using Oms.Domain.OrdersAggregate.Consts;
using Oms.Domain.OrdersAggregate.Entities;
namespace Oms.Application.Services.Orders;
public partial class OrderService
{
internal Task PayForBatchAsync(int orderId, CancellationToken cancellationToken = default) =>
PayAsync(new OrderActionInput { Id = orderId });
internal Task PreDeclareForBatchAsync(int orderId, CancellationToken cancellationToken = default) =>
PreDeclareAsync(new OrderActionInput { Id = orderId });
internal Task InterceptForBatchAsync(int orderId, CancellationToken cancellationToken = default) =>
InterceptAsync(new OrderActionInput { Id = orderId });
internal Task CancelInterceptForBatchAsync(int orderId, CancellationToken cancellationToken = default) =>
CancelInterceptAsync(new OrderActionInput { Id = orderId });
internal Task DeleteForBatchAsync(int orderId, CancellationToken cancellationToken = default) =>
DeleteAsync(new OrderActionInput { Id = orderId });
internal Task<OrderPrintOutput> PrintForBatchAsync(int orderId, CancellationToken cancellationToken = default) =>
PrintAsync(new OrderActionInput { Id = orderId });
internal async Task<LabelPdfGenerateResult> GenerateLabelPdfForBatchAsync(
int orderId,
CancellationToken cancellationToken = default)
{
var order = await GetOrderOrThrowAsync(orderId);
var mapped = (await MapOrderOutputsAsync([order])).First();
return await LabelPdfGenerationService.GenerateAndPersistAsync(
order,
mapped,
templateIdOverride: null,
operatorName: null,
operatorUserId: 0,
cancellationToken);
}
internal async Task MarkProblematicForBatchAsync(
int orderId,
string? payloadJson,
int operatorUserId,
CancellationToken cancellationToken = default)
{
var input = DeserializeBatchPayload<OrderMarkProblematicInput>(payloadJson)
?? throw new BusinessException("问题件参数无效");
input.OrderIds = [orderId];
var payload = await ResolveProblematicPayloadAsync(input);
var request = new ProblematicMarkRequest
{
IssueType = payload.IssueType,
Title = payload.Title,
Content = payload.Content,
IssueTemplateId = payload.IssueTemplateId,
NotifyMethod = payload.NotifyMethod,
Remark = payload.Remark,
};
var order = await GetOrderOrThrowAsync(orderId);
var sendNotification = !string.IsNullOrWhiteSpace(request.NotifyMethod);
var reason = await ExceptionWorkflowService.TryMarkProblematicAsync(
order,
request,
operatorUserId,
sendNotification);
if (!string.IsNullOrWhiteSpace(reason))
{
throw new BusinessException(reason);
}
}
internal async Task MarkClaimedForBatchAsync(
int orderId,
string? payloadJson,
CancellationToken cancellationToken = default)
{
var input = DeserializeBatchPayload<OrderMarkClaimedInput>(payloadJson)
?? throw new BusinessException("理赔件参数无效");
var payload = NormalizeClaimedPayload(input);
var order = await GetOrderOrThrowAsync(orderId);
if (!OrderExceptionStatusRules.CanMarkException(order.OrderStatus))
{
throw new BusinessException(OrderExceptionStatusRules.GetForbiddenReason(order.OrderStatus));
}
var fromStatus = order.OrderStatus;
order.PreviousOrderStatus = fromStatus;
order.OrderStatus = OrderStatusCodes.Claimed;
order.HandleStatus = OrderHandleStatusCodes.Pending;
order.ClaimType = payload.ClaimType;
order.ClaimAmount = payload.ClaimAmount;
order.ClaimCurrency = payload.ClaimCurrency;
order.ClaimReason = payload.ClaimReason;
order.ClaimTime = DateTime.Now;
order.IssueType = null;
order.IssueTitle = null;
order.IssueContent = null;
order.IssueTemplateId = null;
order.IssueRemark = null;
order.IssueTime = null;
await OrderRepository.UpdateAsync(order);
await OrderExceptionEventRepository.InsertAsync(new OrderExceptionEvent
{
OrderId = order.Id,
EventType = OrderExceptionEventTypeCodes.MarkClaimed,
FromStatus = fromStatus,
ToStatus = OrderStatusCodes.Claimed,
FromHandleStatus = null,
ToHandleStatus = OrderHandleStatusCodes.Pending,
ClaimType = payload.ClaimType,
ClaimAmount = payload.ClaimAmount,
ClaimCurrency = payload.ClaimCurrency,
ClaimReason = payload.ClaimReason,
Remark = payload.Remark,
});
}
internal async Task UpdateHandleStatusForBatchAsync(
int orderId,
string? payloadJson,
CancellationToken cancellationToken = default)
{
var input = DeserializeBatchPayload<OrderHandleStatusUpdateInput>(payloadJson)
?? throw new BusinessException("处理状态参数无效");
var targetStatus = input.HandleStatus?.Trim().ToLowerInvariant();
if (!string.Equals(targetStatus, OrderHandleStatusCodes.Processed, StringComparison.Ordinal))
{
throw new BusinessException("仅支持标记为已处理");
}
var order = await GetOrderOrThrowAsync(orderId);
if (order.OrderStatus is not (OrderStatusCodes.Problematic or OrderStatusCodes.Claimed))
{
throw new BusinessException("仅问题件或理赔件可更新处理状态");
}
if (OrderHandleStatusCodes.IsProcessed(order.HandleStatus))
{
throw new BusinessException("订单已处理");
}
order.HandleStatus = OrderHandleStatusCodes.Processed;
await OrderRepository.UpdateAsync(order);
await OrderExceptionEventRepository.InsertAsync(new OrderExceptionEvent
{
OrderId = order.Id,
EventType = OrderExceptionEventTypeCodes.HandleProcessed,
FromStatus = order.OrderStatus,
ToStatus = order.OrderStatus,
FromHandleStatus = OrderHandleStatusCodes.Pending,
ToHandleStatus = OrderHandleStatusCodes.Processed,
Remark = input.Remark?.Trim(),
});
}
private static T? DeserializeBatchPayload<T>(string? payloadJson)
where T : class
{
if (string.IsNullOrWhiteSpace(payloadJson))
{
return null;
}
return JsonConvert.DeserializeObject<T>(payloadJson);
}
}