admin 2 vuotta sitten
vanhempi
commit
678bb10009

+ 80 - 5
src/Hotline.Api/Controllers/PbxController.cs

@@ -48,8 +48,11 @@ namespace Hotline.Api.Controllers
         private readonly ITrunkIvrManagerRepository _trunkIvrManagerRepository;
         private readonly IIvrCategoryRepository _ivrCategoryRepository;
         private readonly IWorkflowApplication _workflowApplication;
-        private readonly IDefinitionDomainService _definitionDomainService;
         private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+        private readonly IIvrDomainService _ivrDomainService;
+        private readonly IIvrCacheManager _ivrCacheManager;
+        private readonly ILogger<TelController> _logger;
+        private readonly ICallDetailRepository _callDetailRepository;
 
         public PbxController(
             ITelRepository telRepository,
@@ -67,8 +70,11 @@ namespace Hotline.Api.Controllers
             ITrunkIvrManagerRepository trunkIvrManagerRepository,
             IIvrCategoryRepository ivrCategoryRepository,
             IWorkflowApplication workflowApplication,
-            IDefinitionDomainService definitionDomainService,
-            ISystemSettingCacheManager systemSettingCacheManager)
+            ISystemSettingCacheManager systemSettingCacheManager,
+            IIvrDomainService ivrDomainService,
+            IIvrCacheManager ivrCacheManager,
+            ILogger<TelController> logger,
+            ICallDetailRepository callDetailRepository)
         {
             _telRepository = telRepository;
             _telRestRepository = telRestRepository;
@@ -85,8 +91,11 @@ namespace Hotline.Api.Controllers
             _trunkIvrManagerRepository = trunkIvrManagerRepository;
             _ivrCategoryRepository = ivrCategoryRepository;
             _workflowApplication = workflowApplication;
-            _definitionDomainService = definitionDomainService;
             _systemSettingCacheManager = systemSettingCacheManager;
+            _ivrDomainService = ivrDomainService;
+            _ivrCacheManager = ivrCacheManager;
+            _logger = logger;
+            _callDetailRepository = callDetailRepository;
         }
 
         #region 话机
@@ -314,7 +323,9 @@ namespace Hotline.Api.Controllers
             var toWork = _userCacheManager.GetWorkByTel(dto.TelNo);
             if (toWork is null)
                 throw UserFriendlyException.SameMessage("转接分机未进行工作");
-
+            var tel =await _deviceManager.QueryTelAsync(dto.TelNo,HttpContext.RequestAborted);
+            if (tel.TelStatus != ETelStatus.Ready)
+                throw UserFriendlyException.SameMessage("被叫分机不在线或正在通话中");
             await _deviceManager.ExtToExtAsync(work.TelNo, dto.TelNo, HttpContext.RequestAborted);
         }
 
@@ -366,6 +377,10 @@ namespace Hotline.Api.Controllers
             if (toWork is null)
                 throw UserFriendlyException.SameMessage("转接分机未进行工作");
 
+            var totel = await _deviceManager.QueryTelAsync(dto.TelNo, HttpContext.RequestAborted);
+            if (totel.TelStatus != ETelStatus.Ready)
+                throw UserFriendlyException.SameMessage("被叫分机不在线或正在通话中");
+
             var tel = await _deviceManager.QueryTelAsync(work.TelNo, HttpContext.RequestAborted);
             if (!string.IsNullOrEmpty(tel.ConversationId))
                 await _deviceManager.VisitorToExtAsync(tel.ConversationId, dto.TelNo, HttpContext.RequestAborted);
@@ -449,6 +464,10 @@ namespace Hotline.Api.Controllers
             if (toWork is null)
                 throw UserFriendlyException.SameMessage("转接分机未进行工作");
 
+            var totel = await _deviceManager.QueryTelAsync(dto.TelNo, HttpContext.RequestAborted);
+            if (totel.TelStatus != ETelStatus.Ready)
+                throw UserFriendlyException.SameMessage("被叫分机不在线或正在通话中");
+
             var tel = await _deviceManager.QueryTelAsync(work.TelNo, HttpContext.RequestAborted);
             if (!string.IsNullOrEmpty(tel.ConversationId))
                 await _deviceManager.OuterToExtAsync(tel.ConversationId, dto.TelNo, HttpContext.RequestAborted);
@@ -743,6 +762,62 @@ namespace Hotline.Api.Controllers
         }
 
 
+        #endregion
+
+        #region 话机业务
+
+        /// <summary>
+        /// 语音评价
+        /// </summary>
+        /// <param name="callId"></param>
+        /// <returns></returns>
+        [HttpGet("evaluate/{callId}")]
+        public async Task Evaluate(string callId)
+        {
+            //检查通话是否存在
+            var call = await _callRepository.GetAsync(callId, HttpContext.RequestAborted);
+            if (call is null)
+            {
+                throw new UserFriendlyException("未找到当前通话");
+            }
+            if (call.CallDirection!= ECallDirection.In)
+            {
+                throw new UserFriendlyException("当前通话不是来电,不能发送评价邀请");
+            }
+            if (call.CallStatus == ECallStatus.Bye)
+            {
+                throw new UserFriendlyException("当前通话已结束");
+            }
+            if (call.CallStatus != ECallStatus.Answered && call.CallStatus != ECallStatus.Answer)
+            {
+                throw new UserFriendlyException("当前未进行通话,不能发送评价邀请");
+            }
+            //获取配置
+            var correct = _ivrDomainService.GetCorrectIvr(call.ToNo, true);
+            if (correct is null)
+                throw new UserFriendlyException("系统未配置评价,请联系管理员");
+            //检查是否有评价录音配置
+            var ivrList = _ivrCacheManager.GetIvrs();
+            var ivr = ivrList.First(x => x.IvrCategoryId == correct.ReturnValue && x.IsRoot);
+
+            _logger.LogInformation("transfer to ivr.no:{ivrNo}", ivr.No);
+
+            //写入子表
+            var detail = new CallDetail()
+            {
+                CallId = call.Id,
+                CallStatus = ECallStatus.Evaluate,
+                ConversationId = call.ConversationId,
+                EventName = "EVALUATE",
+                FromNo = call.FromNo,
+                ToNo = call.ToNo,
+            };
+
+            await _callDetailRepository.AddAsync(detail, HttpContext.RequestAborted);
+
+            await _deviceManager.VisitorToMenuAsync(call.ConversationId, ivr.No, HttpContext.RequestAborted);
+        }
+
         #endregion
     }
 }

+ 13 - 0
src/Hotline.Application/Handlers/CallCenter/CallState/DtmfNotificationHandler.cs

@@ -67,6 +67,19 @@ namespace Hotline.Application.Handlers.CallCenter.CallState
                 }
                 detail.Remark = info;
                 await _callDetailRepository.AddAsync(detail, cancellationToken);
+
+                #region 处理评价
+
+                var callDetail = await _callDetailRepository.GetAsync(x => x.CallId == model.Id && x.EventName == "EVALUATE", cancellationToken);
+                if (callDetail != null)
+                {
+                    callDetail.Remark = info;
+                    await _callDetailRepository.UpdateAsync(callDetail, cancellationToken);
+                }
+
+                #endregion
+
+
                 //调用事件处理
                 var dtmfResult = await _ivrDomainService.GetDtmfAnswerAsync(menuId, info, cancellationToken);
 

+ 30 - 0
src/Hotline.Application/Handlers/CallCenter/CallState/RingExtToOuterNotificationHandler.cs

@@ -2,6 +2,7 @@
 using Hotline.CallCenter.Calls;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Notifications;
+using Hotline.Tools;
 using MediatR;
 
 namespace Hotline.Application.Handlers.CallCenter.CallState
@@ -52,6 +53,35 @@ namespace Hotline.Application.Handlers.CallCenter.CallState
 
                 await _callDetailRepository.AddAsync(detail, cancellationToken);
             }
+            else
+            {
+                var isp = PhoneIspTool.GetPhoneIsp(notification.Outer.To);
+                var callModel = new Call()
+                {
+                    CallStatus = ECallStatus.Alert,
+                    CallDirection = ECallDirection.Out,
+                    CallType = ECallType.ExtToOuter,
+                    ConversationId = notification.Outer.Id,
+                    FromNo = notification.Outer.From,
+                    ToNo = notification.Outer.To,
+                    Trunk = notification.Outer.Trunk,
+                    PhoneIsp = isp
+                };
+                callModel.Modified();
+                var callId = await _callRepository.AddAsync(callModel, cancellationToken);
+                //写入明细
+                var detail = new CallDetail()
+                {
+                    CallId = callId,
+                    CallStatus = ECallStatus.Alert,
+                    ConversationId = notification.Outer.Id,
+                    OMCallId = notification.Outer.CallId,
+                    EventName = notification.Attribute,
+                    FromNo = notification.Outer.From,
+                    ToNo = notification.Outer.To,
+                };
+                await _callDetailRepository.AddAsync(detail, cancellationToken);
+            }
         }
     }
 }

+ 67 - 0
src/Hotline.Application/Handlers/CallCenter/FlowControl/ByeVisitorAndMenuNotificationHandler.cs

@@ -0,0 +1,67 @@
+using Hotline.Caches;
+using Hotline.CallCenter.Calls;
+using Hotline.Realtimes;
+using Hotline.Share.Dtos.Realtime;
+using Hotline.Share.Enums.CallCenter;
+using Hotline.Share.Notifications;
+using MediatR;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Handlers.CallCenter.FlowControl
+{
+    public class ByeVisitorAndMenuNotificationHandler : INotificationHandler<ByeVisitorAndMenuNotification>
+    {
+        private readonly ICallRepository _callRepository;
+        private readonly ICallDetailRepository _callDetailRepository;
+        private readonly IRealtimeService _realtimeService;
+        private readonly IUserCacheManager _userCacheManager;
+
+        public ByeVisitorAndMenuNotificationHandler(ICallRepository callRepository,ICallDetailRepository callDetailRepository,IRealtimeService realtimeService,IUserCacheManager userCacheManager)
+        {
+            _callRepository = callRepository;
+            _callDetailRepository = callDetailRepository;
+            _realtimeService = realtimeService;
+            _userCacheManager = userCacheManager;
+        }
+
+        public async Task Handle(ByeVisitorAndMenuNotification notification, CancellationToken cancellationToken)
+        {
+            var model = await _callRepository.GetAsync(x => x.ConversationId == notification.Visitor.Id && x.FromNo == notification.Visitor.From && x.CreationTime >= DateTime.Now.AddHours(-2), cancellationToken);
+            if (model != null)
+            {
+                model.CallStatus = ECallStatus.Bye;
+                model.EndBy = EEndBy.To;
+                model.RingOffType = ERingOffType.Normal;
+                await _callRepository.UpdateAsync(model, cancellationToken);
+                var detail = new CallDetail()
+                {
+                    CallId = model.Id,
+                    CallStatus = ECallStatus.Bye,
+                    OMCallId = notification.Visitor.CallId,
+                    ConversationId = notification.Visitor.Id,
+                    EventName = notification.Attribute,
+                    FromNo = notification.Visitor.From,
+                    ToNo = notification.Visitor.To,
+                    Remark = notification.Menu.Id
+                };
+                await _callDetailRepository.AddAsync(detail, cancellationToken);
+
+                //查询应答分机号
+                var callDetailAnswer = await _callDetailRepository.GetAsync(x => x.CallId == model.Id && x.EventName == "ANSWER", true, d => d.CreationTime);
+                if (callDetailAnswer != null)
+                {
+                    //调用业务通知 通知订阅
+                    var workModel = _userCacheManager.GetWorkByTel(callDetailAnswer.AnswerNo);
+                    if (workModel != null)
+                    {
+                        await _realtimeService.ByeAsync(workModel.UserId, new ByeDto() { Id = model.Id }, cancellationToken);
+                    }
+                }
+            }
+        }
+    }
+}

+ 1 - 6
src/Hotline.Application/Handlers/CallCenter/FlowControl/IncomingNotificationHandler.cs

@@ -165,12 +165,7 @@ namespace Hotline.Application.Handlers.CallCenter.FlowControl
 
             public ECorrectIvr eCorrectIvr { get; set; }
         }
-
-        public enum ECorrectIvr
-        {
-            Ivr = 0,
-            Group = 1,
-        }
+        
 
         private CorrectIvr GetCorrectCategory(TrunkIvrManager settings)
         {

+ 6 - 0
src/Hotline.Share/Enums/CallCenter/ECallStatus.cs

@@ -61,6 +61,12 @@ public enum ECallStatus
     [Description("呼叫被应答")]
     Answered = 50,
 
+    /// <summary>
+    /// 评价
+    /// </summary>
+    [Description("评价")]
+    Evaluate = 59,
+
     /// <summary>
     /// 通话结束
     /// </summary>

+ 14 - 0
src/Hotline.Share/Enums/CallCenter/ECorrectIvr.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Enums.CallCenter
+{
+    public enum ECorrectIvr
+    {
+        Ivr = 0,
+        Group = 1,
+    }
+}

+ 12 - 0
src/Hotline.Share/Notifications/ByeVisitorAndMenuNotification.cs

@@ -0,0 +1,12 @@
+using Hotline.Share.Notifications.Base;
+using MediatR;
+
+namespace Hotline.Share.Notifications
+{
+    public class ByeVisitorAndMenuNotification:BaseEvent,INotification
+    {
+        public BaseVisitor Visitor { get; set; }
+
+        public BaseMenu Menu { get; set; }
+    }
+}

+ 6 - 0
src/Hotline/CallCenter/Ivrs/IIvrDomainService.cs

@@ -1,4 +1,5 @@
 using Hotline.Share.Dtos.CallCenter;
+using static Hotline.CallCenter.Ivrs.IvrDomainService;
 
 namespace Hotline.CallCenter.Ivrs
 {
@@ -61,5 +62,10 @@ namespace Hotline.CallCenter.Ivrs
         /// <returns></returns>
         Task ReplaceRootAsync(string ivrId, CancellationToken cancellationToken);
 
+        #region IVR流程处理
+
+        CorrectIvr GetCorrectIvr(string to, bool isEvaluate = false);
+
+        #endregion
     }
 }

+ 55 - 1
src/Hotline/CallCenter/Ivrs/IvrDomainService.cs

@@ -1,5 +1,6 @@
 using Hotline.Caches;
 using Hotline.CallCenter.Devices;
+using Hotline.Settings;
 using Hotline.Share.Dtos.CallCenter;
 using Hotline.Share.Enums.CallCenter;
 using MapsterMapper;
@@ -20,6 +21,8 @@ public class IvrDomainService : IIvrDomainService, IScopeDependency
     private readonly IUserCacheManager _userCacheManager;
     private readonly IMapper _mapper;
     private readonly ILogger<IvrDomainService> _logger;
+    private readonly ITypedCache<List<TrunkIvrManager>> _worktimeCache;
+    private readonly ITrunkIvrManagerRepository _trunkIvrManagerRepository;
 
     public IvrDomainService(
         IDeviceManager deviceManager,
@@ -29,7 +32,9 @@ public class IvrDomainService : IIvrDomainService, IScopeDependency
         ITelCacheManager telCacheManager,
         IUserCacheManager userCacheManager,
         IMapper mapper,
-        ILogger<IvrDomainService> logger)
+        ILogger<IvrDomainService> logger,
+        ITypedCache<List<TrunkIvrManager>> worktimeCache,
+        ITrunkIvrManagerRepository trunkIvrManagerRepository)
     {
         _deviceManager = deviceManager;
         _ivrRepository = ivrRepository;
@@ -39,6 +44,8 @@ public class IvrDomainService : IIvrDomainService, IScopeDependency
         _userCacheManager = userCacheManager;
         _mapper = mapper;
         _logger = logger;
+        _worktimeCache = worktimeCache;
+        _trunkIvrManagerRepository = trunkIvrManagerRepository;
     }
 
     /// <summary>
@@ -279,5 +286,52 @@ public class IvrDomainService : IIvrDomainService, IScopeDependency
         return !string.IsNullOrEmpty(strategy.Answer.PreVoice) ? $"{strategy.Answer.PreVoice}+{ivr.Voice}" : null;
     }
 
+    #endregion
+
+    #region IVR流程处理
+
+    
+    public class CorrectIvr
+    {
+        public string ReturnValue { get; set; }
+
+        public ECorrectIvr eCorrectIvr { get; set; }
+    }
+
+    public CorrectIvr GetCorrectIvr(string to,bool isEvaluate =false )
+    {
+        var worktimeSettings = _worktimeCache.GetOrAdd("worktimesettings", d =>
+        {
+            var settings = _trunkIvrManagerRepository.QueryAsync(x => x.IsEnable).GetAwaiter().GetResult();
+            return settings;
+        }, ExpireMode.Absolute,TimeSpan.FromDays(1));
+        var correct = GetCorrectCategory(worktimeSettings.First(x => x.TrunkId == to),isEvaluate);
+        return correct;
+    }
+
+    private CorrectIvr GetCorrectCategory(TrunkIvrManager settings,bool isEvaluate)
+    {
+        if (isEvaluate)
+        {
+            return new CorrectIvr() { eCorrectIvr = ECorrectIvr.Ivr, ReturnValue = settings.EvaluateCategory };
+        }
+
+        if (!settings.WorkDay.Any(x => x.weekValue == ((int)DateTime.Now.DayOfWeek).ToString()))
+            return new CorrectIvr() { eCorrectIvr = settings.RestCategory != "" ? ECorrectIvr.Ivr : ECorrectIvr.Group, ReturnValue = settings.RestCategory != "" ? settings.RestCategory : 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))
+            )
+        {
+            return new CorrectIvr() { eCorrectIvr = settings.WorkCategory != "" ? ECorrectIvr.Ivr : ECorrectIvr.Group, ReturnValue = settings.WorkCategory != "" ? settings.WorkCategory : settings.WorkToGroup };
+        }
+        else
+        {
+            return new CorrectIvr() { eCorrectIvr = settings.RestCategory != "" ? ECorrectIvr.Ivr : ECorrectIvr.Group, ReturnValue = settings.RestCategory != "" ? settings.RestCategory : settings.RestToGroup };
+        }
+    }
+
+
     #endregion
 }

+ 26 - 3
src/Hotline/CallCenter/Tels/TelDomainService.cs

@@ -18,19 +18,22 @@ public class TelDomainService : ITelDomainService, IScopeDependency
     private readonly ITelRestRepository _telRestRepository;
     private readonly ITelHoldRepository _telHoldRepository;
     private readonly IRealtimeService _realtimeService;
+    private readonly ITelCacheManager _telCacheManager;
 
     public TelDomainService(
         IDeviceManager deviceManager,
         ITelRepository telRepository,
         ITelRestRepository telRestRepository,
         ITelHoldRepository telHoldRepository,
-        IRealtimeService realtimeService)
+        IRealtimeService realtimeService,
+        ITelCacheManager telCacheManager)
     {
         _deviceManager = deviceManager;
         _telRepository = telRepository;
         _telRestRepository = telRestRepository;
         _telHoldRepository = telHoldRepository;
         _realtimeService = realtimeService;
+        _telCacheManager = telCacheManager;
     }
 
 
@@ -98,11 +101,20 @@ public class TelDomainService : ITelDomainService, IScopeDependency
         if (telRest == null)
             throw new UserFriendlyException($"无效分机休息编号, telRestId: {id}", "无效分机休息编号");
 
-        await _deviceManager.TelRestAsync(telRest.TelNo, cancellationToken);
+        //await _deviceManager.TelRestAsync(telRest.TelNo, cancellationToken);
+        
 
         telRest.ApplyStatus = ETelRestApplyStatus.Resting;
         telRest.StartTime = DateTime.Now;
         await _telRestRepository.UpdateAsync(telRest, cancellationToken);
+
+        #region 处理设备
+        var tel = _telCacheManager.GetTel(telRest.TelNo);
+        foreach (var group in tel.Groups)
+        {
+            await _deviceManager.ModifyGroupExtAsync(group.No, group.Distribution, group.Voice, tel.No, true, cancellationToken);
+        }
+        #endregion
         //通知前端休息通过
         await _realtimeService.RestApplyPassAsync(telRest.UserId, cancellationToken);
 
@@ -119,12 +131,23 @@ public class TelDomainService : ITelDomainService, IScopeDependency
         var tel = await _telRepository.GetAsync(telId, cancellationToken);
         if (tel is null)
             throw new UserFriendlyException("无效分机编号");
-        await _deviceManager.TelEndRestAsync(tel.No, cancellationToken);
+        //await _deviceManager.TelEndRestAsync(tel.No, cancellationToken);
         var restingTel = await _telRestRepository.GetAsync(d => d.TelId == telId && !d.EndTime.HasValue, cancellationToken);
         if (restingTel is null)
             throw new UserFriendlyException("未查询到分机休息信息");
         restingTel.EndRest();
         await _telRestRepository.UpdateAsync(restingTel, cancellationToken);
+
+        #region 处理设备
+        var telCache = _telCacheManager.GetTel(tel.No);
+        //更新分机组
+        foreach (var group in telCache.Groups)
+        {
+            await _deviceManager.ModifyGroupExtAsync(group.No, group.Distribution, group.Voice, "", true, cancellationToken);
+        }
+
+        #endregion
+
         return restingTel;
     }
 

+ 6 - 0
src/Hotline/Settings/TrunkIvrManager.cs

@@ -81,6 +81,12 @@ namespace Hotline.Settings
         /// 是否启用
         /// </summary>
         public bool IsEnable { get; set; }
+
+        /// <summary>
+        /// 评价语音
+        /// </summary>
+        [SugarColumn(IsNullable = true)]
+        public string EvaluateCategory { get; set; }
     }
 
 }

+ 5 - 0
src/Hotline/Users/UserDomainService.cs

@@ -95,6 +95,11 @@ namespace Hotline.Users
             //初始化解除静音
             await _deviceManager.UnMuteAsync(tel.No, cancellationToken);
 
+            foreach (var group in tel.Groups)
+            {
+                await _deviceManager.ModifyGroupExtAsync(group.No, group.Distribution, group.Voice, "", false, cancellationToken);
+            }
+
             #endregion
             return _mapper.Map<WorkDto>(work);
         }