OrderControllerTest.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. using AutoFixture;
  2. using Hotline.Api.Controllers;
  3. using Hotline.Application.CallCenter;
  4. using Hotline.Caching.Interfaces;
  5. using Hotline.CallCenter.Calls;
  6. using Hotline.FlowEngine.Notifications;
  7. using Hotline.Identity.Accounts;
  8. using Hotline.Identity.Roles;
  9. using Hotline.Orders;
  10. using Hotline.Settings;
  11. using Hotline.Settings.Hotspots;
  12. using Hotline.Share.Dtos.CallCenter;
  13. using Hotline.Share.Dtos.Order;
  14. using Hotline.Share.Dtos.Order.Publish;
  15. using Hotline.Share.Dtos.Snapshot;
  16. using Hotline.Share.Dtos.TrCallCenter;
  17. using Hotline.Share.Enums.CallCenter;
  18. using Hotline.Share.Enums.FlowEngine;
  19. using Hotline.Share.Enums.Order;
  20. using Hotline.Share.Enums.Settings;
  21. using Hotline.Share.Tools;
  22. using Hotline.Snapshot.Interfaces;
  23. using Hotline.Tests.Dto;
  24. using Hotline.Tests.Mock;
  25. using Hotline.Tests.SqlSuger;
  26. using Hotline.Users;
  27. using Mapster;
  28. using MediatR;
  29. using Microsoft.AspNetCore.Http;
  30. using Microsoft.AspNetCore.Mvc;
  31. using Microsoft.Extensions.DependencyInjection;
  32. using Shouldly;
  33. using SqlSugar;
  34. using XF.Domain.Exceptions;
  35. using XF.Domain.Repository;
  36. namespace Hotline.Tests.Controller;
  37. public class OrderControllerTest : TestBase
  38. {
  39. private readonly OrderController _orderController;
  40. private readonly IRepository<Hotspot> _hotspotRepository;
  41. private readonly IOrderRepository _orderRepository;
  42. private readonly OrderServiceMock _orderServiceMock;
  43. private readonly IRepository<OrderPublish> _orderPublishRepository;
  44. private readonly INotificationHandler<EndWorkflowNotify> _orderPublishEndWorkflowHandler;
  45. private readonly IOrderVisitRepository _orderVisitRepository;
  46. private readonly IRepository<OrderVisitDetail> _orderVisitDetailRepository;
  47. private readonly IRepository<SystemSetting> _systemSettingRepository;
  48. private readonly ISystemSettingCacheManager _systemSettingCacheManager;
  49. private readonly IRepository<CallNative> _callNativeRepository;
  50. private readonly IRepository<CallidRelation> _callIdRelationRepository;
  51. private readonly XingTangCallApplication _defaultCallApplication;
  52. private readonly ISqlSugarClient _capSqlClient;
  53. private readonly IIndustryRepository _industryRepository;
  54. private readonly IOrderSnapshotRepository _orderSnapshotRepository;
  55. private readonly ISystemLogRepository _systemLogRepository;
  56. private readonly IOrderVisitDomainService _orderVisitDomainService;
  57. private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
  58. public OrderControllerTest(IAccountRepository accountRepository, IRepository<Role> roleRepository,
  59. UserController userController, IRepository<Hotspot> hotspotRepository, OrderController orderController,
  60. IOrderRepository orderRepository, IServiceScopeFactory scopeFactory, IRepository<User> userRepository,
  61. OrderServiceMock orderServiceMock, IRepository<OrderPublish> orderPublishRepository,
  62. INotificationHandler<EndWorkflowNotify> orderPublishEndWorkflowHandler,
  63. IOrderVisitRepository orderVisitRepository, IRepository<SystemSetting> systemSettingRepository,
  64. ISystemSettingCacheManager systemSettingCacheManager, IRepository<CallNative> callNativeRepository,
  65. IRepository<CallidRelation> callIdRelationRepository, XingTangCallApplication defaultCallApplication,
  66. ISugarUnitOfWork<CapDbContext> capDbContext, IHttpContextAccessor httpContextAccessor, IThirdIdentiyService thirdService,
  67. IThirdAccountRepository thirdAccount, IIndustryRepository industryRepository, IOrderSnapshotRepository orderSnapshotRepository,
  68. ISystemLogRepository systemLogRepository, IOrderVisitDomainService orderVisitDomainService, IRepository<OrderVisitDetail> orderVisitDetailRepository, ISystemDicDataCacheManager systemDicDataCacheManager)
  69. : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdService, thirdAccount)
  70. {
  71. _hotspotRepository = hotspotRepository;
  72. _orderController = orderController;
  73. _orderController.ControllerContext = new ControllerContext
  74. {
  75. HttpContext = new DefaultHttpContext()
  76. };
  77. _orderRepository = orderRepository;
  78. _orderServiceMock = orderServiceMock;
  79. _orderPublishRepository = orderPublishRepository;
  80. _orderPublishEndWorkflowHandler = orderPublishEndWorkflowHandler;
  81. _orderVisitRepository = orderVisitRepository;
  82. _systemSettingRepository = systemSettingRepository;
  83. _systemSettingCacheManager = systemSettingCacheManager;
  84. _callNativeRepository = callNativeRepository;
  85. _callIdRelationRepository = callIdRelationRepository;
  86. _defaultCallApplication = defaultCallApplication;
  87. _capSqlClient = capDbContext.Db;
  88. _industryRepository = industryRepository;
  89. _orderSnapshotRepository = orderSnapshotRepository;
  90. _systemLogRepository = systemLogRepository;
  91. _orderVisitDomainService = orderVisitDomainService;
  92. _orderVisitDetailRepository = orderVisitDetailRepository;
  93. _systemDicDataCacheManager = systemDicDataCacheManager;
  94. }
  95. /// <summary>
  96. /// 创建工单时传入一个错误callId
  97. /// 验证:
  98. /// 测试在延迟消息后会不会把工单测CallId修复成正确的CallId
  99. /// 推送的省上数据是否正确
  100. /// 测试修复后的CallId 再次保存功能, 能通过 systemLog 查询修复记录, 拦截工单保存
  101. /// </summary>
  102. /// <returns></returns>
  103. [Fact]
  104. public async Task OrderCallIdRelevance_Test()
  105. {
  106. var callNo = DateTime.Now.ToString("yyyyMMddhhmmss") + "Flow";
  107. var inDto = _fixture.Build<CallNative>()
  108. .With(m => m.Id, Ulid.NewUlid().ToString())
  109. .With(m => m.CallNo, callNo)
  110. .With(m => m.Direction, ECallDirection.In)
  111. .With(m => m.IsDeleted, false)
  112. .Create();
  113. await _callNativeRepository.AddAsync(inDto);
  114. var inDto2 = _fixture.Build<CallNative>()
  115. .With(m => m.Id, Ulid.NewUlid().ToString())
  116. .With(m => m.Direction, ECallDirection.Out)
  117. .With(m => m.CallNo, callNo)
  118. .With(m => m.AudioFile, string.Empty)
  119. .With(m => m.IsDeleted, false)
  120. .Create();
  121. await _callNativeRepository.AddAsync(inDto2);
  122. var callOrder = new CallidRelation
  123. {
  124. Id = callNo,
  125. CallId = inDto2.Id,
  126. };
  127. await _callIdRelationRepository.AddAsync(callOrder, CancellationToken.None);
  128. SetZuoXi();
  129. var order = _orderServiceMock.CreateOrder(callNo)
  130. .办理到派单员()
  131. .GetCreateResult();
  132. order.ShouldNotBeNull();
  133. await _defaultCallApplication.OrderRelateCallHandlerAsync(order.Id, CancellationToken.None);
  134. var orderEntity = await _orderRepository.GetAsync(order.Id);
  135. orderEntity.ShouldNotBeNull();
  136. orderEntity.CallId.ShouldBe(inDto.Id);
  137. var query = _capSqlClient.Queryable<PublishedEntity>()
  138. .Where(m => m.Name == "hotline.call.connect.order" && m.Content.Contains(order.Id));
  139. var sql = query.ToSqlString();
  140. sql.Contains("id").ShouldBeFalse("字段大小写错误" + sql);
  141. var published = await query.FirstAsync();
  142. published.ShouldNotBeNull("推送数据不存在");
  143. var message = published.Content.FromJson<Message<PublishCallRecrodDto>>();
  144. message.Value.Order.ShouldNotBeNull("推送的工单信息为空");
  145. message.Value.Order.ProvinceNo.ShouldNotBeNull("省工单编号不能为空");
  146. message.Value.Order.No.ShouldBe(order.No, "推送的工单编号错误");
  147. try
  148. {
  149. order = _orderServiceMock.CreateOrder(callNo)
  150. .办理到派单员()
  151. .GetCreateResult();
  152. }
  153. catch (UserFriendlyException e)
  154. {
  155. e.FriendlyMessage.ShouldBe("来电已保存工单!");
  156. }
  157. }
  158. [Fact]
  159. public async Task Visit_Test()
  160. {
  161. SetZuoXi();
  162. var order = _orderServiceMock.CreateOrder()
  163. .办理到一级部门()
  164. .办理到二级部门(Set一级部门)
  165. .办理一级部门汇总(Set二级部门)
  166. .办理到归档(Set一级部门)
  167. .发布工单(SetZuoXi)
  168. .GetCreateResult();
  169. var visit = await _orderVisitRepository.Queryable()
  170. .Where(m => m.OrderId == order.Id)
  171. .FirstAsync();
  172. var visitDetaila = await _orderVisitDetailRepository.Queryable().Where(m => m.VisitId == visit.Id).ToListAsync();
  173. var visitDetail = visitDetaila.Adapt<List<VisitDetailDto>>();
  174. visitDetail.ForEach(m => {
  175. m.SeatEvaluate = ESeatEvaluate.DefaultSatisfied;
  176. var smsReply = _orderVisitDomainService.GetVisitEvaluateByReplyTxt("0");
  177. m.OrgProcessingResults = smsReply.GetOrgProcessingResults(_systemDicDataCacheManager.VisitSatisfaction);
  178. m.OrgHandledAttitude = smsReply.GetOrgHandledAttitude(_systemDicDataCacheManager.VisitSatisfaction);
  179. });
  180. var callNo = DateTime.Now.ToString("yyyyMMddhhmmss") + "Flow";
  181. var inDto = _fixture.Build<CallNative>()
  182. .With(m => m.Id, Ulid.NewUlid().ToString())
  183. .With(m => m.CallNo, callNo)
  184. .With(m => m.Direction, ECallDirection.In)
  185. .With(m => m.IsDeleted, false)
  186. .Create();
  187. await _callNativeRepository.AddAsync(inDto);
  188. _systemLogRepository.Add("回访外呼已经接通", "", new CallRemarkDto { CallId = callNo, CallNumber = inDto.ToNo }.ToJson(), "", 1, visit.Id);
  189. var visitDto = new VisitDto
  190. {
  191. Id = visit.Id,
  192. IsPutThrough = true,
  193. VisitDetails = visitDetail
  194. };
  195. await _orderController.Visit(visitDto);
  196. }
  197. /// <summary>
  198. /// 创建工单并派送给派单员
  199. /// </summary>
  200. /// <returns></returns>
  201. [Fact]
  202. public async Task ToPaiDanYuan_Test()
  203. {
  204. SetZuoXi();
  205. var order = _orderServiceMock.CreateOrder()
  206. .办理到派单员()
  207. .GetCreateResult();
  208. order.ShouldNotBeNull();
  209. }
  210. /// <summary>
  211. /// 创建随手拍工单
  212. /// 验证随手拍工单是否能正常创建
  213. /// </summary>
  214. /// <returns></returns>
  215. [Fact]
  216. public async Task OrderSnapshot_Test()
  217. {
  218. var industryItems = await _industryRepository.Queryable() .Select(d => new { d.Id, d.Name, }) .ToListAsync();
  219. var industry = industryItems.First();
  220. SetZuoXi();
  221. var order = _orderServiceMock.CreateOrder(industryId:industry.Id, industryName: industry.Name)
  222. .办理到派单员()
  223. .办理到归档(SetPaiDanYuan, data =>
  224. {
  225. data.IsEvasive = true;
  226. data.IsInactively = true;
  227. })
  228. .GetCreateResult();
  229. var orderEntity = await _orderRepository.GetAsync(order.Id);
  230. orderEntity.ShouldNotBeNull();
  231. var snapshot = await _orderSnapshotRepository.GetAsync(m => m.Id == order.Id);
  232. snapshot.ShouldNotBeNull();
  233. snapshot.IndustryId.ShouldBe(industry.Id);
  234. snapshot.IndustryName.ShouldBe(industry.Name);
  235. }
  236. /// <summary>
  237. /// 验证中心直办工单归档后 自动发布
  238. /// 是否推诿, 是否不积极
  239. /// </summary>
  240. /// <returns></returns>
  241. [Fact]
  242. public async Task AutoPublish_Test()
  243. {
  244. SetZuoXi();
  245. var order = _orderServiceMock.CreateOrder()
  246. .办理到派单员()
  247. .办理到归档(SetPaiDanYuan, data =>
  248. {
  249. data.IsEvasive = true;
  250. data.IsInactively = true;
  251. })
  252. .GetCreateResult();
  253. var publish = await _orderPublishRepository.GetAsync(m => m.No == order.No);
  254. publish.ShouldNotBeNull(order.No);
  255. var orderEntity = await _orderRepository.GetAsync(order.Id);
  256. orderEntity.ShouldNotBeNull();
  257. orderEntity.IsEvasive.ShouldBeTrue();
  258. orderEntity.IsInactively.ShouldBeTrue();
  259. orderEntity.Status.ShouldBe(EOrderStatus.Visited);
  260. orderEntity.OrgProcessingResults.ShouldNotBeNull();
  261. orderEntity.OrgProcessingResults.Key.ShouldBe("4");
  262. orderEntity.OrgProcessingResults.Value.ShouldBe("满意");
  263. orderEntity.SeatEvaluate.ShouldBe(ESeatEvaluate.Satisfied);
  264. }
  265. [Fact]
  266. public void Set一级部门_Test()
  267. {
  268. Set一级部门();
  269. }
  270. [Fact]
  271. public async Task IsStepUrgent_True_Test()
  272. {
  273. SetZuoXi();
  274. var order = _orderServiceMock.CreateOrder()
  275. .办理到派单员()
  276. .办理到归档(SetPaiDanYuan, data => data.IsStepUrgent = true, workflow =>
  277. {
  278. workflow.BusinessType = EBusinessType.DepartmentLeader;
  279. workflow.Opinion = "测试保存勾选紧急";
  280. })
  281. .GetCreateResult();
  282. order.ShouldNotBeNull();
  283. order.Id.ShouldNotBeNull();
  284. var orderDto = await _orderController.Get(order.Id);
  285. orderDto.IsStepUrgent.ShouldBeTrue();
  286. orderDto.IsStepUrgenText.ShouldBe("紧急");
  287. }
  288. /// <summary>
  289. /// 测试在办理的过程中能正确保存
  290. /// 1.与市民电话联系
  291. /// 2.已赴现场处置
  292. /// 3.其他
  293. /// 4.其他原因
  294. /// 5.经办人
  295. /// 6.经办人电话
  296. /// 7.沟通时间
  297. /// 8.沟通地点
  298. /// </summary>
  299. /// <returns></returns>
  300. [Fact]
  301. public async Task CreateOrder_Test()
  302. {
  303. SetZuoXi();
  304. var order = _orderServiceMock.CreateOrder()
  305. .办理到一级部门()
  306. .办理到二级部门(Set一级部门)
  307. .办理一级部门汇总(Set二级部门)
  308. .GetCreateResult();
  309. order.Id.ShouldNotBeNull();
  310. var orderEntity = await _orderRepository.GetAsync(order.Id);
  311. orderEntity.RealCommunicationAddress.ShouldNotBeNull();
  312. orderEntity.RealCommunicationTime.ShouldNotBeNull();
  313. orderEntity.RealContactLocale.ShouldNotBeNull();
  314. orderEntity.RealContactLocale.ShouldBe(true);
  315. orderEntity.RealHandlerName.ShouldNotBeNull();
  316. orderEntity.RealHandlerPhone.ShouldNotBeNull();
  317. }
  318. [Fact]
  319. public async Task GetOrderHandleTimeConfigByAcceptType_Test()
  320. {
  321. var result = await _orderController.GetOrderHandleTimeConfigByAcceptType(Share.Enums.FlowEngine.EFlowDirection.OrgToCenter, "10");
  322. result.Count.ShouldBe(1);
  323. result.TimeText.ShouldBe("1个工作日");
  324. result.TimeType.ShouldBe(ETimeType.WorkDay);
  325. }
  326. /// <summary>
  327. /// 测试取消发布功能
  328. /// </summary>
  329. /// <returns></returns>
  330. [Fact]
  331. public async Task CancelOrderPublish_Test()
  332. {
  333. var system = await _systemSettingRepository.Queryable()
  334. .Where(m => m.Code == SettingConstants.CancelPublishOrderEnabled)
  335. .FirstAsync();
  336. system.SettingValue[0] = "true";
  337. await _systemSettingRepository.UpdateAsync(system);
  338. _systemSettingCacheManager.DelSystemSetting(SettingConstants.CancelPublishOrderEnabled);
  339. SetZuoXi();
  340. var order = _orderServiceMock.CreateOrder()
  341. .办理到派单员()
  342. .办理到归档(SetPaiDanYuan, data =>
  343. {
  344. data.IsEvasive = true;
  345. data.IsInactively = true;
  346. })
  347. .GetCreateResult();
  348. var publish = await _orderPublishRepository.GetAsync(m => m.No == order.No);
  349. publish.ShouldNotBeNull(order.No);
  350. var orderEntity = await _orderRepository.GetAsync(order.Id);
  351. orderEntity.ShouldNotBeNull();
  352. orderEntity.IsEvasive.ShouldBeTrue();
  353. orderEntity.IsInactively.ShouldBeTrue();
  354. await _orderController.PublishCancelAsync(new PublishCancelInDto { OrderPublishId = publish.Id });
  355. var visit = await _orderVisitRepository.GetAsync(m => m.OrderId == order.Id);
  356. var publishCount = await _orderPublishRepository.Queryable().Where(m => m.OrderId == order.Id && m.IsDeleted == false).CountAsync();
  357. publishCount.ShouldBe(0, "发布工单数据已经被删除, 应该是0条");
  358. visit.ShouldNotBeNull("回访信息不存在");
  359. visit.VisitState.ShouldBe(EVisitState.Visited, "回访状态应该是已回访");
  360. orderEntity = await _orderRepository.GetAsync(order.Id);
  361. orderEntity.Status.ShouldBe(EOrderStatus.Filed);
  362. var publishList = await _orderController.PublishOrderList(new QueryOrderPublishDto
  363. {
  364. PageSize = 1000,
  365. QuerySelf = true
  366. });
  367. publishList.Items.Any(m => m.Id == order.Id).ShouldBeTrue("工单发布列表中没有取消的工单");
  368. SetZuoXi();
  369. // 验证是否能非上次发布人员发布
  370. try
  371. {
  372. await _orderController.PublishOrder(new PublishOrderDto { Id = order.Id });
  373. }
  374. catch (UserFriendlyException e)
  375. {
  376. e.Message.Contains("您不能发布").ShouldBeTrue();
  377. }
  378. // 验证被取消前的发布人能否继续发布
  379. SetPaiDanYuan();
  380. var inDto = _fixture.Create<PublishOrderDto>();
  381. inDto.Id = order.Id;
  382. await _orderController.PublishOrder(inDto);
  383. var published = await _orderPublishRepository.Queryable().Where(m => m.OrderId == order.Id && m.IsDeleted == false).FirstAsync();
  384. published.ShouldNotBeNull();
  385. published.IsDeleted.ShouldBeFalse();
  386. }
  387. /// <summary>
  388. /// 工单批量标注
  389. /// </summary>
  390. /// <returns></returns>
  391. [Fact]
  392. public async Task OrderSignBath_Test()
  393. {
  394. var inDto = new OrderSignBathInDto
  395. {
  396. IsSafetyDepartment = true,
  397. Remark = "单元测试标注",
  398. OrderIds = new List<string>()
  399. };
  400. inDto.OrderIds.Add(_orderServiceMock.CreateSnapshotOrder(SetWeiXin).办理到工单标注(SetZuoXi).GetCreateResult().Id);
  401. inDto.OrderIds.Add(_orderServiceMock.CreateSnapshotOrder(SetWeiXin).办理到工单标注(SetZuoXi).GetCreateResult().Id);
  402. Set班长();
  403. await _orderController.GetNextStepsWithRecommend(inDto.OrderIds.First());
  404. var result = await _orderController.OrderSignBathAsync(inDto);
  405. result.Contains("标注完成").ShouldBe(true);
  406. }
  407. }