SnapshotApplicationBase.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. using Hotline.Orders;
  2. using Hotline.Share.Dtos;
  3. using Hotline.Share.Dtos.Article;
  4. using Hotline.Share.Dtos.Snapshot;
  5. using Hotline.Share.Tools;
  6. using Hotline.Snapshot;
  7. using Hotline.Users;
  8. using SqlSugar;
  9. using XF.Domain.Authentications;
  10. using XF.Domain.Dependency;
  11. using XF.Domain.Repository;
  12. using Hotline.Repository.SqlSugar.Extensions;
  13. using Hotline.Share.Enums;
  14. using Hotline.Share.Enums.Order;
  15. using Hotline.Share.Requests;
  16. using Hotline.Share.Enums.Snapshot;
  17. using Mapster;
  18. using Hotline.Caching.Interfaces;
  19. using NPOI.Util.ArrayExtensions;
  20. using XF.Domain.Exceptions;
  21. using Hotline.Settings;
  22. using Hotline.Share.Dtos.Settings;
  23. using Hotline.File;
  24. using Hotline.Share.Enums.Article;
  25. using Hotline.Tools;
  26. using Hotline.Snapshot.Interfaces;
  27. using DotNetCore.CAP;
  28. using Microsoft.AspNetCore.Http;
  29. using Hotline.Share.Dtos.FlowEngine;
  30. using Hotline.FlowEngine.Workflows;
  31. using Hotline.Share.Enums.FlowEngine;
  32. namespace Hotline.Application.Snapshot;
  33. /// <summary>
  34. /// 随手拍应用层
  35. /// </summary>
  36. public abstract class SnapshotApplicationBase
  37. {
  38. private readonly IThirdAccountRepository _thirdAccountRepository;
  39. private readonly IRepository<Order> _orderRepository;
  40. private readonly ISnapshotBulletinRepository _bulletinRepository;
  41. /// <summary>
  42. /// 行业
  43. /// </summary>
  44. private readonly IRepository<Industry> _industryRepository;
  45. private readonly IThirdIdentiyService _thirdLoginService;
  46. private readonly ISessionContext _sessionContext;
  47. private readonly IRepository<RedPackRecord> _redPackRecordRepository;
  48. private readonly IRepository<OrderSnapshot> _orderSnapshotRepository;
  49. private readonly ISystemSettingCacheManager _systemSettingCacheManager;
  50. private readonly ISystemAreaDomainService _systemAreaDomainService;
  51. private readonly IFileRepository _fileRepository;
  52. private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
  53. private readonly ISnapshotOrderPublishRepository _snapshotOrderPublishRepository;
  54. private readonly IRepository<WorkflowTrace> _workflowTraceRepository;
  55. public SnapshotApplicationBase(IThirdIdentiyService thirdLoginService, IRepository<Industry> industryRepository, ISnapshotBulletinRepository bulletinRepository, ISessionContext sessionContext, IRepository<RedPackRecord> redPackRecordRepository, IRepository<Order> orderRepository, IThirdAccountRepository thirdAccountRepository, IRepository<OrderSnapshot> orderSnapshotRepository, ISystemSettingCacheManager systemSettingCacheManager, ISystemAreaDomainService systemAreaDomainService, IFileRepository fileRepository, ISystemDicDataCacheManager systemDicDataCacheManager, ISnapshotOrderPublishRepository snapshotOrderPublishRepository, IRepository<WorkflowTrace> workflowTraceRepository)
  56. {
  57. _thirdLoginService = thirdLoginService;
  58. _industryRepository = industryRepository;
  59. _bulletinRepository = bulletinRepository;
  60. _sessionContext = sessionContext;
  61. _redPackRecordRepository = redPackRecordRepository;
  62. _orderRepository = orderRepository;
  63. _thirdAccountRepository = thirdAccountRepository;
  64. _orderSnapshotRepository = orderSnapshotRepository;
  65. _systemSettingCacheManager = systemSettingCacheManager;
  66. _systemAreaDomainService = systemAreaDomainService;
  67. _fileRepository = fileRepository;
  68. _systemDicDataCacheManager = systemDicDataCacheManager;
  69. _snapshotOrderPublishRepository = snapshotOrderPublishRepository;
  70. _workflowTraceRepository = workflowTraceRepository;
  71. }
  72. /// <summary>
  73. /// 获取随手拍小程序首页数据
  74. /// </summary>
  75. /// <returns></returns>
  76. public async Task<HomePageOutDto> GetHomePageAsync()
  77. {
  78. var fileServiceUrl = _systemSettingCacheManager.FileServerUrl;
  79. var fileDownloadApi = fileServiceUrl + _systemSettingCacheManager.FileDownloadApi;
  80. var items = await _industryRepository.Queryable()
  81. .Where(m => m.IsEnable)
  82. .OrderBy(m => m.DisplayOrder)
  83. .ToListAsync(m => new HomeIndustryOutDto());
  84. items.ForEach(m =>
  85. {
  86. if (m.BackgroundImgUrl.NotNullOrEmpty())
  87. m.BackgroundImgUrl = fileDownloadApi + m.BackgroundImgUrl;
  88. if (m.BannerImgUrl.NotNullOrEmpty())
  89. m.BannerImgUrl = fileDownloadApi + m.BannerImgUrl;
  90. if (m.CareCellImgUrl.NotNullOrEmpty())
  91. m.CareCellImgUrl = fileDownloadApi + m.CareCellImgUrl;
  92. if (m.CellImgUrl.NotNullOrEmpty())
  93. m.CellImgUrl = fileDownloadApi + m.CellImgUrl;
  94. });
  95. return new HomePageOutDto
  96. {
  97. Banners = _systemSettingCacheManager.AppBanner.Split('|').Select(m => fileDownloadApi + m).ToList(),
  98. Industrys = items
  99. };
  100. }
  101. /// <summary>
  102. /// 获取行业集合
  103. /// </summary>
  104. /// <returns></returns>
  105. public async Task<IList<IndustryOutDto>> GetIndustresAsync()
  106. {
  107. var fileServiceUrl = _systemSettingCacheManager.FileServerUrl;
  108. var fileDownloadApi = fileServiceUrl + _systemSettingCacheManager.FileDownloadApi;
  109. var items = await _industryRepository.Queryable()
  110. .Where(m => m.IsEnable)
  111. .OrderBy(m => m.DisplayOrder)
  112. .ToListAsync(m => new IndustryOutDto());
  113. return items;
  114. }
  115. /// <summary>
  116. /// 行业页面基础数据
  117. /// </summary>
  118. /// <param name="id"></param>
  119. /// <param name="requestAborted"></param>
  120. /// <returns></returns>
  121. public async Task<IndustryBaseOutDto> GetIndustryBaseAsync(string id, CancellationToken requestAborted)
  122. {
  123. var fileServiceUrl = _systemSettingCacheManager.FileServerUrl;
  124. var fileDownloadApi = fileServiceUrl + _systemSettingCacheManager.FileDownloadApi;
  125. var indurstry = await _industryRepository.GetAsync(id, requestAborted)
  126. ?? throw UserFriendlyException.SameMessage("行业不存在:" + id);
  127. var bulletinId = await _bulletinRepository.Queryable()
  128. .Where(m => m.SnapshotBulletinTypeId == indurstry.BulletinTypeGuideId && m.BulletinState == EBulletinState.ReviewPass && m.IsArrive == true)
  129. .OrderByDescending(m => m.CreationTime)
  130. .Select(m => m.Id)
  131. .FirstAsync(requestAborted);
  132. var outDto = new IndustryBaseOutDto
  133. {
  134. Industry = indurstry.Adapt<IndustryOutDto>()
  135. };
  136. outDto.BulletinId = bulletinId;
  137. if (indurstry.IndustryType == EIndustryType.Declare)
  138. {
  139. outDto.AreaTree = (await _systemAreaDomainService.GetAreaTree(parentId: "510300")).Adapt<List<SystemAreaOutDto>>();
  140. outDto.Files = (await _fileRepository.GetByKeyAsync(indurstry.Id, requestAborted)).Adapt<List<IndustryFileDto>>();
  141. outDto.Files.ToList().ForEach(m => m.Url = fileDownloadApi + m.AdditionId);
  142. outDto.WorkplaceName = _systemDicDataCacheManager.WorkplaceName;
  143. outDto.Workplace = _systemDicDataCacheManager.Workplace;
  144. outDto.JobType = _systemDicDataCacheManager.JobType;
  145. outDto.BusinessUnitType = _systemDicDataCacheManager.BusinessUnitType;
  146. }
  147. return outDto;
  148. }
  149. public async Task AddOrderPublishAsync(AddSnapshotOrderPublishInDto dto, CancellationToken cancellation)
  150. {
  151. dto.ValidateObject();
  152. var snapshotOrder = await _orderSnapshotRepository.GetAsync(dto.OrderId)
  153. ?? throw UserFriendlyException.SameMessage("工单不存在");
  154. var order = await _orderRepository.Queryable()
  155. .Where(m => m.Id == dto.OrderId)
  156. .Select(m => new { m.Id, m.No })
  157. .FirstAsync(cancellation)
  158. ?? throw UserFriendlyException.SameMessage("工单不存在");
  159. var entity = dto.Adapt<SnapshotOrderPublish>();
  160. entity.Id = order.Id;
  161. entity.IndustryId = snapshotOrder.IndustryId;
  162. entity.IndustryName = snapshotOrder.IndustryName;
  163. entity.No = order.No;
  164. await _snapshotOrderPublishRepository.AddAsync(entity);
  165. }
  166. /// <summary>
  167. /// 获取公开工单集合
  168. /// </summary>
  169. /// <param name="dto"></param>
  170. /// <param name="requestAborted"></param>
  171. /// <returns></returns>
  172. public async Task<IList<OrderPublishOutDto>> GetOrderPublishAsync(OrderPublishInDto dto, CancellationToken requestAborted)
  173. {
  174. dto.ValidateObject();
  175. var items = await _snapshotOrderPublishRepository.Queryable()
  176. .Where(m => m.IndustryId == dto.IndustryId)
  177. .WhereIF(dto.Keyword.NotNullOrEmpty(), m => m.No.Contains(dto.Keyword) || m.ArrangeTitle.Contains(dto.Keyword))
  178. .Select<OrderPublishOutDto>()
  179. .ToFixedListAsync(dto, requestAborted);
  180. return items;
  181. }
  182. /// <summary>
  183. /// 获取公开的工单详情
  184. /// </summary>
  185. /// <param name="id"></param>
  186. /// <param name="requestAborted"></param>
  187. /// <returns></returns>
  188. public async Task<OrderPublishDetailOutDto> GetOrderPublishDetailAsync(string id, CancellationToken requestAborted)
  189. {
  190. var publish = await _snapshotOrderPublishRepository.GetAsync(id) ??
  191. throw UserFriendlyException.SameMessage("工单不存在");
  192. var outDto = publish.Adapt<OrderPublishDetailOutDto>();
  193. var fileServiceUrl = _systemSettingCacheManager.FileServerUrl;
  194. if (outDto.FileJson != null)
  195. {
  196. foreach (var item in outDto.FileJson)
  197. {
  198. item.Path = fileServiceUrl + item.Path;
  199. }
  200. }
  201. var traces = await _workflowTraceRepository.Queryable()
  202. .Where(m => m.ExternalId == publish.OrderId && m.Status == EWorkflowStepStatus.Handled)
  203. .OrderBy(m => m.AcceptTime)
  204. .ToListAsync(requestAborted);
  205. var centre = traces.Where(m => m.StepType == EStepType.End || m.StepType == EStepType.Start || m.BusinessType == EBusinessType.Send || m.BusinessType == EBusinessType.Seat || m.BusinessType == EBusinessType.File)
  206. .Select(m => new SnapshotWorkflow(m.Id, m.Name, m.HandleTime.Value))
  207. .ToList();
  208. outDto.Workflow = traces.Where(m => !centre.Select(s => s.Id).ToList().Contains(m.Id))
  209. .Select(m => new SnapshotWorkflow(m.Id, m.Name, m.HandleTime.Value))
  210. .ToList();
  211. outDto.Workflow.AddRange(centre);
  212. outDto.Workflow = outDto.Workflow.OrderBy(m => m.HandleTime).ToList();
  213. return outDto;
  214. }
  215. /// <summary>
  216. /// 获取随手拍小程序公告
  217. /// </summary>
  218. /// <returns></returns>
  219. public async Task<IReadOnlyList<BulletinOutDto>> GetBulletinsAsync(BulletinInDto dto, CancellationToken cancellationToken)
  220. {
  221. var items = await _bulletinRepository.Queryable()
  222. .Where(m => m.BulletinState == EBulletinState.ReviewPass)
  223. .LeftJoin<Industry>((bulletin, industry) => bulletin.SnapshotBulletinTypeId == industry.BulletinTypePublicityId)
  224. .Where((bulletin, industry) => industry.Id == dto.IndustryId)
  225. .ToFixedListAsync(dto, cancellationToken);
  226. return items.Adapt<IReadOnlyList<BulletinOutDto>>();
  227. }
  228. /// <summary>
  229. /// 获取个人中心数据
  230. /// </summary>
  231. /// <returns></returns>
  232. public async Task<SnapshotUserInfoOutDto> GetSnapshotUserInfoAsync()
  233. {
  234. var openId = _sessionContext.OpenId;
  235. var thirdAccount = await _thirdAccountRepository.QueryByOpenIdAsync(openId);
  236. var dayTime = DateTime.Now;
  237. var readPack = await _redPackRecordRepository.Queryable()
  238. .Where(m => m.WXOpenId == openId && m.PickupStatus == ERedPackPickupStatus.Received)
  239. .Where(m => m.CreationTime.Date == dayTime.Date)
  240. .Select(m => SqlFunc.AggregateSum(m.Amount))
  241. .FirstAsync();
  242. var outDto = await _orderRepository.Queryable()
  243. .Where(m => m.Contact == thirdAccount.PhoneNumber)
  244. .Select(m => new SnapshotUserInfoOutDto
  245. {
  246. NoReplyCount = SqlFunc.AggregateSum(SqlFunc.IIF(m.Status < EOrderStatus.Filed, 1, 0)),
  247. ReplyCount = SqlFunc.AggregateSum(SqlFunc.IIF(m.Status >= EOrderStatus.Filed, 1, 0)),
  248. AppraiseCount = SqlFunc.AggregateSum(SqlFunc.IIF(m.Status == EOrderStatus.Visited, 1, 0)),
  249. }).FirstAsync();
  250. outDto.DayAmount = readPack;
  251. outDto.TotalAmount = thirdAccount.TotalAmount;
  252. outDto.PhoneNumber = thirdAccount.PhoneNumber;
  253. return outDto;
  254. }
  255. /// <summary>
  256. /// 获取工单列表
  257. /// </summary>
  258. public async Task<IList<OrderOutDto>> GetSnapshotOrdersAsync(OrderInDto dto, CancellationToken cancellationToken)
  259. {
  260. var items = await _orderSnapshotRepository.Queryable()
  261. .LeftJoin<Order>((snapshot, order) => snapshot.Id == order.Id)
  262. .Where((snapshot, order) => order.Contact == _sessionContext.Phone)
  263. .WhereIF(dto.Status == EOrderQueryStatus.Appraise, (snapshot, order) => order.Status == EOrderStatus.Visited)
  264. .WhereIF(dto.Status == EOrderQueryStatus.NoReply, (snapshot, order) => order.Status < EOrderStatus.Filed)
  265. .WhereIF(dto.Status == EOrderQueryStatus.Reply, (snapshot, order) => order.Status >= EOrderStatus.Filed)
  266. .WhereIF(dto.KeyWords.NotNullOrEmpty(), (snapshot, order) => order.Title.Contains(dto.KeyWords))
  267. .Select((snapshot, order) => new OrderOutDto
  268. {
  269. Id = snapshot.Id,
  270. OrderNo = order.No,
  271. Title = order.Title,
  272. Status = order.Status,
  273. IndustryName = snapshot.IndustryName,
  274. CreationTime = order.CreationTime,
  275. Area = order.City
  276. })
  277. .ToFixedListAsync(dto, cancellationToken);
  278. return items;
  279. }
  280. /// <summary>
  281. /// 获取工单详情
  282. /// </summary>
  283. /// <param name="id"></param>
  284. /// <returns></returns>
  285. public async Task<OrderPublishDetailOutDto> GetSnapshotOrderDetailAsync(string id, CancellationToken cancellationToken)
  286. {
  287. var order = await _orderRepository.GetAsync(id) ??
  288. throw UserFriendlyException.SameMessage("工单不存在");
  289. var outDto = order.Adapt<OrderPublishDetailOutDto>();
  290. var fileServiceUrl = _systemSettingCacheManager.FileServerUrl;
  291. if (outDto.FileJson != null)
  292. {
  293. foreach (var item in outDto.FileJson)
  294. {
  295. item.Path = fileServiceUrl + item.Path;
  296. }
  297. }
  298. var traces = await _workflowTraceRepository.Queryable()
  299. .Where(m => m.ExternalId == order.Id && m.Status == EWorkflowStepStatus.Handled)
  300. .OrderBy(m => m.AcceptTime)
  301. .ToListAsync(cancellationToken);
  302. var centre = traces.Where(m => m.StepType == EStepType.End || m.StepType == EStepType.Start || m.BusinessType == EBusinessType.Send || m.BusinessType == EBusinessType.Seat || m.BusinessType == EBusinessType.File)
  303. .Select(m => new SnapshotWorkflow(m.Id, m.Name, m.HandleTime.Value))
  304. .ToList();
  305. outDto.Workflow = traces.Where(m => !centre.Select(s => s.Id).ToList().Contains(m.Id))
  306. .Select(m => new SnapshotWorkflow(m.Id, m.Name, m.HandleTime.Value))
  307. .ToList();
  308. outDto.Workflow.AddRange(centre);
  309. outDto.Workflow = outDto.Workflow.OrderBy(m => m.HandleTime).ToList();
  310. return outDto;
  311. }
  312. /// <summary>
  313. /// 获取当月详细红包列表
  314. /// </summary>
  315. /// <param name="dto"></param>
  316. /// <returns></returns>
  317. public async Task<IList<RedPackOutDto>> GetRedPacksAsync(RedPacksInDto dto, CancellationToken cancellationToken)
  318. {
  319. var items = await _redPackRecordRepository.Queryable(includeDeleted: true)
  320. .Where(m => m.IsDeleted == false)
  321. .Where(m => m.WXOpenId == _sessionContext.OpenId)
  322. .Where(m => m.PickupStatus == dto.Status)
  323. .Where(m => m.CreationTime.ToString("yyyy-MM") == dto.Time)
  324. .LeftJoin<Order>((red, order) => red.OrderId == order.Id)
  325. .Select((red, order) => new RedPackOutDto
  326. {
  327. Amount = red.Amount,
  328. Title = order.Title,
  329. CreationTime = red.CreationTime
  330. })
  331. .ToFixedListAsync(dto, cancellationToken);
  332. return items;
  333. }
  334. /// <summary>
  335. /// 按月统计红包金额
  336. /// </summary>
  337. /// <param name="count"></param>
  338. /// <returns></returns>
  339. public async Task<IReadOnlyList<RedPackDateOutDto>> GetRedPackDateAsync(RedPackDateInDto dto)
  340. {
  341. var openId = _sessionContext.OpenId;
  342. var item = await _redPackRecordRepository.Queryable()
  343. .Where(m => m.WXOpenId == openId)
  344. .Where(m => m.PickupStatus == dto.Status)
  345. .GroupBy(m => m.CreationTime.ToString("yyyy-MM"))
  346. .OrderByDescending(m => m.CreationTime)
  347. .Select(m => new RedPackDateOutDto
  348. {
  349. CreationTime = SqlFunc.AggregateMax(m.CreationTime.Date),
  350. Amount = SqlFunc.AggregateSum(m.Amount)
  351. })
  352. .Take(dto.Count)
  353. .ToListAsync();
  354. return item;
  355. }
  356. /// <summary>
  357. /// 获取随手拍公告详情
  358. /// </summary>
  359. /// <param name="id"></param>
  360. /// <returns></returns>
  361. public async Task<BulletinOutDto> GetBulletinsDetailAsync(string id)
  362. {
  363. var detail = await _bulletinRepository.Queryable()
  364. .Where(m => m.Id == id)
  365. .Where(m => m.BulletinState == Share.Enums.Article.EBulletinState.ReviewPass)
  366. .Select(m => new BulletinOutDto
  367. {
  368. Id = m.Id,
  369. Title = m.Title,
  370. Content = m.Content,
  371. CreationTime = m.CreationTime
  372. })
  373. .FirstAsync();
  374. return detail;
  375. }
  376. /// <summary>
  377. /// 添加随手拍公告
  378. /// </summary>
  379. /// <returns></returns>
  380. public async Task<string> AddBulletinAsync(AddSnapshotBulletinInDto dto)
  381. {
  382. dto.ValidateObject();
  383. var entity = dto.Adapt<SnapshotBulletin>();
  384. entity.BulletinState = EBulletinState.InReview;
  385. entity.Id = await _bulletinRepository.AddAsync(entity);
  386. return entity.Id;
  387. }
  388. /// <summary>
  389. /// 审核公告
  390. /// </summary>
  391. /// <param name="examineBulletinDto"></param>
  392. /// <returns></returns>
  393. public async Task AuditBulletinAsync(ExamineBulletinDto dto)
  394. {
  395. var bulletin = await _bulletinRepository.GetAsync(dto.Id)
  396. ?? throw UserFriendlyException.SameMessage("无效数据");
  397. if (bulletin.BulletinState != EBulletinState.InReview)
  398. throw UserFriendlyException.SameMessage("当前状态不能审核");
  399. bulletin.ExaminOpinion = dto.Reason;
  400. bulletin.ExaminTime = DateTime.Now;
  401. bulletin.ExaminManId = _sessionContext.RequiredUserId;
  402. if (dto.IsPass)
  403. {
  404. bulletin.BulletinState = EBulletinState.ReviewPass;
  405. }
  406. else
  407. {
  408. bulletin.BulletinState = EBulletinState.ReviewNoPass;
  409. }
  410. await _bulletinRepository.UpdateAsync(bulletin);
  411. }
  412. }