123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569 |
- using Hotline.Caches;
- using Hotline.CallCenter.Calls;
- using Hotline.CallCenter.Devices;
- using Hotline.CallCenter.Ivrs;
- using Hotline.CallCenter.Manage;
- using Hotline.CallCenter.Tels;
- using Hotline.Permissions;
- using Hotline.Repository.SqlSugar.CallCenter;
- using Hotline.Share.Dtos.CallCenter;
- using Hotline.Share.Enums.CallCenter;
- using Hotline.Share.Requests;
- using Hotline.Users;
- using MapsterMapper;
- using Microsoft.AspNetCore.Mvc;
- using XF.Domain.Authentications;
- using XF.Domain.Cache;
- using XF.Domain.Exceptions;
- using XF.Utility.EnumExtensions;
- namespace Hotline.Api.Controllers
- {
- /// <summary>
- /// 话机设备管理
- /// </summary>
- public class PbxController : BaseController
- {
- private readonly ITelRepository _telRepository;
- private readonly ITelDomainService _telDomainService;
- private readonly ITypedCache<Tel> _cacheTel;
- private readonly ITypedCache<TelGroup> _cacheTelGroup;
- private readonly ITelGroupRepository _telGroupRepository;
- private readonly IMapper _mapper;
- private readonly IWorkRepository _workRepository;
- private readonly IDeviceManager _deviceManager;
- private readonly IUserCacheManager _userCacheManager;
- private readonly ISessionContext _sessionContext;
- private readonly ICallRepository _callRepository;
- public PbxController(
- ITelRepository telRepository,
- ITelDomainService telDomainService,
- ITypedCache<Tel> cacheTel,
- ITypedCache<TelGroup> cacheTelGroup,
- ITelGroupRepository telGroupRepository,
- IMapper mapper,
- IWorkRepository workRepository,
- IDeviceManager deviceManager,
- IUserCacheManager userCacheManager,
- ISessionContext sessionContext,
- ICallRepository callRepository)
- {
- _telRepository = telRepository;
- _telDomainService = telDomainService;
- _cacheTel = cacheTel;
- _cacheTelGroup = cacheTelGroup;
- _telGroupRepository = telGroupRepository;
- _mapper = mapper;
- _workRepository = workRepository;
- _deviceManager = deviceManager;
- _userCacheManager = userCacheManager;
- _sessionContext = sessionContext;
- _callRepository = callRepository;
- }
- #region 话机
- /// <summary>
- /// 根据设备自动同步分机数据到数据库
- /// </summary>
- /// <returns></returns>
- [Permission(EPermission.SyncTelsAsync)]
- [HttpPut("sync-tels")]
- public async Task SyncTelsAsync()
- {
- var dbTels = await _telRepository.QueryAsync();
- foreach (var dbTel in dbTels)
- {
- _cacheTel.Remove(dbTel.No);
- }
- //await _telRepository.RemoveRangeAsync(dbTels);
- var tels = await _telDomainService.QueryTelsAsync(HttpContext.RequestAborted);
- var list = tels.ExceptBy(dbTels.Select(e => e.No), x => x.No).ToList();
- var delList = dbTels.ExceptBy(tels.Select(e => e.No), x => x.No).ToList();
- for (int i = 0; i < delList.Count; i++)
- {
- delList[i].SoftDelete();
- }
- await _telRepository.UpdateRangeAsync(delList);
- await _telRepository.AddRangeAsync(list);
- }
- /// <summary>
- /// 查询所有分机
- /// </summary>
- /// <returns></returns>
- [Permission(EPermission.QueryTels)]
- [HttpGet("query-tels")]
- public async Task<IReadOnlyList<TelDto>> QueryTels()
- {
- var tels = await _telRepository.QueryExtAsync(d => true, d => d.Includes(x => x.Groups));
- return _mapper.Map<IReadOnlyList<TelDto>>(tels);
- }
- #endregion
- #region 分机组
- /// <summary>
- /// 页面基础信息
- /// </summary>
- /// <returns></returns>
- [Permission(EPermission.GetBaseInfoGroup)]
- [HttpGet("base-info-group")]
- public async Task<dynamic> GetBaseInfoGroup()
- {
- return new
- {
- Distributions = EnumExts.GetDescriptions<EDistribution>()
- };
- }
- /// <summary>
- /// 查询所有分机组
- /// </summary>
- /// <returns></returns>
- [Permission(EPermission.QueryTelGroups)]
- [HttpGet("query-telgroups")]
- public async Task<IReadOnlyList<TelGroupDto>> QueryTelGroups()
- {
- var groups = await _telGroupRepository.QueryExtAsync(d => true, d => d.Includes(x => x.Tels));
- return _mapper.Map<IReadOnlyList<TelGroupDto>>(groups);
- }
- /// <summary>
- /// 新增分机组
- /// </summary>
- /// <param name="dto"></param>
- /// <returns></returns>
- [Permission(EPermission.AddTelGroup)]
- [HttpPost("add-telgroup")]
- public async Task AddTelGroup([FromBody] AddTelGroupDto dto)
- {
- var works = await _workRepository.QueryAsync(d => dto.TelNos.Contains(d.TelNo) && !d.EndTime.HasValue);
- await _deviceManager.AssginConfigGroupAsync(
- dto.No,
- dto.Distribution.ToString().ToLower(),
- ext: works.Select(d => d.TelNo).ToList(),
- voiceFile: dto.Voice ?? null,
- cancellationToken: HttpContext.RequestAborted);
- var group = _mapper.Map<TelGroup>(dto);
- var tels = await _telRepository.QueryAsync(d => dto.TelNos.Contains(d.No));
- group.Tels = tels;
- await _telGroupRepository.AddNavTelsAsync(group, HttpContext.RequestAborted);
- _cacheTelGroup.Remove(dto.No);
- }
- /// <summary>
- /// 更新分机组
- /// </summary>
- /// <param name="dto"></param>
- /// <returns></returns>
- [Permission(EPermission.UpdateTelGroup)]
- [HttpPut("update-telgroup")]
- public async Task UpdateTelGroup([FromBody] UpdateTelGroupDto dto)
- {
- var works = await _workRepository.QueryAsync(d => dto.TelNos.Contains(d.TelNo) && !d.EndTime.HasValue);
- await _deviceManager.AssginConfigGroupAsync(
- dto.No,
- dto.Distribution.ToString().ToLower(),
- ext: works.Select(d => d.TelNo).ToList(),
- voiceFile: dto.Voice ?? null,
- cancellationToken: HttpContext.RequestAborted);
- var group = _mapper.Map<TelGroup>(dto);
- var tels = await _telRepository.QueryAsync(d => dto.TelNos.Contains(d.No));
- group.Tels = tels;
- await _telGroupRepository.UpdateNavTelsAsync(group, HttpContext.RequestAborted);
- _cacheTelGroup.Remove(dto.No);
- }
- #endregion
- #region 话机设备操作
- /// <summary>
- /// 分机休息
- /// </summary>
- /// <returns></returns>
- [Permission(EPermission.Rest)]
- [HttpPut("rest")]
- public async Task Rest()
- {
- var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
- if (work is null)
- throw UserFriendlyException.SameMessage("当前坐席暂未进行工作");
- await _telDomainService.RestAsync(work, HttpContext.RequestAborted);
- }
- /// <summary>
- /// 分机结束休息
- /// </summary>
- [Permission(EPermission.UnRest)]
- [HttpPut("un-rest")]
- public async Task<TelRestDto> UnRest()
- {
- var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
- if (work is null)
- throw UserFriendlyException.SameMessage("当前坐席暂未进行工作");
- var telRest = await _telDomainService.UnRestAsync(work.TelId, HttpContext.RequestAborted);
- return _mapper.Map<TelRestDto>(telRest);
- }
- /// <summary>
- /// 保持通话
- /// </summary>
- [Permission(EPermission.Hold)]
- [HttpPut("hold")]
- public async Task Hold(string callId)
- {
- var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
- if (work is null)
- throw UserFriendlyException.SameMessage("当前坐席暂未进行工作");
- await _telDomainService.HoldAsync(work.TelId,_sessionContext.RequiredUserId,_sessionContext.UserName,callId, HttpContext.RequestAborted);
- }
- /// <summary>
- /// 恢复通话(解除hold状态)
- /// </summary>
- [Permission(EPermission.UnHold)]
- [HttpPut("un-hold")]
- public async Task UnHold(string callId)
- {
- var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
- if (work is null)
- throw UserFriendlyException.SameMessage("当前坐席暂未进行工作");
- await _telDomainService.UnHoldAsync(work.TelId,_sessionContext.RequiredUserId,callId, HttpContext.RequestAborted);
- }
- /// <summary>
- /// 分机呼分机
- /// </summary>
- /// <returns></returns>
- [Permission(EPermission.TelToTel)]
- [HttpPost("tel-to-tel")]
- public async Task TelToTel([FromBody] TelToTelDto dto)
- {
- var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
- if (work is null)
- throw UserFriendlyException.SameMessage("当前坐席暂未进行工作");
- await _deviceManager.ExtToExtAsync(work.TelNo, dto.TelNo, HttpContext.RequestAborted);
- }
- /// <summary>
- /// 分机拨打外部电话
- /// </summary>
- /// <param name="dto"></param>
- /// <returns></returns>
- [Permission(EPermission.TelToOuter)]
- [HttpPost("tel-to-outer")]
- public async Task TelToOuter([FromBody] TelToOuterDto dto)
- {
- var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
- if (work is null)
- throw UserFriendlyException.SameMessage("当前坐席暂未进行工作");
- await _deviceManager.ExtToOuterAsync(work.TelNo, dto.OuterNo, HttpContext.RequestAborted);
- }
- /// <summary>
- /// 指定模拟外线外呼(分机拨打外部电话)
- /// OM设备多个外线时调用此接口
- /// </summary>
- /// <param name="dto"></param>
- /// <returns></returns>
- [Permission(EPermission.TelToOuterByLine)]
- [HttpPost("tel-to-outer-line")]
- public async Task TelToOuterByLine([FromBody] TelToOuterByLineDto dto)
- {
- var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
- if (work is null)
- throw UserFriendlyException.SameMessage("当前坐席暂未进行工作");
- string newOuter = dto.LineId + "," + dto.OuterNo;
- await _deviceManager.ExtToOuterAsync(work.TelNo, newOuter, HttpContext.RequestAborted);
- }
- /// <summary>
- /// 来电转分机
- /// </summary>
- /// <param name="dto"></param>
- /// <returns></returns>
- [Permission(EPermission.VisitorToTel)]
- [HttpPost("visitor-to-tel")]
- public async Task VisitorToTel([FromBody] VisitorToTelDto dto)
- {
- var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
- if (work is null)
- throw UserFriendlyException.SameMessage("当前坐席暂未进行工作");
- var tel = await _deviceManager.QueryTelAsync(work.TelNo, HttpContext.RequestAborted);
- if (!string.IsNullOrEmpty(tel.ConversationId))
- await _deviceManager.VisitorToExtAsync(tel.ConversationId, dto.TelNo, HttpContext.RequestAborted);
- else
- throw UserFriendlyException.SameMessage("当前分机没有通话");
- }
- /// <summary>
- /// 来电转外部电话
- /// </summary>
- /// <param name="dto"></param>
- /// <returns></returns>
- [Permission(EPermission.VisitorToOuter)]
- [HttpPost("visitor-to-outer")]
- public async Task VisitorToOuter([FromBody] VisitorToOuterDto dto)
- {
- var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
- if (work is null)
- throw UserFriendlyException.SameMessage("当前坐席暂未进行工作");
- var tel = await _deviceManager.QueryTelAsync(work.TelNo, HttpContext.RequestAborted);
- if (!string.IsNullOrEmpty(tel.ConversationId))
- await _deviceManager.VisitorToOuterAsync(tel.ConversationId, dto.OuterNo, HttpContext.RequestAborted);
- else
- throw UserFriendlyException.SameMessage("当前分机没有通话");
- }
- /// <summary>
- /// 来电转分机组队列
- /// </summary>
- /// <param name="dto"></param>
- /// <returns></returns>
- [Permission(EPermission.VisitorToGroup)]
- [HttpPost("visitor-to-group")]
- public async Task VisitorToGroup([FromBody] VisitorToGroupDto dto)
- {
- var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
- if (work is null)
- throw UserFriendlyException.SameMessage("当前坐席暂未进行工作");
- var tel = await _deviceManager.QueryTelAsync(work.TelNo, HttpContext.RequestAborted);
- if (!string.IsNullOrEmpty(tel.ConversationId))
- await _deviceManager.VisitorToGroupAsync(tel.ConversationId, dto.groupid, HttpContext.RequestAborted);
- else
- throw UserFriendlyException.SameMessage("当前分机没有通话");
- }
- /// <summary>
- /// 去电转外部电话
- /// </summary>
- /// <param name="dto"></param>
- /// <returns></returns>
- /// <exception cref="UserFriendlyException"></exception>
- [Permission(EPermission.OuterToOuter)]
- [HttpPost("outer-to-outer")]
- public async Task OuterToOuter([FromBody] OuterToOuterDto dto)
- {
- var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
- if (work is null)
- throw UserFriendlyException.SameMessage("当前坐席暂未进行工作");
- var tel = await _deviceManager.QueryTelAsync(work.TelNo, HttpContext.RequestAborted);
- if (!string.IsNullOrEmpty(tel.ConversationId))
- await _deviceManager.OuterToOuterAsync(tel.ConversationId, dto.OuterNo, HttpContext.RequestAborted);
- else
- throw UserFriendlyException.SameMessage("当前分机没有通话");
- }
- /// <summary>
- /// 去电转分机
- /// </summary>
- /// <param name="dto"></param>
- /// <returns></returns>
- /// <exception cref="UserFriendlyException"></exception>
- [Permission(EPermission.OuterToTel)]
- [HttpPost("outer-to-tel")]
- public async Task OuterToTel([FromBody] OuterToTelDto dto)
- {
- var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
- if (work is null)
- throw UserFriendlyException.SameMessage("当前坐席暂未进行工作");
- var tel = await _deviceManager.QueryTelAsync(work.TelNo, HttpContext.RequestAborted);
- if (!string.IsNullOrEmpty(tel.ConversationId))
- await _deviceManager.OuterToExtAsync(tel.ConversationId, dto.TelNo, HttpContext.RequestAborted);
- else
- throw UserFriendlyException.SameMessage("当前分机没有通话");
- }
- /// <summary>
- /// 三方会议
- /// 先建立两方通话,然后调用保持通话接口,拨通第三方分机,然后再调用三方会议接口
- /// 1. 分机 A 正在和 B 通话;
- /// 2. 分机 A 把原通话呼叫保持;
- /// 3. 分机 A 向 C 发起新的呼叫,并建立通话;
- /// 4. 此时,使用该 API 能够实现以分机 A 为主持方建立 A、B、C 的三方会议。
- /// </summary>
- /// <param name="dto">TelNo:会议发起方分机号</param>
- /// <returns></returns>
- /// <exception cref="UserFriendlyException"></exception>
- [Permission(EPermission.Conference)]
- [HttpPost("meeting")]
- public async Task Conference([FromBody] ConferenceDto dto)
- {
- var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
- if (work is null)
- throw UserFriendlyException.SameMessage("当前坐席暂未进行工作");
- var tel = await _deviceManager.QueryTelAsync(work.TelNo, HttpContext.RequestAborted);
- if (!string.IsNullOrEmpty(tel.ConversationId))
- await _deviceManager.ConferenceMeetingAsync(dto.TelNo, HttpContext.RequestAborted);
- else
- throw UserFriendlyException.SameMessage("当前分机没有通话");
- }
- #region 监听和强插
- /// <summary>
- /// 监听分机
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- [Permission(EPermission.MonitorExt)]
- [HttpPost("monitor-ext")]
- public async Task MonitorExt([FromBody] MonitorExtRequest request)
- {
- await _deviceManager.MonitorExtAsync(request.firstTelNo, request.secondTelNo, HttpContext.RequestAborted);
- }
- /// <summary>
- /// 从监听到插播状态变换
- /// 1. 已知:分机 A 在监听分机 B 与其通话方的通话;
- /// 2. 执行分机 A 的从监听到插播状态变换的 API;
- /// 3. 执行成功时,分机 A 与分机 B 建立通话,分机 B 的原通话方听保持音。
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- [Permission(EPermission.MonitorExtToTalk)]
- [HttpPost("monitor-ext-to-talk")]
- public async Task MonitorExtToTalk([FromBody] MonitorExtToTalkRequest request)
- {
- await _deviceManager.MonitorExtToTalkAsync(request.telNo, HttpContext.RequestAborted);
- }
- /// <summary>
- /// 从插播到监听状态变换
- /// 1. 已知:分机 A 在插播分机 B 的通话;
- /// 2. 执行分机 A 的从插播到监听状态变换的 API;
- /// 3. 执行成功时,分机 A 监听分机 B 及其原通话方的通话。
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- [Permission(EPermission.MonitorExtToListen)]
- [HttpPost("monitor-ext-to-listen")]
- public async Task MonitorExtToListen([FromBody] MonitorExtToListenRequest request)
- {
- await _deviceManager.MonitorExtToListenAsync(request.telNo, HttpContext.RequestAborted);
- }
- #endregion
- #region 强插
- /// <summary>
- /// 强插
- /// 1. 已知:分机 A 当前空闲,分机 B 正在通话中;
- /// 2. 执行分机 A 强插分机 B 的 API;
- /// 3. 执行成功时,分机 A 振铃,摘机后即可形成三方通话。
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- [Permission(EPermission.BargeinExt)]
- [HttpPost("bargein-ext")]
- public async Task BargeinExt([FromBody] BargeinExtRequest request)
- {
- await _deviceManager.BargeinExtAsync(request.firstTelNo, request.secondTelNo, HttpContext.RequestAborted);
- }
- #endregion
- #region 强拆
- /// <summary>
- /// 强拆分机
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- [Permission(EPermission.ClearExt)]
- [HttpPost("clear-ext")]
- public async Task ClearExt([FromBody] ClearExtRequest request)
- {
- await _deviceManager.ClearExtAsync(request.ExtId, HttpContext.RequestAborted);
- }
- /// <summary>
- /// 强拆来电
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- [Permission(EPermission.ClearVisitor)]
- [HttpPost("clear-visitor")]
- public async Task ClearVisitor([FromBody] ClearVisitorRequest request)
- {
- //查询当前通话记录
- var call = await _callRepository.GetAsync(request.CallId, HttpContext.RequestAborted);
- if (call is null)
- throw UserFriendlyException.SameMessage("无效通话,无法挂断");
- if (call.CallStatus == ECallStatus.Bye)
- throw UserFriendlyException.SameMessage("通话已结束");
- await _deviceManager.ClearVisitorAsync(call.ConversationId, HttpContext.RequestAborted);
- }
- /// <summary>
- /// 强拆去电
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- [Permission(EPermission.ClearOuter)]
- [HttpPost("clear-outer")]
- public async Task ClearOuter([FromBody] ClearOuterRequest request)
- {
- //查询当前通话记录
- var call = await _callRepository.GetAsync(request.CallId, HttpContext.RequestAborted);
- if (call is null)
- throw UserFriendlyException.SameMessage("无效通话,无法挂断");
- if (call.CallStatus == ECallStatus.Bye)
- throw UserFriendlyException.SameMessage("通话已结束");
- await _deviceManager.ClearOuterAsync(call.ConversationId, HttpContext.RequestAborted);
- }
- #endregion
- #region 静音和取消静音
- /// <summary>
- /// 静音
- /// </summary>
- /// <returns></returns>
- [Permission(EPermission.Mute)]
- [HttpGet("mute")]
- public async Task Mute()
- {
- var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
- if (work is null)
- throw UserFriendlyException.SameMessage("当前坐席暂未进行工作");
- await _deviceManager.MuteAsync(work.TelNo, HttpContext.RequestAborted);
- }
- /// <summary>
- /// 取消静音
- /// </summary>
- /// <returns></returns>
- [Permission(EPermission.UnMute)]
- [HttpGet("unmute")]
- public async Task UnMute()
- {
- var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
- if (work is null)
- throw UserFriendlyException.SameMessage("当前坐席暂未进行工作");
- await _deviceManager.UnMuteAsync(work.TelNo, HttpContext.RequestAborted);
- }
- #endregion
- #endregion
- }
- }
|