using AutoFixture; using Hotline.Api.Controllers; using Hotline.Application.CallCenter; using Hotline.Caching.Interfaces; using Hotline.CallCenter.Calls; using Hotline.FlowEngine.Notifications; using Hotline.Identity.Accounts; using Hotline.Identity.Roles; using Hotline.Orders; using Hotline.Settings; using Hotline.Settings.Hotspots; using Hotline.Share.Dtos.CallCenter; using Hotline.Share.Dtos.Order; using Hotline.Share.Dtos.Order.Publish; using Hotline.Share.Dtos.Snapshot; using Hotline.Share.Dtos.TrCallCenter; using Hotline.Share.Enums.CallCenter; using Hotline.Share.Enums.FlowEngine; using Hotline.Share.Enums.Order; using Hotline.Share.Enums.Settings; using Hotline.Share.Tools; using Hotline.Tests.Dto; using Hotline.Tests.Mock; using Hotline.Tests.SqlSuger; using Hotline.Users; using Mapster; using MediatR; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Shouldly; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using Hotline.FlowEngine.Workflows; using XF.Domain.Authentications; using XF.Domain.Cache; using XF.Domain.Exceptions; using XF.Domain.Repository; using Hotline.ThirdAccountDomainServices.Interfaces; using Hotline.ThirdAccountDomainServices; using Hotline.Snapshot.IRepository; namespace Hotline.Tests.Controller; public class OrderControllerTest : TestBase { private readonly OrderController _orderController; private readonly IRepository _hotspotRepository; private readonly IOrderRepository _orderRepository; private readonly OrderServiceMock _orderServiceMock; private readonly IRepository _orderPublishRepository; private readonly INotificationHandler _orderPublishEndWorkflowHandler; private readonly IOrderVisitRepository _orderVisitRepository; private readonly IRepository _orderVisitDetailRepository; private readonly IRepository _systemSettingRepository; private readonly ISystemSettingCacheManager _systemSettingCacheManager; private readonly IRepository _callNativeRepository; private readonly IRepository _callIdRelationRepository; private readonly XingTangCallApplication _defaultCallApplication; private readonly ISqlSugarClient _capSqlClient; private readonly IIndustryRepository _industryRepository; private readonly IOrderSnapshotRepository _orderSnapshotRepository; private readonly ISystemLogRepository _systemLogRepository; private readonly IOrderVisitDomainService _orderVisitDomainService; private readonly ISystemDicDataCacheManager _systemDicDataCacheManager; private readonly IWorkflowDomainService _workflowDomainService; public OrderControllerTest(IAccountRepository accountRepository, IRepository roleRepository, UserController userController, IRepository hotspotRepository, OrderController orderController, IOrderRepository orderRepository, IServiceScopeFactory scopeFactory, IRepository userRepository, OrderServiceMock orderServiceMock, IRepository orderPublishRepository, INotificationHandler orderPublishEndWorkflowHandler, IOrderVisitRepository orderVisitRepository, IRepository systemSettingRepository, ISystemSettingCacheManager systemSettingCacheManager, IRepository callNativeRepository, IRepository callIdRelationRepository, XingTangCallApplication defaultCallApplication, ISugarUnitOfWork capDbContext, IHttpContextAccessor httpContextAccessor, IThirdIdentiyService thirdService, IThirdAccountRepository thirdAccount, IIndustryRepository industryRepository, IOrderSnapshotRepository orderSnapshotRepository, ISystemLogRepository systemLogRepository, IOrderVisitDomainService orderVisitDomainService, IRepository orderVisitDetailRepository, ISystemDicDataCacheManager systemDicDataCacheManager, IWorkflowDomainService workflowDomainService, ITypedCache cacheSettingData, ThirdAccounSupplierFactory thirdAccountDomainFactory, IServiceProvider serviceProvider) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdService, thirdAccount, cacheSettingData, thirdAccountDomainFactory, serviceProvider) { _hotspotRepository = hotspotRepository; _orderController = orderController; _orderController.ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() }; _orderRepository = orderRepository; _orderServiceMock = orderServiceMock; _orderPublishRepository = orderPublishRepository; _orderPublishEndWorkflowHandler = orderPublishEndWorkflowHandler; _orderVisitRepository = orderVisitRepository; _systemSettingRepository = systemSettingRepository; _systemSettingCacheManager = systemSettingCacheManager; _callNativeRepository = callNativeRepository; _callIdRelationRepository = callIdRelationRepository; _defaultCallApplication = defaultCallApplication; _capSqlClient = capDbContext.Db; _industryRepository = industryRepository; _orderSnapshotRepository = orderSnapshotRepository; _systemLogRepository = systemLogRepository; _orderVisitDomainService = orderVisitDomainService; _orderVisitDetailRepository = orderVisitDetailRepository; _systemDicDataCacheManager = systemDicDataCacheManager; _workflowDomainService = workflowDomainService; } /// /// 创建工单时传入一个错误callId /// 验证: /// 测试在延迟消息后会不会把工单测CallId修复成正确的CallId /// 推送的省上数据是否正确 /// 测试修复后的CallId 再次保存功能, 能通过 systemLog 查询修复记录, 拦截工单保存 /// /// [Fact] public async Task OrderCallIdRelevance_Test() { var callNo = DateTime.Now.ToString("yyyyMMddhhmmss") + "Flow"; var inDto = _fixture.Build() .With(m => m.Id, Ulid.NewUlid().ToString()) .With(m => m.CallNo, callNo) .With(m => m.Direction, ECallDirection.In) .With(m => m.IsDeleted, false) .Create(); await _callNativeRepository.AddAsync(inDto); var inDto2 = _fixture.Build() .With(m => m.Id, Ulid.NewUlid().ToString()) .With(m => m.Direction, ECallDirection.Out) .With(m => m.CallNo, callNo) .With(m => m.AudioFile, string.Empty) .With(m => m.IsDeleted, false) .Create(); await _callNativeRepository.AddAsync(inDto2); var callOrder = new CallidRelation { Id = callNo, CallId = inDto2.Id, }; await _callIdRelationRepository.AddAsync(callOrder, CancellationToken.None); SetZuoXi(); var order = _orderServiceMock.CreateOrder(callNo) .办理到派单员() .GetCreateResult(); order.ShouldNotBeNull(); await _defaultCallApplication.OrderRelateCallHandlerAsync(order.Id, CancellationToken.None); var orderEntity = await _orderRepository.GetAsync(order.Id); orderEntity.ShouldNotBeNull(); orderEntity.CallId.ShouldBe(inDto.Id); var query = _capSqlClient.Queryable() .Where(m => m.Name == "hotline.call.connect.order" && m.Content.Contains(order.Id)); var sql = query.ToSqlString(); sql.Contains("id").ShouldBeFalse("字段大小写错误" + sql); var published = await query.FirstAsync(); published.ShouldNotBeNull("推送数据不存在"); var message = published.Content.FromJson>(); message.Value.Order.ShouldNotBeNull("推送的工单信息为空"); message.Value.Order.ProvinceNo.ShouldNotBeNull("省工单编号不能为空"); message.Value.Order.No.ShouldBe(order.No, "推送的工单编号错误"); try { order = _orderServiceMock.CreateOrder(callNo) .办理到派单员() .GetCreateResult(); } catch (UserFriendlyException e) { e.FriendlyMessage.ShouldBe("来电已保存工单!"); } } [Fact] public async Task Visit_Test() { SetZuoXi(); var order = _orderServiceMock.CreateOrder() .办理到一级部门() .办理到二级部门(Set一级部门) .办理一级部门汇总(Set二级部门) .办理到归档(Set一级部门) .发布工单(SetZuoXi) .GetCreateResult(); var visit = await _orderVisitRepository.Queryable() .Where(m => m.OrderId == order.Id) .FirstAsync(); var visitDetaila = await _orderVisitDetailRepository.Queryable().Where(m => m.VisitId == visit.Id).ToListAsync(); var visitDetail = visitDetaila.Adapt>(); var smsReply = _orderVisitDomainService.GetVisitEvaluateByReplyTxt("0"); visitDetail.ForEach(m => { m.SeatEvaluate = ESeatEvaluate.DefaultSatisfied; m.OrgProcessingResults = smsReply.GetOrgProcessingResults(_systemDicDataCacheManager.VisitSatisfaction); m.OrgHandledAttitude = smsReply.GetOrgHandledAttitude(_systemDicDataCacheManager.VisitSatisfaction); }); var callNo = DateTime.Now.ToString("yyyyMMddhhmmss") + "Flow"; var inDto = _fixture.Build() .With(m => m.Id, Ulid.NewUlid().ToString()) .With(m => m.CallNo, callNo) .With(m => m.Direction, ECallDirection.In) .With(m => m.IsDeleted, false) .Create(); await _callNativeRepository.AddAsync(inDto); _systemLogRepository.Add("回访外呼已经接通", "", new CallRemarkDto { CallId = callNo, CallNumber = inDto.ToNo }.ToJson(), "", 1, visit.Id); var visitDto = new VisitDto { Id = visit.Id, IsPutThrough = true, VisitDetails = visitDetail }; await _orderController.Visit(visitDto); var orderEntity = await _orderRepository.GetAsync(order.Id); orderEntity.OrgProcessingResults.ShouldNotBeNull(); } /// /// 创建工单并派送给派单员 /// /// [Fact] public async Task ToPaiDanYuan_Test() { SetZuoXi(); var order = _orderServiceMock.CreateOrder() .办理到派单员() .GetCreateResult(); order.ShouldNotBeNull(); } /// /// 创建随手拍工单 /// 验证随手拍工单是否能正常创建 /// /// [Fact] public async Task OrderSnapshot_Test() { var industryItems = await _industryRepository.Queryable().Select(d => new { d.Id, d.Name, }).ToListAsync(); var industry = industryItems.First(); SetZuoXi(); var order = _orderServiceMock.CreateOrder(industryId: industry.Id, industryName: industry.Name) .办理到派单员() .办理到归档(SetPaiDanYuan, data => { data.IsEvasive = true; data.IsInactively = true; }) .GetCreateResult(); var orderEntity = await _orderRepository.GetAsync(order.Id); orderEntity.ShouldNotBeNull(); var snapshot = await _orderSnapshotRepository.GetAsync(m => m.Id == order.Id); snapshot.ShouldNotBeNull(); snapshot.IndustryId.ShouldBe(industry.Id); snapshot.IndustryName.ShouldBe(industry.Name); } /// /// 验证中心直办工单归档后 自动发布 /// 是否推诿, 是否不积极 /// /// [Fact] public async Task AutoPublish_Test() { SetZuoXi(); var order = _orderServiceMock.CreateOrder() .办理到派单员() .办理到归档(SetPaiDanYuan, data => { data.IsEvasive = true; data.IsInactively = true; }) .GetCreateResult(); var publish = await _orderPublishRepository.GetAsync(m => m.No == order.No); publish.ShouldNotBeNull(order.No); var orderEntity = await _orderRepository.GetAsync(order.Id); orderEntity.ShouldNotBeNull(); orderEntity.IsEvasive.ShouldBeTrue(); orderEntity.IsInactively.ShouldBeTrue(); orderEntity.Status.ShouldBe(EOrderStatus.Visited); orderEntity.OrgProcessingResults.ShouldNotBeNull(); orderEntity.OrgProcessingResults.Key.ShouldBe("4"); orderEntity.OrgProcessingResults.Value.ShouldBe("满意"); orderEntity.SeatEvaluate.ShouldBe(ESeatEvaluate.Satisfied); } [Fact] public void Set一级部门_Test() { Set一级部门(); } [Fact] public async Task IsStepUrgent_True_Test() { SetZuoXi(); var order = _orderServiceMock.CreateOrder() .办理到派单员() .办理到归档(SetPaiDanYuan, data => data.IsStepUrgent = true, workflow => { workflow.BusinessType = EBusinessType.DepartmentLeader; workflow.Opinion = "测试保存勾选紧急"; }) .GetCreateResult(); order.ShouldNotBeNull(); order.Id.ShouldNotBeNull(); var orderDto = await _orderController.Get(order.Id); orderDto.IsStepUrgent.ShouldBeTrue(); orderDto.IsStepUrgenText.ShouldBe("紧急"); } /// /// 测试在办理的过程中能正确保存 /// 1.与市民电话联系 /// 2.已赴现场处置 /// 3.其他 /// 4.其他原因 /// 5.经办人 /// 6.经办人电话 /// 7.沟通时间 /// 8.沟通地点 /// /// [Fact] public async Task CreateOrder_Test() { SetZuoXi(); var order = _orderServiceMock.CreateOrder() .办理到一级部门() .办理到二级部门(Set一级部门) .办理一级部门汇总(Set二级部门) .GetCreateResult(); order.Id.ShouldNotBeNull(); var orderEntity = await _orderRepository.GetAsync(order.Id); orderEntity.RealCommunicationAddress.ShouldNotBeNull(); orderEntity.RealCommunicationTime.ShouldNotBeNull(); orderEntity.RealContactLocale.ShouldNotBeNull(); orderEntity.RealContactLocale.ShouldBe(true); orderEntity.RealHandlerName.ShouldNotBeNull(); orderEntity.RealHandlerPhone.ShouldNotBeNull(); } [Fact] public async Task GetOrderHandleTimeConfigByAcceptType_Test() { var result = await _orderController.GetOrderHandleTimeConfigByAcceptType(Share.Enums.FlowEngine.EFlowDirection.OrgToCenter, "10"); result.Count.ShouldBe(1); result.TimeText.ShouldBe("1个工作日"); result.TimeType.ShouldBe(ETimeType.WorkDay); } /// /// 测试取消发布功能 /// /// [Fact] public async Task CancelOrderPublish_Test() { var system = await _systemSettingRepository.Queryable() .Where(m => m.Code == SettingConstants.CancelPublishOrderEnabled) .FirstAsync(); system.SettingValue[0] = "true"; await _systemSettingRepository.UpdateAsync(system); _systemSettingCacheManager.DelSystemSetting(SettingConstants.CancelPublishOrderEnabled); SetZuoXi(); var order = _orderServiceMock.CreateOrder() .办理到派单员() .办理到归档(SetPaiDanYuan, data => { data.IsEvasive = true; data.IsInactively = true; }) .GetCreateResult(); var publish = await _orderPublishRepository.GetAsync(m => m.No == order.No); publish.ShouldNotBeNull(order.No); var orderEntity = await _orderRepository.GetAsync(order.Id); orderEntity.ShouldNotBeNull(); orderEntity.IsEvasive.ShouldBeTrue(); orderEntity.IsInactively.ShouldBeTrue(); await _orderController.PublishCancelAsync(new PublishCancelInDto { OrderPublishId = publish.Id }); var visit = await _orderVisitRepository.GetAsync(m => m.OrderId == order.Id); var publishCount = await _orderPublishRepository.Queryable().Where(m => m.OrderId == order.Id && m.IsDeleted == false).CountAsync(); publishCount.ShouldBe(0, "发布工单数据已经被删除, 应该是0条"); visit.ShouldNotBeNull("回访信息不存在"); visit.VisitState.ShouldBe(EVisitState.Visited, "回访状态应该是已回访"); orderEntity = await _orderRepository.GetAsync(order.Id); orderEntity.Status.ShouldBe(EOrderStatus.Filed); var publishList = await _orderController.PublishOrderList(new QueryOrderPublishDto { PageSize = 1000, QuerySelf = true }); publishList.Items.Any(m => m.Id == order.Id).ShouldBeTrue("工单发布列表中没有取消的工单"); SetZuoXi(); // 验证是否能非上次发布人员发布 try { await _orderController.PublishOrder(new PublishOrderDto { Id = order.Id }); } catch (UserFriendlyException e) { e.Message.Contains("您不能发布").ShouldBeTrue(); } // 验证被取消前的发布人能否继续发布 SetPaiDanYuan(); var inDto = _fixture.Create(); inDto.Id = order.Id; await _orderController.PublishOrder(inDto); var published = await _orderPublishRepository.Queryable().Where(m => m.OrderId == order.Id && m.IsDeleted == false).FirstAsync(); published.ShouldNotBeNull(); published.IsDeleted.ShouldBeFalse(); } /// /// 工单批量标注 /// /// [Fact] public async Task OrderSignBath_Test() { var inDto = new OrderSignBathInDto { IsSafetyDepartment = true, Remark = "单元测试标注", OrderIds = new List() }; inDto.OrderIds.Add(_orderServiceMock.CreateSnapshotOrder(SetWeiXin).办理到工单标注(SetZuoXi).GetCreateResult().Id); inDto.OrderIds.Add(_orderServiceMock.CreateSnapshotOrder(SetWeiXin).办理到工单标注(SetZuoXi).GetCreateResult().Id); Set班长(); await _orderController.GetNextStepsWithRecommend(inDto.OrderIds.First()); var result = await _orderController.OrderSignBathAsync(inDto); result.Contains("标注完成").ShouldBe(true); } /// /// 中心直接归档-自动发布后: 回访详情,部门办件结果没有(满意)问题; /// /// [Fact] public async Task OrderVisitCentor_Test() { //ChangeAppScopeYiBin(); //var order = _orderServiceMock.CreateOrder() // .办理到归档() // .GetCreateResult(); ; //var orgVisit = await _orderVisitDetailRepository.Queryable() // .Where(m => m.VisitTarget == EVisitTarget.Org) // .FirstAsync(); //orgVisit.VisitContent.ShouldBe("满意"); } #region 工单流程 /// /// 工单从创建到归档完整流程 /// [Fact] public async Task OrderHandleTest() { SetZuoXi(); var flowInPaiDan = _orderServiceMock.CreateOrder() .办理到派单员(); var orderInfo = flowInPaiDan.GetCreateResult(); //todo check order var order = await _orderRepository.GetAsync(orderInfo.Id); order.ShouldNotBeNull(); order.Status.ShouldBe(EOrderStatus.Handling); order.WorkflowId.ShouldNotBeNull(); var workflow = await _workflowDomainService.GetWorkflowAsync(order.WorkflowId, withSteps: true, withTraces: true); workflow.ShouldNotBeNull(); workflow.Status.ShouldBe(EWorkflowStatus.Runnable); var step = workflow.Steps.FirstOrDefault(d=>d.BusinessType == EBusinessType.Send); var trace = workflow.Traces.FirstOrDefault(d => d.BusinessType == EBusinessType.Send); step.ShouldNotBeNull(); trace.ShouldNotBeNull(); step.Status.ShouldBe(EWorkflowStepStatus.WaitForAccept); trace.Status.ShouldBe(EWorkflowStepStatus.WaitForAccept); step.FlowAssignType.ShouldBe(EFlowAssignType.User); trace.FlowAssignType.ShouldBe(EFlowAssignType.User); step.RoleId.ShouldNotBeNull(); trace.RoleId.ShouldNotBeNull(); var flowInLevelOne = flowInPaiDan.办理到一级部门(SetPaiDanYuan); orderInfo = flowInLevelOne.GetCreateResult(); order = await _orderRepository.GetAsync(orderInfo.Id); order.ShouldNotBeNull(); order.Status.ShouldBe(EOrderStatus.Handling); order.WorkflowId.ShouldNotBeNull(); workflow = await _workflowDomainService.GetWorkflowAsync(order.WorkflowId, withSteps: true, withTraces: true); workflow.ShouldNotBeNull(); workflow.Status.ShouldBe(EWorkflowStatus.Runnable); step = workflow.Steps.FirstOrDefault(d=> d.Status == EWorkflowStepStatus.WaitForAccept); trace = workflow.Traces.FirstOrDefault(d=> d.Status == EWorkflowStepStatus.WaitForAccept); step.ShouldNotBeNull(); trace.ShouldNotBeNull(); step.BusinessType.ShouldBe(EBusinessType.Department); trace.BusinessType.ShouldBe(EBusinessType.Department); step.FlowAssignType.ShouldBe(EFlowAssignType.Org); trace.FlowAssignType.ShouldBe(EFlowAssignType.Org); step.HandlerOrgId.ShouldNotBeNull(); trace.HandlerOrgId.ShouldNotBeNull(); step.HandlerOrgId.CalcOrgLevel().ShouldBe(1); trace.HandlerOrgId.CalcOrgLevel().ShouldBe(1); var flowInLevelTwo = flowInLevelOne.办理到二级部门(Set一级部门); orderInfo = flowInLevelTwo.GetCreateResult(); order = await _orderRepository.GetAsync(orderInfo.Id); order.ShouldNotBeNull(); order.Status.ShouldBe(EOrderStatus.Handling); order.WorkflowId.ShouldNotBeNull(); workflow = await _workflowDomainService.GetWorkflowAsync(order.WorkflowId, withSteps: true, withTraces: true); workflow.ShouldNotBeNull(); workflow.Status.ShouldBe(EWorkflowStatus.Runnable); step = workflow.Steps.FirstOrDefault(d=> d.Status == EWorkflowStepStatus.WaitForAccept); trace = workflow.Traces.FirstOrDefault(d=> d.Status == EWorkflowStepStatus.WaitForAccept); step.ShouldNotBeNull(); trace.ShouldNotBeNull(); step.BusinessType.ShouldBe(EBusinessType.Department); trace.BusinessType.ShouldBe(EBusinessType.Department); step.FlowAssignType.ShouldBe(EFlowAssignType.Org); trace.FlowAssignType.ShouldBe(EFlowAssignType.Org); step.HandlerOrgId.ShouldNotBeNull(); trace.HandlerOrgId.ShouldNotBeNull(); step.HandlerOrgId.CalcOrgLevel().ShouldBe(2); trace.HandlerOrgId.CalcOrgLevel().ShouldBe(2); var flowInLevelOneSummary = flowInLevelTwo.办理一级部门汇总(Set二级部门); orderInfo = flowInLevelOneSummary.GetCreateResult(); order = await _orderRepository.GetAsync(orderInfo.Id); order.ShouldNotBeNull(); order.Status.ShouldBe(EOrderStatus.Handling); order.WorkflowId.ShouldNotBeNull(); workflow = await _workflowDomainService.GetWorkflowAsync(order.WorkflowId, withSteps: true, withTraces: true); workflow.ShouldNotBeNull(); workflow.Status.ShouldBe(EWorkflowStatus.Runnable); step = workflow.Steps.FirstOrDefault(d=> d.Status == EWorkflowStepStatus.WaitForAccept); trace = workflow.Traces.FirstOrDefault(d=> d.Status == EWorkflowStepStatus.WaitForAccept); step.ShouldNotBeNull(); trace.ShouldNotBeNull(); step.StepType.ShouldBe(EStepType.Summary); trace.StepType.ShouldBe(EStepType.Summary); step.BusinessType.ShouldBe(EBusinessType.Department); trace.BusinessType.ShouldBe(EBusinessType.Department); step.FlowAssignType.ShouldBe(EFlowAssignType.Org); trace.FlowAssignType.ShouldBe(EFlowAssignType.Org); step.HandlerOrgId.ShouldNotBeNull(); trace.HandlerOrgId.ShouldNotBeNull(); step.HandlerOrgId.CalcOrgLevel().ShouldBe(1); trace.HandlerOrgId.CalcOrgLevel().ShouldBe(1); var flowInEnd = flowInLevelOneSummary.办理到归档(Set一级部门); orderInfo = flowInEnd.GetCreateResult(); order = await _orderRepository.GetAsync(orderInfo.Id); order.ShouldNotBeNull(); order.Status.ShouldBe(EOrderStatus.Filed); order.FiledTime.ShouldNotBeNull(); order.WorkflowId.ShouldNotBeNull(); workflow = await _workflowDomainService.GetWorkflowAsync(order.WorkflowId, withSteps: true, withTraces: true); workflow.ShouldNotBeNull(); workflow.Status.ShouldBe(EWorkflowStatus.Completed); step = workflow.Steps.FirstOrDefault(d=> d.StepType == EStepType.End); trace = workflow.Traces.FirstOrDefault(d=> d.StepType == EStepType.End); step.ShouldNotBeNull(); trace.ShouldNotBeNull(); // // flowInEnd.发布工单(SetZuoXi) // .GetCreateResult(); } #endregion }