Browse Source

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

TANG JIANG 1 năm trước cách đây
mục cha
commit
29eeacd2c0

+ 79 - 1
src/Hotline.Api/Controllers/IPPbxController.cs

@@ -2,8 +2,12 @@
 using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.Permissions;
+using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Settings;
+using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.TrCallCenter;
+using Hotline.Share.Enums.CallCenter;
+using Hotline.Share.Enums.KnowledgeBase;
 using Hotline.Users;
 using MapsterMapper;
 using Microsoft.AspNetCore.Authorization;
@@ -15,6 +19,7 @@ using Tr.Sdk.Tels;
 using XF.Domain.Authentications;
 using XF.Domain.Filters;
 using XF.Domain.Repository;
+using XF.Utility.EnumExtensions;
 
 namespace Hotline.Api.Controllers
 {
@@ -147,7 +152,7 @@ namespace Hotline.Api.Controllers
 
         #endregion
 
-        #region 通话记录
+        #region 通话记录(对外)
         /// <summary>
         /// 接收通话记录
         /// </summary>
@@ -158,6 +163,43 @@ namespace Hotline.Api.Controllers
         public async Task<OpenResponse> ReceiveCallRecord([FromBody]ReceiveCallRecordDto dto)
         {
             var model = _mapper.Map<TrCallRecord>(dto);
+            model.Duration = 0;
+            model.RingTimes = 0;
+            model.QueueTims = 0;
+            model.OnState = Share.Enums.CallCenter.EOnState.NoOn;
+            //计算通话时长
+            if (model.AnsweredTime != null)
+            {
+                model.OnState = Share.Enums.CallCenter.EOnState.On; //是否接通
+
+                TimeSpan tsend = new TimeSpan(model.OverTime.Ticks);
+                TimeSpan tsbegin = new TimeSpan(model.AnsweredTime.Value.Ticks);
+                model.Duration = Convert.ToInt32(tsend.Subtract(tsbegin).TotalSeconds);
+            }
+            //计算振铃时长
+            if (model.BeginRingTime !=null)
+            {
+                TimeSpan tsbegin = new TimeSpan(model.BeginRingTime.Value.Ticks);
+                if (model.EndRingTimg!=null)
+                {
+                    TimeSpan tsend = new TimeSpan(model.EndRingTimg.Value.Ticks);
+                    model.RingTimes = Convert.ToInt32(tsend.Subtract(tsbegin).TotalSeconds);
+                }
+                else
+                {
+                    TimeSpan tsend = new TimeSpan(model.OverTime.Ticks);
+                    model.RingTimes = Convert.ToInt32(tsend.Subtract(tsbegin).TotalSeconds);
+                }
+            }
+            //计算队列时长
+            if (model.BeginQueueTime!=null && model.EndQueueTime!=null)
+            {
+                TimeSpan tsend = new TimeSpan(model.EndQueueTime.Value.Ticks);
+                TimeSpan tsbegin = new TimeSpan(model.BeginQueueTime.Value.Ticks);
+                model.QueueTims = Convert.ToInt32(tsend.Subtract(tsbegin).TotalSeconds);
+            }
+            
+
             await _trCallRecordRepository.AddAsync(model, HttpContext.RequestAborted);
             return OpenResponse.Ok("success");
         }
@@ -176,6 +218,42 @@ namespace Hotline.Api.Controllers
             return OpenResponse.Ok("success");
         }
 
+        #endregion
+
+        #region 通话记录(对内)
+        /// <summary>
+        /// 获取通话记录列表
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpGet("calls/call-list")]
+        public async Task<PagedDto<TrCallDto>> GetCallList([FromQuery]GetCallListDto dto)
+        {
+            var (total, items) = await _trCallRecordRepository.Queryable()
+                .WhereIF(!string.IsNullOrEmpty(dto.CPN), x => x.CPN.Contains(dto.CPN))
+                .WhereIF(!string.IsNullOrEmpty(dto.CDPN), x => x.CDPN.Contains(dto.CDPN))
+                .WhereIF(dto.CallDirection != null, x => x.CallDirection == dto.CallDirection)
+                .WhereIF(dto.OnState != null, x => x.OnState == dto.OnState)
+                .OrderByDescending(x => x.CreatedTime)
+                .ToPagedListAsync(dto.PageIndex, dto.PageSize);
+            return new PagedDto<TrCallDto>(total, _mapper.Map<IReadOnlyList<TrCallDto>>(items));
+        }
+
+        /// <summary>
+        /// 通话记录基础数据
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet("calls/basedata")]
+        public async Task<object> CallBaseData()
+        {
+            return new
+            {
+                OnState = EnumExts.GetDescriptions<EOnState>(),
+                CallDirection = EnumExts.GetDescriptions<ECallDirection>(),
+            };
+        }
+
+
         #endregion
 
         #endregion

+ 109 - 70
src/Hotline.Api/Controllers/OrderController.cs

@@ -40,6 +40,7 @@ using System.Diagnostics;
 using System.Transactions;
 using System.Collections.Generic;
 using Hotline.CallCenter.Calls;
+using Hotline.DataSharing.Province.Services;
 using Hotline.Share.Dtos.File;
 
 namespace Hotline.Api.Controllers;
@@ -64,6 +65,7 @@ public class OrderController : BaseController
     private readonly IRepository<OrderVisit> _orderVisitRepository;
     private readonly IRepository<OrderVisitDetail> _orderVisitedDetailRepository;
     private readonly ICapPublisher _capPublisher;
+    private readonly IProvinceService _provinceService;
     private readonly IRepository<OrderDelay> _orderDelayRepository;
     private readonly ITimeLimitDomainService _timeLimitDomainService;
     private readonly ISystemSettingCacheManager _systemSettingCacheManager;
@@ -107,6 +109,7 @@ public class OrderController : BaseController
         IRepository<OrderVisit> orderVisitRepository,
         IRepository<OrderVisitDetail> orderVisitedDetailRepository,
         ICapPublisher capPublisher,
+        IProvinceService provinceService,
         IRepository<OrderDelay> orderDelayRepository,
         ITimeLimitDomainService timeLimitDomainService,
         ISystemSettingCacheManager systemSettingCacheManager,
@@ -150,6 +153,7 @@ public class OrderController : BaseController
         _orderVisitRepository = orderVisitRepository;
         _orderVisitedDetailRepository = orderVisitedDetailRepository;
         _capPublisher = capPublisher;
+        _provinceService = provinceService;
         _orderDelayRepository = orderDelayRepository;
         _timeLimitDomainService = timeLimitDomainService;
         _systemSettingCacheManager = systemSettingCacheManager;
@@ -250,8 +254,8 @@ public class OrderController : BaseController
         if (order is { ProcessType: EProcessType.Zhiban, CounterSignType: null })
         {
             orderVisit.VisitState = EVisitState.Visited;
-		}
-		string visitId = await _orderVisitRepository.AddAsync(orderVisit);
+        }
+        string visitId = await _orderVisitRepository.AddAsync(orderVisit);
 
         //新增回访信息
         var visitedDetail = new List<OrderVisitDetail>();
@@ -259,31 +263,32 @@ public class OrderController : BaseController
         var seatDetail = new OrderVisitDetail();
         seatDetail.VisitId = visitId;
         seatDetail.VisitTarget = EVisitTarget.Seat;
-		foreach (var item in dto.IdNames)
-		{
-			var orgDetail = new OrderVisitDetail();
-			orgDetail.VisitId = visitId;
-			orgDetail.VisitOrgCode = item.Key;
-			orgDetail.VisitOrgName = item.Value;
-			orgDetail.VisitTarget = EVisitTarget.Org;
-            if (order is { ProcessType: EProcessType.Zhiban, CounterSignType: null }) {
-	            var satisfy = new Kv() { Key = "4", Value = "满意" };
-	            orgDetail.OrgProcessingResults = satisfy;
-	            orgDetail.OrgHandledAttitude = satisfy;
-			}
-			visitedDetail.Add(orgDetail);
-		}
-		if (order is { ProcessType: EProcessType.Zhiban, CounterSignType: null })
-		{
-			seatDetail.VoiceEvaluate = EVoiceEvaluate.Satisfied;
-			seatDetail.SeatEvaluate = ESeatEvaluate.Satisfied;
-			order.Visited("4", "满意");
-			order.Status = EOrderStatus.Visited;
-			await _orderRepository.UpdateAsync(order, HttpContext.RequestAborted);
-		}
-		visitedDetail.Add(seatDetail);
-		await _orderVisitedDetailRepository.AddRangeAsync(visitedDetail, HttpContext.RequestAborted);
-		await _mediator.Publish(new AddVisitNotify{ OrderVisitedDetails =  visitedDetail }, HttpContext.RequestAborted);
+        foreach (var item in dto.IdNames)
+        {
+            var orgDetail = new OrderVisitDetail();
+            orgDetail.VisitId = visitId;
+            orgDetail.VisitOrgCode = item.Key;
+            orgDetail.VisitOrgName = item.Value;
+            orgDetail.VisitTarget = EVisitTarget.Org;
+            if (order is { ProcessType: EProcessType.Zhiban, CounterSignType: null })
+            {
+                var satisfy = new Kv() { Key = "4", Value = "满意" };
+                orgDetail.OrgProcessingResults = satisfy;
+                orgDetail.OrgHandledAttitude = satisfy;
+            }
+            visitedDetail.Add(orgDetail);
+        }
+        if (order is { ProcessType: EProcessType.Zhiban, CounterSignType: null })
+        {
+            seatDetail.VoiceEvaluate = EVoiceEvaluate.Satisfied;
+            seatDetail.SeatEvaluate = ESeatEvaluate.Satisfied;
+            order.Visited("4", "满意");
+            order.Status = EOrderStatus.Visited;
+            await _orderRepository.UpdateAsync(order, HttpContext.RequestAborted);
+        }
+        visitedDetail.Add(seatDetail);
+        await _orderVisitedDetailRepository.AddRangeAsync(visitedDetail, HttpContext.RequestAborted);
+        await _mediator.Publish(new AddVisitNotify { OrderVisitedDetails = visitedDetail }, HttpContext.RequestAborted);
 
         //TODO 中国政府网
         if (order.SourceChannelCode == "ZGZFW")
@@ -443,7 +448,7 @@ public class OrderController : BaseController
             .Includes(x => x.OrderVisitDetails)
             .WhereIF(dto.VisitState == EVisitStateQuery.NoVisit,
                 x => (x.VisitState == Share.Enums.Order.EVisitState.WaitForVisit ||
-                     x.VisitState == Share.Enums.Order.EVisitState.NoSatisfiedWaitForVisit) && x.Order.IsProvince  == false ) 
+                     x.VisitState == Share.Enums.Order.EVisitState.NoSatisfiedWaitForVisit) && x.Order.IsProvince == false)
             .WhereIF(dto.VisitState == EVisitStateQuery.Visited,
                 x => x.VisitState == Share.Enums.Order.EVisitState.Visited)
             .WhereIF(!string.IsNullOrEmpty(dto.Keyword),
@@ -549,10 +554,10 @@ public class OrderController : BaseController
         //update order
         if (dto.IsPutThrough)
         {
-	        visit.VisitState = Share.Enums.Order.EVisitState.Visited;
-	        visit.VisitTime = DateTime.Now;
-	        visit.VisitType = EVisitType.ArtificialVisit;
-			if (first != null)
+            visit.VisitState = Share.Enums.Order.EVisitState.Visited;
+            visit.VisitTime = DateTime.Now;
+            visit.VisitType = EVisitType.ArtificialVisit;
+            if (first != null)
             {
                 visit.Order.Visited(first.OrgProcessingResults.Key, first.OrgProcessingResults.Value);
             }
@@ -575,20 +580,33 @@ public class OrderController : BaseController
             if (first != null)
             {
                 //推省上
-                _capPublisher.Publish(EventNames.HotlineOrderVisited,
-                    new PublishVisitDto()
-                    {
-                        Order = orderDto,
-                        No = visit.No,
-                        VisitType = visit.VisitType,
-                        VisitName = visit.CreatorName,
-                        VisitTime = visit.VisitTime,
-                        VisitRemark = first.VisitContent,
-                        AreaCode = visit.Order.AreaCode!,
-                        SubjectResultSatifyCode = first.OrgProcessingResults.Key,
-                        FirstSatisfactionCode = visit.Order.FirstVisitResultCode!,
-                        ClientGuid = ""
-                    });
+                //_capPublisher.Publish(EventNames.HotlineOrderVisited,
+                //    new PublishVisitDto()
+                //    {
+                //        Order = orderDto,
+                //        No = visit.No,
+                //        VisitType = visit.VisitType,
+                //        VisitName = visit.CreatorName,
+                //        VisitTime = visit.VisitTime,
+                //        VisitRemark = first.VisitContent,
+                //        AreaCode = visit.Order.AreaCode!,
+                //        SubjectResultSatifyCode = first.OrgProcessingResults.Key,
+                //        FirstSatisfactionCode = visit.Order.FirstVisitResultCode!,
+                //        ClientGuid = ""
+                //    });
+                await _provinceService.SubmitVisitInfo(new PublishVisitDto()
+                {
+                    Order = orderDto,
+                    No = visit.No,
+                    VisitType = visit.VisitType,
+                    VisitName = visit.CreatorName,
+                    VisitTime = visit.VisitTime,
+                    VisitRemark = first.VisitContent,
+                    AreaCode = visit.Order.AreaCode!,
+                    SubjectResultSatifyCode = first.OrgProcessingResults.Key,
+                    FirstSatisfactionCode = visit.Order.FirstVisitResultCode!,
+                    ClientGuid = ""
+                }, HttpContext.RequestAborted);
             }
             if (first != null)
             {
@@ -1146,7 +1164,7 @@ public class OrderController : BaseController
     [HttpGet("screen")]
     public async Task<PagedDto<OrderScreenListDto>> ScreenList([FromQuery] ScreenListDto dto)
     {
-        var  query =  _orderScreenRepository.Queryable()
+        var query = _orderScreenRepository.Queryable()
             .Includes(x => x.Order)
             .Includes(x => x.VisitDetail)
             .Includes(x => x.Visit, d => d.Order)
@@ -1155,11 +1173,11 @@ public class OrderController : BaseController
                 d => d.Visit.Order.Title.Contains(dto.Keyword!) || d.Visit.Order.No.Contains(dto.Keyword!));
         if (dto.Status is EScreenStatus.Apply)
         {
-            query.Where(x =>(x.Status == EScreenStatus.Apply || x.Status == EScreenStatus.Approval)
-			&& SqlFunc.JsonListObjectAny(x.Workflow.HandlerUsers,"Key", _sessionContext.RequiredUserId));
-		}
-        var  (total, items) = await query
-			.WhereIF(dto.Status.HasValue && dto.Status == EScreenStatus.MyHandle,
+            query.Where(x => (x.Status == EScreenStatus.Apply || x.Status == EScreenStatus.Approval)
+            && SqlFunc.JsonListObjectAny(x.Workflow.HandlerUsers, "Key", _sessionContext.RequiredUserId));
+        }
+        var (total, items) = await query
+            .WhereIF(dto.Status.HasValue && dto.Status == EScreenStatus.MyHandle,
                 x => x.Status != EScreenStatus.Apply && x.CreatorId == _sessionContext.UserId)
             .WhereIF(dto.CreationTimeStart.HasValue, d => d.CreationTime >= dto.CreationTimeStart)
             .WhereIF(dto.CreationTimeEnd.HasValue, d => d.CreationTime <= dto.CreationTimeEnd)
@@ -1352,12 +1370,18 @@ public class OrderController : BaseController
                 if (supervise != null)
                 {
                     var superviseDto = _mapper.Map<SuperviseOrderDto>(supervise);
-                    _capPublisher.Publish(EventNames.HotlineOrderSuperviseCourse, new PublishSuperviseDto()
+                    //_capPublisher.Publish(EventNames.HotlineOrderSuperviseCourse, new PublishSuperviseDto()
+                    //{
+                    //    Order = orderDto,
+                    //    Supervise = superviseDto,
+                    //    ClientGuid = ""
+                    //});
+                    await _provinceService.SendSuperviseProcessInfo(new PublishSuperviseDto()
                     {
                         Order = orderDto,
                         Supervise = superviseDto,
                         ClientGuid = ""
-                    });
+                    }, HttpContext.RequestAborted);
                 }
             }
         }
@@ -1390,12 +1414,18 @@ public class OrderController : BaseController
         {
             var orderDto = _mapper.Map<OrderDto>(order);
             var superviseDto = _mapper.Map<SuperviseOrderDto>(supervise);
-            _capPublisher.Publish(EventNames.HotlineOrderSuperviseCourse, new PublishSuperviseDto()
+            //_capPublisher.Publish(EventNames.HotlineOrderSuperviseCourse, new PublishSuperviseDto()
+            //{
+            //    Order = orderDto,
+            //    Supervise = superviseDto,
+            //    ClientGuid = ""
+            //});
+            await _provinceService.SendSuperviseProcessInfo(new PublishSuperviseDto()
             {
                 Order = orderDto,
                 Supervise = superviseDto,
                 ClientGuid = ""
-            });
+            }, HttpContext.RequestAborted);
         }
     }
 
@@ -1421,12 +1451,18 @@ public class OrderController : BaseController
         {
             var orderDto = _mapper.Map<OrderDto>(order);
             var superviseDto = _mapper.Map<SuperviseOrderDto>(supervise);
-            _capPublisher.Publish(EventNames.HotlineOrderSuperviseCourse, new PublishSuperviseDto()
+            //_capPublisher.Publish(EventNames.HotlineOrderSuperviseCourse, new PublishSuperviseDto()
+            //{
+            //    Order = orderDto,
+            //    Supervise = superviseDto,
+            //    ClientGuid = ""
+            //});
+            await _provinceService.SendSuperviseProcessInfo(new PublishSuperviseDto()
             {
                 Order = orderDto,
                 Supervise = superviseDto,
                 ClientGuid = ""
-            });
+            }, HttpContext.RequestAborted);
         }
     }
 
@@ -1443,7 +1479,7 @@ public class OrderController : BaseController
             .Includes(x => x.Order)
             .FirstAsync(x => x.Id == id);
     }
-    
+
     #endregion
 
     #region 工单催办
@@ -1510,12 +1546,12 @@ public class OrderController : BaseController
                 if (urge != null)
                 {
                     var urgeDto = _mapper.Map<UrgeOrderDto>(urge);
-                    _capPublisher.Publish(EventNames.HotlineOrderSuperviseCourse, new PublishUrgeDto()
-                    {
-                        Order = orderDto,
-                        Urge = urgeDto,
-                        ClientGuid = ""
-                    });
+                    //_capPublisher.Publish(EventNames.HotlineOrderSuperviseCourse, new PublishUrgeDto()
+                    //{
+                    //    Order = orderDto,
+                    //    Urge = urgeDto,
+                    //    ClientGuid = ""
+                    //});
                 }
             }
         }
@@ -1577,7 +1613,7 @@ public class OrderController : BaseController
             .Includes(x => x.Order)
             .FirstAsync(x => x.Id == id);
     }
-    
+
     #endregion
 
     #region 工单办理
@@ -1685,7 +1721,7 @@ public class OrderController : BaseController
                 _sessionContext.RequiredOrgId, _sessionContext.OrgName,
                 _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName));
         }
-		var dto = _mapper.Map<OrderDto>(order!);
+        var dto = _mapper.Map<OrderDto>(order!);
 
         var files = await _fileRepository.Queryable().Where(x => x.Key == dto.Id && x.Classify == "受理上传" && string.IsNullOrEmpty(x.FlowKey)).ToListAsync();
 		dto.Files = _mapper.Map<List<FileDto>>(files);
@@ -1700,7 +1736,7 @@ public class OrderController : BaseController
             .Where(x => x.OrderId == id).Distinct().ToListAsync();
         var repeatables = _mapper.Map<List<RepeatableEventDetailOpDto>>(repeatablesMap);
         dto.RepeatableEventDetails = repeatables;
-		return dto;
+        return dto;
     }
 
     /// <summary>
@@ -2081,8 +2117,11 @@ public class OrderController : BaseController
             var order = await _orderRepository.GetAsync(sendBack.OrderId);
             var sendBackDto = _mapper.Map<OrderSendBackDto>(sendBack);
             var OrderDto = _mapper.Map<OrderDto>(order);
-            _capPublisher.Publish(EventNames.HotlineOrderSendBackApplyed,
-                new PublishOrderSendBackDto() { Order = OrderDto, SendBack = sendBackDto, ClientGuid = "" });
+            //_capPublisher.Publish(EventNames.HotlineOrderSendBackApplyed,
+            //    new PublishOrderSendBackDto() { Order = OrderDto, SendBack = sendBackDto, ClientGuid = "" });
+            await _provinceService.GetCaseBackApply(
+                new PublishOrderSendBackDto { Order = OrderDto, SendBack = sendBackDto, ClientGuid = "" },
+                HttpContext.RequestAborted);
         }
     }
 

+ 55 - 33
src/Hotline.Application/Handlers/FlowEngine/WorkflowEndHandler.cs

@@ -2,6 +2,7 @@
 using Hotline.Article;
 using Hotline.Article.Notifications;
 using Hotline.CallCenter.Tels;
+using Hotline.DataSharing.Province.Services;
 using Hotline.FlowEngine.Notifications;
 using Hotline.FlowEngine.WorkflowModules;
 using Hotline.FlowEngine.Workflows;
@@ -30,6 +31,7 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
     private readonly IOrderDomainService _orderDomainService;
     private readonly ITelDomainService _telDomainService;
     private readonly IOrderRepository _orderRepository;
+    private readonly IProvinceService _provinceService;
     private readonly ICapPublisher _capPublisher;
     private readonly IMapper _mapper;
     private readonly IRepository<OrderVisitDetail> _orderVisitedDetailRepository;
@@ -49,6 +51,7 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
         IOrderDomainService orderDomainService,
         ITelDomainService telDomainService,
         IOrderRepository orderRepository,
+        IProvinceService provinceService,
         ICapPublisher capPublisher,
         IMapper mapper,
         IRepository<OrderVisitDetail> orderVisitedDetailRepository,
@@ -68,6 +71,7 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
         _orderDomainService = orderDomainService;
         _telDomainService = telDomainService;
         _orderRepository = orderRepository;
+        _provinceService = provinceService;
         _capPublisher = capPublisher;
         _mapper = mapper;
         _orderScreenRepository = orderScreenRepository;
@@ -101,12 +105,12 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
                 {
                     await _knowledgeDomainService.EndWorkKnowledge(workflow, cancellationToken);
                 }
-                else 
+                else
                 {
-	                await _knowledgeDomainService.TerminateWorkKnowledge(workflow, cancellationToken);
-				}
-	            break;
-			case WorkflowModuleConsts.TelRestApply:
+                    await _knowledgeDomainService.TerminateWorkKnowledge(workflow, cancellationToken);
+                }
+                break;
+            case WorkflowModuleConsts.TelRestApply:
                 await _telDomainService.TelRestApplyPassAsync(workflow.ExternalId, cancellationToken);
                 break;
             case WorkflowModuleConsts.OrderHandle:
@@ -116,7 +120,12 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
                 order.File();
                 await _orderRepository.UpdateAsync(order, cancellationToken);
 
-                await _capPublisher.PublishAsync(EventNames.HotlineOrderFiled, new OrderFlowDto
+                //await _capPublisher.PublishAsync(EventNames.HotlineOrderFiled, new OrderFlowDto
+                //{
+                //    Order = _mapper.Map<OrderDto>(order),
+                //    WorkflowTrace = _mapper.Map<WorkflowTraceDto>(notification.Trace)
+                //}, cancellationToken: cancellationToken);
+                await _provinceService.OrderFiled(new OrderFlowDto
                 {
                     Order = _mapper.Map<OrderDto>(order),
                     WorkflowTrace = _mapper.Map<WorkflowTraceDto>(notification.Trace)
@@ -140,36 +149,49 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
                             visitDetail.OrgProcessingResults = screenSatisfy;
                             visitDetail.OrgHandledAttitude = screenSatisfy;
                             await _orderVisitedDetailRepository.UpdateAsync(visitDetail, cancellationToken);
-							//获取回访信息
-							var visit = await _orderVisitRepository.Queryable().Includes(x => x.Order)
-								.Includes(x => x.OrderVisitDetails)
-								.Where(x => x.Id == screen.VisitId).FirstAsync(cancellationToken);
-							if (visit != null)
-							{
-								//获取回访明细
-								var visitDe = visit.OrderVisitDetails.First(x => x.Id == screen.VisitDetailId);
-								//推省上
-								_capPublisher.Publish(EventNames.HotlineOrderScreenApplyed,
-									new PublishVisitDto()
-									{
-										Order = _mapper.Map<OrderDto>(visit.Order),
-										No = visit.No,
-										VisitType = visit.VisitType,
-										VisitName = visit.CreatorName,
-										VisitTime = visit.VisitTime,
-										VisitRemark = visitDe.VisitContent,
-										AreaCode = visit.Order.AreaCode!,
-										SubjectResultSatifyCode = visitDe.OrgProcessingResults?.Key,
-										FirstSatisfactionCode = visit.Order.FirstVisitResultCode!,
-										ClientGuid = ""
-									});
-							}
-						}
+                            //获取回访信息
+                            var visit = await _orderVisitRepository.Queryable().Includes(x => x.Order)
+                                .Includes(x => x.OrderVisitDetails)
+                                .Where(x => x.Id == screen.VisitId).FirstAsync(cancellationToken);
+                            if (visit != null)
+                            {
+                                //获取回访明细
+                                var visitDe = visit.OrderVisitDetails.First(x => x.Id == screen.VisitDetailId);
+                                //推省上
+                                //_capPublisher.Publish(EventNames.HotlineOrderScreenApplyed,
+                                //    new PublishVisitDto()
+                                //    {
+                                //        Order = _mapper.Map<OrderDto>(visit.Order),
+                                //        No = visit.No,
+                                //        VisitType = visit.VisitType,
+                                //        VisitName = visit.CreatorName,
+                                //        VisitTime = visit.VisitTime,
+                                //        VisitRemark = visitDe.VisitContent,
+                                //        AreaCode = visit.Order.AreaCode!,
+                                //        SubjectResultSatifyCode = visitDe.OrgProcessingResults?.Key,
+                                //        FirstSatisfactionCode = visit.Order.FirstVisitResultCode!,
+                                //        ClientGuid = ""
+                                //    });
+                                await _provinceService.SubmitOrderScreenSuccess(new PublishVisitDto()
+                                {
+                                    Order = _mapper.Map<OrderDto>(visit.Order),
+                                    No = visit.No,
+                                    VisitType = visit.VisitType,
+                                    VisitName = visit.CreatorName,
+                                    VisitTime = visit.VisitTime,
+                                    VisitRemark = visitDe.VisitContent,
+                                    AreaCode = visit.Order.AreaCode!,
+                                    SubjectResultSatifyCode = visitDe.OrgProcessingResults?.Key,
+                                    FirstSatisfactionCode = visit.Order.FirstVisitResultCode!,
+                                    ClientGuid = ""
+                                }, cancellationToken);
+                            }
+                        }
                     }
                     else
                     {
-	                    await _orderRepository.OrderScreenRevisionVisit(screen.VisitId, true, cancellationToken);
-						screen.Status = EScreenStatus.Refuse;
+                        await _orderRepository.OrderScreenRevisionVisit(screen.VisitId, true, cancellationToken);
+                        screen.Status = EScreenStatus.Refuse;
                         screen.ReplyContent = workflow.ActualOpinion;
                         await _orderScreenRepository.UpdateAsync(screen, cancellationToken);
                     }

+ 24 - 6
src/Hotline.Application/Handlers/FlowEngine/WorkflowNextHandler.cs

@@ -1,5 +1,6 @@
 using DotNetCore.CAP;
 using Hotline.Application.FlowEngine;
+using Hotline.DataSharing.Province.Services;
 using Hotline.FlowEngine;
 using Hotline.FlowEngine.Notifications;
 using Hotline.FlowEngine.WorkflowModules;
@@ -38,18 +39,20 @@ public class WorkflowNextHandler : INotificationHandler<NextStepNotify>
     private readonly ITimeLimitDomainService _timeLimitDomainService;
     private readonly IWorkflowDomainService _workflowDomainService;
     private readonly ICapPublisher _capPublisher;
+    private readonly IProvinceService _provinceService;
     private readonly IMapper _mapper;
     private readonly ILogger<WorkflowNextHandler> _logger;
     private readonly IRepository<OrderScreen> _orderScreenRepository;
     private readonly IQualityRepository _qualityRepository;
 
-	public WorkflowNextHandler(
+    public WorkflowNextHandler(
         IOrderDomainService orderDomainService,
         IKnowledgeDomainService knowledgeDomainService,
         IOrderRepository orderRepository,
         ITimeLimitDomainService timeLimitDomainService,
         IWorkflowDomainService workflowDomainService,
         ICapPublisher capPublisher,
+        IProvinceService provinceService,
         IMapper mapper,
         ILogger<WorkflowNextHandler> logger,
         IRepository<OrderScreen> orderScreenRepository,
@@ -61,11 +64,12 @@ public class WorkflowNextHandler : INotificationHandler<NextStepNotify>
         _timeLimitDomainService = timeLimitDomainService;
         _workflowDomainService = workflowDomainService;
         _capPublisher = capPublisher;
+        _provinceService = provinceService;
         _mapper = mapper;
         _logger = logger;
         _orderScreenRepository = orderScreenRepository;
         _qualityRepository = qualityRepository;
-	}
+    }
 
     /// <summary>Handles a notification</summary>
     /// <param name="notification">The notification</param>
@@ -108,7 +112,7 @@ public class WorkflowNextHandler : INotificationHandler<NextStepNotify>
                         order.CenterToOrg(expiredTime.EndTime);
                         //写入质检
                         await _qualityRepository.AddQualityAsync(EQualitySource.Send, order.Id, cancellationToken);
-					}
+                    }
 
                     await _workflowDomainService.UpdateExpiredTimeAsync(workflow, expiredTime.EndTime,
                         expiredTime.RuleStr, data.External.TimeLimit, data.External.TimeLimitUnit, order.CenterToOrgTime, cancellationToken);
@@ -118,7 +122,14 @@ public class WorkflowNextHandler : INotificationHandler<NextStepNotify>
                 await _orderRepository.UpdateAsync(order, cancellationToken);
 
                 var orderDto = _mapper.Map<OrderDto>(order);
-                await _capPublisher.PublishAsync(EventNames.HotlineOrderFlowHandled, new OrderFlowDto
+                //await _capPublisher.PublishAsync(EventNames.HotlineOrderFlowHandled, new OrderFlowDto
+                //{
+                //    Order = orderDto,
+                //    WorkflowTrace = _mapper.Map<WorkflowTraceDto>(notification.Trace),
+                //    ExpiredTimeChanged = expiredTimeChanged,
+                //    HandlerOrgLevel = notification.HandlerOrgCode.CalcOrgLevel()
+                //}, cancellationToken: cancellationToken);
+                await _provinceService.SubmitCaseProcess(new OrderFlowDto
                 {
                     Order = orderDto,
                     WorkflowTrace = _mapper.Map<WorkflowTraceDto>(notification.Trace),
@@ -126,6 +137,7 @@ public class WorkflowNextHandler : INotificationHandler<NextStepNotify>
                     HandlerOrgLevel = notification.HandlerOrgCode.CalcOrgLevel()
                 }, cancellationToken: cancellationToken);
 
+
                 break;
             case WorkflowModuleConsts.KnowledgeAdd:
             case WorkflowModuleConsts.KnowledgeUpdate:
@@ -153,12 +165,18 @@ public class WorkflowNextHandler : INotificationHandler<NextStepNotify>
                                     {
                                         var screenOrderDto = _mapper.Map<OrderDto>(screen.Order);
                                         //推省上
-                                        _capPublisher.Publish(EventNames.HotlineOrderScreenApply, new PublishScreenDto()
+                                        //_capPublisher.Publish(EventNames.HotlineOrderScreenApply, new PublishScreenDto()
+                                        //{
+                                        //    Order = screenOrderDto,
+                                        //    Screen = screenDto,
+                                        //    ClientGuid = ""
+                                        //});
+                                        await _provinceService.ScreenCaseInfoSend(new PublishScreenDto()
                                         {
                                             Order = screenOrderDto,
                                             Screen = screenDto,
                                             ClientGuid = ""
-                                        });
+                                        }, cancellationToken);
                                     }
                                 }
                                 break;

+ 6 - 1
src/Hotline.Application/Handlers/FlowEngine/WorkflowPreviousHandler.cs

@@ -5,6 +5,7 @@ using System.Text;
 using System.Threading.Tasks;
 using DotNetCore.CAP;
 using Hotline.CallCenter.Tels;
+using Hotline.DataSharing.Province.Services;
 using Hotline.FlowEngine.Notifications;
 using Hotline.FlowEngine.WorkflowModules;
 using Hotline.FlowEngine.Workflows;
@@ -27,6 +28,7 @@ namespace Hotline.Application.Handlers.FlowEngine
         private readonly IOrderRepository _orderRepository;
         private readonly IWorkflowDomainService _workflowDomainService;
         private readonly ITimeLimitDomainService _timeLimitDomainService;
+        private readonly IProvinceService _provinceService;
         private readonly ICapPublisher _capPublisher;
         private readonly IMapper _mapper;
 
@@ -35,6 +37,7 @@ namespace Hotline.Application.Handlers.FlowEngine
             IOrderRepository orderRepository,
             IWorkflowDomainService workflowDomainService,
             ITimeLimitDomainService timeLimitDomainService,
+            IProvinceService provinceService,
             ICapPublisher capPublisher,
             IMapper mapper)
         {
@@ -42,6 +45,7 @@ namespace Hotline.Application.Handlers.FlowEngine
             _orderRepository = orderRepository;
             _workflowDomainService = workflowDomainService;
             _timeLimitDomainService = timeLimitDomainService;
+            _provinceService = provinceService;
             _capPublisher = capPublisher;
             _mapper = mapper;
         }
@@ -74,7 +78,8 @@ namespace Hotline.Application.Handlers.FlowEngine
                             expiredTime.RuleStr, null, null, order.CenterToOrgTime, cancellationToken);
 
                         var dto = _mapper.Map<OrderDto>(order);
-                        await _capPublisher.PublishAsync(EventNames.HotlineOrderExpiredTimeUpdate, dto, cancellationToken: cancellationToken);
+                        //await _capPublisher.PublishAsync(EventNames.HotlineOrderExpiredTimeUpdate, dto, cancellationToken: cancellationToken);
+                        await _provinceService.HotlineOrderFlowRecalled(dto, cancellationToken);
                     }
 
                     break;

+ 6 - 1
src/Hotline.Application/Handlers/FlowEngine/WorkflowRecallHandler.cs

@@ -1,4 +1,5 @@
 using DotNetCore.CAP;
+using Hotline.DataSharing.Province.Services;
 using Hotline.FlowEngine.Notifications;
 using Hotline.FlowEngine.WorkflowModules;
 using Hotline.FlowEngine.Workflows;
@@ -20,6 +21,7 @@ public class WorkflowRecallHandler : INotificationHandler<RecallNotify>
     private readonly IOrderRepository _orderRepository;
     private readonly IWorkflowDomainService _workflowDomainService;
     private readonly ITimeLimitDomainService _timeLimitDomainService;
+    private readonly IProvinceService _provinceService;
     private readonly ICapPublisher _capPublisher;
     private readonly IMapper _mapper;
 
@@ -28,6 +30,7 @@ public class WorkflowRecallHandler : INotificationHandler<RecallNotify>
         IOrderRepository orderRepository,
         IWorkflowDomainService workflowDomainService,
         ITimeLimitDomainService timeLimitDomainService,
+        IProvinceService provinceService,
         ICapPublisher capPublisher,
         IMapper mapper)
     {
@@ -35,6 +38,7 @@ public class WorkflowRecallHandler : INotificationHandler<RecallNotify>
         _orderRepository = orderRepository;
         _workflowDomainService = workflowDomainService;
         _timeLimitDomainService = timeLimitDomainService;
+        _provinceService = provinceService;
         _capPublisher = capPublisher;
         _mapper = mapper;
     }
@@ -68,7 +72,8 @@ public class WorkflowRecallHandler : INotificationHandler<RecallNotify>
                         expiredTime.RuleStr, data.External.TimeLimit, data.External.TimeLimitUnit, order.CenterToOrgTime, cancellationToken);
 
                     var dto = _mapper.Map<OrderDto>(order);
-                    await _capPublisher.PublishAsync(EventNames.HotlineOrderExpiredTimeUpdate, dto, cancellationToken: cancellationToken);
+                    //await _capPublisher.PublishAsync(EventNames.HotlineOrderExpiredTimeUpdate, dto, cancellationToken: cancellationToken);
+                    await _provinceService.HotlineOrderFlowRecalled(dto, cancellationToken);
                 }
 
                 break;

+ 8 - 2
src/Hotline.Application/Handlers/FlowEngine/WorkflowStartHandler.cs

@@ -3,6 +3,7 @@ using System.Text.Json;
 using System.Text.Unicode;
 using DotNetCore.CAP;
 using Hotline.CallCenter.Tels;
+using Hotline.DataSharing.Province.Services;
 using Hotline.FlowEngine.Notifications;
 using Hotline.FlowEngine.WorkflowModules;
 using Hotline.KnowledgeBase;
@@ -25,6 +26,7 @@ namespace Hotline.Application.Handlers.FlowEngine
         private readonly ITelDomainService _telDomainService;
         private readonly IOrderRepository _orderRepository;
         private readonly ICapPublisher _capPublisher;
+        private readonly IProvinceService _provinceService;
         private readonly IMapper _mapper;
         private readonly ILogger<WorkflowStartHandler> _logger;
         private readonly IQualityRepository _qualityRepository;
@@ -35,6 +37,7 @@ namespace Hotline.Application.Handlers.FlowEngine
             ITelDomainService telDomainService,
             IOrderRepository orderRepository,
             ICapPublisher capPublisher,
+            IProvinceService provinceService,
             IMapper mapper,
             ILogger<WorkflowStartHandler> logger,
             IQualityRepository qualityRepository)
@@ -44,6 +47,7 @@ namespace Hotline.Application.Handlers.FlowEngine
             _telDomainService = telDomainService;
             _orderRepository = orderRepository;
             _capPublisher = capPublisher;
+            _provinceService = provinceService;
             _mapper = mapper;
             _logger = logger;
             _qualityRepository = qualityRepository;
@@ -69,8 +73,10 @@ namespace Hotline.Application.Handlers.FlowEngine
                     order.StartManageFlow();
                     _mapper.Map(workflow, order);
                     await _orderRepository.UpdateAsync(order, cancellationToken);
-                    await _capPublisher.PublishAsync(EventNames.HotlineOrderFlowStarted, _mapper.Map<OrderDto>(order),
-                        cancellationToken: cancellationToken);
+                    //await _capPublisher.PublishAsync(EventNames.HotlineOrderFlowStarted, _mapper.Map<OrderDto>(order),
+                    //    cancellationToken: cancellationToken);
+                    await _provinceService.OrderFlowStarted(_mapper.Map<OrderDto>(order), cancellationToken);
+
                     //写入质检
                     await _qualityRepository.AddQualityAsync(EQualitySource.Accepted, order.Id, cancellationToken);
 					break;

+ 177 - 1
src/Hotline.Share/Dtos/TrCallCenter/TrTelDao.cs

@@ -1,4 +1,8 @@
 
+using Hotline.Share.Enums.CallCenter;
+using Hotline.Share.Requests;
+using XF.Utility.EnumExtensions;
+
 namespace Hotline.Share.Dtos.TrCallCenter
 {
     #region 分机
@@ -76,7 +80,7 @@ namespace Hotline.Share.Dtos.TrCallCenter
     #endregion
 
 
-    #region 通话记录
+    #region 通话记录(对外)
 
     public class ReceiveCallRecordDto
     {
@@ -220,5 +224,177 @@ namespace Hotline.Share.Dtos.TrCallCenter
         public int type { get;set; }
     }
 
+    #endregion
+
+    #region 通话记录(对内)
+
+    public record class GetCallListDto: PagedRequest
+    {
+        /// <summary>
+        /// 主叫
+        /// </summary>
+        public string CPN { get; set; }
+        /// <summary>
+        /// 被叫
+        /// </summary>
+        public string CDPN { get; set; }
+        /// <summary>
+        /// 电话方向
+        /// </summary>
+        public ECallDirection? CallDirection { get; set; }
+
+        /// <summary>
+        /// 通话状态
+        /// </summary>
+        public EOnState? OnState { get; set; }
+
+    }
+
+
+    public class TrCallDto
+    {
+        /// <summary>
+        /// 可直接访问的通话录音地址
+        /// </summary>
+        public string? RecordingFileUrl { get; set; }
+        /// <summary>
+        /// 呼叫方向
+        /// </summary>
+        public ECallDirection CallDirection { get; set; }
+
+        /// <summary>
+        /// 呼叫方向
+        /// </summary>
+        public string CallDirectionText => CallDirection.GetDescription();
+
+        /// <summary>
+        /// 挂断方
+        /// </summary>
+        public EEndBy? EndBy { get; set; }
+
+        /// <summary>
+        /// 挂断方
+        /// </summary>
+        public string EndByText => EndBy.GetDescription();
+
+
+        /// <summary>
+        /// 坐席侧通话流水,无坐席参与则为手机侧通话流水
+        /// </summary>
+        public string CallAccept { get; set; }
+        /// <summary>
+        /// 主叫
+        /// </summary>
+        public string CPN { get; set; }
+        /// <summary>
+        /// 主叫名称
+        /// </summary>
+        public string CPNName { get; set; }
+        /// <summary>
+        /// 被叫
+        /// </summary>
+        public string CDPN { get; set; }
+        /// <summary>
+        /// 通话录音绝对路径
+        /// </summary>
+        public string? RecordingFileName { get; set; }
+        /// <summary>
+        /// 通话创建时间
+        /// </summary>
+        public DateTime CreatedTime { get; set; }
+        /// <summary>
+        /// 接通时间
+        /// </summary>
+        public DateTime? AnsweredTime { get; set; }
+        /// <summary>
+        /// 挂机时间
+        /// </summary>
+        public DateTime OverTime { get; set; }
+        /// <summary>
+        /// 线路号
+        /// </summary>
+        public string Gateway { get; set; }
+        /// <summary>
+        /// 呼出时自定义的内容
+        /// </summary>
+        public string? OtherStr { get; set; }
+        /// <summary>
+        /// 手机侧通话流水,无坐席参与则为空
+        /// </summary>
+        public string? OtherAccept { get; set; }
+        /// <summary>
+        /// 状态码,语音信箱:999/其他:16
+        /// </summary>
+        public string Status { get; set; }
+        /// <summary>
+        /// IVR开始时间
+        /// </summary>
+        public DateTime? BeginIvrTime { get; set; }
+        /// <summary>
+        /// IVR结束时间
+        /// </summary>
+        public DateTime? EndIvrTime { get; set; }
+        /// <summary>
+        /// 开始等待时间
+        /// </summary>
+        public DateTime? BeginQueueTime { get; set; }
+        /// <summary>
+        /// 结束等待时间
+        /// </summary>
+        public DateTime? EndQueueTime { get; set; }
+        /// <summary>
+        /// 开始振铃时间
+        /// </summary>
+        public DateTime? BeginRingTime { get; set; }
+        /// <summary>
+        /// 结束振铃时间
+        /// </summary>
+        public DateTime? EndRingTimg { get; set; }
+        /// <summary>
+        /// 队列ID
+        /// </summary>
+        public string? OlaQueue { get; set; }
+        /// <summary>
+        /// 批量外呼流水
+        /// </summary>
+        public string? BatchAccept { get; set; }
+        /// <summary>
+        /// IVR按键信息
+        /// </summary>
+        public string? IvrDtmf { get; set; }
+        /// <summary>
+        /// IVR按键对应动作,需解码
+        /// </summary>
+        public string? DtmfType { get; set; }
+
+        #region 自定义字段
+        /// <summary>
+        /// 通话时长(挂机时间-接通时间)
+        /// </summary>
+        public int Duration { get; set; }
+
+        /// <summary>
+        /// 振铃时长(振铃结束时间-振铃开始时间)
+        /// </summary>
+        public int RingTimes { get; set; }
+
+        /// <summary>
+        /// 排队时长(排队结束时间-排队开始时间)
+        /// </summary>
+        public int QueueTims { get; set; }
+
+        /// <summary>
+        /// 通话状态
+        /// </summary>
+        public EOnState OnState { get; set; }
+
+        /// <summary>
+        /// 通话状态
+        /// </summary>
+        public string OnStateText => OnState.GetDescription();
+        #endregion
+    }
+
+
     #endregion
 }

+ 25 - 0
src/Hotline/CallCenter/Calls/TrCallRecord.cs

@@ -111,5 +111,30 @@ namespace Hotline.CallCenter.Calls
         /// IVR按键对应动作,需解码
         /// </summary>
         public string? DtmfType { get; set; }
+
+        #region 自定义字段
+        /// <summary>
+        /// 通话时长(挂机时间-接通时间)
+        /// </summary>
+        [SugarColumn(DefaultValue = "0")]
+        public int Duration { get; set; }
+
+        /// <summary>
+        /// 振铃时长(振铃结束时间-振铃开始时间)
+        /// </summary>
+        [SugarColumn(DefaultValue = "0")]
+        public int RingTimes { get; set; }
+
+        /// <summary>
+        /// 排队时长(排队结束时间-排队开始时间)
+        /// </summary>
+        [SugarColumn(DefaultValue = "0")]
+        public int QueueTims { get; set; }
+
+        /// <summary>
+        /// 通话状态
+        /// </summary>
+        public EOnState OnState { get; set; }
+        #endregion
     }
 }