CallReportApplicationBase.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. using Hotline.Caching.Interfaces;
  2. using Hotline.Repository.SqlSugar.Extensions;
  3. using Hotline.CallCenter.Calls;
  4. using Hotline.Repository.SqlSugar.CallCenter;
  5. using Hotline.Settings;
  6. using Hotline.Share.Dtos.CallCenter;
  7. using Hotline.Share.Dtos.TrCallCenter;
  8. using Hotline.Share.Enums.CallCenter;
  9. using Hotline.Share.Requests;
  10. using SqlSugar;
  11. using XF.Domain.Exceptions;
  12. using Hotline.Orders;
  13. using Hotline.Share.Enums.User;
  14. using Hotline.Users;
  15. using XF.Domain.Repository;
  16. using Hotline.CallCenter.Tels;
  17. namespace Hotline.Application.StatisticalReport.CallReport;
  18. public abstract class CallReportApplicationBase : ICallReportApplication
  19. {
  20. private readonly ISystemSettingCacheManager _systemSettingCacheManager;
  21. private readonly ICallNativeRepository _callNativeRepository;
  22. private readonly IRepository<User> _userRepository;
  23. private readonly IRepository<Work> _workRepository;
  24. private readonly IRepository<TelRest> _telRestRepository;
  25. protected CallReportApplicationBase(ISystemSettingCacheManager systemSettingCacheManager, ICallNativeRepository callNativeRepository, IRepository<User> userRepository, IRepository<Work> workRepository, IRepository<TelRest> telRestRepository)
  26. {
  27. _systemSettingCacheManager = systemSettingCacheManager;
  28. _callNativeRepository = callNativeRepository;
  29. _userRepository = userRepository;
  30. _workRepository = workRepository;
  31. _telRestRepository = telRestRepository;
  32. }
  33. public virtual async Task<List<CallHotLineDto>> GetCallHotLineListAsync(BiQueryGateWayDto dto, CancellationToken requestAborted)
  34. {
  35. int noConnectByeTimes = _systemSettingCacheManager.NoConnectByeTimes;
  36. int effectiveTimes = _systemSettingCacheManager.EffectiveTimes;
  37. int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes;
  38. int ringTimes = _systemSettingCacheManager.RingTimes;
  39. return await _callNativeRepository.GetCallHotLineListAsync(dto, noConnectByeTimes, effectiveTimes, connectByeTimes, ringTimes);
  40. }
  41. public virtual async Task<List<TrCallHourDto>> GetCallHourListAsync(BiQueryHourCallDto dto, CancellationToken requestAborted)
  42. {
  43. int noConnectByeTimes = _systemSettingCacheManager.NoConnectByeTimes;
  44. int effectiveTimes = _systemSettingCacheManager.EffectiveTimes;
  45. int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes;
  46. return await _callNativeRepository.GetCallHourList(dto.StartTime, dto.EndTime, noConnectByeTimes, effectiveTimes, connectByeTimes, dto.Source);
  47. }
  48. public virtual async Task<TotalData<BiSeatSwitchDto>> GetCallListAsync(QueryCallListDto dto, CancellationToken requestAborted)
  49. {
  50. int noConnectByeTimes = _systemSettingCacheManager.NoConnectByeTimes;
  51. int effectiveTimes = _systemSettingCacheManager.EffectiveTimes;
  52. int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes;
  53. return await _callNativeRepository.GetCallList(dto, noConnectByeTimes, effectiveTimes, connectByeTimes);
  54. }
  55. public virtual async Task<List<BiCallDto>> QueryCallsAsync(BiQueryCallsDto dto, CancellationToken cancellationToken)
  56. {
  57. return await _callNativeRepository.GetQueryCalls(dto.StartTime.Value, dto.EndTime.Value, dto.Line);
  58. }
  59. /// <summary>
  60. /// 话务日期明细
  61. /// </summary>
  62. /// <param name="dto"></param>
  63. /// <returns></returns>
  64. public virtual async Task<List<QueryCallsDetailDto>> QueryCallsDetailAsync(BiQueryCallsDto dto)
  65. {
  66. //超时接通量
  67. int CallInOverConnRingTime = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.CallInOverConnRingTime)?.SettingValue[0]);
  68. //坐席超时挂断时间
  69. int SeatChaoTime = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.SeatChaoTime)?.SettingValue[0]);
  70. //未接秒挂时间
  71. int noConnectByeTimes = _systemSettingCacheManager.NoConnectByeTimes;
  72. //呼入有效时间
  73. int effectiveTimes = _systemSettingCacheManager.EffectiveTimes;
  74. //接通秒挂时间
  75. int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes;
  76. var callData = await _callNativeRepository.Queryable()
  77. .Where(p => p.CreationTime >= dto.StartTime && p.CreationTime <= dto.EndTime)
  78. // .Where(p => p.Gateway != "82826886" && SqlFunc.Length(p.Gateway) != 4)
  79. .WhereIF(!string.IsNullOrEmpty(dto.Keyword), p => p.ToNo == dto.Keyword)
  80. .GroupBy(p => p.CreationTime.ToString("yyyy-MM-dd"))
  81. .Select(p => new QueryCallsDetailDto
  82. {
  83. Date = p.CreationTime.ToString("yyyy-MM-dd"),
  84. InTotal = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In, 1, 0)),//呼入总量
  85. InConnectionQuantity = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.AnsweredTime != null, 1, 0)),//呼入接通量
  86. NotAcceptedHang = SqlFunc.AggregateSum(SqlFunc.IIF(p.Duration == 0 && p.RingDuration <= noConnectByeTimes && p.RingDuration > 0 && p.Direction == ECallDirection.In, 1, 0)), //未接通秒挂
  87. TotalDurationIncomingCalls = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.AnsweredTime != null, p.Duration, 0)), //呼入总时长
  88. InAvailableAnswer = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.AnsweredTime != null && p.Duration >= effectiveTimes, 1, 0)),//有效接通量
  89. InHangupImmediateWhenAnswered = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.Duration > 0 && p.Duration <= connectByeTimes, 1, 0)), //呼入接通秒挂
  90. TimeoutConnection = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.AnsweredTime != null && p.RingDuration >= CallInOverConnRingTime, 1, 0)),//超时接通量
  91. TimeoutSuspension = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.AnsweredTime != null && p.Duration >= SeatChaoTime, 1, 0)),//超时挂断量
  92. QueueByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.WaitDuration > 0 && p.RingDuration == 0 && p.AnsweredTime == null, 1, 0)), //队列挂断
  93. IvrByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.BeginIvrTime.HasValue && !p.BeginQueueTime.HasValue && !p.BeginRingTime.HasValue && p.AnsweredTime == null, 1, 0)), //IVR挂断
  94. OutTotal = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.Out, 1, 0)),//呼出总量
  95. OutConnectionQuantity = SqlFunc.AggregateSum(SqlFunc.IIF(p.AnsweredTime != null && p.Direction == ECallDirection.Out && p.AnsweredTime != null, 1, 0))
  96. })
  97. .OrderBy(p => p.Date)
  98. .ToListAsync();
  99. return callData;
  100. }
  101. /// <summary>
  102. /// 话务日期明细-呼入总量/接通总量
  103. /// </summary>
  104. /// <param name="dto"></param>
  105. /// <returns></returns>
  106. public virtual async Task<(int, List<CallRecordOutDto>)> QueryCallsDetailInTotalAsync(BiQueryCallsDto dto, bool isAll)
  107. {
  108. var recordPrefix = _systemSettingCacheManager.RecordPrefix;
  109. var query = _callNativeRepository.Queryable()
  110. .LeftJoin<Order>((p, o) => p.Id == o.CallId)
  111. .Where((p, o) => p.CreationTime >= dto.StartTime && p.CreationTime <= dto.EndTime && p.Direction == ECallDirection.In)
  112. .WhereIF(dto.TypeCode == "2", (p, o) => p.AnsweredTime != null)
  113. .WhereIF(!string.IsNullOrEmpty(dto.Keyword), (p, o) => p.TelNo == dto.Keyword)
  114. .OrderByDescending((p, o) => p.CreationTime)
  115. .Select((p, o) => new CallRecordOutDto
  116. {
  117. OtherAccept = p.Id,
  118. OrderId = o.Id,
  119. OrderNo = o.No,
  120. OrderTitle = o.Title,
  121. Cdpn = p.ToNo,
  122. Cpn = p.FromNo,
  123. RecordingFileUrl = recordPrefix + p.AudioFile,
  124. RecordingFileName = p.AudioFile,
  125. RecordingBaseAddress = recordPrefix,
  126. RecordingAbsolutePath = p.AudioFile
  127. }, true);
  128. if (isAll)
  129. {
  130. return (0, await query.ToListAsync());
  131. }
  132. return await query.ToPagedListAsync(dto.PageIndex, dto.PageSize);
  133. }
  134. public virtual async Task<List<QueryCallsDetailDto>> QueryCallsHourDetailAsync(BiQueryCallsDto dto, CancellationToken cancellationToken)
  135. {
  136. //超时接通量
  137. int CallInOverConnRingTime = _systemSettingCacheManager.CallInOverConnRingTime;
  138. //坐席超时挂断时间
  139. int SeatChaoTime = _systemSettingCacheManager.SeatChaoTime;
  140. //未接秒挂时间
  141. int noConnectByeTimes = _systemSettingCacheManager.NoConnectByeTimes;
  142. //呼入有效时间
  143. int effectiveTimes = _systemSettingCacheManager.EffectiveTimes;
  144. //接通秒挂时间
  145. int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes;
  146. return await _callNativeRepository.QueryCallsHourDetail(dto.StartTime.Value, dto.EndTime.Value, noConnectByeTimes, effectiveTimes, connectByeTimes, CallInOverConnRingTime, SeatChaoTime, dto.Line);
  147. }
  148. /// <summary>
  149. /// 坐席话务统计分析
  150. /// </summary>
  151. /// <param name="dto"></param>
  152. /// <returns></returns>
  153. public virtual async Task<List<BiSeatCallsDto>> QuerySeatCallAsync(ReportRequiredPagedRequest dto, CancellationToken cancellationToken)
  154. {
  155. int noConnectByeTimes = _systemSettingCacheManager.NoConnectByeTimes;
  156. int effectiveTimes = _systemSettingCacheManager.EffectiveTimes;
  157. int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes;
  158. var list = await _userRepository.Queryable()
  159. .LeftJoin<CallNative>((u, c) => u.Id == c.UserId)
  160. .Where(u => !u.IsDeleted && u.UserType == EUserType.Seat)
  161. .Where((u, c) => c.CreationTime >= dto.StartTime)
  162. .Where((u, c) => c.CreationTime <= dto.EndTime)
  163. .GroupBy((u, c) => new { c.UserName, c.UserId })
  164. .Select((u, c) => new BiSeatCallsDto
  165. {
  166. Name = c.UserName,
  167. UserId = c.UserId,
  168. InTotal = SqlFunc.AggregateSum(SqlFunc.IIF(c.Direction == ECallDirection.In, 1, 0)),
  169. OutTotal = SqlFunc.AggregateSum(SqlFunc.IIF(c.Direction == ECallDirection.Out, 1, 0)),
  170. InAnswered = SqlFunc.AggregateSum(SqlFunc.IIF(c.Direction == ECallDirection.In && c.AnsweredTime != null, 1, 0)),
  171. OutAnswered = SqlFunc.AggregateSum(SqlFunc.IIF(c.Direction == ECallDirection.Out && c.AnsweredTime != null, 1, 0)),
  172. InHangupImmediate = SqlFunc.AggregateSum(SqlFunc.IIF(c.Direction == ECallDirection.In && c.AnsweredTime == null && c.RingDuration < noConnectByeTimes, 1, 0)),
  173. InHanguped = SqlFunc.AggregateSum(SqlFunc.IIF(c.Direction == ECallDirection.In && c.AnsweredTime == null, 1, 0)),
  174. InDurationAvg = SqlFunc.Ceil(SqlFunc.AggregateAvg(SqlFunc.IIF(c.Direction == ECallDirection.In && c.AnsweredTime != null, c.Duration, 0))),
  175. OutDurationAvg = SqlFunc.Ceil(SqlFunc.AggregateAvg(SqlFunc.IIF(c.Direction == ECallDirection.Out && c.AnsweredTime != null, c.Duration, 0))),
  176. InAvailableAnswer = SqlFunc.AggregateSum(SqlFunc.IIF(c.Direction == ECallDirection.In && c.AnsweredTime != null && c.Duration >= effectiveTimes, 1, 0)),
  177. InHangupImmediateWhenAnswered = SqlFunc.AggregateSum(SqlFunc.IIF(c.Direction == ECallDirection.In && c.AnsweredTime != null && c.Duration < connectByeTimes, 1, 0)),
  178. })
  179. .MergeTable()
  180. .ToListAsync();
  181. list.ForEach(d =>
  182. {
  183. d.LoginDuration = _workRepository.Queryable().Where(q => q.UserId == d.UserId && q.CreationTime >= dto.StartTime && q.CreationTime <= dto.EndTime).Sum(q => q.WorkingDuration);
  184. if (d.LoginDuration != null)
  185. {
  186. d.LoginDuration = Math.Round(d.LoginDuration.Value, digits: 2);
  187. }
  188. d.RestDuration = _telRestRepository.Queryable().Where(q => q.UserId == d.UserId && q.CreationTime >= dto.StartTime && q.CreationTime <= dto.EndTime).Sum(q => q.RestDuration);
  189. d.RestDuration = Math.Round(d.RestDuration, digits: 2);
  190. });
  191. return list;
  192. }
  193. public virtual async Task<(int, List<BiSeatSwitchDto>)> QuerySeatSwitchAsync(QuerySeatSwitchRequest dto, CancellationToken cancellationToken)
  194. {
  195. return await _callNativeRepository.Queryable()
  196. .Where(x => !string.IsNullOrEmpty(x.AgentTransferNumber))
  197. .WhereIF(!string.IsNullOrEmpty(dto.UserName), x => x.UserName.Contains(dto.UserName))
  198. .WhereIF(!string.IsNullOrEmpty(dto.CDPN), x => x.ToNo.Contains(dto.CDPN))
  199. .Where(x => x.CreationTime >= dto.StartTime)
  200. .Where(x => x.CreationTime <= dto.EndTime)
  201. .Select(x => new BiSeatSwitchDto
  202. {
  203. UserId = x.UserId,
  204. CPN = x.FromNo,
  205. CDPN = x.ToNo,
  206. CreatedTime = x.CreationTime,
  207. TelNo = x.TelNo,
  208. UserName = x.UserName,
  209. })
  210. .OrderByDescending(x => x.CreatedTime)
  211. .ToPagedListAsync(dto.PageIndex, dto.PageSize, cancellationToken);
  212. }
  213. }