using DotNetCore.CAP; using Hotline.Application.CallCenter; using Hotline.Application.OrderApp; using Hotline.Caching.Interfaces; using Hotline.CallCenter.Tels; using Hotline.Configurations; using Hotline.EventBus; using Hotline.FlowEngine.Notifications; using Hotline.FlowEngine.WorkflowModules; using Hotline.JudicialManagement.Notifies; using Hotline.KnowledgeBase; using Hotline.Orders; using Hotline.Schedulings; using Hotline.Settings; using Hotline.Settings.TimeLimitDomain; using Hotline.Share.Dtos.FlowEngine.Workflow; using Hotline.Share.Dtos.Order; using Hotline.Share.Dtos.TrCallCenter; using Hotline.Share.Enums.CallCenter; using Hotline.Share.Enums.FlowEngine; using Hotline.Share.Enums.Order; using Hotline.Share.Tools; using Hotline.Snapshot.Notifications; using MapsterMapper; using MediatR; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using SqlSugar.Extensions; using XF.Domain.Exceptions; using XF.Domain.Repository; namespace Hotline.Application.Handlers.FlowEngine; public class WorkflowEndHandler : INotificationHandler { private readonly IKnowledgeDomainService _knowledgeDomainService; private readonly IOrderDomainService _orderDomainService; private readonly IOrderApplication _orderApplication; private readonly ITelDomainService _telDomainService; private readonly IOrderRepository _orderRepository; private readonly ICapPublisher _capPublisher; private readonly IMapper _mapper; private readonly IRepository _orderDelayRepository; private readonly ILogger _logger; private readonly IKnowledgeRepository _knowledgeRepository; private readonly ICallApplication _callApplication; private readonly IOptionsSnapshot _appOptions; private readonly ISystemSettingCacheManager _systemSettingCacheManager; private readonly Publisher _publisher; private readonly ICalcExpireTime _expireTime; private readonly IRepository _orderTerminateRepository; private readonly IRepository _orderSecondaryHandlingRepository; private readonly IRepository _schedulingRepository; public WorkflowEndHandler( IMapper mapper, IKnowledgeDomainService knowledgeDomainService, IOrderDomainService orderDomainService, IOrderApplication orderApplication, ITelDomainService telDomainService, IOrderRepository orderRepository, IKnowledgeRepository knowledgeRepository, IRepository orderDelayRepository, ICapPublisher capPublisher, ICallApplication callApplication, IOptionsSnapshot appOptions, ISystemSettingCacheManager systemSettingCacheManager, Publisher publisher, ILogger logger, ICalcExpireTime expireTime, IRepository orderTerminateRepository, IRepository orderSecondaryHandlingRepository, IRepository schedulingRepository) { _mapper = mapper; _knowledgeDomainService = knowledgeDomainService; _orderDomainService = orderDomainService; _orderApplication = orderApplication; _telDomainService = telDomainService; _orderRepository = orderRepository; _knowledgeRepository = knowledgeRepository; _orderDelayRepository = orderDelayRepository; _capPublisher = capPublisher; _callApplication = callApplication; _appOptions = appOptions; _systemSettingCacheManager = systemSettingCacheManager; _publisher = publisher; _logger = logger; _expireTime = expireTime; _orderTerminateRepository = orderTerminateRepository; _orderSecondaryHandlingRepository = orderSecondaryHandlingRepository; _schedulingRepository = schedulingRepository; } /// Handles a notification /// The notification /// Cancellation token public async Task Handle(EndWorkflowNotify notification, CancellationToken cancellationToken) { try { var workflow = notification.Workflow; //审批是否通过 var isReviewPass = workflow.IsReviewPass(); switch (workflow.ModuleCode) { case WorkflowModuleConsts.KnowledgeAdd://新增知识库 case WorkflowModuleConsts.KnowledgeUpdate://修改知识库 case WorkflowModuleConsts.KnowledgeDelete://删除知识库 case WorkflowModuleConsts.KnowledgeOffshelf: //下架知识库 //var knowledgeWork = await _knowledgeWorkFlowRepository.Queryable().Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken); var knowledge = await _knowledgeRepository.Queryable().Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken); knowledge.Flowed(workflow.FlowedUserIds, workflow.FlowedOrgIds, workflow.HandlerUsers, workflow.HandlerOrgs); await _knowledgeRepository.UpdateAsync(knowledge, cancellationToken); if (isReviewPass) { await _knowledgeDomainService.EndWorkKnowledge(workflow, cancellationToken); } else { await _knowledgeDomainService.TerminateWorkKnowledge(workflow, cancellationToken); } break; case WorkflowModuleConsts.TelRestApply: await _telDomainService.TelRestApplyPassAsync(workflow.ExternalId, cancellationToken); break; case WorkflowModuleConsts.OrderHandle: case WorkflowModuleConsts.OrderHandleSnapshot: var order = await _orderDomainService.GetOrderAsync(workflow.ExternalId, withExtension: true, cancellationToken: cancellationToken); //order.CheckIfFiled(); //order.UpdateHandlingStatus(workflow.IsInCountersign); _mapper.Map(workflow, order); var now = DateTime.Now; var handleDuration = order.CenterToOrgTime.HasValue && order.ActualHandleTime.HasValue ? // _timeLimitDomainService.CalcWorkTime( await _expireTime.CalcWorkTime( order.CenterToOrgTime.Value, order.ActualHandleTime.Value, order.ProcessType is EProcessType.Zhiban) : 0; var fileDuration = order.CenterToOrgTime.HasValue ? //_timeLimitDomainService.CalcWorkTime( await _expireTime.CalcWorkTime( order.CenterToOrgTime.Value, now, order.ProcessType is EProcessType.Zhiban) : 0; var allDuration = order.StartTime.HasValue ? // _timeLimitDomainService.CalcWorkTime( await _expireTime.CalcWorkTime( order.StartTime.Value, now, order.ProcessType is EProcessType.Zhiban) : 0; var creationTimeHandleDurationWorkday = order.ActualHandleTime.HasValue ? //_timeLimitDomainService.CalcWorkTimeEx( await _expireTime.CalcWorkTimeEx( order.CreationTime, now, order.ProcessType is EProcessType.Zhiban) : 0; var centerToOrgHandleDurationWorkday = order.ActualHandleTime.HasValue && order.CenterToOrgTime.HasValue ? //_timeLimitDomainService.CalcWorkTimeEx( await _expireTime.CalcWorkTimeEx( order.CenterToOrgTime.Value, now, order.ProcessType is EProcessType.Zhiban) : 0; creationTimeHandleDurationWorkday = creationTimeHandleDurationWorkday <= 0 ? 10 : creationTimeHandleDurationWorkday; centerToOrgHandleDurationWorkday = centerToOrgHandleDurationWorkday <= 0 ? 10 : centerToOrgHandleDurationWorkday; double? secondaryHandlingDurationWorkday = null; DateTime? secondaryHandlingAuditTime = null; //计算二次办理时长 if (_appOptions.Value.IsZiGong) { //查询是否有二次办理申请通过的 var orderSecondaryData = await _orderSecondaryHandlingRepository.GetAsync(p => p.OrderId == order.Id && p.IsHandel == false && p.State == ESecondaryHandlingState.End, cancellationToken); if (orderSecondaryData != null) { //处理办理时长 orderSecondaryData.IsHandel = true; await _orderSecondaryHandlingRepository.UpdateAsync(orderSecondaryData, cancellationToken); if (orderSecondaryData.AuditTime.HasValue) { secondaryHandlingAuditTime = orderSecondaryData.AuditTime; secondaryHandlingDurationWorkday = orderSecondaryData.AuditTime.HasValue ? await _expireTime.CalcWorkTimeEx( orderSecondaryData.AuditTime.Value, now, false) : null; } } } order.File(now, handleDuration, fileDuration, allDuration, creationTimeHandleDurationWorkday, centerToOrgHandleDurationWorkday, secondaryHandlingDurationWorkday, secondaryHandlingAuditTime); order.FileUserId = notification.Trace.HandlerId; order.FileUserName = notification.Trace.HandlerName; order.FileUserOrgId = notification.Trace.HandlerOrgId; order.FileUserOrgName = notification.Trace.HandlerOrgName; order.FileOrgIsCenter = notification.Trace.HandlerOrgIsCenter; order.FileOpinion = notification.Dto.Opinion; /* *需求: *1、判断工单属于哪种归档类型,需由谁归档来判断,热线中心归档的就叫中心归档件,部门归档就叫做部门归档件 2、若工单发起过部门会签,最后由热线中心归档,应属于叫中心归档件和非会签件 3、若工单发起过中心会签,最后由部门归档,应属于部门归档件和非会签件 */ if (_appOptions.Value.IsYiBin) { if (order.FileOrgIsCenter.Value) { if (order.CounterSignType is ECounterSignType.Department) order.CounterSignType = null; } else { if (order.CounterSignType is ECounterSignType.Center) order.CounterSignType = null; } //随机派单员(市中心综合办) if (order.FileUserOrgId == "001175") { try { var schedulingList = await _schedulingRepository.Queryable().Includes(x => x.SchedulingUser).Where(x => x.SchedulingTime!.Value.Date == DateTime.Now.Date).ToListAsync(); if (schedulingList != null && schedulingList.Count > 0) { int randomNum = Random.Shared.Next(schedulingList.Count); var userId = schedulingList[randomNum].SchedulingUser.UserId; order.WaitForPublisherId = userId; } } catch { } } } //记录冗余归档数据 if (notification.Workflow.Steps.Any(x => x.BusinessType == Share.Enums.FlowEngine.EBusinessType.Send)) { order.FileUserRole = EFileUserType.Dispatch; } else { order.FileUserRole = EFileUserType.Seat; } if (order.ProcessType == EProcessType.Jiaoban) { order.FileUserRole = EFileUserType.Org; } //是否已解决 order.IsResolved = notification.Dto.External == null ? false : notification.Dto.External.IsResolved; //await _orderRepository.UpdateAsync(order, cancellationToken); await _orderRepository.Updateable(order) .ExecuteCommandAsync(cancellationToken); //var callRecord = await _trCallRecordRepository.GetAsync(p => p.CallAccept == order.CallId, cancellationToken); //由CallAccept改为OtherAccept //var callRecord = await _trCallRecordRepository.GetAsync(p => p.OtherAccept == order.CallId, cancellationToken); var orderFlowDto = new OrderFlowDto { Order = _mapper.Map(order), WorkflowTrace = _mapper.Map(notification.Trace) }; // if (callRecord != null) // { // orderFlowDto.TrCallRecordDto = _mapper.Map(callRecord); // } if (order.SourceChannelCode == AppDefaults.SourceChannel.DianHua && !string.IsNullOrEmpty(order.CallId)) { if (_appOptions.Value.GetDefaultAppScopeConfiguration().CallCenterType == AppDefaults.CallCenterType.TianRun) { // var callRecord = await _trCallRecordRepository.GetAsync(p => p.OtherAccept == order.CallId, cancellationToken); var callRecord = await _callApplication.GetTianrunCallAsync(order.CallId, cancellationToken); if (callRecord != null) { orderFlowDto.TrCallRecordDto = _mapper.Map(callRecord); } } else if (_appOptions.Value.GetDefaultAppScopeConfiguration().CallCenterType == AppDefaults.CallCenterType.XingTang) { var call = await _callApplication.GetCallAsync(order.CallId, cancellationToken); if (call is not null) { orderFlowDto.TrCallRecordDto = _mapper.Map(call); // 工单开始办理如果获取的通话记录是呼出并且录音文件是空就不推送通话记录 // 如果 通话记录是呼入, 并且没有录音文件 if (_systemSettingCacheManager.OrderStartHandlerPushCallIsNull && call.Direction == ECallDirection.Out && call.AudioFile.IsNullOrEmpty()) { orderFlowDto.TrCallRecordDto = null; } } } } //这里需要判断是否是警情退回 orderFlowDto.IsNonPoliceReturn = notification.Dto.External == null ? false : notification.Dto.External.IsPoliceReturn; //如果是泸州,这里需要查询是否有工单延期 if (_appOptions.Value.IsLuZhou) { var orderDelay = await _orderDelayRepository.Queryable().Where(p => p.OrderId == order.Id && p.DelayState == EDelayState.Pass) .OrderByDescending(p => p.ApplyDelayTime) .FirstAsync(); orderFlowDto.OrderSearchDelay = _mapper.Map(orderDelay); } await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderFiled, orderFlowDto, cancellationToken: cancellationToken); if (_systemSettingCacheManager.Snapshot) { await _publisher.PublishAsync(new SnapshotOrderFiledNotification(order.Id), PublishStrategy.ParallelWhenAll, cancellationToken); } await _orderDomainService.OrderAutomaticPublishAsync(order, cancellationToken); //try //{ // //写入质检 针对受理之后直接结束的工单 // await _qualityApplication.AddQualityAsync(EQualitySource.Accepted, order.Id, cancellationToken); //} //catch (Exception e) //{ // _logger.LogError($"写入质检异常!orderId: {order.Id}, \r\n{e.Message}"); //} //司法行政监督管理-工单处理 //如果没开启则不处理 var isOpenJudicialManagement = _systemSettingCacheManager.GetSetting(SettingConstants.IsOpenJudicialManagement)?.SettingValue[0]; if (isOpenJudicialManagement == "true") await _publisher.PublishAsync(new JudicialManagementOrderNotify(order), PublishStrategy.ParallelWhenAll, cancellationToken); //推诿工单 // await _enforcementApplication.AddPassTheBuckOrderAsync(order, _sessionContext.OrgId, _sessionContext.OrgName, cancellationToken); break; case WorkflowModuleConsts.OrderDelay: var delay = await _orderDelayRepository.GetAsync(workflow.ExternalId, cancellationToken); if (delay != null) { //delay.Flowed(workflow.FlowedUserIds, workflow.FlowedOrgIds, workflow.HandlerUsers, workflow.HandlerOrgs); delay.DelayState = isReviewPass ? EDelayState.Pass : EDelayState.NoPass; await _orderDelayRepository.Updateable(delay) .UpdateColumns(d => d.DelayState) .ExecuteCommandAsync(cancellationToken); if (isReviewPass) { //处理工单延期 await _orderApplication.DelayOrderExpiredTimeAsync(delay.OrderId, delay.DelayNum, delay.DelayUnit, delay.IsProDelay, cancellationToken); } } break; case WorkflowModuleConsts.OrderTerminate: var orderTerminate = await _orderTerminateRepository.Queryable() .Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken); if (orderTerminate != null) { orderTerminate.Status = isReviewPass ? ETerminateStatus.End : ETerminateStatus.Refuse; await _orderTerminateRepository.UpdateAsync(orderTerminate, cancellationToken); } break; } } catch (Exception e) { _logger.LogError($"{nameof(WorkflowEndHandler)}异常,{e}"); throw; } } }