OrderApplication.cs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. using DotNetCore.CAP;
  2. using Hotline.Application.Quality;
  3. using Hotline.Caching.Interfaces;
  4. using Hotline.File;
  5. using Hotline.FlowEngine.Workflows;
  6. using Hotline.Orders;
  7. using Hotline.Repository.SqlSugar.Extensions;
  8. using Hotline.Repository.SqlSugar.Ts;
  9. using Hotline.Settings.TimeLimits;
  10. using Hotline.Share.Dtos;
  11. using Hotline.Share.Dtos.DataSharing.PusherHotlineDto;
  12. using Hotline.Share.Dtos.File;
  13. using Hotline.Share.Dtos.FlowEngine;
  14. using Hotline.Share.Dtos.FlowEngine.Workflow;
  15. using Hotline.Share.Dtos.Order;
  16. using Hotline.Share.Dtos.Settings;
  17. using Hotline.Share.Enums.Order;
  18. using Hotline.Share.Enums.Quality;
  19. using Hotline.Share.Enums.Settings;
  20. using Hotline.Share.Requests;
  21. using Hotline.Tools;
  22. using MapsterMapper;
  23. using Microsoft.AspNetCore.Mvc.Formatters;
  24. using Microsoft.EntityFrameworkCore.Metadata.Internal;
  25. using Novacode;
  26. using SqlSugar;
  27. using System.Data;
  28. using System.Dynamic;
  29. using Hotline.Settings;
  30. using XF.Domain.Authentications;
  31. using XF.Domain.Constants;
  32. using XF.Domain.Dependency;
  33. using XF.Domain.Exceptions;
  34. using XF.Domain.Repository;
  35. namespace Hotline.Application.Orders;
  36. public class OrderApplication : IOrderApplication, IScopeDependency
  37. {
  38. private readonly IOrderDomainService _orderDomainService;
  39. private readonly IWorkflowDomainService _workflowDomainService;
  40. private readonly IOrderRepository _orderRepository;
  41. private readonly ITimeLimitDomainService _timeLimitDomainService;
  42. private readonly IMapper _mapper;
  43. private readonly ISystemSettingCacheManager _systemSettingCacheManager;
  44. private readonly IRepository<OrderWord> _orderWrodRepository;
  45. private readonly IRepositoryTextSearch<OrderTs> _repositoryts;
  46. private readonly IFileRepository _fileRepository;
  47. private readonly ISessionContext _sessionContext;
  48. private readonly IRepository<OrderVisit> _orderVisitRepository;
  49. private readonly IRepository<OrderVisitDetail> _orderVisitDetailRepository;
  50. private readonly IQualityApplication _qualityApplication;
  51. private readonly ICapPublisher _capPublisher;
  52. public OrderApplication(
  53. IOrderDomainService orderDomainService,
  54. IOrderRepository orderRepository,
  55. IWorkflowDomainService workflowDomainService,
  56. ITimeLimitDomainService timeLimitDomainService,
  57. ISystemSettingCacheManager systemSettingCacheManager,
  58. IMapper mapper,
  59. IRepository<OrderWord> orderWrodRepository,
  60. IRepositoryTextSearch<OrderTs> repositoryts,
  61. IFileRepository fileRepository,
  62. ISessionContext sessionContext,
  63. IRepository<OrderVisit> orderVisitRepository,
  64. IRepository<OrderVisitDetail> orderVisitDetailRepository,
  65. IQualityApplication qualityApplication,
  66. ICapPublisher capPublisher
  67. )
  68. {
  69. _orderDomainService = orderDomainService;
  70. _workflowDomainService = workflowDomainService;
  71. _orderRepository = orderRepository;
  72. _timeLimitDomainService = timeLimitDomainService;
  73. _mapper = mapper;
  74. _systemSettingCacheManager = systemSettingCacheManager;
  75. _orderWrodRepository = orderWrodRepository;
  76. _repositoryts = repositoryts;
  77. _fileRepository = fileRepository;
  78. _sessionContext = sessionContext;
  79. _orderVisitRepository = orderVisitRepository;
  80. _orderVisitDetailRepository = orderVisitDetailRepository;
  81. _qualityApplication = qualityApplication;
  82. _capPublisher = capPublisher;
  83. }
  84. /// <summary>
  85. /// 更新工单办理期满时间(延期调用,其他不调用)
  86. /// 1.更新工单 2.更新流程 3.推送省平台
  87. /// </summary>
  88. /// <returns></returns>
  89. public async Task DelayOrderExpiredTimeAsync(string orderId, int timeCount, ETimeType timeType, CancellationToken cancellationToken)
  90. {
  91. var order = await _orderDomainService.GetOrderAsync(orderId, cancellationToken: cancellationToken);
  92. var expiredTimeConfig =
  93. _timeLimitDomainService.CalcEndTime(DateTime.Now, new TimeConfig(timeCount, timeType), order.AcceptTypeCode);
  94. order.TimeLimit = expiredTimeConfig.TimeText;
  95. order.TimeLimitCount = expiredTimeConfig.Count;
  96. order.TimeLimitUnit = expiredTimeConfig.TimeType;
  97. order.ExpiredTime = expiredTimeConfig.ExpiredTime;
  98. order.NearlyExpiredTime = expiredTimeConfig.NearlyExpiredTime;
  99. //if (string.IsNullOrEmpty(order.WorkflowId))
  100. // throw new UserFriendlyException("该工单流程id异常");
  101. //var workflow = await _workflowDomainService.GetWorkflowAsync(order.WorkflowId, cancellationToken: cancellationToken);
  102. //await _workflowDomainService.UpdateExpiredTimeAsync(workflow, expiredTimeConfig.ExpiredTime,
  103. // expiredTimeConfig.TimeText, expiredTimeConfig.Count, expiredTimeConfig.TimeType, expiredTimeConfig.NearlyExpiredTime, cancellationToken);
  104. if (string.IsNullOrEmpty(order.WorkflowId))
  105. throw new UserFriendlyException("该工单流程id异常");
  106. await _workflowDomainService.UpdateUnhandleExpiredTimeAsync(order.WorkflowId, expiredTimeConfig.ExpiredTime, cancellationToken);
  107. await _orderRepository.UpdateAsync(order, cancellationToken);
  108. }
  109. /// <summary>
  110. /// 新增工单办理流程记录
  111. /// </summary>
  112. public async Task AddOrderTracesAsync(string orderId, ICollection<WorkflowTraceDto> traces, CancellationToken cancellationToken)
  113. {
  114. var order = await _orderRepository.GetAsync(orderId, cancellationToken);
  115. if (order is null)
  116. throw new UserFriendlyException("工单不存在");
  117. if (string.IsNullOrEmpty(order.WorkflowId))
  118. throw new UserFriendlyException("工单未开启流程");
  119. await _workflowDomainService.AddTracesAsync(order.WorkflowId, _mapper.Map<List<WorkflowTrace>>(traces),
  120. cancellationToken);
  121. }
  122. /// <summary>
  123. /// 撤销工单
  124. /// </summary>
  125. public async Task CancelOrderAsync(string orderId, string opinion, CancellationToken cancellationToken)
  126. {
  127. var order = await _orderRepository.GetAsync(orderId, cancellationToken);
  128. if (order is null)
  129. throw new UserFriendlyException("工单不存在");
  130. if (!string.IsNullOrEmpty(order.WorkflowId))
  131. {
  132. //结束流程
  133. await _workflowDomainService.TerminateAsync(new TerminateDto
  134. {
  135. WorkflowId = order.WorkflowId,
  136. Opinion = opinion
  137. }, cancellationToken);
  138. }
  139. //归档工单
  140. var now = DateTime.Now;
  141. var handleDuration = order.StartTime.HasValue
  142. ? _timeLimitDomainService.CalcWorkTime(order.StartTime.Value,
  143. now, order.ProcessType is EProcessType.Zhiban)
  144. : 0;
  145. var fileDuration = order.CenterToOrgTime.HasValue
  146. ? _timeLimitDomainService.CalcWorkTime(order.CenterToOrgTime.Value,
  147. now, order.ProcessType is EProcessType.Zhiban)
  148. : 0;
  149. var allDuration = order.StartTime.HasValue
  150. ? _timeLimitDomainService.CalcWorkTime(order.StartTime.Value, now,
  151. order.ProcessType is EProcessType.Zhiban)
  152. : 0;
  153. order.File(now, handleDuration, fileDuration, allDuration);
  154. await _orderRepository.UpdateAsync(order, cancellationToken);
  155. }
  156. /// <summary>
  157. /// 即将超期列表
  158. /// </summary>
  159. /// <param name="dto"></param>
  160. /// <param name="cancellationToken"></param>
  161. /// <returns></returns>
  162. public ISugarQueryable<Order> GetAboutToExpireAsync(AboutToExpireListDto dto)
  163. {
  164. var setting = _systemSettingCacheManager.GetSetting(SettingConstants.OrderAboutToExpire);
  165. var value = setting?.SettingValue[0];
  166. value = string.IsNullOrEmpty(value) ? "0" : value;
  167. DateTime stTime = DateTime.Now.AddDays(int.Parse(value));
  168. stTime = _timeLimitDomainService.WorkDay(DateTime.Now);
  169. DateTime stTime2 = _timeLimitDomainService.WorkDay(DateTime.Now);
  170. return _orderRepository.Queryable(canView: true)
  171. .WhereIF(dto.IsProvince.HasValue, d => d.IsProvince == dto.IsProvince)
  172. .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No.Contains(dto.No!))
  173. .WhereIF(!string.IsNullOrEmpty(dto.Title), d => d.Title.Contains(dto.Title!))
  174. .WhereIF(dto.Delay.HasValue && dto.Delay == true,d=>d.OrderDelays.Any() == true)
  175. .WhereIF(dto.Delay.HasValue && dto.Delay == false, d => d.OrderDelays.Any() == false)
  176. .Where(d => d.ExpiredTime != null &&
  177. d.Status != EOrderStatus.Filed && d.Status != EOrderStatus.Published && d.Status != EOrderStatus.Visited && stTime >= d.ExpiredTime.Value && stTime2 <= d.ExpiredTime.Value)
  178. .OrderByDescending(d => d.CreationTime);
  179. }
  180. // /// <summary>
  181. // /// 即将超期节点列表
  182. // /// </summary>
  183. // /// <param name="dto"></param>
  184. // /// <param name="cancellationToken"></param>
  185. // /// <returns></returns>
  186. //public async Task<PagedDto<WorkflowOrderDto>> GetAboutToExpireNodeAsync(AboutToExpireListDto dto, CancellationToken cancellationToken)
  187. //{
  188. // var setting = _systemSettingCacheManager.GetSetting(SettingConstants.OrderAboutToExpire);
  189. // var value = setting?.SettingValue[0];
  190. // value = string.IsNullOrEmpty(value) ? "0" : value;
  191. // DateTime stTime = DateTime.Now.AddDays(int.Parse(value));
  192. // stTime = _timeLimitDomainService.WorkDay(DateTime.Now);
  193. // DateTime stTime2 = _timeLimitDomainService.WorkDay(DateTime.Now);
  194. // RefAsync<int> total = 0;
  195. // var items = await Db.Queryable<Workflow>()
  196. // .LeftJoin<Order>((x, o) => x.ExternalId == o.Id)
  197. // .Where(x => x.ModuleCode == "OrderHandle")
  198. // .WhereIF(dto.IsProvince.HasValue, (x, o) => o.IsProvince == dto.IsProvince)
  199. // .WhereIF(!string.IsNullOrEmpty(dto.Keyword), (x, o) => o.Title.Contains(dto.Keyword!) || o.No.Contains(dto.Keyword!))
  200. // .Where((x, o) => (int)x.Status < 20 && stTime >= x.ExpiredTime && stTime2 <= x.ExpiredTime)
  201. // .Select((x, o) => new WorkflowOrder { Order = o }, true)
  202. // .OrderByDescending(x => x.CreationTime)
  203. // .ToPageListAsync(dto.PageIndex, dto.PageSize, total, cancellationToken);
  204. // return new PagedDto<WorkflowOrderDto>(total, _mapper.Map<IReadOnlyList<WorkflowOrderDto>>(items));
  205. //}
  206. /// <summary>
  207. /// 已超期列表
  208. /// </summary>
  209. /// <param name="dto"></param>
  210. /// <param name="cancellationToken"></param>
  211. /// <returns></returns>
  212. public ISugarQueryable<Order> GetToExpireAsync(AboutToExpireListDto dto)
  213. {
  214. DateTime stTime = _timeLimitDomainService.WorkDay(DateTime.Now);
  215. return _orderRepository.Queryable(canView: true)
  216. .WhereIF(dto.IsProvince.HasValue, d => d.IsProvince == dto.IsProvince)
  217. //.WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Title.Contains(dto.Keyword!) || d.No.Contains(dto.Keyword!))
  218. .WhereIF(!string.IsNullOrEmpty(dto.No), x => x.No.Contains(dto.No))
  219. .WhereIF(!string.IsNullOrEmpty(dto.Title), x => x.Title.Contains(dto.Title!))
  220. .WhereIF(dto.Delay.HasValue && dto.Delay == true, d => d.OrderDelays.Any() == true)
  221. .WhereIF(dto.Delay.HasValue && dto.Delay == false, d => d.OrderDelays.Any() == false)
  222. .Where(d => d.ExpiredTime != null &&
  223. (((d.Status == EOrderStatus.Filed || d.Status == EOrderStatus.Published || d.Status == EOrderStatus.Visited) && d.FiledTime >= d.ExpiredTime) ||
  224. ((d.Status != EOrderStatus.Filed && d.Status != EOrderStatus.Published && d.Status != EOrderStatus.Visited) && stTime >= d.ExpiredTime.Value)))
  225. .OrderByDescending(x => x.CreationTime);
  226. }
  227. // /// <summary>
  228. // /// 已超期节点列表
  229. // /// </summary>
  230. // /// <param name="dto"></param>
  231. // /// <param name="cancellationToken"></param>
  232. // /// <returns></returns>
  233. // public async Task<PagedDto<WorkflowOrderDto>> GetToExpireNodeAsync(AboutToExpireListDto dto, CancellationToken cancellationToken)
  234. // {
  235. // DateTime stTime = _timeLimitDomainService.WorkDay(DateTime.Now);
  236. //RefAsync<int> total = 0;
  237. //var items= await Db.Queryable<Workflow>()
  238. // .LeftJoin<Order>((x,o)=>x.ExternalId == o.Id)
  239. // .Where(x => x.ModuleCode == "OrderHandle")
  240. // .WhereIF(dto.IsProvince.HasValue, (x, o) => o.IsProvince == dto.IsProvince)
  241. // .WhereIF(!string.IsNullOrEmpty(dto.Keyword), (x, o) => o.Title.Contains(dto.Keyword!) || o.No.Contains(dto.Keyword!))
  242. // .Where((x,o) => (((int)x.Status >= 20 && x.EndTime >= x.ExpiredTime) || ((int)x.Status < 20 && stTime >= x.ExpiredTime)))
  243. // .Select((x, o) => new WorkflowOrder { Order = o }, true)
  244. // .OrderByDescending(x => x.CreationTime)
  245. // .ToPageListAsync(dto.PageIndex, dto.PageSize, total, cancellationToken);
  246. // return new PagedDto<WorkflowOrderDto>(total, _mapper.Map<IReadOnlyList<WorkflowOrderDto>>(items));
  247. // }
  248. /// <summary>
  249. /// 工单关键字分词
  250. /// </summary>
  251. /// <param name="inputStr"></param>
  252. /// <returns></returns>
  253. public async Task OrderParticiple(string inputStr, string orderId, CancellationToken cancellationToken)
  254. {
  255. var words = await _orderWrodRepository.Queryable().Where(x => x.IsEnable == 1 && x.Classify.Contains("普通标签")).Select(x => x.Tag).ToListAsync(cancellationToken);
  256. var res = new List<string>();
  257. if (words.Any()) res = ParticipleTool.SegMMDouble(inputStr, ref words);
  258. var participles = await _orderWrodRepository.Queryable().In(x => x.Tag, res).ToListAsync(cancellationToken);
  259. if (participles.Any())
  260. {
  261. //关键词
  262. var tags = participles.Select(x => x.Tag).ToList();
  263. var tagsStr = string.Join(",", tags);
  264. await _orderRepository.Updateable().SetColumns(x => x.TagNames == tagsStr).Where(x => x.Id == orderId).ExecuteCommandAsync(cancellationToken);
  265. List<string> synonyms = participles.Select(x => x.Synonym).ToList();
  266. if (synonyms.Any())
  267. {
  268. var synonymsStr = string.Join(",", synonyms);
  269. synonyms = synonymsStr.Split(",").Distinct().ToList();
  270. tags.AddRange(synonyms);
  271. }
  272. var vector = await _orderRepository.Queryable().Where(x => x.Id == orderId).ToListAsync(cancellationToken);
  273. if (vector.Any()) await _repositoryts.UpdateVectorAsync(orderId, tags, cancellationToken);
  274. else await _repositoryts.AddVectorAsync(orderId, DateTime.Now, tags, cancellationToken);
  275. }
  276. }
  277. public async Task OrderSensitiveParticiple(string inputStr, string orderId, CancellationToken cancellationToken)
  278. {
  279. var words = await _orderWrodRepository.Queryable().Where(x => x.IsEnable == 1 && x.Classify.Contains("敏感标签")).Select(x => x.Tag).ToListAsync(cancellationToken);
  280. var res = new List<string>();
  281. if (words.Any()) res = ParticipleTool.SegMMDouble(inputStr, ref words);
  282. if (res.Any())
  283. {
  284. var intersect = words.Intersect(res).ToList();
  285. await _orderRepository.Updateable().SetColumns(o => new Order() { Sensitive = intersect }).Where(o => o.Id == orderId).ExecuteCommandAsync(cancellationToken);
  286. }
  287. }
  288. /// <summary>
  289. /// 接收外部平台工单
  290. /// </summary>
  291. public Task<AddOrderResponse> ReceiveOrderFromExternalAsync(AddOrderDto dto, ISessionContext current, CancellationToken cancellationToken)
  292. {
  293. switch (dto.Source)
  294. {
  295. case ESource.ProvinceStraight:
  296. return ReceiveOrderFromProvinceAsync(dto, dto.Files, current, cancellationToken);
  297. case ESource.Police110:
  298. case ESource.CityDataExchangeLz:
  299. case ESource.CityDataExchangeYB:
  300. case ESource.CityDataExchangeZG:
  301. case ESource.CityDataExchangeNJ:
  302. case ESource.WebPortal:
  303. case ESource.ConvergenceMedia:
  304. case ESource.IYIBIN:
  305. case ESource.ZHYB:
  306. case ESource.ZZPT:
  307. case ESource.WLLZ:
  308. return ReceiveOrderFromOtherPlatformAsync(dto, dto.Files, current, cancellationToken);
  309. case ESource.Hotline:
  310. case ESource.HotlineImport:
  311. default:
  312. throw new ArgumentOutOfRangeException();
  313. }
  314. }
  315. /// <summary>
  316. /// 接收外部平台修改工单附件
  317. /// </summary>
  318. public async Task UpdateOrderFilesAnonymousAsync(UpdateOrderFilesDto dto, CancellationToken cancellationToken)
  319. {
  320. if (string.IsNullOrEmpty(dto.Id) && string.IsNullOrEmpty(dto.OrderNo))
  321. throw new UserFriendlyException("工单外部编号不能为空");
  322. var order = await _orderRepository.Queryable()
  323. .FirstAsync(d => d.Id == dto.Id || d.No == dto.OrderNo, cancellationToken);
  324. if (order != null && dto.Files != null && dto.Files.Any())
  325. {
  326. order.FileJson = await _fileRepository.AddFileAsync(dto.Files, order.Id, "", cancellationToken);
  327. await _orderRepository.UpdateAsync(order, cancellationToken);
  328. }
  329. }
  330. /// <summary>
  331. /// 工单回访
  332. /// </summary>
  333. /// <param name="dto"></param>
  334. /// <param name="cancellationToken"></param>
  335. /// <returns></returns>
  336. public async Task OrderVisitWeb(OrderVisitWebDto dto, CancellationToken cancellationToken)
  337. {
  338. var visit = await _orderVisitRepository.Queryable()
  339. .Includes(x => x.Order)
  340. .Includes(x => x.OrderVisitDetails)
  341. .FirstAsync(x => x.Id == dto.Id);
  342. if (visit != null)
  343. {
  344. var first = dto.OrderVisitDetailDto.FirstOrDefault(x => x.VisitTarget == EVisitTarget.Org);
  345. if (first != null)
  346. {
  347. visit.NowEvaluate = first.OrgProcessingResults;
  348. visit.Order.Visited(first.OrgProcessingResults.Key, first.OrgProcessingResults.Value);
  349. }
  350. visit.VisitState = EVisitState.Visited;
  351. visit.VisitTime = dto.VisitTime;
  352. visit.VisitType = dto.VisitType;
  353. for (int i = 0; i < visit.OrderVisitDetails.Count; i++)
  354. {
  355. var detail = visit.OrderVisitDetails[i];
  356. var detaildto = dto.OrderVisitDetailDto.FirstOrDefault(x => x.Id == detail.Id);
  357. if (detaildto != null)
  358. {
  359. _mapper.Map(detaildto, visit.OrderVisitDetails[i]);
  360. }
  361. }
  362. await _orderVisitRepository.UpdateAsync(visit, cancellationToken);
  363. await _orderVisitDetailRepository.UpdateRangeAsync(visit.OrderVisitDetails, cancellationToken);
  364. await _orderRepository.UpdateAsync(visit.Order, cancellationToken);
  365. var orderDto = _mapper.Map<OrderDto>(visit.Order);
  366. if (first != null)
  367. {
  368. //推省上
  369. await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderVisited,
  370. new PublishVisitDto()
  371. {
  372. Order = orderDto,
  373. No = visit.No,
  374. VisitType = visit.VisitType,
  375. VisitName = visit.CreatorName,
  376. VisitTime = visit.VisitTime,
  377. VisitRemark = string.IsNullOrEmpty(first.VisitContent) ? first.OrgProcessingResults?.Value : first.VisitContent,
  378. AreaCode = visit.Order.AreaCode!,
  379. SubjectResultSatifyCode = first.OrgProcessingResults.Key,
  380. FirstSatisfactionCode = visit.Order.FirstVisitResultCode!,
  381. ClientGuid = ""
  382. }, cancellationToken: cancellationToken);
  383. }
  384. //写入质检
  385. await _qualityApplication.AddQualityAsync(EQualitySource.Visit, orderDto.Id, visit.Id,
  386. cancellationToken);
  387. }
  388. }
  389. public ISugarQueryable<Order> QueryOrders(QueryOrderDto dto)
  390. {
  391. var isCenter = _sessionContext.OrgIsCenter;
  392. return _orderRepository.Queryable(canView: isCenter ? false : true)
  393. .Includes(x => x.OrderScreens)
  394. .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Title.Contains(dto.Keyword!)) //标题
  395. .WhereIF(!string.IsNullOrEmpty(dto.ProvinceNo), d => d.ProvinceNo.Contains(dto.ProvinceNo)) //省本地编号
  396. .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No.Contains(dto.No)) //工单编码
  397. //.WhereIF(!string.IsNullOrEmpty(dto.Content), d => d.Content.Contains(dto.Content!))
  398. .WhereIF(dto.AcceptTypes.Any(), d => dto.AcceptTypes.Contains(d.AcceptTypeCode)) //受理类型
  399. .WhereIF(dto.Channels.Any(), d => dto.Channels.Contains(d.SourceChannelCode)) //来源渠道
  400. .WhereIF(dto.HotspotIds.Any(), d => dto.HotspotIds.Contains(d.HotspotId)) //热点类型
  401. .WhereIF(!string.IsNullOrEmpty(dto.TransferPhone), d => d.TransferPhone.Contains(dto.TransferPhone!)) //转接号码
  402. //.WhereIF(dto.OrgCodes.Any(), d => d.Workflow.Assigns.Any(s => dto.OrgCodes.Contains(s.OrgCode)))
  403. .WhereIF(dto.OrgCodes.Any(), d => dto.OrgCodes.Contains(d.ActualHandleOrgCode)) //接办部门
  404. .WhereIF(!string.IsNullOrEmpty(dto.NameOrNo), d => d.AcceptorName.Contains(dto.NameOrNo!) || d.AcceptorStaffNo.Contains(dto.NameOrNo!)) //受理人/坐席
  405. .WhereIF(dto.CreationTimeStart.HasValue, d => d.CreationTime >= dto.CreationTimeStart) //受理时间开始
  406. .WhereIF(dto.CreationTimeEnd.HasValue, d => d.CreationTime <= dto.CreationTimeEnd) //受理时间结束
  407. .WhereIF(dto.EmergencyLevels.Any(), d => dto.EmergencyLevels.Contains(d.EmergencyLevel)) //紧急程度
  408. .WhereIF(!string.IsNullOrEmpty(dto.FromPhone), d => d.FromPhone.Contains(dto.FromPhone)) //来电号码
  409. .WhereIF(!string.IsNullOrEmpty(dto.PhoneNo), d => d.Contact.Contains(dto.PhoneNo!)) //联系电话
  410. .WhereIF(!string.IsNullOrEmpty(dto.PushTypeCode), d => d.PushTypeCode == dto.PushTypeCode) //推送分类
  411. .WhereIF(dto.ExpiredTimeStart.HasValue, d => d.ExpiredTime >= dto.ExpiredTimeStart) //超期时间开始
  412. .WhereIF(dto.ExpiredTimeEnd.HasValue, d => d.ExpiredTime <= dto.ExpiredTimeEnd) //超期时间结束
  413. .WhereIF(dto.Statuses.Any(), d => dto.Statuses.Contains(d.Status)) //工单状态
  414. .WhereIF(dto.Statuses.Any(d => d == EOrderStatus.SpecialToUnAccept), d => d.Status <= EOrderStatus.SpecialToUnAccept)
  415. .WhereIF(!string.IsNullOrEmpty(dto.ActualHandlerName), d => d.ActualHandlerName.Contains(dto.ActualHandlerName)) //接办人
  416. .WhereIF(dto.IsScreen == true, d => d.OrderScreens.Any(x => x.Status != EScreenStatus.Refuse)) //有甄别
  417. .WhereIF(dto.IsScreen == false, d => !d.OrderScreens.Any(x => x.Status != EScreenStatus.Refuse)) //无甄别
  418. .WhereIF(!string.IsNullOrEmpty(dto.CurrentStepCode), d => d.ActualHandleStepCode == dto.CurrentStepCode) //当前办理节点
  419. .WhereIF(dto.ActualHandleTimeStart.HasValue, d => d.ActualHandleTime >= dto.ActualHandleTimeStart) //办结时间开始
  420. .WhereIF(dto.ActualHandleTimeEnd.HasValue, d => d.ActualHandleTime <= dto.ActualHandleTimeEnd) //办结时间结束
  421. .WhereIF(dto.IsOverTime == true, d => (d.ExpiredTime < DateTime.Now && d.Status < EOrderStatus.Filed) || (d.ExpiredTime < d.ActualHandleTime && d.Status >= EOrderStatus.Filed)) //是 超期
  422. .WhereIF(dto.IsOverTime == false, d => (d.ExpiredTime > DateTime.Now && d.Status < EOrderStatus.Filed) || (d.ExpiredTime > d.ActualHandleTime && d.Status >= EOrderStatus.Filed)) //否 超期
  423. .WhereIF(dto.IdentityType != null, d => d.IdentityType == dto.IdentityType) //来电主体
  424. .WhereIF(!string.IsNullOrEmpty(dto.FromName), d => d.FromName.Contains(dto.FromName)) //来电人姓名
  425. .WhereIF(dto.AreaCodes.Any(), d => dto.AreaCodes.Contains(d.AreaCode)) //区域
  426. .WhereIF(dto.IsProvinceOrder.HasValue && dto.IsProvinceOrder == true, x => x.IsProvince == true)
  427. .WhereIF(dto.IsProvinceOrder.HasValue && dto.IsProvinceOrder == false, x => x.IsProvince == false)
  428. .WhereIF(!string.IsNullOrEmpty(dto.SensitiveWord), x => SqlFunc.JsonArrayAny(x.Sensitive, dto.SensitiveWord))
  429. .WhereIF(dto.IsSensitiveWord.HasValue && dto.IsSensitiveWord == true, x => x.Sensitive != null && SqlFunc.JsonArrayLength(x.Sensitive) > 0)
  430. .OrderByDescending(d => d.CreationTime);
  431. }
  432. /// <summary>
  433. /// 未签收统计
  434. /// </summary>
  435. /// <param name="dto"></param>
  436. /// <returns></returns>
  437. public ISugarQueryable<Order,WorkflowStep> QueryUnsignedOrders(QueryUnsignedOrdersRequest dto) {
  438. if (dto.EndTime.HasValue)
  439. dto.EndTime = dto.EndTime.Value.AddDays(1).AddSeconds(-1);
  440. var IsCenter = _sessionContext.OrgIsCenter;
  441. return _orderRepository.Queryable()
  442. .LeftJoin<WorkflowStep>((x,ws)=>x.Id == ws.ExternalId)
  443. .WhereIF(dto.StartTime.HasValue, (x,ws) => ws.CreationTime >= dto.StartTime)
  444. .WhereIF(dto.EndTime.HasValue, (x, ws) => ws.CreationTime <= dto.EndTime)
  445. .WhereIF(dto.Level == 0 && IsCenter == false, (x, ws) => ws.AcceptorOrgId.StartsWith(_sessionContext.OrgId))
  446. .WhereIF(dto.Level == 1,(x,ws)=> ws.AcceptorOrgId == _sessionContext.OrgId)
  447. .WhereIF(dto.Level == 2, (x, ws) => ws.AcceptorOrgId.StartsWith(_sessionContext.OrgId))
  448. .WhereIF(dto.Signed == 0 ,(x,ws)=>ws.Status == Share.Enums.FlowEngine.EWorkflowStepStatus.WaitForAccept)
  449. .WhereIF(dto.Signed == 1, (x, ws) => ws.Status == Share.Enums.FlowEngine.EWorkflowStepStatus.WaitForHandle)
  450. .Where((x,ws)=>ws.CountersignPosition == Share.Enums.FlowEngine.ECountersignPosition.None && x.Status > EOrderStatus.WaitForAccept)
  451. .OrderByDescending((x,ws) => ws.CreationTime);
  452. }
  453. /// <summary>
  454. /// 信件来源统计
  455. /// </summary>
  456. /// <param name="dto"></param>
  457. /// <returns></returns>
  458. public ISugarQueryable<Order> QueryOrderSource(QueryOrderSourceRequest dto)
  459. {
  460. if (dto.EndTime.HasValue)
  461. dto.EndTime = dto.EndTime.Value.AddDays(1).AddSeconds(-1);
  462. return _orderRepository.Queryable()
  463. .WhereIF(dto.StartTime.HasValue, d => d.CreationTime >= dto.StartTime)
  464. .WhereIF(dto.EndTime.HasValue, d => d.CreationTime <= dto.EndTime)
  465. .WhereIF(dto.IdentityType.HasValue , d=>d.IdentityType == dto.IdentityType)
  466. .Where(d=> d.SourceChannel != null && d.SourceChannel !="");
  467. }
  468. /// <summary>
  469. /// 信件来源统计列表
  470. /// </summary>
  471. /// <param name="dto"></param>
  472. /// <returns></returns>
  473. public async Task<List<OrderSourceHeaderVo>> QueryOrderSourceList(QueryOrderSourceRequest dto)
  474. {
  475. if (dto.EndTime.HasValue)
  476. dto.EndTime = dto.EndTime.Value.AddDays(1).AddSeconds(-1);
  477. var data = await _orderRepository.Queryable()
  478. .WhereIF(dto.StartTime.HasValue, d => d.CreationTime >= dto.StartTime)
  479. .WhereIF(dto.EndTime.HasValue, d => d.CreationTime <= dto.EndTime)
  480. .WhereIF(dto.IdentityType.HasValue, d => d.IdentityType == dto.IdentityType)
  481. .Where(d => d.SourceChannel != null && d.SourceChannel != "")
  482. .GroupBy(d => new { Time = d.CreationTime.ToString("yyyy-MM-dd"), d.SourceChannel })
  483. .Select(d => new OrderSourceHeaderVo
  484. {
  485. Time = d.CreationTime.ToString("yyyy-MM-dd"),
  486. Phone = SqlFunc.AggregateSum(SqlFunc.IIF(d.SourceChannelCode == "RGDH", 1, 0)),
  487. Web = SqlFunc.AggregateSum(SqlFunc.IIF(d.SourceChannelCode == "YTW", 1, 0)),
  488. Rests = SqlFunc.AggregateSum(SqlFunc.IIF(d.SourceChannelCode == "QT", 1, 0)),
  489. Created = SqlFunc.AggregateSum(SqlFunc.IIF(d.SourceChannelCode == "ZJ", 1, 0)),
  490. WeChat = SqlFunc.AggregateSum(SqlFunc.IIF(d.SourceChannelCode == "WX", 1, 0)),
  491. App = SqlFunc.AggregateSum(SqlFunc.IIF(d.SourceChannelCode == "AP", 1, 0)),
  492. WisdomYB = SqlFunc.AggregateSum(SqlFunc.IIF(d.SourceChannelCode == "ZHYB", 1, 0)),
  493. Platform = SqlFunc.AggregateSum(SqlFunc.IIF(d.SourceChannelCode == "ZZPT", 1, 0)),
  494. Platform12328 = SqlFunc.AggregateSum(SqlFunc.IIF(d.SourceChannelCode == "S12328", 1, 0)),
  495. MayorAndNetizens = SqlFunc.AggregateSum(SqlFunc.IIF(d.SourceChannelCode == "SZYSM", 1, 0)),
  496. MediaYB = SqlFunc.AggregateSum(SqlFunc.IIF(d.SourceChannelCode == "YBRMT", 1, 0)),
  497. Platform12345 = SqlFunc.AggregateSum(SqlFunc.IIF(d.SourceChannelCode == "S12345", 1, 0)),
  498. Interaction = SqlFunc.AggregateSum(SqlFunc.IIF(d.SourceChannelCode == "SZMHD", 1, 0)),
  499. ServiceYB = SqlFunc.AggregateSum(SqlFunc.IIF(d.SourceChannelCode == "YBS", 1, 0)),
  500. CityTransfer = SqlFunc.AggregateSum(SqlFunc.IIF(d.SourceChannelCode == "SZHZ", 1, 0)),
  501. Platform110 = SqlFunc.AggregateSum(SqlFunc.IIF(d.SourceChannelCode == "YB110", 1, 0)),
  502. NoService = SqlFunc.AggregateSum(SqlFunc.IIF(d.SourceChannelCode == "SMZXBNCS", 1, 0)),
  503. Iyb = SqlFunc.AggregateSum(SqlFunc.IIF(d.SourceChannelCode == "IYB", 1, 0))
  504. }).ToListAsync() ;
  505. var totalVo = new OrderSourceHeaderVo()
  506. {
  507. Time = "合计",
  508. Phone = data.Sum(x => x.Phone),
  509. Web = data.Sum(x => x.Web),
  510. Rests = data.Sum(x => x.Rests),
  511. Created = data.Sum(x => x.Created),
  512. WeChat = data.Sum(x => x.WeChat),
  513. App = data.Sum(x => x.App),
  514. WisdomYB = data.Sum(x => x.WisdomYB),
  515. Platform = data.Sum(x => x.Platform),
  516. Platform12328 = data.Sum(x => x.Platform12328),
  517. MayorAndNetizens = data.Sum(x => x.MayorAndNetizens),
  518. MediaYB = data.Sum(x => x.MediaYB),
  519. Platform12345 = data.Sum(x => x.Platform12345),
  520. Interaction = data.Sum(x => x.Interaction),
  521. ServiceYB = data.Sum(x => x.ServiceYB),
  522. CityTransfer = data.Sum(x => x.CityTransfer),
  523. Platform110 = data.Sum(x => x.Platform110),
  524. NoService = data.Sum(x => x.NoService),
  525. Iyb = data.Sum(x => x.Iyb)
  526. };
  527. data.Add(totalVo);
  528. return data;
  529. }
  530. public ISugarQueryable<Order> QueryOrderSourceDetail(QueryOrderSourceDetailRequest dto) {
  531. return _orderRepository.Queryable()
  532. .WhereIF(string.IsNullOrEmpty(dto.SourceChannel),d=>d.SourceChannel == dto.SourceChannel)
  533. .WhereIF(dto.Time.HasValue,d=>d.CreationTime.ToString("yyyy-MM-dd") == dto.Time.Value.ToString("yyyy-MM-dd"))
  534. .WhereIF(dto.IdentityType.HasValue, d => d.IdentityType == dto.IdentityType)
  535. .Where(d => d.SourceChannel != null && d.SourceChannel != "");
  536. }
  537. #region private
  538. /// <summary>
  539. /// 接受外部工单(除省平台)
  540. /// </summary>
  541. /// <param name="dto"></param>
  542. /// <param name="cancellationToken"></param>
  543. /// <returns></returns>
  544. private async Task<AddOrderResponse> ReceiveOrderFromOtherPlatformAsync(AddOrderDto dto, List<FileDto> files,
  545. ISessionContext current, CancellationToken cancellationToken)
  546. {
  547. if (string.IsNullOrEmpty(dto.ExternalId))
  548. throw new UserFriendlyException("工单外部编号不能为空");
  549. var order = await _orderRepository.Queryable()
  550. .FirstAsync(d => d.ExternalId == dto.ExternalId, cancellationToken);
  551. if (order == null)
  552. {
  553. order = _mapper.Map<Order>(dto);
  554. order.InitId();
  555. if (files != null && files.Any())
  556. order.FileJson = await _fileRepository.AddFileAsync(files, order.Id, "", cancellationToken);
  557. await _orderDomainService.AddAsync(order, cancellationToken: cancellationToken);
  558. }
  559. else
  560. {
  561. _mapper.Map(dto, order);
  562. if (files != null && files.Any())
  563. order.FileJson = await _fileRepository.AddFileAsync(files, order.Id, "", cancellationToken);
  564. await _orderRepository.UpdateAsync(order, cancellationToken);
  565. }
  566. return _mapper.Map<AddOrderResponse>(order);
  567. }
  568. /// <summary>
  569. /// 接受省平台工单
  570. /// </summary>
  571. private async Task<AddOrderResponse> ReceiveOrderFromProvinceAsync(AddOrderDto dto, List<FileDto> files,
  572. ISessionContext current, CancellationToken cancellationToken)
  573. {
  574. if (string.IsNullOrEmpty(dto.ProvinceNo))
  575. throw new UserFriendlyException("无效省工单编号");
  576. var orderExtension = await _orderDomainService.GetOrderExtensionsAsync(dto.ProvinceNo, cancellationToken);
  577. var order = await _orderRepository.GetAsync(d => d.ProvinceNo == dto.ProvinceNo, cancellationToken);
  578. if (order is null)
  579. {
  580. order = _mapper.Map<Order>(dto);
  581. order.InitId();
  582. if (files != null && files.Any())
  583. order.FileJson = await _fileRepository.AddFileAsync(files, order.Id, "", cancellationToken);
  584. await _orderDomainService.AddAsync(order, cancellationToken: cancellationToken);
  585. if (orderExtension is not null)
  586. {
  587. orderExtension.Id = order.Id;
  588. if (dto.OrderExtension != null)
  589. _mapper.Map(dto.OrderExtension, orderExtension);
  590. await _orderDomainService.UpdateExtensionAsync(orderExtension, cancellationToken);
  591. }
  592. }
  593. else
  594. {
  595. _mapper.Map(dto, order);
  596. if (files != null && files.Any())
  597. order.FileJson = await _fileRepository.AddFileAsync(files, order.Id, "", cancellationToken);
  598. await _orderRepository.UpdateAsync(order, cancellationToken);
  599. if (orderExtension is not null)
  600. {
  601. orderExtension.Id = order.Id;
  602. if (dto.OrderExtension != null)
  603. _mapper.Map(dto.OrderExtension, orderExtension);
  604. await _orderDomainService.UpdateExtensionAsync(orderExtension, cancellationToken);
  605. }
  606. //特提(撤回至发起)
  607. if (!string.IsNullOrEmpty(order.WorkflowId))
  608. await _workflowDomainService.RecallToStartStepAsync(order.WorkflowId, "省工单重派", current, cancellationToken);
  609. }
  610. return _mapper.Map<AddOrderResponse>(order);
  611. }
  612. #endregion
  613. }