using Hotline.Caching.Interfaces; using Hotline.Repository.SqlSugar.Extensions; using Hotline.CallCenter.Calls; using Hotline.Repository.SqlSugar.CallCenter; using Hotline.Settings; using Hotline.Share.Dtos.CallCenter; using Hotline.Share.Dtos.TrCallCenter; using Hotline.Share.Enums.CallCenter; using Hotline.Share.Requests; using SqlSugar; using XF.Domain.Exceptions; using Hotline.Orders; using Hotline.Share.Enums.User; using Hotline.Users; using XF.Domain.Repository; using Hotline.CallCenter.Tels; using Hotline.Share.Dtos; using Hotline.Share.Tools; using JiebaNet.Segmenter.Common; using XingTang.Sdk; using Mapster; namespace Hotline.Application.StatisticalReport.CallReport; public abstract class CallReportApplicationBase : ICallReportApplication { private readonly ISystemSettingCacheManager _systemSettingCacheManager; private readonly ISystemDicDataCacheManager _systemDicDataCacheManager; private readonly ICallNativeRepository _callNativeRepository; private readonly IRepository _userRepository; private readonly IRepository _workRepository; private readonly IRepository _telRestRepository; protected CallReportApplicationBase(ISystemSettingCacheManager systemSettingCacheManager, ICallNativeRepository callNativeRepository, IRepository userRepository, IRepository workRepository, IRepository telRestRepository, ISystemDicDataCacheManager systemDicDataCacheManager) { _systemSettingCacheManager = systemSettingCacheManager; _callNativeRepository = callNativeRepository; _userRepository = userRepository; _workRepository = workRepository; _telRestRepository = telRestRepository; _systemDicDataCacheManager = systemDicDataCacheManager; } public virtual async Task> GetCallHotLineListAsync(BiQueryGateWayDto dto, CancellationToken requestAborted) { int noConnectByeTimes = _systemSettingCacheManager.NoConnectByeTimes; int effectiveTimes = _systemSettingCacheManager.EffectiveTimes; int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes; int ringTimes = _systemSettingCacheManager.RingTimes; var hotlines = _systemDicDataCacheManager.CallForwardingSource.Select(m => m.DicDataValue).ToList(); return await _callNativeRepository.GetCallHotLineListAsync(dto, noConnectByeTimes, effectiveTimes, connectByeTimes, ringTimes, hotlines); } public virtual async Task> GetCallHourListAsync(BiQueryHourCallDto dto, CancellationToken requestAborted) { int noConnectByeTimes = _systemSettingCacheManager.NoConnectByeTimes; int effectiveTimes = _systemSettingCacheManager.EffectiveTimes; int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes; return await _callNativeRepository.GetCallHourList(dto.StartTime, dto.EndTime, noConnectByeTimes, effectiveTimes, connectByeTimes, dto.Source); } public virtual async Task> GetCallListAsync(QueryCallListDto dto, CancellationToken requestAborted) { int noConnectByeTimes = _systemSettingCacheManager.NoConnectByeTimes; int effectiveTimes = _systemSettingCacheManager.EffectiveTimes; int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes; return await _callNativeRepository.GetCallList(dto, noConnectByeTimes, effectiveTimes, connectByeTimes); } public virtual async Task> QueryCallsAsync(BiQueryCallsDto dto, CancellationToken cancellationToken) { return await _callNativeRepository.GetQueryCalls(dto.StartTime.Value, dto.EndTime.Value, dto.Line); } /// /// 话务日期明细 /// /// /// public virtual async Task> QueryCallsDetailAsync(BiQueryCallsDto dto) { //超时接通量 int CallInOverConnRingTime = _systemSettingCacheManager.CallInOverConnRingTime; //坐席超时挂断时间 int SeatChaoTime = _systemSettingCacheManager.SeatChaoTime; //未接秒挂时间 int noConnectByeTimes = _systemSettingCacheManager.NoConnectByeTimes; //呼入有效时间 int effectiveTimes = _systemSettingCacheManager.EffectiveTimes; //接通秒挂时间 int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes; var callData = await _callNativeRepository.Queryable() .Where(p => p.BeginIvrTime >= dto.StartTime && p.BeginIvrTime <= dto.EndTime && p.CallState != ECallState.Invalid) .WhereIF(!string.IsNullOrEmpty(dto.Keyword), p => p.ToNo == dto.Keyword) .GroupBy(p => p.BeginIvrTime.Value.ToString("yyyy-MM-dd")) .Select(p => new QueryCallsDetailDto { Date = p.BeginIvrTime.Value.ToString("yyyy-MM-dd"), InTotal = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In, 1, 0)),//呼入总量 InConnectionQuantity = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.AnsweredTime != null, 1, 0)),//呼入接通量 NotAcceptedHang = SqlFunc.AggregateSum(SqlFunc.IIF(p.RingDuration <= noConnectByeTimes && p.RingDuration > 0 && p.Direction == ECallDirection.In, 1, 0)), //未接通秒挂 TotalDurationIncomingCalls = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.AnsweredTime != null, p.Duration, 0)), //呼入总时长 InAvailableAnswer = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.AnsweredTime != null && p.Duration >= effectiveTimes, 1, 0)),//有效接通量 InHangupImmediateWhenAnswered = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.Duration > 0 && p.Duration <= connectByeTimes, 1, 0)), //呼入接通秒挂 TimeoutConnection = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.AnsweredTime != null && p.RingDuration >= CallInOverConnRingTime, 1, 0)),//超时接通量 TimeoutSuspension = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.AnsweredTime != null && p.Duration >= SeatChaoTime, 1, 0)),//超时挂断量 QueueByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.CallState == ECallState.NotAcceptedHang, 1, 0)), //队列挂断 IvrByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.CallState == ECallState.IVRNoAccept, 1, 0)), //IVR挂断 OutTotal = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.Out, 1, 0)),//呼出总量 OutConnectionQuantity = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.Out && p.AnsweredTime != null, 1, 0)) }) .OrderBy(p => p.Date) .ToListAsync(); return callData; } /// /// 话务日期明细-呼入总量/接通总量 /// /// /// public virtual async Task<(int, List)> QueryCallsDetailInTotalAsync(BiQueryCallsDto dto, bool isAll) { var recordPrefix = _systemSettingCacheManager.RecordPrefix; var query = _callNativeRepository.Queryable(includeDeleted: true) .LeftJoin((p, o) => p.Id == o.CallId) .Where((p, o) => p.BeginIvrTime >= dto.StartTime && p.BeginIvrTime <= dto.EndTime && p.Direction == ECallDirection.In && p.CallState != ECallState.Invalid) .WhereIF(dto.TypeCode == "2", (p, o) => p.AnsweredTime != null) .WhereIF(!string.IsNullOrEmpty(dto.Keyword), (p, o) => p.TelNo == dto.Keyword) .OrderByDescending((p, o) => p.BeginIvrTime) .Select((p, o) => new CallRecordOutDto { OtherAccept = p.Id, OrderId = o.Id, OrderNo = o.No, OrderTitle = o.Title, Cdpn = p.ToNo, Cpn = p.FromNo, RecordingFileUrl = recordPrefix + p.AudioFile, RecordingFileName = p.AudioFile, RecordingBaseAddress = recordPrefix, RecordingAbsolutePath = p.AudioFile }, true); if (isAll) { return (0, await query.ToListAsync()); } return await query.ToPagedListAsync(dto.PageIndex, dto.PageSize); } public virtual async Task> QueryCallsDetailStatisticsAsync(StartEndTimeDto dto, CancellationToken cancellationToken) { //超时接通量 int CallInOverConnRingTime = _systemSettingCacheManager.CallInOverConnRingTime; //坐席超时挂断时间 int SeatChaoTime = _systemSettingCacheManager.SeatChaoTime; //未接秒挂时间 int noConnectByeTimes = _systemSettingCacheManager.NoConnectByeTimes; //呼入有效时间 int effectiveTimes = _systemSettingCacheManager.EffectiveTimes; //接通秒挂时间 int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes; var callData = await _callNativeRepository.Queryable() .Where(p => p.BeginIvrTime >= dto.StartTime && p.BeginIvrTime <= dto.EndTime && p.CallState != ECallState.Invalid) .GroupBy(p => p.BeginIvrTime.Value.ToString("yyyy-MM-dd")) .Select(p => new QueryCallsDetailStatistics { Date = p.BeginIvrTime.Value.ToString("yyyy-MM-dd"), //InTotal = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In, 1, 0)),//呼入总量 InConnectionQuantity = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.AnsweredTime != null, 1, 0)),//呼入接通量 NotAcceptedHang = SqlFunc.AggregateSum(SqlFunc.IIF(p.CallState == ECallState.NotAcceptedHang && p.Direction == ECallDirection.In, 1, 0)), //呼入队列挂断 InNotAnswered = SqlFunc.AggregateSum(SqlFunc.IIF(p.Duration == 0 && p.TelNo != "0" && p.Direction == ECallDirection.In, 1, 0)), // 挂机量 IvrByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.CallState == ECallState.IVRNoAccept && p.AnsweredTime == null, 1, 0)), //IVR挂断 OutConnectionQuantity = SqlFunc.AggregateSum(SqlFunc.IIF(p.AnsweredTime != null && p.Direction == ECallDirection.Out, 1, 0)), // 呼出接通量 OutNotAnswered = SqlFunc.AggregateSum(SqlFunc.IIF(p.AnsweredTime == null && p.Direction == ECallDirection.Out, 1, 0)), // 呼出未接量 }) .OrderBy(p => p.Date) .ToListAsync(cancellationToken); return callData; } public virtual async Task> QueryCallsHourDetailAsync(BiQueryCallsDto dto, CancellationToken cancellationToken) { //超时接通量 int CallInOverConnRingTime = _systemSettingCacheManager.CallInOverConnRingTime; //坐席超时挂断时间 int SeatChaoTime = _systemSettingCacheManager.SeatChaoTime; //未接秒挂时间 int noConnectByeTimes = _systemSettingCacheManager.NoConnectByeTimes; //呼入有效时间 int effectiveTimes = _systemSettingCacheManager.EffectiveTimes; //接通秒挂时间 int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes; return await _callNativeRepository.QueryCallsHourDetail(dto.StartTime.Value, dto.EndTime.Value, noConnectByeTimes, effectiveTimes, connectByeTimes, CallInOverConnRingTime, SeatChaoTime, dto.Line); } public virtual async Task<(int, List)> QueryCallsStatisticsDetailAsync(QueryCallsStatisticsDetailInDto dto, CancellationToken cancellationToken) { dto.FieldName ??= string.Empty; dto.FieldName = dto.FieldName.ToLower(); //超时接通量 int CallInOverConnRingTime = _systemSettingCacheManager.CallInOverConnRingTime; //坐席超时挂断时间 int SeatChaoTime = _systemSettingCacheManager.SeatChaoTime; //未接秒挂时间 int noConnectByeTimes = _systemSettingCacheManager.NoConnectByeTimes; //呼入有效时间 int effectiveTimes = _systemSettingCacheManager.EffectiveTimes; //接通秒挂时间 int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes; var query = _callNativeRepository.Queryable(includeDeleted: true) .LeftJoin((c, o) => c.Id == o.CallId) .Where((c, o) => c.CallState != ECallState.Invalid) .WhereIF(dto.OrderNo.NotNullOrEmpty(), (c, o) => o.No == dto.OrderNo) .WhereIF(dto.FromNo.NotNullOrEmpty(), (c, o) => c.FromNo == dto.FromNo) .WhereIF(dto.ToNo.NotNullOrEmpty(), (c, o) => c.ToNo == dto.ToNo) .WhereIF(dto.TelNo.NotNullOrEmpty(), (c, o) => c.TelNo == dto.TelNo) .WhereIF(dto.EndBy.IsNotNull(), (c, o) => c.EndBy == dto.EndBy) .Where((c, o) => c.BeginIvrTime >= dto.StartTime && c.BeginIvrTime <= dto.EndTime); if (dto.FieldName == "intotal") // 呼入总量 query = query.Where((c, o) => c.Direction == ECallDirection.In); if (dto.FieldName == "inconnectionquantity") // 呼入接通 query = query.Where((c, o) => c.Direction == ECallDirection.In && c.AnsweredTime != null); if (dto.FieldName == "innotanswered") // 挂机量 query = query.Where((c, o) => c.Direction == ECallDirection.In && c.Duration ==0 && c.TelNo != "0"); if (dto.FieldName == "notacceptedhang") // 呼入队列挂断 query = query.Where((c, o) => c.Duration == 0 && c.RingDuration <= noConnectByeTimes && c.RingDuration > 0 && c.Direction == ECallDirection.In); if (dto.FieldName == "ivrbyecount") // 呼入IVR挂断 query = query.Where((c, o) => c.Direction == ECallDirection.In && c.BeginIvrTime.HasValue && !c.BeginQueueTime.HasValue && !c.BeginRingTime.HasValue && c.AnsweredTime == null); if (dto.FieldName == "outconnectionquantity") // 呼出接通量 query = query.Where((c, o) => c.AnsweredTime != null && c.Direction == ECallDirection.Out); if (dto.FieldName == "outnotanswered") // 呼出未接通 query = query.Where((c, o) => c.AnsweredTime == null && c.Direction == ECallDirection.Out); return await query .Select() .ToPagedListAsync(dto.PageIndex, dto.PageSize, cancellationToken); } /// /// 坐席话务统计分析 /// /// /// public virtual async Task> QuerySeatCallAsync(ReportRequiredPagedRequest dto, CancellationToken cancellationToken) { int noConnectByeTimes = _systemSettingCacheManager.NoConnectByeTimes; int effectiveTimes = _systemSettingCacheManager.EffectiveTimes; int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes; var list = await _userRepository.Queryable() .LeftJoin((u, c) => u.Id == c.UserId) .Where((u, c) => c.CallState != ECallState.Invalid) .Where(u => !u.IsDeleted && u.UserType == EUserType.Seat) .Where((u, c) => c.BeginIvrTime >= dto.StartTime) .Where((u, c) => c.BeginIvrTime <= dto.EndTime) .GroupBy((u, c) => new { c.UserName, c.UserId }) .Select((u, c) => new BiSeatCallsDto { Name = c.UserName, UserId = c.UserId, InTotal = SqlFunc.AggregateSum(SqlFunc.IIF(c.Direction == ECallDirection.In, 1, 0)), OutTotal = SqlFunc.AggregateSum(SqlFunc.IIF(c.Direction == ECallDirection.Out, 1, 0)), InAnswered = SqlFunc.AggregateSum(SqlFunc.IIF(c.Direction == ECallDirection.In && c.AnsweredTime != null, 1, 0)), // 呼入接通量 OutAnswered = SqlFunc.AggregateSum(SqlFunc.IIF(c.Direction == ECallDirection.Out && c.AnsweredTime != null, 1, 0)), // 呼出接通量 InHangupImmediate = SqlFunc.AggregateSum(SqlFunc.IIF(c.Direction == ECallDirection.In && c.AnsweredTime == null && c.RingDuration < noConnectByeTimes, 1, 0)), // 呼入秒挂 InHanguped = SqlFunc.AggregateSum(SqlFunc.IIF(c.Direction == ECallDirection.In && c.CallState == ECallState.Missed, 1, 0)), //呼入未接 InDurationAvg = SqlFunc.Ceil(SqlFunc.AggregateAvg(SqlFunc.IIF(c.Direction == ECallDirection.In && c.AnsweredTime != null, c.Duration, 0))), // 呼入平均时长 OutDurationAvg = SqlFunc.Ceil(SqlFunc.AggregateAvg(SqlFunc.IIF(c.Direction == ECallDirection.Out && c.AnsweredTime != null, c.Duration, 0))), // 呼出平均时长 InAvailableAnswer = SqlFunc.AggregateSum(SqlFunc.IIF(c.Direction == ECallDirection.In && c.AnsweredTime != null && c.Duration >= effectiveTimes, 1, 0)), // 有效接通量 InHangupImmediateWhenAnswered = SqlFunc.AggregateSum(SqlFunc.IIF(c.Direction == ECallDirection.In && c.AnsweredTime != null && c.Duration < connectByeTimes, 1, 0)), // 呼入接通秒挂 }) .MergeTable() .ToListAsync(); list.ForEach(d => { d.LoginDuration = _workRepository.Queryable().Where(q => q.UserId == d.UserId && q.CreationTime >= dto.StartTime && q.CreationTime <= dto.EndTime).Sum(q => q.WorkingDuration); if (d.LoginDuration != null) { d.LoginDuration = Math.Round(d.LoginDuration.Value, digits: 2); } d.RestDuration = _telRestRepository.Queryable().Where(q => q.UserId == d.UserId && q.CreationTime >= dto.StartTime && q.CreationTime <= dto.EndTime).Sum(q => q.RestDuration); d.RestDuration = Math.Round(d.RestDuration, digits: 2); }); return list; } public virtual async Task<(int, List)> QuerySeatSwitchAsync(QuerySeatSwitchRequest dto, CancellationToken cancellationToken) { return await _callNativeRepository.Queryable() .Where(x => !string.IsNullOrEmpty(x.AgentTransferNumber) && x.CallState != ECallState.Invalid) .WhereIF(!string.IsNullOrEmpty(dto.UserName), x => x.UserName.Contains(dto.UserName)) .WhereIF(!string.IsNullOrEmpty(dto.CDPN), x => x.ToNo.Contains(dto.CDPN)) .Where(x => x.BeginIvrTime >= dto.StartTime) .Where(x => x.BeginIvrTime <= dto.EndTime) .Select(x => new BiSeatSwitchDto { UserId = x.UserId, CPN = x.FromNo, CDPN = x.ToNo, CreatedTime = x.CreationTime, TelNo = x.TelNo, UserName = x.UserName, }) .OrderByDescending(x => x.CreatedTime) .ToPagedListAsync(dto.PageIndex, dto.PageSize, cancellationToken); } }