Browse Source

Merge branch 'release' of http://git.12345lm.cn/Fengwo/hotline into release

Dun.Jason 6 tháng trước cách đây
mục cha
commit
b59f07c4dd

+ 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>
     /// 热线号码统计

+ 8 - 2
src/Hotline.Api/Controllers/KnowledgeController.cs

@@ -491,8 +491,14 @@ namespace Hotline.Api.Controllers
             if (knowledgeWords.Any())
                 knowledgeShowInfoDto.KeywordsDto = _mapper.Map<List<KnowledgeWordDto>>(knowledgeWords);
 
-            var files = await _fileRepository.Queryable().Where(x => x.Key == knowledge.Id).ToListAsync();
-            if (files.Any()) knowledgeShowInfoDto.Files = _mapper.Map<List<FileDto>>(files);
+            if (knowledgeShowInfoDto.FileJson != null && knowledgeShowInfoDto.FileJson.Any())
+            {
+	            var ids = knowledgeShowInfoDto.FileJson.Select(x => x.Id).ToList();
+	            knowledgeShowInfoDto.Files = await _fileRepository.GetFilesAsync(ids, HttpContext.RequestAborted);
+            }
+
+			//var files = await _fileRepository.Queryable().Where(x => x.Key == knowledge.Id).ToListAsync();
+   //         if (files.Any()) knowledgeShowInfoDto.Files = _mapper.Map<List<FileDto>>(files);
 
             if (IsAddPv == true)
                 _mediator.Publish(new GetKnowledgeInfoNotify(knowledge));

+ 141 - 103
src/Hotline.Api/Controllers/OrderController.cs

@@ -3585,9 +3585,9 @@ public class OrderController : BaseController
             var startDto = _mapper.Map<StartWorkflowDto>(dto.Workflow);
             startDto.DefinitionModuleCode = WorkflowModuleConsts.OrderHandle;
             startDto.Title = order.Title;
-            var startStep = await _workflowDomainService.StartAsync(startDto, order.Id, order.ExpiredTime, HttpContext.RequestAborted);
+            var (workflow,startStep) = await _workflowDomainService.StartAsync(startDto, order.Id, order.ExpiredTime, HttpContext.RequestAborted);
 
-            await HandleOrderAsync(order, startStep, dto.Data, dto.Workflow, HttpContext.RequestAborted);
+            await HandleOrderAsync(order, workflow, startStep, dto.Data, dto.Workflow, HttpContext.RequestAborted);
         }
         catch (Exception e)
         {
@@ -3635,43 +3635,10 @@ public class OrderController : BaseController
 
         var workflow = await _workflowDomainService.GetWorkflowAsync(dto.Workflow.WorkflowId, withSteps: true, withTraces: true,
             cancellationToken: HttpContext.RequestAborted);
-        //1.是否是判断节点  2.是否存在历史派单节点  3.存在获取上个派单节点  4.不存在走平均派单
-        if (dto.Data.OrderAssignMode is EOrderAssignMode.AdjoinLevel && dto.Workflow.BusinessType == EBusinessType.Send)
-        {
-            var sendOrderTraces = workflow.Traces.Where(x => x.BusinessType == EBusinessType.Send);
-            if (sendOrderTraces.Any())
-            {
-                var sendOrderTrace = workflow.Traces.Where(x => x.BusinessType == EBusinessType.Send)
-                    .OrderByDescending(x => x.CreationTime)
-                    .FirstOrDefault();
-                dto.Workflow.NextHandlers = new List<FlowStepHandler>
-                {
-                    new FlowStepHandler()
-                    {
-                        Key = sendOrderTrace.HandlerId, Value = sendOrderTrace.HandlerName,
-                        UserId = sendOrderTrace.HandlerId, Username = sendOrderTrace.HandlerName,
-                        OrgId = sendOrderTrace.HandlerOrgId, OrgName = sendOrderTrace.HandlerOrgName
-                    }
-                };
-            }
-            else
-            {
-                // 平均派单
-                var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
-                if (averageSendOrder)
-                {
-                    if (!dto.Workflow.NextHandlers.Any())
-                    {
-                        var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
-                        dto.Workflow.NextHandlers = new List<FlowStepHandler> { handler };
-                    }
-                }
-            }
-        }
 
         //await _workflowApplication.NextAsync(dto.WorkflowDto, order.ExpiredTime, HttpContext.RequestAborted);
         var startStep = workflow.Steps.First(d => d.Id == dto.Workflow.StepId);
-        await HandleOrderAsync(order, startStep, dto.Data, dto.Workflow, HttpContext.RequestAborted);
+        await HandleOrderAsync(order, workflow, startStep, dto.Data, dto.Workflow, HttpContext.RequestAborted);
 
         if (_appOptions.Value.IsZiGong && dto.Data.Transpond.HasValue && dto.Data.Transpond.Value)
         {
@@ -3684,7 +3651,8 @@ public class OrderController : BaseController
         }
     }
 
-    private async Task HandleOrderAsync(Order order, WorkflowStep startStep, OrderHandleFlowDto orderHandleFlowDto, BasicWorkflowDto workflowDto,
+    private async Task HandleOrderAsync(Order order, Workflow workflow, WorkflowStep startStep, OrderHandleFlowDto orderHandleFlowDto,
+        BasicWorkflowDto workflowDto,
         CancellationToken cancellationToken)
     {
         switch (orderHandleFlowDto.OrderAssignMode)
@@ -3694,15 +3662,53 @@ public class OrderController : BaseController
                 nextDto.WorkflowId = startStep.WorkflowId;
                 nextDto.StepId = startStep.Id;
 
-                // 平均派单
-                var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder)
-                    .SettingValue[0]);
-                if (workflowDto.BusinessType == EBusinessType.Send && averageSendOrder)
+                // 宜宾需求: 1.是否是判断节点  2.是否存在历史派单节点  3.存在获取上个派单节点  4.不存在走平均派单
+                if (workflowDto.BusinessType == EBusinessType.Send)
                 {
-                    if (!nextDto.NextHandlers.Any())
+                    if (_appOptions.Value.IsYiBin)
                     {
-                        var handler = await _orderDomainService.AverageOrder(cancellationToken);
-                        nextDto.NextHandlers = new List<FlowStepHandler> { handler };
+                        var sendOrderTraces = workflow.Traces.Where(x => x.BusinessType == EBusinessType.Send);
+                        if (sendOrderTraces.Any())
+                        {
+                            var sendOrderTrace = workflow.Traces.Where(x => x.BusinessType == EBusinessType.Send)
+                                .OrderByDescending(x => x.CreationTime)
+                                .FirstOrDefault();
+                            nextDto.NextHandlers = new List<FlowStepHandler>
+                            {
+                                new FlowStepHandler()
+                                {
+                                    Key = sendOrderTrace.HandlerId, Value = sendOrderTrace.HandlerName,
+                                    UserId = sendOrderTrace.HandlerId, Username = sendOrderTrace.HandlerName,
+                                    OrgId = sendOrderTrace.HandlerOrgId, OrgName = sendOrderTrace.HandlerOrgName
+                                }
+                            };
+                        }
+                        else
+                        {
+                            // 平均派单
+                            var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
+                            if (averageSendOrder)
+                            {
+                                if (!nextDto.NextHandlers.Any())
+                                {
+                                    var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
+                                    nextDto.NextHandlers = new List<FlowStepHandler> { handler };
+                                }
+                            }
+                        }
+                    }
+                    else
+                    {
+                        // 平均派单
+                        var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
+                        if (averageSendOrder)
+                        {
+                            if (!nextDto.NextHandlers.Any())
+                            {
+                                var handler = await _orderDomainService.AverageOrder(cancellationToken);
+                                nextDto.NextHandlers = new List<FlowStepHandler> { handler };
+                            }
+                        }
                     }
                 }
 
@@ -4861,22 +4867,31 @@ public class OrderController : BaseController
             //{
             // var expiredTime = _timeLimitDomainService.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
 
-            ExpiredTimeWithConfig? expiredTime = null;
-            if (_appOptions.Value.IsZiGong)
-            {
-                if (dto.FlowDirection != null)
-                {
-                    expiredTime = await _expireTime.CalcExpiredTime(DateTime.Now, dto.FlowDirection.Value, order.Adapt<OrderTimeClacInfo>());
-                }
-                else
-                {
-                    expiredTime = await _expireTime.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
-                }
-            }
-            else
+            ExpiredTimeWithConfig? expiredTime = new ExpiredTimeWithConfig();
+
+            if (dto.FlowDirection is EFlowDirection.OrgToOrg)
             {
-                expiredTime = await _expireTime.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
-            }
+	            expiredTime.ExpiredTime = order.ExpiredTime.Value;
+	            expiredTime.NearlyExpiredTime = order.NearlyExpiredTime.Value;
+	            expiredTime.NearlyExpiredTimeOne = order.NearlyExpiredTimeOne.Value;
+			}
+            else {
+				if (_appOptions.Value.IsZiGong)
+				{
+					if (dto.FlowDirection != null)
+					{
+						expiredTime = await _expireTime.CalcExpiredTime(DateTime.Now, dto.FlowDirection.Value, order.Adapt<OrderTimeClacInfo>());
+					}
+					else
+					{
+						expiredTime = await _expireTime.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
+					}
+				}
+				else
+				{
+					expiredTime = await _expireTime.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
+				}
+			}
 
             var processType = dto.FlowDirection is EFlowDirection.OrgToCenter or EFlowDirection.CenterToCenter or EFlowDirection.FiledToCenter
                 ? EProcessType.Zhiban
@@ -5150,16 +5165,40 @@ public class OrderController : BaseController
                 HandlerType = special.HandlerType.Value,
                 BusinessType = special.BusinessType.Value
             };
-            //if (dto.AlterTime)
-            //    recall.External = new External { TimeLimit = dto.TimeLimit, TimeLimitUnit = dto.TimeLimitUnit };
-            //if (dto.Files.Any()) recall.Files = dto.Files;
-            // 计算期满时间
-            //if (dto.AlterTime)
-            //{
-            //var expiredTime = _timeLimitDomainService.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
-            var expiredTime = await _expireTime.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
-            //var processType = dto.FlowDirection == EFlowDirection.OrgToCenter || dto.FlowDirection == EFlowDirection.CenterToCenter ? EProcessType.Zhiban : EProcessType.Jiaoban;
-            var processType = special.FlowDirection is EFlowDirection.OrgToCenter or EFlowDirection.CenterToCenter or EFlowDirection.FiledToCenter
+			ExpiredTimeWithConfig? expiredTime = new ExpiredTimeWithConfig();
+
+			if (dto.FlowDirection is EFlowDirection.OrgToOrg)
+			{
+				expiredTime.ExpiredTime = order.ExpiredTime.Value;
+				expiredTime.NearlyExpiredTime = order.NearlyExpiredTime.Value;
+				expiredTime.NearlyExpiredTimeOne = order.NearlyExpiredTimeOne.Value;
+			}
+			else
+			{
+				if (_appOptions.Value.IsZiGong)
+				{
+					if (dto.FlowDirection != null)
+					{
+						expiredTime = await _expireTime.CalcExpiredTime(DateTime.Now, dto.FlowDirection.Value, order.Adapt<OrderTimeClacInfo>());
+					}
+					else
+					{
+						expiredTime = await _expireTime.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
+					}
+				}
+				else
+				{
+					expiredTime = await _expireTime.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
+				}
+			}
+			if (expiredTime.ExpiredTime < order.ExpiredTime)
+			{
+				expiredTime.ExpiredTime = order.ExpiredTime.Value;
+				expiredTime.NearlyExpiredTime = order.NearlyExpiredTime.Value;
+				expiredTime.NearlyExpiredTimeOne = order.NearlyExpiredTimeOne.Value;
+			}
+
+			var processType = special.FlowDirection is EFlowDirection.OrgToCenter or EFlowDirection.CenterToCenter or EFlowDirection.FiledToCenter
                 ? EProcessType.Zhiban
                 : EProcessType.Jiaoban;
             await _orderRepository.Updateable().SetColumns(o => new Orders.Order()
@@ -5214,18 +5253,6 @@ public class OrderController : BaseController
                 await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderFlowRecalled,
                     new PublishSpecialDto { Order = _mapper.Map<OrderDto>(order), Special = _mapper.Map<OrderSpecialDto>(special) },
                     cancellationToken: HttpContext.RequestAborted);
-
-                //try
-                //{
-                //    await _provinceService.RevokeOrder(
-                //        new PublishSpecialDto
-                //        { Order = _mapper.Map<OrderDto>(order), Special = _mapper.Map<OrderSpecialDto>(special) },
-                //        HttpContext.RequestAborted);
-                //}
-                //catch (Exception e)
-                //{
-                //    _logger.LogError("_provinceService.RevokeOrder throw exception: {ex}", e.Message);
-                //}
             }
         }
         else
@@ -5273,16 +5300,39 @@ public class OrderController : BaseController
                     HandlerType = special.HandlerType.Value,
                     BusinessType = special.BusinessType.Value
                 };
-                //if (dto.AlterTime)
-                //    recall.External = new External { TimeLimit = dto.TimeLimit, TimeLimitUnit = dto.TimeLimitUnit };
-                //if (dto.Files.Any()) recall.Files = dto.Files;
-                // 计算期满时间
-                //if (dto.AlterTime)
-                //{
-                // var expiredTime = _timeLimitDomainService.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
-                var expiredTime = await _expireTime.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
-                //var processType = dto.FlowDirection == EFlowDirection.OrgToCenter || dto.FlowDirection == EFlowDirection.CenterToCenter ? EProcessType.Zhiban : EProcessType.Jiaoban;
-                var processType = special.FlowDirection is EFlowDirection.OrgToCenter or EFlowDirection.CenterToCenter or EFlowDirection.FiledToCenter
+				ExpiredTimeWithConfig? expiredTime = new ExpiredTimeWithConfig();
+
+				if (dto.FlowDirection is EFlowDirection.OrgToOrg)
+				{
+					expiredTime.ExpiredTime = order.ExpiredTime.Value;
+					expiredTime.NearlyExpiredTime = order.NearlyExpiredTime.Value;
+					expiredTime.NearlyExpiredTimeOne = order.NearlyExpiredTimeOne.Value;
+				}
+				else
+				{
+					if (_appOptions.Value.IsZiGong)
+					{
+						if (dto.FlowDirection != null)
+						{
+							expiredTime = await _expireTime.CalcExpiredTime(DateTime.Now, dto.FlowDirection.Value, order.Adapt<OrderTimeClacInfo>());
+						}
+						else
+						{
+							expiredTime = await _expireTime.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
+						}
+					}
+					else
+					{
+						expiredTime = await _expireTime.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
+					}
+				}
+				if (expiredTime.ExpiredTime < order.ExpiredTime)
+				{
+					expiredTime.ExpiredTime = order.ExpiredTime.Value;
+					expiredTime.NearlyExpiredTime = order.NearlyExpiredTime.Value;
+					expiredTime.NearlyExpiredTimeOne = order.NearlyExpiredTimeOne.Value;
+				}
+				var processType = special.FlowDirection is EFlowDirection.OrgToCenter or EFlowDirection.CenterToCenter or EFlowDirection.FiledToCenter
                     ? EProcessType.Zhiban
                     : EProcessType.Jiaoban;
                 await _orderRepository.Updateable().SetColumns(o => new Orders.Order()
@@ -5338,18 +5388,6 @@ public class OrderController : BaseController
                     await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderFlowRecalled,
                         new PublishSpecialDto { Order = _mapper.Map<OrderDto>(order), Special = _mapper.Map<OrderSpecialDto>(special) },
                         cancellationToken: HttpContext.RequestAborted);
-
-                    //try
-                    //{
-                    //    await _provinceService.RevokeOrder(
-                    //        new PublishSpecialDto
-                    //        { Order = _mapper.Map<OrderDto>(order), Special = _mapper.Map<OrderSpecialDto>(special) },
-                    //        HttpContext.RequestAborted);
-                    //}
-                    //catch (Exception e)
-                    //{
-                    //    _logger.LogError("_provinceService.RevokeOrder throw exception: {ex}", e.Message);
-                    //}
                 }
             }
             else

+ 1 - 1
src/Hotline.Application/Handlers/FlowEngine/WorkflowNextHandler.cs

@@ -141,7 +141,7 @@ public class WorkflowNextHandler : INotificationHandler<NextStepNotify>
 
                     if (data.FlowDirection is EFlowDirection.CenterToOrg)
                         order.SendBackAuditEndTime = await _expireTime.GetWorkDay(DateTime.Now);
-                    await _orderRepository.UpdateAsync(order, cancellationToken);
+                    await _orderRepository.Updateable(order).ExecuteCommandAsync(cancellationToken);
 
                     //司法行政监督管理-推诿工单
                     //如果没开启则不处理

+ 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;

+ 1 - 0
src/Hotline.Application/Mappers/OrderMapperConfigs.cs

@@ -111,6 +111,7 @@ public class OrderMapperConfigs : IRegister
             .Ignore(d => d.AcceptorName)
             .Ignore(d => d.AcceptorStaffNo)
             .Ignore(d => d.ExternalId)
+            .Ignore(d=>d.CreationTime)
             //.AfterMapping((s, d) =>
             //{
             //    //d.UpdateHandlingStatus(s.IsInCountersign);

+ 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((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.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)),//有效接通

+ 2 - 0
src/Hotline.Share/Dtos/Knowledge/KnowledgeDto.cs

@@ -240,6 +240,8 @@ namespace Hotline.Share.Dtos.Knowledge
 
         public List<FileDto> Files { get; set; }
 
+        public List<FileJson>? FileJson { get; set; }
+
 	}
 
     public class KnowledgeBaseDto {

+ 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/Hotline/FlowEngine/Workflows/IWorkflowDomainService.cs

@@ -30,7 +30,7 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// new
         /// </summary>
-        Task<WorkflowStep> StartAsync(StartWorkflowDto dto, string externalId, DateTime? expiredTime, CancellationToken cancellationToken = default);
+        Task<(Workflow, WorkflowStep)> StartAsync(StartWorkflowDto dto, string externalId, DateTime? expiredTime, CancellationToken cancellationToken = default);
 
         /// <summary>
         /// new

+ 5 - 4
src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs

@@ -148,7 +148,7 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// new(开启流程并停留在开始节点,开始节点为待办节点,指派给当前操作人)
         /// </summary>
-        public async Task<WorkflowStep> StartAsync(StartWorkflowDto dto,
+        public async Task<(Workflow, WorkflowStep)> StartAsync(StartWorkflowDto dto,
             string externalId, DateTime? expiredTime, CancellationToken cancellationToken = default)
         {
             //     var validator = new StartWorkflowDtoValidator();
@@ -246,7 +246,7 @@ namespace Hotline.FlowEngine.Workflows
             await _publisher.PublishAsync(new StartWorkflowNotify(workflow, dto, flowAssignInfo.FlowAssignType, startTrace),
                 PublishStrategy.ParallelWhenAll, cancellationToken);
 
-            return startStep;
+            return (workflow, startStep);
         }
 
         /// <summary>
@@ -639,11 +639,12 @@ namespace Hotline.FlowEngine.Workflows
             _mapper.Map(currentStep, trace);
             currentStep.WorkflowTrace = trace;
 
-            workflow.ActualHandleStepAcceptTime = currentStep.AcceptTime;
             await _workflowStepRepository.UpdateNav(currentStep)
-                .Include(d => d.Workflow)
                 .Include(d => d.WorkflowTrace)
                 .ExecuteCommandAsync();
+            
+            workflow.ActualHandleStepAcceptTime = currentStep.AcceptTime;
+            await _workflowRepository.Updateable(workflow).ExecuteCommandAsync(cancellationToken);
 
             return workflow.ActualHandleStepAcceptTime;
         }

+ 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; }