xf 2 năm trước cách đây
mục cha
commit
ce8836714f

+ 2 - 5
src/CallCenter.Api/Realtimes/CallCenterHub.cs

@@ -14,10 +14,7 @@ public class CallCenterHub : Hub
     private readonly IWorkRepository _workRepository;
     private readonly IUserCacheManager _userCacheManager;
 
-    public CallCenterHub(
-        ISessionContext sessionContext,
-        IWorkRepository workRepository,
-        IUserCacheManager userCacheManager)
+    public CallCenterHub(ISessionContext sessionContext, IWorkRepository workRepository, IUserCacheManager userCacheManager)
     {
         _sessionContext = sessionContext;
         _workRepository = workRepository;
@@ -36,7 +33,7 @@ public class CallCenterHub : Hub
             throw new UserFriendlyException($"未查询到上班记录, userId: {userId}");
         work.SignalRId = Context.ConnectionId;
         await _workRepository.UpdateAsync(work, Context.ConnectionAborted);
-        //todo 清理对应work cache
+        _userCacheManager.UpdateWorkByUser(work);
         await base.OnConnectedAsync();
     }
 

+ 7 - 6
src/CallCenter.Api/StartupExtensions.cs

@@ -48,8 +48,7 @@ internal static class StartupExtensions
         // Add services to the container.
         services
             .BatchInjectServices()
-            .AddApplication()
-            ;
+            .AddApplication();
 
         //services.AddIdentityClient(
         //    new IdentityClientConfiguration("http://identity.fengwo.com", "http://open.identity.fengwo.com"),
@@ -135,10 +134,12 @@ internal static class StartupExtensions
         });
 
         //signalR
-        builder.Services.AddSignalR().AddStackExchangeRedis(configuration.GetConnectionString("Redis"), options =>
-        {
-            options.Configuration.ChannelPrefix = "callcenter:signalR:";
-        });
+        builder.Services.AddSignalR()
+        //    .AddStackExchangeRedis(configuration.GetConnectionString("Redis"), options =>
+        //{
+        //    options.Configuration.ChannelPrefix = "callcenter:signalR:";
+        //})
+            ;
 
         /* CORS */
         services.AddCors(options =>

+ 52 - 9
src/CallCenter.Api/appsettings.json

@@ -37,7 +37,7 @@
   },
   "AllowedHosts": "*",
   "DeviceConfigs": {
-    "Address": "http://192.168.100.100/xml",
+    "Address": "http://192.168.100.101/xml",
     "Authorize": true,
     "ReceiveKey": "E1BBD1BB-A269-44",
     "SendKey": "2A-BA92-160A3B1D",
@@ -49,16 +49,59 @@
   },
   "Swagger": true,
   "Cors": {
-    "Origins": [ "http://localhost:8888", "http://callcenter-admin.fengwo.com", "http://admin.call.fengwo.com" ]
+    "Origins": [ "http://localhost:8888","http://localhost:8695","http://callcenter-admin.fengwo.com", "http://admin.call.fengwo.com" ]
   },
   "WorkTimeSettings": {
-    "MorningBegin": "08:00",
-    "MorningEnd": "12:00",
-    "AfterBegin": "15:00",
-    "AfterEnd": "21:00",
-    "WorkDay": [ 1, 2, 3, 4, 5, 0, 6 ],
-    "WorkCategory": "08da9b9f-a35d-4ade-8ea7-55e8abbcdefd",
-    "RestCategory": "08daa5f5-ac7a-4ced-8295-1c78baa15f9e"
+    "LineSetting": [
+      {
+        "NumNo": "12345",
+        "MorningBegin": "00:00",
+        "MorningEnd": "12:00",
+        "AfterBegin": "12:00",
+        "AfterEnd": "23:59",
+        "WorkDay": [ 1, 2, 3, 4, 5, 6, 0 ],
+        "WorkCategory": "",
+        "RestCategory": "",
+        "WorkToGroup": "600",
+        "RestToGroup": "600"
+      },
+      {
+        "NumNo": "12333",
+        "MorningBegin": "07:00",
+        "MorningEnd": "12:00",
+        "AfterBegin": "12:00",
+        "AfterEnd": "23:00",
+        "WorkDay": [ 1, 2, 3, 4, 5, 6, 0 ],
+        "WorkCategory": "",
+        "RestCategory": "",
+        "WorkToGroup": "604",
+        "RestToGroup": "600"
+      },
+      {
+        "NumNo": "12319",
+        "MorningBegin": "00:00",
+        "MorningEnd": "12:00",
+        "AfterBegin": "12:00",
+        "AfterEnd": "23:59",
+        "WorkDay": [ 1, 2, 3, 4, 5, 6, 0 ],
+        "WorkCategory": "",
+        "RestCategory": "",
+        "WorkToGroup": "600",
+        "RestToGroup": "600"
+      },
+      {
+        "NumNo": "12315",
+        "MorningBegin": "00:00",
+        "MorningEnd": "12:00",
+        "AfterBegin": "12:00",
+        "AfterEnd": "23:59",
+        "WorkDay": [ 1, 2, 3, 4, 5, 6, 0 ],
+        "WorkCategory": "",
+        "RestCategory": "",
+        "WorkToGroup": "",
+        "RestToGroup": ""
+      }
+    ]
   },
   "RecordSettings": {
     "Remote": "http://192.168.100.100/mcc/Recorder/",

+ 14 - 2
src/CallCenter.Application/Handlers/CallState/RingVisitorToExtNotificationHandler.cs

@@ -1,5 +1,7 @@
-using CallCenter.Calls;
+using CallCenter.Caches;
+using CallCenter.Calls;
 using CallCenter.Notifications;
+using CallCenter.Realtimes;
 using CallCenter.Share.Enums;
 using MediatR;
 
@@ -9,10 +11,14 @@ namespace CallCenter.Application.Handlers
     {
         private readonly ICallRepository _callRepository;
         private readonly ICallDetailRepository _callDetailRepository;
-        public RingVisitorToExtNotificationHandler(ICallRepository callRepository, ICallDetailRepository callDetailRepository)
+        private readonly IRealtimeService _realtimeService;
+        private readonly IUserCacheManager _userCacheManager;
+        public RingVisitorToExtNotificationHandler(ICallRepository callRepository, ICallDetailRepository callDetailRepository,IRealtimeService realtimeService,IUserCacheManager userCacheManager)
         {
             _callRepository = callRepository;
             _callDetailRepository = callDetailRepository;
+            _realtimeService = realtimeService;
+            _userCacheManager = userCacheManager;
         }
 
 
@@ -36,6 +42,12 @@ namespace CallCenter.Application.Handlers
                     ToNo = notification.Visitor.To
                 };
                 await _callDetailRepository.AddAsync(detail, cancellationToken);
+
+                //调用业务弹屏 通知前端
+                var workModel = _userCacheManager.GetWorkByTel(notification.TelNo);
+                if (workModel!=null) {
+                    await _realtimeService.RingAsync(workModel.UserId, new RingDto() { CallType = ECallType.VisitorCallIn, From = notification.Visitor.From, To = notification.Visitor.To, Id = model.Id },cancellationToken);
+                }
             }
         }
     }

+ 15 - 2
src/CallCenter.Application/Handlers/FlowControl/AnsweredVisitorToExtNotificationHandler.cs

@@ -1,4 +1,6 @@
-using CallCenter.Calls;
+using CallCenter.Caches;
+using CallCenter.Calls;
+using CallCenter.Realtimes;
 using CallCenter.Share.Enums;
 using CallCenter.Share.Notifications;
 using MediatR;
@@ -9,11 +11,15 @@ namespace CallCenter.Application.Handlers
     {
         private readonly ICallRepository _callRepository;
         private readonly ICallDetailRepository _callDetailRepository;
+        private readonly IRealtimeService _realtimeService;
+        private readonly IUserCacheManager _userCacheManager;
 
-        public AnsweredVisitorToExtNotificationHandler(ICallRepository callRepository, ICallDetailRepository callDetailRepository)
+        public AnsweredVisitorToExtNotificationHandler(ICallRepository callRepository, ICallDetailRepository callDetailRepository,IRealtimeService realtimeService,IUserCacheManager userCacheManager)
         {
             _callRepository = callRepository;
             _callDetailRepository = callDetailRepository;
+            _realtimeService = realtimeService;
+            _userCacheManager = userCacheManager;
         }
 
         public async Task Handle(AnsweredVisitorToExtNotification notification, CancellationToken cancellationToken)
@@ -38,6 +44,13 @@ namespace CallCenter.Application.Handlers
                     ToNo = notification.Visitor.To,
                 };
                 await _callDetailRepository.AddAsync(detail, cancellationToken);
+
+                //调用业务通知 通知前端
+                var workModel = _userCacheManager.GetWorkByTel(notification.TelNo);
+                if (workModel != null)
+                {
+                    await _realtimeService.AnsweredAsync(workModel.UserId, new AnseredDto() { From = notification.Visitor.From, Id = model.Id, To = notification.TelNo }, cancellationToken);
+                }
             }
         }
     }

+ 15 - 2
src/CallCenter.Application/Handlers/FlowControl/ByeVisitorAndExtNotificationHandler.cs

@@ -1,5 +1,7 @@
-using CallCenter.Calls;
+using CallCenter.Caches;
+using CallCenter.Calls;
 using CallCenter.Notifications;
+using CallCenter.Realtimes;
 using CallCenter.Share.Enums;
 using MediatR;
 
@@ -9,11 +11,15 @@ namespace CallCenter.Application.Handlers
     {
         private readonly ICallRepository _callRepository;
         private readonly ICallDetailRepository _callDetailRepository;
+        private readonly IRealtimeService _realtimeService;
+        private readonly IUserCacheManager _userCacheManager;
 
-        public ByeVisitorAndExtNotificationHandler(ICallRepository callRepository, ICallDetailRepository callDetailRepository)
+        public ByeVisitorAndExtNotificationHandler(ICallRepository callRepository, ICallDetailRepository callDetailRepository,IRealtimeService realtimeService,IUserCacheManager userCacheManager)
         {
             _callRepository = callRepository;
             _callDetailRepository = callDetailRepository;
+            _realtimeService = realtimeService;
+            _userCacheManager = userCacheManager;
         }
 
         public async Task Handle(ByeVisitorAndExtNotification notification, CancellationToken cancellationToken)
@@ -39,6 +45,13 @@ namespace CallCenter.Application.Handlers
                     Recording = notification.Recording,
                 };
                 await _callDetailRepository.AddAsync(detail, cancellationToken);
+
+                //调用业务通知 通知前端
+                var workModel = _userCacheManager.GetWorkByTel(notification.TelNo);
+                if (workModel!=null)
+                {
+                    await _realtimeService.ByeAsync(workModel.UserId, new ByeDto() { Id = model.Id }, cancellationToken);
+                }
             }
         }
     }

+ 80 - 0
src/CallCenter.Application/Handlers/FlowControl/IncomingNotificationHandler.cs

@@ -12,6 +12,7 @@ using XF.Domain.Constants;
 using XF.Domain.Cache;
 using CallCenter.Settings;
 using Microsoft.Extensions.Logging;
+using NewRock.Sdk.Transfer.Queue.Request;
 
 namespace CallCenter.Application.Handlers
 {
@@ -85,6 +86,35 @@ namespace CallCenter.Application.Handlers
                         Visitor = new VisitorToMenuVisitor() { Id = notification.Visitor.Id }
                     },
                     _options.Value.ReceiveKey, _options.Value.Expired, cancellationToken);
+
+                var correct = GetCorrectIvr(notification.Visitor.To);
+                switch (correct.eCorrectIvr)
+                {
+                    //跳转IVR
+                    case ECorrectIvr.Ivr:
+                        _logger.LogInformation("transfer to ivr.no: {ivrNo}", correct.ReturnValue );
+                        await _newRockClient.VisitorToMenu(new VisitorToMenuRequest()
+                        {
+                            Attribute = "Connect",
+                            Menu = new VisitorToMenuMenu() { Id = correct.ReturnValue },
+                            Visitor = new VisitorToMenuVisitor() { Id = notification.Visitor.Id}
+                        },
+                        _options.Value.ReceiveKey,_options.Value.Expired,cancellationToken);
+                        break;
+                    //直接转分机组
+                    case ECorrectIvr.Group:
+                        _logger.LogInformation("transfer to group.no:{groupNo}", correct.ReturnValue);
+                        await _newRockClient.VisitorToGroupQueue(new VisitorToGroupQueueRequest()
+                        {
+                            Attribute= "Queue",
+                            Visitor = new VisitorToGroupQueueVisitor() { Id = notification.Visitor.Id },
+                            Group= new VisitorToGroupQueueGroup() { Id = correct.ReturnValue }
+                        },
+                        _options.Value.ReceiveKey,_options.Value.Expired,cancellationToken);
+                        break;
+                    default:
+                        break;
+                }
             }
             else
             {
@@ -101,6 +131,56 @@ namespace CallCenter.Application.Handlers
             return ivr;
         }
 
+        private CorrectIvr GetCorrectIvr(string to)
+        {
+            var worktimeSettings = _worktimeCache.GetOrAdd("worktimesettings", d => _worktimeOptions.Value, ExpireMode.Absolute, TimeSpan.FromDays(1));
+            var correct = GetCorrectCategory(worktimeSettings.LineSetting.First(x => x.NumNo == to));
+            return correct;
+        }
+
+        private CorrectIvr GetCorrectCategory(LineSetting settings)
+        {
+            if (!settings.WorkDay.Contains((int)DateTime.Now.DayOfWeek))
+                return new CorrectIvr() { eCorrectIvr = ECorrectIvr.Group, ReturnValue = settings.RestToGroup };
+            var time = TimeOnly.FromDateTime(DateTime.Now);
+            if ((time >= TimeOnly.Parse(settings.MorningBegin) && time <= TimeOnly.Parse(settings.MorningEnd))
+                || (time >= TimeOnly.Parse(settings.AfterBegin) && time <= TimeOnly.Parse(settings.AfterEnd)))
+            {
+                if (!string.IsNullOrEmpty(settings.WorkCategory))
+                {
+                    return new CorrectIvr() { eCorrectIvr = ECorrectIvr.Ivr, ReturnValue = settings.WorkCategory };
+                }
+                else
+                {
+                    return new CorrectIvr() { eCorrectIvr = ECorrectIvr.Group, ReturnValue = settings.WorkToGroup };
+                }
+            }
+            else
+            {
+                if (!string.IsNullOrEmpty(settings.RestCategory))
+                {
+                    return new CorrectIvr() { eCorrectIvr = ECorrectIvr.Ivr, ReturnValue = settings.RestCategory };
+                }
+                else
+                {
+                    return new CorrectIvr() { eCorrectIvr = ECorrectIvr.Group,ReturnValue = settings.RestToGroup };
+                }
+            }
+        }
+
+        public class CorrectIvr
+        {
+            public string ReturnValue { get; set; }
+
+            public ECorrectIvr eCorrectIvr { get; set; }
+        }
+
+        public enum ECorrectIvr
+        {
+            Ivr = 0,
+            Group = 1,
+        }
+
         private string GetCorrectCategory(WorkTimeSettings settings)
         {
             if (!settings.WorkDay.Contains((int)DateTime.Now.DayOfWeek))

+ 37 - 0
src/CallCenter.Share/Dtos/OutCallDto.cs

@@ -9,6 +9,9 @@ namespace CallCenter.Share.Dtos
 {
     public class OutCallDto
     {
+        /// <summary>
+        /// 通话记录ID
+        /// </summary>
         public string CallId { get; set; }
 
         /// <summary>
@@ -76,5 +79,39 @@ namespace CallCenter.Share.Dtos
         /// </summary>
         public EOnState OnState { get; set; }
 
+        #region 2022年12月26日新增
+
+        /// <summary>
+        /// 进入IVR时间
+        /// </summary>
+        public DateTime? InIvrTime { get; set; }
+
+        /// <summary>
+        /// 离开IVR时间(进入队列时间)
+        /// </summary>
+        public DateTime? OutIvrTime { get; set; }
+        
+        /// <summary>
+        /// 进入队列时间
+        /// </summary>
+        public DateTime? InGroupTime{ get; set; }
+
+        /// <summary>
+        /// 离开队列时间(振铃时间)
+        /// </summary>
+        public DateTime? OutGroupTime { get; set; }
+
+        /// <summary>
+        /// 进入坐席时间(振铃时间)
+        /// </summary>
+        public DateTime? InSeaTime { get; set; }
+
+        /// <summary>
+        /// 离开坐席时间(应答时间)
+        /// </summary>
+        public DateTime? ConnSeaTime { get; set; }
+
+        #endregion
+
     }
 }

+ 8 - 0
src/CallCenter/Caches/IUserCacheManager.cs

@@ -37,5 +37,13 @@ namespace CallCenter.Caches
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
         Task<bool> IsWorkingByUserAsync(string userId, CancellationToken cancellationToken);
+
+
+        /// <summary>
+        /// 根据用户更新工作记录
+        /// </summary>
+        /// <param name="work"></param>
+        /// <returns></returns>
+        void UpdateWorkByUser(Work work);
     }
 }

+ 10 - 0
src/CallCenter/Caches/UserCacheManager.cs

@@ -98,4 +98,14 @@ public class UserCacheManager : IUserCacheManager, IScopeDependency
 
         return true;
     }
+
+    /// <summary>
+    /// 根据用户更新工作记录
+    /// </summary>
+    /// <param name="work"></param>
+    /// <returns></returns>
+    public void UpdateWorkByUser(Work work)
+    {
+        _cacheWork.Update(Work.GetKey(KeyMode.UserId, work.UserId), d => work);
+    }
 }

+ 16 - 0
src/CallCenter/Settings/WorkTimeSettings.cs

@@ -21,5 +21,21 @@ namespace CallCenter.Settings
         public string WorkCategory { get; set; }
 
         public string RestCategory { get; set; }
+
+        public List<LineSetting> LineSetting { get; set; }
+    }
+
+    public class LineSetting
+    {
+        public string NumNo { get; set; }
+        public string MorningBegin { get; set; }
+        public string MorningEnd { get; set; }
+        public string AfterBegin { get; set; }
+        public string AfterEnd { get; set; }
+        public List<int> WorkDay { get; set; }
+        public string WorkCategory { get; set; }
+        public string RestCategory { get; set; }
+        public string WorkToGroup { get; set; }
+        public string RestToGroup { get; set; }
     }
 }