浏览代码

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

田爽 4 月之前
父节点
当前提交
3d01c029e4
共有 24 个文件被更改,包括 404 次插入95 次删除
  1. 31 20
      src/Hotline.Api/Controllers/Bi/BiOrderController.cs
  2. 22 8
      src/Hotline.Api/Controllers/OrderController.cs
  3. 0 1
      src/Hotline.Api/Controllers/PushMessageController.cs
  4. 45 3
      src/Hotline.Api/Controllers/PushProvinceController.cs
  5. 0 11
      src/Hotline.Api/Controllers/SysController.cs
  6. 1 1
      src/Hotline.Api/Controllers/TestController.cs
  7. 61 4
      src/Hotline.Api/Controllers/WebPortalController.cs
  8. 39 7
      src/Hotline.Application/CallCenter/DefaultCallApplication.cs
  9. 13 1
      src/Hotline.Application/CallCenter/ICallApplication.cs
  10. 2 1
      src/Hotline.Application/CallCenter/TianRunCallApplication.cs
  11. 1 1
      src/Hotline.Application/Handlers/Order/OrderVisitSmsHandler.cs
  12. 8 3
      src/Hotline.Application/Orders/OrderApplication.cs
  13. 17 11
      src/Hotline.Application/StatisticalReport/OrderReportApplication.cs
  14. 53 5
      src/Hotline.Repository.SqlSugar/Orders/OrderRepository.cs
  15. 5 0
      src/Hotline.Share/Dtos/Bi/BiOrderDto.cs
  16. 14 0
      src/Hotline.Share/Dtos/WebPortal/ArticleDetailsDto.cs
  17. 5 0
      src/Hotline.Share/Dtos/WebPortal/GetOrderCodePwd.cs
  18. 45 2
      src/Hotline.Share/Requests/PagedKeywordRequest.cs
  19. 6 0
      src/Hotline/CallCenter/Calls/CallNative.cs
  20. 14 0
      src/Hotline/Orders/IOrderRepository.cs
  21. 6 5
      src/Hotline/Settings/TimeLimitDomain/ExpireTimeHandler.cs
  22. 6 5
      src/Hotline/Settings/TimeLimitDomain/ExpireTimeLimitBase.cs
  23. 9 5
      src/Hotline/Settings/TimeLimitDomain/ExpireTimeSupplier/WorkDaySupplier.cs
  24. 1 1
      src/Hotline/Settings/TimeLimitDomain/IExpireTimeHandler.cs

+ 31 - 20
src/Hotline.Api/Controllers/Bi/BiOrderController.cs

@@ -26,6 +26,7 @@ using Hotline.Share.Enums.Order;
 using Hotline.Share.Requests;
 using Hotline.Tools;
 using MapsterMapper;
+using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
 using Org.BouncyCastle.Utilities.Collections;
 using SqlSugar;
@@ -1054,6 +1055,7 @@ namespace Hotline.Api.Controllers.Bi
         /// <param name="dto"></param>
         /// <returns></returns>
         [HttpPost("visit-measure-statistics-export")]
+        [AllowAnonymous]
         public async Task<FileStreamResult> VisitMeasureStatisticsExport([FromBody] ExportExcelDto<VisitMeasureStatisticsRequest> dto)
         {
             var query = _orderVisitRepository.Queryable()
@@ -1080,7 +1082,7 @@ namespace Hotline.Api.Controllers.Bi
             list.Add(SumModel);
             dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
             var dtos = list
-             .Select(stu => _mapper.Map(stu, typeof(VisitAndOrgSatisfactionStatisticsDto), dynamicClass))
+             .Select(stu => _mapper.Map(stu, typeof(VisitMeasureStatisticsModelDto), dynamicClass))
              .Cast<object>()
              .ToList();
 
@@ -4631,6 +4633,33 @@ namespace Hotline.Api.Controllers.Bi
 
         }
 
+        #region 智能回访统计
+
+        /// <summary>
+        /// 智能回访数据统计
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpGet("aivisit-statistics")]
+        public async Task<QueryAiVisitStatisticsResp> QueryAiVisitStatistics([FromQuery] QueryAiVisitStatisticsRequest dto)
+        {
+            var model = await _orderRepository.QueryAiVisitStatistics(dto);
+            return model;
+        }
+
+        /// <summary>
+        /// 智能回访有效性分析
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpGet("aivisit-effective-analysis")]
+        public async Task<QueryAiVisitEffectiveAnalysisResp> QueryAiVisitEffectiveAnalysis([FromQuery]QueryAiVisitStatisticsRequest dto)
+        {
+            var model = await _orderRepository.QueryAiVisitEffectiveAnalysis(dto);
+            return model;
+        }
+        #endregion
+
         /// <summary>
         /// 扭转信件统计
         /// </summary>
@@ -4693,24 +4722,6 @@ namespace Hotline.Api.Controllers.Bi
         }
 
 
-        #region 智能回访统计
-
-        /// <summary>
-        /// 智能回访数据统计
-        /// </summary>
-        /// <param name="dto"></param>
-        /// <returns></returns>
-        [HttpGet("aivisit-statistics")]
-        public async Task QueryAiVisitStatistics([FromQuery] QueryAiVisitStatisticsRequest dto)
-        {
-            _aiOrderVisitDetailRepository.Queryable().Where(x => x.CreationTime >= dto.StartTime && x.CreationTime <= dto.EndTime).GroupBy(x => x.OrderId)
-                .Select(x => new QueryAiVisitStatisticsResp()
-                {
-                    AiVisitCount = SqlFunc.AggregateCount(x.OrderId),
-                    AiVisitSuccessCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.OrderVisit.IsEffectiveAiVisit==true,1,0))
-                });
-        }
-
-        #endregion
+        
     }
 }

+ 22 - 8
src/Hotline.Api/Controllers/OrderController.cs

@@ -69,6 +69,7 @@ using Hotline.Share.Dtos.CallCenter;
 using NPOI.SS.Formula.Functions;
 using System.Threading;
 using Hotline.Share.Mq;
+using Hotline.CallCenter.Calls;
 
 namespace Hotline.Api.Controllers;
 
@@ -927,8 +928,10 @@ public class OrderController : BaseController
             .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Order.Title.StartsWith(dto.Keyword!))
             .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No == dto.No)
             .WhereIF(dto.VisitType != null, d => d.VisitType == dto.VisitType)
-            .WhereIF(dto.FiledType != null && dto.FiledType == FiledType.CenterFiled, d => d.Order.ProcessType == EProcessType.Zhiban)
-            .WhereIF(dto.FiledType != null && dto.FiledType == FiledType.OrgFiled, d => d.Order.ProcessType == EProcessType.Jiaoban)
+              //.WhereIF(dto.FiledType != null && dto.FiledType == FiledType.CenterFiled, d => d.Order.ProcessType == EProcessType.Zhiban)
+              //.WhereIF(dto.FiledType != null && dto.FiledType == FiledType.OrgFiled, d => d.Order.ProcessType == EProcessType.Jiaoban)
+              .WhereIF(dto.FiledType != null && dto.FiledType == FiledType.CenterFiled, d => d.Order.FileOrgIsCenter == true)
+            .WhereIF(dto.FiledType != null && dto.FiledType == FiledType.OrgFiled, d => d.Order.FileOrgIsCenter == false)
             .WhereIF(dto.IsCountersign != null && dto.IsCountersign == true, d => d.Order.CounterSignType != null)
             .WhereIF(dto.IsCountersign != null && dto.IsCountersign == false, d => d.Order.CounterSignType == null)
             .WhereIF(dto.QuerySelf.HasValue && dto.QuerySelf.Value, d => d.EmployeeId == _sessionContext.RequiredUserId)
@@ -1038,12 +1041,24 @@ public class OrderController : BaseController
         }
         else if (_appOptions.Value.GetDefaultAppScopeConfiguration().CallCenterType == AppDefaults.CallCenterType.XingTang)
         {
-            var call = await _callApplication.GetCallAsync(orderVisit.CallId, HttpContext.RequestAborted);
+            CallNative? call = null;
+            if (orderVisit.CallId == null && orderVisit.VisitTime.HasValue)
+            { 
+                call = await _callApplication.GetCallByTimeAndToNoAsync(orderVisit.Order.Contact, orderVisit.VisitTime.Value,  HttpContext.RequestAborted);
+            }
+            call = await _callApplication.GetCallAsync(orderVisit.CallId, HttpContext.RequestAborted);
             if (call is not null)
             {
-                recordingFileUrl = call.AudioFile;
-                recordingBaseAddress = call.AudioFile;
-                recordingAbsolutePath = call.AudioFile;
+                if (call.AudioFile.IsNullOrEmpty())
+                {
+                    call = await _callApplication.GetCallByCallNoAsync(call.CallNo, HttpContext.RequestAborted);
+                }
+                if (call is not null)
+                {
+                    recordingFileUrl = call.AudioFile;
+                    recordingBaseAddress = call.AudioFile;
+                    recordingAbsolutePath = call.AudioFile;
+                }
             }
         }
 
@@ -3375,7 +3390,6 @@ public class OrderController : BaseController
             orderTerminateList.Any(x => x.Status == ETerminateStatus.Refuse) ? "不同意" :
             orderTerminateList.Any(x => x.Status == ETerminateStatus.Approval || x.Status == ETerminateStatus.SendBack) ? "审批中" : null;
 
-
         return _sessionContext.OrgIsCenter ? dto : dto.DataMask();
     }
 
@@ -3674,7 +3688,7 @@ public class OrderController : BaseController
             }
         }
 
-        await _orderRepository.UpdateNav(order).Include(d => d.OrderExtension).ExecuteCommandAsync();
+        await _orderRepository.UpdateNav(order, new UpdateNavRootOptions { IgnoreColumns = ["CallId"] }).Include(d => d.OrderExtension).ExecuteCommandAsync();
 
         //订阅此事件的内部处理工单数据只能更新各自业务的字段,不能全部更新
         //修改工单其他处理事件  (受理短信)

+ 0 - 1
src/Hotline.Api/Controllers/PushMessageController.cs

@@ -199,7 +199,6 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <param name="pagedDto"></param>
         /// <returns></returns>
-        [Permission(EPermission.AllMessage)]
         [HttpGet("getlist")]
         public async Task<PagedDto<MessageDataDto>> AllMessage([FromQuery] MessagePagedDto pagedDto)
         {

+ 45 - 3
src/Hotline.Api/Controllers/PushProvinceController.cs

@@ -12,7 +12,9 @@ using MapsterMapper;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Options;
+using Microsoft.VisualBasic;
 using SqlSugar;
+using System.Text;
 using XF.Domain.Repository;
 
 namespace Hotline.Api.Controllers
@@ -55,7 +57,7 @@ namespace Hotline.Api.Controllers
         {
             Provinces = Provinces.Trim();
             string[] provinceNos = Provinces.Split(',');
-            for (int i = 0; i < provinceNos.Length; i++)
+            for (int i = 0;i < provinceNos.Length;i++)
             {
                 provinceNos[i] = provinceNos[i].Trim();
             }
@@ -84,7 +86,7 @@ namespace Hotline.Api.Controllers
         {
             Provinces = Provinces.Trim();
             string[] provinceNos = Provinces.Split(',');
-            for (int i = 0; i < provinceNos.Length; i++)
+            for (int i = 0;i < provinceNos.Length;i++)
             {
                 provinceNos[i] = provinceNos[i].Trim();
             }
@@ -135,7 +137,7 @@ namespace Hotline.Api.Controllers
         {
             Provinces = Provinces.Trim();
             string[] provinceNos = Provinces.Split(',');
-            for (int i = 0; i < provinceNos.Length; i++)
+            for (int i = 0;i < provinceNos.Length;i++)
             {
                 provinceNos[i] = provinceNos[i].Trim();
             }
@@ -182,6 +184,46 @@ namespace Hotline.Api.Controllers
             }
         }
 
+        /// <summary>
+        /// 根据省编号重新执行工单和通话记录关联并推送省上
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost("OrderRelateCallHandler")]
+        [AllowAnonymous]
+        public async Task<dynamic> OrderRelateCallHandlerAsync([FromBody] string provinces)
+        {
+            provinces = provinces.Trim();
+            var provinceNos = provinces.Split(',');
+            for (int i = 0;i < provinceNos.Length;i++)
+            {
+                provinceNos[i] = provinceNos[i].Trim();
+            }
+
+            var orders = await _orderRepository.Queryable()
+                .Where(d => provinceNos.Contains(d.ProvinceNo))
+                .Select(d => new { d.Id, d.ProvinceNo })
+                .ToListAsync(HttpContext.RequestAborted);
 
+            dynamic sb = new System.Dynamic.ExpandoObject();
+            var sbs = new List<dynamic>();
+            foreach (var order in orders)
+            {
+                sb.OrderId = order.Id;
+                sb.ProvinceNo = order.ProvinceNo;
+                try
+                {
+                    sb.Message = await _callApplication.OrderRelateCallHandlerAsync(order.Id, HttpContext.RequestAborted);
+                }
+                catch (ArgumentException e)
+                {
+                    sb.Message = e.Message;
+                }
+                finally
+                {
+                    sbs.Add(sb);
+                }
+            }
+            return sbs;
+        }
     }
 }

+ 0 - 11
src/Hotline.Api/Controllers/SysController.cs

@@ -279,7 +279,6 @@ namespace Hotline.Api.Controllers
         /// 获取字典类型列表
         /// </summary>
         /// <returns></returns>
-        [Permission(EPermission.GetSysDicType)]
         [HttpPost("dictype-list")]
         public async Task<List<SystemDicType>> GetSysDicType()
         {
@@ -291,7 +290,6 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <param name="dto"></param>
         /// <returns></returns>
-        [Permission(EPermission.GetSysDicData)]
         [HttpGet("dictdata-type")]
         public async Task<List<SystemDicData>> GetSysDicData([FromQuery] GetSysDicDataDto dto)
         {
@@ -315,7 +313,6 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <param name="id"></param>
         /// <returns></returns>
-        [Permission(EPermission.SysDicDataModel)]
         [HttpGet("dicdata/{id}")]
         public async Task<SystemDicData> SysDicDataModel(string id)
         {
@@ -330,7 +327,6 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <param name="dto"></param>
         /// <returns></returns>
-        [Permission(EPermission.AddDicData)]
         [HttpPost("add-dicdata")]
         public async Task AddDicData([FromBody] AddDicDataDto dto)
         {
@@ -349,7 +345,6 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <param name="dto"></param>
         /// <returns></returns>
-        [Permission(EPermission.UpdateDicData)]
         [HttpPost("update-dicdata")]
         public async Task UpdateDicData([FromBody] UpdateDicDataDto dto)
         {
@@ -384,7 +379,6 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <param name="dto"></param>
         /// <returns></returns>
-        [Permission(EPermission.AddArea)]
         [HttpPost("area")]
         public async Task AddArea([FromBody] AddAreaDto dto)
         {
@@ -423,7 +417,6 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <param name="dto"></param>
         /// <returns></returns>
-        [Permission(EPermission.ModifyArea)]
         [HttpPut("area")]
         public async Task ModifyArea([FromBody] ModifyAreaDto dto)
         {
@@ -450,7 +443,6 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <param name="id"></param>
         /// <returns></returns>
-        [Permission(EPermission.DelArea)]
         [HttpDelete("area/{id}")]
         public async Task DelArea(string id)
         {
@@ -468,7 +460,6 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <param name="id"></param>
         /// <returns></returns>
-        [Permission(EPermission.GetArea)]
         [HttpGet("area/{id}")]
         public async Task<SystemArea?> GetArea(string id)
         {
@@ -538,7 +529,6 @@ namespace Hotline.Api.Controllers
         /// 查询公开常用意见
         /// </summary>
         /// <returns></returns>
-        [Permission(EPermission.QueryOpenCommonOpinionList)]
         [HttpGet("open-common")]
         public async Task<PagedDto<SystemCommonOpinion>> QueryOpenCommonOpinionList([FromQuery] QueryCommonDto dto)
         {
@@ -556,7 +546,6 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <param name="dto"></param>
         /// <returns></returns>
-        [Permission(EPermission.CommonModify)]
         [HttpPost("common-modify")]
         public async Task CommonModify([FromBody] CommonModifyDto dto)
         {

+ 1 - 1
src/Hotline.Api/Controllers/TestController.cs

@@ -519,7 +519,7 @@ ICallApplication callApplication,
         //var r = _timeLimitDomainService.CalcExpiredTime(DateTime.Now, EFlowDirection.CenterToCenter, batchId);
         //var r = _timeLimitDomainService.CalcEndTime(DateTime.Parse("2024-09-12 14:45:47"), Share.Enums.Settings.ETimeType.WorkDay, 2, 80, 50);
         //_capPublisher.PublishDelay((DateTime.Now.AddMinutes(2) - DateTime.Now), EventNames.OrderRelateCall, "123");
-        var times = _expireTime.CalcExpiredTime(DateTime.Parse("2024-12-09 18:37:13"), DateTime.Parse("2024-12-09 18:37:13"), EFlowDirection.CenterToOrg,new OrderTimeClacInfo() { AcceptTypeCode="15"});
+        var times = _expireTime.CalcExpiredTime(DateTime.Parse("2024-12-10 20:37:58"), DateTime.Parse("2024-12-10 20:37:58"), EFlowDirection.CenterToOrg,new OrderTimeClacInfo() { AcceptTypeCode= "10" });
         //await _capPublisher.PublishDelay(EventNames.OrderRelateCall, "123", cancellationToken: HttpContext.RequestAborted);
         return OpenResponse.Ok(times.Result);
     }

+ 61 - 4
src/Hotline.Api/Controllers/WebPortalController.cs

@@ -117,11 +117,14 @@ namespace Hotline.Api.Controllers
         [HttpPost("getarticlelist")]
         public async Task<OpenResponse> GetArticleList([FromBody] QueryArticleListDto dto)
         {
+            if (string.IsNullOrEmpty(dto.PushRanges))
+                dto.PushRanges = "2";
+
             RefAsync<int> total = 0;
             var items = await _bulletinRepository.Queryable()
                 .Where(p => p.LoseEfficacyTime >= DateTime.Now)
                 .Where(p => p.IsArrive == true)
-                .Where(p => SqlFunc.JsonListObjectAny(p.PushRanges, "Key", "2"))
+                .Where(p => SqlFunc.JsonListObjectAny(p.PushRanges, "Key", dto.PushRanges))
                  .Where(p => p.BulletinTypeId == dto.NoticeType)
                  .WhereIF(!string.IsNullOrEmpty(dto.Condition), p => p.Title.Contains(dto.Condition))
                  .OrderByDescending(p => p.CreationTime)
@@ -160,10 +163,13 @@ namespace Hotline.Api.Controllers
         [HttpPost("getarticlelistbynum")]
         public async Task<OpenResponse> GetArticleListByNum([FromBody] ArticleIdByNumDto dto)
         {
+            if (string.IsNullOrEmpty(dto.PushRanges))
+                dto.PushRanges = "2";
+
             var items = await _bulletinRepository.Queryable()
                 .Where(p => p.LoseEfficacyTime >= DateTime.Now)
                 .Where(p => p.IsArrive == true)
-                .Where(p => SqlFunc.JsonListObjectAny(p.PushRanges, "Key", "2"))
+                .Where(p => SqlFunc.JsonListObjectAny(p.PushRanges, "Key", dto.PushRanges))
                 //  .Where(p => p.BulletinTypeId == dto.BulletinTypeId)
                 .WhereIF(!string.IsNullOrEmpty(dto.BulletinTypeId), p => p.BulletinTypeId == dto.BulletinTypeId)
                 .WhereIF(!string.IsNullOrEmpty(dto.CheckChar), p => p.Content.Contains(dto.CheckChar))
@@ -269,10 +275,13 @@ namespace Hotline.Api.Controllers
         [HttpPost("getpreviousandnext")]
         public async Task<OpenResponse> GetPreviousAndNext([FromBody] ArticlePreviousAndNextDto dto)
         {
+            if (string.IsNullOrEmpty(dto.PushRanges))
+                dto.PushRanges = "2";
+
             var sugar = _bulletinRepository.Queryable()
                 .Where(p => p.LoseEfficacyTime >= DateTime.Now)
                  .Where(p => p.IsArrive == true)
-                .Where(p => SqlFunc.JsonListObjectAny(p.PushRanges, "Key", "2"))
+                .Where(p => SqlFunc.JsonListObjectAny(p.PushRanges, "Key", dto.PushRanges))
                 ;
             if (dto.FullSearch == "1")//全文搜索
             {
@@ -347,6 +356,9 @@ namespace Hotline.Api.Controllers
         [HttpPost("getfulltextsearchlist")]
         public async Task<OpenResponse> GetFullTextSearchList([FromBody] QueryArticleListDto dto)
         {
+            if (string.IsNullOrEmpty(dto.PushRanges))
+                dto.PushRanges = "2";
+
             if (string.IsNullOrEmpty(dto.Condition))
                 return OpenResponse.Ok(WebPortalDeResponse<FullTextSearchListDataDto>.Success(null));
 
@@ -354,7 +366,7 @@ namespace Hotline.Api.Controllers
             var items = await _bulletinRepository.Queryable()
                 .Where(p => p.LoseEfficacyTime >= DateTime.Now)
                  .Where(p => p.IsArrive == true)
-                .Where(p => SqlFunc.JsonListObjectAny(p.PushRanges, "Key", "2"))
+                .Where(p => SqlFunc.JsonListObjectAny(p.PushRanges, "Key", dto.PushRanges))
                 .WhereIF(_appOptions.Value.IsYiBin, p => p.BulletinTypeId == "1" || p.BulletinTypeId == "5" || p.BulletinTypeId == "6" || p.BulletinTypeId == "7" || p.BulletinTypeId == "3" || p.BulletinTypeId == "4")
                 .WhereIF(_appOptions.Value.IsZiGong, p => p.BulletinTypeId == "5013" || p.BulletinTypeId == "5016" || p.BulletinTypeId == "5011" || p.BulletinTypeId == "5015" || p.BulletinTypeId == "5017")
                 .WhereIF(!string.IsNullOrEmpty(dto.Condition), p => p.Title.Contains(dto.Condition))
@@ -491,6 +503,51 @@ namespace Hotline.Api.Controllers
             }
             return OpenResponse.Ok(WebPortalDeResponse<OrderListReturnDto>.Success(new OrderListReturnDto(), "成功"));
 
+        }
+
+        /// <summary>
+        /// 用户中心用户写信数据
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("getorderbyuserphonelist")]
+        [AllowAnonymous]
+        public async Task<OpenResponse> GetOrderByUserPhoneList([FromBody] QueryOrderListByUserDto dto)
+        {
+            RefAsync<int> total = 0;
+            var items = await _webFlowAcceptRepository.Queryable()
+               .LeftJoin<Hotline.Orders.Order>((o, or) => o.OrderId == or.Id)
+               .Where(o => o.Mobile == dto.PhoneNum)
+           //重新构建数据
+           .Select((o, or) => new
+           {
+               FlowID = o.OrderId,
+               FlowCode = o.Code,
+               FlowPwd = o.Pwd,
+               FlowTitle = o.Title,
+               FlowFromName = or.SourceChannel,
+               FlowPurTypeName = o.PurTypeName,
+               ConTypeName = or.HotspotName,
+               FlowAddDate = o.CreationTime,
+               RSFlagName = or.Status < EOrderStatus.Filed ? "办理中" : "办理完成",
+               Mobile = o.Mobile
+           })
+           //将结果合并成一个表
+           .MergeTable()
+           .Where(p => p.Mobile == dto.PhoneNum)
+           .ToPageListAsync(dto.PageIndex, dto.PageSize, total);
+
+            //计算总页数
+            int nPageCount = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(total) / dto.PageSize));
+            OrderListReturnDto returnDto = new()
+            {
+                PageNum = dto.PageIndex,
+                PageCount = nPageCount,
+                Data = _mapper.Map<IReadOnlyList<OrderListDto>>(items)
+            };
+
+            return OpenResponse.Ok(WebPortalDeResponse<OrderListReturnDto>.Success(returnDto, "成功"));
+
         }
         #endregion
 

+ 39 - 7
src/Hotline.Application/CallCenter/DefaultCallApplication.cs

@@ -240,6 +240,7 @@ public abstract class DefaultCallApplication : ICallApplication
         var query = _callNativeRepository.Queryable(includeDeleted: true)
             .LeftJoin<Order>((d, o) => d.Id == o.CallId)
             .LeftJoin<OrderVisit>((d, o, v) => d.Id == v.CallId)
+            .Where((d, o, v) => d.IsDeleted == false)
             // .WhereIF(string.IsNullOrEmpty(dto.ToNo), (d, o, v) => d.GroupId != "0")
             .WhereIF(!string.IsNullOrEmpty(dto.OrderNo), (d, o, v) => o.No == dto.OrderNo)
             .WhereIF(!string.IsNullOrEmpty(dto.FromNo), d => d.FromNo.Contains(dto.FromNo!))
@@ -377,8 +378,36 @@ public abstract class DefaultCallApplication : ICallApplication
     {
         if (string.IsNullOrEmpty(callId)) return null;
         if (callId.StartsWith("2024"))
-            return await _callNativeRepository.GetAsync(m => m.CallNo == callId, cancellationToken);
-        return await _callNativeRepository.GetAsync(callId, cancellationToken);
+            return await _callNativeRepository.GetAsync(m => m.CallNo == callId && m.IsDeleted == false, cancellationToken);
+        return await _callNativeRepository.GetAsync(m => m.Id == callId && m.IsDeleted == false, cancellationToken);
+    }
+
+    /// <summary>
+    /// 查询通话记录
+    /// 因为自贡的系统中有回访和通话记录未关联的异常数据, 使用此方法查询通话记录
+    /// </summary>
+    public virtual async Task<CallNative> GetCallByTimeAndToNoAsync(string toNo, DateTime time, CancellationToken cancellationToken)
+    {
+        var beginTime = time.AddMinutes(-5);
+        var items = await _callNativeRepository.Queryable()
+            .Where(m => m.BeginIvrTime >= beginTime && m.BeginIvrTime <= time)
+            .Where(m => m.ToNo == toNo && m.Direction == ECallDirection.Out && !string.IsNullOrEmpty(m.AudioFile) && m.IsDeleted == false)
+            .OrderBy(m => m.BeginIvrTime)
+            .ToListAsync(cancellationToken);
+        if (items.NotNullOrEmpty())
+            return items.First();
+        return null;
+    }
+
+    /// <summary>
+    /// 查询通话记录
+    /// 因为自贡的系统中有回访和通话记录未关联的异常数据, 使用此方法查询通话记录
+    /// </summary>
+    public virtual async Task<CallNative> GetCallByCallNoAsync(string callNo, CancellationToken cancellationToken)
+    {
+        return  await _callNativeRepository.Queryable()
+            .Where(m => m.CallNo == callNo && !string.IsNullOrEmpty(m.AudioFile))
+            .FirstAsync(cancellationToken);
     }
 
     /// <summary>
@@ -498,7 +527,7 @@ public abstract class DefaultCallApplication : ICallApplication
     /// <param name="orderId"></param>
     /// <param name="cancellationToken"></param>
     /// <returns></returns>
-    public virtual async Task OrderRelateCallHandlerAsync(string orderId, CancellationToken cancellationToken)
+    public virtual async Task<string> OrderRelateCallHandlerAsync(string orderId, CancellationToken cancellationToken)
     {
         var orderCall = await _orderRepository.Queryable()
             .LeftJoin<CallNative>((o, c) => o.CallId == c.Id)
@@ -507,8 +536,9 @@ public abstract class DefaultCallApplication : ICallApplication
             .FirstAsync(cancellationToken);
         if (orderCall is null || orderCall.CallNo.IsNullOrEmpty())
         {
-            _logger.LogInformation($"延迟更新工单通话, 工单: {orderId} 根据 order.id left join call_native 信息为空; 消息队列无须重试");
-            return;
+            string message = $"延迟更新工单通话, 工单: {orderId} 根据 order.id left join call_native 信息为空; 消息队列无须重试";
+            _logger.LogInformation(message);
+            return message;
         }
 
         var call = await _callNativeRepository.Queryable()
@@ -530,7 +560,7 @@ public abstract class DefaultCallApplication : ICallApplication
                 Order = (await _orderRepository.GetAsync(orderId, cancellationToken)).Adapt<OrderDto>(),
                 TrCallRecordDto = call.Adapt<TrCallDto>()
             }, cancellationToken: cancellationToken);
-            return;
+            return "需要更新的callId 和 order.callId 相同(完成推省上)";
         }
 
         await _orderRepository.Updateable()
@@ -549,7 +579,9 @@ public abstract class DefaultCallApplication : ICallApplication
             Order = (await _orderRepository.GetAsync(orderId, cancellationToken)).Adapt<OrderDto>(),
             TrCallRecordDto = call.Adapt<TrCallDto>()
         }, cancellationToken: cancellationToken);
-        _systemLogRepository.Add("延迟更新工单通话", orderId, $"原CallId: {orderCall.CallId}, 更新CallId: {call.Id}", status: 1);
+        var msg = $"原CallId: {orderCall.CallId}, 更新CallId: {call.Id}";
+        _systemLogRepository.Add("延迟更新工单通话", orderId, msg, status: 1);
+        return msg + "(完成推省上)";
     }
 
     /// <summary>

+ 13 - 1
src/Hotline.Application/CallCenter/ICallApplication.cs

@@ -108,6 +108,18 @@ namespace Hotline.Application.CallCenter
         /// <returns></returns>
         Task<CallNative?> GetCallAsync(string callId, CancellationToken cancellationToken);
 
+        /// <summary>
+        /// 查询通话记录
+        /// 因为自贡的系统中有回访和通话记录未关联的异常数据, 使用此方法查询通话记录
+        /// </summary>
+        Task<CallNative> GetCallByTimeAndToNoAsync(string toNo, DateTime time, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 查询通话记录
+        /// 因为自贡的系统中有回访和通话记录未关联的异常数据, 使用此方法查询通话记录
+        /// </summary>
+        Task<CallNative> GetCallByCallNoAsync(string callNo, CancellationToken cancellationToken);
+
         /// <summary>
         /// 查询通话记录
         /// </summary>
@@ -144,7 +156,7 @@ namespace Hotline.Application.CallCenter
         /// <param name="orderId"></param>
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
-        Task OrderRelateCallHandlerAsync(string orderId, CancellationToken cancellationToken);
+        Task<string> OrderRelateCallHandlerAsync(string orderId, CancellationToken cancellationToken);
 
         /// <summary>
         /// 查询通话记录

+ 2 - 1
src/Hotline.Application/CallCenter/TianRunCallApplication.cs

@@ -192,7 +192,7 @@ namespace Hotline.Application.CallCenter
         /// <param name="orderId"></param>
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
-        public override async Task OrderRelateCallHandlerAsync(string orderId, CancellationToken cancellationToken)
+        public override async Task<string> OrderRelateCallHandlerAsync(string orderId, CancellationToken cancellationToken)
         {
             var order = await _orderRepository.GetAsync(orderId, cancellationToken);
             if (order != null)
@@ -211,6 +211,7 @@ namespace Hotline.Application.CallCenter
                     }
                 }
             }
+            return "ok";
         }
 
         /// <summary>

+ 1 - 1
src/Hotline.Application/Handlers/Order/OrderVisitSmsHandler.cs

@@ -36,7 +36,7 @@ public class OrderVisitSmsHandler : INotificationHandler<ReceiveMessageNotify>
         var msg = await _messageRepository
             .Queryable()
             .Where(m => m.TelNumber == notification.NotifyDto.TelNumber)
-            .Where(m => m.PushBusiness == EPushBusiness.VisitSms)
+            .Where(m => m.PushBusiness == EPushBusiness.VisitSms || m.PushBusiness == EPushBusiness.SearchSms)
             .Where(m => m.SendState == ESendState.Success)
             .OrderByDescending(m => m.CreationTime)
             .FirstAsync();

+ 8 - 3
src/Hotline.Application/Orders/OrderApplication.cs

@@ -688,8 +688,10 @@ public class OrderApplication : IOrderApplication, IScopeDependency
                                                                                               //.WhereIF(dto.PubRange == EPublicState.NoPub, d => !d.OrderPublish.PublishState)
             .WhereIF(dto.IsProvinceOrder.HasValue && dto.IsProvinceOrder == true, d => d.Source == ESource.ProvinceStraight)
             .WhereIF(dto.IsProvinceOrder.HasValue && dto.IsProvinceOrder == false, d => d.Source != ESource.ProvinceStraight)
-            .WhereIF(dto.FiledType != null && dto.FiledType == FiledType.CenterFiled, d => d.ProcessType == EProcessType.Zhiban)
-            .WhereIF(dto.FiledType != null && dto.FiledType == FiledType.OrgFiled, d => d.ProcessType == EProcessType.Jiaoban)
+            //.WhereIF(dto.FiledType != null && dto.FiledType == FiledType.CenterFiled, d => d.ProcessType == EProcessType.Zhiban)
+            //.WhereIF(dto.FiledType != null && dto.FiledType == FiledType.OrgFiled, d => d.ProcessType == EProcessType.Jiaoban)
+            .WhereIF(dto.FiledType != null && dto.FiledType == FiledType.CenterFiled, d => d.FileOrgIsCenter == true)
+            .WhereIF(dto.FiledType != null && dto.FiledType == FiledType.OrgFiled, d =>d. FileOrgIsCenter == false)
             .WhereIF(dto.IsCountersign != null && dto.IsCountersign == true, d => d.CounterSignType != null)
             .WhereIF(dto.IsCountersign != null && dto.IsCountersign == false, d => d.CounterSignType == null)
             .WhereIF(dto.AcceptTypes.Any(), d => dto.AcceptTypes.Contains(d.AcceptType))
@@ -751,7 +753,10 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             .LeftJoin<User>((order, user) => order.WaitForPublisherId == user.Id)
             .Where((order, user) => order.CreationTime >= dto.StartTime && order.CreationTime <= dto.EndTime &&
                                     !string.IsNullOrEmpty(order.WaitForPublisherId))
-            .WhereIF(dto.ProcessType != null, (order, user) => order.ProcessType == dto.ProcessType)
+                 //  .WhereIF(dto.ProcessType != null, (order, user) => order.ProcessType == dto.ProcessType)
+             .WhereIF(dto.ProcessType != null && dto.ProcessType ==EProcessType.Zhiban, (order, user) => order.FileOrgIsCenter == true)
+            .WhereIF(dto.ProcessType != null && dto.ProcessType == EProcessType.Jiaoban, (order, user) => order.FileOrgIsCenter == false)
+
             .GroupBy((order, user) => new { order.WaitForPublisherId, user.Name })
             .Select((order, user) => new PublishedOrderStatisticsDto
             {

+ 17 - 11
src/Hotline.Application/StatisticalReport/OrderReportApplication.cs

@@ -1507,15 +1507,17 @@ namespace Hotline.Application.StatisticalReport
                   OrgCode = IsCenter == true ? it.ActualHandleOrgCode.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("6")) : it.ActualHandleOrgCode.Substring(0, _sessionContext.RequiredOrgId.Length + 3),
                   AcceptTypeCode = it.AcceptTypeCode,
                   ProcessType = it.ProcessType,
-                  AllTime = it.ProcessType == EProcessType.Zhiban ? it.CreationTimeHandleDurationWorkday : it.CenterToOrgHandleDurationWorkday
-
+                  AllTime = it.ProcessType == EProcessType.Zhiban ? it.CreationTimeHandleDurationWorkday : it.CenterToOrgHandleDurationWorkday,
+                  FileOrgIsCenter = it.FileOrgIsCenter
               })
               .MergeTable()
               .LeftJoin<SystemOrganize>((it, o) => it.OrgCode == o.Id)
-              //.WhereIF(TypeCode == 1, (it, o) => it.OrgCode == "001")
-              //.WhereIF(TypeCode == 2, (it, o) => it.OrgCode != "001")
-              .WhereIF(dto.TypeCode == 1, (it, o) => it.ProcessType == EProcessType.Zhiban)
-              .WhereIF(dto.TypeCode == 2, (it, o) => it.ProcessType == EProcessType.Jiaoban)
+               //.WhereIF(TypeCode == 1, (it, o) => it.OrgCode == "001")
+               //.WhereIF(TypeCode == 2, (it, o) => it.OrgCode != "001")
+               //.WhereIF(dto.TypeCode == 1, (it, o) => it.ProcessType == EProcessType.Zhiban)
+               //.WhereIF(dto.TypeCode == 2, (it, o) => it.ProcessType == EProcessType.Jiaoban)
+               .WhereIF(dto.TypeCode == 1, (it, o) => it.FileOrgIsCenter == true)
+              .WhereIF(dto.TypeCode == 2, (it, o) => it.FileOrgIsCenter == false)
               .WhereIF(IsCenter == false, (it, o) => it.OrgCode.StartsWith(_sessionContext.RequiredOrgId))
                .GroupBy((it, o) => new
                {
@@ -1578,13 +1580,15 @@ namespace Hotline.Application.StatisticalReport
                  .WhereIF(!string.IsNullOrEmpty(dto.OrgCode) && dto.OrgCode != "001" && _sessionContext.RequiredOrgId != dto.OrgCode, p => p.ActualHandleOrgCode.StartsWith(dto.OrgCode))
                  .WhereIF(!string.IsNullOrEmpty(dto.OrgCode) && dto.OrgCode != "001" && _sessionContext.RequiredOrgId == dto.OrgCode, p => p.ActualHandleOrgCode == dto.OrgCode)
                  .WhereIF(!string.IsNullOrEmpty(dto.AcceptTypeCode), p => p.AcceptTypeCode == dto.AcceptTypeCode)
-                 .WhereIF(dto.TypeCode == 1, p => p.ProcessType == EProcessType.Zhiban)
-                 .WhereIF(dto.TypeCode == 2, p => p.ProcessType == EProcessType.Jiaoban)
+                   //.WhereIF(dto.TypeCode == 1, p => p.ProcessType == EProcessType.Zhiban)
+                   //.WhereIF(dto.TypeCode == 2, p => p.ProcessType == EProcessType.Jiaoban)
+                   .WhereIF(dto.TypeCode == 1, p => p.FileOrgIsCenter == true)
+                 .WhereIF(dto.TypeCode == 2, p => p.FileOrgIsCenter == false)
                  .OrderByDescending(d => d.CreationTime)
                  .MergeTable();
         }
 
-        
+
 
         /// <summary>
         /// 受理类型统计
@@ -1644,6 +1648,7 @@ namespace Hotline.Application.StatisticalReport
                 .WhereIF(dto.StartTime.HasValue, x => x.OrderVisit.VisitTime >= dto.StartTime.Value)
                 .WhereIF(dto.EndTime.HasValue, x => x.OrderVisit.VisitTime <= dto.EndTime.Value)
                 .WhereIF(isCenter == false, x => x.VisitOrgCode.StartsWith(_sessionContext.RequiredOrgId))
+                .WhereIF(!string.IsNullOrEmpty(dto.OrgProcessingResults), x => SqlFunc.JsonListObjectAny(x.OrgProcessingResults, "Key", dto.OrgProcessingResults))
                 .GroupBy((x, org) => new { VisitOrgName = org.Name, VisitOrgCode = x.VisitOrgCode.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("6")) })
                 .Select((x, org) => new BiVisitNoSatisfiedDto
                 {
@@ -1658,7 +1663,7 @@ namespace Hotline.Application.StatisticalReport
 
                 list.AddRange(table);
             }
- 
+
             return (dissatisfiedReason, list);
         }
 
@@ -1734,7 +1739,7 @@ namespace Hotline.Application.StatisticalReport
         /// <param name="dto"></param>
         /// <returns></returns>
         [HttpGet("visit-nosatisfied-detail")]
-        public ISugarQueryable<OrderVisitDetail> BiQueryVisitNoSatisfiedDetail( BiQueryVisitNoSatisfiedDetailDto dto)
+        public ISugarQueryable<OrderVisitDetail> BiQueryVisitNoSatisfiedDetail(BiQueryVisitNoSatisfiedDetailDto dto)
         {
             var IsCenter = _sessionContext.OrgIsCenter;
 
@@ -1750,6 +1755,7 @@ namespace Hotline.Application.StatisticalReport
                 .WhereIF(IsCenter == false, x => x.VisitOrgCode.StartsWith(_sessionContext.RequiredOrgId))
                 .WhereIF(IsCenter == true, x => x.VisitOrgCode.StartsWith(dto.OrgCode))
                 .WhereIF(!string.IsNullOrEmpty(dto.Keyword), x => x.OrderVisit.Order.No.Contains(dto.Keyword) || x.OrderVisit.Order.Title.Contains(dto.Keyword))
+                .WhereIF(!string.IsNullOrEmpty(dto.OrgProcessingResults), x => SqlFunc.JsonListObjectAny(x.OrgProcessingResults, "Key", dto.OrgProcessingResults))
                 .OrderBy(x => x.OrderVisit.VisitTime);
 
             return query;

+ 53 - 5
src/Hotline.Repository.SqlSugar/Orders/OrderRepository.cs

@@ -35,6 +35,7 @@ namespace Hotline.Repository.SqlSugar.Orders
         private readonly IRepository<OrderVisitDetail> _orderVisitDetailRepository;
         private readonly ISessionContext _sessionContext;
         private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+        private readonly IRepository<AiOrderVisitDetail> _aiOrderVisitDetailRepository;
 
 		public OrderRepository(ISugarUnitOfWork<HotlineDbContext> uow,
             IDataPermissionFilterBuilder dataPermissionFilterBuilder,
@@ -42,7 +43,8 @@ namespace Hotline.Repository.SqlSugar.Orders
             IRepository<OrderVisit> orderVisitRepository,
             ISessionContext sessionContext,
             IRepository<OrderVisitDetail> orderVisitDetailRepository,
-            ISystemSettingCacheManager systemSettingCacheManager
+            ISystemSettingCacheManager systemSettingCacheManager,
+            IRepository<AiOrderVisitDetail> aiOrderVisitDetailRepository
 			) : base(uow, dataPermissionFilterBuilder)
         {
             _dataPermissionFilterBuilder = dataPermissionFilterBuilder;
@@ -51,7 +53,7 @@ namespace Hotline.Repository.SqlSugar.Orders
             _sessionContext = sessionContext;
             _orderVisitDetailRepository = orderVisitDetailRepository;
             _systemSettingCacheManager = systemSettingCacheManager;
-
+            _aiOrderVisitDetailRepository = aiOrderVisitDetailRepository;
         }
 
         public async Task<string> AddOrderNavAsync(Order order, CancellationToken cancellationToken)
@@ -1234,8 +1236,10 @@ namespace Hotline.Repository.SqlSugar.Orders
             .WhereIF(dto.JudgeState != null, d => d.JudgeState == dto.JudgeState)
             .WhereIF(dto.OrgJudge != null, d => d.OrgJudge == dto.OrgJudge)
             .WhereIF(dto.SeatJudge != null, d => d.SeatJudge == dto.SeatJudge)
-            .WhereIF(dto.FiledType != null && dto.FiledType == FiledType.CenterFiled, d => d.Order.ProcessType == EProcessType.Zhiban)
-            .WhereIF(dto.FiledType != null && dto.FiledType == FiledType.OrgFiled, d => d.Order.ProcessType == EProcessType.Jiaoban)
+            //.WhereIF(dto.FiledType != null && dto.FiledType == FiledType.CenterFiled, d => d.Order.ProcessType == EProcessType.Zhiban)
+            //.WhereIF(dto.FiledType != null && dto.FiledType == FiledType.OrgFiled, d => d.Order.ProcessType == EProcessType.Jiaoban)
+                  .WhereIF(dto.FiledType != null && dto.FiledType == FiledType.CenterFiled, d => d.Order.FileOrgIsCenter == true)
+            .WhereIF(dto.FiledType != null && dto.FiledType == FiledType.OrgFiled, d => d.Order.FileOrgIsCenter == false)
             .WhereIF(!string.IsNullOrEmpty(dto.TransferPhone), x => x.Order.TransferPhone == dto.TransferPhone)
             .OrderByDescending(x => x.VisitTime);
         }
@@ -1432,7 +1436,51 @@ namespace Hotline.Repository.SqlSugar.Orders
                 ;
 
 		}
-	}
+
+        /// <summary>
+        /// 智能回访数据统计
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        public async Task<QueryAiVisitStatisticsResp> QueryAiVisitStatistics(QueryAiVisitStatisticsRequest dto)
+        {
+            var list = await _aiOrderVisitDetailRepository.Queryable().Includes(x=>x.OrderVisit).Where(x => x.CreationTime >= dto.StartTime && x.CreationTime <= dto.EndTime && x.OrderVisit.VisitState == EVisitState.Visited).ToListAsync();
+            return new QueryAiVisitStatisticsResp()
+            {
+                AiVisitCount = list.Count,
+                AiVisitSuccessCount = list.Where(x=>x.OrderVisit.IsEffectiveAiVisit==true).Count(), // SqlFunc.AggregateSum(SqlFunc.IIF(x.OrderVisit.IsEffectiveAiVisit == true, 1, 0)),
+                AiVisitArtificialReviewCount = list.Where(x => x.OrderVisit.IsEffectiveAiVisit == false).Count(),
+                AiVisitCallReviewCount = list.Where(x=> x.OrderVisit.IsEffectiveAiVisit == false && !string.IsNullOrEmpty(x.OrderVisit.CallId)).Count(),
+                AiVisitHandReviewCount = list.Where(x=>x.OrderVisit.IsEffectiveAiVisit == false && string.IsNullOrEmpty(x.OrderVisit.CallId)).Count()
+            };
+        }
+
+        /// <summary>
+        /// 智能回访有效性分析
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        public async Task<QueryAiVisitEffectiveAnalysisResp> QueryAiVisitEffectiveAnalysis(QueryAiVisitStatisticsRequest dto)
+        {
+            var list = await _aiOrderVisitDetailRepository.Queryable().Includes(x => x.OrderVisit).Where(x => x.CreationTime >= dto.StartTime && x.CreationTime <= dto.EndTime && x.OrderVisit.VisitState == EVisitState.Visited).ToListAsync();
+            return new QueryAiVisitEffectiveAnalysisResp()
+            {
+                AiVisitCount = list.Count,
+                AiVisitSuccessCount =list.Where(x=> x.OrderVisit.IsEffectiveAiVisit == true).Count(),
+                AiVisitCallTwoFailCount = list.Where(x=> x.CallTimes>=1 && x.AiIsContact ==null && x.AiVolved ==null && (x.AiOrgProcessingResults == null || x.AiOrgProcessingResults?.Key == null)).Count(),
+                AiVisitHaveOneAnswerCount = list.Where(x=>x.OrderVisit.IsEffectiveAiVisit == false &&
+                                                                            (x.AiIsContact!= null && x.AiVolved==null && (x.AiOrgProcessingResults == null || x.AiOrgProcessingResults.Key == null)) ||
+                                                                            (x.AiIsContact == null && x.AiVolved != null && (x.AiOrgProcessingResults == null || x.AiOrgProcessingResults?.Key == null)) ||
+                                                                            (x.AiIsContact == null && x.AiVolved == null && (x.AiOrgProcessingResults != null || x.AiOrgProcessingResults?.Key != null))).Count(), //只有一个答案
+                AiVisitHaveTwoAnswerCount = list.Where(x=> x.OrderVisit.IsEffectiveAiVisit == false &&
+                                                                            (x.AiIsContact != null && x.AiVolved != null && (x.AiOrgProcessingResults == null || x.AiOrgProcessingResults?.Key == null)) ||
+                                                                            (x.AiIsContact != null && x.AiVolved == null && (x.AiOrgProcessingResults != null || x.AiOrgProcessingResults?.Key != null)) ||
+                                                                            (x.AiIsContact == null && x.AiVolved != null && (x.AiOrgProcessingResults != null || x.AiOrgProcessingResults?.Key != null))).Count(),//只有两个答案
+                AiVisitHaveThreeAnswerCount = list.Where(x=> x.OrderVisit.IsEffectiveAiVisit == false && x.AiIsContact != null && x.AiVolved != null && (x.AiOrgProcessingResults != null || x.AiOrgProcessingResults?.Key != null)).Count()//有三个答案
+            };
+        }
+
+    }
 
     public class OrderScreenRepository : BaseRepositoryWorkflow<OrderScreen>, IOrderScreenRepository, IScopeDependency
     {

+ 5 - 0
src/Hotline.Share/Dtos/Bi/BiOrderDto.cs

@@ -5,6 +5,11 @@ namespace Hotline.Share.Dtos.Bi
 {
     public record QueryVisitNoSatisfiedDto
     {
+        /// <summary>
+        /// 回访结果 空查询全部,-1:视为满意;2:不满意
+        /// </summary>
+        public string? OrgProcessingResults { get; set; }
+
         public string OrgName { get; set; }
 
         public DateTime? StartTime { get; set; }

+ 14 - 0
src/Hotline.Share/Dtos/WebPortal/ArticleDetailsDto.cs

@@ -54,6 +54,11 @@ namespace Hotline.Share.Dtos.WebPortal
         /// 显示位置
         /// </summary>
         public string? BulletinDisplayLocation { get; set; }
+
+        /// <summary>
+        /// 公告范围,不传默认查询门户网站  0:全部;1:微信小程序;2:门户网站;4:市民APP;8:部门APP
+        /// </summary>
+        public string? PushRanges {  get; set; }
     }
 
     /// <summary>
@@ -91,6 +96,10 @@ namespace Hotline.Share.Dtos.WebPortal
         /// </summary>
         public string? FullSearch { get; set; }
 
+        /// <summary>
+        /// 公告范围,不传默认查询门户网站  0:全部;1:微信小程序;2:门户网站;4:市民APP;8:部门APP
+        /// </summary>
+        public string? PushRanges { get; set; }
     }
 
     /// <summary>
@@ -256,6 +265,11 @@ namespace Hotline.Share.Dtos.WebPortal
         /// 标题
         /// </summary>
         public string? Condition { get; set; }
+
+        /// <summary>
+        /// 公告范围,不传默认查询门户网站  0:全部;1:微信小程序;2:门户网站;4:市民APP;8:部门APP
+        /// </summary>
+        public string? PushRanges { get; set; }
     }
 
     /// <summary>

+ 5 - 0
src/Hotline.Share/Dtos/WebPortal/GetOrderCodePwd.cs

@@ -68,6 +68,11 @@ namespace Hotline.Share.Dtos.WebPortal
         /// 信件编号
         /// </summary>
         public string? UserId { get; set; }
+
+        /// <summary>
+        /// 手机号码
+        /// </summary>
+        public string? PhoneNum { get; set; }
     }
 
     /// <summary>

+ 45 - 2
src/Hotline.Share/Requests/PagedKeywordRequest.cs

@@ -291,12 +291,22 @@ public record QuerySeatSwitchRequest : ReportRequiredPagedRequest
 
 public record QueryVisitNoSatiisfiedRequest : ReportPagedRequest
 {
+    /// <summary>
+    /// 回访结果 空查询全部,-1:视为满意;2:不满意
+    /// </summary>
+    public string? OrgProcessingResults { get; set; }
+
     public string? OrgName { get; set; }
 
 }
 
 public record BiQueryVisitNoSatisfiedDetailDto : ReportPagedRequest
 {
+    /// <summary>
+    /// 回访结果 空查询全部,-1:视为满意;2:不满意
+    /// </summary>
+    public string? OrgProcessingResults { get; set; }
+
     /// <summary>
     /// 部门Code
     /// </summary>
@@ -1375,12 +1385,12 @@ public class QueryAiVisitStatisticsRequest
 public class QueryAiVisitStatisticsResp
 {
     /// <summary>
-    /// 智能回访总量
+    /// 智能回访总量(去重)
     /// </summary>
     public int AiVisitCount { get; set; }
 
     /// <summary>
-    /// 智能回访有效量
+    /// 智能回访有效量(去重)
     /// </summary>
     public int AiVisitSuccessCount { get; set;}
 
@@ -1400,4 +1410,37 @@ public class QueryAiVisitStatisticsResp
     public int AiVisitHandReviewCount { get; set; }
 }
 
+
+public class QueryAiVisitEffectiveAnalysisResp
+{
+    /// <summary>
+    /// 智能回访总量(不去重)
+    /// </summary>
+    public int AiVisitCount { get; set; }
+
+    /// <summary>
+    /// 智能回访有效量(不去重)
+    /// </summary>
+    public int AiVisitSuccessCount { get; set; }
+
+    /// <summary>
+    /// 拨打两次失败量
+    /// </summary>
+    public int AiVisitCallTwoFailCount { get; set; }
+
+    /// <summary>
+    /// 有一个答案的数量
+    /// </summary>
+    public int AiVisitHaveOneAnswerCount{ get; set; }
+
+    /// <summary>
+    /// 有两个答案的数量
+    /// </summary>
+    public int AiVisitHaveTwoAnswerCount { get; set; }
+
+    /// <summary>
+    /// 有三个答案的数量
+    /// </summary>
+    public int AiVisitHaveThreeAnswerCount { get; set; }
+}
 #endregion

+ 6 - 0
src/Hotline/CallCenter/Calls/CallNative.cs

@@ -172,5 +172,11 @@ namespace Hotline.CallCenter.Calls
         /// </summary>
         [SugarColumn(ColumnDescription = "呼叫状态")]
         public ECallState? CallState { get; set; }
+
+        /// <summary>
+        /// 软删除
+        /// </summary>
+        [SugarColumn(ColumnDescription = "软删除", DefaultValue = "f")]
+        public bool IsDeleted { get; set; }
     }
 }

+ 14 - 0
src/Hotline/Orders/IOrderRepository.cs

@@ -186,6 +186,20 @@ namespace Hotline.Orders
 		/// <returns></returns>
 		ISugarQueryable<OrgVisitDetailListResp> OrgVisitDetailFiltrationList(OrgVisitDetailListReq dto, string orgId);
 
+        /// <summary>
+        /// 智能回访数据统计
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        Task<QueryAiVisitStatisticsResp> QueryAiVisitStatistics(QueryAiVisitStatisticsRequest dto);
+
+        /// <summary>
+        /// 智能回访有效性分析
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        Task<QueryAiVisitEffectiveAnalysisResp> QueryAiVisitEffectiveAnalysis(QueryAiVisitStatisticsRequest dto);
+
     }
 
     public interface IOrderScreenRepository : IRepositoryWorkflow<OrderScreen>

+ 6 - 5
src/Hotline/Settings/TimeLimitDomain/ExpireTimeHandler.cs

@@ -525,14 +525,15 @@ public class ExpireTimeHandler : IExpireTimeHandler, IScopeDependency
     /// <param name="Percentage">即将超期百分比</param>
     /// <param name="PercentageOne">超期百分比第一级</param>
     /// <returns></returns>
-    public async Task<TimeResult> CalcEndTimeAsync(DateTime beginTime,DateTime startTime, ETimeType timeType, IList<string>? workTime, int timeValue, int Percentage, int PercentageOne)
+    public async Task<TimeResult> CalcEndTimeAsync(DateTime beginTime,DateTime startTime,  IList<string>? workTime,TimeLimitSetting setting)
     {
-        return await _expireTimeFactory.GetSupplier(timeType).CalcEndTimeNewAsync(beginTime,startTime, new TimeConfig
+        return await _expireTimeFactory.GetSupplier(setting.TimeType).CalcEndTimeNewAsync(beginTime,startTime, new TimeConfig
         {
             WorkTime = workTime,
-            Count = timeValue,
-            Percentage = Percentage,
-            PercentageOne = PercentageOne,
+            Count = setting.TimeValue,
+            Percentage = setting.Percentage,
+            PercentageOne = setting.PercentageOne,
+             EndType = setting.EndType ?? 1
         });
 
         //var startTime = beginTime;

+ 6 - 5
src/Hotline/Settings/TimeLimitDomain/ExpireTimeLimitBase.cs

@@ -229,7 +229,7 @@ public abstract class ExpireTimeLimitBase
         if (setting == null) return null;
 
         var workTime = GetWorkTimes(SettingConstants.WorkTime);
-        var result = await _expireTimeHandler.CalcEndTimeAsync(beginTime,startTime, setting.TimeType, workTime, setting.TimeValue, setting.Percentage, setting.PercentageOne);
+        var result = await _expireTimeHandler.CalcEndTimeAsync(beginTime,startTime, workTime,setting);
         var expiredTimeWithConfig = new ExpiredTimeWithConfig
         {
             TimeText = setting.TimeValue + "个" + setting.TimeType.GetDescription(),
@@ -248,7 +248,7 @@ public abstract class ExpireTimeLimitBase
         var workTime = GetWorkTimes(SettingConstants.WorkTime);
         var setting = _timeLimitSettingRepository.Queryable().First(x => x.BusCode == busCode);
         if (setting == null) return null;
-        return await _expireTimeHandler.CalcEndTimeAsync(beginTime,startTime, timeType, workTime, timeValue, setting.Percentage, setting.PercentageOne);
+        return await _expireTimeHandler.CalcEndTimeAsync(beginTime,startTime, workTime,setting);
     }
 
     /// <summary>
@@ -263,7 +263,7 @@ public abstract class ExpireTimeLimitBase
     public virtual async Task<TimeResult> CalcEndTime(DateTime beginTime,DateTime startTime, ETimeType timeType, int timeValue, int Percentage, int PercentageOne)
     {
         var workTime = GetWorkTimes(SettingConstants.WorkTime);
-        return await _expireTimeHandler.CalcEndTimeAsync(beginTime,startTime, timeType, workTime, timeValue, Percentage, PercentageOne);
+        return await _expireTimeHandler.CalcEndTimeAsync(beginTime,startTime,  workTime, new TimeLimitSetting() { TimeType = timeType,TimeValue = timeValue,Percentage=Percentage,PercentageOne=PercentageOne });
     }
 
     /// <summary>
@@ -321,10 +321,11 @@ public abstract class ExpireTimeLimitBase
             case EFlowDirection.CenterToOrg:
                 if (!timeType.HasValue || !timeValue.HasValue)
                     throw new UserFriendlyException("无效参数");
-                return await _expireTimeHandler.CalcEndTimeAsync(DateTime.Now,DateTime.Now, timeType.Value, workTime, timeValue.Value, Percentage, PercentageOne);
+                return await _expireTimeHandler.CalcEndTimeAsync(DateTime.Now,DateTime.Now, workTime, new TimeLimitSetting() { TimeType = timeType.Value, TimeValue = timeValue.Value, Percentage = Percentage, PercentageOne = PercentageOne });
             case EFlowDirection.OrgToCenter:
                 //todo 根据配置
-                return await _expireTimeHandler.CalcEndTimeAsync(DateTime.Now,DateTime.Now, ETimeType.Day, workTime, 1, Percentage, PercentageOne);
+                //return await _expireTimeHandler.CalcEndTimeAsync(DateTime.Now,DateTime.Now, ETimeType.Day, workTime, 1, Percentage, PercentageOne);
+                return await _expireTimeHandler.CalcEndTimeAsync(DateTime.Now, DateTime.Now, workTime, new TimeLimitSetting() { TimeType =  ETimeType.Day, TimeValue = 1, Percentage = Percentage, PercentageOne = PercentageOne });
             default:
                 throw new ArgumentOutOfRangeException(nameof(flowDirection), flowDirection, null);
         }

+ 9 - 5
src/Hotline/Settings/TimeLimitDomain/ExpireTimeSupplier/WorkDaySupplier.cs

@@ -137,6 +137,10 @@ public class WorkDaySupplier : IExpireTimeSupplier, IScopeDependency
         DateTime WorkBeginTime = DateTime.Parse(beginTime.ToShortDateString() + " " + timeConfig.WorkTime[0] + ":00");
         DateTime WorkEndTime = DateTime.Parse(beginTime.ToShortDateString() + " " + timeConfig.WorkTime[1] + ":00");
 
+        if (timeConfig.EndType == null)
+        {
+            timeConfig.EndType = 1;
+        }
         //获取期满时间收尾方式
         var endType = timeConfig.EndType;
 
@@ -206,14 +210,14 @@ public class WorkDaySupplier : IExpireTimeSupplier, IScopeDependency
                         }
                         break;
                     }
-                    else if(!await IsWorkDay(beginTime) && beginTime> DateTime.Parse(beginTime.ToShortDateString() + " " + timeConfig.WorkTime[1] + ":00"))
+                    else if(!await IsWorkDay(beginTime))
                     {
                         beginTime = DateTime.Parse(beginTime.AddDays(-1).ToShortDateString() +" "+ timeConfig.WorkTime[1] + ":00");
                     }
-                    else
-                    {
-                        beginTime = beginTime.AddDays(1);
-                    }
+                    //else
+                    //{
+                    //    beginTime = beginTime.AddDays(1);
+                    //}
                 }
 
                 var scondsPlan = (beginTime - startTime).TotalSeconds * (timeConfig.Percentage / 100.00);

+ 1 - 1
src/Hotline/Settings/TimeLimitDomain/IExpireTimeHandler.cs

@@ -52,7 +52,7 @@ public interface IExpireTimeHandler
     /// <param name="Percentage">即将超期百分比</param>
     /// <param name="PercentageOne">超期百分比第一级</param>
     /// <returns></returns>
-    public Task<TimeResult> CalcEndTimeAsync(DateTime beginTime,DateTime startTime, ETimeType timeType, IList<string>? workTime, int timeValue, int Percentage, int PercentageOne);
+    public Task<TimeResult> CalcEndTimeAsync(DateTime beginTime,DateTime startTime,IList<string>? workTime, TimeLimitSetting setting);
 
     Task<TimeResult> CalcEndTimeAsync(DateTime beginTime,DateTime startTime, TimeConfig timeConfig);