admin/src/Oms.Application/Services/Orders/Issues/OrderExceptionWorkflowService.cs
zzh 90f6c25b04 feat(order): 问题件/理赔件标记、通知投递与自动触发
新增 mark-problematic/mark-claimed/handle-status 接口,异常事件审计表与占位符渲染;inApp/email 通知及下单/入库/出库自动触发运行时。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-06 16:46:52 +08:00

155 lines
5.4 KiB
C#

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? Remark { get; set; }
public IEnumerable<int>? ExtraReceiverUserIds { get; set; }
}
public class OrderExceptionWorkflowService(
IOrderRepository orderRepository,
IOrderExceptionEventRepository orderExceptionEventRepository,
IIssueTemplateRepository issueTemplateRepository,
OrderIssueNotificationService issueNotificationService) : IScopedDependency
{
public async Task<string?> TryMarkProblematicAsync(
Order order,
ProblematicMarkRequest request,
int operatorUserId,
bool sendNotification)
{
if (!OrderExceptionStatusRules.CanMarkException(order.OrderStatus))
{
return OrderExceptionStatusRules.GetForbiddenReason(order.OrderStatus);
}
var context = IssuePlaceholderContext.FromOrder(order, request.IssueType, request.Title);
var renderedTitle = IssuePlaceholderRenderer.Render(request.Title, context);
var renderedContent = IssuePlaceholderRenderer.Render(request.Content, context);
var fromStatus = order.OrderStatus;
order.PreviousOrderStatus = fromStatus;
order.OrderStatus = OrderStatusCodes.Problematic;
order.HandleStatus = OrderHandleStatusCodes.Pending;
order.IssueType = request.IssueType;
order.IssueTitle = renderedTitle;
order.IssueContent = renderedContent;
order.IssueTemplateId = request.IssueTemplateId;
order.IssueRemark = request.Remark;
order.IssueTime = DateTime.Now;
order.ClaimType = null;
order.ClaimAmount = null;
order.ClaimCurrency = null;
order.ClaimReason = null;
order.ClaimTime = null;
await orderRepository.UpdateAsync(order);
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 = request.NotifyMethod,
Remark = request.Remark
});
if (sendNotification)
{
await issueNotificationService.DispatchAsync(
order,
renderedTitle,
renderedContent,
request.NotifyMethod,
operatorUserId,
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;
}
}