IPPbxController.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. using Hotline.Application.CallCenter.Calls;
  2. using Hotline.Caching.Interfaces;
  3. using Hotline.Caching.Services;
  4. using Hotline.CallCenter.Calls;
  5. using Hotline.Orders;
  6. using Hotline.Permissions;
  7. using Hotline.Repository.SqlSugar.Extensions;
  8. using Hotline.Settings;
  9. using Hotline.Share.Dtos;
  10. using Hotline.Share.Dtos.Order;
  11. using Hotline.Share.Dtos.TrCallCenter;
  12. using Hotline.Share.Enums.CallCenter;
  13. using Hotline.Share.Enums.KnowledgeBase;
  14. using Hotline.Users;
  15. using MapsterMapper;
  16. using Microsoft.AspNetCore.Authorization;
  17. using Microsoft.AspNetCore.Mvc;
  18. using System.Text.Json;
  19. using Tr.Sdk;
  20. using Tr.Sdk.Blacklist;
  21. using Tr.Sdk.Tels;
  22. using XF.Domain.Authentications;
  23. using XF.Domain.Filters;
  24. using XF.Domain.Repository;
  25. using XF.Utility.EnumExtensions;
  26. namespace Hotline.Api.Controllers
  27. {
  28. public class IPPbxController:BaseController
  29. {
  30. private readonly ITrClient _trClient;
  31. private readonly IMapper _mapper;
  32. private readonly IUserDomainService _userDomainService;
  33. private readonly ISessionContext _sessionContext;
  34. private readonly IRepository<TrCallRecord> _trCallRecordRepository;
  35. private readonly ITrApplication _trApplication;
  36. private readonly IRepository<TrCallEvaluate> _trCallEvaluate;
  37. private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
  38. private readonly ILogger<IPPbxController> _logger;
  39. private readonly IOrderRepository _orderRepository;
  40. private readonly IRepository<OrderVisit> _orderVisitRepository;
  41. private readonly IUserCacheManager _userCacheManager;
  42. public IPPbxController(ITrClient trClient,IMapper mapper,IUserDomainService userDomainService,ISessionContext sessionContext,IRepository<TrCallRecord> trCallRecordRepository,ITrApplication trApplication,IRepository<TrCallEvaluate> trCallRecord,ISystemDicDataCacheManager systemDicDataCacheManager,ILogger<IPPbxController> logger,IOrderRepository orderRepository,IRepository<OrderVisit> orderVisitRepository, IUserCacheManager userCacheManager)
  43. {
  44. _trClient = trClient;
  45. _mapper = mapper;
  46. _userDomainService = userDomainService;
  47. _sessionContext = sessionContext;
  48. _trCallRecordRepository = trCallRecordRepository;
  49. _trApplication = trApplication;
  50. _trCallEvaluate = trCallRecord;
  51. _systemDicDataCacheManager = systemDicDataCacheManager;
  52. _logger = logger;
  53. _orderRepository = orderRepository;
  54. _orderVisitRepository = orderVisitRepository;
  55. _userCacheManager = userCacheManager;
  56. }
  57. #region 添添呼
  58. #region 分机
  59. /// <summary>
  60. /// 查询所有分机
  61. /// </summary>
  62. /// <returns></returns>
  63. [HttpGet("query-tels")]
  64. public async Task<IReadOnlyList<TrTelDto>> TrQueryTels()
  65. {
  66. var tels = await _trClient.QueryTelsAsync(new QueryTelRequest() { }, HttpContext.RequestAborted);
  67. return _mapper.Map<IReadOnlyList<TrTelDto>>(tels);
  68. }
  69. #endregion
  70. #region 黑白名单
  71. /// <summary>
  72. /// 新增黑白名单
  73. /// </summary>
  74. /// <param name="dto"></param>
  75. /// <returns></returns>
  76. [Permission(EPermission.AddBlackList)]
  77. [HttpPost("add-blacklist")]
  78. public async Task AddBlacklist([FromBody]TrAddBlacklistDto dto)
  79. {
  80. await _trClient.AddBlacklistAsync(new AddBlacklistRequest() { Phone = dto.Phone, SpecialFlag = dto.SpecialFlag }, HttpContext.RequestAborted);
  81. }
  82. /// <summary>
  83. /// 删除黑白名单
  84. /// </summary>
  85. /// <param name="dto"></param>
  86. /// <returns></returns>
  87. [Permission(EPermission.RemoveBlacklist)]
  88. [HttpPost("remove-blacklist")]
  89. public async Task DelBlacklist([FromBody]TrDelBlacklistDto dto)
  90. {
  91. await _trClient.DelBlacklistAsync(new DelBlacklistRequest() { Phone = dto.Phone, SpecialFlag = dto.SpecialFlag }, HttpContext.RequestAborted);
  92. }
  93. /// <summary>
  94. /// 查询黑白名单
  95. /// </summary>
  96. /// <returns></returns>
  97. [Permission(EPermission.QueryPagedBlack)]
  98. [HttpGet("query-blacklist")]
  99. public async Task<IReadOnlyList<TrQueryBlacklistResponseDto>> QueryBlacklist([FromQuery]TrQueryBlacklistDto dto)
  100. {
  101. var blacklist = await _trClient.QueryBlacklistAsync(new QueryBlacklistRequest() { Phone = dto.Phone, SpecialFlag = dto.SpecialFlag }, HttpContext.RequestAborted);
  102. return _mapper.Map<IReadOnlyList<TrQueryBlacklistResponseDto>>(blacklist);
  103. }
  104. #endregion
  105. #region 签入后调用
  106. /// <summary>
  107. /// 上班
  108. /// </summary>
  109. /// <param name="dto"></param>
  110. /// <returns></returns>
  111. [Permission(EPermission.OnDuty)]
  112. [HttpPost("on-duty")]
  113. public async Task<TrOnDutyResponseDto> OnDuty([FromBody] TrOnDutyDto dto)
  114. {
  115. //return await _userDomainService.TrOnDutyAsync(_sessionContext.RequiredUserId,dto.TelNo, HttpContext.RequestAborted);
  116. return await _trApplication.OnSign(_sessionContext.RequiredUserId, dto.TelNo, HttpContext.RequestAborted);
  117. }
  118. /// <summary>
  119. /// 下班
  120. /// </summary>
  121. /// <returns></returns>
  122. [Permission(EPermission.OffDuty)]
  123. [HttpPost("off-duty")]
  124. public async Task OffDuty()
  125. {
  126. await _userDomainService.TrOffDutyAsync(_sessionContext.RequiredUserId, HttpContext.RequestAborted);
  127. }
  128. #endregion
  129. #region 添添呼小休
  130. /// <summary>
  131. /// 小休页面基础数据
  132. /// </summary>
  133. /// <returns></returns>
  134. [HttpGet("rest/basedata")]
  135. public async Task<object> RestBasePaegData()
  136. {
  137. var rsp = new
  138. {
  139. RestReason = _systemDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.RestReason)
  140. };
  141. return rsp;
  142. }
  143. #endregion
  144. #region 通话记录(对外)
  145. /// <summary>
  146. /// 接收通话记录
  147. /// </summary>
  148. /// <param name="dto"></param>
  149. /// <returns></returns>
  150. [AllowAnonymous]
  151. [HttpPost("receivecallrecord")]
  152. public async Task<OpenResponse> ReceiveCallRecord([FromBody]ReceiveCallRecordDto dto)
  153. {
  154. var model = _mapper.Map<TrCallRecord>(dto);
  155. model.Duration = 0;
  156. model.RingTimes = 0;
  157. model.QueueTims = 0;
  158. model.OnState = Share.Enums.CallCenter.EOnState.NoOn;
  159. //计算通话时长
  160. if (model.AnsweredTime != null)
  161. {
  162. model.OnState = Share.Enums.CallCenter.EOnState.On; //是否接通
  163. TimeSpan tsend = new TimeSpan(model.OverTime.Ticks);
  164. TimeSpan tsbegin = new TimeSpan(model.AnsweredTime.Value.Ticks);
  165. model.Duration = Convert.ToInt32(tsend.Subtract(tsbegin).TotalSeconds);
  166. }
  167. //计算振铃时长
  168. if (model.BeginRingTime !=null)
  169. {
  170. TimeSpan tsbegin = new TimeSpan(model.BeginRingTime.Value.Ticks);
  171. if (model.EndRingTimg!=null)
  172. {
  173. TimeSpan tsend = new TimeSpan(model.EndRingTimg.Value.Ticks);
  174. model.RingTimes = Convert.ToInt32(tsend.Subtract(tsbegin).TotalSeconds);
  175. }
  176. else
  177. {
  178. TimeSpan tsend = new TimeSpan(model.OverTime.Ticks);
  179. model.RingTimes = Convert.ToInt32(tsend.Subtract(tsbegin).TotalSeconds);
  180. }
  181. }
  182. //计算队列时长
  183. if (model.BeginQueueTime!=null && model.EndQueueTime!=null)
  184. {
  185. TimeSpan tsend = new TimeSpan(model.EndQueueTime.Value.Ticks);
  186. TimeSpan tsbegin = new TimeSpan(model.BeginQueueTime.Value.Ticks);
  187. model.QueueTims = Convert.ToInt32(tsend.Subtract(tsbegin).TotalSeconds);
  188. }
  189. //获取分机号
  190. if (model.CallDirection == ECallDirection.In)
  191. {
  192. model.TelNo = model.CDPN;
  193. }
  194. else
  195. {
  196. model.TelNo = model.CPN;
  197. }
  198. //获取用户
  199. var work = await _userCacheManager.GetWorkByTelNoLast(model.TelNo);
  200. if (work!=null)
  201. {
  202. model.UserId = work.UserId;
  203. model.UserName = work.UserName;
  204. model.StaffNo = work.StaffNo;
  205. }
  206. await _trCallRecordRepository.AddAsync(model, HttpContext.RequestAborted);
  207. return OpenResponse.Ok("success");
  208. }
  209. /// <summary>
  210. /// 接受通话语音评价
  211. /// </summary>
  212. /// <param name="dto"></param>
  213. /// <returns></returns>
  214. [AllowAnonymous]
  215. [HttpPost("receivecallevaluate")]
  216. public async Task<OpenResponse> ReceiveCallEvaluate([FromBody]ReceiveCallEvaluateDto dto)
  217. {
  218. var model = _mapper.Map<TrCallEvaluate>(dto);
  219. await _trCallEvaluate.AddAsync(model, HttpContext.RequestAborted);
  220. return OpenResponse.Ok("success");
  221. }
  222. #endregion
  223. #region 通话记录(对内)
  224. /// <summary>
  225. /// 获取通话记录列表
  226. /// </summary>
  227. /// <param name="dto"></param>
  228. /// <returns></returns>
  229. [HttpGet("calls/call-list")]
  230. public async Task<PagedDto<TrCallDto>> GetCallList([FromQuery]GetCallListDto dto)
  231. {
  232. var (total, items) = await _trCallRecordRepository.Queryable()
  233. .WhereIF(!string.IsNullOrEmpty(dto.CPN), x => x.CPN.Contains(dto.CPN))
  234. .WhereIF(!string.IsNullOrEmpty(dto.CDPN), x => x.CDPN.Contains(dto.CDPN))
  235. .WhereIF(dto.CallDirection != null, x => x.CallDirection == dto.CallDirection)
  236. .WhereIF(dto.OnState != null, x => x.OnState == dto.OnState)
  237. .OrderByDescending(x => x.CreatedTime)
  238. .ToPagedListAsync(dto.PageIndex, dto.PageSize);
  239. return new PagedDto<TrCallDto>(total, _mapper.Map<IReadOnlyList<TrCallDto>>(items));
  240. }
  241. /// <summary>
  242. /// 通话记录基础数据
  243. /// </summary>
  244. /// <returns></returns>
  245. [HttpGet("calls/basedata")]
  246. public async Task<object> CallBaseData()
  247. {
  248. return new
  249. {
  250. OnState = EnumExts.GetDescriptions<EOnState>(),
  251. CallDirection = EnumExts.GetDescriptions<ECallDirection>(),
  252. };
  253. }
  254. #endregion
  255. #region 关联
  256. /// <summary>
  257. /// 可关联工单
  258. /// </summary>
  259. /// <param name="dto"></param>
  260. /// <returns></returns>
  261. [HttpGet("canlink-order")]
  262. public async Task<PagedDto<OrderDto>> CanLinkCallRecordOrder([FromQuery]CanLinkCallRecordOrderDto dto)
  263. {
  264. var (total,items) = await _orderRepository.Queryable()
  265. .Where(x=> string.IsNullOrEmpty(x.CallId) && x.Source == Share.Enums.Order.ESource.Hotline)
  266. .WhereIF(!string.IsNullOrEmpty(dto.Keyword),x=>x.No.Contains(dto.Keyword))
  267. .OrderByDescending(x=>x.CreationTime)
  268. .ToPagedListAsync(dto.PageIndex,dto.PageSize,HttpContext.RequestAborted);
  269. return new PagedDto<OrderDto>(total, _mapper.Map<IReadOnlyList<OrderDto>>(items));
  270. }
  271. /// <summary>
  272. /// 可关联回访
  273. /// </summary>
  274. /// <param name="dto"></param>
  275. /// <returns></returns>
  276. [HttpGet("canlink-ordervisit")]
  277. public async Task<PagedDto<OrderVisitDto>> CanLinkCallRecordOrderVisit([FromQuery]CanLinkCallRecordOrderVisitDto dto)
  278. {
  279. var (total, items) = await _orderVisitRepository.Queryable()
  280. .Includes(x=>x.Order)
  281. .Where(x => string.IsNullOrEmpty(x.CallId) && x.VisitType == Share.Enums.Order.EVisitType.ArtificialVisit)
  282. .WhereIF(!string.IsNullOrEmpty(dto.Keyword), x => x.No.Contains(dto.Keyword))
  283. .OrderByDescending(x => x.CreationTime)
  284. .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
  285. return new PagedDto<OrderVisitDto>(total, _mapper.Map<IReadOnlyList<OrderVisitDto>>(items));
  286. }
  287. /// <summary>
  288. /// 关联工单或者回访
  289. /// </summary>
  290. /// <param name="dto"></param>
  291. /// <returns></returns>
  292. [HttpPost("link-callrecord")]
  293. public async Task LinkCallRecord([FromBody]LinkCallRecordDto dto)
  294. {
  295. if (dto.IsOrder)
  296. {
  297. //工单
  298. var order = await _orderRepository.GetAsync(x => x.Id == dto.Id, HttpContext.RequestAborted);
  299. order.CallId = dto.CallId;
  300. await _orderRepository.UpdateAsync(order);
  301. }
  302. else
  303. {
  304. //回访
  305. var visit = await _orderVisitRepository.GetAsync(x => x.Id == dto.Id, HttpContext.RequestAborted);
  306. visit.CallId = dto.CallId;
  307. await _orderVisitRepository.UpdateAsync(visit);
  308. }
  309. }
  310. #endregion
  311. #endregion
  312. }
  313. }