OrderApplication.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. using Hotline.Caching.Interfaces;
  2. using Hotline.File;
  3. using Hotline.FlowEngine.Workflows;
  4. using Hotline.Orders;
  5. using Hotline.Repository.SqlSugar.Extensions;
  6. using Hotline.Repository.SqlSugar.Ts;
  7. using Hotline.Settings.TimeLimits;
  8. using Hotline.Share.Dtos;
  9. using Hotline.Share.Dtos.File;
  10. using Hotline.Share.Dtos.FlowEngine;
  11. using Hotline.Share.Dtos.FlowEngine.Workflow;
  12. using Hotline.Share.Dtos.Order;
  13. using Hotline.Share.Dtos.Settings;
  14. using Hotline.Share.Enums.Order;
  15. using Hotline.Share.Enums.Settings;
  16. using Hotline.Tools;
  17. using Hotline.Users;
  18. using MapsterMapper;
  19. using XF.Domain.Constants;
  20. using XF.Domain.Dependency;
  21. using XF.Domain.Entities;
  22. using XF.Domain.Exceptions;
  23. using XF.Domain.Repository;
  24. namespace Hotline.Application.Orders;
  25. public class OrderApplication : IOrderApplication, IScopeDependency
  26. {
  27. private readonly IOrderDomainService _orderDomainService;
  28. private readonly IWorkflowDomainService _workflowDomainService;
  29. private readonly IOrderRepository _orderRepository;
  30. private readonly ITimeLimitDomainService _timeLimitDomainService;
  31. private readonly IMapper _mapper;
  32. private readonly ISystemSettingCacheManager _systemSettingCacheManager;
  33. private readonly IRepository<OrderWord> _orderWrodRepository;
  34. private readonly IRepositoryTextSearch<OrderTs> _repositoryts;
  35. private readonly IFileRepository _fileRepository;
  36. public OrderApplication(
  37. IOrderDomainService orderDomainService,
  38. IOrderRepository orderRepository,
  39. IWorkflowDomainService workflowDomainService,
  40. ITimeLimitDomainService timeLimitDomainService,
  41. ISystemSettingCacheManager systemSettingCacheManager,
  42. IMapper mapper,
  43. IRepository<OrderWord> orderWrodRepository,
  44. IRepositoryTextSearch<OrderTs> repositoryts,
  45. IFileRepository fileRepository
  46. )
  47. {
  48. _orderDomainService = orderDomainService;
  49. _workflowDomainService = workflowDomainService;
  50. _orderRepository = orderRepository;
  51. _timeLimitDomainService = timeLimitDomainService;
  52. _mapper = mapper;
  53. _systemSettingCacheManager = systemSettingCacheManager;
  54. _orderWrodRepository = orderWrodRepository;
  55. _repositoryts = repositoryts;
  56. _fileRepository = fileRepository;
  57. }
  58. /// <summary>
  59. /// 更新工单办理期满时间
  60. /// 1.更新工单 2.更新流程 3.推送省平台
  61. /// </summary>
  62. /// <returns></returns>
  63. public async Task DelayOrderExpiredTimeAsync(string orderId, int timeCount, ETimeType timeType, CancellationToken cancellationToken)
  64. {
  65. var order = await _orderDomainService.GetOrderAsync(orderId, cancellationToken: cancellationToken);
  66. var expiredTimeConfig =
  67. _timeLimitDomainService.CalcEndTime(order.ExpiredTime.Value, new TimeConfig(timeCount, timeType), order.AcceptTypeCode);
  68. order.TimeLimit = expiredTimeConfig.TimeText;
  69. order.TimeLimitCount = expiredTimeConfig.Count;
  70. order.TimeLimitUnit = expiredTimeConfig.TimeType;
  71. order.ExpiredTime = expiredTimeConfig.ExpiredTime;
  72. order.NearlyExpiredTime = expiredTimeConfig.NearlyExpiredTime;
  73. //if (string.IsNullOrEmpty(order.WorkflowId))
  74. // throw new UserFriendlyException("该工单流程id异常");
  75. //var workflow = await _workflowDomainService.GetWorkflowAsync(order.WorkflowId, cancellationToken: cancellationToken);
  76. //await _workflowDomainService.UpdateExpiredTimeAsync(workflow, expiredTimeConfig.ExpiredTime,
  77. // expiredTimeConfig.TimeText, expiredTimeConfig.Count, expiredTimeConfig.TimeType, expiredTimeConfig.NearlyExpiredTime, cancellationToken);
  78. await _orderRepository.UpdateAsync(order, cancellationToken);
  79. }
  80. /// <summary>
  81. /// 新增工单办理流程记录
  82. /// </summary>
  83. public async Task AddOrderTracesAsync(string orderId, ICollection<WorkflowTraceDto> traces, CancellationToken cancellationToken)
  84. {
  85. var order = await _orderRepository.GetAsync(orderId, cancellationToken);
  86. if (order is null)
  87. throw new UserFriendlyException("工单不存在");
  88. if (string.IsNullOrEmpty(order.WorkflowId))
  89. throw new UserFriendlyException("工单未开启流程");
  90. await _workflowDomainService.AddTracesAsync(order.WorkflowId, _mapper.Map<List<WorkflowTrace>>(traces),
  91. cancellationToken);
  92. }
  93. /// <summary>
  94. /// 撤销工单
  95. /// </summary>
  96. public async Task CancelOrderAsync(string orderId, string opinion, CancellationToken cancellationToken)
  97. {
  98. var order = await _orderRepository.GetAsync(orderId, cancellationToken);
  99. if (order is null)
  100. throw new UserFriendlyException("工单不存在");
  101. if (!string.IsNullOrEmpty(order.WorkflowId))
  102. {
  103. //结束流程
  104. await _workflowDomainService.TerminateAsync(new TerminateDto
  105. {
  106. WorkflowId = order.WorkflowId,
  107. Opinion = opinion
  108. }, cancellationToken);
  109. }
  110. //归档工单
  111. var now = DateTime.Now;
  112. var handleDuration = order.StartTime.HasValue
  113. ? _timeLimitDomainService.CalcWorkTime(order.StartTime.Value,
  114. now, order.ProcessType is EProcessType.Zhiban)
  115. : 0;
  116. var fileDuration = order.CenterToOrgTime.HasValue
  117. ? _timeLimitDomainService.CalcWorkTime(order.CenterToOrgTime.Value,
  118. now, order.ProcessType is EProcessType.Zhiban)
  119. : 0;
  120. var allDuration = order.StartTime.HasValue
  121. ? _timeLimitDomainService.CalcWorkTime(order.StartTime.Value, now,
  122. order.ProcessType is EProcessType.Zhiban)
  123. : 0;
  124. order.File(now, handleDuration, fileDuration, allDuration);
  125. await _orderRepository.UpdateAsync(order, cancellationToken);
  126. }
  127. /// <summary>
  128. /// 即将超期列表
  129. /// </summary>
  130. /// <param name="dto"></param>
  131. /// <param name="cancellationToken"></param>
  132. /// <returns></returns>
  133. public async Task<PagedDto<OrderDto>> GetAboutToExpireAsync(AboutToExpireListDto dto, CancellationToken cancellationToken)
  134. {
  135. var setting = _systemSettingCacheManager.GetSetting(SettingConstants.OrderAboutToExpire);
  136. var value = setting?.SettingValue[0];
  137. value = string.IsNullOrEmpty(value) ? "0" : value;
  138. DateTime stTime = DateTime.Now.AddDays(int.Parse(value));
  139. stTime = _timeLimitDomainService.WorkDay(DateTime.Now);
  140. DateTime stTime2 = _timeLimitDomainService.WorkDay(DateTime.Now);
  141. var (total, items) = await _orderRepository.Queryable(viewFilter:true)
  142. .WhereIF(dto.IsProvince.HasValue, x => x.IsProvince == dto.IsProvince)
  143. .WhereIF(!string.IsNullOrEmpty(dto.Keyword), x => x.Title.Contains(dto.Keyword!) || x.No.Contains(dto.Keyword!))
  144. .Where(x => x.ExpiredTime != null &&
  145. x.Status != EOrderStatus.Filed && x.Status != EOrderStatus.Published && x.Status != EOrderStatus.Visited && stTime >= x.ExpiredTime.Value && stTime2 <= x.ExpiredTime.Value)
  146. .OrderByDescending(x => x.CreationTime)
  147. .ToPagedListAsync(dto.PageIndex, dto.PageSize, cancellationToken);
  148. return new PagedDto<OrderDto>(total, _mapper.Map<IReadOnlyList<OrderDto>>(items));
  149. }
  150. // /// <summary>
  151. // /// 即将超期节点列表
  152. // /// </summary>
  153. // /// <param name="dto"></param>
  154. // /// <param name="cancellationToken"></param>
  155. // /// <returns></returns>
  156. //public async Task<PagedDto<WorkflowOrderDto>> GetAboutToExpireNodeAsync(AboutToExpireListDto dto, CancellationToken cancellationToken)
  157. //{
  158. // var setting = _systemSettingCacheManager.GetSetting(SettingConstants.OrderAboutToExpire);
  159. // var value = setting?.SettingValue[0];
  160. // value = string.IsNullOrEmpty(value) ? "0" : value;
  161. // DateTime stTime = DateTime.Now.AddDays(int.Parse(value));
  162. // stTime = _timeLimitDomainService.WorkDay(DateTime.Now);
  163. // DateTime stTime2 = _timeLimitDomainService.WorkDay(DateTime.Now);
  164. // RefAsync<int> total = 0;
  165. // var items = await Db.Queryable<Workflow>()
  166. // .LeftJoin<Order>((x, o) => x.ExternalId == o.Id)
  167. // .Where(x => x.ModuleCode == "OrderHandle")
  168. // .WhereIF(dto.IsProvince.HasValue, (x, o) => o.IsProvince == dto.IsProvince)
  169. // .WhereIF(!string.IsNullOrEmpty(dto.Keyword), (x, o) => o.Title.Contains(dto.Keyword!) || o.No.Contains(dto.Keyword!))
  170. // .Where((x, o) => (int)x.Status < 20 && stTime >= x.ExpiredTime && stTime2 <= x.ExpiredTime)
  171. // .Select((x, o) => new WorkflowOrder { Order = o }, true)
  172. // .OrderByDescending(x => x.CreationTime)
  173. // .ToPageListAsync(dto.PageIndex, dto.PageSize, total, cancellationToken);
  174. // return new PagedDto<WorkflowOrderDto>(total, _mapper.Map<IReadOnlyList<WorkflowOrderDto>>(items));
  175. //}
  176. /// <summary>
  177. /// 已超期列表
  178. /// </summary>
  179. /// <param name="dto"></param>
  180. /// <param name="cancellationToken"></param>
  181. /// <returns></returns>
  182. public async Task<PagedDto<OrderDto>> GetToExpireAsync(AboutToExpireListDto dto, CancellationToken cancellationToken)
  183. {
  184. DateTime stTime = _timeLimitDomainService.WorkDay(DateTime.Now);
  185. var (total, items) = await _orderRepository.Queryable(viewFilter:true)
  186. .WhereIF(dto.IsProvince.HasValue, x => x.IsProvince == dto.IsProvince)
  187. .WhereIF(!string.IsNullOrEmpty(dto.Keyword), x => x.Title.Contains(dto.Keyword!) || x.No.Contains(dto.Keyword!))
  188. //.WhereIF(!string.IsNullOrEmpty(dto.No), x => x.No == dto.No)
  189. //.WhereIF(!string.IsNullOrEmpty(dto.Title), x => x.Title.Contains(dto.Title!))
  190. .Where(x => x.ExpiredTime != null &&
  191. (((x.Status == EOrderStatus.Filed || x.Status == EOrderStatus.Published || x.Status == EOrderStatus.Visited) && x.FiledTime >= x.ExpiredTime) ||
  192. ((x.Status != EOrderStatus.Filed && x.Status != EOrderStatus.Published && x.Status != EOrderStatus.Visited) && stTime >= x.ExpiredTime.Value)))
  193. .OrderByDescending(x => x.CreationTime)
  194. .ToPagedListAsync(dto.PageIndex, dto.PageSize, cancellationToken);
  195. return new PagedDto<OrderDto>(total, _mapper.Map<IReadOnlyList<OrderDto>>(items));
  196. }
  197. // /// <summary>
  198. // /// 已超期节点列表
  199. // /// </summary>
  200. // /// <param name="dto"></param>
  201. // /// <param name="cancellationToken"></param>
  202. // /// <returns></returns>
  203. // public async Task<PagedDto<WorkflowOrderDto>> GetToExpireNodeAsync(AboutToExpireListDto dto, CancellationToken cancellationToken)
  204. // {
  205. // DateTime stTime = _timeLimitDomainService.WorkDay(DateTime.Now);
  206. //RefAsync<int> total = 0;
  207. //var items= await Db.Queryable<Workflow>()
  208. // .LeftJoin<Order>((x,o)=>x.ExternalId == o.Id)
  209. // .Where(x => x.ModuleCode == "OrderHandle")
  210. // .WhereIF(dto.IsProvince.HasValue, (x, o) => o.IsProvince == dto.IsProvince)
  211. // .WhereIF(!string.IsNullOrEmpty(dto.Keyword), (x, o) => o.Title.Contains(dto.Keyword!) || o.No.Contains(dto.Keyword!))
  212. // .Where((x,o) => (((int)x.Status >= 20 && x.EndTime >= x.ExpiredTime) || ((int)x.Status < 20 && stTime >= x.ExpiredTime)))
  213. // .Select((x, o) => new WorkflowOrder { Order = o }, true)
  214. // .OrderByDescending(x => x.CreationTime)
  215. // .ToPageListAsync(dto.PageIndex, dto.PageSize, total, cancellationToken);
  216. // return new PagedDto<WorkflowOrderDto>(total, _mapper.Map<IReadOnlyList<WorkflowOrderDto>>(items));
  217. // }
  218. /// <summary>
  219. /// 工单关键字分词
  220. /// </summary>
  221. /// <param name="inputStr"></param>
  222. /// <returns></returns>
  223. public async Task OrderParticiple(string inputStr, string orderId, CancellationToken cancellationToken)
  224. {
  225. var words = await _orderWrodRepository.Queryable().Where(x => x.IsEnable == 1 && x.Classify.Contains("普通标签")).Select(x => x.Tag).ToListAsync(cancellationToken);
  226. var res = new List<string>();
  227. if (words.Any()) res = ParticipleTool.SegMMDouble(inputStr, ref words);
  228. var participles = await _orderWrodRepository.Queryable().In(x => x.Tag, res).ToListAsync(cancellationToken);
  229. if (participles.Any())
  230. {
  231. //关键词
  232. var tags = participles.Select(x => x.Tag).ToList();
  233. var tagsStr = string.Join(",", tags);
  234. await _orderRepository.Updateable().SetColumns(x => x.TagNames == tagsStr).Where(x => x.Id == orderId).ExecuteCommandAsync(cancellationToken);
  235. List<string> synonyms = participles.Select(x => x.Synonym).ToList();
  236. if (synonyms.Any())
  237. {
  238. var synonymsStr = string.Join(",", synonyms);
  239. synonyms = synonymsStr.Split(",").Distinct().ToList();
  240. tags.AddRange(synonyms);
  241. }
  242. var vector = await _orderRepository.Queryable().Where(x => x.Id == orderId).ToListAsync(cancellationToken);
  243. if (vector.Any()) await _repositoryts.UpdateVectorAsync(orderId, tags, cancellationToken);
  244. else await _repositoryts.AddVectorAsync(orderId, DateTime.Now, tags, cancellationToken);
  245. }
  246. }
  247. /// <summary>
  248. /// 接收外部平台工单
  249. /// </summary>
  250. public Task<AddOrderResponse> ReceiveOrderFromExternalAsync(AddOrderDto dto, CancellationToken cancellationToken)
  251. {
  252. switch (dto.Source)
  253. {
  254. case ESource.ProvinceStraight:
  255. return ReceiveOrderFromProvinceAsync(dto, dto.Files, cancellationToken);
  256. case ESource.Police110:
  257. case ESource.CityDataExchangeLz:
  258. case ESource.ConvergenceMedia:
  259. case ESource.WebPortal:
  260. return ReceiveOrderFromOtherPlatformAsync(dto, dto.Files, cancellationToken);
  261. case ESource.Hotline:
  262. case ESource.HotlineImport:
  263. default:
  264. throw new ArgumentOutOfRangeException();
  265. }
  266. }
  267. #region private
  268. /// <summary>
  269. /// 接受外部工单(除省平台)
  270. /// </summary>
  271. /// <param name="dto"></param>
  272. /// <param name="cancellationToken"></param>
  273. /// <returns></returns>
  274. private async Task<AddOrderResponse> ReceiveOrderFromOtherPlatformAsync(AddOrderDto dto, List<FileDto> files, CancellationToken cancellationToken)
  275. {
  276. if (string.IsNullOrEmpty(dto.ExternalId))
  277. throw new UserFriendlyException("工单外部编号不能为空");
  278. var order = await _orderRepository.Queryable()
  279. .FirstAsync(d => d.ExternalId == dto.ExternalId, cancellationToken);
  280. if (order == null)
  281. {
  282. order = _mapper.Map<Order>(dto);
  283. order.InitId();
  284. if (files != null && files.Any())
  285. order.FileJson = await _fileRepository.AddFileAsync(files, order.Id, "", cancellationToken);
  286. await _orderDomainService.AddAsync(order, cancellationToken: cancellationToken);
  287. }
  288. else
  289. {
  290. _mapper.Map(dto, order);
  291. if (files != null && files.Any())
  292. order.FileJson = await _fileRepository.AddFileAsync(files, order.Id, "", cancellationToken);
  293. await _orderRepository.UpdateAsync(order, cancellationToken);
  294. }
  295. return _mapper.Map<AddOrderResponse>(order);
  296. }
  297. /// <summary>
  298. /// 接受省平台工单
  299. /// </summary>
  300. private async Task<AddOrderResponse> ReceiveOrderFromProvinceAsync(AddOrderDto dto, List<FileDto> files, CancellationToken cancellationToken)
  301. {
  302. if (string.IsNullOrEmpty(dto.ProvinceNo))
  303. throw new UserFriendlyException("无效省工单编号");
  304. var orderExtension = await _orderDomainService.GetOrderExtensionsAsync(dto.ProvinceNo, cancellationToken);
  305. var order = await _orderRepository.GetAsync(d => d.ProvinceNo == dto.ProvinceNo, cancellationToken);
  306. if (order is null)
  307. {
  308. order = _mapper.Map<Order>(dto);
  309. order.InitId();
  310. if (files != null && files.Any())
  311. order.FileJson = await _fileRepository.AddFileAsync(files, order.Id, "", cancellationToken);
  312. await _orderDomainService.AddAsync(order, cancellationToken: cancellationToken);
  313. if (orderExtension is not null)
  314. {
  315. orderExtension.Id = order.Id;
  316. if (dto.OrderExtension != null)
  317. _mapper.Map(dto.OrderExtension, orderExtension);
  318. await _orderDomainService.UpdateExtensionAsync(orderExtension, cancellationToken);
  319. }
  320. }
  321. else
  322. {
  323. _mapper.Map(dto, order);
  324. if (files != null && files.Any())
  325. order.FileJson = await _fileRepository.AddFileAsync(files, order.Id, "", cancellationToken);
  326. await _orderRepository.UpdateAsync(order, cancellationToken);
  327. if (orderExtension is not null)
  328. {
  329. orderExtension.Id = order.Id;
  330. if (dto.OrderExtension != null)
  331. _mapper.Map(dto.OrderExtension, orderExtension);
  332. await _orderDomainService.UpdateExtensionAsync(orderExtension, cancellationToken);
  333. }
  334. //特提(撤回至发起)
  335. if (!string.IsNullOrEmpty(order.WorkflowId))
  336. await _workflowDomainService.RecallToStartStepAsync(order.WorkflowId, "省工单重派", cancellationToken);
  337. }
  338. return _mapper.Map<AddOrderResponse>(order);
  339. }
  340. #endregion
  341. }