IPPbxController.cs 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067
  1. using DocumentFormat.OpenXml.Office2010.Excel;
  2. using DocumentFormat.OpenXml.Spreadsheet;
  3. using DotNetCore.CAP;
  4. using Hotline.Api.Filter;
  5. using Hotline.Application.CallCenter;
  6. using Hotline.Application.Systems;
  7. using Hotline.Application.Tels;
  8. using Hotline.Caching.Interfaces;
  9. using Hotline.CallCenter.Calls;
  10. using Hotline.CallCenter.Tels;
  11. using Hotline.CallCenter.Tels.CallTelDomain;
  12. using Hotline.Configurations;
  13. using Hotline.EventBus;
  14. using Hotline.Orders;
  15. using Hotline.Quality;
  16. using Hotline.Quality.Notifications;
  17. using Hotline.Repository.SqlSugar.Extensions;
  18. using Hotline.Settings;
  19. using Hotline.Share.Dtos;
  20. using Hotline.Share.Dtos.CallCenter;
  21. using Hotline.Share.Dtos.Order;
  22. using Hotline.Share.Dtos.Quality;
  23. using Hotline.Share.Dtos.TrCallCenter;
  24. using Hotline.Share.Enums.CallCenter;
  25. using Hotline.Share.Enums.Quality;
  26. using Hotline.Share.Requests;
  27. using Hotline.Share.Tools;
  28. using Hotline.Tools;
  29. using Hotline.Users;
  30. using Mapster;
  31. using MapsterMapper;
  32. using Microsoft.AspNetCore.Authorization;
  33. using Microsoft.AspNetCore.Mvc;
  34. using Microsoft.Extensions.Options;
  35. using Newtonsoft.Json;
  36. using SqlSugar;
  37. using XF.Domain.Authentications;
  38. using XF.Domain.Cache;
  39. using XF.Domain.Exceptions;
  40. using XF.Domain.Filters;
  41. using XF.Domain.Repository;
  42. using XF.Utility.EnumExtensions;
  43. namespace Hotline.Api.Controllers
  44. {
  45. public class IPPbxController : BaseController
  46. {
  47. private readonly IMapper _mapper;
  48. private readonly IUserDomainService _userDomainService;
  49. private readonly ISessionContext _sessionContext;
  50. private readonly IRepository<TrCallRecord> _trCallRecordRepository;
  51. private readonly ICallApplication _callApplication;
  52. private readonly IRepository<TrCallEvaluate> _trCallEvaluate;
  53. private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
  54. private readonly ILogger<IPPbxController> _logger;
  55. private readonly IOrderRepository _orderRepository;
  56. private readonly IRepository<OrderVisit> _orderVisitRepository;
  57. private readonly IUserCacheManager _userCacheManager;
  58. private readonly ICapPublisher _capPublisher;
  59. private readonly ITelRestRepository _telRestRepository;
  60. private readonly IRepository<User> _userRepository;
  61. private readonly ITelApplication _telApplication;
  62. private readonly IRepository<Quality.Quality> _qualiteyRepository;
  63. private readonly IRepository<QualityTemplate> _qualityTemplate;
  64. private readonly ISystemSettingCacheManager _systemSettingCacheManager;
  65. private readonly IRepository<TelActionRecord> _telActionRecordRepository;
  66. private readonly ITypedCache<Work> _cacheWork;
  67. private readonly ISystemMobilAreaApplication _systemMobilAreaApplication;
  68. private readonly IRepository<Work> _workRepository;
  69. private readonly Publisher _publisher;
  70. private readonly ITrCallRecordRepository _callRecordRepository;
  71. private readonly IIPPbxApplication _iPPbxApplication;
  72. private readonly ICallTelClient _callTelClient;
  73. private readonly IRepository<TelOperation> _telOperationRepository;
  74. private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
  75. private readonly IRepository<VoiceEvaluationButton> _voiceEvaluationButtonRepository;
  76. private readonly IOrderVisitDomainService _orderVisitDomainService;
  77. public IPPbxController(IMapper mapper, IUserDomainService userDomainService,
  78. ISessionContext sessionContext, IRepository<TrCallRecord> trCallRecordRepository,
  79. ICallApplication callApplication, IRepository<TrCallEvaluate> trCallRecord,
  80. ISystemDicDataCacheManager systemDicDataCacheManager, ILogger<IPPbxController> logger,
  81. IOrderRepository orderRepository, IRepository<OrderVisit> orderVisitRepository,
  82. IUserCacheManager userCacheManager, ICapPublisher capPublisher,
  83. ITelRestRepository telRestRepository, IRepository<User> userRepository,
  84. ITelApplication telApplication, IRepository<Quality.Quality> qualiteyRepository,
  85. IRepository<QualityTemplate> qualityTemplate,
  86. ISystemSettingCacheManager systemSettingCacheManager, IRepository<TelActionRecord> telActionRecordRepository,
  87. ISystemMobilAreaApplication systemMobilAreaApplication,
  88. IRepository<Work> workRepository,
  89. Publisher publisher,
  90. ITrCallRecordRepository callRecordRepository,
  91. ITypedCache<Work> cacheWork,
  92. IIPPbxApplication iPPbxApplication,
  93. ICallTelClient callTelClient,
  94. IRepository<TelOperation> telOperationRepository,
  95. IOptionsSnapshot<AppConfiguration> appOptions,
  96. IRepository<VoiceEvaluationButton> voiceEvaluationButtonRepository,
  97. IOrderVisitDomainService orderVisitDomainService)
  98. {
  99. _mapper = mapper;
  100. _userDomainService = userDomainService;
  101. _sessionContext = sessionContext;
  102. _trCallRecordRepository = trCallRecordRepository;
  103. _callApplication = callApplication;
  104. _trCallEvaluate = trCallRecord;
  105. _systemDicDataCacheManager = systemDicDataCacheManager;
  106. _logger = logger;
  107. _orderRepository = orderRepository;
  108. _orderVisitRepository = orderVisitRepository;
  109. _userCacheManager = userCacheManager;
  110. _capPublisher = capPublisher;
  111. _telRestRepository = telRestRepository;
  112. _userRepository = userRepository;
  113. _telApplication = telApplication;
  114. _qualiteyRepository = qualiteyRepository;
  115. _qualityTemplate = qualityTemplate;
  116. _systemSettingCacheManager = systemSettingCacheManager;
  117. _telActionRecordRepository = telActionRecordRepository;
  118. _systemMobilAreaApplication = systemMobilAreaApplication;
  119. _workRepository = workRepository;
  120. _publisher = publisher;
  121. _callRecordRepository = callRecordRepository;
  122. _cacheWork = cacheWork;
  123. _iPPbxApplication = iPPbxApplication;
  124. _callTelClient = callTelClient;
  125. _telOperationRepository = telOperationRepository;
  126. _appOptions = appOptions;
  127. _voiceEvaluationButtonRepository = voiceEvaluationButtonRepository;
  128. _orderVisitDomainService = orderVisitDomainService;
  129. }
  130. #region 添添呼
  131. #region 分机
  132. /// <summary>
  133. /// 查询所有分机
  134. /// </summary>
  135. /// <returns></returns>
  136. [HttpGet("query-tels")]
  137. public async Task<List<TrTelDto>> TrQueryTels()
  138. {
  139. var tels = await _callTelClient.QueryTelsAsync(new QueryTelRequest() { }, HttpContext.RequestAborted);
  140. var listenTels = _systemSettingCacheManager.GetSetting(SettingConstants.ListenTels)?.SettingValue;
  141. tels = tels.Where(m => listenTels.Contains(m.TelNo) == false).ToList();
  142. var returnlist = _mapper.Map<List<TrTelDto>>(tels);
  143. string callOutQueueId = _systemSettingCacheManager.GetSetting(SettingConstants.CallOutQueueId).SettingValue[0];
  144. returnlist.ForEach(x =>
  145. {
  146. x.CallOutQueue = callOutQueueId;
  147. });
  148. return returnlist;
  149. }
  150. /// <summary>
  151. /// 查询所有分机状态
  152. /// </summary>
  153. /// <returns></returns>
  154. [HttpGet("query-telstate")]
  155. [AllowAnonymous]
  156. public async Task<IReadOnlyList<TrTelStateDto>> TrQueryTelState([FromQuery] string? state, bool hasListen)
  157. {
  158. var tels = await _callTelClient.QueryTelStateAsync(new QueryTelStateRequest() { State = state }, HttpContext.RequestAborted);
  159. var listenTels = _systemSettingCacheManager.GetSetting(SettingConstants.ListenTels)?.SettingValue;
  160. var workList = await _workRepository.Queryable().Where(d => 1 == 1 && !d.EndTime.HasValue).ToListAsync();
  161. var query = from tel in tels.AgentList
  162. join works in workList on tel.TelNo equals works.TelNo into workD
  163. from work in workD.DefaultIfEmpty()
  164. select new TrTelStateDto
  165. {
  166. Id = tel.Id,
  167. TelNo = tel.TelNo,
  168. ChannelUUid = tel.ChannelUUid,
  169. TelName = tel.TelName,
  170. Type = tel.Type,
  171. Weight = tel.Weight,
  172. Queue = tel.Queue,
  173. State = tel.State,
  174. OldState = tel.OldState,
  175. Device = tel.Device,
  176. SipIp = tel.SipIp,
  177. PrivateData = tel.PrivateData,
  178. SipState = tel.SipState,
  179. CreatedAt = tel.CreatedAt,
  180. UpdatedAt = tel.UpdatedAt,
  181. CallDirection = tel.CallDirection,
  182. OtherNumber = tel.OtherNumber,
  183. GateWay = tel.GateWay,
  184. AnsweredAt = tel.AnsweredAt,
  185. WorkUserId = (work != null) ? work.UserId : "",
  186. WorkUserName = (work != null) ? work.UserName : "",
  187. };
  188. //if (hasListen == false)
  189. //{
  190. // query = query.Where(m => listenTels.Contains(m.TelNo) == false);
  191. //}
  192. var list = query.OrderBy(x => x.TelNo).OrderByDescending(x => x.CreatedAt).ToList();
  193. return list;// _mapper.Map<IReadOnlyList<TrTelStateDto>>(tels.AgentList);
  194. }
  195. /// <summary>
  196. /// 查询坐席分机状态及通话信息
  197. /// </summary>
  198. /// <param name="telno"></param>
  199. /// <returns></returns>
  200. [HttpGet("query-telstatebyno")]
  201. [AllowAnonymous]
  202. public async Task<TrTelStateDto> TrQueryTelStateByTelNo([FromQuery] string? telno)
  203. {
  204. var tels = await _callTelClient.QueryTelStateAsync(new QueryTelStateRequest() { TelNo = telno }, HttpContext.RequestAborted);
  205. var listenTels = _systemSettingCacheManager.GetSetting(SettingConstants.ListenTels)?.SettingValue;
  206. tels.Agents = tels.Agents?.Where(m => listenTels.Contains(m.TelNo) == false).ToList();
  207. var tel = tels.Agents?.FirstOrDefault();
  208. if (tel != null)
  209. {
  210. var work = _userCacheManager.GetWorkByTel(tel.TelNo);
  211. var result = new TrTelStateDto
  212. {
  213. Id = tel.Id,
  214. TelNo = tel.TelNo,
  215. ChannelUUid = tel.ChannelUUid,
  216. TelName = tel.TelName,
  217. Type = tel.Type,
  218. Weight = tel.Weight,
  219. Queue = tel.Queue,
  220. State = tel.State,
  221. OldState = tel.OldState,
  222. Device = tel.Device,
  223. SipIp = tel.SipIp,
  224. PrivateData = tel.PrivateData,
  225. SipState = tel.SipState,
  226. CreatedAt = tel.CreatedAt,
  227. UpdatedAt = tel.UpdatedAt,
  228. CallDirection = tel.CallDirection,
  229. OtherNumber = tel.OtherNumber,
  230. GateWay = tel.GateWay,
  231. AnsweredAt = tel.AnsweredAt,
  232. WorkUserId = (work != null) ? work.UserId : "",
  233. WorkUserName = (work != null) ? work.UserName : "",
  234. };
  235. result.OnStateCount = await _trCallRecordRepository.Queryable().Where(x => x.CDPN == telno && x.CallDirection == ECallDirection.In && x.OnState == EOnState.On && x.CreatedTime.Date == DateTime.Now.Date).CountAsync();
  236. return result;
  237. }
  238. return null;
  239. }
  240. #endregion
  241. #region 黑白名单
  242. /// <summary>
  243. /// 新增黑白名单
  244. /// </summary>
  245. /// <param name="dto"></param>
  246. /// <returns></returns>
  247. [LogFilter("新增黑白名单")]
  248. [HttpPost("add-blacklist")]
  249. public async Task AddBlacklist([FromBody] TrAddBlacklistDto dto)
  250. {
  251. await _callTelClient.AddBlacklistAsync(new AddBlacklistRequest() { Phone = dto.Phone, SpecialFlag = dto.SpecialFlag }, HttpContext.RequestAborted);
  252. }
  253. /// <summary>
  254. /// 删除黑白名单
  255. /// </summary>
  256. /// <param name="dto"></param>
  257. /// <returns></returns>
  258. [LogFilter("删除黑白名单")]
  259. [HttpPost("remove-blacklist")]
  260. public async Task DelBlacklist([FromBody] TrDelBlacklistDto dto)
  261. {
  262. await _callTelClient.DelBlacklistAsync(new DelBlacklistRequest() { Phone = dto.Phone, SpecialFlag = dto.SpecialFlag }, HttpContext.RequestAborted);
  263. }
  264. /// <summary>
  265. /// 查询黑白名单
  266. /// </summary>
  267. /// <returns></returns>
  268. [HttpGet("query-blacklist")]
  269. public async Task<IReadOnlyList<TrQueryBlacklistResponseDto>> QueryBlacklist([FromQuery] TrQueryBlacklistDto dto)
  270. {
  271. var blacklist = await _callTelClient.QueryBlacklistAsync(new QueryBlacklistRequest() { Phone = dto.Phone, SpecialFlag = dto.SpecialFlag }, HttpContext.RequestAborted);
  272. return _mapper.Map<IReadOnlyList<TrQueryBlacklistResponseDto>>(blacklist);
  273. }
  274. #endregion
  275. #region 签入后调用
  276. /// <summary>
  277. /// 上班
  278. /// </summary>
  279. /// <param name="dto"></param>
  280. /// <returns></returns>
  281. [HttpPost("on-duty")]
  282. public async Task<TrOnDutyResponseDto> OnDuty([FromBody] TrOnDutyDto dto)
  283. {
  284. return await _callApplication.SignInAsync(new SignInDto() { TelNo = dto.TelNo, TelModelState = dto.TelModelState }, HttpContext.RequestAborted);
  285. }
  286. /// <summary>
  287. /// 下班
  288. /// </summary>
  289. /// <returns></returns>
  290. [HttpPost("off-duty")]
  291. public async Task OffDuty()
  292. {
  293. //await _userDomainService.TrOffDutyAsync(_sessionContext.RequiredUserId, HttpContext.RequestAborted);
  294. await _telApplication.SignOutAsync(_sessionContext.RequiredUserId, HttpContext.RequestAborted);
  295. }
  296. /// <summary>
  297. /// 查询当前用户的分机状态
  298. /// </summary>
  299. /// <returns></returns>
  300. [HttpGet("tel-state")]
  301. public async Task<TrOnDutyResponseDto> TelState()
  302. {
  303. return await _callApplication.GetTelStateAsync(HttpContext.RequestAborted);
  304. }
  305. /// <summary>
  306. /// 切换状态
  307. /// </summary>
  308. /// <param name="userId"></param>
  309. /// <param name="isCallOut"></param>
  310. /// <returns></returns>
  311. [HttpPost("change-telmodel")]
  312. public async Task ChangeTelModel([FromBody] ChangeTelModelDto dto)
  313. {
  314. await _callApplication.ChangeTelModel(dto.isCallOut, HttpContext.RequestAborted);
  315. }
  316. /// <summary>
  317. /// 下班-管理手动操作
  318. /// </summary>
  319. /// <param name="telNo"></param>
  320. /// <returns></returns>
  321. [HttpGet("off-duty-manage")]
  322. public async Task OffDuty([FromQuery] string telNo)
  323. {
  324. await _telApplication.SignOutByTelNoAsync(telNo, HttpContext.RequestAborted);
  325. }
  326. //提供关闭浏览器事件触发调用
  327. [HttpPost("off-duty-no-auth")]
  328. [AllowAnonymous]
  329. public async Task OffDutyWithNoAuthorize([FromQuery] string userId)
  330. {
  331. if (string.IsNullOrEmpty(userId))
  332. throw new UserFriendlyException("无效用户编号");
  333. await _telApplication.SignOutByTelNoAsync(userId, HttpContext.RequestAborted);
  334. }
  335. #endregion
  336. #region 添添呼小休
  337. /// <summary>
  338. /// 小休页面基础数据
  339. /// </summary>
  340. /// <returns></returns>
  341. [HttpGet("rest/basedata")]
  342. public async Task<object> RestBasePaegData()
  343. {
  344. var rsp = new
  345. {
  346. RestReason = _systemDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.RestReason)
  347. };
  348. return rsp;
  349. }
  350. /// <summary>
  351. /// 小休
  352. /// </summary>
  353. /// <returns></returns>
  354. [HttpPost("rest")]
  355. [LogFilter("分机小休")]
  356. public async Task Rest([FromBody] TrRestDto dto)
  357. {
  358. var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
  359. if (work == null)
  360. throw UserFriendlyException.SameMessage("分机未签入,不能休息");
  361. var isResting = await _telRestRepository.IsRestingAsync(work.TelNo, HttpContext.RequestAborted);
  362. if (isResting)
  363. throw UserFriendlyException.SameMessage("当前坐席正在休息");
  364. var user = await _userRepository.GetAsync(work.UserId, HttpContext.RequestAborted);
  365. var telRest = new TelRest(work.TelNo, work.TelNo, work.UserId, work.UserName, dto.Reason, false, user.StaffNo);
  366. await _telRestRepository.AddAsync(telRest, HttpContext.RequestAborted);
  367. var telAction = new TelActionRecord(work.UserId, work.UserName, work.TelNo, work.QueueId, EActionType.TelRest);
  368. await _telActionRecordRepository.AddAsync(telAction, HttpContext.RequestAborted);
  369. }
  370. /// <summary>
  371. /// 取消小休
  372. /// </summary>
  373. /// <returns></returns>
  374. [HttpPost("unrest")]
  375. [LogFilter("分机取消小休")]
  376. public async Task UnRest()
  377. {
  378. var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
  379. if (work is null)
  380. throw UserFriendlyException.SameMessage("分机未签入,不能操作");
  381. var telRest = await _telRestRepository.GetAsync(x => x.TelNo == work.TelNo && !x.EndTime.HasValue, HttpContext.RequestAborted);
  382. if (telRest is null)
  383. throw UserFriendlyException.SameMessage("未查询到分机休息信息");
  384. telRest.EndRest();
  385. await _telRestRepository.UpdateAsync(telRest, HttpContext.RequestAborted);
  386. var telAction = await _telActionRecordRepository.GetAsync(x => x.TelNo == work.TelNo && x.ActionType == EActionType.TelRest && !x.EndTime.HasValue, HttpContext.RequestAborted);
  387. if (telAction != null)
  388. {
  389. telAction.EndAction();
  390. await _telActionRecordRepository.UpdateAsync(telAction);
  391. }
  392. }
  393. #endregion
  394. #region 添添呼话后整理
  395. /// <summary>
  396. /// 分机动作开始
  397. /// </summary>
  398. /// <returns></returns>
  399. [HttpGet("callennd-arrange-begin")]
  400. public async Task CallEndArrangeBegin(int actionType)
  401. {
  402. var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
  403. if (work is null)
  404. throw UserFriendlyException.SameMessage("分机未签入,不能操作");
  405. //判断如果已经存在未结束的该动作就不新增
  406. var ishas = await _telActionRecordRepository.AnyAsync(x => x.TelNo == work.TelNo && x.ActionType == (EActionType)actionType && !x.EndTime.HasValue, HttpContext.RequestAborted);
  407. if (!ishas)
  408. {
  409. var telAction = new TelActionRecord(work.UserId, work.UserName, work.TelNo, work.QueueId, (EActionType)actionType);
  410. await _telActionRecordRepository.AddAsync(telAction, HttpContext.RequestAborted);
  411. }
  412. }
  413. /// <summary>
  414. /// 分机动作结束
  415. /// </summary>
  416. /// <returns></returns>
  417. [HttpGet("callend-arrange-end")]
  418. public async Task CallEndArrangeEnd(int actionType)
  419. {
  420. var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
  421. if (work is null)
  422. throw UserFriendlyException.SameMessage("分机未签入,不能操作");
  423. var telAction = await _telActionRecordRepository.GetAsync(x => x.TelNo == work.TelNo && x.ActionType == (EActionType)actionType && !x.EndTime.HasValue, HttpContext.RequestAborted);
  424. if (telAction != null)
  425. {
  426. telAction.EndAction();
  427. await _telActionRecordRepository.UpdateAsync(telAction);
  428. }
  429. }
  430. #endregion
  431. #region 通话记录(对外)
  432. /// <summary>
  433. /// 接收通话记录
  434. /// </summary>
  435. /// <param name="dto"></param>
  436. /// <returns></returns>
  437. [AllowAnonymous]
  438. [HttpPost("receivecallrecord")]
  439. public async Task<OpenResponse> ReceiveCallRecord([FromBody] ReceiveCallRecordDto dto)
  440. {
  441. _logger.LogInformation($"收到通话记录结果回传:{JsonConvert.SerializeObject(dto)}");
  442. //bool isHas = await _trCallRecordRepository.AnyAsync(x => x.OtherAccept == dto.other_accept, HttpContext.RequestAborted);
  443. //if (isHas)
  444. //{
  445. // return OpenResponse.Ok("success");
  446. //}
  447. var model = _mapper.Map<TrCallRecord>(dto);
  448. model.Duration = 0;
  449. model.RingTimes = 0;
  450. model.QueueTims = 0;
  451. model.OnState = Share.Enums.CallCenter.EOnState.NoOn;
  452. //计算通话时长
  453. if (model.AnsweredTime != null)
  454. {
  455. model.OnState = Share.Enums.CallCenter.EOnState.On; //是否接通
  456. TimeSpan tsend = new TimeSpan(model.OverTime.Ticks);
  457. TimeSpan tsbegin = new TimeSpan(model.AnsweredTime.Value.Ticks);
  458. model.Duration = Convert.ToInt32(tsend.Subtract(tsbegin).TotalSeconds);
  459. }
  460. //计算振铃时长
  461. if (model.BeginRingTime != null)
  462. {
  463. TimeSpan tsbegin = new TimeSpan(model.BeginRingTime.Value.Ticks);
  464. if (model.EndRingTimg != null)
  465. {
  466. TimeSpan tsend = new TimeSpan(model.EndRingTimg.Value.Ticks);
  467. model.RingTimes = Convert.ToInt32(tsend.Subtract(tsbegin).TotalSeconds);
  468. }
  469. else
  470. {
  471. TimeSpan tsend = new TimeSpan(model.OverTime.Ticks);
  472. model.RingTimes = Convert.ToInt32(tsend.Subtract(tsbegin).TotalSeconds);
  473. }
  474. }
  475. //计算队列时长
  476. if (model.BeginQueueTime != null && model.EndQueueTime != null)
  477. {
  478. TimeSpan tsend = new TimeSpan(model.EndQueueTime.Value.Ticks);
  479. TimeSpan tsbegin = new TimeSpan(model.BeginQueueTime.Value.Ticks);
  480. model.QueueTims = Convert.ToInt32(tsend.Subtract(tsbegin).TotalSeconds);
  481. }
  482. //获取分机号
  483. if (model.CallDirection == ECallDirection.In)
  484. {
  485. model.TelNo = model.CDPN;
  486. try
  487. {
  488. var areaModel = await _systemMobilAreaApplication.GetPhoneCardArea(model.CPN, HttpContext.RequestAborted);
  489. if (areaModel != null)
  490. {
  491. model.MobileAreaName = areaModel.MobileAreaName;
  492. model.OFlag = areaModel.OFlag;
  493. model.OperatorName = areaModel.OperatorName;
  494. }
  495. }
  496. catch { }
  497. }
  498. else
  499. {
  500. model.TelNo = model.CPN;
  501. try
  502. {
  503. var areaModel = await _systemMobilAreaApplication.GetPhoneCardArea(model.CDPN, HttpContext.RequestAborted);
  504. if (areaModel != null)
  505. {
  506. model.MobileAreaName = areaModel.MobileAreaName;
  507. model.OFlag = areaModel.OFlag;
  508. model.OperatorName = areaModel.OperatorName;
  509. }
  510. }
  511. catch { }
  512. }
  513. //判断是否是内部通话(目前分机都为4位)
  514. if (model.CPN.Length == 4 && model.CDPN.Length == 4) //是内部通话
  515. {
  516. model.TelNo = model.CDPN;//如果是内部通话 响应分机为被叫号码
  517. }
  518. if (!string.IsNullOrEmpty(dto.phoneTypes))
  519. {
  520. model.PhoneTypes = (EPhoneTypes)Convert.ToInt32(dto.phoneTypes);
  521. }
  522. //获取关联 工单或是回访
  523. //var order = await _orderRepository.GetAsync(x => x.CallId == model.CallAccept, HttpContext.RequestAborted);//由CallAccept改为OtherAccept
  524. var order = await _orderRepository.GetAsync(x => x.CallId == model.OtherAccept && string.IsNullOrEmpty(x.CallId) == false, HttpContext.RequestAborted);
  525. if (order != null)
  526. {
  527. model.CallOrderType = ECallOrderType.Order;
  528. model.ExternalId = order.Id;
  529. try
  530. {
  531. // 写入智能质检
  532. var teAny = await _qualityTemplate.Queryable()
  533. .LeftJoin<QualityTemplateDetail>((x, d) => x.Id == d.TemplateId)
  534. .LeftJoin<QualityItem>((x, d, i) => d.ItemId == i.Id)
  535. .Where((x, d, i) => i.IsIntelligent == 1)
  536. .Where((x, d, i) => x.Grouping == ETemplateGrouping.Accepted).AnyAsync();
  537. if (teAny)
  538. {
  539. var quality = await _qualiteyRepository.Queryable().Where(x => x.OrderId == order.Id && x.Source == Share.Enums.Quality.EQualitySource.Accepted).FirstAsync();
  540. if (quality != null)
  541. {
  542. var setting = _systemSettingCacheManager.GetSetting(SettingConstants.ViteRecordPrefix);
  543. //await _aiQualityService.CreateAiOrderQualityTask(quality, model, order, setting?.SettingValue[0], HttpContext.RequestAborted);
  544. try
  545. {
  546. //_aiQualityService.CreateAiOrderQualityTask(
  547. //quality,
  548. //model.RecordingAbsolutePath,
  549. //model.CPN,
  550. //model.CreatedTime,
  551. //order, setting?.SettingValue[0], HttpContext.RequestAborted);
  552. var handler = new AiQualityHandler()
  553. {
  554. Id = quality.Id,
  555. Source = quality.Source.ToString(),
  556. AudioFile = model.RecordingAbsolutePath,
  557. FromNo = model.CPN,
  558. CallStartTime = model.CreatedTime,
  559. ViteRecordPrefix = setting?.SettingValue[0],
  560. };
  561. await _publisher.PublishAsync(new AiOrderQualityNotify(handler), PublishStrategy.ParallelNoWait, HttpContext.RequestAborted);
  562. }
  563. catch (Exception e)
  564. {
  565. _logger.LogError($"写入智能质检异常2!, \r\n{e.Message}");
  566. }
  567. }
  568. }
  569. }
  570. catch { }
  571. }
  572. else
  573. {
  574. //var orderVisit = await _orderVisitRepository.GetAsync(x => x.CallId == model.CallAccept, HttpContext.RequestAborted);//由CallAccept改为OtherAccept
  575. var orderVisit = await _orderVisitRepository.GetAsync(x => x.CallId == model.OtherAccept && string.IsNullOrEmpty(x.CallId) == false, HttpContext.RequestAborted);
  576. if (orderVisit != null)
  577. {
  578. model.CallOrderType = ECallOrderType.Visit;
  579. model.ExternalId = orderVisit.Id;
  580. }
  581. }
  582. //获取用户
  583. var work = await _userCacheManager.GetWorkByTelNoLast(model.TelNo);
  584. if (!string.IsNullOrEmpty(model.AgentTransferNumber))
  585. {
  586. work = await _userCacheManager.GetWorkByTelNoLast(model.AgentTransferNumber);
  587. }
  588. if (work != null)
  589. {
  590. model.UserId = work.UserId;
  591. model.UserName = work.UserName;
  592. model.StaffNo = work.StaffNo;
  593. }
  594. await _trCallRecordRepository.AddAsync(model, HttpContext.RequestAborted);
  595. var publishCallRecordDto = new PublishCallRecrodDto() { };
  596. if (order != null)
  597. {
  598. var orderDto = _mapper.Map<OrderDto>(order);
  599. publishCallRecordDto.Order = orderDto;
  600. }
  601. var trCallDto = _mapper.Map<TrCallDto>(model);
  602. publishCallRecordDto.TrCallRecordDto = trCallDto;
  603. if (string.IsNullOrEmpty(model.AgentTransferNumber))
  604. {
  605. //推省上
  606. await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineCallBye, publishCallRecordDto);
  607. }
  608. return OpenResponse.Ok("success");
  609. }
  610. /// <summary>
  611. /// 接受通话语音评价
  612. /// </summary>
  613. /// <param name="dto"></param>
  614. /// <returns></returns>
  615. [AllowAnonymous]
  616. [HttpPost("receivecallevaluate")]
  617. public async Task<OpenResponse> ReceiveCallEvaluate([FromBody] ReceiveCallEvaluateDto dto)
  618. {
  619. var model = _mapper.Map<TrCallEvaluate>(dto);
  620. await _trCallEvaluate.AddAsync(model, HttpContext.RequestAborted);
  621. return OpenResponse.Ok("success");
  622. }
  623. /// <summary>
  624. /// 话机状态通知
  625. /// </summary>
  626. /// <param name="dto"></param>
  627. /// <returns></returns>
  628. [AllowAnonymous]
  629. [StaffNoSessionContextFilter()]
  630. [HttpPost("notify/status")]
  631. [LogFilterAlpha("话机状态通知")]
  632. public async Task NotifyStatus(NotifyInDto dto)
  633. {
  634. var userName = _sessionContext.UserName;
  635. if (dto.Status == 0) // 签出
  636. {
  637. //await _iPPbxApplication.ResetTelStatus(null, dto.TelNo, CancellationToken.None);
  638. }
  639. //dto.Status = await _callTelClient.GetStatusAsync(dto.Status);
  640. //await _callApplication.EndActionAsync(dto.Adapt<EndActionInDto>());
  641. }
  642. /// <summary>
  643. /// 兴唐语音评价接口
  644. /// </summary>
  645. /// <param name="dto"></param>
  646. /// <returns></returns>
  647. [AllowAnonymous]
  648. [StaffNoSessionContextFilter()]
  649. [HttpPost("voiceevaluation/button")]
  650. [LogFilterAlpha("兴唐语音评价通知")]
  651. public async Task VoiceEvaluationButton(VoiceEvaluationButtonDto dto)
  652. {
  653. var data = _mapper.Map<VoiceEvaluationButton>(dto);
  654. await _voiceEvaluationButtonRepository.AddAsync(data, HttpContext.RequestAborted);
  655. try
  656. {
  657. await _orderVisitDomainService.UpdateVoiceReplyAsync(data);
  658. }
  659. catch (Exception)
  660. {
  661. }
  662. }
  663. #endregion
  664. #region 通话记录(对内)
  665. /// <summary>
  666. /// 获取通话记录列表
  667. /// </summary>
  668. /// <param name="dto"></param>
  669. /// <returns></returns>
  670. [HttpGet("calls/call-list")]
  671. public async Task<PagedDto<TrCallDto>> GetCallList([FromQuery] GetCallListDto dto)
  672. {
  673. var (total, items) = await _callRecordRepository.GetCallList(dto)
  674. .ToPagedListAsync(dto.PageIndex, dto.PageSize);
  675. var isCallBindOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.IsCallBindOrder).SettingValue[0]);
  676. var itemsResult = _mapper.Map<List<TrCallDto>>(items);
  677. itemsResult.ForEach(x =>
  678. {
  679. if (isCallBindOrder)
  680. {
  681. if (x.Order == null && !string.IsNullOrEmpty(x.OtherAccept))
  682. x.IsBindOrder = true;
  683. else
  684. x.IsBindOrder = false;
  685. }
  686. else
  687. {
  688. if (string.IsNullOrEmpty(x.UserId) && x.Order == null && !string.IsNullOrEmpty(x.OtherAccept))
  689. x.IsBindOrder = true;
  690. else
  691. x.IsBindOrder = false;
  692. }
  693. });
  694. return new PagedDto<TrCallDto>(total, itemsResult);
  695. //return new PagedDto<TrCallDto>(total, _mapper.Map<IReadOnlyList<TrCallDto>>(items));
  696. }
  697. /// <summary>
  698. /// 通话记录导出
  699. /// </summary>
  700. /// <param name="dto"></param>
  701. /// <returns></returns>
  702. [HttpPost("calls/call-list/export")]
  703. [LogFilterAlpha("导出日志")]
  704. public async Task<FileStreamResult> GetCallListExport([FromBody] ExportExcelDto<GetCallListDto> dto)
  705. {
  706. var query = _callRecordRepository.GetCallList(dto.QueryDto);
  707. List<TrCallRecord> data;
  708. if (dto.IsExportAll)
  709. {
  710. data = await query.ToListAsync(HttpContext.RequestAborted);
  711. }
  712. else
  713. {
  714. var (_, items) = await query.ToPagedListAsync(dto.QueryDto, HttpContext.RequestAborted);
  715. data = items;
  716. }
  717. var dataDtos = _mapper.Map<ICollection<TrCallDto>>(data);
  718. dto.ColumnInfos.ForEach(x =>
  719. {
  720. if (x.Prop == "cpn")
  721. {
  722. x.Prop = "CPN";
  723. }
  724. if (x.Prop == "cdpn")
  725. {
  726. x.Prop = "CDPN";
  727. }
  728. });
  729. dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass<TrCallDto>(dto.ColumnInfos);
  730. var dtos = dataDtos
  731. .Select(stu => _mapper.Map(stu, typeof(TrCallDto), dynamicClass))
  732. .Cast<object>()
  733. .ToList();
  734. var stream = ExcelHelper.CreateStream(dtos);
  735. return ExcelStreamResult(stream, "通话记录");
  736. }
  737. /// <summary>
  738. /// 通话记录基础数据
  739. /// </summary>
  740. /// <returns></returns>
  741. [HttpGet("calls/basedata")]
  742. public async Task<object> CallBaseData()
  743. {
  744. return new
  745. {
  746. OnState = EnumExts.GetDescriptions<EOnState>(),
  747. CallDirection = EnumExts.GetDescriptions<ECallDirection>(),
  748. EndBy = EnumExts.GetDescriptions<EEndBy>(),
  749. PhoneTypes = EnumExts.GetDescriptions<EPhoneTypes>()
  750. };
  751. }
  752. #endregion
  753. #region 关联
  754. /// <summary>
  755. /// 可关联工单
  756. /// </summary>
  757. /// <param name="dto"></param>
  758. /// <returns></returns>
  759. [HttpGet("canlink-order")]
  760. public async Task<PagedDto<OrderDto>> CanLinkCallRecordOrder([FromQuery] CanLinkCallRecordOrderDto dto)
  761. {
  762. var (total, items) = await _orderRepository.Queryable()
  763. .Where(x => string.IsNullOrEmpty(x.CallId) && x.Source == Share.Enums.Order.ESource.Hotline)
  764. .WhereIF(!string.IsNullOrEmpty(dto.Keyword), x => x.No.Contains(dto.Keyword) || x.Title.Contains(dto.Keyword))
  765. .OrderByDescending(x => x.CreationTime)
  766. .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
  767. return new PagedDto<OrderDto>(total, _mapper.Map<IReadOnlyList<OrderDto>>(items));
  768. }
  769. /// <summary>
  770. /// 可关联回访
  771. /// </summary>
  772. /// <param name="dto"></param>
  773. /// <returns></returns>
  774. [HttpGet("canlink-ordervisit")]
  775. public async Task<PagedDto<OrderVisitDto>> CanLinkCallRecordOrderVisit([FromQuery] CanLinkCallRecordOrderVisitDto dto)
  776. {
  777. var (total, items) = await _orderVisitRepository.Queryable()
  778. .Includes(x => x.Order)
  779. .Where(x => string.IsNullOrEmpty(x.CallId) && x.VisitType == Share.Enums.Order.EVisitType.ArtificialVisit)
  780. .WhereIF(!string.IsNullOrEmpty(dto.Keyword), x => x.No.Contains(dto.Keyword))
  781. .OrderByDescending(x => x.CreationTime)
  782. .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
  783. return new PagedDto<OrderVisitDto>(total, _mapper.Map<IReadOnlyList<OrderVisitDto>>(items));
  784. }
  785. /// <summary>
  786. /// 关联工单或者回访
  787. /// </summary>
  788. /// <param name="dto"></param>
  789. /// <returns></returns>
  790. [HttpPost("link-callrecord")]
  791. public async Task LinkCallRecord([FromBody] LinkCallRecordDto dto)
  792. {
  793. //var trRecord = await _trCallRecordRepository.GetAsync(x => x.CallAccept == dto.CallId, HttpContext.RequestAborted);//由CallAccept改为OtherAccept
  794. if (dto.IsOrder)
  795. {
  796. var trRecords = await _trCallRecordRepository.Queryable().Where(x => x.OtherAccept == dto.CallId).ToListAsync(HttpContext.RequestAborted);
  797. foreach (var trRecord in trRecords)
  798. {
  799. if (trRecord.CallOrderType == ECallOrderType.Order && !string.IsNullOrEmpty(trRecord.ExternalId))
  800. throw UserFriendlyException.SameMessage("通话记录已经关联工单");
  801. //工单
  802. var order = await _orderRepository.GetAsync(x => x.Id == dto.Id, HttpContext.RequestAborted);
  803. //if (!string.IsNullOrEmpty(order.CallId))
  804. // throw UserFriendlyException.SameMessage("通话记录已经关联工单");
  805. order.CallId = dto.CallId;
  806. order.FromPhone = trRecord.CPN;
  807. order.TransferPhone = trRecord.Gateway;
  808. await _orderRepository.UpdateAsync(order, HttpContext.RequestAborted);
  809. trRecord.CallOrderType = ECallOrderType.Order;
  810. trRecord.ExternalId = order.Id;
  811. await _trCallRecordRepository.UpdateAsync(trRecord, HttpContext.RequestAborted);
  812. //推省上
  813. await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineCallConnectWithOrder, new PublishCallRecrodDto() { Order = _mapper.Map<OrderDto>(order), TrCallRecordDto = _mapper.Map<TrCallDto>(trRecord) });
  814. }
  815. }
  816. else
  817. {
  818. var trRecord = await _trCallRecordRepository.GetAsync(x => x.OtherAccept == dto.CallId, HttpContext.RequestAborted);
  819. //回访
  820. var visit = await _orderVisitRepository.GetAsync(x => x.Id == dto.Id, HttpContext.RequestAborted);
  821. visit.CallId = dto.CallId;
  822. await _orderVisitRepository.UpdateAsync(visit, HttpContext.RequestAborted);
  823. trRecord.CallOrderType = ECallOrderType.Visit;
  824. trRecord.ExternalId = visit.Id;
  825. await _trCallRecordRepository.UpdateAsync(trRecord, HttpContext.RequestAborted);
  826. }
  827. }
  828. #endregion
  829. #region 坐席动作记录
  830. /// <summary>
  831. /// 坐席动作列表
  832. /// </summary>
  833. /// <param name="dto"></param>
  834. /// <returns></returns>
  835. [HttpGet("telaction-list")]
  836. public async Task<PagedDto<TelActionListRep>> TelActionList([FromQuery] TelActionListDto dto)
  837. {
  838. var (total, items) = await _telActionRecordRepository.Queryable()
  839. .WhereIF(string.IsNullOrEmpty(dto.TelNo) == false, x => x.TelNo.Contains(dto.TelNo))
  840. .WhereIF(dto.ActionTtype != null, x => x.ActionType == dto.ActionTtype)
  841. .WhereIF(string.IsNullOrEmpty(dto.UserName) == false, x => x.UserName.Contains(dto.UserName))
  842. .WhereIF(dto.StartTime.HasValue, x => x.CreationTime >= dto.StartTime)
  843. .WhereIF(dto.EndTime.HasValue, x => x.CreationTime <= dto.EndTime)
  844. .WhereIF(_appOptions.Value.IsLuZhou,x=>x.ActionType != EActionType.CallEndArrange)
  845. .OrderByDescending(x => x.CreationTime)
  846. .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
  847. return new PagedDto<TelActionListRep>(total, _mapper.Map<IReadOnlyList<TelActionListRep>>(items));
  848. }
  849. /// <summary>
  850. /// 坐席动作列表导出
  851. /// </summary>
  852. /// <param name="dto"></param>
  853. /// <returns></returns>
  854. [HttpPost("telaction-list/export")]
  855. [LogFilterAlpha("导出日志")]
  856. public async Task<FileStreamResult> TelActionListExport([FromBody] ExportExcelDto<TelActionListDto> dto)
  857. {
  858. var total = 0;
  859. var items = new List<TelActionRecord>();
  860. var query = _telActionRecordRepository.Queryable()
  861. .WhereIF(string.IsNullOrEmpty(dto.QueryDto.TelNo) == false, x => x.TelNo.Contains(dto.QueryDto.TelNo))
  862. .WhereIF(dto.QueryDto.ActionTtype != null, x => x.ActionType == dto.QueryDto.ActionTtype)
  863. .WhereIF(string.IsNullOrEmpty(dto.QueryDto.UserName) == false, x => x.UserName.Contains(dto.QueryDto.UserName))
  864. .WhereIF(dto.QueryDto.StartTime.HasValue, x => x.CreationTime >= dto.QueryDto.StartTime)
  865. .WhereIF(dto.QueryDto.EndTime.HasValue, x => x.CreationTime <= dto.QueryDto.EndTime)
  866. .OrderByDescending(x => x.CreationTime);
  867. if (dto.IsExportAll)
  868. {
  869. items = await query.ToListAsync();
  870. }
  871. else
  872. {
  873. (total, items) = await query.ToPagedListAsync(dto.QueryDto.PageIndex, dto.QueryDto.PageSize);
  874. }
  875. dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass<TelActionListRep>(dto.ColumnInfos);
  876. var dtos = _mapper.Map<ICollection<TelActionListRep>>(items)
  877. .Select(stu => _mapper.Map(stu, typeof(TelActionListRep), dynamicClass))
  878. .Cast<object>()
  879. .ToList();
  880. var stream = ExcelHelper.CreateStream(dtos);
  881. return ExcelStreamResult(stream, "坐席动作列表");
  882. }
  883. /// <summary>
  884. /// 坐席动作列表基础数据
  885. /// </summary>
  886. /// <returns></returns>
  887. [HttpGet("telaction-basedata")]
  888. public async Task<object> TelActionBaseData()
  889. {
  890. var actionType = EnumExts.GetDescriptions<EActionType>();
  891. if (_appOptions.Value.GetDefaultAppScopeConfiguration().CallCenterType == AppDefaults.CallCenterType.XingTang)
  892. {
  893. actionType = actionType.Where(m => new int[] { 4, 5 }.Contains(m.Key) == false).ToList();
  894. }
  895. return new
  896. {
  897. ActionType = actionType,
  898. };
  899. }
  900. #endregion
  901. #region 话务队列信息
  902. /// <summary>
  903. /// 今日等待数
  904. /// </summary>
  905. /// <returns></returns>
  906. [HttpGet("query-todaywaitnum")]
  907. public async Task<int> QueryToDayWaitNum()
  908. {
  909. int count = await _trCallRecordRepository.Queryable()
  910. .Where(x => x.CreatedTime.Date == DateTime.Now.Date)
  911. .Where(x => x.QueueTims > 0 && x.Duration == 0)
  912. .CountAsync();
  913. return count;
  914. }
  915. #endregion
  916. #region 话机状态
  917. /// <summary>
  918. /// 重置话机状态
  919. /// </summary>
  920. /// <returns></returns>
  921. [HttpPost("tels-status-refreshservice")]
  922. [AllowAnonymous]
  923. public async Task TelsStatusRefreshService()
  924. {
  925. try
  926. {
  927. var list = await _workRepository.Queryable().Where(x => 1 == 1 && !x.EndTime.HasValue).ToListAsync();
  928. var tellist = await _callTelClient.QueryTelStateAsync(new QueryTelStateRequest { }, HttpContext.RequestAborted);
  929. foreach (var item in list)
  930. {
  931. var telmodel = tellist.AgentList.First(x => x.TelNo == item.TelNo);
  932. if (telmodel != null)
  933. {
  934. if (telmodel.State == "logout")
  935. {
  936. await _iPPbxApplication.ResetTelStatus(item.Id, null, HttpContext.RequestAborted);
  937. //var telRest = await _telRestRepository.GetAsync(x => x.TelNo == item.TelNo && !x.EndTime.HasValue, HttpContext.RequestAborted);
  938. //if (telRest is not null)
  939. //{
  940. // telRest.EndRest();
  941. // await _telRestRepository.UpdateAsync(telRest, HttpContext.RequestAborted);
  942. //}
  943. //item.OffDuty();
  944. //await _workRepository.UpdateAsync(item, HttpContext.RequestAborted);
  945. //_cacheWork.Remove(item.GetKey(KeyMode.UserId));
  946. //_cacheWork.Remove(item.GetKey(KeyMode.TelNo));
  947. //var listx = await _telActionRecordRepository.Queryable().Where(x => x.TelNo == item.TelNo && !x.EndTime.HasValue).ToListAsync();
  948. //foreach (var itemx in listx)
  949. //{
  950. // itemx.EndAction();
  951. // await _telActionRecordRepository.UpdateAsync(itemx);
  952. //}
  953. }
  954. }
  955. }
  956. }
  957. catch
  958. {
  959. }
  960. }
  961. #endregion
  962. #endregion
  963. }
  964. }