Parcourir la source

Merge branch 'release' of http://110.188.24.182:10023/Fengwo/hotline into release

tangjiang il y a 7 mois
Parent
commit
cf323a4674

+ 33 - 23
src/Hotline.Api/Controllers/Bigscreen/SeatController.cs

@@ -1,20 +1,26 @@
 using Hotline.Application.Bigscreen;
 using Hotline.Caching.Interfaces;
 using Hotline.Settings;
+using Hotline.Share.Dtos.TrCallCenter;
+using Mapster;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
+using Tr.Sdk;
+using Tr.Sdk.Tels;
 
 namespace Hotline.Api.Controllers.Bigscreen
 {
-	public class SeatController : BaseController
-	{
-		private readonly ISeatStateDataService _seatStateDataService;
-		private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+    public class SeatController : BaseController
+    {
+        private readonly ISeatStateDataService _seatStateDataService;
+        private readonly ITrClient _trClient;
+        private readonly ISystemSettingCacheManager _systemSettingCacheManager;
 
-        public SeatController(ISeatStateDataService seatStateDataService, ISystemSettingCacheManager systemSettingCacheManager)
+        public SeatController(ISeatStateDataService seatStateDataService, ISystemSettingCacheManager systemSettingCacheManager, ITrClient trClient)
         {
             _seatStateDataService = seatStateDataService;
             _systemSettingCacheManager = systemSettingCacheManager;
+            _trClient = trClient;
         }
 
         /// <summary>
@@ -22,28 +28,32 @@ namespace Hotline.Api.Controllers.Bigscreen
         /// </summary>
         /// <returns></returns>
         [AllowAnonymous]
-		[HttpGet("base_data")]
-		public async Task<object> GetSeatStateData() {
-			var call24 = await _seatStateDataService.GetCall24(HttpContext.RequestAborted);
-			var callTop10 = await _seatStateDataService.GetCallTop10(HttpContext.RequestAborted);
-			var callList =await _seatStateDataService.GetCallList(HttpContext.RequestAborted);
-			var callAverage = await _seatStateDataService.GetCallAverage(HttpContext.RequestAborted);
-			
+        [HttpGet("base_data")]
+        public async Task<object> GetSeatStateData()
+        {
+            var call24 = await _seatStateDataService.GetCall24(HttpContext.RequestAborted);
+            var callTop10 = await _seatStateDataService.GetCallTop10(HttpContext.RequestAborted);
+            var callList = await _seatStateDataService.GetCallList(HttpContext.RequestAborted);
+            var callAverage = await _seatStateDataService.GetCallAverage(HttpContext.RequestAborted);
+
 
             return new object[] { call24, callTop10, callList, callAverage };
-		}
+        }
 
 
-		/// <summary>
-		/// 获取监听分机
-		/// </summary>
-		/// <returns></returns>
-		[AllowAnonymous]
-		[HttpGet("query-listentels")]
-		public async Task<object> GetListenTels()
-		{
+        /// <summary>
+        /// 获取监听分机
+        /// </summary>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpGet("query-listentels")]
+        public async Task<List<TelOutDto>> GetListenTels()
+        {
             var listenTels = _systemSettingCacheManager.GetSetting(SettingConstants.ListenTels)?.SettingValue;
-			return listenTels;
+            return (await _trClient.QueryTelsAsync(new QueryTelRequest(), HttpContext.RequestAborted))
+                .Where(m => listenTels.Contains(m.TelNo))
+                .ToList()
+                .Adapt<List<TelOutDto>>();
         }
-	}
+    }
 }

+ 12 - 2
src/Hotline.Api/Controllers/IPPbxController.cs

@@ -120,6 +120,8 @@ namespace Hotline.Api.Controllers
         public async Task<List<TrTelDto>> TrQueryTels()
         {
             var tels = await _trClient.QueryTelsAsync(new QueryTelRequest() { }, HttpContext.RequestAborted);
+            var listenTels = _systemSettingCacheManager.GetSetting(SettingConstants.ListenTels)?.SettingValue;
+            tels = tels.Where(m => listenTels.Contains(m.TelNo) ==  false).ToList();
             var returnlist = _mapper.Map<List<TrTelDto>>(tels);
             string callOutQueueId = _systemSettingCacheManager.GetSetting(SettingConstants.CallOutQueueId).SettingValue[0];
             returnlist.ForEach(x =>
@@ -136,10 +138,10 @@ namespace Hotline.Api.Controllers
         /// <returns></returns>
         [HttpGet("query-telstate")]
         [AllowAnonymous]
-        public async Task<IReadOnlyList<TrTelStateDto>> TrQueryTelState([FromQuery] string? state)
+        public async Task<IReadOnlyList<TrTelStateDto>> TrQueryTelState([FromQuery] string? state, bool hasListen)
         {
             var tels = await _trClient.QueryTelStateAsync(new QueryTelStateRequest() { State = state }, HttpContext.RequestAborted);
-            //var workList = _userCacheManager.GetWorks();
+            var listenTels = _systemSettingCacheManager.GetSetting(SettingConstants.ListenTels)?.SettingValue;
             var workList = await _workRepository.Queryable().Where(d=> 1 == 1 && !d.EndTime.HasValue).ToListAsync();
             var query = from tel in tels.AgentList
                         join works in workList on tel.TelNo equals works.TelNo into workD
@@ -168,6 +170,10 @@ namespace Hotline.Api.Controllers
                             WorkUserId = (work != null) ? work.UserId: "",
                             WorkUserName = (work != null) ? work.UserName: "",
                         };
+            if (hasListen == false)
+            {
+                query = query.Where(m => listenTels.Contains(m.TelNo) == false);
+            }
             var list = query.OrderBy(x=>x.TelNo).OrderByDescending(x=>x.CreatedAt).ToList();
             return list;// _mapper.Map<IReadOnlyList<TrTelStateDto>>(tels.AgentList);
         }
@@ -182,6 +188,10 @@ namespace Hotline.Api.Controllers
         public async Task<TrTelStateDto> TrQueryTelStateByTelNo([FromQuery]string? telno)
         {
             var tels = await _trClient.QueryTelStateAsync(new QueryTelStateRequest() { TelNo = telno }, HttpContext.RequestAborted);
+
+            var listenTels = _systemSettingCacheManager.GetSetting(SettingConstants.ListenTels)?.SettingValue;
+            tels.Agents = tels.Agents?.Where(m => listenTels.Contains(m.TelNo) == false).ToList();
+
             var tel = tels.Agents?.FirstOrDefault();
             if (tel != null)
             {

+ 107 - 67
src/Hotline.Api/Controllers/OrderController.cs

@@ -63,6 +63,7 @@ using XF.Domain.Entities;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
+using static NPOI.SS.Format.CellNumberFormatter;
 
 namespace Hotline.Api.Controllers;
 
@@ -132,7 +133,7 @@ public class OrderController : BaseController
     private readonly ICalcExpireTime _expireTime;
     private readonly IRepository<OrderPushType> _orderPushTypeRepository;
 
-	public OrderController(
+    public OrderController(
         IOrderDomainService orderDomainService,
         IOrderRepository orderRepository,
         IWorkflowApplication workflowApplication,
@@ -253,7 +254,7 @@ public class OrderController : BaseController
         _expireTime = expireTime;
         _orderPushTypeRepository = orderPushTypeRepository;
 
-	}
+    }
     #endregion 
 
     #region 工单发布
@@ -603,7 +604,7 @@ public class OrderController : BaseController
         return res;
     }
 
-  
+
     /// <summary>
     /// 已发布列表
     /// </summary>
@@ -962,7 +963,7 @@ public class OrderController : BaseController
         }
 
         //_mapper.Map(dto.VisitDetails,visit.OrderVisitDetails);
-        for (int i = 0; i < visit.OrderVisitDetails.Count; i++)
+        for (int i = 0;i < visit.OrderVisitDetails.Count;i++)
         {
             var detail = visit.OrderVisitDetails[i];
             var detaildto = dto.VisitDetails.FirstOrDefault(x => x.Id == detail.Id);
@@ -1770,7 +1771,9 @@ public class OrderController : BaseController
             .Includes(x => x.OrderVisit, y => y.Employee)
             //.LeftJoin<OrderScreen>((x, s) => x.Id == s.VisitDetailId && s.IsDeleted == false)
             .Includes(x => x.OrderScreens)
-            .Where(x => x.OrderScreens.Any(s => s.Status == EScreenStatus.SendBack && s.SendBackApply == true) || x.OrderScreens.Any(s => (s.Status != EScreenStatus.SendBack && s.SendBackApply != true)) == false)
+            .Where(x => x.OrderScreens.Any(s => s.Status == EScreenStatus.SendBack && s.SendBackApply == true) || x.OrderScreens.Any() == false
+            //|| x.OrderScreens.Any(s => (s.Status != EScreenStatus.SendBack && s.SendBackApply != true)) == false
+            )
             .WhereIF(dto.ScreenSendBack is 1, x => x.OrderScreens.Any(s => s.Status == EScreenStatus.SendBack && s.SendBackApply == true))
             .WhereIF(dto.ScreenSendBack is 2, x => x.OrderScreens.Any(s => (s.Status != EScreenStatus.SendBack && s.SendBackApply != true)) == false)
             .WhereIF(!string.IsNullOrEmpty(dto.No), x => x.OrderVisit.Order!.No!.Contains(dto.No!))
@@ -1967,7 +1970,7 @@ public class OrderController : BaseController
         try
         {
             //二次回访会改写数据
-            await _orderRepository.OrderScreenRevisionVisit(visit.Id, false, HttpContext.RequestAborted);
+            //await _orderRepository.OrderScreenRevisionVisit(visit.Id, false, HttpContext.RequestAborted);
             var startDto = _mapper.Map<StartWorkflowDto>(dto.Workflow);
             startDto.DefinitionModuleCode = WorkflowModuleConsts.OrderScreen;
             startDto.Opinion = dto.Data.Content;
@@ -2654,19 +2657,19 @@ public class OrderController : BaseController
     [HttpGet("history_all")]
     public async Task<PagedDto<OrderDto>> QueryAll([FromQuery] QueryOrderHistoryDto dto)
     {
-	    var (total, items) = await _orderRepository.Queryable()
-		    .WhereIF(!string.IsNullOrEmpty(dto.PhoneNo),d => d.Contact == dto.PhoneNo)
-		    .WhereIF(!string.IsNullOrEmpty(dto.OrderId), d => d.Id != dto.OrderId)
-		    .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Title.Contains(dto.Keyword!) || d.No.Contains(dto.Keyword!))
-		    .OrderByDescending(d => d.CreationTime)
-		    .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
-	    return new PagedDto<OrderDto>(total, _mapper.Map<IReadOnlyList<OrderDto>>(items));
+        var (total, items) = await _orderRepository.Queryable()
+            .WhereIF(!string.IsNullOrEmpty(dto.PhoneNo), d => d.Contact == dto.PhoneNo)
+            .WhereIF(!string.IsNullOrEmpty(dto.OrderId), d => d.Id != dto.OrderId)
+            .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Title.Contains(dto.Keyword!) || d.No.Contains(dto.Keyword!))
+            .OrderByDescending(d => d.CreationTime)
+            .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+        return new PagedDto<OrderDto>(total, _mapper.Map<IReadOnlyList<OrderDto>>(items));
     }
 
-	/// <summary>
-	/// 查询重复工单
-	/// </summary>
-	[HttpGet("duplicate")]
+    /// <summary>
+    /// 查询重复工单
+    /// </summary>
+    [HttpGet("duplicate")]
     public async Task<PagedDto<OrderDto>> Query([FromQuery] QueryOrderDuplicateDto dto)
     {
         if (!dto.OrderIds.Any())
@@ -2930,16 +2933,16 @@ public class OrderController : BaseController
         // 工单推送分类
         if (dto.OrderPushTypes.Any())
         {
-	        var pushTypeAny = await _orderPushTypeRepository.AnyAsync(x => x.OrderId == order.Id);
-	        if (pushTypeAny)
-		        await _orderPushTypeRepository.RemoveAsync(x => x.OrderId == order.Id);
-	        dto.OrderPushTypes.ForEach(x => x.OrderId = order.Id);
+            var pushTypeAny = await _orderPushTypeRepository.AnyAsync(x => x.OrderId == order.Id);
+            if (pushTypeAny)
+                await _orderPushTypeRepository.RemoveAsync(x => x.OrderId == order.Id);
+            dto.OrderPushTypes.ForEach(x => x.OrderId = order.Id);
             var orderPushTypes = _mapper.Map<List<OrderPushType>>(dto.OrderPushTypes);
-			await _orderPushTypeRepository.AddRangeAsync(orderPushTypes);
-	        var pushTypes = dto.OrderPushTypes.Select(x => x.PushType);
-	        order.PushType = string.Join(",", pushTypes);
+            await _orderPushTypeRepository.AddRangeAsync(orderPushTypes);
+            var pushTypes = dto.OrderPushTypes.Select(x => x.PushType);
+            order.PushType = string.Join(",", pushTypes);
         }
-		await _orderDomainService.AddAsync(order, true, HttpContext.RequestAborted);
+        await _orderDomainService.AddAsync(order, true, HttpContext.RequestAborted);
 
         //订阅此事件的内部处理工单数据只能更新各自业务的字段,不能全部更新
         //新增工单其他处理事件  (受理短信)
@@ -3124,24 +3127,24 @@ public class OrderController : BaseController
         // 工单推送分类
         if (dto.OrderPushTypes.Any())
         {
-	        var pushTypeAny = await _orderPushTypeRepository.AnyAsync(x => x.OrderId == order.Id);
-	        if (pushTypeAny)
-		        await _orderPushTypeRepository.RemoveAsync(x => x.OrderId == order.Id);
-	        order.OrderPushTypes.ForEach(x => x.OrderId = order.Id);
-			var orderPushTypes = _mapper.Map<List<OrderPushType>>(dto.OrderPushTypes);
-			await _orderPushTypeRepository.AddRangeAsync(orderPushTypes);
-			var pushTypes = dto.OrderPushTypes.Select(x => x.PushType);
-			order.PushType = string.Join(",", pushTypes);
-		}
-		await _orderRepository.UpdateNav(order).Include(d => d.OrderExtension).ExecuteCommandAsync();
+            var pushTypeAny = await _orderPushTypeRepository.AnyAsync(x => x.OrderId == order.Id);
+            if (pushTypeAny)
+                await _orderPushTypeRepository.RemoveAsync(x => x.OrderId == order.Id);
+            order.OrderPushTypes.ForEach(x => x.OrderId = order.Id);
+            var orderPushTypes = _mapper.Map<List<OrderPushType>>(dto.OrderPushTypes);
+            await _orderPushTypeRepository.AddRangeAsync(orderPushTypes);
+            var pushTypes = dto.OrderPushTypes.Select(x => x.PushType);
+            order.PushType = string.Join(",", pushTypes);
+        }
+        await _orderRepository.UpdateNav(order).Include(d => d.OrderExtension).ExecuteCommandAsync();
 
         //订阅此事件的内部处理工单数据只能更新各自业务的字段,不能全部更新
         //修改工单其他处理事件  (受理短信)
         await _publisher.PublishAsync(new UpdateOrderNotify(order), PublishStrategy.ParallelWhenAll, HttpContext.RequestAborted);
 
 
-		//敏感分词
-		await _orderApplication.OrderSensitiveParticiple(dto.Content, order.Id, HttpContext.RequestAborted);
+        //敏感分词
+        await _orderApplication.OrderSensitiveParticiple(dto.Content, order.Id, HttpContext.RequestAborted);
         // 副本工单
         var copy = new OrderCopy();
         _mapper.Map(order, copy);
@@ -3152,7 +3155,7 @@ public class OrderController : BaseController
         copy.InitId();
         await _orderCopyRepository.AddAsync(copy, HttpContext.RequestAborted);
         return new { Id = order.Id, No = order.No, Password = order.Password };
-	}
+    }
 
     /// <summary>
     /// 开始工单办理流程
@@ -3193,9 +3196,17 @@ public class OrderController : BaseController
         //}
         else
         {
-            //期满时间
-            //expiredTimeConfig = _timeLimitDomainService.CalcExpiredTime(DateTime.Now, EFlowDirection.CenterToOrg, order.AcceptTypeCode);
-            expiredTimeConfig = await _expireTime.CalcExpiredTime(DateTime.Now, EFlowDirection.CenterToOrg, order.Adapt<OrderTimeClacInfo>());
+            if (_appOptions.Value.IsZiGong)
+            {
+                expiredTimeConfig = await _expireTime.CalcExpiredTime(DateTime.Now, EFlowDirection.CenterToCenter, order.Adapt<OrderTimeClacInfo>());
+            }
+            else
+            {
+                //期满时间
+                //expiredTimeConfig = _timeLimitDomainService.CalcExpiredTime(DateTime.Now, EFlowDirection.CenterToOrg, order.AcceptTypeCode);
+                expiredTimeConfig = await _expireTime.CalcExpiredTime(DateTime.Now, EFlowDirection.CenterToOrg, order.Adapt<OrderTimeClacInfo>());
+            }
+
         }
 
         _mapper.Map(expiredTimeConfig, order);
@@ -3312,16 +3323,22 @@ public class OrderController : BaseController
         }
         else if (dto.FlowDirection is EFlowDirection.CenterToCenter)
         {
-            // expiredTimeConfig = _timeLimitDomainService.CalcExpiredTime(DateTime.Now, EFlowDirection.CenterToCenter, order.AcceptTypeCode);
-            expiredTimeConfig = await _expireTime.CalcExpiredTime(DateTime.Now, EFlowDirection.CenterToCenter, order.Adapt<OrderTimeClacInfo>());
-            order.CenterToCenter(expiredTimeConfig.TimeText, expiredTimeConfig.Count,
-                expiredTimeConfig.TimeType, expiredTimeConfig.ExpiredTime, expiredTimeConfig.NearlyExpiredTime, expiredTimeConfig.NearlyExpiredTimeOne);
-            //TODO发送短信即将超期
-            //_capPublisher.PublishDelay(expiredTimeConfig.NearlyExpiredTime - DateTime.Now, EventNames.HotlineOrderNearlyExpiredTimeSms, new PublishNearlyExpiredTimeSmsDto() { OrderId = order.Id });
+            if (_appOptions.Value.IsZiGong == false)
+            {
+                // expiredTimeConfig = _timeLimitDomainService.CalcExpiredTime(DateTime.Now, EFlowDirection.CenterToCenter, order.AcceptTypeCode);
+                expiredTimeConfig = await _expireTime.CalcExpiredTime(DateTime.Now, EFlowDirection.CenterToCenter, order.Adapt<OrderTimeClacInfo>());
+                order.CenterToCenter(expiredTimeConfig.TimeText, expiredTimeConfig.Count,
+                    expiredTimeConfig.TimeType, expiredTimeConfig.ExpiredTime, expiredTimeConfig.NearlyExpiredTime, expiredTimeConfig.NearlyExpiredTimeOne);
+                //TODO发送短信即将超期
+                //_capPublisher.PublishDelay(expiredTimeConfig.NearlyExpiredTime - DateTime.Now, EventNames.HotlineOrderNearlyExpiredTimeSms, new PublishNearlyExpiredTimeSmsDto() { OrderId = order.Id });
+            }
         }
 
-        _mapper.Map(expiredTimeConfig, order);
-        await _orderRepository.UpdateAsync(order, HttpContext.RequestAborted);
+        if (expiredTimeConfig is not null)
+        {
+            _mapper.Map(expiredTimeConfig, order);
+            await _orderRepository.UpdateAsync(order, HttpContext.RequestAborted);
+        }
         //1.是否是判断节点  2.是否存在历史派单节点  3.存在获取上个派单节点  4.不存在走平均派单
         if (dto.BusinessType == EBusinessType.Send)
         {
@@ -4422,7 +4439,7 @@ public class OrderController : BaseController
         model.Status = order.Status;
 
         var audit = true;
-		var specialAduit = _systemSettingCacheManager.GetSetting(SettingConstants.SpecialAduit);
+        var specialAduit = _systemSettingCacheManager.GetSetting(SettingConstants.SpecialAduit);
         if (bool.Parse(specialAduit?.SettingValue[0]))
         {
             //if (!dto.Audit) model.State = 1;
@@ -4435,10 +4452,11 @@ public class OrderController : BaseController
                 if (roles != null && roles.Contains(item)) audit = false;
             }
         }
-        else {
+        else
+        {
             audit = false;
-		}
-		if (!audit) model.State = 1;
+        }
+        if (!audit) model.State = 1;
 
         model.InitId();
         if (dto.Files.Any())
@@ -4446,16 +4464,16 @@ public class OrderController : BaseController
         await _orderSpecialRepository.AddAsync(model, HttpContext.RequestAborted);
         if (model.State == 1)
         {
-	        if (_appOptions.Value.IsZiGong && dto.BusinessType == EBusinessType.Send)
-	        {
-				// 平均派单
-				var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
-				if (averageSendOrder)
-				{
-					var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
-					dto.NextHandlers = new List<FlowStepHandler> { handler };
-				}
-			}
+            if (_appOptions.Value.IsZiGong && dto.BusinessType == EBusinessType.Send)
+            {
+                // 平均派单
+                var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
+                if (averageSendOrder)
+                {
+                    var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
+                    dto.NextHandlers = new List<FlowStepHandler> { handler };
+                }
+            }
 
             var recall = new RecallDto
             {
@@ -4472,7 +4490,29 @@ public class OrderController : BaseController
             //if (dto.AlterTime)
             //{
             // var expiredTime = _timeLimitDomainService.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
-            var expiredTime = await _expireTime.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
+
+            ExpiredTimeWithConfig? expiredTime = null;
+            _logger.LogInformation($"特提时间计算 special: {dto.ToJson()}");
+            if (_appOptions.Value.IsZiGong)
+            {
+                _logger.LogInformation($"特提时间计算 自贡");
+                if (dto.FlowDirection != null)
+                {
+                    _logger.LogInformation($"特提时间计算 自贡 有方向");
+                    expiredTime = await _expireTime.CalcExpiredTime(DateTime.Now, dto.FlowDirection.Value, order.Adapt<OrderTimeClacInfo>());
+                }
+                else
+                {
+                    _logger.LogInformation($"特提时间计算 自贡 无方向");
+                    expiredTime = await _expireTime.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
+                }
+            }
+            else
+            {
+                _logger.LogInformation($"特提时间计算 非自贡");
+                expiredTime = await _expireTime.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
+            }
+
             var processType = dto.FlowDirection is EFlowDirection.OrgToCenter or EFlowDirection.CenterToCenter or EFlowDirection.FiledToCenter
                 ? EProcessType.Zhiban
                 : EProcessType.Jiaoban;
@@ -5006,9 +5046,9 @@ public class OrderController : BaseController
             //.WhereIF(dto.EmergencyLevels.Any(), d => dto.EmergencyLevels.Contains(d.EmergencyLevel))
             .WhereIF(!string.IsNullOrEmpty(dto.PhoneNo),
                 d => d.FromPhone.Contains(dto.PhoneNo!) || d.Contact.Contains(dto.PhoneNo!))
-			 //.WhereIF(!string.IsNullOrEmpty(dto.PushTypeCode), d => d.PushTypeCode == dto.PushTypeCode)
-			.WhereIF(!string.IsNullOrEmpty(dto.PushTypeCode), d => d.OrderPushTypes.Any(opt => opt.PushTypeCode == dto.PushTypeCode)) //推送分类
-			.WhereIF(dto.ExpiredTimeStart.HasValue, d => d.ExpiredTime >= dto.ExpiredTimeStart)
+            //.WhereIF(!string.IsNullOrEmpty(dto.PushTypeCode), d => d.PushTypeCode == dto.PushTypeCode)
+            .WhereIF(!string.IsNullOrEmpty(dto.PushTypeCode), d => d.OrderPushTypes.Any(opt => opt.PushTypeCode == dto.PushTypeCode)) //推送分类
+            .WhereIF(dto.ExpiredTimeStart.HasValue, d => d.ExpiredTime >= dto.ExpiredTimeStart)
             .WhereIF(dto.ExpiredTimeEnd.HasValue, d => d.ExpiredTime <= dto.ExpiredTimeEnd)
             .WhereIF(dto.Status.HasValue, d => d.Status == dto.Status)//工单状态
             .WhereIF(dto.Status != null && dto.Status == EOrderStatus.BackToUnAccept, d => d.Status <= EOrderStatus.HandOverToUnAccept);

+ 1 - 0
src/Hotline.Application.Tests/Domain/ZiGongExpireTimeTest.cs

@@ -51,6 +51,7 @@ public class ZiGongExpireTimeTest
     [InlineData("企业投诉件单元测试", "2024-09-05 14:00:00", "CenterToOrg", "2024-09-09 14:00:00")]
     [InlineData("四川省12345咨询件单元测试", "2024-09-05 14:00:00", "CenterToOrg", "2024-09-06 14:00:00")]
     [InlineData("四川省12345建议件单元测试", "2024-09-05 14:00:00", "CenterToOrg", "2024-09-10 14:00:00")]
+    [InlineData("中心到中心24小时", "2024-09-12 14:00:00", "CenterToCenter", "2024-09-13 14:00:00")]
     public async Task CalcExpiredTime_Test(string title, string beginTxt, string flowTxt, string expected)
     {
         var beginTime = DateTime.Parse(beginTxt);

+ 1 - 0
src/Hotline.Application.Tests/Hotline.Application.Tests.csproj

@@ -36,6 +36,7 @@
     <ProjectReference Include="..\Hotline.Api\Hotline.Api.csproj" />
     <ProjectReference Include="..\Hotline.Application\Hotline.Application.csproj" />
     <ProjectReference Include="..\Hotline.Repository.SqlSugar\Hotline.Repository.SqlSugar.csproj" />
+    <ProjectReference Include="..\Tr.Sdk\Tr.Sdk.csproj" />
     <ProjectReference Include="..\XF.Domain.Repository\XF.Domain.Repository.csproj" />
     <ProjectReference Include="..\XF.Domain\XF.Domain.csproj" />
   </ItemGroup>

+ 10 - 0
src/Hotline.Application.Tests/Startup.cs

@@ -1,4 +1,5 @@
 using Microsoft.AspNetCore.Hosting;
+using Tr.Sdk;
 using Hotline.Repository.SqlSugar.Extensions;
 using Microsoft.AspNetCore.TestHost;
 using Microsoft.Extensions.Configuration;
@@ -36,6 +37,7 @@ using XF.Domain.Options;
 using Hotline.Settings.TimeLimitDomain;
 using Hotline.Settings.TimeLimitDomain.ExpireTimeSupplier;
 using Microsoft.AspNetCore.WebSockets;
+using Hotline.CallCenter.Configs;
 
 namespace Hotline.Application.Tests;
 public class Startup
@@ -75,6 +77,14 @@ public class Startup
             var appConfigurationSection = configuration.GetRequiredSection(nameof(AppConfiguration));
             var appConfiguration = appConfigurationSection.Get<AppConfiguration>();
             if (appConfiguration is null) throw new ArgumentNullException(nameof(appConfiguration));
+
+            //var callCenterConfigurationSection = configuration.GetRequiredSection(nameof(CallCenterConfiguration));
+            //var callCenterConfiguration = callCenterConfigurationSection.Get<CallCenterConfiguration>();
+            //services.AddTrSdk(callCenterConfiguration.TianRun.Address,
+            //            callCenterConfiguration.TianRun.Username,
+            //            callCenterConfiguration.TianRun.Password);
+
+
             services.Configure<AppConfiguration>(d => appConfigurationSection.Bind(d));
             services.Configure<IdentityConfiguration>(d => configuration.GetSection(nameof(IdentityConfiguration)).Bind(d));
 

+ 26 - 4
src/Hotline.Application/FlowEngine/WorkflowApplication.cs

@@ -76,7 +76,8 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         ISessionContext sessionContext,
         IMapper mapper,
         IFileRepository fileRepository,
-        ILogger<WorkflowApplication> logger)
+        ISystemSettingCacheManager systemSettingCacheManager,
+		ILogger<WorkflowApplication> logger)
     {
         _definitionDomainService = definitionDomainService;
         _workflowDomainService = workflowDomainService;
@@ -96,7 +97,9 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         _mapper = mapper;
         _fileRepository = fileRepository;
         _logger = logger;
-    }
+        _systemSettingCacheManager = systemSettingCacheManager;
+
+	}
 
     public async Task<string> StartWorkflowAsync(StartWorkflowDto dto, ISessionContext current, string externalId,
         DateTime? expiredTime, CancellationToken cancellationToken = default)
@@ -325,8 +328,27 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         var targetStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, dto.NextStepCode);
         if (targetStepDefine.StepType is EStepType.End)
             throw UserFriendlyException.SameMessage("结束节点不支持撤回");
-        //var isStartCountersign = targetStepDefine.CouldPrevStartCountersign(dto.NextHandlers.Count);
-        var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.HandlerType, dto.IsStartCountersign,
+		//var isStartCountersign = targetStepDefine.CouldPrevStartCountersign(dto.NextHandlers.Count);
+		var targetStep = workflow.Steps.FirstOrDefault(d => d.Code == dto.NextStepCode && d.IsOrigin);
+		if (targetStep is null)
+			throw UserFriendlyException.SameMessage("该流程尚未流转至该节点");
+
+		///退回到派单组 没有下一步办理人 获取之前节点办理人
+		if (!dto.NextHandlers.Any())
+		{
+			dto.NextHandlers.Add(new FlowStepHandler()
+			{
+				UserId = targetStep.HandlerId,
+				Username = targetStep.HandlerName,
+				OrgId = targetStep.HandlerOrgId,
+				OrgName = targetStep.HandlerOrgName,
+				Key = targetStep.HandlerId,
+				Value = targetStep.HandlerName,
+				RoleId = targetStep.RoleId,
+				RoleName = targetStep.RoleName
+			});
+		}
+		var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.HandlerType, dto.IsStartCountersign,
             dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), cancellationToken);
 
         //var stepHandlers = await GetNextStepHandlersAsync(workflow, targetStepDefine, dto, cancellationToken);

+ 23 - 4
src/Hotline.Application/Handlers/FlowEngine/WorkflowNextHandler.cs

@@ -2,6 +2,7 @@
 using Hotline.Application.JudicialManagement;
 using Hotline.Application.Quality;
 using Hotline.Caching.Interfaces;
+using Hotline.Configurations;
 using Hotline.EventBus;
 using Hotline.FlowEngine.Notifications;
 using Hotline.FlowEngine.WorkflowModules;
@@ -11,19 +12,23 @@ using Hotline.KnowledgeBase;
 using Hotline.Orders;
 using Hotline.Push.Notifies;
 using Hotline.Settings;
+using Hotline.Settings.TimeLimitDomain;
 using Hotline.Settings.TimeLimits;
 using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Share.Dtos.Order;
+using Hotline.Share.Dtos.Settings;
 using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Push;
 using Hotline.Share.Enums.Quality;
 using Hotline.Share.Mq;
 using Hotline.Users;
+using Mapster;
 using MapsterMapper;
 using MediatR;
 using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
 using XF.Domain.Authentications;
 using XF.Domain.Entities;
 using XF.Domain.Repository;
@@ -33,6 +38,8 @@ namespace Hotline.Application.Handlers.FlowEngine;
 public class WorkflowNextHandler : INotificationHandler<NextStepNotify>
 {
     private readonly IOrderDomainService _orderDomainService;
+    private readonly ICalcExpireTime _expireTime;
+    private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
     private readonly IOrderRepository _orderRepository;
     private readonly ICapPublisher _capPublisher;
     private readonly IMapper _mapper;
@@ -59,7 +66,9 @@ public class WorkflowNextHandler : INotificationHandler<NextStepNotify>
         IRepository<User> userRepository,
         IMediator mediator,
         ISystemSettingCacheManager systemSettingCacheManager,
-        Publisher publisher)
+        Publisher publisher,
+        IOptionsSnapshot<AppConfiguration> appOptions,
+        ICalcExpireTime expireTime)
     {
         _orderDomainService = orderDomainService;
         _orderRepository = orderRepository;
@@ -74,6 +83,8 @@ public class WorkflowNextHandler : INotificationHandler<NextStepNotify>
         _mediator = mediator;
         _systemSettingCacheManager = systemSettingCacheManager;
         _publisher = publisher;
+        _appOptions = appOptions;
+        _expireTime = expireTime;
     }
 
     /// <summary>Handles a notification</summary>
@@ -105,6 +116,14 @@ public class WorkflowNextHandler : INotificationHandler<NextStepNotify>
                     order.UpdateHandlingStatus(workflow.IsInCountersign);
                     _mapper.Map(workflow, order);
 
+                    if (_appOptions.Value.IsZiGong && data.FlowDirection is EFlowDirection.CenterToOrg)
+                    {
+                        var expiredTimeConfig = await _expireTime.CalcExpiredTime(DateTime.Now, EFlowDirection.CenterToOrg , order.Adapt<OrderTimeClacInfo>());
+                        order.CenterToOrg(expiredTimeConfig.TimeText, expiredTimeConfig.Count,
+                            expiredTimeConfig.TimeType, expiredTimeConfig.ExpiredTime, expiredTimeConfig.NearlyExpiredTime
+                            , expiredTimeConfig.NearlyExpiredTimeOne, notification.Dto.Opinion,notification.Trace.HandlerId, notification.Trace.HandlerName,true);
+                    }
+
                     //var expiredTimeChanged = false;
                     //if (data.FlowDirection.HasValue
                     //    && data.External.TimeLimit.HasValue                   
@@ -182,7 +201,7 @@ public class WorkflowNextHandler : INotificationHandler<NextStepNotify>
                                     }
                                     break;
                                 case EFlowAssignType.User:
-                                    var userCodes = notification.Trace.NextHandlers.Select(x=>x.UserId); //notification.FlowAssignInfo.HandlerObjects.Select(x => x.Key);
+                                    var userCodes = notification.Trace.NextHandlers.Select(x => x.UserId); //notification.FlowAssignInfo.HandlerObjects.Select(x => x.Key);
                                     var userList = await _userRepository.Queryable()
                                         .Where(x => userCodes.Contains(x.Id) && !string.IsNullOrEmpty(x.PhoneNo))
                                         .ToListAsync(cancellationToken);
@@ -219,9 +238,9 @@ public class WorkflowNextHandler : INotificationHandler<NextStepNotify>
                         HandlerOrgLevel = notification.HandlerOrgId.CalcOrgLevel()
                     }, cancellationToken: cancellationToken);
                     if (data.FlowDirection is EFlowDirection.CenterToOrg)
-	                    await _qualityApplication.AddQualityAsync(EQualitySource.Send, order.Id, cancellationToken);
+                        await _qualityApplication.AddQualityAsync(EQualitySource.Send, order.Id, cancellationToken);
 
-					break;
+                    break;
                 case WorkflowModuleConsts.KnowledgeAdd:
                 case WorkflowModuleConsts.KnowledgeUpdate:
                 case WorkflowModuleConsts.KnowledgeDelete:

+ 5 - 0
src/Hotline.Application/Mappers/MapperConfigs.cs

@@ -10,6 +10,7 @@ using Hotline.Share.Dtos.OrderExportWord;
 using Hotline.Share.Dtos.Org;
 using Hotline.Share.Dtos.Push.FWMessage;
 using Hotline.Share.Dtos.Settings;
+using Hotline.Share.Dtos.TrCallCenter;
 using Hotline.Share.Enums.Order;
 using Mapster;
 using XF.Domain.Entities;
@@ -20,6 +21,10 @@ namespace Hotline.Application.Mappers
     {
         public void Register(TypeAdapterConfig config)
         {
+            config.ForType<Tr.Sdk.Tels.QueryTelResponse, TelOutDto>()
+                .Map(m => m.TelPwd, x => x.Password)
+                .Map(m =>m.Queue, x => x.QueueId);
+
             config.ForType<TimeLimitSettingInventory, TimeConfig>()
                 .Map(d => d.Count, m => m.TimeValue);
             config.ForType<TimeResult, ExpiredTimeWithConfig>()

+ 1 - 0
src/Hotline.Application/Tels/ITelApplication.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using Hotline.Share.Dtos.TrCallCenter;
 using Microsoft.AspNetCore.Http;
 
 namespace Hotline.Application.Tels

+ 11 - 5
src/Hotline.Application/Tels/TelApplication.cs

@@ -1,9 +1,12 @@
 using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.CallCenter.Tels;
+using Hotline.Share.Dtos.TrCallCenter;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Users;
 using Microsoft.AspNetCore.Http;
+using Tr.Sdk;
+using Tr.Sdk.Tels;
 using XF.Domain.Cache;
 using XF.Domain.Dependency;
 using XF.Domain.Exceptions;
@@ -14,6 +17,7 @@ namespace Hotline.Application.Tels;
 public class TelApplication : ITelApplication, IScopeDependency
 {
     private readonly IUserCacheManager _userCacheManager;
+    private readonly ITrClient _trClient;
     private readonly ITelRestRepository _telRestRepository;
     private readonly ITypedCache<Work> _cacheWork;
     private readonly IWorkRepository _workRepository;
@@ -24,13 +28,15 @@ public class TelApplication : ITelApplication, IScopeDependency
         ITelRestRepository telRestRepository,
         ITypedCache<Work> cacheWork,
         IWorkRepository workRepository,
-        IRepository<TelActionRecord> telActionRecordRepository)
+        IRepository<TelActionRecord> telActionRecordRepository,
+        ITrClient trClient)
     {
         _userCacheManager = userCacheManager;
         _telRestRepository = telRestRepository;
         _cacheWork = cacheWork;
         _workRepository = workRepository;
         _telActionRecordRepository = telActionRecordRepository;
+        _trClient = trClient;
     }
 
     /// <summary>
@@ -52,7 +58,7 @@ public class TelApplication : ITelApplication, IScopeDependency
         await _workRepository.UpdateAsync(work, cancellationToken);
         _cacheWork.Remove(work.GetKey(KeyMode.UserId));
         _cacheWork.Remove(work.GetKey(KeyMode.TelNo));
-        
+
         var list = await _telActionRecordRepository.Queryable().Where(x => x.TelNo == work.TelNo && !x.EndTime.HasValue).ToListAsync();
         foreach (var item in list)
         {
@@ -75,7 +81,7 @@ public class TelApplication : ITelApplication, IScopeDependency
         //}
     }
 
-    public async Task SignOutByTelNoAsync(string telNo,CancellationToken cancellationToken)
+    public async Task SignOutByTelNoAsync(string telNo, CancellationToken cancellationToken)
     {
         var work = _userCacheManager.GetWorkByTelNoExp(telNo);
         if (work is null) return;
@@ -95,7 +101,7 @@ public class TelApplication : ITelApplication, IScopeDependency
 
 
         var list = await _telActionRecordRepository.Queryable().Where(x => x.TelNo == work.TelNo && !x.EndTime.HasValue).ToListAsync();
-        foreach ( var item in list )
+        foreach (var item in list)
         {
             item.EndAction();
             await _telActionRecordRepository.UpdateAsync(item);
@@ -115,4 +121,4 @@ public class TelApplication : ITelApplication, IScopeDependency
         //    await _telActionRecordRepository.UpdateAsync(telarrangeAction);
         //}
     }
-}
+  }

+ 7 - 1
src/Hotline.Share/Dtos/Settings/TimeConfig.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Settings;
 using Hotline.Share.Tools;
@@ -26,6 +27,11 @@ namespace Hotline.Share.Dtos.Settings
         /// </summary>
         public string? AcceptTypeCode { get; set; }
 
+        /// <summary>
+        /// 流程方向
+        /// </summary>
+        public EFlowDirection FlowDirection { get; set; }
+
         /// <summary>
         /// 24小时办结
         /// </summary>
@@ -75,7 +81,7 @@ namespace Hotline.Share.Dtos.Settings
                 if (timeText.IsNullOrEmpty()) return $"{Count}个{TimeType.GetDescription()}";
                 return timeText;
             }
-            set 
+            set
             {
                 timeText = value;
             }

+ 7 - 0
src/Hotline.Share/Dtos/TrCallCenter/TrTelDao.cs

@@ -195,6 +195,13 @@ namespace Hotline.Share.Dtos.TrCallCenter
         public bool isCallOut { get; set; }
     }
 
+    public class TelOutDto
+    {
+        public string TelNo { get; set; }
+        public string TelPwd { get; set; }
+        public string Queue { get; set; }
+    }
+
     public class TrOnDutyResponseDto
     {
         public string? TelNo { get; set; }

+ 1 - 0
src/Hotline.Share/Hotline.Share.csproj

@@ -15,6 +15,7 @@
     <PackageReference Include="Mapster" Version="7.3.0" />
     <PackageReference Include="MediatR.Contracts" Version="1.0.1" />
     <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
+    <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
     <PackageReference Include="XF.Utility.EnumExtensions" Version="1.0.4" />
   </ItemGroup>
 

+ 6 - 0
src/Hotline.Share/Tools/ObjectExtensions.cs

@@ -1,4 +1,5 @@
 using System;
+using Newtonsoft.Json;
 using System.Collections.Generic;
 using System.Linq;
 using System.Reflection;
@@ -26,4 +27,9 @@ public static class ObjectExtensions
             if (result == false) break;
         }
     }
+
+    public static string ToJson(this object obj)
+    {
+        return JsonConvert.SerializeObject(obj);
+    }
 }

+ 9 - 3
src/Hotline/Settings/TimeLimitDomain/ZiGongExpireTimeLimit.cs

@@ -7,6 +7,7 @@ using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Tools;
 using Mapster;
 using MapsterMapper;
+using Microsoft.Extensions.Logging;
 using XF.Domain.Dependency;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
@@ -27,8 +28,9 @@ public class ZiGongExpireTimeLimit : ExpireTimeLimitBase, ICalcExpireTime, IScop
     private readonly IMapper _mapper;
     private readonly IRepository<SystemSetting> _systemSettingRepository;
     private readonly IDaySettingRepository _daySettingRepository;
+    private readonly ILogger<ZiGongExpireTimeLimit> _logger;
 
-    public ZiGongExpireTimeLimit(ISystemSettingCacheManager systemSettingCacheManager, ITimeLimitSettingRepository timeLimitSettingRepository, IExpireTimeHandler expireTimeHandler, IMapper mapper, IRepository<SystemSetting> systemSettingRepository, IDaySettingRepository daySettingRepository, ITimeLimitSettingAttributeRepository timeLimitSettingAttributeRepository, ITimeLimitSettingInventoryRepository timeLimitSettingInventoryRepository) : base(systemSettingCacheManager, timeLimitSettingRepository, expireTimeHandler, mapper, daySettingRepository)
+    public ZiGongExpireTimeLimit(ISystemSettingCacheManager systemSettingCacheManager, ITimeLimitSettingRepository timeLimitSettingRepository, IExpireTimeHandler expireTimeHandler, IMapper mapper, IRepository<SystemSetting> systemSettingRepository, IDaySettingRepository daySettingRepository, ITimeLimitSettingAttributeRepository timeLimitSettingAttributeRepository, ITimeLimitSettingInventoryRepository timeLimitSettingInventoryRepository, ILogger<ZiGongExpireTimeLimit> logger) : base(systemSettingCacheManager, timeLimitSettingRepository, expireTimeHandler, mapper, daySettingRepository)
     {
         _systemSettingCacheManager = systemSettingCacheManager;
         _timeLimitSettingRepository = timeLimitSettingRepository;
@@ -38,13 +40,17 @@ public class ZiGongExpireTimeLimit : ExpireTimeLimitBase, ICalcExpireTime, IScop
         _daySettingRepository = daySettingRepository;
         _timeLimitSettingAttributeRepository = timeLimitSettingAttributeRepository;
         _timeLimitSettingInventoryRepository = timeLimitSettingInventoryRepository;
+        _logger = logger;
     }
 
     public override async Task<ExpiredTimeWithConfig> CalcExpiredTime(DateTime beginTime, EFlowDirection flowDirection, OrderTimeClacInfo order)
     {
-        if (EFlowDirection.CenterToOrg == flowDirection)
+        order.FlowDirection = flowDirection;
+        if (EFlowDirection.CenterToOrg == flowDirection || EFlowDirection.CenterToCenter == flowDirection)
         {
             var timeConfig = await GetTimeConfigByOrderAsync(order);
+            if (order.FlowDirection == EFlowDirection.CenterToCenter)
+                _logger.LogInformation($"中心到中心的订单:{timeConfig.ToJson()}");
             timeConfig.WorkTime = GetWorkTimes(SettingConstants.WorkTime);
             var result = await _expireTimeHandler.CalcEndTimeAsync(beginTime, timeConfig);
 
@@ -99,7 +105,7 @@ public class ZiGongExpireTimeLimit : ExpireTimeLimitBase, ICalcExpireTime, IScop
             timeLimitAttribute ??= noBusCodeAttribute;
             if (timeLimitAttribute is null) return true;
             code += timeLimitAttribute.Code;
-            if (timeLimitAttribute is not null && false == timeLimitAttribute.IsCommon)
+            if (timeLimitAttribute.IsCommon == false)
                 return false;
             return true;
         });