using Hotline.Caching.Interfaces; using Hotline.CallCenter.BlackLists; using Hotline.CallCenter.Calls; using Hotline.CallCenter.Tels; using Hotline.Orders; using Hotline.Repository.SqlSugar.Extensions; using Hotline.Share.Dtos; using Hotline.Share.Dtos.CallCenter; using Hotline.Share.Dtos.TrCallCenter; using Hotline.Share.Enums.CallCenter; using Hotline.Users; using MapsterMapper; using Microsoft.Extensions.Logging; using SqlSugar; using XF.Domain.Authentications; using XF.Domain.Cache; using XF.Domain.Exceptions; using XF.Domain.Repository; namespace Hotline.Application.CallCenter; public abstract class DefaultCallApplication : ICallApplication { private readonly IRepository _telRepository; private readonly IRepository _telGroupRepository; private readonly IWorkRepository _workRepository; private readonly ITelRestRepository _telRestRepository; private readonly IRepository _callNativeRepository; private readonly IRepository _telOperationRepository; private readonly IRepository _callIdRelationRepository; private readonly ITypedCache _cacheWork; private readonly IUserCacheManager _userCacheManager; private readonly ISessionContext _sessionContext; private readonly IMapper _mapper; private readonly ILogger _logger; public DefaultCallApplication( IRepository telRepository, IRepository telGroupRepository, IWorkRepository workRepository, ITelRestRepository telRestRepository, IRepository callNativeRepository, IRepository telOperationRepository, IRepository callIdRelationRepository, ITypedCache cacheWork, IUserCacheManager userCacheManager, ISessionContext sessionContext, IMapper mapper, ILogger logger) { _telRepository = telRepository; _telGroupRepository = telGroupRepository; _workRepository = workRepository; _telRestRepository = telRestRepository; _callNativeRepository = callNativeRepository; _telOperationRepository = telOperationRepository; _callIdRelationRepository = callIdRelationRepository; _cacheWork = cacheWork; _userCacheManager = userCacheManager; _sessionContext = sessionContext; _mapper = mapper; _logger = logger; } public DefaultCallApplication() { } /// /// 查询分机 /// public virtual async Task> QueryTelsAsync(CancellationToken cancellationToken) { return _mapper.Map>(await _telRepository.Queryable() .Includes(x => x.Groups) .ToListAsync(cancellationToken)); } /// /// 查询分机组 /// public virtual async Task> QueryTelGroupsAsync(CancellationToken cancellationToken) { return await _telGroupRepository.Queryable() .Select() .ToListAsync(cancellationToken); } /// /// 新增黑名单 /// public abstract Task AddBlackListAsync(AddBlacklistDto dto, CancellationToken cancellationToken); /// /// 删除黑名单 /// public abstract Task RemoveBlackListAsync(string id, CancellationToken cancellationToken); /// /// 查询黑名单 /// public abstract Task> QueryBlackListsAsync(CancellationToken cancellationToken); /// /// 签入 /// public virtual async Task SignInAsync(SignInDto dto, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(dto.TelNo)) throw UserFriendlyException.SameMessage("无效分机号"); var work = _userCacheManager.GetWorkByUserNoExp(_sessionContext.RequiredUserId); if (work is not null) { //if (work.TelNo != dto.TelNo) //{ // throw UserFriendlyException.SameMessage("当前用户已签入其他分机"); //} throw UserFriendlyException.SameMessage("当前用户已签入"); } var telWork = _userCacheManager.GetWorkByTelNoExp(dto.TelNo); if (telWork is not null) { throw UserFriendlyException.SameMessage("当前分机已被占用"); } work = new Work(_sessionContext.RequiredUserId, _sessionContext.UserName, dto.TelNo, dto.TelNo, null, null, dto.GroupId, _sessionContext.StaffNo, null); await _workRepository.AddAsync(work, cancellationToken); return new TrOnDutyResponseDto { TelNo = dto.TelNo, QueueId = dto.GroupId, StartTime = work.StartTime, }; } /// /// 签出 /// public virtual async Task SingOutAsync(CancellationToken cancellationToken) { var work = _userCacheManager.GetWorkByUserNoExp(_sessionContext.RequiredUserId); if (work is null) return; var telRest = await _telRestRepository.GetAsync(x => x.TelNo == work.TelNo && !x.EndTime.HasValue, cancellationToken); if (telRest is not null) { telRest.EndRest(); await _telRestRepository.UpdateAsync(telRest, cancellationToken); } work.OffDuty(); await _workRepository.UpdateAsync(work, cancellationToken); await _cacheWork.RemoveAsync(work.GetKey(KeyMode.UserId), cancellationToken); await _cacheWork.RemoveAsync(work.GetKey(KeyMode.TelNo), cancellationToken); } /// /// 签出 /// public virtual async Task SingOutAsync(string telNo, CancellationToken cancellationToken) { var work = _userCacheManager.GetWorkByTelNoExp(telNo); if (work is null) return; var telRest = await _telRestRepository.GetAsync(x => x.TelNo == work.TelNo && !x.EndTime.HasValue, cancellationToken); if (telRest is not null) { telRest.EndRest(); await _telRestRepository.UpdateAsync(telRest, cancellationToken); } work.OffDuty(); await _workRepository.UpdateAsync(work, cancellationToken); await _cacheWork.RemoveAsync(work.GetKey(KeyMode.UserId), cancellationToken); await _cacheWork.RemoveAsync(work.GetKey(KeyMode.TelNo), cancellationToken); } /// /// 查询当前用户的分机状态 /// /// /// public virtual async Task GetTelStateAsync(CancellationToken cancellationToken) { var work = _userCacheManager.GetWorkByUserNoExp(_sessionContext.RequiredUserId); if (work is null) return null; return await Task.FromResult(new TrOnDutyResponseDto { TelNo = work.TelNo, QueueId = work.QueueId, StartTime = work.StartTime, StaffNo = work.StaffNo }); } /// /// 定量查询通话记录 /// public virtual async Task> QueryCallsFixedAsync(QueryCallsFixedDto dto, CancellationToken cancellationToken) { var query = _callNativeRepository.Queryable(includeDeleted: true) .LeftJoin((d, o) => d.Id == o.CallId) .Where((d, o) => d.GroupId == "1") .WhereIF(!string.IsNullOrEmpty(dto.OrderNo), (d, o) => o.No == dto.OrderNo) .WhereIF(!string.IsNullOrEmpty(dto.FromNo), d => d.FromNo == dto.FromNo) .WhereIF(!string.IsNullOrEmpty(dto.ToNo), d => d.ToNo == dto.ToNo) .WhereIF(!string.IsNullOrEmpty(dto.UserName), d => d.UserName == dto.UserName) .WhereIF(!string.IsNullOrEmpty(dto.TelNo), d => d.TelNo == dto.TelNo) .WhereIF(dto.EndBy != null, d => d.EndBy == dto.EndBy) .WhereIF(dto.CallStartTimeStart != null, d => d.BeginIvrTime >= dto.CallStartTimeStart) .WhereIF(dto.CallStartTimeEnd != null, d => d.BeginIvrTime <= dto.CallStartTimeEnd) .WhereIF(dto.IsConnected != null, d => d.AnsweredTime != null) .WhereIF(dto.Direction != null, d => d.Direction == dto.Direction) .WhereIF(dto.WaitDurationStart != null && dto.WaitDurationStart > 0, d => d.WaitDuration >= dto.WaitDurationStart) .WhereIF(dto.WaitDurationEnd != null && dto.WaitDurationEnd > 0, d => d.WaitDuration <= dto.WaitDurationEnd) .WhereIF(dto.IsMissOrder != null && dto.IsMissOrder.Value == true, (d, o) => string.IsNullOrEmpty(o.Id) == true) .WhereIF(dto.IsMissOrder != null && dto.IsMissOrder.Value == false, (d, o) => string.IsNullOrEmpty(o.Id) == false) .OrderByDescending(d => d.Id); query = query.WhereIF(dto.Type == 3, (d, o) => d.AnsweredTime == null); query = query.WhereIF(dto.Type == 1, (d, o) => d.Direction == ECallDirection.In); query = query.WhereIF(dto.Type == 2, (d, o) => d.Direction == ECallDirection.Out); var items = await query.Select((d, o) => new CallNativeDto { OrderId = o.Id, OrderNo = o.No, Title = o.Title, Gateway = SqlFunc.Subqueryable() .Where(m => m.GroupId == "0" && m.CallNo == d.CallNo) .Select(m => m.ToNo) }, true) .ToFixedListAsync(dto, cancellationToken); items.Where(m => m.Gateway != null) .ToList().ForEach(m => { var toNo = m.Gateway; m.Gateway = m.ToNo; m.ToNo = toNo; }); return items; } /// /// 查询分机操作记录(定量) /// public virtual async Task> QueryTelOperationsAsync(QueryTelOperationsFixedDto dto, CancellationToken cancellationToken) { return await _telOperationRepository.Queryable() .WhereIF(!string.IsNullOrEmpty(dto.UserName), d => d.UserName == dto.UserName) .WhereIF(!string.IsNullOrEmpty(dto.StaffNo), d => d.StaffNo == dto.StaffNo) .WhereIF(!string.IsNullOrEmpty(dto.GroupId), d => d.GroupId == dto.GroupId) .WhereIF(dto.OperateState != null, d => d.OperateState == dto.OperateState) .OrderByDescending(d => d.Id) .ToFixedListAsync(dto, cancellationToken); } /// /// 依据通话记录编号获取映射后的callId /// public virtual async Task GetOrSetCallIdAsync(string callNo, CancellationToken cancellationToken) { var callOrder = await _callIdRelationRepository.GetAsync(callNo, cancellationToken); if (callOrder == null) { callOrder = new CallidRelation { Id = callNo, CallId = Ulid.NewUlid().ToString(), }; try { await _callIdRelationRepository.AddAsync(callOrder, cancellationToken); } catch (Exception e) { _logger.LogError($"写入callidRelation失败:{e.Message}"); return await GetOrSetCallIdAsync(callNo, cancellationToken); } } return callOrder.CallId; } public async Task GetRelationAsync(string callNo, CancellationToken cancellation) { return await _callIdRelationRepository.GetAsync(callNo, cancellation); } public async Task AddRelationAsync(CallidRelation relation, CancellationToken cancellation) { await _callIdRelationRepository.AddAsync(relation, cancellation); } /// /// 乐观并发更新映射关系 /// public virtual async Task UpdateRelationOptLockAsync(CallidRelation relation, CancellationToken cancellationToken) { return await _callIdRelationRepository.Updateable(relation).ExecuteCommandWithOptLockAsync(); } /// /// 查询通话记录 /// public virtual async Task GetCallAsync(string callId, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(callId)) return null; return await _callNativeRepository.GetAsync(callId, cancellationToken); } /// /// 查询通话记录列表 /// /// /// /// public virtual async Task> GetCallListAsync(string callId, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(callId)) return null; return await _callNativeRepository.Queryable().Where(x => x.Id == callId).ToListAsync(cancellationToken); } /// /// 查询通话记录 /// public virtual async Task> QueryCallsAsync( string? phone, ECallDirection? direction, DateTime? callStartTimeStart, DateTime? callStartTimeEnd, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(phone)) return new List(); return await _callNativeRepository.Queryable() .WhereIF(direction.HasValue, d => d.Direction == direction) .WhereIF(callStartTimeStart != null, d => d.BeginIvrTime >= callStartTimeStart) .WhereIF(callStartTimeEnd != null, d => d.BeginIvrTime <= callStartTimeEnd) .Where(d => d.FromNo == phone || d.ToNo == phone) .OrderBy(d => d.CreationTime) .ToListAsync(cancellationToken); } #region tianrun 临时方案 public virtual Task GetTianrunCallAsync(string callId, CancellationToken cancellationToken) { throw new NotImplementedException(); } /// /// 根据转写ID获取通话信息 /// /// public virtual async Task GetTianrunCallTransliterationAsync(string transliterationId, CancellationToken cancellationToken) { throw new NotImplementedException(); } public virtual async Task EditTransliterationStateAsync(string callId, ECallTransliterationState state, string transliterationId, CancellationToken cancellationToken) { throw new NotImplementedException(); } /// /// 关联通话记录与order(添润) /// public virtual Task RelateTianrunCallWithOrderAsync(string callId, string orderId, CancellationToken cancellationToken) { throw new NotImplementedException(); } /// /// 查询通话记录 /// public virtual Task> QueryTianrunCallsAsync( string? phone = null, ECallDirection? direction = null, DateTime? callStartTimeStart = null, DateTime? callStartTimeEnd = null, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } /// /// 查询分机操作选项 /// /// public abstract List GetTelOperationOptions(); #endregion }