BiCallController.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. using Hotline.Application.StatisticalReport;
  2. using Hotline.Caching.Interfaces;
  3. using Hotline.CallCenter.Calls;
  4. using Hotline.CallCenter.Tels;
  5. using Hotline.Repository.SqlSugar.Extensions;
  6. using Hotline.Settings;
  7. using Hotline.Share.Dtos;
  8. using Hotline.Share.Dtos.CallCenter;
  9. using Hotline.Share.Dtos.Order;
  10. using Hotline.Share.Dtos.TrCallCenter;
  11. using Hotline.Share.Enums.CallCenter;
  12. using Hotline.Share.Enums.User;
  13. using Hotline.Share.Requests;
  14. using Hotline.Tools;
  15. using Hotline.Users;
  16. using MapsterMapper;
  17. using Microsoft.AspNetCore.Authorization;
  18. using Microsoft.AspNetCore.Mvc;
  19. using NPOI.SS.Formula.Functions;
  20. using SqlSugar;
  21. using System.Data;
  22. using XF.Domain.Exceptions;
  23. using XF.Domain.Repository;
  24. namespace Hotline.Api.Controllers.Bi;
  25. /// <summary>
  26. /// 话务报表
  27. /// </summary>
  28. public class BiCallController : BaseController
  29. {
  30. private readonly IRepository<TrCallRecord> _trCallRecordRepository;
  31. private readonly IRepository<User> _userRepository;
  32. private readonly IRepository<TelRest> _telRestRepository;
  33. private readonly IMapper _mapper;
  34. private readonly ITrCallRecordRepository _trCallRecordRepositoryEx;
  35. private readonly ISystemSettingCacheManager _systemSettingCacheManager;
  36. private readonly IRepository<Work> _workRepository;
  37. private readonly ISystemDicDataCacheManager _sysDicDataCacheManager;
  38. private readonly ICallReportApplication _callReportApplication;
  39. public BiCallController(
  40. IRepository<TrCallRecord> trCallRecordRepository,
  41. IRepository<User> userRepository,
  42. IRepository<TelRest> telRestRepository,
  43. IMapper mapper,
  44. ITrCallRecordRepository trCallRecordRepositoryEx,
  45. ISystemSettingCacheManager systemSettingCacheManager,
  46. ISystemDicDataCacheManager sysDicDataCacheManager,
  47. IRepository<Work> workRepository,
  48. ICallReportApplication callReportApplication)
  49. {
  50. _trCallRecordRepository = trCallRecordRepository;
  51. _userRepository = userRepository;
  52. _telRestRepository = telRestRepository;
  53. _mapper = mapper;
  54. _trCallRecordRepositoryEx = trCallRecordRepositoryEx;
  55. _systemSettingCacheManager = systemSettingCacheManager;
  56. _workRepository = workRepository;
  57. _sysDicDataCacheManager = sysDicDataCacheManager;
  58. _callReportApplication = callReportApplication;
  59. }
  60. /// <summary>
  61. /// 话务统计分析
  62. /// </summary>
  63. /// <param name="dto"></param>
  64. /// <returns></returns>
  65. [HttpGet("calls")]
  66. [AllowAnonymous]
  67. public async Task<List<BiCallDto>> QueryCallsAsync([FromQuery] BiQueryCallsDto dto)
  68. {
  69. if (!dto.StartTime.HasValue || !dto.EndTime.HasValue) throw UserFriendlyException.SameMessage("请选择时间!");
  70. #region 注释
  71. //var items = await _trCallRecordRepository.Queryable()
  72. // .Where(d => d.CreatedTime >= dto.StartTime && d.CreatedTime <= dto.EndTime)
  73. // .Where(d => d.CallDirection == ECallDirection.In)
  74. // .WhereIF(!string.IsNullOrEmpty(dto.Line), d => d.Gateway == dto.Line)
  75. // .Select(d => new
  76. // {
  77. // d.CreatedTime.Year,
  78. // d.CreatedTime.Month,
  79. // d.CreatedTime.Day,
  80. // d.CreatedTime.Hour,
  81. // d.AnsweredTime,
  82. // d.EndBy
  83. // })
  84. // .MergeTable()
  85. // .GroupBy(d => new
  86. // {
  87. // d.Year,
  88. // d.Month,
  89. // d.Day,
  90. // d.Hour
  91. // })
  92. // .Select(d => new BiCallDto
  93. // {
  94. // Hour = d.Hour,
  95. // Total = SqlFunc.AggregateCount(d.Hour),
  96. // Answered = SqlFunc.AggregateSum(SqlFunc.IIF(d.AnsweredTime != null, 1, 0)),
  97. // Hanguped = SqlFunc.AggregateSum(SqlFunc.IIF(d.AnsweredTime == null && d.EndBy != null && d.EndBy.Value == EEndBy.To, 1, 0))
  98. // })
  99. // .OrderBy(d => d.Hour)
  100. // .ToListAsync(HttpContext.RequestAborted);
  101. //if (items.Count < 24)
  102. //{
  103. // for (var i = 0; i < 24; i++)
  104. // {
  105. // var item = items.FirstOrDefault(d => d.Hour == i);
  106. // if (item is null)
  107. // items.Add(new BiCallDto { Hour = i });
  108. // }
  109. // items = items.OrderBy(d => d.Hour).ToList();
  110. //}
  111. #endregion
  112. dto.EndTime = dto.EndTime.Value.AddDays(1).AddSeconds(-1);
  113. return await _trCallRecordRepositoryEx.GetQueryCalls(dto.StartTime.Value, dto.EndTime.Value, dto.Keyword);
  114. }
  115. /// <summary>
  116. /// 话务统计分析---导出
  117. /// </summary>
  118. /// <param name="dto"></param>
  119. /// <returns></returns>
  120. [HttpPost("calls_export")]
  121. [AllowAnonymous]
  122. public async Task<FileStreamResult> ExportQueryCallsAsync([FromBody] ExportExcelDto<BiQueryCallsDto> dto)
  123. {
  124. if (!dto.QueryDto.StartTime.HasValue || !dto.QueryDto.EndTime.HasValue) throw UserFriendlyException.SameMessage("请选择时间!");
  125. dto.QueryDto.EndTime = dto.QueryDto.EndTime.Value.AddDays(1).AddSeconds(-1);
  126. var list = await _trCallRecordRepositoryEx.GetQueryCalls(dto.QueryDto.StartTime.Value, dto.QueryDto.EndTime.Value, dto.QueryDto.Keyword);
  127. if (list != null && list.Count > 0)
  128. {
  129. list.Add(new BiCallDto()
  130. {
  131. HourRange = "合计",
  132. Hour = 13,
  133. Total = list.Sum(p => p.Total),
  134. Answered = list.Sum(p => p.Answered),
  135. Hanguped = list.Sum(p => p.Hanguped)
  136. });
  137. }
  138. dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
  139. var dtos = list
  140. .Select(stu => _mapper.Map(stu, typeof(BiCallDto), dynamicClass))
  141. .Cast<object>()
  142. .ToList();
  143. var stream = ExcelHelper.CreateStream(dtos);
  144. return ExcelStreamResult(stream, "话务统计分析");
  145. }
  146. /// <summary>
  147. /// 话务日期明细
  148. /// </summary>
  149. /// <param name="dto"></param>
  150. /// <returns></returns>
  151. [HttpGet("query_calls_detail")]
  152. public async Task<object> QueryCallsDetailAsync([FromQuery] BiQueryCallsDto dto)
  153. {
  154. if (!dto.StartTime.HasValue || !dto.EndTime.HasValue)
  155. throw UserFriendlyException.SameMessage("请选择时间!");
  156. dto.EndTime = dto.EndTime.Value.AddDays(1).AddSeconds(-1);
  157. var items = await _callReportApplication.QueryCallsDetailAsync(dto);
  158. var total = new QueryCallsDetailDto
  159. {
  160. Date = "合计",
  161. Hour = "",
  162. InTotal = items.Sum(p => p.InTotal),
  163. InConnectionQuantity = items.Sum(p => p.InConnectionQuantity),
  164. NotAcceptedHang = items.Sum(p => p.NotAcceptedHang),
  165. TotalDurationIncomingCalls = items.Sum(p => p.TotalDurationIncomingCalls),
  166. InAvailableAnswer = items.Sum(p => p.InAvailableAnswer),
  167. InHangupImmediateWhenAnswered = items.Sum(p => p.InHangupImmediateWhenAnswered),
  168. TimeoutConnection = items.Sum(p => p.TimeoutConnection),
  169. TimeoutSuspension = items.Sum(p => p.TimeoutSuspension),
  170. QueueByeCount = items.Sum(p => p.QueueByeCount),
  171. IvrByeCount = items.Sum(p => p.IvrByeCount),
  172. OutTotal = items.Sum(p => p.OutTotal),
  173. OutConnectionQuantity = items.Sum(p => p.OutConnectionQuantity)
  174. };
  175. return new { List = items, Total = total };
  176. }
  177. /// <summary>
  178. /// 话务日期明细--导出
  179. /// </summary>
  180. /// <param name="dto"></param>
  181. /// <returns></returns>
  182. [HttpPost("query_calls_detail_export")]
  183. public async Task<FileStreamResult> QueryCallsDetailExportAsync([FromBody] ExportExcelDto<BiQueryCallsDto> dto)
  184. {
  185. if (!dto.QueryDto.StartTime.HasValue || !dto.QueryDto.EndTime.HasValue)
  186. throw UserFriendlyException.SameMessage("请选择时间!");
  187. dto.QueryDto.EndTime = dto.QueryDto.EndTime.Value.AddDays(1).AddSeconds(-1);
  188. var items = await _callReportApplication.QueryCallsDetailAsync(dto.QueryDto);
  189. var total = new QueryCallsDetailDto
  190. {
  191. Date = "合计",
  192. Hour = "",
  193. InTotal = items.Sum(p => p.InTotal),
  194. InConnectionQuantity = items.Sum(p => p.InConnectionQuantity),
  195. NotAcceptedHang = items.Sum(p => p.NotAcceptedHang),
  196. TotalDurationIncomingCalls = items.Sum(p => p.TotalDurationIncomingCalls),
  197. InAvailableAnswer = items.Sum(p => p.InAvailableAnswer),
  198. InHangupImmediateWhenAnswered = items.Sum(p => p.InHangupImmediateWhenAnswered),
  199. TimeoutConnection = items.Sum(p => p.TimeoutConnection),
  200. TimeoutSuspension = items.Sum(p => p.TimeoutSuspension),
  201. QueueByeCount = items.Sum(p => p.QueueByeCount),
  202. IvrByeCount = items.Sum(p => p.IvrByeCount),
  203. OutTotal = items.Sum(p => p.OutTotal),
  204. OutConnectionQuantity = items.Sum(p => p.OutConnectionQuantity)
  205. };
  206. items.Add(total);
  207. dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
  208. var dtos = items
  209. .Select(stu => _mapper.Map(stu, typeof(QueryCallsDetailDto), dynamicClass))
  210. .Cast<object>()
  211. .ToList();
  212. var stream = ExcelHelper.CreateStream(dtos);
  213. return ExcelStreamResult(stream, "话务日期明细数据");
  214. }
  215. /// <summary>
  216. /// 话务日期明细--呼入明细
  217. /// </summary>
  218. /// <param name="dto"></param>
  219. /// <returns></returns>
  220. [HttpGet("query_incall_calls_list")]
  221. public async Task<PagedDto<TrCallDtoNew>> GetInCallCallListAsync([FromQuery] BiQueryCallsDto dto)
  222. {
  223. var (total, items) = await _callReportApplication.QueryCallsDetailInTotalAsync(dto)
  224. .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
  225. return new PagedDto<TrCallDtoNew>(total, _mapper.Map<IReadOnlyList<TrCallDtoNew>>(items));
  226. }
  227. /// <summary>
  228. /// 话务日期明细----导出
  229. /// </summary>
  230. /// <param name="dto"></param>
  231. /// <returns></returns>
  232. [HttpPost("query_incall_calls_list_export")]
  233. public async Task<FileStreamResult> GetInCallCallListExportAsync([FromBody] ExportExcelDto<BiQueryCallsDto> dto)
  234. {
  235. var query = _callReportApplication.QueryCallsDetailInTotalAsync(dto.QueryDto);
  236. List<TrCallRecord> data;
  237. if (dto.IsExportAll)
  238. {
  239. data = await query.ToListAsync(HttpContext.RequestAborted);
  240. }
  241. else
  242. {
  243. var (_, items) = await query.ToPagedListAsync(dto.QueryDto, HttpContext.RequestAborted);
  244. data = items;
  245. }
  246. var dataDtos = _mapper.Map<ICollection<TrCallDtoNew>>(data);
  247. dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
  248. var dtos = dataDtos
  249. .Select(stu => _mapper.Map(stu, typeof(TrCallDtoNew), dynamicClass))
  250. .Cast<object>()
  251. .ToList();
  252. var stream = ExcelHelper.CreateStream(dtos);
  253. string name = dto.QueryDto.TypeCode == "2" ? "话务日期-接通明细数据" : "话务日期-总量明细数据";
  254. return ExcelStreamResult(stream, name);
  255. }
  256. /// <summary>
  257. /// 话务日期明细--时间段
  258. /// </summary>
  259. /// <param name="dto"></param>
  260. /// <returns></returns>
  261. [HttpGet("query_calls_hour_detail_list")]
  262. public async Task<object> QueryCallsHourDetailListAsync([FromQuery] BiQueryCallsDto dto)
  263. {
  264. if (!dto.StartTime.HasValue || !dto.EndTime.HasValue)
  265. throw UserFriendlyException.SameMessage("请选择时间!");
  266. dto.EndTime = dto.EndTime.Value.AddDays(1).AddSeconds(-1);
  267. //超时接通量
  268. int CallInOverConnRingTime = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.CallInOverConnRingTime)?.SettingValue[0]);
  269. //坐席超时挂断时间
  270. int SeatChaoTime = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.SeatChaoTime)?.SettingValue[0]);
  271. //未接秒挂时间
  272. int noConnectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.NoConnectByeTimes)?.SettingValue[0]);
  273. //呼入有效时间
  274. int effectiveTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.EffectiveTimes)?.SettingValue[0]);
  275. //接通秒挂时间
  276. int connectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.ConnectByeTimes)?.SettingValue[0]);
  277. var items = await _trCallRecordRepositoryEx.QueryCallsHourDetail(dto.StartTime.Value, dto.EndTime.Value, noConnectByeTimes, effectiveTimes
  278. , connectByeTimes, CallInOverConnRingTime, SeatChaoTime, dto.Keyword);
  279. var total = new QueryCallsDetailDto
  280. {
  281. Date = "",
  282. Hour = "合计",
  283. InTotal = items.Sum(p => p.InTotal),
  284. InConnectionQuantity = items.Sum(p => p.InConnectionQuantity),
  285. NotAcceptedHang = items.Sum(p => p.NotAcceptedHang),
  286. TotalDurationIncomingCalls = items.Sum(p => p.TotalDurationIncomingCalls),
  287. InAvailableAnswer = items.Sum(p => p.InAvailableAnswer),
  288. InHangupImmediateWhenAnswered = items.Sum(p => p.InHangupImmediateWhenAnswered),
  289. TimeoutConnection = items.Sum(p => p.TimeoutConnection),
  290. TimeoutSuspension = items.Sum(p => p.TimeoutSuspension),
  291. QueueByeCount = items.Sum(p => p.QueueByeCount),
  292. IvrByeCount = items.Sum(p => p.IvrByeCount),
  293. OutTotal = items.Sum(p => p.OutTotal),
  294. OutConnectionQuantity = items.Sum(p => p.OutConnectionQuantity)
  295. };
  296. return new { List = items, Total = total };
  297. }
  298. /// <summary>
  299. /// 话务日期明细--时间段--导出
  300. /// </summary>
  301. /// <param name="dto"></param>
  302. /// <returns></returns>
  303. [HttpPost("query_calls_hour_detail_list_export")]
  304. public async Task<FileStreamResult> QueryCallsHourDetailListExportAsync([FromBody] ExportExcelDto<BiQueryCallsDto> dto)
  305. {
  306. if (!dto.QueryDto.StartTime.HasValue || !dto.QueryDto.EndTime.HasValue)
  307. throw UserFriendlyException.SameMessage("请选择时间!");
  308. dto.QueryDto.EndTime = dto.QueryDto.EndTime.Value.AddDays(1).AddSeconds(-1);
  309. //超时接通量
  310. int CallInOverConnRingTime = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.CallInOverConnRingTime)?.SettingValue[0]);
  311. //坐席超时挂断时间
  312. int SeatChaoTime = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.SeatChaoTime)?.SettingValue[0]);
  313. //未接秒挂时间
  314. int noConnectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.NoConnectByeTimes)?.SettingValue[0]);
  315. //呼入有效时间
  316. int effectiveTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.EffectiveTimes)?.SettingValue[0]);
  317. //接通秒挂时间
  318. int connectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.ConnectByeTimes)?.SettingValue[0]);
  319. var items = await _trCallRecordRepositoryEx.QueryCallsHourDetail(dto.QueryDto.StartTime.Value, dto.QueryDto.EndTime.Value, noConnectByeTimes, effectiveTimes
  320. , connectByeTimes, CallInOverConnRingTime, SeatChaoTime, dto.QueryDto.Keyword);
  321. var total = new QueryCallsDetailDto
  322. {
  323. Date = "",
  324. Hour = "合计",
  325. InTotal = items.Sum(p => p.InTotal),
  326. InConnectionQuantity = items.Sum(p => p.InConnectionQuantity),
  327. NotAcceptedHang = items.Sum(p => p.NotAcceptedHang),
  328. TotalDurationIncomingCalls = items.Sum(p => p.TotalDurationIncomingCalls),
  329. InAvailableAnswer = items.Sum(p => p.InAvailableAnswer),
  330. InHangupImmediateWhenAnswered = items.Sum(p => p.InHangupImmediateWhenAnswered),
  331. TimeoutConnection = items.Sum(p => p.TimeoutConnection),
  332. TimeoutSuspension = items.Sum(p => p.TimeoutSuspension),
  333. QueueByeCount = items.Sum(p => p.QueueByeCount),
  334. IvrByeCount = items.Sum(p => p.IvrByeCount),
  335. OutTotal = items.Sum(p => p.OutTotal),
  336. OutConnectionQuantity = items.Sum(p => p.OutConnectionQuantity)
  337. };
  338. items.Add(total);
  339. dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
  340. var dtos = items
  341. .Select(stu => _mapper.Map(stu, typeof(QueryCallsDetailDto), dynamicClass))
  342. .Cast<object>()
  343. .ToList();
  344. var stream = ExcelHelper.CreateStream(dtos);
  345. return ExcelStreamResult(stream, "话务日期明细-时间段");
  346. }
  347. /// <summary>
  348. /// 坐席话务统计分析
  349. /// </summary>
  350. /// <param name="dto"></param>
  351. /// <returns></returns>
  352. [HttpPost("seats/export")]
  353. public async Task<FileStreamResult> ExportSeatss([FromBody] ExportExcelDto<ReportPagedRequest> dto)
  354. {
  355. if (!dto.QueryDto.StartTime.HasValue || !dto.QueryDto.EndTime.HasValue) throw UserFriendlyException.SameMessage("请选择时间!");
  356. dto.QueryDto.EndTime = dto.QueryDto.EndTime.Value.AddDays(1).AddSeconds(-1);
  357. var list = await _callReportApplication.QuerySeatCallAsync(dto.QueryDto);
  358. if (list != null && list.Count > 0)
  359. {
  360. list.Add(new BiSeatCallsDto()
  361. {
  362. Name = "合计",
  363. InTotal = list.Sum(p => p.InTotal),
  364. InAnswered = list.Sum(p => p.InAnswered),
  365. InHangupImmediate = list.Sum(p => p.InHangupImmediate),
  366. InHanguped = list.Sum(m => m.InHanguped),
  367. OutDurationAvg = list.Sum(m => m.OutDurationAvg),
  368. InAvailableAnswer = list.Sum(m => m.InAvailableAnswer),
  369. InHangupImmediateWhenAnswered = list.Sum(m => m.InHangupImmediateWhenAnswered),
  370. OutTotal = list.Sum(m => m.OutTotal),
  371. OutAnswered = list.Sum(m => m.OutAnswered),
  372. LoginDuration = list.Sum(m => m.LoginDuration),
  373. RestDuration = list.Sum(m => m.RestDuration),
  374. InDurationAvg = list.Sum(m => m.InDurationAvg)
  375. });
  376. }
  377. dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
  378. var dtos = list
  379. .Select(stu => _mapper.Map(stu, typeof(BiSeatCallsDto), dynamicClass))
  380. .Cast<object>()
  381. .ToList();
  382. var stream = ExcelHelper.CreateStream(dtos);
  383. return ExcelStreamResult(stream, "坐席话务统计分析");
  384. }
  385. /// <summary>
  386. /// 坐席话务统计分析
  387. /// </summary>
  388. /// <param name="dto"></param>
  389. /// <returns></returns>
  390. [HttpGet("seats")]
  391. [AllowAnonymous]
  392. public async Task<IReadOnlyList<BiSeatCallsDto>> QuerySeatCallsAsync([FromQuery] ReportPagedRequest dto)
  393. {
  394. if (!dto.StartTime.HasValue || !dto.EndTime.HasValue) throw UserFriendlyException.SameMessage("请选择时间!");
  395. dto.EndTime = dto.EndTime.Value.AddDays(1).AddSeconds(-1);
  396. //获取配置
  397. int noConnectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.NoConnectByeTimes)?.SettingValue[0]);
  398. int effectiveTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.EffectiveTimes)?.SettingValue[0]);
  399. int connectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.ConnectByeTimes)?.SettingValue[0]);
  400. var list = await _userRepository.Queryable()
  401. .LeftJoin<TrCallRecord>((u, c) => u.Id == c.UserId)
  402. .Where(u => !u.IsDeleted && u.UserType == EUserType.Seat)
  403. .WhereIF(dto.StartTime.HasValue, (u, c) => c.CreatedTime >= dto.StartTime.Value)
  404. .WhereIF(dto.EndTime.HasValue, (u, c) => c.CreatedTime <= dto.EndTime.Value)
  405. .GroupBy((u, c) => new { c.UserName, c.UserId })
  406. .Select((u, c) => new BiSeatCallsDto
  407. {
  408. Name = c.UserName,
  409. UserId = c.UserId,
  410. InTotal = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.In, 1, 0)),
  411. OutTotal = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.Out, 1, 0)),
  412. InAnswered = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.In && c.AnsweredTime != null, 1, 0)),
  413. OutAnswered = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.Out && c.AnsweredTime != null, 1, 0)),
  414. InHangupImmediate = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.In && c.AnsweredTime == null && c.RingTimes < noConnectByeTimes, 1, 0)),
  415. InHanguped = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.In && c.AnsweredTime == null, 1, 0)),
  416. InDurationAvg = SqlFunc.Ceil(SqlFunc.AggregateAvg(SqlFunc.IIF(c.CallDirection == ECallDirection.In && c.AnsweredTime != null, c.Duration, 0))),
  417. OutDurationAvg = SqlFunc.Ceil(SqlFunc.AggregateAvg(SqlFunc.IIF(c.CallDirection == ECallDirection.Out && c.AnsweredTime != null, c.Duration, 0))),
  418. InAvailableAnswer = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.In && c.AnsweredTime != null && c.Duration >= effectiveTimes, 1, 0)),
  419. InHangupImmediateWhenAnswered = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.In && c.AnsweredTime != null && c.Duration < connectByeTimes, 1, 0)),
  420. })
  421. .MergeTable()
  422. .ToListAsync(HttpContext.RequestAborted);
  423. list.ForEach(d =>
  424. {
  425. d.LoginDuration = _workRepository.Queryable().Where(q => q.UserId == d.UserId && q.CreationTime >= dto.StartTime && q.CreationTime <= dto.EndTime).Sum(q => q.WorkingDuration);
  426. if (d.LoginDuration != null)
  427. {
  428. d.LoginDuration = Math.Round(d.LoginDuration.Value, digits: 2);
  429. }
  430. d.RestDuration = _telRestRepository.Queryable().Where(q => q.UserId == d.UserId && q.CreationTime >= dto.StartTime && q.CreationTime <= dto.EndTime).Sum(q => q.RestDuration);
  431. d.RestDuration = Math.Round(d.RestDuration, digits: 2);
  432. });
  433. return list;
  434. }
  435. /// <summary>
  436. /// 小休统计
  437. /// </summary>
  438. /// <param name="dto"></param>
  439. /// <returns></returns>
  440. [HttpGet("rests")]
  441. [AllowAnonymous]
  442. public async Task<IReadOnlyList<BiSeatRestDto>> QuerySeatRest([FromQuery] QuerySeatRestRequest dto)
  443. {
  444. if (!dto.StartTime.HasValue || !dto.EndTime.HasValue) throw UserFriendlyException.SameMessage("请选择时间!");
  445. dto.EndTime = dto.EndTime.Value.AddDays(1).AddSeconds(-1);
  446. return await _telRestRepository.Queryable()
  447. .WhereIF(!string.IsNullOrEmpty(dto.UserName), x => x.UserName.Contains(dto.UserName))
  448. .WhereIF(!string.IsNullOrEmpty(dto.StaffNo), x => x.StaffNo.Contains(dto.StaffNo))
  449. .WhereIF(dto.StartTime.HasValue, x => x.CreationTime >= dto.StartTime.Value)
  450. .WhereIF(dto.EndTime.HasValue, x => x.CreationTime <= dto.EndTime.Value)
  451. .GroupBy(x => new { x.UserId, x.StaffNo, x.UserName })
  452. .Select(x => new BiSeatRestDto
  453. {
  454. UserId = x.UserId,
  455. StaffNo = x.StaffNo,
  456. UserName = x.UserName,
  457. RestCount = SqlFunc.AggregateCount(x.Id),
  458. RestDuration = SqlFunc.AggregateSum(x.RestDuration / 60) / SqlFunc.AggregateCount(x.Id)
  459. })
  460. .OrderByIF(dto.SortRule is 0, a => a.RestDuration, OrderByType.Asc)
  461. .OrderByIF(dto.SortRule is 1, a => a.RestDuration, OrderByType.Desc)
  462. .MergeTable()
  463. .ToListAsync(HttpContext.RequestAborted);
  464. }
  465. /// <summary>
  466. /// 坐席转接统计
  467. /// </summary>
  468. /// <param name="dto"></param>
  469. /// <returns></returns>
  470. [HttpGet("seatswitch")]
  471. [AllowAnonymous]
  472. public async Task<PagedDto<BiSeatSwitchDto>> QuerySeatSwitch([FromQuery] QuerySeatSwitchRequest dto)
  473. {
  474. if (!dto.StartTime.HasValue || !dto.EndTime.HasValue) throw UserFriendlyException.SameMessage("请选择时间!");
  475. dto.EndTime = dto.EndTime.Value.AddDays(1).AddSeconds(-1);
  476. var (total, items) = await _trCallRecordRepository.Queryable()
  477. .Where(x => !string.IsNullOrEmpty(x.AgentTransferNumber))
  478. .WhereIF(!string.IsNullOrEmpty(dto.UserName), x => x.UserName.Contains(dto.UserName))
  479. .WhereIF(!string.IsNullOrEmpty(dto.CDPN), x => x.CDPN.Contains(dto.CDPN))
  480. .WhereIF(dto.StartTime.HasValue, x => x.CreatedTime >= dto.StartTime.Value)
  481. .WhereIF(dto.EndTime.HasValue, x => x.CreatedTime <= dto.EndTime.Value)
  482. .Select(x => new BiSeatSwitchDto
  483. {
  484. UserId = x.UserId,
  485. CPN = x.CPN,
  486. CDPN = x.CDPN,
  487. CreatedTime = x.CreatedTime,
  488. TelNo = x.AgentTransferNumber,
  489. UserName = x.UserName,
  490. })
  491. .OrderByDescending(x => x.CreatedTime)
  492. .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
  493. return new PagedDto<BiSeatSwitchDto>(total, _mapper.Map<IReadOnlyList<BiSeatSwitchDto>>(items));
  494. }
  495. /// <summary>
  496. /// 小时统计
  497. /// </summary>
  498. /// <returns></returns>
  499. [HttpGet("hourcall")]
  500. [AllowAnonymous]
  501. public async Task<List<TrCallHourDto>> QueryHourCall([FromQuery] DateTime StartTime, DateTime? EndTime, string source)
  502. {
  503. //获取配置
  504. int noConnectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.NoConnectByeTimes)?.SettingValue[0]);
  505. int effectiveTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.EffectiveTimes)?.SettingValue[0]);
  506. int connectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.ConnectByeTimes)?.SettingValue[0]);
  507. var list = await _trCallRecordRepositoryEx.GetCallHourList(StartTime, EndTime, noConnectByeTimes, effectiveTimes, connectByeTimes, source);
  508. return list;
  509. }
  510. /// <summary>
  511. /// 小时统计--导出
  512. /// </summary>
  513. /// <returns></returns>
  514. [HttpPost("hourcall/export")]
  515. [AllowAnonymous]
  516. public async Task<FileStreamResult> ExportQueryHourCall([FromBody] ExportExcelDto<BiQueryHourCallDto> dto)
  517. {
  518. //获取配置
  519. int noConnectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.NoConnectByeTimes)?.SettingValue[0]);
  520. int effectiveTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.EffectiveTimes)?.SettingValue[0]);
  521. int connectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.ConnectByeTimes)?.SettingValue[0]);
  522. var list = await _trCallRecordRepositoryEx.GetCallHourList(dto.QueryDto.StartTime, dto.QueryDto.EndTime, noConnectByeTimes, effectiveTimes, connectByeTimes, dto.QueryDto.Source);
  523. if (list != null && list.Count > 0)
  524. {
  525. list.Add(new TrCallHourDto()
  526. {
  527. HourTo = "合计",
  528. EffectiveCount = list.Sum(p => p.EffectiveCount),
  529. ConnectByeCount = list.Sum(p => p.ConnectByeCount),
  530. NoConnectByeCount = list.Sum(p => p.NoConnectByeCount),
  531. QueueByeCount = list.Sum(m => m.QueueByeCount),
  532. IvrByeCount = list.Sum(m => m.IvrByeCount)
  533. });
  534. }
  535. dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
  536. var dtos = list
  537. .Select(stu => _mapper.Map(stu, typeof(TrCallHourDto), dynamicClass))
  538. .Cast<object>()
  539. .ToList();
  540. var stream = ExcelHelper.CreateStream(dtos);
  541. return ExcelStreamResult(stream, "通话时段分析");
  542. }
  543. /// <summary>
  544. /// 通话时段统计明细
  545. /// </summary>
  546. /// <returns></returns>
  547. [HttpGet("hourcall_list")]
  548. public async Task<object> QueryCallList([FromQuery] DateTime StartTime, DateTime? EndTime, string type, string source, TimeSpan? startHourTo, int pageIndex, int pageSize)
  549. {
  550. //获取配置
  551. int noConnectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.NoConnectByeTimes)?.SettingValue[0]);
  552. int effectiveTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.EffectiveTimes)?.SettingValue[0]);
  553. int connectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.ConnectByeTimes)?.SettingValue[0]);
  554. var list = await _trCallRecordRepositoryEx.GetCallList(StartTime, EndTime, noConnectByeTimes, effectiveTimes, connectByeTimes, type, source, startHourTo, pageIndex, pageSize);
  555. return list;
  556. }
  557. /// <summary>
  558. /// 通话时段统计明细获取基本信息
  559. /// </summary>
  560. /// <param name="id"></param>
  561. /// <returns></returns>
  562. [HttpGet("hourcall_list_base")]
  563. public async Task<object> ReTransactBaseData()
  564. {
  565. var rsp = new
  566. {
  567. CallForwardingSource = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.CallForwardingSource),
  568. CallForwardingType = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.CallForwardingType),
  569. };
  570. return rsp;
  571. }
  572. /// <summary>
  573. /// 热线号码统计
  574. /// </summary>
  575. /// <param name="StartTime"></param>
  576. /// <param name="EndTime"></param>
  577. /// <returns></returns>
  578. [AllowAnonymous]
  579. [HttpGet("gateway-query")]
  580. public async Task<List<CallHotLineDto>> QueryGateWay(DateTime StartTime, DateTime EndTime, string gateway)
  581. {
  582. //获取配置
  583. int noConnectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.NoConnectByeTimes)?.SettingValue[0]);
  584. int effectiveTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.EffectiveTimes)?.SettingValue[0]);
  585. int connectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.ConnectByeTimes)?.SettingValue[0]);
  586. int ringTims = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.RingTims)?.SettingValue[0]);
  587. var list = await _trCallRecordRepositoryEx.GetCallHotLineList(StartTime, EndTime, gateway, noConnectByeTimes, effectiveTimes, connectByeTimes, ringTims);
  588. return list;
  589. }
  590. }