CallController.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. using Hotline.Application.CallCenter;
  2. using Hotline.Share.Tools;
  3. using Hotline.Caching.Interfaces;
  4. using Hotline.CallCenter.BlackLists;
  5. using Hotline.CallCenter.Calls;
  6. using Hotline.CallCenter.Configs;
  7. using Hotline.CallCenter.Tels;
  8. using Hotline.EventBus;
  9. using Hotline.Quality.Notifications;
  10. using Hotline.Settings;
  11. using Hotline.Share.Dtos;
  12. using Hotline.Share.Dtos.CallCenter;
  13. using Hotline.Share.Dtos.Quality;
  14. using Hotline.Share.Dtos.TrCallCenter;
  15. using Hotline.Share.Enums.CallCenter;
  16. using Hotline.Repository.SqlSugar.Extensions;
  17. using Microsoft.AspNetCore.Mvc;
  18. using Microsoft.Extensions.Options;
  19. using System.Threading;
  20. using XF.Domain.Exceptions;
  21. using XF.Utility.EnumExtensions;
  22. using XF.Domain.Repository;
  23. using MapsterMapper;
  24. using SqlSugar;
  25. using Hotline.Api.Filter;
  26. using Microsoft.AspNetCore.Authorization;
  27. using Hotline.Share.Dtos.Order;
  28. using Hotline.Tools;
  29. using XF.Domain.Authentications;
  30. namespace Hotline.Api.Controllers
  31. {
  32. public class CallController : BaseController
  33. {
  34. private readonly ICallApplication _callApplication;
  35. private readonly Publisher _publisher;
  36. private readonly IMapper _mapper;
  37. private readonly IOptionsSnapshot<CallCenterConfiguration> _callcenterOptions;
  38. private readonly ISystemSettingCacheManager _systemSettingCacheManager;
  39. private readonly IRepository<TelOperationXthx> _telOperationXthxRepository;
  40. public CallController(
  41. ICallApplication callApplication,
  42. Publisher publisher,
  43. IMapper mapper,
  44. IOptionsSnapshot<CallCenterConfiguration> callcenterOptions,
  45. ISystemSettingCacheManager systemSettingCacheManager,
  46. IRepository<TelOperationXthx> telOperationXthxRepository)
  47. {
  48. _callApplication = callApplication;
  49. _publisher = publisher;
  50. _mapper = mapper;
  51. _callcenterOptions = callcenterOptions;
  52. _systemSettingCacheManager = systemSettingCacheManager;
  53. _telOperationXthxRepository = telOperationXthxRepository;
  54. }
  55. /// <summary>
  56. /// 查询分机组和分机
  57. /// </summary>
  58. [HttpGet("querygrouptel")]
  59. public Task<IReadOnlyList<TelGroupDto>> QueryGroupTel()
  60. => _callApplication.QueryGroupTel(HttpContext.RequestAborted);
  61. /// <summary>
  62. /// 查询分机
  63. /// </summary>
  64. [HttpGet("tels")]
  65. public async Task<IList<TelDto>> QueryTels()
  66. => (await _callApplication.QueryTelsAsync(new QueryTelsInDto(null) { PageSize = 99999 }, HttpContext.RequestAborted)).Item2;
  67. /// <summary>
  68. /// 查询分机
  69. /// </summary>
  70. [HttpGet("tels-paged")]
  71. public async Task<PagedDto<TelDto>> QueryTelsAsync([FromQuery] QueryTelsInDto dto)
  72. => (await _callApplication.QueryTelsAsync(dto, HttpContext.RequestAborted)).ToPaged();
  73. /// <summary>
  74. /// 查询分机组
  75. /// </summary>
  76. [HttpGet("groups")]
  77. public Task<IReadOnlyList<TelGroupDto>> QueryTelGroups()
  78. => _callApplication.QueryTelGroupsAsync(HttpContext.RequestAborted);
  79. #region 黑名单
  80. //Task<string> AddBlackListAsync(AddBlacklistDto dto, CancellationToken cancellationToken);
  81. //Task RemoveBlackListAsync(string id, CancellationToken cancellationToken);
  82. //Task<List<Blacklist>> QueryBlackListsAsync(CancellationToken cancellationToken);
  83. #endregion
  84. /// <summary>
  85. /// 签入
  86. /// </summary>
  87. [HttpPost("signin")]
  88. [LogFilterAlpha("分机签入")]
  89. public Task<TrOnDutyResponseDto> SignIn([FromBody] SignInDto dto)
  90. => _callApplication.SignInAsync(dto, HttpContext.RequestAborted);
  91. /// <summary>
  92. /// 签出
  93. /// </summary>
  94. [HttpPost("signout")]
  95. [LogFilterAlpha("分机签出")]
  96. public Task SignOut()
  97. => _callApplication.SingOutAsync(HttpContext.RequestAborted);
  98. /// <summary>
  99. /// 签出
  100. /// </summary>
  101. [HttpPost("signout/{telNo}")]
  102. [LogFilterAlpha("分机签出")]
  103. public Task SignOut(string telNo)
  104. => _callApplication.SingOutAsync(telNo, HttpContext.RequestAborted);
  105. /// <summary>
  106. /// 查询当前用户的分机状态
  107. /// </summary>
  108. /// <returns></returns>
  109. [HttpGet("tel-state")]
  110. public Task<TrOnDutyResponseDto> GetTelState()
  111. => _callApplication.GetTelStateAsync(HttpContext.RequestAborted);
  112. /// <summary>
  113. /// 查询通话记录(固定数据量)
  114. /// </summary>
  115. /// <param name="dto"></param>
  116. /// <returns></returns>
  117. [HttpGet("calls-fixed")]
  118. public async Task<List<CallNativeDto>> QueryCallsFixed([FromQuery] QueryCallsFixedDto dto)
  119. => await _callApplication.QueryCallsFixedAsync(dto, HttpContext.RequestAborted).ToPageListWithoutTotalAsync(dto, HttpContext.RequestAborted);
  120. /// <summary>
  121. /// 查询通话记录(固定数据量)
  122. /// </summary>
  123. /// <param name="dto"></param>
  124. /// <returns></returns>
  125. [HttpGet("calls-fixed/count")]
  126. public async Task<int> QueryCallsFixedCount([FromQuery] QueryCallsFixedDto dto)
  127. => await _callApplication.QueryCallsFixedAsync(dto, HttpContext.RequestAborted).CountAsync(HttpContext.RequestAborted);
  128. /// <summary>
  129. /// 导出通话记录
  130. /// </summary>
  131. /// <param name="dto"></param>
  132. /// <returns></returns>
  133. [HttpPost("calls-fixed/export")]
  134. [LogFilterAlpha("导出日志")]
  135. public async Task<FileStreamResult> ExportOrders([FromBody] ExportExcelDto<QueryCallsFixedDto> dto)
  136. {
  137. var query = _callApplication.QueryCallsFixedAsync(dto.QueryDto, HttpContext.RequestAborted);
  138. List<CallNativeDto> callls;
  139. if (dto.IsExportAll)
  140. {
  141. callls = await query.ToListAsync(HttpContext.RequestAborted);
  142. }
  143. else
  144. {
  145. var (_, items) = await query.ToPagedListAsync(dto.QueryDto, HttpContext.RequestAborted);
  146. callls = items;
  147. }
  148. dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass<CallNativeDto>(dto.ColumnInfos);
  149. var dtos = callls
  150. .Select(stu => _mapper.Map(stu, typeof(CallNativeDto), dynamicClass))
  151. .Cast<object>()
  152. .ToList();
  153. var stream = ExcelHelper.CreateStream(dtos);
  154. return ExcelStreamResult(stream, "通话记录");
  155. }
  156. /// <summary>
  157. /// 查询通话记录
  158. /// </summary>
  159. /// <param name="callId"></param>
  160. /// <returns></returns>
  161. [HttpGet("{callId}")]
  162. public Task<List<CallNative>> GetCall(string callId)
  163. {
  164. //为兼容天润通话记录返回集合
  165. if (string.IsNullOrEmpty(callId)) return default;
  166. return _callApplication.GetCallListAsync(callId, HttpContext.RequestAborted);
  167. }
  168. /// <summary>
  169. /// 通话记录基础数据
  170. /// </summary>
  171. /// <returns></returns>
  172. [HttpGet("base-data")]
  173. public object BaseData()
  174. {
  175. return new
  176. {
  177. Direction = EnumExts.GetDescriptions<ECallDirection>(),
  178. EndBy = EnumExts.GetDescriptions<EEndBy>(),
  179. };
  180. }
  181. /// <summary>
  182. /// 查询坐席操作记录(固定数据量)
  183. /// </summary>
  184. /// <param name="dto"></param>
  185. /// <returns></returns>
  186. [HttpGet("tel-operations-fixed")]
  187. public async Task<IReadOnlyList<TelOperation>> QueryTelOperationsAsync([FromQuery] QueryTelOperationsFixedDto dto) =>
  188. await _callApplication.QueryTelOperationsAsync(dto).ToPageListWithoutTotalAsync(dto, HttpContext.RequestAborted);
  189. /// <summary>
  190. /// 查询坐席操作记录(固定数据量)
  191. /// </summary>
  192. /// <param name="dto"></param>
  193. /// <returns></returns>
  194. [HttpGet("tel-operations-fixed/count")]
  195. public async Task<int> QueryTelOperationsCountAsync([FromQuery] QueryTelOperationsFixedDto dto) =>
  196. await _callApplication.QueryTelOperationsAsync(dto).CountAsync(HttpContext.RequestAborted);
  197. /// <summary>
  198. /// 查询坐席操作记录基础数据
  199. /// </summary>
  200. [HttpGet("base-data-tel-operation")]
  201. public object BaseDataTelOperation()
  202. {
  203. return new
  204. {
  205. Operations = _callApplication.GetTelOperationOptions()
  206. };
  207. }
  208. /// <summary>
  209. /// 通话转写
  210. /// </summary>
  211. /// <param name="dto"></param>
  212. /// <returns></returns>
  213. [HttpPost("calls/transliteration")]
  214. public async Task CallTransliteration([FromBody] CallTransliteration dto)
  215. {
  216. foreach (var id in dto.Ids)
  217. {
  218. var call = await _callApplication.GetTianrunCallAsync(id, HttpContext.RequestAborted);
  219. if (call is null)
  220. throw UserFriendlyException.SameMessage("通话信息错误");
  221. if (call.TransliterationState == ECallTransliterationState.Underway)
  222. throw UserFriendlyException.SameMessage("正在转写中,请勿重复点击,请稍作等待");
  223. if (call.TransliterationState == ECallTransliterationState.Succeed)
  224. throw UserFriendlyException.SameMessage("转写成功,不能重新转写");
  225. call.InitTransliterationId();
  226. await _callApplication.EditTransliterationStateAsync(call.Id, ECallTransliterationState.Underway, call.TransliterationId, HttpContext.RequestAborted);
  227. var audioFile = call.RecordingAbsolutePath;
  228. var fromNo = call.CPN;
  229. var callStartTime = call.CreatedTime;
  230. var Id = call.TransliterationId;
  231. var setting = _systemSettingCacheManager.GetSetting(SettingConstants.ViteRecordPrefix);
  232. var handler = new AiQualityHandler()
  233. {
  234. Id = Id,
  235. Source = "AiAnswered",
  236. AudioFile = audioFile,
  237. FromNo = fromNo,
  238. CallStartTime = callStartTime,
  239. ViteRecordPrefix = setting?.SettingValue[0],
  240. };
  241. await _publisher.PublishAsync(new AiOrderQualityNotify(handler), PublishStrategy.ParallelNoWait, HttpContext.RequestAborted);
  242. }
  243. }
  244. #region 坐席动作类型统计
  245. /// <summary>
  246. /// 坐席动作类型统计
  247. /// </summary>
  248. /// <param name="dto"></param>
  249. /// <returns></returns>
  250. [HttpGet("telaction/list")]
  251. public async Task<PagedDto<TelActionListXthx>> TelActionList([FromQuery] TelActionXthxDto dto)
  252. {
  253. var (total, items) = await _telOperationXthxRepository.Queryable()
  254. .WhereIF(string.IsNullOrEmpty(dto.TelNo) == false, x => x.TelNo.Contains(dto.TelNo))
  255. .WhereIF(dto.OperationStatus != null, x => x.OperationStatus == dto.OperationStatus)
  256. .WhereIF(string.IsNullOrEmpty(dto.UserName) == false, x => x.UserName.Contains(dto.UserName))
  257. .WhereIF(dto.StartTime.HasValue, x => x.StartTime >= dto.StartTime)
  258. .WhereIF(dto.EndTime.HasValue, x => x.StartTime <= dto.EndTime)
  259. .OrderByDescending(x => x.StartTime)
  260. .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
  261. return new PagedDto<TelActionListXthx>(total, _mapper.Map<IReadOnlyList<TelActionListXthx>>(items));
  262. }
  263. #endregion
  264. }
  265. }