Parcourir la source

Merge branch 'fix/bug_call' into release

qinchaoyue il y a 6 mois
Parent
commit
6d1b9a1c7a

+ 9 - 4
src/Hotline.Api/Controllers/Bi/BiCallController.cs

@@ -19,6 +19,7 @@ using SqlSugar;
 using System.Data;
 using System.Linq.Dynamic.Core;
 using XF.Domain.Repository;
+using Hotline.Settings;
 
 namespace Hotline.Api.Controllers.Bi;
 
@@ -482,10 +483,14 @@ public class BiCallController : BaseController
     /// <returns></returns>
     [HttpGet("hourcall_list_base")]
     public async Task<object> ReTransactBaseData()
-        => _baseDataApplication
-        .CallForwardingType()
-        .CallForwardingSource()
-        .Build();
+    {
+        var rsp = new
+        {
+            CallForwardingSource = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.CallForwardingSource),
+            CallForwardingType = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.CallForwardingType),
+        };
+        return rsp;
+    }
 
     /// <summary>
     /// 热线号码统计

+ 21 - 1
src/Hotline.Application/Jobs/XingTangCallsSyncJob.cs

@@ -1,4 +1,5 @@
 using Hotline.Application.CallCenter;
+using Hotline.Share.Tools;
 using Quartz;
 using SqlSugar;
 using Hotline.CallCenter.Calls;
@@ -84,12 +85,31 @@ namespace Hotline.Application.Jobs
                 foreach (var call in calls)
                 {
                     call.Id = await GetCallIdAsync(call.CallNo, context.CancellationToken);
-                    var user = users.FirstOrDefault(d => d.StaffNo == call.StaffNo);
+                    var user = users.FirstOrDefault(d => d.StaffNo == call.StaffNo && d.StaffNo != "0");
                     if (user is not null)
                     {
                         call.UserId = user.Id;
                         call.UserName = user.Name;
                     }
+
+                    if (call.RingDuration == 0)
+                    {
+                        if (call.BeginRingTime != null)
+                        {
+                            if (call.EndRingTime != null)
+                            {
+                                call.RingDuration = call.BeginRingTime.Value.GetDifferenceSeconds(call.EndRingTime.Value);
+                            }
+                            else
+                            {
+                                call.RingDuration = call.BeginRingTime.Value.GetDifferenceSeconds(call.EndTime);
+                            }
+                        }
+                    }
+                    if (call.GroupId == "0" && call.CallState != ECallState.IVRNoAccept)
+                    {
+                        call.CallState = ECallState.Invalid;
+                    }
                 }
 
                 await _callRepository.AddRangeAsync(calls, context.CancellationToken);

+ 10 - 1
src/Hotline.Application/Mappers/CallMapperConfigs.cs

@@ -83,9 +83,18 @@ namespace Hotline.Application.Mappers
                 .Map(d => d.WaitDuration, s => s.WaitTime)
                 .Map(d => d.AudioFile, s => s.AudioFile)
                 .Map(d => d.AgentTransferNumber, s => s.TransCalled)
+                .AfterMapping((s, d) => {
+                    if (s.CallState == 0 || s.CallState == 4 || s.CallState == 5 || s.CallState == 8)
+                        d.CallState = ECallState.On;
+                    if (s.CallState == 1)
+                        d.CallState = ECallState.NotAcceptedHang;
+                    if (s.CallState == 2 || s.CallState == 3 || s.CallState == 7)
+                        d.CallState = ECallState.Missed;
+                    if (s.CallState == 6)
+                        d.CallState = ECallState.IVRNoAccept;
+                    })
                 .AfterMapping((s, d) =>
                 {
-                    //todo 等待兴唐补全Disposition字段
                     d.EndBy = d.Direction == ECallDirection.In
                         ? EEndBy.From
                         : EEndBy.To;

+ 33 - 30
src/Hotline.Application/StatisticalReport/CallReport/CallReportApplicationBase.cs

@@ -17,6 +17,8 @@ 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;
 
@@ -86,25 +88,24 @@ public abstract class CallReportApplicationBase : ICallReportApplication
         int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes;
 
         var callData = await _callNativeRepository.Queryable()
-                .Where(p => p.CreationTime >= dto.StartTime && p.CreationTime <= dto.EndTime)
-                 // .Where(p => p.Gateway != "82826886" && SqlFunc.Length(p.Gateway) != 4)
+                .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.CreationTime.ToString("yyyy-MM-dd"))
+                 .GroupBy(p => p.BeginIvrTime.Value.ToString("yyyy-MM-dd"))
                 .Select(p => new QueryCallsDetailDto
                 {
-                    Date = p.CreationTime.ToString("yyyy-MM-dd"),
+                    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.Duration == 0 && p.RingDuration <= noConnectByeTimes && p.RingDuration > 0 && p.Direction == ECallDirection.In, 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.WaitDuration > 0 && p.RingDuration == 0 && p.AnsweredTime == null, 1, 0)), //队列挂断
-                    IvrByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.BeginIvrTime.HasValue && !p.BeginQueueTime.HasValue && !p.BeginRingTime.HasValue && p.AnsweredTime == null, 1, 0)), //IVR挂断
+                    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.AnsweredTime != null && p.Direction == ECallDirection.Out && p.AnsweredTime != null, 1, 0))
+                    OutConnectionQuantity = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.Out && p.AnsweredTime != null, 1, 0))
                 })
                 .OrderBy(p => p.Date)
                 .ToListAsync();
@@ -120,12 +121,12 @@ public abstract class CallReportApplicationBase : ICallReportApplication
     public virtual async Task<(int, List<CallRecordOutDto>)> QueryCallsDetailInTotalAsync(BiQueryCallsDto dto, bool isAll)
     {
         var recordPrefix = _systemSettingCacheManager.RecordPrefix;
-        var query = _callNativeRepository.Queryable()
+        var query = _callNativeRepository.Queryable(includeDeleted: true)
                 .LeftJoin<Order>((p, o) => p.Id == o.CallId)
-                .Where((p, o) => p.CreationTime >= dto.StartTime && p.CreationTime <= dto.EndTime && p.Direction == ECallDirection.In)
+                .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.CreationTime)
+                 .OrderByDescending((p, o) => p.BeginIvrTime)
                  .Select((p, o) => new CallRecordOutDto
                  {
                      OtherAccept = p.Id,
@@ -161,16 +162,16 @@ public abstract class CallReportApplicationBase : ICallReportApplication
         int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes;
 
         var callData = await _callNativeRepository.Queryable()
-                .Where(p => p.CreationTime >= dto.StartTime && p.CreationTime <= dto.EndTime && p.GroupId != "0")
-                .GroupBy(p => p.CreationTime.ToString("yyyy-MM-dd"))
+                .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.CreationTime.ToString("yyyy-MM-dd"),
+                    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.Duration == 0 && p.RingDuration <= noConnectByeTimes && p.RingDuration > 0 && p.Direction == ECallDirection.In, 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.BeginIvrTime.HasValue && !p.BeginQueueTime.HasValue && !p.BeginRingTime.HasValue && p.AnsweredTime == null, 1, 0)), //IVR挂断
+                    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)), // 呼出未接量
                 })
@@ -211,12 +212,13 @@ public abstract class CallReportApplicationBase : ICallReportApplication
         int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes;
         var query = _callNativeRepository.Queryable(includeDeleted: true)
             .LeftJoin<Order>((c, o) => c.Id == o.CallId)
+            .Where(m => m.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.CreationTime >= dto.StartTime && c.CreationTime <= dto.EndTime);
+            .Where((c, o) => c.BeginIvrTime >= dto.StartTime && c.BeginIvrTime <= dto.EndTime);
 
         if (dto.FieldName == "intotal") // 呼入总量
             query = query.Where((c, o) => c.Direction == ECallDirection.In);
@@ -250,9 +252,10 @@ public abstract class CallReportApplicationBase : ICallReportApplication
 
         var list = await _userRepository.Queryable()
               .LeftJoin<CallNative>((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.CreationTime >= dto.StartTime)
-              .Where((u, c) => c.CreationTime <= dto.EndTime)
+              .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
               {
@@ -260,14 +263,14 @@ public abstract class CallReportApplicationBase : ICallReportApplication
                   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.AnsweredTime == null, 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)),
+                  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();
@@ -291,11 +294,11 @@ public abstract class CallReportApplicationBase : ICallReportApplication
     public virtual async Task<(int, List<BiSeatSwitchDto>)> QuerySeatSwitchAsync(QuerySeatSwitchRequest dto, CancellationToken cancellationToken)
     {
         return await _callNativeRepository.Queryable()
-            .Where(x => !string.IsNullOrEmpty(x.AgentTransferNumber))
+            .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.CreationTime >= dto.StartTime)
-            .Where(x => x.CreationTime <= dto.EndTime)
+            .Where(x => x.BeginIvrTime >= dto.StartTime)
+            .Where(x => x.BeginIvrTime <= dto.EndTime)
             .Select(x => new BiSeatSwitchDto
             {
                 UserId = x.UserId,

+ 32 - 25
src/Hotline.Repository.SqlSugar/CallCenter/CallNativeRepository.cs

@@ -37,7 +37,7 @@ public class CallNativeRepository : BaseRepository<CallNative>, ICallNativeRepos
         }
 
         var list = await Db.Reportable(dts).ToQueryable<DateTime>()
-            .LeftJoin<CallNative>((it, o) => o.CreationTime >= it.ColumnName && o.CreationTime < it.ColumnName.AddHours(1) && o.Direction == ECallDirection.In)
+            .LeftJoin<CallNative>((it, o) => o.BeginIvrTime >= it.ColumnName && o.BeginIvrTime < it.ColumnName.AddHours(1) && o.Direction == ECallDirection.In && o.CallState != ECallState.Invalid)
              //.Where((it, o) => o.CallDirection == ECallDirection.In)
              .WhereIF(!string.IsNullOrEmpty(source), (it, o) => o.ToNo == source)
             .GroupBy((it, o) => it.ColumnName)
@@ -45,11 +45,11 @@ public class CallNativeRepository : BaseRepository<CallNative>, ICallNativeRepos
             {
                 DateTimeTo = it.ColumnName,
                 Hour = it.ColumnName.Hour, //小时段
-                EffectiveCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.Duration >= effectiveTimes, 1, 0)),//有效接通
-                ConnectByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.Duration > 0 && o.Duration <= connectByeTimes, 1, 0)), //接通秒挂
-                NoConnectByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.Duration == 0 && o.RingDuration <= noConnectByeTimes && o.RingDuration > 0, 1, 0)), //未接通秒挂
-                QueueByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.WaitDuration > 0 && o.RingDuration == 0 && o.AnsweredTime == null, 1, 0)), //队列挂断
-                IvrByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.BeginIvrTime.HasValue && !o.BeginQueueTime.HasValue && !o.BeginRingTime.HasValue && o.AnsweredTime == null, 1, 0)), //IVR挂断
+                EffectiveCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.AnsweredTime != null, 1, 0)),//有效接通
+                ConnectByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.AnsweredTime != null && o.Duration < connectByeTimes, 1, 0)), //接通秒挂
+                NoConnectByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.RingDuration <= noConnectByeTimes && o.RingDuration > 0, 1, 0)), //未接通秒挂
+                QueueByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.CallState == ECallState.NotAcceptedHang, 1, 0)), //队列挂断
+                IvrByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.CallState == ECallState.IVRNoAccept, 1, 0)), //IVR挂断
             })
             .MergeTable()
             .OrderBy(x => x.Hour)
@@ -92,23 +92,22 @@ public class CallNativeRepository : BaseRepository<CallNative>, ICallNativeRepos
         var listHour = Db.Reportable(dts).ToQueryable<int>();
 
         var list = Db.Queryable<CallNative>()
-              .Where(p => p.CreationTime >= beginDate && p.CreationTime <= endDate)
-              // .Where(p => p.Gateway != "82826886" && SqlFunc.Length(p.Gateway) != 4)
+              .Where(p => p.BeginIvrTime >= beginDate && p.BeginIvrTime <= endDate && p.CallState != ECallState.Invalid)
               .WhereIF(!string.IsNullOrEmpty(Line), p => p.TelNo == Line)
-               .GroupBy(p => p.CreationTime.Hour)
+               .GroupBy(p => p.BeginIvrTime.Value.Hour)
                .Select(p => new
                {
-                   Hour = p.CreationTime.Hour, //小时段
+                   Hour = p.BeginIvrTime.Value.Hour, //小时段
                    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.Duration == 0 && p.RingDuration <= noConnectByeTimes && p.RingDuration > 0, 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.WaitDuration > 0 && p.RingDuration == 0 && p.AnsweredTime == null, 1, 0)), //队列挂断
-                   IvrByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.BeginIvrTime.HasValue && !p.BeginQueueTime.HasValue && !p.BeginRingTime.HasValue && p.AnsweredTime == null, 1, 0)), //IVR挂断
+                   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))
                })
@@ -137,6 +136,13 @@ public class CallNativeRepository : BaseRepository<CallNative>, ICallNativeRepos
         return listCall;
     }
 
+    /// <summary>
+    /// 话务统计分析
+    /// </summary>
+    /// <param name="beginDate"></param>
+    /// <param name="endDate"></param>
+    /// <param name="Line"></param>
+    /// <returns></returns>
     public async Task<List<BiCallDto>> GetQueryCalls(DateTime beginDate, DateTime endDate, string? Line)
     {
         List<int> dts = new List<int>();
@@ -148,15 +154,16 @@ public class CallNativeRepository : BaseRepository<CallNative>, ICallNativeRepos
         var listHour = Db.Reportable(dts).ToQueryable<int>();
 
         var list = Db.Queryable<CallNative>()
-             .Where(p => p.CreationTime >= beginDate && p.CreationTime <= endDate && p.Direction == ECallDirection.In)
+             .Where(p => p.BeginIvrTime >= beginDate && p.BeginIvrTime <= endDate && p.Direction == ECallDirection.In && p.CallState != ECallState.Invalid)
              .WhereIF(!string.IsNullOrEmpty(Line), p => p.ToNo == Line)
-           .GroupBy(p => p.CreationTime.Hour)
+           .GroupBy(p => p.BeginIvrTime.Value.Hour)
            .Select(p => new
            {
-               Hour = p.CreationTime.Hour, //小时段
+               Hour = p.BeginIvrTime.Value.Hour, //小时段
                Total = SqlFunc.AggregateCount(p.Id),
                Answered = SqlFunc.AggregateSum(SqlFunc.IIF(p.AnsweredTime != null, 1, 0)), //应答数
-               Hanguped = SqlFunc.AggregateSum(SqlFunc.IIF(p.AnsweredTime == null && p.EndBy != null && p.EndBy.Value == EEndBy.To, 1, 0)),//挂断数
+               Hanguped = SqlFunc.AggregateSum(SqlFunc.IIF(p.AnsweredTime == null && 
+               (p.CallState == ECallState.Missed || p.CallState == ECallState.NotAcceptedHang || p.CallState == ECallState.IVRNoAccept), 1, 0)),//挂断数
            })
            // .GroupBy(p => p.Hour)
            .MergeTable();
@@ -192,11 +199,11 @@ public class CallNativeRepository : BaseRepository<CallNative>, ICallNativeRepos
         }
         RefAsync<int> total = 0;
         var res = await Db.Queryable<CallNative>()
-            .Where(x => x.CreationTime >= dto.StartTime && x.CreationTime <= dto.EndTime)
-            .Where(x => x.Direction == ECallDirection.In)
+            .Where(x => x.BeginIvrTime >= dto.StartTime && x.BeginIvrTime <= dto.EndTime)
+            .Where(x => x.Direction == ECallDirection.In && x.CallState != ECallState.Invalid)
             .WhereIF(!string.IsNullOrEmpty(dto.Source), x => x.ToNo == dto.Source)
-            .WhereIF(!string.IsNullOrEmpty(dto.Type) && ("QueueBye".Equals(dto.Type) || "queueByeCount".Equals(dto.Type)), x => x.WaitDuration > 0 && x.RingDuration == 0) //队列挂断
-            .WhereIF(!string.IsNullOrEmpty(dto.Type) && ("IvrBye".Equals(dto.Type) || "ivrByeCount".Equals(dto.Type)), x => x.BeginIvrTime.HasValue && !x.BeginQueueTime.HasValue && !x.BeginRingTime.HasValue && x.AnsweredTime == null)//IVR挂断
+            .WhereIF(!string.IsNullOrEmpty(dto.Type) && ("QueueBye".Equals(dto.Type) || "queueByeCount".Equals(dto.Type)), x => x.CallState == ECallState.NotAcceptedHang) //队列挂断
+            .WhereIF(!string.IsNullOrEmpty(dto.Type) && ("IvrBye".Equals(dto.Type) || "ivrByeCount".Equals(dto.Type)), x => x.CallState == ECallState.IVRNoAccept)//IVR挂断
             .WhereIF(!string.IsNullOrEmpty(dto.Type) && ("Effective".Equals(dto.Type) || "effectiveCount".Equals(dto.Type)), x => x.Duration >= effectiveTimes) //有效接通
             .WhereIF(!string.IsNullOrEmpty(dto.Type) && "Invalid".Equals(dto.Type), x => x.Duration > 0 && x.Duration < effectiveTimes)//无效接通
             .WhereIF(!string.IsNullOrEmpty(dto.Type) && "connectByeCount".Equals(dto.Type), x => x.Duration > 0 && x.Duration <= connectByeTimes)  //接通秒挂
@@ -207,13 +214,13 @@ public class CallNativeRepository : BaseRepository<CallNative>, ICallNativeRepos
             || (x.Duration >= effectiveTimes)//有效接通
             || (x.BeginIvrTime.HasValue && !x.BeginQueueTime.HasValue && !x.BeginRingTime.HasValue && x.AnsweredTime == null) //IVR挂断
             || (x.WaitDuration > 0 && x.RingDuration == 0))//队列挂断
-            .WhereIF(dto.StartHourTo.HasValue, x => SqlFunc.ToTime(x.CreationTime.ToString("HH:mm:ss")) >= dto.StartHourTo.Value && SqlFunc.ToTime(x.CreationTime.ToString("HH:mm:ss")) < endHourTo)
+            .WhereIF(dto.StartHourTo.HasValue, x => SqlFunc.ToTime(x.BeginIvrTime.Value.ToString("HH:mm:ss")) >= dto.StartHourTo.Value && SqlFunc.ToTime(x.BeginIvrTime.Value.ToString("HH:mm:ss")) < endHourTo)
             .Select(x => new BiSeatSwitchDto
             {
                 Id = x.Id,
                 CPN = x.FromNo,
                 CDPN = x.ToNo,
-                CreatedTime = x.CreationTime
+                CreatedTime = x.BeginIvrTime.Value
             })
             .ToPageListAsync(dto.PageIndex, dto.PageSize, total);
         return new TotalData<BiSeatSwitchDto>(res, total.Value);
@@ -222,13 +229,13 @@ public class CallNativeRepository : BaseRepository<CallNative>, ICallNativeRepos
     public async Task<List<CallHotLineDto>> GetCallHotLineListAsync(BiQueryGateWayDto dto, int noConnectByeTimes, int effectiveTimes, int connectByeTimes, int ringTims)
     {
         var list = await Db.Queryable<CallNative>()
-            .Where(x => x.CreationTime >= dto.StartTime && x.CreationTime <= dto.EndTime && SqlFunc.Length(x.ToNo) > 4)
+            .Where(x => x.BeginIvrTime >= dto.StartTime && x.BeginIvrTime <= dto.EndTime && SqlFunc.Length(x.ToNo) > 4 && x.CallState != ECallState.Invalid)
             .WhereIF(!string.IsNullOrEmpty(dto.Gateway), x => x.ToNo == dto.Gateway)
             .GroupBy(x => x.ToNo)
             .Select(x => new CallHotLineDto()
             {
                 GateWay = x.ToNo,
-                CallInCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.Direction == ECallDirection.In, 1, 0)),//接通
+                CallInCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.Direction == ECallDirection.In, 1, 0)),//呼入
                 ConnectCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.Direction == ECallDirection.In && x.AnsweredTime != null, 1, 0)),//接通
                 NoConnectByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.Direction == ECallDirection.In && x.Duration == 0 && x.RingDuration <= noConnectByeTimes && x.RingDuration > 0, 1, 0)), //未接通秒挂
                 EffectiveCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.Direction == ECallDirection.In && x.Duration >= effectiveTimes, 1, 0)),//有效接通

+ 38 - 0
src/Hotline.Share/Enums/CallCenter/ECallState.cs

@@ -1,14 +1,52 @@
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
 namespace Hotline.Share.Enums.CallCenter
 {
+    /// <summary>
+    /// 呼叫状态
+    /// 兴唐状态: 0: 留言; 1: 队列等待超时; 2: 坐席振铃超时; 3: 未接; 4: 满意度; 5: 通话; 6: IVR; 7: 振铃挂机; 8: 分机互打挂机; 9: ASR交互;
+    /// </summary>
     public enum ECallState
     {
+        /// <summary>
+        /// 无效
+        /// 兴唐: SkillId = 0
+        /// </summary>
+        [Description("无效")]
+        Invalid = 0,
+
+        /// <summary>
+        /// 接通
+        /// 兴唐: 状态: 0, 4, 5, 8
+        /// </summary>
+        [Description("接通")]
         On = 1,
+
+        /// <summary>
+        /// 未接
+        /// 兴唐 状态: 2 , 3, 7
+        /// </summary>
+        [Description("未接")]
         Missed = 2,
+
+        /// <summary>
+        /// 呼入队列挂断
+        /// 兴唐 状态: 1
+        /// </summary>
+        [Description("呼入队列挂断")]
+        NotAcceptedHang = 3,
+
+
+        /// <summary>
+        /// IVR挂断
+        /// 兴唐 状态: 6
+        /// </summary>
+        [Description("IVR挂断")]
+        IVRNoAccept = 5
     }
 }

+ 20 - 0
src/Hotline.Share/Tools/DateTimeExtensions.cs

@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Tools;
+public static class DateTimeExtensions
+{
+    /// <summary>
+    /// 获取两个时间之间的差
+    /// </summary>
+    /// <param name="beginDateTime">开始时间</param>
+    /// <param name="endDateTime">结束时间</param>
+    /// <returns>相距秒数</returns>
+    public static int GetDifferenceSeconds(this DateTime beginDateTime, DateTime endDateTime)
+    {
+        return (int)(beginDateTime - endDateTime).TotalSeconds;
+    }
+}

+ 7 - 6
src/Hotline/CallCenter/Calls/CallNative.cs

@@ -161,15 +161,16 @@ namespace Hotline.CallCenter.Calls
         [SugarColumn(ColumnDescription = "电话回复内容")]
         public string? ReplyTxt { get; set; }
 
-        //public string? ExternalId { get; set; }
-
-        //public string? OrderNo { get; set; }
-
-        //public string? Title { get; set; }
-
         /// <summary>
         /// 转接分机号
         /// </summary>
+        [SugarColumn(ColumnDescription = "转接分机号")]
         public string? AgentTransferNumber { get; set; }
+
+        /// <summary>
+        /// 呼叫状态
+        /// </summary>
+        [SugarColumn(ColumnDescription = "呼叫状态")]
+        public ECallState? CallState { get; set; }
     }
 }

+ 1 - 1
src/XingTang.Sdk/XingtangCall.cs

@@ -81,7 +81,7 @@ public class XingtangCall
     public int RingTime { get; set; }
 
     /// <summary>
-    /// 呼叫状态 0 留言 1 队列等待超时 2 坐席振铃超时 3 未接 4 满意度 5 通话 6 IVR 7 振铃挂机 8 分机互打挂机 9 ASR交互
+    /// 呼叫状态 0: 留言; 1: 队列等待超时; 2: 坐席振铃超时; 3: 未接; 4: 满意度; 5: 通话; 6: IVR; 7: 振铃挂机; 8: 分机互打挂机; 9: ASR交互;
     /// </summary>
     public int CallState { get; set; }