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? ExtraReceiverUserIds { get; set; } } public class OrderExceptionWorkflowService( IOrderRepository orderRepository, IOrderExceptionEventRepository orderExceptionEventRepository, IIssueTemplateRepository issueTemplateRepository, IssueHandlerResolver issueHandlerResolver, OrderIssueNotificationService issueNotificationService) : IScopedDependency { public async Task 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 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; } }