浏览代码

合并代码

qinchaoyue 5 月之前
父节点
当前提交
dfe57865ef
共有 25 个文件被更改,包括 584 次插入229 次删除
  1. 66 26
      src/Hotline.Api/Controllers/OrderController.cs
  2. 44 70
      src/Hotline.Api/Controllers/WorkflowController.cs
  3. 10 2
      src/Hotline.Application.Tests/Application/DefaultCallApplicationTest.cs
  4. 9 3
      src/Hotline.Application.Tests/Application/KnowApplicationTest.cs
  5. 0 78
      src/Hotline.Application.Tests/Controller/DefaultHttpContextAccessor.cs
  6. 130 0
      src/Hotline.Application.Tests/Controller/DefaultSessionContext.cs
  7. 1 1
      src/Hotline.Application.Tests/Controller/KnowledgeControllerTest.cs
  8. 27 3
      src/Hotline.Application.Tests/Controller/OrderControllerTest.cs
  9. 3 1
      src/Hotline.Application.Tests/Domain/OrderVisitDomainServiceTest.cs
  10. 30 0
      src/Hotline.Application.Tests/Dto/CapDto.cs
  11. 17 0
      src/Hotline.Application.Tests/Dto/PublishedEntity.cs
  12. 10 0
      src/Hotline.Application.Tests/Repository/FWMQRepositoryTest.cs
  13. 11 0
      src/Hotline.Application.Tests/SqlSuger/CapDbContext.cs
  14. 33 0
      src/Hotline.Application.Tests/SqlSuger/CapDbExtensions.cs
  15. 5 1
      src/Hotline.Application.Tests/Startup.cs
  16. 37 6
      src/Hotline.Application.Tests/TestBase.cs
  17. 2 1
      src/Hotline.Application.Tests/appsettings.Development.json
  18. 20 20
      src/Hotline.Application/CallCenter/DefaultCallApplication.cs
  19. 12 0
      src/Hotline.Application/FlowEngine/IWorkflowApplication.cs
  20. 81 8
      src/Hotline.Application/FlowEngine/WorkflowApplication.cs
  21. 1 1
      src/Hotline.Application/Orders/OrderApplication.cs
  22. 14 0
      src/Hotline.Repository.SqlSugar/Extensions/SqlSugarStartupExtensions.cs
  23. 19 4
      src/Hotline/Orders/OrderDomainService.cs
  24. 1 2
      src/Hotline/Orders/OrderVisitDomainService.cs
  25. 1 2
      src/Hotline/Push/FWMessage/PushDomainService.cs

+ 66 - 26
src/Hotline.Api/Controllers/OrderController.cs

@@ -740,8 +740,9 @@ public class OrderController : BaseController
         var users = await _userRepository.Queryable()
             .Includes(d => d.Organization)
             .Includes(d => d.Roles)
-            .Where(d => d.Roles.Any(x => roles.Contains(x.Name)))
-            .ToListAsync(HttpContext.RequestAborted);
+            .WhereIF(!_appOptions.Value.IsZiGong,d => d.Roles.Any(x => roles.Contains(x.Name)))
+            .WhereIF(_appOptions.Value.IsZiGong, d => d.OrgId == OrgSeedData.CenterId)
+			.ToListAsync(HttpContext.RequestAborted);
         return users.Select(d => new OrderMigrationHandler
         {
             UserId = d.Id,
@@ -1285,7 +1286,8 @@ public class OrderController : BaseController
         var users = await _userRepository.Queryable()
             .Includes(d => d.Organization)
             .Includes(d => d.Roles)
-            .Where(d => d.Roles.Any(x => roles.Contains(x.Name)))
+            .WhereIF(!_appOptions.Value.IsZiGong,d => d.Roles.Any(x => roles.Contains(x.Name)))
+            .WhereIF(_appOptions.Value.IsZiGong,d=> d.OrgId == OrgSeedData.CenterId)
             .ToListAsync(HttpContext.RequestAborted);
         return users.Select(d => new OrderMigrationHandler
         {
@@ -3757,6 +3759,11 @@ public class OrderController : BaseController
                     {
                         await AverageSendOrderAsync(nextDto, HttpContext.RequestAborted);
                     }
+                    var flowStepHandler = nextDto.NextHandlers.FirstOrDefault();
+                    await _orderRepository.Updateable().SetColumns(o => new Order()
+                    {
+	                    CenterToOrgHandlerId = flowStepHandler.UserId, CenterToOrgHandlerName = flowStepHandler.Username
+                    }).Where(o=>o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
                 }
 
                 await _workflowDomainService.NextAsync(_sessionContext, nextDto, order.ExpiredTime, isAutoFillSummaryOpinion, cancellationToken);
@@ -4248,11 +4255,13 @@ public class OrderController : BaseController
         //    .ToPagedListAsync(dto, HttpContext.RequestAborted);
 
         var (total, items) = await _orderRepository.Queryable()
+	        .Includes(d=>d.Workflow.Steps)
             .Where(d => SqlFunc.Subqueryable<WorkflowTrace>()
                             .Where(step => step.ExternalId == d.Id &&
                                            step.HandlerOrgId == OrgSeedData.CenterId &&
-                                           step.Status < EWorkflowStepStatus.Handled).Any() ||
-                        string.IsNullOrEmpty(d.WorkflowId)
+                                           step.Status < EWorkflowStepStatus.Handled)
+                            .WhereIF(!string.IsNullOrEmpty(dto.CenterToOrgHandlerName),step=>step.BusinessType == EBusinessType.Send && step.HandlerName.Contains(dto.CenterToOrgHandlerName)).Any() ||
+                        (string.IsNullOrEmpty(d.WorkflowId) && string.IsNullOrEmpty(dto.CenterToOrgHandlerName))
             )
             .Where(d => d.Source < ESource.MLSQ || d.Source > ESource.WZSC)
             .Where(d => d.Status != EOrderStatus.BackToProvince && d.Status < EOrderStatus.Filed)
@@ -4268,7 +4277,7 @@ public class OrderController : BaseController
             .WhereIF(dto.ExpiredStatus is EExpiredStatus.Normal, d => DateTime.Now < d.NearlyExpiredTime)
             .WhereIF(dto.ExpiredStatus is EExpiredStatus.GoingToExpired, d => DateTime.Now > d.NearlyExpiredTime && DateTime.Now < d.ExpiredTime)
             .WhereIF(dto.ExpiredStatus is EExpiredStatus.Expired, d => DateTime.Now >= d.ExpiredTime)
-            .WhereIF(!string.IsNullOrEmpty(dto.CenterToOrgHandlerName), d => d.CenterToOrgHandlerName == dto.CenterToOrgHandlerName)
+            //.WhereIF(!string.IsNullOrEmpty(dto.CenterToOrgHandlerName), d => d.CenterToOrgHandlerName.Contains(dto.CenterToOrgHandlerName))
             .WhereIF(dto.IsUrgent.HasValue, d => d.IsUrgent == dto.IsUrgent!.Value)
             .OrderBy(d => d.Status)
             .OrderByIF(string.IsNullOrEmpty(dto.SortField), d => d.CreationTime, OrderByType.Desc)
@@ -4465,7 +4474,12 @@ public class OrderController : BaseController
                 audit.State = ESendBackAuditState.End;
                 audit.AuditUser = "默认通过";
                 audit.AuditTime = DateTime.Now;
-                var flowDirection = await _workflowApplication.PreviousAsync(dto, HttpContext.RequestAborted);
+                if (prevStep.BusinessType == EBusinessType.Send)
+                {
+	                await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { CenterToOrgHandlerId = dto.Handler.UserId, CenterToOrgHandlerName = dto.Handler.Username })
+		                .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+                }
+				var flowDirection = await _workflowApplication.PreviousAsync(dto, HttpContext.RequestAborted);
                 var processType = flowDirection == EFlowDirection.OrgToCenter || flowDirection == EFlowDirection.CenterToCenter
                     ? EProcessType.Zhiban
                     : EProcessType.Jiaoban;
@@ -4481,7 +4495,12 @@ public class OrderController : BaseController
             audit.State = ESendBackAuditState.End;
             audit.AuditUser = "默认通过";
             audit.AuditTime = DateTime.Now;
-            var flowDirection = await _workflowApplication.PreviousAsync(dto, HttpContext.RequestAborted);
+            if (prevStep.BusinessType == EBusinessType.Send)
+            {
+	            await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { CenterToOrgHandlerId = dto.Handler.UserId, CenterToOrgHandlerName = dto.Handler.Username })
+		            .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+            }
+			var flowDirection = await _workflowApplication.PreviousAsync(dto, HttpContext.RequestAborted);
             var processType = flowDirection == EFlowDirection.OrgToCenter || flowDirection == EFlowDirection.CenterToCenter
                 ? EProcessType.Zhiban
                 : EProcessType.Jiaoban;
@@ -4489,8 +4508,9 @@ public class OrderController : BaseController
                 order.SendBackNum = order.SendBackNum.HasValue ? order.SendBackNum.Value + 1 : 1;
             await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ProcessType = processType, SendBackNum = order.SendBackNum })
                 .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
-            //发送短信TODO
-        }
+
+			//发送短信TODO
+		}
 
         await _orderSendBackAuditRepository.AddAsync(audit, HttpContext.RequestAborted);
     }
@@ -4536,7 +4556,9 @@ public class OrderController : BaseController
                         var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
                         sendBack.SendBackData.Handler = handler;
                     }
-                }
+                    await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { CenterToOrgHandlerId = sendBack.SendBackData.Handler.UserId, CenterToOrgHandlerName = sendBack.SendBackData.Handler.Username })
+	                    .Where(o => o.Id == sendBack.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+				}
             }
 
             //string applicantId, string applicantOrgId, string[] applicantRoleIds,
@@ -4554,8 +4576,8 @@ public class OrderController : BaseController
                 order.SendBackNum = order.SendBackNum.HasValue ? order.SendBackNum.Value + 1 : 1;
             await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ProcessType = processType, SendBackNum = order.SendBackNum })
                 .Where(o => o.Id == sendBack.OrderId).ExecuteCommandAsync(HttpContext.RequestAborted);
-            //发送短信TODO
-        }
+			//发送短信TODO
+		}
         else
         {
             await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { Status = sendBack.Status.Value })
@@ -4606,7 +4628,9 @@ public class OrderController : BaseController
                             var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
                             sendBack.SendBackData.Handler = handler;
                         }
-                    }
+                        await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { CenterToOrgHandlerId = sendBack.SendBackData.Handler.UserId, CenterToOrgHandlerName = sendBack.SendBackData.Handler.Username })
+	                        .Where(o => o.Id == sendBack.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+					}
                 }
 
                 sendBack.SendBackData.ExpiredTime = order.ExpiredTime;
@@ -5071,8 +5095,11 @@ public class OrderController : BaseController
             var orderDto = _mapper.Map<OrderDto>(order);
             await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto,
                 cancellationToken: HttpContext.RequestAborted);
-            //}
-            await _workflowApplication.RecallAsync(recall, expiredTime.ExpiredTime, order.Status >= EOrderStatus.Filed, EWorkflowTraceType.Recall,
+            var flowStepHandler = recall.NextHandlers.FirstOrDefault();
+			await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { CenterToOrgHandlerId = flowStepHandler.UserId, CenterToOrgHandlerName = flowStepHandler.Username })
+	            .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+			//}
+			await _workflowApplication.RecallAsync(recall, expiredTime.ExpiredTime, order.Status >= EOrderStatus.Filed, EWorkflowTraceType.Recall,
                 HttpContext.RequestAborted);
 
             if (order.Status >= EOrderStatus.Filed)
@@ -5258,7 +5285,10 @@ public class OrderController : BaseController
             var processType = dto.FlowDirection is EFlowDirection.OrgToCenter or EFlowDirection.CenterToCenter or EFlowDirection.FiledToCenter
                 ? EProcessType.Zhiban
                 : EProcessType.Jiaoban;
-            await _workflowApplication.RecallAsync(recall, endTime, order.Status >= EOrderStatus.Filed, EWorkflowTraceType.Redo,
+			var flowStepHandler = recall.NextHandlers.FirstOrDefault();
+			await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { CenterToOrgHandlerId = flowStepHandler.UserId, CenterToOrgHandlerName = flowStepHandler.Username })
+				.Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+			await _workflowApplication.RecallAsync(recall, endTime, order.Status >= EOrderStatus.Filed, EWorkflowTraceType.Redo,
                 HttpContext.RequestAborted);
             //var publish = await _orderPublishRepository.GetAsync(x => x.OrderId == dto.OrderId);
             //if (publish != null)
@@ -5400,11 +5430,13 @@ public class OrderController : BaseController
             var orderDto = _mapper.Map<OrderDto>(order);
             await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto,
                 cancellationToken: HttpContext.RequestAborted);
-            //}
+			//}
+			var flowStepHandler = recall.NextHandlers.FirstOrDefault();
+			await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { CenterToOrgHandlerId = flowStepHandler.UserId, CenterToOrgHandlerName = flowStepHandler.Username })
+				.Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+			//todo 特提重办,按审批通过时间依据中心派至部门的规则计算期满时间,更新order
 
-            //todo 特提重办,按审批通过时间依据中心派至部门的规则计算期满时间,更新order
-
-            await _workflowApplication.RecallAsync(recall, expiredTime.ExpiredTime, order.Status >= EOrderStatus.Filed, EWorkflowTraceType.Recall,
+			await _workflowApplication.RecallAsync(recall, expiredTime.ExpiredTime, order.Status >= EOrderStatus.Filed, EWorkflowTraceType.Recall,
                 HttpContext.RequestAborted);
             if (order.Status >= EOrderStatus.Filed)
             {
@@ -5548,11 +5580,13 @@ public class OrderController : BaseController
                 var orderDto = _mapper.Map<OrderDto>(order);
                 await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto,
                     cancellationToken: HttpContext.RequestAborted);
-                //}
-
-                //todo 特提重办,按审批通过时间依据中心派至部门的规则计算期满时间,更新order
+				//}
+				var flowStepHandler = recall.NextHandlers.FirstOrDefault();
+				await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { CenterToOrgHandlerId = flowStepHandler.UserId, CenterToOrgHandlerName = flowStepHandler.Username })
+					.Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+				//todo 特提重办,按审批通过时间依据中心派至部门的规则计算期满时间,更新order
 
-                await _workflowApplication.RecallAsync(recall, expiredTime.ExpiredTime, order.Status >= EOrderStatus.Filed, EWorkflowTraceType.Recall,
+				await _workflowApplication.RecallAsync(recall, expiredTime.ExpiredTime, order.Status >= EOrderStatus.Filed, EWorkflowTraceType.Recall,
                     HttpContext.RequestAborted);
                 if (order.Status >= EOrderStatus.Filed)
                 {
@@ -7518,7 +7552,13 @@ public class OrderController : BaseController
                     .SetColumns(o => new Orders.Order() { Status = status, SignerId = dto.Handler.UserId })
                     .Where(o => o.Id == dto.OrderId).ExecuteCommandAsync(HttpContext.RequestAborted);
             }
-            else
+            //else if
+            //{
+            //    await _orderRepository.Updateable()
+            //        .SetColumns(o => new Orders.Order() { Status = status, CenterToOrgHandlerId = dto.Handler.UserId, CenterToOrgHandlerName = dto.Handler.Username })
+            //        .Where(o => o.Id == dto.OrderId).ExecuteCommandAsync(HttpContext.RequestAborted);
+            //}
+            else 
             {
                 await _orderRepository.Updateable()
                     .SetColumns(o => new Orders.Order() { Status = status })

+ 44 - 70
src/Hotline.Api/Controllers/WorkflowController.cs

@@ -26,6 +26,7 @@ using Hotline.Share.Dtos.Order;
 using Hotline.Share.Dtos.FlowEngine.Definition;
 using Hotline.Settings.TimeLimits;
 using Hotline.SeedData;
+using Hotline.Tools;
 
 namespace Hotline.Api.Controllers;
 
@@ -556,71 +557,7 @@ public class WorkflowController : BaseController
     {
         RefAsync<int> total = 0;
 
-        var Role = _sessionContext.Roles;
-
-        var query = _workflowCountersignRepository.Queryable()
-            .Includes(x => x.Members)
-            .LeftJoin<Workflow>((c, w) => c.WorkflowId == w.Id)
-            .InnerJoin<Order>((c, w, o) => w.ExternalId == o.Id)
-            .WhereIF(!string.IsNullOrEmpty(dto.Title), (c, w, o) => o.Title.Contains(dto.Title))
-            .WhereIF(!string.IsNullOrEmpty(dto.OrderNo), (c, w, o) => o.No == dto.OrderNo)
-            .WhereIF(!string.IsNullOrEmpty(dto.AcceptType), (c, w, o) => o.AcceptTypeCode == dto.AcceptType)//受理类型
-            .WhereIF(!string.IsNullOrEmpty(dto.Channel), (c, w, o) => o.SourceChannelCode == dto.Channel)//受理类型
-            .WhereIF(!string.IsNullOrEmpty(dto.Hotspot), (c, w, o) => o.HotspotSpliceName != null && o.HotspotSpliceName.Contains(dto.Hotspot))//热点类型
-            .WhereIF(!string.IsNullOrEmpty(dto.OrgId), (c, w, o) => c.FinisherOrgId == dto.OrgId) //接办部门
-            .WhereIF(dto.CounterSignType != null, (c, w, o) => c.CounterSignType == dto.CounterSignType) //会签类型
-        ;
-
-        var rolePaiDan = _systemSettingCacheManager.GetSetting(SettingConstants.RolePaiDan)?.SettingValue[0];//派单员角色
-        var seatsMonitor = _systemSettingCacheManager.GetSetting(SettingConstants.SeatsMonitor)?.SettingValue[0];//班长角色
-        var isAdmin = false;
-        var systemAdministrator = _systemSettingCacheManager.GetSetting(SettingConstants.SystemAdministrator)?.SettingValue[0];//管理员角色
-        if (!string.IsNullOrEmpty(systemAdministrator) && (_sessionContext.Roles.Contains(systemAdministrator) || _sessionContext.Roles.Contains(RoleSeedData.AdminRole)))
-            isAdmin = true;
-
-        //发起会签:班长角色能看所有会签信件;新增管理员能看到所有会签
-        //派单员角色只能看到自己发起的会签信件;
-        //承办部门用户能看到自己发起的和同级部门用户发起的会签件
-        if (dto.InitiatedCountersignature.HasValue && dto.InitiatedCountersignature == true)
-        {
-            if (_sessionContext.Roles.Any(p => p == seatsMonitor) || isAdmin)
-            {
-
-            }
-            else
-            if (_sessionContext.Roles.Any(p => p == rolePaiDan))
-            {
-                query = query.Where((c, w, o) => c.StarterId == _sessionContext.UserId);
-            }
-            else
-            {
-                query = query.Where((c, w, o) => c.StarterOrgId == _sessionContext.RequiredOrgId);
-            }
-        }
-
-        //会签已办(会签状态为已结束):
-        //班长角色能看所有已结束的会签信件;新增管理员能看到所有会签
-        //派单员不会办理会签件只会发起会签件所以这一点派单员角色可以忽略;
-        //承办部门用户能看到和同级部门用户已办理过的会签件
-        if (dto.HandleCountersignature.HasValue && dto.HandleCountersignature == true)
-        {
-            if (_sessionContext.Roles.Any(p => p == seatsMonitor) || isAdmin)
-            {
-                query = query.Where((c, w, o) => c.EndTime.HasValue);
-            }
-            else
-           if (_sessionContext.Roles.Any(p => p == rolePaiDan))
-            {
-                query = query.Where((c, w, o) => c.Members.Any(m => m.Key == _sessionContext.RequiredOrgId) && c.EndTime.HasValue);
-            }
-            else
-            {
-                query = query.Where((c, w, o) => c.Members.Any(m => m.Key == _sessionContext.OrgId || m.Key == _sessionContext.RequiredOrgId) && c.EndTime.HasValue);
-            }
-        }
-
-        var items = await query
-            .OrderByDescending((c, w, o) => o.ExpiredTime)
+        var items = await _workflowApplication.QueryOrderCountersigns(dto,_sessionContext)
             .Select((c, w, o) => new { c, o })
             .ToPageListAsync(dto.PageIndex, dto.PageSize, total, HttpContext.RequestAborted);
 
@@ -635,11 +572,48 @@ public class WorkflowController : BaseController
         return new PagedDto<WorkflowCountersignDto>(total, dtos);
     }
 
-    /// <summary>
-    /// 
-    /// </summary>
-    /// <returns></returns>
-    [HttpGet("order-countersign-base-data")]
+
+
+	/// <summary>
+	/// 会签信息导出
+	/// </summary>
+	/// <returns></returns>
+	[HttpPost("order-countersign/_export")]
+    public async Task<FileStreamResult> ScreenListExport([FromBody] ExportExcelDto<QueryOrderCountersignDto> dto)
+    {
+	    var query = _workflowApplication.QueryOrderCountersigns(dto.QueryDto, _sessionContext)
+		    .Select((c, w, o) => c);
+	    List<WorkflowCountersign> data;
+	    if (dto.IsExportAll)
+	    {
+		    data = await query.ToListAsync(HttpContext.RequestAborted);
+	    }
+	    else
+	    {
+		    var (_, items) = await query.ToPagedListAsync(dto.QueryDto, HttpContext.RequestAborted);
+		    data = items;
+	    }
+
+	    var dataDtos = _mapper.Map<ICollection<WorkflowCountersignDto>>(data);
+
+	    dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
+
+	    var dtos = dataDtos
+		    .Select(stu => _mapper.Map(stu, typeof(WorkflowCountersignDto), dynamicClass))
+		    .Cast<object>()
+		    .ToList();
+
+	    var stream = ExcelHelper.CreateStream(dtos);
+
+	    return ExcelStreamResult(stream, "会签信息数据");
+    }
+
+
+	/// <summary>
+	/// 
+	/// </summary>
+	/// <returns></returns>
+	[HttpGet("order-countersign-base-data")]
     public async Task<object> QueryOrderCountersignsBaseData()
     {
 

+ 10 - 2
src/Hotline.Application.Tests/Application/DefaultCallApplicationTest.cs

@@ -9,13 +9,14 @@ using Hotline.Share.Dtos.CallCenter;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Users;
+using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.DependencyInjection;
 using Shouldly;
 using SqlSugar.Extensions;
 using XF.Domain.Repository;
 
 namespace Hotline.Application.Tests.Application;
-public class DefaultCallApplicationTest
+public class DefaultCallApplicationTest : TestBase
 {
     private readonly XingTangCallApplication _defaultCallApplication;
     private readonly IOrderVisitRepository _orderVisitRepository;
@@ -23,7 +24,7 @@ public class DefaultCallApplicationTest
     public readonly IFixture _fixture;
     private readonly IOrderRepository _orderRepository;
 
-    public DefaultCallApplicationTest(XingTangCallApplication defaultCallApplication, IOrderVisitRepository orderVisitRepository, IRepository<CallNative> callNativeRepository, IOrderRepository orderRepository)
+    public DefaultCallApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, XingTangCallApplication defaultCallApplication, IOrderVisitRepository orderVisitRepository, IRepository<CallNative> callNativeRepository, IOrderRepository orderRepository) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor)
     {
         _fixture = new Fixture();
         _defaultCallApplication = defaultCallApplication;
@@ -132,4 +133,11 @@ public class DefaultCallApplicationTest
         (await _orderRepository.Queryable().Where(m => m.Id == orderId).Select(m => m.CallId).FirstAsync())
             .ShouldBe(inDto.Id);
     }
+
+    //[Fact]
+    public async Task OrderRelateCallHandler2_Test()
+    {
+        var orderId = "08dd0d21-0221-43cf-8230-3179dc6aefca";
+        await _defaultCallApplication.OrderRelateCallHandlerAsync(orderId, CancellationToken.None);
+    }
 }

+ 9 - 3
src/Hotline.Application.Tests/Application/KnowApplicationTest.cs

@@ -1,10 +1,16 @@
-using Hotline.Application.Knowledge;
+using Hotline.Api.Controllers;
+using Hotline.Application.Knowledge;
+using Hotline.Identity.Accounts;
+using Hotline.Identity.Roles;
 using Hotline.KnowledgeBase;
 using Hotline.KnowledgeBase.Notifies;
 using Hotline.Share.Dtos.Knowledge;
 using Hotline.Share.Tools;
+using Hotline.Users;
 using Mapster;
 using MediatR;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
 using Shouldly;
 using System;
 using System.Collections.Generic;
@@ -15,7 +21,7 @@ using System.Threading.Tasks;
 using XF.Domain.Repository;
 
 namespace Hotline.Application.Tests.Application;
-public class KnowApplicationTest
+public class KnowApplicationTest : TestBase
 {
     private readonly IKnowApplication _knowApplication;
     private readonly IRepository<KnowledgeRelationType> _knowledgeRelationTypeRepository;
@@ -25,7 +31,7 @@ public class KnowApplicationTest
     private readonly IRepository<KnowledgeWord> _knowledgeWordRepository;
     private readonly IRepository<KnowledgeHotWord> _knowledgeHotWordRepository;
 
-    public KnowApplicationTest(IKnowApplication knowApplication, IRepository<KnowledgeRelationType> knowledgeRelationTypeRepository, IMediator mediator, IRepository<KnowledgeBase.Knowledge> knowledgeRepository, IKnowledgeDomainService knowledgeDomainService, IRepository<KnowledgeWord> knowledgeWordRepository, IRepository<KnowledgeHotWord> knowledgeHotWordRepository)
+    public KnowApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, IKnowApplication knowApplication, IRepository<KnowledgeRelationType> knowledgeRelationTypeRepository, IMediator mediator, IRepository<KnowledgeBase.Knowledge> knowledgeRepository, IKnowledgeDomainService knowledgeDomainService, IRepository<KnowledgeWord> knowledgeWordRepository, IRepository<KnowledgeHotWord> knowledgeHotWordRepository) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor)
     {
         _knowApplication = knowApplication;
         _knowledgeRelationTypeRepository = knowledgeRelationTypeRepository;

+ 0 - 78
src/Hotline.Application.Tests/Controller/DefaultHttpContextAccessor.cs

@@ -1,78 +0,0 @@
-using Hotline.Application.Tests.Infrastructure;
-using Hotline.Identity.Accounts;
-using Microsoft.AspNetCore.Http;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Security.Authentication;
-using System.Security.Claims;
-using System.Text;
-using System.Threading.Tasks;
-using XF.Domain.Authentications;
-using XF.Domain.Dependency;
-using XF.Domain.Repository;
-
-namespace Hotline.Application.Tests.Controller;
-public class DefaultHttpContextAccessor : ISessionContext, IScopeDependency
-{
-    private HttpContext _content = new DefaultHttpContext();
-
-    private HttpContext GetContext()
-    {
-        var context = new DefaultHttpContext();
-        //var openId = new Claim(AppClaimTypes.OpenId, "测试生成的OpenId");
-        var id = new ClaimsIdentity("身份");
-        //id.AddClaim(openId);
-        context.User = new ClaimsPrincipal(id);
-        //OpenId = context.User.FindFirstValue(AppClaimTypes.OpenId);
-        return context;
-
-    }
-    public HttpContext? HttpContext { get => GetContext(); set => _content = value; }
-
-
-    public string? OpenId { get; init; }
-
-    /// <summary>
-    /// Id of current tenant or null for host
-    /// </summary>
-    public string? UserId
-    {
-        get
-        {
-            return TestSessionConstants.UserId;
-        }
-        init { }
-    }
-
-    /// <summary>
-    /// Id of current user or throw Exception for guest
-    /// </summary>
-    /// <exception cref="AuthenticationException"></exception>
-    public string RequiredUserId { get { return TestSessionConstants.UserId; } }
-    public string? UserName { get { return TestSessionConstants.UserName; } init { } }
-    public string? Phone { get; init; }
-
-    /// <summary>
-    /// Roles
-    /// </summary>
-    public string[] Roles { get { return TestSessionConstants.Roles; } init { } }
-    public string? OrgId { get { return TestSessionConstants.OrgId; } init { } }
-    public string RequiredOrgId { get { return TestSessionConstants.OrgId; } }
-    public string? OrgName { get; init; }
-    public int OrgLevel { get; init; }
-    public string? OrgAreaCode { get; init; }
-    public bool OrgIsCenter { get; init; }
-
-    /// <summary>
-    /// 部门行政区划名称
-    /// </summary>
-    public string? OrgAreaName { get; init; }
-    public string? AreaId { get; init; }
-    public string? ClientId { get; init; }
-
-    /// <summary>
-    /// 工号
-    /// </summary>
-    public string? StaffNo { get; init; }
-}

+ 130 - 0
src/Hotline.Application.Tests/Controller/DefaultSessionContext.cs

@@ -0,0 +1,130 @@
+using Hotline.Application.Tests.Infrastructure;
+using Hotline.Identity.Accounts;
+using IdentityModel;
+using Microsoft.AspNetCore.Http;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Authentication;
+using System.Security.Claims;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Authentications;
+using XF.Domain.Dependency;
+using XF.Domain.Repository;
+
+namespace Hotline.Application.Tests.Controller;
+public class DefaultSessionContext : ISessionContext, IScopeDependency
+{
+    private readonly IHttpContextAccessor _contextAccessor;
+    public DefaultSessionContext(IHttpContextAccessor httpContextAccessor)
+    {
+        _contextAccessor = httpContextAccessor;
+        //Roles = user.Claims.Where(d => d.Type == JwtClaimTypes.Role).Select(d => d.Value).ToArray();
+    }
+    private HttpContext _content = new DefaultHttpContext();
+
+    private HttpContext GetContext()
+    {
+        return _contextAccessor.HttpContext;
+    }
+    public HttpContext? HttpContext { get => GetContext(); set => _content = value; }
+
+
+    public string? OpenId { get; set; }
+
+    /// <summary>
+    /// Id of current tenant or null for host
+    /// </summary>
+    public string? UserId
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier); }
+        init { }
+    }
+
+    /// <summary>
+    /// Id of current user or throw Exception for guest
+    /// </summary>
+    /// <exception cref="AuthenticationException"></exception>
+    public string RequiredUserId => _contextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
+    public string? UserName
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.UserDisplayName); }
+        init { }
+    }
+
+    public string? Phone
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(JwtClaimTypes.PhoneNumber); }
+        init { }
+    }
+
+    /// <summary>
+    /// Roles
+    /// </summary>
+    public string[] Roles
+    {
+        get { return _contextAccessor.HttpContext.User.Claims.Where(d => d.Type == ClaimTypes.Role).Select(d => d.Value).ToArray(); }
+        init { }
+    }
+
+    public string? OrgId
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.DepartmentId); }
+        init { }
+    }
+
+    public string RequiredOrgId => _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.DepartmentId);
+    public string? OrgName
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.DepartmentName); }
+        init { }
+    }
+
+    public int OrgLevel
+    {
+        get { return _contextAccessor.HttpContext.User.FindIntValue(AppClaimTypes.DepartmentLevel); }
+        init { }
+    }
+
+    public string? OrgAreaCode
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.DepartmentAreaCode); }
+        init { }
+    }
+
+    public bool OrgIsCenter
+    {
+        get { return _contextAccessor.HttpContext.User.FindBoolValue(AppClaimTypes.DepartmentIsCenter); }
+        init { }
+    }
+
+    /// <summary>
+    /// 部门行政区划名称
+    /// </summary>
+    public string? OrgAreaName
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.DepartmentAreaName); }
+        init { }
+    }
+
+    public string? AreaId
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.AreaId); }
+        init { }
+    }
+    public string? ClientId
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(JwtClaimTypes.ClientId); }
+        init { }
+    }
+
+    /// <summary>
+    /// 工号
+    /// </summary>
+    public string? StaffNo
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.StaffNo); }
+        init { }
+    }
+}

+ 1 - 1
src/Hotline.Application.Tests/Controller/KnowledgeControllerTest.cs

@@ -27,7 +27,7 @@ public class KnowledgeControllerTest : TestBase
     private readonly KnowledgeController _knowledgeController;
     private readonly IRepository<KnowledgeBase.Knowledge> _knowledgeRepository;
 
-    public KnowledgeControllerTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, KnowledgeServiceMock knowledgeServiceMock, KnowledgeController knowledgeController, IRepository<KnowledgeBase.Knowledge> knowledgeRepository) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository)
+    public KnowledgeControllerTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, KnowledgeServiceMock knowledgeServiceMock, KnowledgeController knowledgeController, IRepository<KnowledgeBase.Knowledge> knowledgeRepository, IHttpContextAccessor httpContextAccessor) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor)
     {
         _knowledgeServiceMock = knowledgeServiceMock;
         _knowledgeController = knowledgeController;

+ 27 - 3
src/Hotline.Application.Tests/Controller/OrderControllerTest.cs

@@ -2,8 +2,10 @@
 using Castle.DynamicProxy;
 using Hotline.Api.Controllers;
 using Hotline.Application.CallCenter;
+using Hotline.Application.Tests.Dto;
 using Hotline.Application.Tests.Infrastructure;
 using Hotline.Application.Tests.Mock;
+using Hotline.Application.Tests.SqlSuger;
 using Hotline.Caching.Interfaces;
 using Hotline.Caching.Services;
 using Hotline.CallCenter.Calls;
@@ -12,12 +14,14 @@ using Hotline.FlowEngine.WorkflowModules;
 using Hotline.Identity.Accounts;
 using Hotline.Identity.Roles;
 using Hotline.Orders;
+using Hotline.Repository.SqlSugar;
 using Hotline.Settings;
 using Hotline.Settings.Hotspots;
 using Hotline.Share.Dtos.File;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Dtos.Order.Publish;
+using Hotline.Share.Dtos.TrCallCenter;
 using Hotline.Share.Dtos.Users;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Enums.FlowEngine;
@@ -32,6 +36,7 @@ using Microsoft.AspNetCore.Mvc.Testing;
 using Microsoft.Extensions.DependencyInjection;
 using Moq;
 using Shouldly;
+using SqlSugar;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -57,8 +62,9 @@ public class OrderControllerTest : TestBase
     private readonly IRepository<CallNative> _callNativeRepository; 
     private readonly IRepository<CallidRelation> _callIdRelationRepository;
     private readonly XingTangCallApplication _defaultCallApplication;
+    private readonly ISqlSugarClient _capSqlClient;
 
-    public OrderControllerTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IRepository<Hotspot> hotspotRepository, OrderController orderController, IOrderRepository orderRepository, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, OrderServiceMock orderServiceMock, IRepository<OrderPublish> orderPublishRepository, INotificationHandler<EndWorkflowNotify> orderPublishEndWorkflowHandler, IOrderVisitRepository orderVisitRepository, IRepository<SystemSetting> systemSettingRepository, ISystemSettingCacheManager systemSettingCacheManager, IRepository<CallNative> callNativeRepository, IRepository<CallidRelation> callIdRelationRepository, XingTangCallApplication defaultCallApplication) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository)
+    public OrderControllerTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IRepository<Hotspot> hotspotRepository, OrderController orderController, IOrderRepository orderRepository, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, OrderServiceMock orderServiceMock, IRepository<OrderPublish> orderPublishRepository, INotificationHandler<EndWorkflowNotify> orderPublishEndWorkflowHandler, IOrderVisitRepository orderVisitRepository, IRepository<SystemSetting> systemSettingRepository, ISystemSettingCacheManager systemSettingCacheManager, IRepository<CallNative> callNativeRepository, IRepository<CallidRelation> callIdRelationRepository, XingTangCallApplication defaultCallApplication, ISugarUnitOfWork<CapDbContext> capDbContext, IHttpContextAccessor httpContextAccessor) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor)
     {
         _hotspotRepository = hotspotRepository;
         _orderController = orderController;
@@ -76,11 +82,14 @@ public class OrderControllerTest : TestBase
         _callNativeRepository = callNativeRepository;
         _callIdRelationRepository = callIdRelationRepository;
         _defaultCallApplication = defaultCallApplication;
+        _capSqlClient = capDbContext.Db;
     }
 
     /// <summary>
-    /// 创建工单时传入一个错误callId,
+    /// 创建工单时传入一个错误callId
+    /// 验证: 
     /// 测试在延迟消息后会不会把工单测CallId修复成正确的CallId
+    /// 推送的省上数据是否正确
     /// </summary>
     /// <returns></returns>
     [Fact]
@@ -117,6 +126,16 @@ public class OrderControllerTest : TestBase
         var orderEntity = await _orderRepository.GetAsync(order.Id);
         orderEntity.ShouldNotBeNull();
         orderEntity.CallId.ShouldBe(inDto.Id);
+        var query = _capSqlClient.Queryable<PublishedEntity>()
+            .Where(m => m.Name == "hotline.call.connect.order" && m.Content.Contains(order.Id));
+        var sql = query.ToSqlString();
+        sql.Contains("id").ShouldBeFalse("字段大小写错误" + sql);
+        var published = await query.FirstAsync();
+        published.ShouldNotBeNull("推送数据不存在");
+        var message = published.Content.FromJson<Message<PublishCallRecrodDto>>();
+        message.Value.Order.ShouldNotBeNull("推送的工单信息为空");
+        message.Value.Order.ProvinceNo.ShouldNotBeNull("省工单编号不能为空");
+        message.Value.Order.No.ShouldBe(order.No, "推送的工单编号错误");
     }
 
     /// <summary>
@@ -134,7 +153,7 @@ public class OrderControllerTest : TestBase
     }
 
     /// <summary>
-    /// 验证中心直办工单归档后自动发布
+    /// 验证中心直办工单归档后 自动发布
     /// 是否推诿, 是否不积极
     /// </summary>
     /// <returns></returns>
@@ -156,6 +175,11 @@ public class OrderControllerTest : TestBase
         orderEntity.ShouldNotBeNull();
         orderEntity.IsEvasive.ShouldBeTrue();
         orderEntity.IsInactively.ShouldBeTrue();
+        orderEntity.Status.ShouldBe(EOrderStatus.Visited);
+        orderEntity.OrgProcessingResults.ShouldNotBeNull();
+        orderEntity.OrgProcessingResults.Key.ShouldBe("4");
+        orderEntity.OrgProcessingResults.Value.ShouldBe("满意");
+        orderEntity.SeatEvaluate.ShouldBe(ESeatEvaluate.Satisfied);
     }
 
     [Fact]

+ 3 - 1
src/Hotline.Application.Tests/Domain/OrderVisitDomainServiceTest.cs

@@ -13,6 +13,7 @@ using Hotline.Share.Enums.Push;
 using Hotline.Share.Tools;
 using Hotline.Users;
 using Mapster;
+using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.DependencyInjection;
 using Shouldly;
 using XF.Domain.Repository;
@@ -27,7 +28,7 @@ public class OrderVisitDomainServiceTest : TestBase
     private readonly IOrderRepository _orderRepository;
     private readonly OrderServiceMock _orderServiceMock;
 
-    public OrderVisitDomainServiceTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IOrderVisitDomainService orderVisitDomainService, IOrderVisitRepository orderVisitRepository, IRepository<OrderVisitDetail> orderVisitDetailRepository, Publisher publisher, IOrderRepository orderRepository, OrderServiceMock orderServiceMock) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository)
+    public OrderVisitDomainServiceTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IOrderVisitDomainService orderVisitDomainService, IOrderVisitRepository orderVisitRepository, IRepository<OrderVisitDetail> orderVisitDetailRepository, Publisher publisher, IOrderRepository orderRepository, OrderServiceMock orderServiceMock, IHttpContextAccessor httpContextAccessor) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor)
     {
         _orderVisitDomainService = orderVisitDomainService;
         _orderVisitRepository = orderVisitRepository;
@@ -153,6 +154,7 @@ public class OrderVisitDomainServiceTest : TestBase
                 org.OrgProcessingResults.Key.ShouldBe(orgResuktKey);
                 org.OrgProcessingResults.Value.ShouldBe(orgResuktValue);
 
+                // 验证跟新工单上的字段是否成功
                 orderEntity.OrgProcessingResults.ShouldNotBeNull();
                 orderEntity.OrgProcessingResults.Key.ShouldBe(orgResuktKey);
                 orderEntity.OrgProcessingResults.Value.ShouldBe(orgResuktValue);

+ 30 - 0
src/Hotline.Application.Tests/Dto/CapDto.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Tests.Dto;
+public class CapDto
+{
+}
+
+public class Message<T>
+{
+    public Headers Headers { get; set; }
+    public T Value { get; set; }
+}
+
+public class Headers
+{
+    public string CapCallbackName { get; set; }
+    public string CapMsgId { get; set; }
+    public string CapCorrId { get; set; }
+    public string CapCorrSeq { get; set; }
+    public string CapMsgName { get; set; }
+    public string CapMsgType { get; set; }
+    public string CapDelayTime { get; set; }
+    public string CapSentTime { get; set; }
+    public string CapMsgGroup { get; set; }
+    public string CapExecInstanceId { get; set; }
+}   

+ 17 - 0
src/Hotline.Application.Tests/Dto/PublishedEntity.cs

@@ -0,0 +1,17 @@
+using SqlSugar;
+using System;
+
+namespace Hotline.Application.Tests.Dto;
+[SugarTable("published")]
+public class PublishedEntity
+{
+    [SugarColumn(IsPrimaryKey = true)]
+    public long Id { get; set; }
+    public string Version { get; set; }
+    public string Name { get; set; }
+    public string Content { get; set; }
+    public int Retries { get; set; }
+    public DateTime Added { get; set; }
+    public DateTime? ExpiresAt { get; set; }
+    public string StatusName { get; set; }
+}

+ 10 - 0
src/Hotline.Application.Tests/Repository/FWMQRepositoryTest.cs

@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Tests.Repository;
+internal class FWMQRepositoryTest
+{
+}

+ 11 - 0
src/Hotline.Application.Tests/SqlSuger/CapDbContext.cs

@@ -0,0 +1,11 @@
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Tests.SqlSuger;
+public class CapDbContext : SugarUnitOfWork
+{
+}

+ 33 - 0
src/Hotline.Application.Tests/SqlSuger/CapDbExtensions.cs

@@ -0,0 +1,33 @@
+using Hotline.CallCenter.Configs;
+using Hotline.Repository.SqlSugar;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Tests.SqlSuger;
+public static class CAPDbExtensions
+{
+    public static IServiceCollection AddCAPDb(this IServiceCollection services, IConfiguration configuration)
+    {
+        SqlSugarScope sqlSugar = new(new ConnectionConfig() 
+        { 
+            DbType = DbType.PostgreSQL,
+            ConnectionString = configuration.GetConnectionString("CAP"),
+            IsAutoCloseConnection = true,
+            InitKeyType = InitKeyType.Attribute,
+            MoreSettings = new ConnMoreSettings()
+            {
+                PgSqlIsAutoToLower = false
+            }
+        });
+
+        ISugarUnitOfWork<CapDbContext> context = new SugarUnitOfWork<CapDbContext>(sqlSugar);
+        services.AddSingleton(context);
+        return services;
+    }
+}

+ 5 - 1
src/Hotline.Application.Tests/Startup.cs

@@ -57,6 +57,8 @@ using Hotline.Authentications;
 using Hotline.FlowEngine.Notifications;
 using Hotline.Application.Handlers.FlowEngine;
 using Hotline.Application.Jobs;
+using Hotline.Application.Tests.SqlSuger;
+using Microsoft.AspNetCore.Http;
 
 namespace Hotline.Application.Tests;
 public class Startup
@@ -110,6 +112,7 @@ public class Startup
 
             //sqlsugar
             services.AddSqlSugar(configuration);
+            services.AddCAPDb(configuration);
 
             // application services
             services.AddScoped<ISnapshotApplication, SnapshotApplicationBase>();
@@ -168,7 +171,8 @@ public class Startup
             services.AddScoped<UserController>();
             services.AddScoped<KnowledgeController>();
             services.AddScoped<PushMessageController>();
-            services.AddScoped<ISessionContext, DefaultHttpContextAccessor>();
+            services.AddHttpContextAccessor();
+            services.AddScoped<ISessionContext, Controller.DefaultSessionContext>();
             services.AddScoped<ISessionContextProvider, SessionContextProvider>();
             services.AddScoped<ICallApplication, XingTangCallApplication>();
             services.AddScoped<XingTangCallApplication>();

+ 37 - 6
src/Hotline.Application.Tests/TestBase.cs

@@ -3,13 +3,17 @@ using Hotline.Api.Controllers;
 using Hotline.Application.Tests.Infrastructure;
 using Hotline.Identity.Accounts;
 using Hotline.Identity.Roles;
+using Hotline.Settings;
 using Hotline.Share.Dtos.Users;
 using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.User;
 using Hotline.Users;
+using IdentityModel;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.DependencyInjection;
+using System.Security.Claims;
+using XF.Domain.Authentications;
 using XF.Domain.Repository;
 
 namespace Hotline.Application.Tests;
@@ -21,9 +25,11 @@ public class TestBase
     public readonly UserController _userController;
     public readonly IFixture _fixture;
     private readonly IServiceScopeFactory _scopeFactory;
+    public readonly IHttpContextAccessor _httpContextAccessor;
 
-    public TestBase(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository)
+    public TestBase(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor)
     {
+
         _fixture = new Fixture();
         _accountRepository = accountRepository;
         _roleRepository = roleRepository;
@@ -34,6 +40,13 @@ public class TestBase
         };
         _scopeFactory = scopeFactory;
         _userRepository = userRepository;
+        _httpContextAccessor = httpContextAccessor;
+
+        if (httpContextAccessor.HttpContext is null)
+        {
+            httpContextAccessor.HttpContext = new DefaultHttpContext();
+            SetZuoXi();
+        }
     }
 
     public void SetPaiDanYuan()
@@ -85,15 +98,33 @@ public class TestBase
                 UserName = userName
             };
             var accountId = _userController.Add(newUser).GetAwaiter().GetResult();
-            TestSessionConstants.UserId = accountId;
+            // TestSessionConstants.UserId = accountId;
             account = _accountRepository.GetExtAsync(
                 d => d.UserName == userName,
                 d => d.Includes(x => x.Roles)).GetAwaiter().GetResult();
         }
         var user = _userRepository.GetAsync(account.Id).GetAwaiter().GetResult();
-        TestSessionConstants.UserId = account.Id;
-        TestSessionConstants.Roles = account.Roles.Select(m => m.Id).ToArray();
-        TestSessionConstants.UserName = account.UserName;
-        TestSessionConstants.OrgId = user.OrgId;
+
+        List<Claim> userClaims = [ 
+            new(JwtClaimTypes.Subject, account.Id),
+            new(JwtClaimTypes.PhoneNumber, account.PhoneNo ?? string.Empty),
+            new(ClaimTypes.NameIdentifier, user.Id),
+            new(AppClaimTypes.UserDisplayName, account.Name),
+            new(AppClaimTypes.DepartmentId, user.OrgId ?? string.Empty), 
+            new(AppClaimTypes.DepartmentIsCenter, user.Organization?.IsCenter.ToString() ?? false.ToString()), 
+            new(AppClaimTypes.DepartmentName, user.Organization?.Name ?? string.Empty), 
+            new(AppClaimTypes.DepartmentAreaCode, user.Organization?.AreaCode ?? string.Empty), 
+            new(AppClaimTypes.DepartmentAreaName, user.Organization?.AreaName ?? string.Empty), 
+            new(AppClaimTypes.DepartmentLevel, user.Organization?.Level.ToString() ?? string.Empty), 
+            new(AppClaimTypes.AreaId, user.OrgId?.GetHigherOrgId() ?? string.Empty),
+        ];
+        ClaimsIdentity identity = new ClaimsIdentity(userClaims);
+        var principal = new ClaimsPrincipal(identity);
+        _httpContextAccessor.HttpContext.User = principal;
+
+        //TestSessionConstants.UserId = account.Id;
+        //TestSessionConstants.Roles = account.Roles.Select(m => m.Id).ToArray();
+        //TestSessionConstants.UserName = account.UserName;
+        //TestSessionConstants.OrgId = user.OrgId;
     }
 }

+ 2 - 1
src/Hotline.Application.Tests/appsettings.Development.json

@@ -68,7 +68,8 @@
         }
     },
     "ConnectionStrings": {
-         "Hotline": "PORT=5432;DATABASE=hotline_dev;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;"
+        "Hotline": "PORT=5432;DATABASE=hotline_dev;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;",
+        "CAP": "PORT=5432;DATABASE=fwmq;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;Search Path=cap"
         //"Hotline": "PORT=5432;DATABASE=hotline;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;"
     },
     "Cache": {

+ 20 - 20
src/Hotline.Application/CallCenter/DefaultCallApplication.cs

@@ -499,29 +499,29 @@ public abstract class DefaultCallApplication : ICallApplication
     /// <returns></returns>
     public virtual async Task OrderRelateCallHandlerAsync(string orderId, CancellationToken cancellationToken)
     {
-        var callId = await _orderRepository.Queryable()
-            .Where(m => m.Id == orderId)
-            .Select(m => m.CallId)
-            .FirstAsync(cancellationToken);
-        if (callId.IsNullOrEmpty()) return;
-
-        var callNo = await _callNativeRepository.Queryable()
-            .Where(m => m.Id == callId)
-            .Select(m => m.CallNo)
+        var orderCall = await _orderRepository.Queryable()
+            .LeftJoin<CallNative>((o, c) => o.CallId == c.Id)
+            .Where((o, c) => o.Id == orderId)
+            .Select((o, c) => new { o.CallId, c.CallNo })
             .FirstAsync(cancellationToken);
-        if (callNo.IsNullOrEmpty()) return;
-
-        var callNative = await _callNativeRepository.Queryable()
-            .Where(m => m.CallNo == callNo)
-            .ToListAsync(cancellationToken);
+        if (orderCall is null || orderCall.CallNo.IsNullOrEmpty())
+        {
+            _logger.LogError($"延迟更新工单通话, 工单: {orderId} 根据 order.id left join call_native 信息为空; 消息队列无须重试");
+            return;
+        }
 
-        var call = callNative.Where(m => m.Direction == ECallDirection.In)
+        var call = await _callNativeRepository.Queryable()
+            .Where(m => m.CallNo == orderCall.CallNo && m.Direction == ECallDirection.In)
             .OrderByDescending(m => m.Duration)
-            .First();
+            .FirstAsync(cancellationToken);
+        if (call == null)
+        {
+            var message = $"延迟更新工单通话, 工单: {orderId} 根据 CallNo: {orderCall.CallNo} direction = 0 查询 call_native 信息为空; 等兴唐把数据同步过来, 队列重试;";
+            throw new ArgumentNullException(message);
+        }
 
-        // 只有一条通话记录, 不处理
         // 需要更新的callId 和 order.callId 相同, 不处理
-        if (callNative.Count < 2 || callId == call.Id)
+        if (orderCall.CallId == call.Id)
         {
             // 推省上
             await _capPublisher.PublishAsync(EventNames.HotlineCallConnectWithOrder, new PublishCallRecrodDto()
@@ -539,7 +539,7 @@ public abstract class DefaultCallApplication : ICallApplication
 
         await _callIdRelationRepository.Updateable()
             .SetColumns(m => m.CallId == call.Id)
-            .Where(m => m.Id == callNo)
+            .Where(m => m.Id == orderCall.CallId)
             .ExecuteCommandAsync(cancellationToken);
 
         // 推省上
@@ -548,7 +548,7 @@ public abstract class DefaultCallApplication : ICallApplication
             Order = (await _orderRepository.GetAsync(orderId, cancellationToken)).Adapt<OrderDto>(),
             TrCallRecordDto = call.Adapt<TrCallDto>()
         }, cancellationToken: cancellationToken);
-        _systemLogRepository.Add("延迟更新工单通话", orderId, $"原CallId: {callId}, 更新CallId: {call.Id}", status: 1);
+        _systemLogRepository.Add("延迟更新工单通话", orderId, $"原CallId: {orderCall.CallId}, 更新CallId: {call.Id}", status: 1);
     }
 
     /// <summary>

+ 12 - 0
src/Hotline.Application/FlowEngine/IWorkflowApplication.cs

@@ -7,11 +7,13 @@ using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.File;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.FlowEngine.Definition;
+using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Dtos.Order.Handle;
 using Hotline.Share.Dtos.Settings;
 using Hotline.Share.Enums.FlowEngine;
 using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
 using XF.Domain.Authentications;
 
 namespace Hotline.Application.FlowEngine
@@ -120,5 +122,15 @@ namespace Hotline.Application.FlowEngine
         /// 更新省平台办理结果节点附件
         /// </summary>
         Task UpdateProvinceHandleResultFilesAsync(string workflowId, List<FileDto> files, CancellationToken cancellationToken);
+
+		/// <summary>
+		/// 查询会签信息
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <param name="_sessionContext"></param>
+		/// <returns></returns>
+		ISugarQueryable<WorkflowCountersign, Workflow, Order> QueryOrderCountersigns(QueryOrderCountersignDto dto,
+	        ISessionContext _sessionContext);
+
     }
 }

+ 81 - 8
src/Hotline.Application/FlowEngine/WorkflowApplication.cs

@@ -37,6 +37,9 @@ using Microsoft.Extensions.Options;
 using Newtonsoft.Json;
 using NPOI.SS.Formula.Functions;
 using DocumentFormat.OpenXml.Drawing;
+using Hotline.Share.Dtos.FlowEngine.Workflow;
+using SqlSugar;
+using Hotline.SeedData;
 
 namespace Hotline.Application.FlowEngine;
 
@@ -1729,17 +1732,87 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         }
     }
 
+    public ISugarQueryable<WorkflowCountersign,Workflow,Order> QueryOrderCountersigns(QueryOrderCountersignDto dto, ISessionContext _sessionContext)
+    {
+		var Role = _sessionContext.Roles;
+
+		var query = _workflowCountersignRepository.Queryable()
+			.Includes(x => x.Members)
+			.LeftJoin<Workflow>((c, w) => c.WorkflowId == w.Id)
+			.InnerJoin<Order>((c, w, o) => w.ExternalId == o.Id)
+			.WhereIF(!string.IsNullOrEmpty(dto.Title), (c, w, o) => o.Title.Contains(dto.Title))
+			.WhereIF(!string.IsNullOrEmpty(dto.OrderNo), (c, w, o) => o.No == dto.OrderNo)
+			.WhereIF(!string.IsNullOrEmpty(dto.AcceptType), (c, w, o) => o.AcceptTypeCode == dto.AcceptType)//受理类型
+			.WhereIF(!string.IsNullOrEmpty(dto.Channel), (c, w, o) => o.SourceChannelCode == dto.Channel)//受理类型
+			.WhereIF(!string.IsNullOrEmpty(dto.Hotspot), (c, w, o) => o.HotspotSpliceName != null && o.HotspotSpliceName.Contains(dto.Hotspot))//热点类型
+			.WhereIF(!string.IsNullOrEmpty(dto.OrgId), (c, w, o) => c.FinisherOrgId == dto.OrgId) //接办部门
+			.WhereIF(dto.CounterSignType != null, (c, w, o) => c.CounterSignType == dto.CounterSignType) //会签类型
+		;
+
+		var rolePaiDan = _systemSettingCacheManager.GetSetting(SettingConstants.RolePaiDan)?.SettingValue[0];//派单员角色
+		var seatsMonitor = _systemSettingCacheManager.GetSetting(SettingConstants.SeatsMonitor)?.SettingValue[0];//班长角色
+		var isAdmin = false;
+		var systemAdministrator = _systemSettingCacheManager.GetSetting(SettingConstants.SystemAdministrator)?.SettingValue[0];//管理员角色
+		if (!string.IsNullOrEmpty(systemAdministrator) && (_sessionContext.Roles.Contains(systemAdministrator) || _sessionContext.Roles.Contains(RoleSeedData.AdminRole)))
+			isAdmin = true;
+
+		//发起会签:班长角色能看所有会签信件;新增管理员能看到所有会签
+		//派单员角色只能看到自己发起的会签信件;
+		//承办部门用户能看到自己发起的和同级部门用户发起的会签件
+		if (dto.InitiatedCountersignature.HasValue && dto.InitiatedCountersignature == true)
+		{
+			if (_sessionContext.Roles.Any(p => p == seatsMonitor) || isAdmin)
+			{
+
+			}
+			else
+			if (_sessionContext.Roles.Any(p => p == rolePaiDan))
+			{
+				query = query.Where((c, w, o) => c.StarterId == _sessionContext.UserId);
+			}
+			else
+			{
+				query = query.Where((c, w, o) => c.StarterOrgId == _sessionContext.RequiredOrgId);
+			}
+		}
+
+		//会签已办(会签状态为已结束):
+		//班长角色能看所有已结束的会签信件;新增管理员能看到所有会签
+		//派单员不会办理会签件只会发起会签件所以这一点派单员角色可以忽略;
+		//承办部门用户能看到和同级部门用户已办理过的会签件
+		if (dto.HandleCountersignature.HasValue && dto.HandleCountersignature == true)
+		{
+			if (_sessionContext.Roles.Any(p => p == seatsMonitor) || isAdmin)
+			{
+				query = query.Where((c, w, o) => c.EndTime.HasValue);
+			}
+			else
+		   if (_sessionContext.Roles.Any(p => p == rolePaiDan))
+			{
+				query = query.Where((c, w, o) => c.Members.Any(m => m.Key == _sessionContext.RequiredOrgId) && c.EndTime.HasValue);
+			}
+			else
+			{
+				query = query.Where((c, w, o) => c.Members.Any(m => m.Key == _sessionContext.OrgId || m.Key == _sessionContext.RequiredOrgId) && c.EndTime.HasValue);
+			}
+		}
+
+		var items =  query
+			.OrderByDescending((c, w, o) => o.ExpiredTime);
+		return items;
+    }
+
 
     #region private
 
-    /// <summary>
-    /// 查询流程业务模块
-    /// </summary>
-    /// <param name="code"></param>
-    /// <param name="cancellationToken"></param>
-    /// <returns></returns>
-    /// <exception cref="UserFriendlyException"></exception>
-    public async Task<WorkflowModule> GetWorkflowModuleAsync(string code, CancellationToken cancellationToken)
+	/// <summary>
+	/// 查询流程业务模块
+	/// </summary>
+	/// <param name="code"></param>
+	/// <param name="cancellationToken"></param>
+	/// <returns></returns>
+	/// <exception cref="UserFriendlyException"></exception>
+	public async Task<WorkflowModule> GetWorkflowModuleAsync(string code, CancellationToken cancellationToken)
     {
         var wfModule = await _wfModuleCacheManager.GetWorkflowModuleAsync(code, cancellationToken);
         if (wfModule == null)

+ 1 - 1
src/Hotline.Application/Orders/OrderApplication.cs

@@ -2520,7 +2520,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             .WhereIF(!string.IsNullOrEmpty(dto.HotspotSpliceName), d => d.Order!.Hotspot.HotSpotFullName!.StartsWith(dto.HotspotSpliceName!))
             .WhereIF(!string.IsNullOrEmpty(dto.SourceChannel), d => d.Order!.SourceChannelCode! == dto.SourceChannel!)
             .WhereIF(!string.IsNullOrEmpty(dto.VisitOrgName), d => d.VisitDetail.VisitOrgName!.Contains(dto.VisitOrgName!))
-            .WhereIF(!string.IsNullOrEmpty(dto.CreatorOrgName), d => d.CreatorOrgName == dto.CreatorOrgName)
+            .WhereIF(!string.IsNullOrEmpty(dto.CreatorOrgName), d => d.CreatorOrgName!.Contains(dto.CreatorOrgName!))
             .WhereIF(!string.IsNullOrEmpty(dto.CreatorName), d => d.CreatorName == dto.CreatorName)
             .WhereIF(dto.IsProvince.HasValue, d => d.Order!.IsProvince == dto.IsProvince)
             .WhereIF(dto.IsSendBackApplyNum is true, d => d.SendBackApplyNum > 0)

+ 14 - 0
src/Hotline.Repository.SqlSugar/Extensions/SqlSugarStartupExtensions.cs

@@ -258,6 +258,20 @@ namespace Hotline.Repository.SqlSugar.Extensions
                 }
             };
 
+            db.Aop.DataChangesExecuted = (sql, entityInfo) =>
+            {
+                if (entityInfo.EntityColumnInfo.IsPrimarykey && entityInfo.OperationType == DataFilterType.UpdateByObject)
+                {
+                    var entityName = entityInfo.EntityName;
+                    Console.WriteLine(entityName);
+                }
+                if (entityInfo.EntityColumnInfo.IsPrimarykey && entityInfo.OperationType == DataFilterType.InsertByObject)
+                {
+                    var entityName = entityInfo.EntityName;
+                    Console.WriteLine(entityName);
+                }
+            };
+
             db.Aop.DataExecuting = (oldValue, entityInfo) =>
             {
                 //inset生效

+ 19 - 4
src/Hotline/Orders/OrderDomainService.cs

@@ -33,6 +33,7 @@ using Microsoft.AspNetCore.Builder.Extensions;
 using Hotline.Configurations;
 using Microsoft.Extensions.Options;
 using Hotline.Share.Tools;
+using Hotline.Orders.Notifications;
 
 namespace Hotline.Orders;
 
@@ -274,7 +275,7 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
         await _orderVisitDetailRepository.AddRangeAsync(visitedDetail, cancellationToken);
         await _publisher.PublishAsync(new ContingencyManagementNotify(order, order.Title, order.Content, order.ActualOpinion),
     PublishStrategy.ParallelWhenAll, cancellationToken);
-
+        await _publisher.PublishAsync(new UpdateOrderVisitNotify(orderVisit.Adapt<OrderVisitNotifyDto>()), cancellationToken);
 
         if (order.IsProvince == false && orderVisit.VisitState == EVisitState.Visited)
         {
@@ -521,7 +522,13 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
                 .Where(s => s.Id == scheduling.Id).ExecuteCommandAsync(cancellationToken);
             if (sendNum <= 0) return;
             var sendSteps = steps.Take(sendNum).ToList();
-            await _workflowDomainService.ChangeHandlerBatchAsync(new List<(string userId, string username, string orgId, string orgName, string? roleId, string? roleName, ICollection<WorkflowStep> steps)>
+            await _orderRepository.Updateable().SetColumns(o => new Order()
+	            {
+		            CenterToOrgHandlerId = user.OrgId,
+		            CenterToOrgHandlerName = user.Name
+			}).Where(o => sendSteps.Any(s => s.ExternalId == o.Id)).ExecuteCommandAsync(cancellationToken);
+
+			await _workflowDomainService.ChangeHandlerBatchAsync(new List<(string userId, string username, string orgId, string orgName, string? roleId, string? roleName, ICollection<WorkflowStep> steps)>
             {
                 new(user.Id, user.Name, user.OrgId, user.Organization.Name,roleId,"派单员", sendSteps)
             }, cancellationToken);
@@ -559,18 +566,26 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
                     var size = avg + (i < remaining ? 1 : 0);
                     if (size > 0)
                     {
-                        handlers.Add(new(
+	                    var stSteps = steps.Skip(skip).Take(size).ToList();
+
+						handlers.Add(new(
                             scheduling.SchedulingUser.UserId,
                             scheduling.SchedulingUser.UserName,
                             scheduling.SchedulingUser.OrgId,
                             scheduling.SchedulingUser.OrgIdName,
                             roleId, "派单员",
-                            steps.Skip(skip).Take(size).ToList()));
+                            stSteps));
                         skip += size;
                         scheduling.SendOrderNum += size;
                         await _schedulingRepository.Updateable()
                             .SetColumns(s => new Scheduling() { SendOrderNum = scheduling.SendOrderNum })
                             .Where(s => s.Id == scheduling.Id).ExecuteCommandAsync(cancellationToken);
+                        await _orderRepository.Updateable().SetColumns(o => new Order()
+	                        {
+		                        CenterToOrgHandlerId = scheduling.SchedulingUser.UserId,
+		                        CenterToOrgHandlerName = scheduling.SchedulingUser.UserName
+	                        })
+	                        .Where(o => stSteps.Any(s=>s.ExternalId == o.Id)).ExecuteCommandAsync(cancellationToken);
                     }
                 }
                 if (handlers.Any())

+ 1 - 2
src/Hotline/Orders/OrderVisitDomainService.cs

@@ -43,7 +43,6 @@ public class OrderVisitDomainService : IOrderVisitDomainService, IScopeDependenc
     /// <returns></returns>
     public async Task UpdateSmsReplyAsync(MessageDto data)
     {
-        _logger.LogInformation($"UpdateSmsReplyAsync 收到通知: {data.ToJson()}");
         if (data.IsSmsReply == false || data.SmsReplyContent.IsNullOrEmpty()) return;
 
         var orderVisits = await _orderVisitRepository.Queryable()
@@ -51,7 +50,7 @@ public class OrderVisitDomainService : IOrderVisitDomainService, IScopeDependenc
             .Where(m => m.Order.Contact == data.TelNumber)
             .Where(m => m.VisitState == EVisitState.SMSVisiting)
             .ToListAsync();
-
+            
         foreach (var orderVisit in orderVisits)
         {
             await UpdateSmsReplyAsync(orderVisit, data.SmsReplyContent!.Trim());

+ 1 - 2
src/Hotline/Push/FWMessage/PushDomainService.cs

@@ -130,7 +130,6 @@ public class PushDomainService : IPushDomainService, IScopeDependency
     /// <returns></returns>
     public async Task PushMsgUpdateStateAsync(PushReceiveMessageDto dto, CancellationToken cancellation)
     {
-        _logger.LogInformation("收到短信通知 PushMsgUpdateStateAsync");
         var data = await _messageRepository.GetAsync(p => p.Id == dto.ExternalId, cancellation);
         if (data != null)
         {
@@ -192,4 +191,4 @@ public class PushDomainService : IPushDomainService, IScopeDependency
         }
     }
     
-}
+}