xf 9 місяців тому
батько
коміт
3bae57262f

+ 84 - 28
src/Hotline.Api/Controllers/CommonPController.cs

@@ -10,9 +10,13 @@ using Microsoft.AspNetCore.Mvc;
 using MongoDB.Driver;
 using SqlSugar;
 using System.Reflection.Metadata;
+using Hotline.Application.CallCenter;
+using Hotline.CallCenter.Configs;
 using Hotline.FlowEngine.Workflows;
 using Hotline.Settings.TimeLimits;
+using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Enums.Order;
+using Microsoft.Extensions.Options;
 using XF.Domain.Authentications;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
@@ -28,40 +32,46 @@ namespace Hotline.Api.Controllers
         private readonly ISystemAreaDomainService _systemAreaDomainService;
         private readonly IMapper _mapper;
         private readonly ISessionContext _sessionContext;
-        private readonly IRepository<TrCallRecord> _trCallRecordRepository;
+        // private readonly IRepository<TrCallRecord> _trCallRecordRepository;
         private readonly IOrderRepository _orderRepository;
         private readonly IOrderDelayRepository _orderDelayRepository;
         private readonly ITimeLimitDomainService _timeLimitDomainService;
         private readonly IOrderScreenRepository _orderScreenRepository;
         private readonly IRepository<OrderVisitDetail> _orderVisitedDetailRepository;
+        private readonly ICallApplication _callApplication;
+        private readonly IOptionsSnapshot<CallCenterConfiguration> _callcenterOptions;
 
         public CommonPController(
             ISystemCommonOpinionDomainService commonOpinionDomainService,
             ISystemAreaDomainService systemAreaDomainService,
             ISessionContext sessionContext,
-            IRepository<TrCallRecord> trCallRecordRepository,
+            // IRepository<TrCallRecord> trCallRecordRepository,
             IOrderRepository orderRepository,
             IMapper mapper,
             IOrderDelayRepository orderDelayRepository,
             ITimeLimitDomainService timeLimitDomainService,
             IOrderScreenRepository orderScreenRepository,
-            IRepository<OrderVisitDetail> orderVisitedDetailRepository)
+            IRepository<OrderVisitDetail> orderVisitedDetailRepository,
+            ICallApplication callApplication,
+            IOptionsSnapshot<CallCenterConfiguration> callcenterOptions)
         {
             _commonOpinionDomainService = commonOpinionDomainService;
             _systemAreaDomainService = systemAreaDomainService;
             _mapper = mapper;
             _sessionContext = sessionContext;
-            _trCallRecordRepository = trCallRecordRepository;
+            // _trCallRecordRepository = trCallRecordRepository;
             _orderRepository = orderRepository;
             _orderDelayRepository = orderDelayRepository;
             _timeLimitDomainService = timeLimitDomainService;
             _orderScreenRepository = orderScreenRepository;
             _orderVisitedDetailRepository = orderVisitedDetailRepository;
+            _callApplication = callApplication;
+            _callcenterOptions = callcenterOptions;
         }
 
 
-
         #region 省市区
+
         /// <summary>
         /// 获取省市区树形
         /// </summary>
@@ -89,46 +99,88 @@ namespace Hotline.Api.Controllers
                 var orderQuery = _orderRepository.Queryable(false, false, false)
                     .Includes(o => o.Workflow, w => w.Steps);
                 //今日来电
-                var tadayCalls = await _trCallRecordRepository.Queryable()
-                    .Where(x => x.CallDirection == Share.Enums.CallCenter.ECallDirection.In && tadayTime.Equals(x.CreatedTime.ToString("yyyy-MM-dd"))).ToListAsync();
-                var callNum = tadayCalls.Count();
-                var validCallNum = tadayCalls.Where(x => x.Duration > 0 || x.QueueTims > 0 || x.RingTimes > 0).Count();
-                //今日接通率
-                var answeredNum = tadayCalls.Where(x => x.Duration > 0).Count();
-                var answeredRate = validCallNum > 0 ? Math.Round((double.Parse(answeredNum.ToString()) / double.Parse(validCallNum.ToString())) * 100, 2) + "%" : "-";
+                //  var tadayCalls = await _trCallRecordRepository.Queryable()
+                //      .Where(x => x.CallDirection == Share.Enums.CallCenter.ECallDirection.In && tadayTime.Equals(x.CreatedTime.ToString("yyyy-MM-dd"))).ToListAsync();
+                //  var callNum = tadayCalls.Count();
+                //  var validCallNum = tadayCalls.Where(x => x.Duration > 0 || x.QueueTims > 0 || x.RingTimes > 0).Count();
+                //  //今日接通率
+                //  var answeredNum = tadayCalls.Where(x => x.Duration > 0).Count();
+                // var answeredRate = validCallNum > 0 ? Math.Round((double.Parse(answeredNum.ToString()) / double.Parse(validCallNum.ToString())) * 100, 2) + "%" : "-";
+
+                int callNum = 0, validCallNum = 0, answeredNum = 0;
+                var answeredRate = string.Empty;
+                var today = DateTime.Today.Date;
+                if (_callcenterOptions.Value.CallCenterType == AppDefaults.CallCenterType.TianRun)
+                {
+                    var calls = await _callApplication.QueryTianrunCallsAsync(
+                        direction: ECallDirection.In,
+                        callStartTimeStart: today,
+                        callStartTimeEnd: today.AddDays(1).AddSeconds(-1),
+                        cancellationToken: HttpContext.RequestAborted);
+                    callNum = calls.Count();
+                    validCallNum = calls.Where(x => x.Duration > 0 || x.QueueTims > 0 || x.RingTimes > 0).Count();
+                    //今日接通率
+                    answeredNum = calls.Where(x => x.Duration > 0).Count();
+                    answeredRate = validCallNum > 0
+                        ? Math.Round((double.Parse(answeredNum.ToString()) / double.Parse(validCallNum.ToString())) * 100, 2) + "%"
+                        : "-";
+                }
+                else if (_callcenterOptions.Value.CallCenterType == AppDefaults.CallCenterType.XingTang)
+                {
+                    var calls = await _callApplication.QueryCallsAsync(
+                        callStartTimeStart: today,
+                        callStartTimeEnd: today.AddDays(1).AddSeconds(-1),
+                        cancellationToken: HttpContext.RequestAborted);
+                    callNum = calls.Count();
+                    validCallNum = calls.Where(x => x.Duration > 0 || x.WaitDuration > 0 || x.RingDuration > 0).Count();
+                    //今日接通率
+                    answeredNum = calls.Where(x => x.Duration > 0).Count();
+                    answeredRate = validCallNum > 0
+                        ? Math.Round((double.Parse(answeredNum.ToString()) / double.Parse(validCallNum.ToString())) * 100, 2) + "%"
+                        : "-";
+                }
+
                 //今日受理工单
                 var tadayOrders = await orderQuery
                     .Where(o => tadayTime.Equals(o.CreationTime.ToString("yyyy-MM-dd"))).ToListAsync();
                 var orderNum = tadayOrders.Count();
                 var directlyNum = tadayOrders.Where(o => o.ProcessType == Share.Enums.Order.EProcessType.Zhiban).Count();
-                return new { CallNum = callNum, ValidCallNum = validCallNum, AnsweredNum = answeredNum, AnsweredRate = answeredRate, OrderNum = orderNum, DirectlyNum = directlyNum };
+                return new
+                {
+                    CallNum = callNum, ValidCallNum = validCallNum, AnsweredNum = answeredNum, AnsweredRate = answeredRate, OrderNum = orderNum,
+                    DirectlyNum = directlyNum
+                };
             }
+
             //部门
             //今日待办 tasksOkNum
             //var time = DateTime.Parse(tadayTime);
             //工单
             var order = await _orderRepository.Queryable()
-               .Where(o => SqlFunc.JsonListObjectAny(o.HandlerUsers, "Key", _sessionContext.RequiredUserId) || SqlFunc.JsonListObjectAny(o.HandlerOrgs, "Key", _sessionContext.RequiredOrgId))
-               .GroupBy(o => o.Id).MergeTable()
-               .Select(o => new
-               {
-                   aboutExpire = SqlFunc.AggregateSum(SqlFunc.IIF(DateTime.Now > o.NearlyExpiredTime!.Value && DateTime.Now < o.ExpiredTime!.Value, 1, 0)),
-                   havExpired = SqlFunc.AggregateSum(SqlFunc.IIF(DateTime.Now > o.ExpiredTime!.Value, 1, 0)),
-                   countersignHandle = SqlFunc.AggregateSum(SqlFunc.IIF(o.CounterSignType.HasValue, 1, 0)),
-
-               }).FirstAsync();
+                .Where(o => SqlFunc.JsonListObjectAny(o.HandlerUsers, "Key", _sessionContext.RequiredUserId) ||
+                            SqlFunc.JsonListObjectAny(o.HandlerOrgs, "Key", _sessionContext.RequiredOrgId))
+                .GroupBy(o => o.Id).MergeTable()
+                .Select(o => new
+                {
+                    aboutExpire = SqlFunc.AggregateSum(SqlFunc.IIF(DateTime.Now > o.NearlyExpiredTime!.Value && DateTime.Now < o.ExpiredTime!.Value,
+                        1, 0)),
+                    havExpired = SqlFunc.AggregateSum(SqlFunc.IIF(DateTime.Now > o.ExpiredTime!.Value, 1, 0)),
+                    countersignHandle = SqlFunc.AggregateSum(SqlFunc.IIF(o.CounterSignType.HasValue, 1, 0)),
+                }).FirstAsync();
             var aboutExpire = order?.aboutExpire ?? 0;
             var havExpired = order?.havExpired ?? 0;
             var countersignHandle = order?.countersignHandle ?? 0;
             //延期
             var delay = await _orderDelayRepository.Queryable()
                 .Includes(x => x.Workflow)
-                .Where(x => SqlFunc.JsonListObjectAny(x.HandlerUsers, "Key", _sessionContext.RequiredUserId) || SqlFunc.JsonListObjectAny(x.HandlerOrgs, "Key", _sessionContext.RequiredOrgId))
+                .Where(x => SqlFunc.JsonListObjectAny(x.HandlerUsers, "Key", _sessionContext.RequiredUserId) ||
+                            SqlFunc.JsonListObjectAny(x.HandlerOrgs, "Key", _sessionContext.RequiredOrgId))
                 .Where(x => x.DelayState == EDelayState.Examining).CountAsync();
             //甄别
             var screenAudit = await _orderScreenRepository.Queryable()
                 .Includes(x => x.Workflow)
-                 .Where(x => SqlFunc.JsonListObjectAny(x.HandlerUsers, "Key", _sessionContext.RequiredUserId) || SqlFunc.JsonListObjectAny(x.HandlerOrgs, "Key", _sessionContext.RequiredOrgId))
+                .Where(x => SqlFunc.JsonListObjectAny(x.HandlerUsers, "Key", _sessionContext.RequiredUserId) ||
+                            SqlFunc.JsonListObjectAny(x.HandlerOrgs, "Key", _sessionContext.RequiredOrgId))
                 .Where(x => x.Status == EScreenStatus.Apply)
                 .CountAsync();
             var workTime = _timeLimitDomainService.CalcWorkTimeReduce(DateTime.Now, 5);
@@ -139,9 +191,13 @@ namespace Hotline.Api.Controllers
                 .Where(x => x.OrderVisit.VisitTime < DateTime.Now && x.OrderVisit.VisitTime > workTime)
                 .Where((x, s) => x.OrderVisit.VisitState == EVisitState.Visited && x.OrderVisit.IsCanHandle)
                 .Where((x, s) => x.VisitTarget == EVisitTarget.Org && x.VisitOrgCode == _sessionContext.OrgId && (
-                SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "1" || SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "2" ||
-                SqlFunc.JsonField(x.OrgHandledAttitude, "Key") == "1" || SqlFunc.JsonField(x.OrgHandledAttitude, "Key") == "2")).CountAsync();
-            return new { AboutExpire = aboutExpire, HavExpired = havExpired, CountersignHandle = countersignHandle, ScreenAudit = screenAudit, Delay = delay, ScreenHandle = screenHandle };
+                    SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "1" || SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "2" ||
+                    SqlFunc.JsonField(x.OrgHandledAttitude, "Key") == "1" || SqlFunc.JsonField(x.OrgHandledAttitude, "Key") == "2")).CountAsync();
+            return new
+            {
+                AboutExpire = aboutExpire, HavExpired = havExpired, CountersignHandle = countersignHandle, ScreenAudit = screenAudit, Delay = delay,
+                ScreenHandle = screenHandle
+            };
         }
     }
-}
+}

Різницю між файлами не показано, бо вона завелика
+ 235 - 131
src/Hotline.Api/Controllers/OrderController.cs


+ 77 - 22
src/Hotline.Api/Controllers/QualityController.cs

@@ -25,6 +25,9 @@ using Hotline.Ai.Quality;
 using Newtonsoft.Json;
 using Polly;
 using Hotline.Api.Filter;
+using Hotline.Application.CallCenter;
+using Hotline.CallCenter.Configs;
+using Microsoft.Extensions.Options;
 using XF.Domain.Constants;
 
 namespace Hotline.Api.Controllers
@@ -40,12 +43,14 @@ namespace Hotline.Api.Controllers
 		private readonly IRepository<QualityTemplateDetail> _qualiteyTemplateDetail;
 		private readonly IRepository<QualityProhibited> _qualiteyProhibited;
 		private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
-		private readonly IRepository<TrCallRecord> _trCallRecordRepository;
+		// private readonly IRepository<TrCallRecord> _trCallRecordRepository;
 		private readonly IQualityApplication _qualityApplication;
 		private readonly IOrderRepository _orderRepository;
 		private readonly IAiQualityService _aiQualityService;
 		private readonly ILogger<QualityController> _logger;
 		private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+		private readonly ICallApplication _callApplication;
+		private readonly IOptionsSnapshot<CallCenterConfiguration> _callcenterOptions;
 
 		public QualityController(
 			ISessionContext sessionContext,
@@ -57,13 +62,14 @@ namespace Hotline.Api.Controllers
 			IRepository<QualityTemplateDetail> qualiteyTemplateDetail,
 			IRepository<QualityProhibited> qualiteyProhibited,
 			ISystemDicDataCacheManager systemDicDataCacheManager,
-			IRepository<TrCallRecord> trCallRecordRepository,
+			// IRepository<TrCallRecord> trCallRecordRepository,
 			IQualityApplication qualityApplication,
 			IOrderRepository orderRepository,
 			IAiQualityService aiQualityService,
 			ILogger<QualityController> logger,
-			ISystemSettingCacheManager systemSettingCacheManager
-		)
+			ISystemSettingCacheManager systemSettingCacheManager,
+			ICallApplication callApplication,
+			IOptionsSnapshot<CallCenterConfiguration> callcenterOptions)
 		{
 			_sessionContext = sessionContext;
 			_mapper = mapper;
@@ -74,12 +80,14 @@ namespace Hotline.Api.Controllers
 			_qualiteyTemplateDetail = qualiteyTemplateDetail;
 			_qualiteyProhibited = qualiteyProhibited;
 			_systemDicDataCacheManager = systemDicDataCacheManager;
-			_trCallRecordRepository = trCallRecordRepository;
+			// _trCallRecordRepository = trCallRecordRepository;
 			_qualityApplication = qualityApplication;
 			_orderRepository = orderRepository;
 			_aiQualityService = aiQualityService;
 			_logger = logger;
 			_systemSettingCacheManager = systemSettingCacheManager;
+			_callApplication = callApplication;
+			_callcenterOptions = callcenterOptions;
 		}
 		#region 质检管理
 		/// <summary>
@@ -152,25 +160,72 @@ namespace Hotline.Api.Controllers
 				.Includes(x => x.QualityDetails)
 				.FirstAsync(x => x.Id == id);
 			var qualityDto = _mapper.Map<QualityDto>(quality);
-			if (qualityDto.Order != null) {
-                //var call = await _trCallRecordRepository.Queryable().Where(x => x.CallAccept == qualityDto.Order.CallId).FirstAsync(); //由CallAccept改为OtherAccept
-                var call = await _trCallRecordRepository.Queryable().Where(x => x.OtherAccept == qualityDto.Order.CallId).FirstAsync();
-                if (call != null)
-                {
-					qualityDto.Order.RecordingBaseAddress = call.RecordingBaseAddress;
-					qualityDto.Order.RecordingAbsolutePath = call.RecordingAbsolutePath;
-                }
+			// if (qualityDto.Order != null) {
+   //              //var call = await _trCallRecordRepository.Queryable().Where(x => x.CallAccept == qualityDto.Order.CallId).FirstAsync(); //由CallAccept改为OtherAccept
+   //              var call = await _trCallRecordRepository.Queryable().Where(x => x.OtherAccept == qualityDto.Order.CallId).FirstAsync();
+   //              if (call != null)
+   //              {
+			// 		qualityDto.Order.RecordingBaseAddress = call.RecordingBaseAddress;
+			// 		qualityDto.Order.RecordingAbsolutePath = call.RecordingAbsolutePath;
+   //              }
+			// }
+			// if (qualityDto.Visit != null)
+			// {
+   //              //var call = await _trCallRecordRepository.Queryable().Where(x => x.CallAccept == qualityDto.Visit.CallId).FirstAsync(); //由CallAccept改为OtherAccept
+   //              var call = await _trCallRecordRepository.Queryable().Where(x => x.OtherAccept == qualityDto.Visit.CallId).FirstAsync();
+   //              if (call != null)
+   //              {
+   //                  qualityDto.Visit.RecordingBaseAddress = call.RecordingBaseAddress;
+   //                  qualityDto.Visit.RecordingAbsolutePath = call.RecordingAbsolutePath;
+   //              }
+   //          }
+			
+			if (_callcenterOptions.Value.CallCenterType == AppDefaults.CallCenterType.TianRun)
+			{
+				if (qualityDto.Order != null) {
+					//var call = await _trCallRecordRepository.Queryable().Where(x => x.CallAccept == qualityDto.Order.CallId).FirstAsync(); //由CallAccept改为OtherAccept
+					//var call = await _trCallRecordRepository.Queryable().Where(x => x.OtherAccept == qualityDto.Order.CallId).FirstAsync();
+					var call = await _callApplication.GetTianrunCallAsync(qualityDto.Order.CallId, HttpContext.RequestAborted); 
+					if (call != null)
+					{
+						qualityDto.Order.RecordingBaseAddress = call.RecordingBaseAddress;
+						qualityDto.Order.RecordingAbsolutePath = call.RecordingAbsolutePath;
+					}
+				}
+				if (qualityDto.Visit != null)
+				{
+					//var call = await _trCallRecordRepository.Queryable().Where(x => x.CallAccept == qualityDto.Visit.CallId).FirstAsync(); //由CallAccept改为OtherAccept
+					// var call = await _trCallRecordRepository.Queryable().Where(x => x.OtherAccept == qualityDto.Visit.CallId).FirstAsync();
+					var call = await _callApplication.GetTianrunCallAsync(qualityDto.Visit.CallId, HttpContext.RequestAborted);
+					if (call != null)
+					{
+						qualityDto.Visit.RecordingBaseAddress = call.RecordingBaseAddress;
+						qualityDto.Visit.RecordingAbsolutePath = call.RecordingAbsolutePath;
+					}
+				}
 			}
-			if (qualityDto.Visit != null)
+			else if (_callcenterOptions.Value.CallCenterType == AppDefaults.CallCenterType.XingTang)
 			{
-                //var call = await _trCallRecordRepository.Queryable().Where(x => x.CallAccept == qualityDto.Visit.CallId).FirstAsync(); //由CallAccept改为OtherAccept
-                var call = await _trCallRecordRepository.Queryable().Where(x => x.OtherAccept == qualityDto.Visit.CallId).FirstAsync();
-                if (call != null)
-                {
-                    qualityDto.Visit.RecordingBaseAddress = call.RecordingBaseAddress;
-                    qualityDto.Visit.RecordingAbsolutePath = call.RecordingAbsolutePath;
-                }
-            }
+				if (!string.IsNullOrEmpty(qualityDto?.Order?.CallId))
+				{
+					var call = await _callApplication.GetCallAsync(qualityDto.Order.CallId, HttpContext.RequestAborted); 
+					if (call != null)
+					{
+						qualityDto.Order.RecordingBaseAddress = call.AudioFile;
+						qualityDto.Order.RecordingAbsolutePath = call.AudioFile;
+					}
+				}
+				if (!string.IsNullOrEmpty(qualityDto.Visit.CallId))
+				{
+					var call = await _callApplication.GetCallAsync(qualityDto.Visit.CallId, HttpContext.RequestAborted);
+					if (call != null)
+					{
+						qualityDto.Visit.RecordingBaseAddress = call.AudioFile;
+						qualityDto.Visit.RecordingAbsolutePath = call.AudioFile;
+					}
+				}
+			}
+			
 			return qualityDto;
 		}
 		#endregion

+ 361 - 0
src/Hotline.Application/CallCenter/DefaultCallApplication.cs

@@ -0,0 +1,361 @@
+using Hotline.Caching.Interfaces;
+using Hotline.CallCenter.BlackLists;
+using Hotline.CallCenter.Calls;
+using Hotline.CallCenter.Tels;
+using Hotline.Orders;
+using Hotline.Repository.SqlSugar.Extensions;
+using Hotline.Share.Dtos.CallCenter;
+using Hotline.Share.Dtos.TrCallCenter;
+using Hotline.Share.Enums.CallCenter;
+using Hotline.Users;
+using MapsterMapper;
+using Microsoft.Extensions.Logging;
+using XF.Domain.Authentications;
+using XF.Domain.Cache;
+using XF.Domain.Exceptions;
+using XF.Domain.Repository;
+
+namespace Hotline.Application.CallCenter;
+
+public abstract class DefaultCallApplication : ICallApplication
+{
+    private readonly IRepository<Tel> _telRepository;
+    private readonly IRepository<TelGroup> _telGroupRepository;
+    private readonly IWorkRepository _workRepository;
+    private readonly ITelRestRepository _telRestRepository;
+    private readonly IRepository<CallNative> _callNativeRepository;
+    private readonly IRepository<TelOperation> _telOperationRepository;
+    private readonly IRepository<CallidRelation> _callIdRelationRepository;
+    private readonly ITypedCache<Work> _cacheWork;
+    private readonly IUserCacheManager _userCacheManager;
+    private readonly ISessionContext _sessionContext;
+    private readonly IMapper _mapper;
+    private readonly ILogger<DefaultCallApplication> _logger;
+
+    public DefaultCallApplication(
+        IRepository<Tel> telRepository,
+        IRepository<TelGroup> telGroupRepository,
+        IWorkRepository workRepository,
+        ITelRestRepository telRestRepository,
+        IRepository<CallNative> callNativeRepository,
+        IRepository<TelOperation> telOperationRepository,
+        IRepository<CallidRelation> callIdRelationRepository,
+        ITypedCache<Work> cacheWork,
+        IUserCacheManager userCacheManager,
+        ISessionContext sessionContext,
+        IMapper mapper,
+        ILogger<DefaultCallApplication> logger)
+    {
+        _telRepository = telRepository;
+        _telGroupRepository = telGroupRepository;
+        _workRepository = workRepository;
+        _telRestRepository = telRestRepository;
+        _callNativeRepository = callNativeRepository;
+        _telOperationRepository = telOperationRepository;
+        _callIdRelationRepository = callIdRelationRepository;
+        _cacheWork = cacheWork;
+        _userCacheManager = userCacheManager;
+        _sessionContext = sessionContext;
+        _mapper = mapper;
+        _logger = logger;
+    }
+
+    public DefaultCallApplication()
+    {
+    }
+
+    /// <summary>
+    /// 查询分机
+    /// </summary>
+    public async Task<IReadOnlyList<TelDto>> QueryTelsAsync(CancellationToken cancellationToken)
+    {
+        return await _telRepository.Queryable()
+            .Select<TelDto>()
+            .ToListAsync(cancellationToken);
+    }
+
+    /// <summary>
+    /// 查询分机组
+    /// </summary>
+    public async Task<IReadOnlyList<TelGroupDto>> QueryTelGroupsAsync(CancellationToken cancellationToken)
+    {
+        return await _telGroupRepository.Queryable()
+            .Select<TelGroupDto>()
+            .ToListAsync(cancellationToken);
+    }
+
+    /// <summary>
+    /// 新增黑名单
+    /// </summary>
+    public abstract Task<string> AddBlackListAsync(AddBlacklistDto dto, CancellationToken cancellationToken);
+
+    /// <summary>
+    /// 删除黑名单
+    /// </summary>
+    public abstract Task RemoveBlackListAsync(string id, CancellationToken cancellationToken);
+
+    /// <summary>
+    /// 查询黑名单
+    /// </summary>
+    public abstract Task<List<Blacklist>> QueryBlackListsAsync(CancellationToken cancellationToken);
+
+    /// <summary>
+    /// 签入
+    /// </summary>
+    public async Task<TrOnDutyResponseDto> SignInAsync(SignInDto dto, CancellationToken cancellationToken)
+    {
+        if (string.IsNullOrEmpty(dto.TelNo))
+            throw UserFriendlyException.SameMessage("无效分机号");
+        var work = _userCacheManager.GetWorkByUserNoExp(_sessionContext.RequiredUserId);
+        if (work is not null)
+        {
+            //if (work.TelNo != dto.TelNo)
+            //{
+            //    throw UserFriendlyException.SameMessage("当前用户已签入其他分机");
+            //}
+            throw UserFriendlyException.SameMessage("当前用户已签入");
+        }
+
+        var telWork = _userCacheManager.GetWorkByTelNoExp(dto.TelNo);
+        if (telWork is not null)
+        {
+            throw UserFriendlyException.SameMessage("当前分机已被占用");
+        }
+
+        work = new Work(_sessionContext.RequiredUserId, _sessionContext.UserName,
+            dto.TelNo, dto.TelNo, null, null,
+            dto.GroupId, _sessionContext.StaffNo, null);
+        await _workRepository.AddAsync(work, cancellationToken);
+
+        return new TrOnDutyResponseDto
+        {
+            TelNo = dto.TelNo,
+            QueueId = dto.GroupId,
+            StartTime = work.StartTime,
+        };
+    }
+
+    /// <summary>
+    /// 签出
+    /// </summary>
+    public async Task SingOutAsync(CancellationToken cancellationToken)
+    {
+        var work = _userCacheManager.GetWorkByUserNoExp(_sessionContext.RequiredUserId);
+        if (work is null) return;
+
+        var telRest =
+            await _telRestRepository.GetAsync(x => x.TelNo == work.TelNo && !x.EndTime.HasValue, cancellationToken);
+        if (telRest is not null)
+        {
+            telRest.EndRest();
+            await _telRestRepository.UpdateAsync(telRest, cancellationToken);
+        }
+
+        work.OffDuty();
+        await _workRepository.UpdateAsync(work, cancellationToken);
+        await _cacheWork.RemoveAsync(work.GetKey(KeyMode.UserId), cancellationToken);
+        await _cacheWork.RemoveAsync(work.GetKey(KeyMode.TelNo), cancellationToken);
+    }
+
+    /// <summary>
+    /// 签出
+    /// </summary>
+    public async Task SingOutAsync(string telNo, CancellationToken cancellationToken)
+    {
+        var work = _userCacheManager.GetWorkByTelNoExp(telNo);
+        if (work is null) return;
+
+        var telRest =
+            await _telRestRepository.GetAsync(x => x.TelNo == work.TelNo && !x.EndTime.HasValue, cancellationToken);
+        if (telRest is not null)
+        {
+            telRest.EndRest();
+            await _telRestRepository.UpdateAsync(telRest, cancellationToken);
+        }
+
+        work.OffDuty();
+        await _workRepository.UpdateAsync(work, cancellationToken);
+        await _cacheWork.RemoveAsync(work.GetKey(KeyMode.UserId), cancellationToken);
+        await _cacheWork.RemoveAsync(work.GetKey(KeyMode.TelNo), cancellationToken);
+    }
+
+    /// <summary>
+    /// 查询当前用户的分机状态
+    /// </summary>
+    /// <param name="cancellationToken"></param>
+    /// <returns></returns>
+    public async Task<TrOnDutyResponseDto> GetTelStateAsync(CancellationToken cancellationToken)
+    {
+        var work = _userCacheManager.GetWorkByUserNoExp(_sessionContext.RequiredUserId);
+        if (work is null) return null;
+        return await Task.FromResult(new TrOnDutyResponseDto
+        {
+            TelNo = work.TelNo,
+            QueueId = work.QueueId,
+            StartTime = work.StartTime,
+        });
+    }
+
+    /// <summary>
+    /// 定量查询通话记录
+    /// </summary>
+    public async Task<IReadOnlyList<CallNativeDto>> QueryCallsFixedAsync(QueryCallsFixedDto dto, CancellationToken cancellationToken)
+    {
+        return await _callNativeRepository.Queryable(includeDeleted: true)
+            .LeftJoin<Order>((d, o) => d.CallNo == o.CallId)
+            .WhereIF(!string.IsNullOrEmpty(dto.OrderNo), (d, o) => o.No == dto.OrderNo)
+            .WhereIF(!string.IsNullOrEmpty(dto.FromNo), d => d.FromNo == dto.FromNo)
+            .WhereIF(!string.IsNullOrEmpty(dto.ToNo), d => d.ToNo == dto.ToNo)
+            .WhereIF(!string.IsNullOrEmpty(dto.UserName), d => d.UserName == dto.UserName)
+            .WhereIF(!string.IsNullOrEmpty(dto.TelNo), d => d.TelNo == dto.TelNo)
+            .WhereIF(dto.EndBy != null, d => d.EndBy == dto.EndBy)
+            .WhereIF(dto.CallStartTimeStart != null, d => d.BeginIvrTime >= dto.CallStartTimeStart)
+            .WhereIF(dto.CallStartTimeEnd != null, d => d.BeginIvrTime <= dto.CallStartTimeEnd)
+            .WhereIF(dto.IsConnected != null, d => d.AnsweredTime != null)
+            .WhereIF(dto.Direction != null, d => d.Direction == dto.Direction)
+            .WhereIF(dto.WaitDurationStart != null && dto.WaitDurationStart > 0, d => d.WaitDuration >= dto.WaitDurationStart)
+            .WhereIF(dto.WaitDurationEnd != null && dto.WaitDurationEnd > 0, d => d.WaitDuration <= dto.WaitDurationEnd)
+            .Select((d, o) => new CallNativeDto
+            {
+                OrderId = o.Id,
+                OrderNo = o.No,
+                Title = o.Title,
+            }, true)
+            .ToFixedListAsync(dto, cancellationToken);
+    }
+
+    /// <summary>
+    /// 查询分机操作记录(定量)
+    /// </summary>
+    public async Task<IReadOnlyList<TelOperation>> QueryTelOperationsAsync(QueryTelOperationsFixedDto dto, CancellationToken cancellationToken)
+    {
+        return await _telOperationRepository.Queryable()
+            .WhereIF(!string.IsNullOrEmpty(dto.UserName), d => d.UserName == dto.UserName)
+            .WhereIF(!string.IsNullOrEmpty(dto.StaffNo), d => d.StaffNo == dto.StaffNo)
+            .WhereIF(!string.IsNullOrEmpty(dto.GroupId), d => d.GroupId == dto.GroupId)
+            .WhereIF(dto.OperateState != null, d => d.OperateState == dto.OperateState)
+            .ToFixedListAsync(dto, cancellationToken);
+    }
+
+    /// <summary>
+    /// 依据通话记录编号获取映射后的callId
+    /// </summary>
+    public async Task<string> GetOrSetCallIdAsync(string callNo, CancellationToken cancellationToken)
+    {
+        var callOrder = await _callIdRelationRepository.GetAsync(callNo, cancellationToken);
+        if (callOrder == null)
+        {
+            callOrder = new CallidRelation
+            {
+                Id = callNo,
+                CallId = Ulid.NewUlid().ToString(),
+            };
+            try
+            {
+                await _callIdRelationRepository.AddAsync(callOrder, cancellationToken);
+            }
+            catch (Exception e)
+            {
+                _logger.LogError($"写入callidRelation失败:{e.Message}");
+            }
+        }
+
+        return callOrder.CallId;
+    }
+
+    /// <summary>
+    /// 批量获取callId
+    /// </summary>
+    public async Task<List<(string callNo, string callId)>> GetOrSetCallIdRangeAsync(List<string> callNos,
+        CancellationToken cancellationToken)
+    {
+        var relations = await _callIdRelationRepository.Queryable()
+            .Where(d => callNos.Contains(d.Id))
+            .ToListAsync(cancellationToken);
+
+        var rsp = new List<(string callNo, string callId)>();
+        var newRelations = new List<CallidRelation>();
+        foreach (var callNo in callNos)
+        {
+            var relation = relations.FirstOrDefault(d => d.Id == callNo);
+            if (relation is null)
+            {
+                relation = new CallidRelation
+                {
+                    Id = callNo,
+                    CallId = Ulid.NewUlid().ToString(),
+                };
+                newRelations.Add(relation);
+                rsp.Add(new(relation.Id, relation.CallId));
+            }
+            else
+            {
+                rsp.Add(new(relation.Id, relation.CallId));
+            }
+        }
+
+        await _callIdRelationRepository.AddRangeAsync(newRelations, cancellationToken);
+        return rsp;
+    }
+
+    /// <summary>
+    /// 查询通话记录
+    /// </summary>
+    public Task<CallNative?> GetCallAsync(string callId, CancellationToken cancellationToken)
+    {
+        if (string.IsNullOrEmpty(callId)) return null;
+        return _callNativeRepository.GetAsync(callId, cancellationToken);
+    }
+
+    /// <summary>
+    /// 查询通话记录
+    /// </summary>
+    public async Task<List<CallNative>> QueryCallsAsync(
+        string? phone,
+        ECallDirection? direction,
+        DateTime? callStartTimeStart,
+        DateTime? callStartTimeEnd,
+        CancellationToken cancellationToken)
+    {
+        if (string.IsNullOrEmpty(phone))
+            return new List<CallNative>();
+        return await _callNativeRepository.Queryable()
+            .WhereIF(direction.HasValue, d => d.Direction == direction)
+            .WhereIF(callStartTimeStart != null, d => d.BeginIvrTime >= callStartTimeStart)
+            .WhereIF(callStartTimeEnd != null, d => d.BeginIvrTime <= callStartTimeEnd)
+            .Where(d => d.FromNo == phone || d.ToNo == phone)
+            .OrderBy(d => d.CreationTime)
+            .ToListAsync(cancellationToken);
+    }
+
+    #region tianrun 临时方案
+
+    public Task<TrCallRecord?> GetTianrunCallAsync(string callId, CancellationToken cancellationToken)
+    {
+        throw new NotImplementedException();
+    }
+
+    /// <summary>
+    /// 关联通话记录与order(添润)
+    /// </summary>
+    public Task RelateTianrunCallWithOrderAsync(string callId, string orderId,
+        CancellationToken cancellationToken)
+    {
+        throw new NotImplementedException();
+    }
+
+    /// <summary>
+    /// 查询通话记录
+    /// </summary>
+    public Task<List<TrCallRecord>> QueryTianrunCallsAsync(
+        string? phone, 
+        ECallDirection? direction = null,
+        DateTime? callStartTimeStart = null,
+        DateTime? callStartTimeEnd = null,
+        CancellationToken cancellationToken = default)
+    {
+        throw new NotImplementedException();
+    }
+
+    #endregion
+}

+ 20 - 3
src/Hotline.Application/CallCenter/ICallApplication.cs

@@ -54,6 +54,10 @@ namespace Hotline.Application.CallCenter
         /// 签出
         /// </summary>
         Task SingOutAsync(CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 签出
+        /// </summary>
         Task SingOutAsync(string telNo, CancellationToken cancellationToken);
 
         /// <summary>
@@ -89,7 +93,15 @@ namespace Hotline.Application.CallCenter
         /// </summary>
         Task<CallNative?> GetCallAsync(string callId, CancellationToken cancellationToken);
 
-        Task<List<CallNative>> QueryCallsAsync(string phone, ECallDirection? direction = null, CancellationToken cancellationToken = default);
+        /// <summary>
+        /// 查询通话记录
+        /// </summary>
+        Task<List<CallNative>> QueryCallsAsync(
+            string? phone = null,
+            ECallDirection? direction = null,
+            DateTime? callStartTimeStart = null,
+            DateTime? callStartTimeEnd = null,
+            CancellationToken cancellationToken = default);
 
         #region tianrun
 
@@ -103,8 +115,13 @@ namespace Hotline.Application.CallCenter
         /// <summary>
         /// 查询通话记录
         /// </summary>
-        Task<List<TrCallRecord>> QueryTianrunCallsAsync(string phone, ECallDirection? direction = null, CancellationToken cancellationToken = default);
+        Task<List<TrCallRecord>> QueryTianrunCallsAsync(
+            string? phone = null, 
+            ECallDirection? direction = null,
+            DateTime? callStartTimeStart = null,
+            DateTime? callStartTimeEnd = null,
+            CancellationToken cancellationToken = default);
 
         #endregion
     }
-}
+}

+ 23 - 91
src/Hotline.Application/CallCenter/TianRunCallApplication.cs

@@ -20,7 +20,7 @@ using XF.Domain.Repository;
 
 namespace Hotline.Application.CallCenter
 {
-    public class TianRunCallApplication : ICallApplication
+    public class TianRunCallApplication : DefaultCallApplication
     {
         private readonly ISessionContext _sessionContext;
         private readonly IRepository<TrCallRecord> _trCallRecordRepository;
@@ -34,7 +34,7 @@ namespace Hotline.Application.CallCenter
             ITrApplication trApplication,
             ITelApplication telApplication,
             ISystemSettingCacheManager systemSettingCacheManager
-            )
+        )
         {
             _sessionContext = sessionContext;
             _trCallRecordRepository = trCallRecordRepository;
@@ -43,26 +43,10 @@ namespace Hotline.Application.CallCenter
             _systemSettingCacheManager = systemSettingCacheManager;
         }
 
-        /// <summary>
-        /// 查询分机
-        /// </summary>
-        public Task<IReadOnlyList<TelDto>> QueryTelsAsync(CancellationToken cancellationToken)
-        {
-            throw new NotImplementedException();
-        }
-
-        /// <summary>
-        /// 查询分机组
-        /// </summary>
-        public Task<IReadOnlyList<TelGroupDto>> QueryTelGroupsAsync(CancellationToken cancellationToken)
-        {
-            throw new NotImplementedException();
-        }
-
         /// <summary>
         /// 新增黑名单
         /// </summary>
-        public async Task<string> AddBlackListAsync(AddBlacklistDto dto, CancellationToken cancellationToken)
+        public override async Task<string> AddBlackListAsync(AddBlacklistDto dto, CancellationToken cancellationToken)
         {
             throw new NotImplementedException();
         }
@@ -70,7 +54,7 @@ namespace Hotline.Application.CallCenter
         /// <summary>
         /// 删除黑名单
         /// </summary>
-        public async Task RemoveBlackListAsync(string id, CancellationToken cancellationToken)
+        public override async Task RemoveBlackListAsync(string id, CancellationToken cancellationToken)
         {
             throw new NotImplementedException();
         }
@@ -78,7 +62,7 @@ namespace Hotline.Application.CallCenter
         /// <summary>
         /// 查询黑名单
         /// </summary>
-        public async Task<List<Blacklist>> QueryBlackListsAsync(CancellationToken cancellationToken)
+        public override async Task<List<Blacklist>> QueryBlackListsAsync(CancellationToken cancellationToken)
         {
             throw new NotImplementedException();
         }
@@ -86,16 +70,16 @@ namespace Hotline.Application.CallCenter
         /// <summary>
         /// 签入
         /// </summary>
-        public Task<TrOnDutyResponseDto> SignInAsync(SignInDto dto, CancellationToken cancellationToken) =>
-            _trApplication.OnSign(_sessionContext.RequiredUserId, dto.TelNo, (ETelModel)dto.TelModelState, cancellationToken);
+        public new async Task<TrOnDutyResponseDto> SignInAsync(SignInDto dto, CancellationToken cancellationToken) =>
+            await _trApplication.OnSign(_sessionContext.RequiredUserId, dto.TelNo, (ETelModel)dto.TelModelState, cancellationToken);
 
         /// <summary>
         /// 签出
         /// </summary>
-        public Task SingOutAsync(CancellationToken cancellationToken) =>
+        public new Task SingOutAsync(CancellationToken cancellationToken) =>
             _telApplication.SignOutAsync(_sessionContext.RequiredUserId, cancellationToken);
 
-        public Task SingOutAsync(string telNo, CancellationToken cancellationToken) =>
+        public new Task SingOutAsync(string telNo, CancellationToken cancellationToken) =>
             _telApplication.SignOutByTelNoAsync(telNo, cancellationToken);
 
         /// <summary>
@@ -103,63 +87,11 @@ namespace Hotline.Application.CallCenter
         /// </summary>
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
-        public Task<TrOnDutyResponseDto> GetTelStateAsync(CancellationToken cancellationToken) =>
+        public new Task<TrOnDutyResponseDto> GetTelStateAsync(CancellationToken cancellationToken) =>
             _trApplication.TelState(_sessionContext.RequiredUserId, cancellationToken);
 
-        /// <summary>
-        /// 定量查询通话记录
-        /// </summary>
-        Task<IReadOnlyList<CallNativeDto>> ICallApplication.QueryCallsFixedAsync(QueryCallsFixedDto dto, CancellationToken cancellationToken)
-        {
-            throw new NotImplementedException();
-        }
-
-        /// <summary>
-        /// 关联通话记录与工单或回访
-        /// </summary>
-        public Task RelateCallToOrderAsync(LinkCallRecordDto dto, CancellationToken cancellationToken)
-        {
-            throw new NotImplementedException();
-        }
-
-        /// <summary>
-        /// 查询分机操作记录(定量)
-        /// </summary>
-        public async Task<IReadOnlyList<TelOperation>> QueryTelOperationsAsync(QueryTelOperationsFixedDto dto, CancellationToken cancellationToken)
-        {
-            throw new NotImplementedException();
-        }
-
-        /// <summary>
-        /// 依据通话记录编号获取映射后的callId
-        /// </summary>
-        public async Task<string> GetOrSetCallIdAsync(string callNo, CancellationToken cancellationToken)
-        {
-            throw new NotImplementedException();
-        }
 
-        /// <summary>
-        /// 批量获取callId
-        /// </summary>
-        public async Task<List<(string callNo, string callId)>> GetOrSetCallIdRangeAsync(List<string> callNos, CancellationToken cancellationToken)
-        {
-            throw new NotImplementedException();
-        }
-
-        /// <summary>
-        /// 查询通话记录
-        /// </summary>
-        public async Task<CallNative?> GetCallAsync(string callId, CancellationToken cancellationToken)
-        {
-            throw new NotImplementedException();
-        }
-
-        public async Task<List<CallNative>> QueryCallsAsync(string phone, ECallDirection? direction, CancellationToken cancellationToken)
-        {
-            throw new NotImplementedException();
-        }
-
-        public async Task<TrCallRecord?> GetTianrunCallAsync(string callId, CancellationToken cancellationToken)
+        public new async Task<TrCallRecord?> GetTianrunCallAsync(string callId, CancellationToken cancellationToken)
         {
             if (string.IsNullOrEmpty(callId)) return null;
             var callRecord = await _trCallRecordRepository.GetAsync(
@@ -170,7 +102,7 @@ namespace Hotline.Application.CallCenter
         /// <summary>
         /// 关联通话记录与order(添润)
         /// </summary>
-        public async Task RelateTianrunCallWithOrderAsync(string callId, string orderId, CancellationToken cancellationToken)
+        public new async Task RelateTianrunCallWithOrderAsync(string callId, string orderId, CancellationToken cancellationToken)
         {
             var callRecord = await _trCallRecordRepository.GetAsync(
                 p => p.OtherAccept == callId && string.IsNullOrEmpty(p.OtherAccept) == false, cancellationToken);
@@ -185,19 +117,19 @@ namespace Hotline.Application.CallCenter
         /// <summary>
         /// 查询通话记录
         /// </summary>
-        public async Task<List<TrCallRecord>> QueryTianrunCallsAsync(string phone, ECallDirection? direction, CancellationToken cancellationToken)
+        public new async Task<List<TrCallRecord>> QueryTianrunCallsAsync(
+            string? phone,
+            ECallDirection? direction,
+            DateTime? callStartTimeStart,
+            DateTime? callStartTimeEnd,
+            CancellationToken cancellationToken)
         {
             return await _trCallRecordRepository.Queryable()
-                .Where(x => x.CallDirection == 0 && x.CPN == phone)
+                .WhereIF(!string.IsNullOrEmpty(phone), d => d.CPN == phone || d.CDPN == phone)
+                .WhereIF(direction != null, d => d.CallDirection == direction)
+                .WhereIF(callStartTimeStart != null, d => d.BeginIvrTime >= callStartTimeStart)
+                .WhereIF(callStartTimeEnd != null, d => d.BeginIvrTime <= callStartTimeEnd)
                 .OrderBy(x => x.CreatedTime).ToListAsync(cancellationToken);
         }
-
-        /// <summary>
-        /// 定量查询通话记录
-        /// </summary>
-        public async Task<IReadOnlyList<CallNative>> QueryCallsFixedAsync(QueryCallsFixedDto dto, CancellationToken cancellationToken)
-        {
-            throw new NotImplementedException();
-        }
     }
-}
+}

+ 307 - 280
src/Hotline.Application/CallCenter/XingTangCallApplication.cs

@@ -30,20 +30,20 @@ using XF.Domain.Repository;
 
 namespace Hotline.Application.CallCenter
 {
-    public class XingTangCallApplication : ICallApplication
+    public class XingTangCallApplication : DefaultCallApplication
     {
-        private readonly IRepository<Tel> _telRepository;
-        private readonly IRepository<TelGroup> _telGroupRepository;
-        private readonly IWorkRepository _workRepository;
-        private readonly ITelRestRepository _telRestRepository;
-        private readonly IRepository<CallNative> _callNativeRepository;
-        private readonly IRepository<TelOperation> _teloperationRepository;
-        private readonly IRepository<CallidRelation> _callidRelationRepository;
-        private readonly ITypedCache<Work> _cacheWork;
-        private readonly IUserCacheManager _userCacheManager;
-        private readonly ISessionContext _sessionContext;
-        private readonly IMapper _mapper;
-        private readonly ILogger<XingTangCallApplication> _logger;
+        // private readonly IRepository<Tel> _telRepository;
+        // private readonly IRepository<TelGroup> _telGroupRepository;
+        // private readonly IWorkRepository _workRepository;
+        // private readonly ITelRestRepository _telRestRepository;
+        // private readonly IRepository<CallNative> _callNativeRepository;
+        // private readonly IRepository<TelOperation> _teloperationRepository;
+        // private readonly IRepository<CallidRelation> _callidRelationRepository;
+        // private readonly ITypedCache<Work> _cacheWork;
+        // private readonly IUserCacheManager _userCacheManager;
+        // private readonly ISessionContext _sessionContext;
+        // private readonly IMapper _mapper;
+        // private readonly ILogger<XingTangCallApplication> _logger;
 
         public XingTangCallApplication(
             IRepository<Tel> telRepository,
@@ -57,297 +57,324 @@ namespace Hotline.Application.CallCenter
             IUserCacheManager userCacheManager,
             ISessionContext sessionContext,
             IMapper mapper,
-            ILogger<XingTangCallApplication> logger)
+            ILogger<XingTangCallApplication> logger
+        ) : base(telRepository, telGroupRepository, workRepository, telRestRepository, callNativeRepository,
+            teloperationRepository, callidRelationRepository, cacheWork, userCacheManager, sessionContext, mapper,
+            logger)
         {
-            _telRepository = telRepository;
-            _telGroupRepository = telGroupRepository;
-            _workRepository = workRepository;
-            _telRestRepository = telRestRepository;
-            _callNativeRepository = callNativeRepository;
-            _teloperationRepository = teloperationRepository;
-            _callidRelationRepository = callidRelationRepository;
-            _cacheWork = cacheWork;
-            _userCacheManager = userCacheManager;
-            _sessionContext = sessionContext;
-            _mapper = mapper;
-            _logger = logger;
+            // _telRepository = telRepository;
+            // _telGroupRepository = telGroupRepository;
+            // _workRepository = workRepository;
+            // _telRestRepository = telRestRepository;
+            // _callNativeRepository = callNativeRepository;
+            // _teloperationRepository = teloperationRepository;
+            // _callidRelationRepository = callidRelationRepository;
+            // _cacheWork = cacheWork;
+            // _userCacheManager = userCacheManager;
+            // _sessionContext = sessionContext;
+            // _mapper = mapper;
+            // _logger = logger;
         }
 
-        /// <summary>
-        /// 查询分机
-        /// </summary>
-        public async Task<IReadOnlyList<TelDto>> QueryTelsAsync(CancellationToken cancellationToken)
-        {
-            return await _telRepository.Queryable()
-                .Select<TelDto>()
-                .ToListAsync(cancellationToken);
-        }
-
-        /// <summary>
-        /// 查询分机组
-        /// </summary>
-        public async Task<IReadOnlyList<TelGroupDto>> QueryTelGroupsAsync(CancellationToken cancellationToken)
-        {
-            return await _telGroupRepository.Queryable()
-                .Select<TelGroupDto>()
-                .ToListAsync(cancellationToken);
-        }
-
-        /// <summary>
-        /// 新增黑名单
-        /// </summary>
-        public async Task<string> AddBlackListAsync(AddBlacklistDto dto, CancellationToken cancellationToken)
-        {
-            throw new NotImplementedException();
-        }
-
-        /// <summary>
-        /// 删除黑名单
-        /// </summary>
-        public async Task RemoveBlackListAsync(string id, CancellationToken cancellationToken)
-        {
-            throw new NotImplementedException();
-        }
-
-        /// <summary>
-        /// 查询黑名单
-        /// </summary>
-        public async Task<List<Blacklist>> QueryBlackListsAsync(CancellationToken cancellationToken)
-        {
-            throw new NotImplementedException();
-        }
-
-
-        /// <summary>
-        /// 签入
-        /// </summary>
-        public async Task<TrOnDutyResponseDto> SignInAsync(SignInDto dto, CancellationToken cancellationToken)
-        {
-            if (string.IsNullOrEmpty(dto.TelNo))
-                throw UserFriendlyException.SameMessage("无效分机号");
-            var work = _userCacheManager.GetWorkByUserNoExp(_sessionContext.RequiredUserId);
-            if (work is not null)
-            {
-                //if (work.TelNo != dto.TelNo)
-                //{
-                //    throw UserFriendlyException.SameMessage("当前用户已签入其他分机");
-                //}
-                throw UserFriendlyException.SameMessage("当前用户已签入");
-            }
-
-            var telWork = _userCacheManager.GetWorkByTelNoExp(dto.TelNo);
-            if (telWork is not null)
-            {
-                throw UserFriendlyException.SameMessage("当前分机已被占用");
-            }
-
-            work = new Work(_sessionContext.RequiredUserId, _sessionContext.UserName,
-                dto.TelNo, dto.TelNo, null, null,
-                dto.GroupId, _sessionContext.StaffNo, null);
-            await _workRepository.AddAsync(work, cancellationToken);
-
-            return new TrOnDutyResponseDto
-            {
-                TelNo = dto.TelNo,
-                QueueId = dto.GroupId,
-                StartTime = work.StartTime,
-            };
-        }
-
-        /// <summary>
-        /// 签出
-        /// </summary>
-        public async Task SingOutAsync(CancellationToken cancellationToken)
-        {
-            var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
-            if (work is null) return;
-
-            var telRest = await _telRestRepository.GetAsync(x => x.TelNo == work.TelNo && !x.EndTime.HasValue, cancellationToken);
-            if (telRest is not null)
-            {
-                telRest.EndRest();
-                await _telRestRepository.UpdateAsync(telRest, cancellationToken);
-            }
-
-            work.OffDuty();
-            await _workRepository.UpdateAsync(work, cancellationToken);
-            await _cacheWork.RemoveAsync(work.GetKey(KeyMode.UserId), cancellationToken);
-            await _cacheWork.RemoveAsync(work.GetKey(KeyMode.TelNo), cancellationToken);
-        }
-
-        public async Task SingOutAsync(string telNo, CancellationToken cancellationToken)
-        {
-            var work = _userCacheManager.GetWorkByTelNoExp(telNo);
-            if (work is null) return;
+        // /// <summary>
+        // /// 查询分机
+        // /// </summary>
+        // public async Task<IReadOnlyList<TelDto>> QueryTelsAsync(CancellationToken cancellationToken)
+        // {
+        //     return await _telRepository.Queryable()
+        //         .Select<TelDto>()
+        //         .ToListAsync(cancellationToken);
+        // }
+        //
+        // /// <summary>
+        // /// 查询分机组
+        // /// </summary>
+        // public async Task<IReadOnlyList<TelGroupDto>> QueryTelGroupsAsync(CancellationToken cancellationToken)
+        // {
+        //     return await _telGroupRepository.Queryable()
+        //         .Select<TelGroupDto>()
+        //         .ToListAsync(cancellationToken);
+        // }
 
-            var telRest = await _telRestRepository.GetAsync(x => x.TelNo == work.TelNo && !x.EndTime.HasValue, cancellationToken);
-            if (telRest is not null)
-            {
-                telRest.EndRest();
-                await _telRestRepository.UpdateAsync(telRest, cancellationToken);
-            }
-
-            work.OffDuty();
-            await _workRepository.UpdateAsync(work, cancellationToken);
-            await _cacheWork.RemoveAsync(work.GetKey(KeyMode.UserId), cancellationToken);
-            await _cacheWork.RemoveAsync(work.GetKey(KeyMode.TelNo), cancellationToken);
-        }
-
-        /// <summary>
-        /// 查询当前用户的分机状态
-        /// </summary>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        public async Task<TrOnDutyResponseDto> GetTelStateAsync(CancellationToken cancellationToken)
-        {
-            var work = _userCacheManager.GetWorkByUserNoExp(_sessionContext.RequiredUserId);
-            if (work is null) return null;
-            return await Task.FromResult(new TrOnDutyResponseDto
-            {
-                TelNo = work.TelNo,
-                QueueId = work.QueueId,
-                StartTime = work.StartTime,
-            });
-        }
-
-        /// <summary>
-        /// 定量查询通话记录
-        /// </summary>
-        public async Task<IReadOnlyList<CallNativeDto>> QueryCallsFixedAsync(QueryCallsFixedDto dto, CancellationToken cancellationToken)
-        {
-            return await _callNativeRepository.Queryable(includeDeleted: true)
-                .LeftJoin<Order>((d, o) => d.CallNo == o.CallId)
-                .WhereIF(!string.IsNullOrEmpty(dto.OrderNo), (d, o) => o.No == dto.OrderNo)
-                .WhereIF(!string.IsNullOrEmpty(dto.FromNo), d => d.FromNo == dto.FromNo)
-                .WhereIF(!string.IsNullOrEmpty(dto.ToNo), d => d.ToNo == dto.ToNo)
-                .WhereIF(!string.IsNullOrEmpty(dto.UserName), d => d.UserName == dto.UserName)
-                .WhereIF(!string.IsNullOrEmpty(dto.TelNo), d => d.TelNo == dto.TelNo)
-                .WhereIF(dto.EndBy != null, d => d.EndBy == dto.EndBy)
-                .WhereIF(dto.CallStartTimeBT != null, d => d.BeginIvrTime >= dto.CallStartTimeBT)
-                .WhereIF(dto.CallStartTimeLT != null, d => d.BeginIvrTime <= dto.CallStartTimeLT)
-                .WhereIF(dto.IsConnected != null, d => d.AnsweredTime != null)
-                .WhereIF(dto.Direction != null, d => d.Direction == dto.Direction)
-                .Select((d, o) => new CallNativeDto
-                {
-                    OrderId = o.Id,
-                    OrderNo = o.No,
-                    Title = o.Title,
-                }, true)
-                .ToFixedListAsync(dto, cancellationToken);
-        }
-
-        /// <summary>
-        /// 查询分机操作记录(定量)
-        /// </summary>
-        public async Task<IReadOnlyList<TelOperation>> QueryTelOperationsAsync(QueryTelOperationsFixedDto dto, CancellationToken cancellationToken)
-        {
-            return await _teloperationRepository.Queryable()
-                .WhereIF(!string.IsNullOrEmpty(dto.UserName), d => d.UserName == dto.UserName)
-                .WhereIF(!string.IsNullOrEmpty(dto.StaffNo), d => d.StaffNo == dto.StaffNo)
-                .WhereIF(!string.IsNullOrEmpty(dto.GroupId), d => d.GroupId == dto.GroupId)
-                .WhereIF(dto.OperateState != null, d => d.OperateState == dto.OperateState)
-                .ToFixedListAsync(dto, cancellationToken);
-        }
+        // /// <summary>
+        // /// 新增黑名单
+        // /// </summary>
+        // public async Task<string> AddBlackListAsync(AddBlacklistDto dto, CancellationToken cancellationToken)
+        // {
+        //     throw new NotImplementedException();
+        // }
+        //
+        // /// <summary>
+        // /// 删除黑名单
+        // /// </summary>
+        // public async Task RemoveBlackListAsync(string id, CancellationToken cancellationToken)
+        // {
+        //     throw new NotImplementedException();
+        // }
+        //
+        // /// <summary>
+        // /// 查询黑名单
+        // /// </summary>
+        // public async Task<List<Blacklist>> QueryBlackListsAsync(CancellationToken cancellationToken)
+        // {
+        //     throw new NotImplementedException();
+        // }
 
-        /// <summary>
-        /// 依据通话记录编号获取映射后的callId
-        /// </summary>
-        public async Task<string> GetOrSetCallIdAsync(string callNo, CancellationToken cancellationToken)
-        {
-            var callOrder = await _callidRelationRepository.GetAsync(callNo, cancellationToken);
-            if (callOrder == null)
-            {
-                callOrder = new CallidRelation
-                {
-                    Id = callNo,
-                    CallId = Ulid.NewUlid().ToString(),
-                };
-                try
-                {
-                    await _callidRelationRepository.AddAsync(callOrder, cancellationToken);
-                }
-                catch (Exception e)
-                {
-                    _logger.LogError($"写入callidRelation失败:{e.Message}");
-                }
-            }
-            return callOrder.CallId;
-        }
 
-        /// <summary>
-        /// 批量获取callId
-        /// </summary>
-        public async Task<List<(string callNo, string callId)>> GetOrSetCallIdRangeAsync(List<string> callNos, CancellationToken cancellationToken)
-        {
-            var relations = await _callidRelationRepository.Queryable()
-                .Where(d => callNos.Contains(d.Id))
-                .ToListAsync(cancellationToken);
+        // /// <summary>
+        // /// 签入
+        // /// </summary>
+        // public async Task<TrOnDutyResponseDto> SignInAsync(SignInDto dto, CancellationToken cancellationToken)
+        // {
+        //     if (string.IsNullOrEmpty(dto.TelNo))
+        //         throw UserFriendlyException.SameMessage("无效分机号");
+        //     var work = _userCacheManager.GetWorkByUserNoExp(_sessionContext.RequiredUserId);
+        //     if (work is not null)
+        //     {
+        //         //if (work.TelNo != dto.TelNo)
+        //         //{
+        //         //    throw UserFriendlyException.SameMessage("当前用户已签入其他分机");
+        //         //}
+        //         throw UserFriendlyException.SameMessage("当前用户已签入");
+        //     }
+        //
+        //     var telWork = _userCacheManager.GetWorkByTelNoExp(dto.TelNo);
+        //     if (telWork is not null)
+        //     {
+        //         throw UserFriendlyException.SameMessage("当前分机已被占用");
+        //     }
+        //
+        //     work = new Work(_sessionContext.RequiredUserId, _sessionContext.UserName,
+        //         dto.TelNo, dto.TelNo, null, null,
+        //         dto.GroupId, _sessionContext.StaffNo, null);
+        //     await _workRepository.AddAsync(work, cancellationToken);
+        //
+        //     return new TrOnDutyResponseDto
+        //     {
+        //         TelNo = dto.TelNo,
+        //         QueueId = dto.GroupId,
+        //         StartTime = work.StartTime,
+        //     };
+        // }
+        //
+        // /// <summary>
+        // /// 签出
+        // /// </summary>
+        // public async Task SingOutAsync(CancellationToken cancellationToken)
+        // {
+        //     var work = _userCacheManager.GetWorkByUserNoExp(_sessionContext.RequiredUserId);
+        //     if (work is null) return;
+        //
+        //     var telRest = await _telRestRepository.GetAsync(x => x.TelNo == work.TelNo && !x.EndTime.HasValue, cancellationToken);
+        //     if (telRest is not null)
+        //     {
+        //         telRest.EndRest();
+        //         await _telRestRepository.UpdateAsync(telRest, cancellationToken);
+        //     }
+        //
+        //     work.OffDuty();
+        //     await _workRepository.UpdateAsync(work, cancellationToken);
+        //     await _cacheWork.RemoveAsync(work.GetKey(KeyMode.UserId), cancellationToken);
+        //     await _cacheWork.RemoveAsync(work.GetKey(KeyMode.TelNo), cancellationToken);
+        // }
+        //
+        // public async Task SingOutAsync(string telNo, CancellationToken cancellationToken)
+        // {
+        //     var work = _userCacheManager.GetWorkByTelNoExp(telNo);
+        //     if (work is null) return;
+        //
+        //     var telRest = await _telRestRepository.GetAsync(x => x.TelNo == work.TelNo && !x.EndTime.HasValue, cancellationToken);
+        //     if (telRest is not null)
+        //     {
+        //         telRest.EndRest();
+        //         await _telRestRepository.UpdateAsync(telRest, cancellationToken);
+        //     }
+        //
+        //     work.OffDuty();
+        //     await _workRepository.UpdateAsync(work, cancellationToken);
+        //     await _cacheWork.RemoveAsync(work.GetKey(KeyMode.UserId), cancellationToken);
+        //     await _cacheWork.RemoveAsync(work.GetKey(KeyMode.TelNo), cancellationToken);
+        // }
+        //
+        // /// <summary>
+        // /// 查询当前用户的分机状态
+        // /// </summary>
+        // /// <param name="cancellationToken"></param>
+        // /// <returns></returns>
+        // public async Task<TrOnDutyResponseDto> GetTelStateAsync(CancellationToken cancellationToken)
+        // {
+        //     var work = _userCacheManager.GetWorkByUserNoExp(_sessionContext.RequiredUserId);
+        //     if (work is null) return null;
+        //     return await Task.FromResult(new TrOnDutyResponseDto
+        //     {
+        //         TelNo = work.TelNo,
+        //         QueueId = work.QueueId,
+        //         StartTime = work.StartTime,
+        //     });
+        // }
+        //
+        // /// <summary>
+        // /// 定量查询通话记录
+        // /// </summary>
+        // public async Task<IReadOnlyList<CallNativeDto>> QueryCallsFixedAsync(QueryCallsFixedDto dto, CancellationToken cancellationToken)
+        // {
+        //     return await _callNativeRepository.Queryable(includeDeleted: true)
+        //         .LeftJoin<Order>((d, o) => d.CallNo == o.CallId)
+        //         .WhereIF(!string.IsNullOrEmpty(dto.OrderNo), (d, o) => o.No == dto.OrderNo)
+        //         .WhereIF(!string.IsNullOrEmpty(dto.FromNo), d => d.FromNo == dto.FromNo)
+        //         .WhereIF(!string.IsNullOrEmpty(dto.ToNo), d => d.ToNo == dto.ToNo)
+        //         .WhereIF(!string.IsNullOrEmpty(dto.UserName), d => d.UserName == dto.UserName)
+        //         .WhereIF(!string.IsNullOrEmpty(dto.TelNo), d => d.TelNo == dto.TelNo)
+        //         .WhereIF(dto.EndBy != null, d => d.EndBy == dto.EndBy)
+        //         .WhereIF(dto.CallStartTimeBT != null, d => d.BeginIvrTime >= dto.CallStartTimeBT)
+        //         .WhereIF(dto.CallStartTimeLT != null, d => d.BeginIvrTime <= dto.CallStartTimeLT)
+        //         .WhereIF(dto.IsConnected != null, d => d.AnsweredTime != null)
+        //         .WhereIF(dto.Direction != null, d => d.Direction == dto.Direction)
+        //         .Select((d, o) => new CallNativeDto
+        //         {
+        //             OrderId = o.Id,
+        //             OrderNo = o.No,
+        //             Title = o.Title,
+        //         }, true)
+        //         .ToFixedListAsync(dto, cancellationToken);
+        // }
+        //
+        // /// <summary>
+        // /// 查询分机操作记录(定量)
+        // /// </summary>
+        // public async Task<IReadOnlyList<TelOperation>> QueryTelOperationsAsync(QueryTelOperationsFixedDto dto, CancellationToken cancellationToken)
+        // {
+        //     return await _teloperationRepository.Queryable()
+        //         .WhereIF(!string.IsNullOrEmpty(dto.UserName), d => d.UserName == dto.UserName)
+        //         .WhereIF(!string.IsNullOrEmpty(dto.StaffNo), d => d.StaffNo == dto.StaffNo)
+        //         .WhereIF(!string.IsNullOrEmpty(dto.GroupId), d => d.GroupId == dto.GroupId)
+        //         .WhereIF(dto.OperateState != null, d => d.OperateState == dto.OperateState)
+        //         .ToFixedListAsync(dto, cancellationToken);
+        // }
 
-            var rsp = new List<(string callNo, string callId)>();
-            var newRelations = new List<CallidRelation>();
-            foreach (var callNo in callNos)
-            {
-                var relation = relations.FirstOrDefault(d => d.Id == callNo);
-                if (relation is null)
-                {
-                    relation = new CallidRelation
-                    {
-                        Id = callNo,
-                        CallId = Ulid.NewUlid().ToString(),
-                    };
-                    newRelations.Add(relation);
-                    rsp.Add(new(relation.Id, relation.CallId));
-                }
-                else
-                {
-                    rsp.Add(new(relation.Id, relation.CallId));
-                }
-            }
+        // /// <summary>
+        // /// 依据通话记录编号获取映射后的callId
+        // /// </summary>
+        // public async Task<string> GetOrSetCallIdAsync(string callNo, CancellationToken cancellationToken)
+        // {
+        //     var callOrder = await _callidRelationRepository.GetAsync(callNo, cancellationToken);
+        //     if (callOrder == null)
+        //     {
+        //         callOrder = new CallidRelation
+        //         {
+        //             Id = callNo,
+        //             CallId = Ulid.NewUlid().ToString(),
+        //         };
+        //         try
+        //         {
+        //             await _callidRelationRepository.AddAsync(callOrder, cancellationToken);
+        //         }
+        //         catch (Exception e)
+        //         {
+        //             _logger.LogError($"写入callidRelation失败:{e.Message}");
+        //         }
+        //     }
+        //     return callOrder.CallId;
+        // }
 
-            await _callidRelationRepository.AddRangeAsync(newRelations, cancellationToken);
-            return rsp;
-        }
+        // /// <summary>
+        // /// 批量获取callId
+        // /// </summary>
+        // public async Task<List<(string callNo, string callId)>> GetOrSetCallIdRangeAsync(List<string> callNos, CancellationToken cancellationToken)
+        // {
+        //     var relations = await _callidRelationRepository.Queryable()
+        //         .Where(d => callNos.Contains(d.Id))
+        //         .ToListAsync(cancellationToken);
+        //
+        //     var rsp = new List<(string callNo, string callId)>();
+        //     var newRelations = new List<CallidRelation>();
+        //     foreach (var callNo in callNos)
+        //     {
+        //         var relation = relations.FirstOrDefault(d => d.Id == callNo);
+        //         if (relation is null)
+        //         {
+        //             relation = new CallidRelation
+        //             {
+        //                 Id = callNo,
+        //                 CallId = Ulid.NewUlid().ToString(),
+        //             };
+        //             newRelations.Add(relation);
+        //             rsp.Add(new(relation.Id, relation.CallId));
+        //         }
+        //         else
+        //         {
+        //             rsp.Add(new(relation.Id, relation.CallId));
+        //         }
+        //     }
+        //
+        //     await _callidRelationRepository.AddRangeAsync(newRelations, cancellationToken);
+        //     return rsp;
+        // }
+        //
+        // /// <summary>
+        // /// 查询通话记录
+        // /// </summary>
+        // public Task<CallNative?> GetCallAsync(string callId, CancellationToken cancellationToken)
+        // {
+        //     if (string.IsNullOrEmpty(callId)) return null;
+        //     return _callNativeRepository.GetAsync(callId, cancellationToken);
+        // }
+        //
+        // public async Task<List<CallNative>> QueryCallsAsync(string phone, ECallDirection? direction, CancellationToken cancellationToken)
+        // {
+        //     if (string.IsNullOrEmpty(phone))
+        //         return new List<CallNative>();
+        //     return await _callNativeRepository.Queryable()
+        //         .WhereIF(direction.HasValue, d=>d.Direction == direction)
+        //         .Where(d=>d.FromNo == phone || d.ToNo == phone)
+        //         .OrderBy(d=>d.CreationTime)
+        //         .ToListAsync(cancellationToken);
+        // }
 
+        // public async Task<TrCallRecord?> GetTianrunCallAsync(string callId, CancellationToken cancellationToken)
+        // {
+        //     throw new NotImplementedException();
+        // }
+        //
+        // /// <summary>
+        // /// 关联通话记录与order(添润)
+        // /// </summary>
+        // public async Task RelateTianrunCallWithOrderAsync(string callId, string orderId, CancellationToken cancellationToken)
+        // {
+        //     throw new NotImplementedException();
+        // }
+        //
+        // /// <summary>
+        // /// 查询通话记录
+        // /// </summary>
+        // public async Task<List<TrCallRecord>> QueryTianrunCallsAsync(string phone, ECallDirection? direction, CancellationToken cancellationToken)
+        // {
+        //     throw new NotImplementedException();
+        // }
         /// <summary>
-        /// 查询通话记录
+        /// 新增黑名单
         /// </summary>
-        public Task<CallNative?> GetCallAsync(string callId, CancellationToken cancellationToken)
-        {
-            if (string.IsNullOrEmpty(callId)) return null;
-            return _callNativeRepository.GetAsync(callId, cancellationToken);
-        }
-
-        public async Task<List<CallNative>> QueryCallsAsync(string phone, ECallDirection? direction, CancellationToken cancellationToken)
-        {
-            if (string.IsNullOrEmpty(phone))
-                return new List<CallNative>();
-            return await _callNativeRepository.Queryable()
-                .WhereIF(direction.HasValue, d=>d.Direction == direction)
-                .Where(d=>d.FromNo == phone || d.ToNo == phone)
-                .OrderBy(d=>d.CreationTime)
-                .ToListAsync(cancellationToken);
-        }
-
-        public async Task<TrCallRecord?> GetTianrunCallAsync(string callId, CancellationToken cancellationToken)
+        public override async Task<string> AddBlackListAsync(AddBlacklistDto dto, CancellationToken cancellationToken)
         {
             throw new NotImplementedException();
         }
 
         /// <summary>
-        /// 关联通话记录与order(添润)
+        /// 删除黑名单
         /// </summary>
-        public async Task RelateTianrunCallWithOrderAsync(string callId, string orderId, CancellationToken cancellationToken)
+        public override async Task RemoveBlackListAsync(string id, CancellationToken cancellationToken)
         {
             throw new NotImplementedException();
         }
 
         /// <summary>
-        /// 查询通话记录
+        /// 查询黑名单
         /// </summary>
-        public async Task<List<TrCallRecord>> QueryTianrunCallsAsync(string phone, ECallDirection? direction, CancellationToken cancellationToken)
+        public override async Task<List<Blacklist>> QueryBlackListsAsync(CancellationToken cancellationToken)
         {
             throw new NotImplementedException();
         }
-    }
 
-}
+       
+    }
+}

+ 32 - 6
src/Hotline.Application/Handlers/FlowEngine/WorkflowEndHandler.cs

@@ -1,9 +1,11 @@
 using DotNetCore.CAP;
+using Hotline.Application.CallCenter;
 using Hotline.Application.JudicialManagement;
 using Hotline.Application.Orders;
 using Hotline.Application.Quality;
 using Hotline.Article;
 using Hotline.CallCenter.Calls;
+using Hotline.CallCenter.Configs;
 using Hotline.CallCenter.Tels;
 using Hotline.FlowEngine.Notifications;
 using Hotline.FlowEngine.WorkflowModules;
@@ -19,6 +21,7 @@ using Hotline.Share.Enums.Quality;
 using MapsterMapper;
 using MediatR;
 using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
 using StackExchange.Redis;
 using XF.Domain.Authentications;
 using XF.Domain.Repository;
@@ -49,9 +52,11 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
     private readonly ILogger<WorkflowEndHandler> _logger;
     private readonly IKnowledgeRepository _knowledgeRepository;
     private readonly IKnowledgeWorkFlowRepository _knowledgeWorkFlowRepository;
-    private readonly IRepository<TrCallRecord> _trCallRecordRepository;
+    // private readonly IRepository<TrCallRecord> _trCallRecordRepository;
     private readonly IQualityApplication _qualityApplication;
     private readonly IEnforcementApplication _enforcementApplication;
+    private readonly ICallApplication _callApplication;
+    private readonly IOptionsSnapshot<CallCenterConfiguration> _callcenterOptions;
     private readonly ISessionContext _sessionContext;
 
     public WorkflowEndHandler(
@@ -77,9 +82,11 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
         IKnowledgeRepository knowledgeRepository,
         IKnowledgeWorkFlowRepository knowledgeWorkFlowRepository,
         ILogger<WorkflowEndHandler> logger,
-        IRepository<TrCallRecord> trCallRecordRepository,
+        // IRepository<TrCallRecord> trCallRecordRepository,
          IQualityApplication qualityApplication,
         IEnforcementApplication enforcementApplication,
+        ICallApplication callApplication,
+        IOptionsSnapshot<CallCenterConfiguration> callcenterOptions,
         ISessionContext sessionContext)
     {
         _knowledgeDomainService = knowledgeDomainService;
@@ -103,9 +110,11 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
         _logger = logger;
         _knowledgeRepository = knowledgeRepository;
         _knowledgeWorkFlowRepository = knowledgeWorkFlowRepository;
-        _trCallRecordRepository = trCallRecordRepository;
+        // _trCallRecordRepository = trCallRecordRepository;
         _qualityApplication = qualityApplication;
         _enforcementApplication = enforcementApplication;
+        _callApplication = callApplication;
+        _callcenterOptions = callcenterOptions;
         _sessionContext = sessionContext;
     }
 
@@ -193,16 +202,33 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
 
                 await _orderRepository.UpdateAsync(order, cancellationToken);
                 //var callRecord = await _trCallRecordRepository.GetAsync(p => p.CallAccept == order.CallId, cancellationToken); //由CallAccept改为OtherAccept
-                var callRecord = await _trCallRecordRepository.GetAsync(p => p.OtherAccept == order.CallId, cancellationToken);
+                //var callRecord = await _trCallRecordRepository.GetAsync(p => p.OtherAccept == order.CallId, cancellationToken);
                 var orderFlowDto = new OrderFlowDto
                 {
                     Order = _mapper.Map<OrderDto>(order),
                     WorkflowTrace = _mapper.Map<WorkflowTraceDto>(notification.Trace)
                 };
-                if (callRecord != null)
+                // if (callRecord != null)
+                // {
+                //     orderFlowDto.TrCallRecordDto = _mapper.Map<TrCallDto>(callRecord);
+                // }
+                
+                if (_callcenterOptions.Value.CallCenterType == AppDefaults.CallCenterType.TianRun)
                 {
-                    orderFlowDto.TrCallRecordDto = _mapper.Map<TrCallDto>(callRecord);
+                    // var callRecord = await _trCallRecordRepository.GetAsync(p => p.OtherAccept == order.CallId, cancellationToken);
+                    var callRecord = await _callApplication.GetTianrunCallAsync(order.CallId, cancellationToken);
+                    if (callRecord != null)
+                    {
+                        orderFlowDto.TrCallRecordDto = _mapper.Map<TrCallDto>(callRecord);
+                    }
+                }
+                else if (_callcenterOptions.Value.CallCenterType == AppDefaults.CallCenterType.XingTang)
+                {
+                    var call = await _callApplication.GetCallAsync(order.CallId, cancellationToken);
+                    if(call is not null)
+                        orderFlowDto.TrCallRecordDto = _mapper.Map<TrCallDto>(call);
                 }
+                
                 //这里需要判断是否是警情退回
                 orderFlowDto.IsNonPoliceReturn = notification.Dto.External == null ? false : notification.Dto.External.IsPoliceReturn;
                 await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderFiled, orderFlowDto, cancellationToken: cancellationToken);

+ 44 - 11
src/Hotline.Application/Handlers/FlowEngine/WorkflowStartHandler.cs

@@ -1,6 +1,8 @@
 using DotNetCore.CAP;
+using Hotline.Application.CallCenter;
 using Hotline.Application.Quality;
 using Hotline.CallCenter.Calls;
+using Hotline.CallCenter.Configs;
 using Hotline.CallCenter.Tels;
 using Hotline.FlowEngine.Notifications;
 using Hotline.FlowEngine.WorkflowModules;
@@ -18,6 +20,7 @@ using MapsterMapper;
 using MediatR;
 using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
 using XF.Domain.Entities;
 using XF.Domain.Repository;
 
@@ -33,10 +36,12 @@ namespace Hotline.Application.Handlers.FlowEngine
         private readonly IMapper _mapper;
         private readonly ILogger<WorkflowStartHandler> _logger;
         private readonly IQualityApplication _qualityApplication;
-        private readonly IRepository<TrCallRecord> _trCallRecordRepository;
+        // private readonly IRepository<TrCallRecord> _trCallRecordRepository;
         private readonly IOrderScreenRepository _orderScreenRepository;
         private readonly IOrderDelayRepository _orderDelayRepository;
         private readonly IRepository<User> _userRepository;
+        private readonly ICallApplication _callApplication;
+        private readonly IOptionsSnapshot<CallCenterConfiguration> _callcenterOptions;
         private readonly IMediator _mediator;
 
         public WorkflowStartHandler(
@@ -48,10 +53,12 @@ namespace Hotline.Application.Handlers.FlowEngine
             IQualityApplication qualityApplication,
             IMapper mapper,
             ILogger<WorkflowStartHandler> logger,
-            IRepository<TrCallRecord> trCallRecordRepository,
+            // IRepository<TrCallRecord> trCallRecordRepository,
             IOrderScreenRepository orderScreenRepository,
             IOrderDelayRepository orderDelayRepository,
             IRepository<User> userRepository,
+            ICallApplication callApplication,
+            IOptionsSnapshot<CallCenterConfiguration> callcenterOptions,
             IMediator mediator
         )
         {
@@ -63,10 +70,12 @@ namespace Hotline.Application.Handlers.FlowEngine
             _qualityApplication = qualityApplication;
             _mapper = mapper;
             _logger = logger;
-            _trCallRecordRepository = trCallRecordRepository;
+            // _trCallRecordRepository = trCallRecordRepository;
             _orderScreenRepository = orderScreenRepository;
             _orderDelayRepository = orderDelayRepository;
             _userRepository = userRepository;
+            _callApplication = callApplication;
+            _callcenterOptions = callcenterOptions;
             _mediator = mediator;
         }
 
@@ -95,12 +104,30 @@ namespace Hotline.Application.Handlers.FlowEngine
                     var publishCallRecordDto = new PublishCallRecrodDto() { };
                     //查询通话记录
                     //var callRecord = await _trCallRecordRepository.GetAsync(p => p.CallAccept == order.CallId, cancellationToken); //由CallAccept改为OtherAccept
-                    var callRecord = await _trCallRecordRepository.GetAsync(p => p.OtherAccept == order.CallId, cancellationToken);
-                    if (callRecord != null)
+                    // var callRecord = await _trCallRecordRepository.GetAsync(p => p.OtherAccept == order.CallId, cancellationToken);
+                    // if (callRecord != null)
+                    // {
+                    //     publishCallRecordDto.TrCallRecordDto = _mapper.Map<TrCallDto>(callRecord);
+                    // }
+
+
+                    if (_callcenterOptions.Value.CallCenterType == AppDefaults.CallCenterType.TianRun)
+                    {
+                        // var callRecord = await _trCallRecordRepository.GetAsync(p => p.OtherAccept == order.CallId, cancellationToken);
+                        var callRecord = await _callApplication.GetTianrunCallAsync(order.CallId, cancellationToken);
+                        if (callRecord != null)
+                        {
+                            publishCallRecordDto.TrCallRecordDto = _mapper.Map<TrCallDto>(callRecord);
+                        }
+                    }
+                    else if (_callcenterOptions.Value.CallCenterType == AppDefaults.CallCenterType.XingTang)
                     {
-                        publishCallRecordDto.TrCallRecordDto = _mapper.Map<TrCallDto>(callRecord);
+                        var call = await _callApplication.GetCallAsync(order.CallId, cancellationToken);
+                        if(call is not null)
+                            publishCallRecordDto.TrCallRecordDto = _mapper.Map<TrCallDto>(call);
                     }
 
+
                     publishCallRecordDto.Order = _mapper.Map<OrderDto>(order);
                     publishCallRecordDto.WorkflowTrace = _mapper.Map<WorkflowTraceDto>(notification.Trace);
 
@@ -115,7 +142,8 @@ namespace Hotline.Application.Handlers.FlowEngine
                             {
                                 case EFlowAssignType.Org:
                                     var orgCodes = notification.FlowAssignInfo.HandlerObjects.Select(x => x.Key);
-                                    var orgList = await _userRepository.Queryable().Where(x => orgCodes.Contains(x.OrgId) && x.Roles.Any(d => d.Id == "08dae71e-0eca-4bc4-89fe-7eaefae8a98e")).ToListAsync();
+                                    var orgList = await _userRepository.Queryable().Where(x =>
+                                        orgCodes.Contains(x.OrgId) && x.Roles.Any(d => d.Id == "08dae71e-0eca-4bc4-89fe-7eaefae8a98e")).ToListAsync();
                                     foreach (var item in orgList)
                                     {
                                         if (!string.IsNullOrEmpty(item.PhoneNo))
@@ -131,15 +159,16 @@ namespace Hotline.Application.Handlers.FlowEngine
                                                 TemplateCode = "1007",
                                                 Params = new List<string>() { order.No },
                                                 TelNumber = item.PhoneNo,
-
                                             };
                                             await _mediator.Publish(new PushMessageNotify(messageDto), cancellationToken);
                                         }
                                     }
+
                                     break;
                                 case EFlowAssignType.User:
                                     var userCodes = notification.FlowAssignInfo.HandlerObjects.Select(x => x.Key);
-                                    var userList = await _userRepository.Queryable().Where(x => userCodes.Contains(x.Id) && x.Roles.Any(d => d.Id == "08dae71e-0eca-4bc4-89fe-7eaefae8a98e")).ToListAsync();
+                                    var userList = await _userRepository.Queryable().Where(x =>
+                                        userCodes.Contains(x.Id) && x.Roles.Any(d => d.Id == "08dae71e-0eca-4bc4-89fe-7eaefae8a98e")).ToListAsync();
                                     foreach (var item in userList)
                                     {
                                         var messageDto = new Share.Dtos.Push.MessageDto
@@ -153,17 +182,19 @@ namespace Hotline.Application.Handlers.FlowEngine
                                             TemplateCode = "1007",
                                             Params = new List<string>() { order.No },
                                             TelNumber = item.PhoneNo,
-
                                         };
                                         await _mediator.Publish(new PushMessageNotify(messageDto), cancellationToken);
                                     }
+
                                     break;
                                 default:
                                     break;
                             }
                         }
                     }
-                    catch{}
+                    catch
+                    {
+                    }
 
                     //写入质检
                     await _qualityApplication.AddQualityAsync(EQualitySource.Accepted, order.Id, cancellationToken);
@@ -184,6 +215,7 @@ namespace Hotline.Application.Handlers.FlowEngine
                         screen.Flowed(workflow.FlowedUserIds, workflow.FlowedOrgIds, workflow.HandlerUsers, workflow.HandlerOrgs);
                         await _orderScreenRepository.UpdateAsync(screen, cancellationToken);
                     }
+
                     break;
                 case WorkflowModuleConsts.OrderDelay:
                     var orderDelay = await _orderDelayRepository.Queryable()
@@ -194,6 +226,7 @@ namespace Hotline.Application.Handlers.FlowEngine
                         orderDelay.Flowed(workflow.FlowedUserIds, workflow.FlowedOrgIds, workflow.HandlerUsers, workflow.HandlerOrgs);
                         await _orderDelayRepository.UpdateAsync(orderDelay, cancellationToken);
                     }
+
                     break;
             }
         }

+ 8 - 0
src/Hotline.Application/Jobs/XingTangTelOperationSyncJob.cs

@@ -4,6 +4,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using Hotline.Caching.Interfaces;
 using Microsoft.Extensions.Logging;
 using Hotline.Repository.SqlSugar;
 using SqlSugar;
@@ -20,6 +21,7 @@ namespace Hotline.Application.Jobs
     {
         private readonly IRepository<User> _userRepository;
         private readonly IRepository<TelOperation> _telOperationRepository;
+        private readonly IUserCacheManager _userCacheManager;
         private readonly IMapper _mapper;
         private readonly ILogger<XingTangTelOperationSyncJob> _logger;
         private readonly ISqlSugarClient _db;
@@ -28,12 +30,14 @@ namespace Hotline.Application.Jobs
             ISugarUnitOfWork<XingTangDbContext> uow,
             IRepository<User> userRepository,
             IRepository<TelOperation> telOperationRepository,
+            IUserCacheManager userCacheManager,
             IMapper mapper,
             ILogger<XingTangTelOperationSyncJob> logger)
         {
             _db = uow.Db;
             _userRepository = userRepository;
             _telOperationRepository = telOperationRepository;
+            _userCacheManager = userCacheManager;
             _mapper = mapper;
             _logger = logger;
         }
@@ -73,7 +77,11 @@ namespace Hotline.Application.Jobs
                     {
                         operation.UserId = user.Id;
                         operation.UserName = user.Name;
+                        var work = _userCacheManager.GetWorkByUserNoExp(user.Id);
+                        if (work is not null)
+                            operation.GroupId = work.QueueId;
                     }
+                    
                 }
 
                 await _telOperationRepository.AddRangeAsync(operations, context.CancellationToken);

+ 18 - 6
src/Hotline.Application/Mappers/CallMapperConfigs.cs

@@ -7,6 +7,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using Hotline.CallCenter.Tels;
 using XingTang.Sdk;
 
 namespace Hotline.Application.Mappers
@@ -89,16 +90,27 @@ namespace Hotline.Application.Mappers
                         : EEndBy.To;
                 });
 
+            config.ForType<XingtangSeatOperation, TelOperation>()
+                .Map(d => d.StaffNo, s => s.UserCode)
+                .Map(d => d.TelNo, s => s.Ext)
+                .Map(d => d.OperateState, s => s.ExecutionState)
+                .Map(d => d.OperateStateText, s => s.ExtutionStateText)
+                .Map(d => d.OperateTime, s => s.ExecutionTime)
+                ;
+
             config.ForType<CallNative, TrCallDto>()
-                .Map(d => d.CallDirection, s => s.Direction)
                 .Map(d => d.CPN, s => s.FromNo)
-                .Map(d => d.CDPN, s => s.ToNo)
-                .Map(d => d.CreatedTime, s => s.CreationTime)
+                .Map(d => d.OnState, s => s.AnsweredTime.HasValue ? EOnState.On : EOnState.NoOn)
+                .Map(d => d.CallDirection, s => s.Direction)
+                .Map(d => d.AnsweredTime, s => s.AnsweredTime)
                 .Map(d => d.OverTime, s => s.EndTime)
-                .Map(d => d.Gateway, s => s.ToNo)
-                .Map(d => d.EndRingTimg, s => s.EndRingTime)
+                .Map(d => d.BeginIvrTime, s => s.BeginIvrTime)
+                .Map(d => d.BeginQueueTime, s => s.BeginQueueTime)
+                .Map(d => d.BeginRingTime, s => s.BeginRingTime)
+                .Map(d => d.Duration, s => s.Duration)
+                .Map(d => d.TelNo, s => s.TelNo)
+                .Map(d => d.RecordingFileUrl, s => s.AudioFile)
                 ;
-
         }
 
         private DateTime? FormatDateTime(string? time)

+ 12 - 2
src/Hotline.Share/Dtos/CallCenter/QueryCallsFixedDto.cs

@@ -24,12 +24,12 @@ namespace Hotline.Share.Dtos.CallCenter
         /// <summary>
         /// 呼入时间
         /// </summary>
-        public DateTime? CallStartTimeBT { get; set; }
+        public DateTime? CallStartTimeStart { get; set; }
 
         /// <summary>
         /// 呼入时间
         /// </summary>
-        public DateTime? CallStartTimeLT { get; set; }
+        public DateTime? CallStartTimeEnd { get; set; }
 
         /// <summary>
         /// 是否接通
@@ -40,5 +40,15 @@ namespace Hotline.Share.Dtos.CallCenter
         /// 通话方向
         /// </summary>
         public ECallDirection? Direction { get; set; }
+
+        /// <summary>
+        /// 等待时长
+        /// </summary>
+        public int? WaitDurationStart { get; set; }
+        
+        /// <summary>
+        /// 等待时长
+        /// </summary>
+        public int? WaitDurationEnd { get; set; }
     }
 }

Деякі файли не було показано, через те що забагато файлів було змінено