admin/src/Oms.Application/Services/Orders/Issues/OrderExceptionWorkflowService.cs
zzh 34c47a3173
All checks were successful
Build and Deploy / deploy (push) Successful in 1m0s
fix: 创单后置步骤随订单落库,提交后再单独执行
进程重启或事务回滚不再丢掉 9610、轨迹和揽收;排货完成后再补轨迹、面单和计费相关触发。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-14 20:07:51 +08:00

203 lines
7.3 KiB
C#

using Fake.Application;
using Fake.DependencyInjection;
using Oms.Domain.ChannelsAggregate.Consts;
using Oms.Domain.ChannelsAggregate.Entities;
using Oms.Domain.ChannelsAggregate.Repositories;
using Oms.Domain.OrdersAggregate.Consts;
using Oms.Domain.OrdersAggregate.Entities;
using Oms.Domain.OrdersAggregate.Repositories;
using Oms.Domain.Shared.Enums;
namespace Oms.Application.Services.Orders.Issues;
public sealed class ProblematicMarkRequest
{
public string IssueType { get; set; } = string.Empty;
public string Title { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
public int? IssueTemplateId { get; set; }
public string? NotifyMethod { get; set; }
public string? HandlerNames { get; set; }
public string? Remark { get; set; }
public IEnumerable<int>? ExtraReceiverUserIds { get; set; }
}
public class OrderExceptionWorkflowService(
IOrderRepository orderRepository,
IOrderExceptionEventRepository orderExceptionEventRepository,
IIssueTemplateRepository issueTemplateRepository,
IssueHandlerResolver issueHandlerResolver,
OrderIssueNotificationService issueNotificationService) : IScopedDependency
{
public async Task<string?> TryMarkProblematicAsync(
Order order,
ProblematicMarkRequest request,
int operatorUserId,
bool sendNotification)
{
var current = await orderRepository.FirstOrDefaultAsync(x => x.Id == order.Id) ?? order;
if (!OrderExceptionStatusRules.CanMarkException(current.OrderStatus))
{
return OrderExceptionStatusRules.GetForbiddenReason(current.OrderStatus);
}
IssueTemplate? template = null;
if (request.IssueTemplateId is int templateId && templateId > 0)
{
template = await issueTemplateRepository.FirstOrDefaultAsync(x => x.Id == templateId);
}
IssueHandlerResolution handlerResolution;
try
{
handlerResolution = await issueHandlerResolver.ResolveAsync(current, template, request.HandlerNames);
}
catch (BusinessException ex)
{
return ex.Message;
}
var context = IssuePlaceholderContext.FromOrder(current, request.IssueType, request.Title);
var renderedTitle = IssuePlaceholderRenderer.Render(request.Title, context);
var renderedContent = IssuePlaceholderRenderer.Render(request.Content, context);
var notifyMethod = request.NotifyMethod;
if (IssueHandlerTypeCodes.IsCustomer(handlerResolution.HandlerType))
{
notifyMethod = IssueNotifyMethodCodes.InApp;
}
var fromStatus = current.OrderStatus;
var isAppend = string.Equals(fromStatus, OrderStatusCodes.Problematic, StringComparison.OrdinalIgnoreCase);
if (!isAppend)
{
current.PreviousOrderStatus = fromStatus;
current.OrderStatus = OrderStatusCodes.Problematic;
}
current.HandleStatus = OrderHandleStatusCodes.Pending;
current.IssueType = request.IssueType;
current.IssueTitle = renderedTitle;
current.IssueContent = renderedContent;
current.IssueTemplateId = request.IssueTemplateId;
current.IssueRemark = request.Remark;
current.IssueTime = DateTime.Now;
current.ClaimType = null;
current.ClaimAmount = null;
current.ClaimCurrency = null;
current.ClaimReason = null;
current.ClaimTime = null;
await orderRepository.UpdateAsync(current);
order.OrderStatus = current.OrderStatus;
order.PreviousOrderStatus = current.PreviousOrderStatus;
order.HandleStatus = current.HandleStatus;
order.IssueType = current.IssueType;
order.IssueTitle = current.IssueTitle;
order.IssueContent = current.IssueContent;
order.IssueTemplateId = current.IssueTemplateId;
order.IssueRemark = current.IssueRemark;
order.IssueTime = current.IssueTime;
await orderExceptionEventRepository.InsertAsync(new OrderExceptionEvent
{
OrderId = order.Id,
EventType = OrderExceptionEventTypeCodes.MarkProblematic,
FromStatus = fromStatus,
ToStatus = OrderStatusCodes.Problematic,
FromHandleStatus = null,
ToHandleStatus = OrderHandleStatusCodes.Pending,
IssueType = request.IssueType,
Title = renderedTitle,
Content = renderedContent,
IssueTemplateId = request.IssueTemplateId,
NotifyMethod = notifyMethod,
HandlerNames = handlerResolution.HandlerNames,
HandlerType = handlerResolution.HandlerType,
HandlerUserId = handlerResolution.HandlerUserId,
Remark = request.Remark
});
if (sendNotification && !string.IsNullOrWhiteSpace(notifyMethod))
{
var notifyUserIds = handlerResolution.NotifyUserIds.Count > 0
? handlerResolution.NotifyUserIds
: null;
await issueNotificationService.DispatchAsync(
current,
renderedTitle,
renderedContent,
notifyMethod,
operatorUserId,
notifyUserIds,
request.ExtraReceiverUserIds);
}
return null;
}
public async Task<ProblematicMarkRequest?> BuildRequestFromTemplateAsync(
string? organizationId,
string issueType,
string? titleOverride,
string? contentOverride,
string? remark)
{
var normalizedType = issueType.Trim().ToLowerInvariant();
if (!IssueTypeCodes.All.Contains(normalizedType))
{
normalizedType = IssueTypeCodes.Other;
}
var normalizedOrg = NormalizeOrganizationId(organizationId);
var hasOrgFilter = normalizedOrg != null;
var orgValue = normalizedOrg ?? string.Empty;
var templates = await issueTemplateRepository.GetListAsync(x =>
x.Status == EnableStatus.Enabled
&& x.IssueType == normalizedType
&& (!hasOrgFilter
|| x.OrganizationId == orgValue
|| x.OrganizationId == null
|| x.OrganizationId == string.Empty));
var template = templates.FirstOrDefault(x => x.OrganizationId == normalizedOrg)
?? templates.FirstOrDefault(x => string.IsNullOrWhiteSpace(x.OrganizationId))
?? templates.FirstOrDefault();
var title = !string.IsNullOrWhiteSpace(titleOverride)
? titleOverride.Trim()
: template?.Title?.Trim();
var content = !string.IsNullOrWhiteSpace(contentOverride)
? contentOverride.Trim()
: template?.Content?.Trim();
if (string.IsNullOrWhiteSpace(title) || string.IsNullOrWhiteSpace(content))
{
return null;
}
return new ProblematicMarkRequest
{
IssueType = normalizedType,
Title = title,
Content = content,
IssueTemplateId = template?.Id,
NotifyMethod = template == null ? null : IssueNotifyMethodCodes.Normalize(template.NotifyMethod),
Remark = remark
};
}
private static string? NormalizeOrganizationId(string? organizationId)
{
var text = organizationId?.Trim();
return string.IsNullOrWhiteSpace(text) ? null : text;
}
}