Bläddra i källkod

Merge branch 'master' of http://110.188.24.182:10023/Fengwo/hotline

田爽 1 år sedan
förälder
incheckning
3b23a125ae

+ 34 - 91
src/Hotline.Api/Controllers/Bi/BiOrderController.cs

@@ -420,68 +420,6 @@ namespace Hotline.Api.Controllers.Bi
 			return new PagedDto<OrderSpecialDto>(total, _mapper.Map<IReadOnlyList<OrderSpecialDto>>(items));
 		}
 
-		/// <summary>
-		/// 受理类型前十
-		/// </summary>
-		/// <param name="dto"></param>
-		/// <returns></returns>
-		//[HttpGet("accept_type_top10_list")]
-		//public async Task<object> AcceptTypeTop10List([FromQuery] ReportPagedRequest dto)
-  //      {
-	 //       if (!dto.StartTime.HasValue || !dto.EndTime.HasValue) throw UserFriendlyException.SameMessage("请选择时间!");
-  //          dto.PageIndex = 1;
-  //          dto.PageSize = 10;
-  //          var acceptType = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.AcceptType);
-  //          List<dynamic> listReturn =  new List<dynamic>();
-  //          var validQuery = await _orderRepository.Queryable(false, false, false)
-	 //           .WhereIF(dto.StartTime.HasValue, x => x.CreationTime >= dto.StartTime)
-	 //           .WhereIF(dto.EndTime.HasValue, x => x.CreationTime <= dto.EndTime)
-	 //           .Select(x => new
-	 //           {
-		//            AcceptType = x.AcceptType,
-		//            OneHotspot = SqlFunc.Substring(x.HotspotSpliceName, 0, SqlFunc.CharIndex("-", x.HotspotSpliceName + "-")),
-		//            Id = x.Id
-	 //           }).MergeTable()
-	 //           .GroupBy(x => new { x.OneHotspot })
-	 //           .Select(x => new 
-	 //           {
-		//            Name = x.OneHotspot,
-		//            Id = "0",
-		//            AcceptName = "有效受理量",
-		//            Num = SqlFunc.AggregateSum(SqlFunc.IIF(true, 1, 0)),
-	 //           }).ToListAsync();
-  //          listReturn.AddRange(validQuery);
-		//	foreach (var item in acceptType)
-  //          {
-		//		var query = await _orderRepository.Queryable(false, false, false)
-		//			.WhereIF(dto.StartTime.HasValue, x => x.CreationTime >= dto.StartTime)
-		//			.WhereIF(dto.EndTime.HasValue, x => x.CreationTime <= dto.EndTime)
-		//			.Select(x => new
-		//			{
-		//				AcceptType = x.AcceptType,
-		//				OneHotspot = SqlFunc.Substring(x.HotspotSpliceName, 0, SqlFunc.CharIndex("-", x.HotspotSpliceName + "-")),
-		//				Id = x.Id
-		//			}).MergeTable()
-		//			.GroupBy(x => new { x.OneHotspot })
-		//			.Select(x => new 
-		//			{
-		//				Name = x.OneHotspot,
-  //                      Id = item.Id,
-  //                      AcceptName = item.DicDataName,
-		//				Num = SqlFunc.AggregateSum(SqlFunc.IIF(item.DicDataName.Equals(x.AcceptType), 1, 0)),
-		//			}).ToListAsync();
-		//		listReturn.AddRange(query);
-		//	}
-  // //         if (!string.IsNullOrEmpty(dto.SortField))
-  // //         {
-  // //             listReturn = dto.SortRule == 0 ? (from items in listReturn orderby items.Num  select items).ToList() : (from items in listReturn orderby items.Num descending select items).ToList();
-		//	//}
-  // //         else {
-	 // //          listReturn = (from items in listReturn orderby items.Num  descending select items).ToList() ;
-		//	//}
-	 //       return new { Header= acceptType,Data = listReturn };
-		//}
-
 		/// <summary>
 		/// 受理类型前十
 		/// </summary>
@@ -610,6 +548,8 @@ namespace Hotline.Api.Controllers.Bi
                 var list = await _hotspotTypeRepository.Queryable()
                 .LeftJoin<Order>((it, o) => it.Id == o.HotspotId)
                 .Where((it, o) => o.StartTime >= StartDate && o.StartTime <= EndDate && o.Id!=null)
+				.WhereIF(TypeId==1,(it,o)=> o.IdentityType == EIdentityType.Citizen)
+				.WhereIF(TypeId==2,(it,o)=> o.IdentityType == EIdentityType.Enterprise)
                 .GroupBy((it, o) => new { Id = it.Id.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("2")) })
                 .Select((it, o) => new
                 {
@@ -635,6 +575,8 @@ namespace Hotline.Api.Controllers.Bi
                 var list = await _hotspotTypeRepository.Queryable()
                 .LeftJoin<Order>((it, o)=> it.Id == o.HotspotId)
                 .Where((it, o) => o.StartTime >= StartDate && o.StartTime <= EndDate && it.ParentId.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>(countx)) == HotspotCode)
+                .WhereIF(TypeId == 1, (it, o) => o.IdentityType == EIdentityType.Citizen)
+                .WhereIF(TypeId == 2, (it, o) => o.IdentityType == EIdentityType.Enterprise)
                 .GroupBy((it, o) => new { Id = it.Id.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>(count)) })
                 .Select((it, o) => new
                 {
@@ -661,33 +603,34 @@ namespace Hotline.Api.Controllers.Bi
 
 
 
-        /// <summary>
-        /// 部门满意度统计
-        /// </summary>
-        /// <param name="StartDate"></param>
-        /// <param name="EndDate"></param>
-        /// <param name="OrgName"></param>
-        /// <param name="TypeId">1:办件结果 2:办件态度</param>
-        /// <param name="LineNum"></param>
-        /// <returns></returns>
-  //      [HttpGet("visit-org-satisfaction-statistics")]
-		//public async Task VisitAndOrgSatisfactionStatistics(DateTime StartDate,DateTime EndDate,string OrgName,int TypeId,string? LineNum )
-		//{
-		//	EndDate = EndDate.AddDays(1).AddSeconds(-1);
-		//	var list = await _orderVisitDetailRepository.Queryable()
-		//		.Includes(x => x.OrderVisit)
-		//		.Where(x => x.OrderVisit.VisitTime >= StartDate && x.OrderVisit.VisitTime <= EndDate && x.VisitTarget == EVisitTarget.Org)
-		//		.WhereIF(!string.IsNullOrEmpty(OrgName),x=>x.VisitOrgName.Contains(OrgName))
-		//		.GroupBy(x=> new { x.VisitOrgCode ,x.VisitOrgName })
-		//		.Select(x => new
-		//		{ 
-		//			OrgName = x.VisitOrgName,
-		//			OrgCode= x.VisitOrgCode,
-
-		//		})
-		//		.ToListAsync();
-
-
-  //      }
-    }
+		/// <summary>
+		/// 部门满意度统计
+		/// </summary>
+		/// <param name="StartDate"></param>
+		/// <param name="EndDate"></param>
+		/// <param name="OrgName"></param>
+		/// <param name="TypeId">1:办件结果 2:办件态度</param>
+		/// <param name="LineNum"></param>
+		/// <returns></returns>
+		[HttpGet("visit-org-satisfaction-statistics")]
+		public async Task<List<VisitAndOrgSatisfactionStatisticsDto>> VisitAndOrgSatisfactionStatistics(DateTime StartDate, DateTime EndDate, string OrgName, int TypeId, string? LineNum)
+		{
+			EndDate = EndDate.AddDays(1).AddSeconds(-1);
+			var list = await _orderVisitDetailRepository.Queryable()
+				.Includes(x => x.OrderVisit)
+				.Where(x => x.OrderVisit.VisitTime >= StartDate && x.OrderVisit.VisitTime <= EndDate && x.VisitTarget == EVisitTarget.Org && x.OrderVisit.VisitState == EVisitState.Visited)
+				.WhereIF(!string.IsNullOrEmpty(OrgName), x => x.VisitOrgName.Contains(OrgName))
+				.GroupBy(x => new { x.VisitOrgCode, x.VisitOrgName })
+				.Select(x => new VisitAndOrgSatisfactionStatisticsDto()
+                {
+					OrgName = x.VisitOrgName,
+					OrgCode = x.VisitOrgCode,
+					TotalSumCount = SqlFunc.AggregateCount(x.VisitOrgCode),
+					VerySatisfiedCount = SqlFunc.AggregateSum(SqlFunc.IIF(SqlFunc.JsonListObjectAny(x.OrgProcessingResults,"key","5"),1,0)),//非常满意数
+				})
+				.ToListAsync();
+
+			return list;
+		}
+	}
 }

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

@@ -184,7 +184,7 @@ public class TestController : BaseController
 
         //int a = _timeLimitDomainService.CalcWorkTime(DateTime.Now, DateTime.Parse("2023-09-11 16:21:00"));
         //int m = _timeLimitDomainService.CalcWorkTime(DateTime.Parse("2023-09-19 12:00:00"), DateTime.Parse("2023-09-20 18:00:00"), false);
-        //var r = _timeLimitDomainService.CalcEndTime(DateTime.Parse("2024-01-31 18:00:00"), ETimeType.WorkDay,1,false,50);
+        var r = _timeLimitDomainService.CalcEndTime(DateTime.Parse("2024-02-29 10:12:33"), ETimeType.WorkDay,3,false,80);
         //await _wfModuleDomainService.PersistenceModulesAsync(HttpContext.RequestAborted);
 
         //var rsp = await _daprClient.InvokeMethodAsync<ApiResponse<string>>(HttpMethod.Get, "identity", "api/v1/Test/time", HttpContext.RequestAborted);

+ 22 - 49
src/Hotline.Application/FlowEngine/WorkflowApplication.cs

@@ -113,8 +113,10 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         //如果发起会签需检查是否支持发起会签
         var startStepDefine = definition.FindStartStepDefine();
 
-        var firstStepDefine = startStepDefine.InstanceMode is EInstanceMode.Dynamic &&
-                              !DynamicShouldTerminal(startStepDefine, _sessionContext.OrgLevel)
+        //下一节点是否为动态节点
+        var isNextDynamic = startStepDefine.InstanceMode is EInstanceMode.Dynamic &&
+                                      !_workflowDomainService.DynamicShouldTerminal(startStepDefine, _sessionContext.OrgLevel);
+        var firstStepDefine = isNextDynamic
             ? startStepDefine
             : definition.FindStepDefine(dto.NextStepCode);
         if (firstStepDefine is null)
@@ -161,10 +163,10 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         workflow.Traces.Add(startTrace);
 
         var flowAssignInfo =
-            await GetNextStepFlowAssignInfoAsync(workflow, startStepDefine, startStep, firstStepDefine, dto, cancellationToken);
+            await GetNextStepFlowAssignInfoAsync(workflow, startStep, dto, firstStepDefine, isNextDynamic, cancellationToken);
 
-        await _workflowDomainService.StartAsync(workflow, startStep, dto, firstStepDefine, flowAssignInfo,
-            cancellationToken);
+        await _workflowDomainService.StartAsync(workflow, startStep, dto, firstStepDefine, isNextDynamic,
+            flowAssignInfo, cancellationToken);
 
         return workflow.Id;
     }
@@ -189,8 +191,12 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
 
         var currentStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, currentStep.Code);
 
+        //下一节点是否为动态节点
+        var isNextDynamic = currentStepDefine.InstanceMode is EInstanceMode.Dynamic &&
+                            !_workflowDomainService.DynamicShouldTerminal(currentStepDefine, _sessionContext.OrgLevel);
+
         StepDefine nextStepDefine;
-        if ((currentStep.InstanceMode is EInstanceMode.Dynamic && !DynamicShouldTerminal(currentStep))
+        if (isNextDynamic
             || (currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
             || dto.IsStartCountersign)
         {
@@ -222,11 +228,10 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         }
 
         var flowAssignInfo =
-            await GetNextStepFlowAssignInfoAsync(workflow, currentStepDefine, currentStep, nextStepDefine, dto,
-                cancellationToken);
+            await GetNextStepFlowAssignInfoAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic, cancellationToken);
 
-        await _workflowDomainService.NextAsync(workflow, currentStep, dto, nextStepDefine, flowAssignInfo,
-            cancellationToken);
+        await _workflowDomainService.NextAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic,
+            flowAssignInfo, cancellationToken);
     }
 
     /// <summary>
@@ -480,7 +485,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         //};
 
         if (startStepDefine.InstanceMode is EInstanceMode.Dynamic &&
-            !DynamicShouldTerminal(startStepDefine, _sessionContext.OrgLevel))
+            !_workflowDomainService.DynamicShouldTerminal(startStepDefine, _sessionContext.OrgLevel))
         {
             //var nextStepOption = CreateDynamicStep(startStep.InstancePolicy);
             //dto.Steps = new List<NextStepOption> { nextStepOption };
@@ -531,7 +536,9 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             IsMainHandlerShow = workflow.WorkflowDefinition.IsMainHandlerShow,
         };
 
-        if (currentStep.InstanceMode is EInstanceMode.Dynamic && !DynamicShouldTerminal(currentStep))
+        var currentStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, currentStep.Code);
+
+        if (currentStep.InstanceMode is EInstanceMode.Dynamic && !_workflowDomainService.DynamicShouldTerminal(currentStepDefine, _sessionContext.OrgLevel))
         {
             //动态生成下一步
             var nextStepOption = await GetDynamicStepAsync(currentStep.InstancePolicy.Value, currentStep.StepType,
@@ -846,38 +853,6 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         }
     }
 
-    private bool DynamicShouldTerminal(WorkflowStep step)
-    {
-        if (step.InstanceMode is not EInstanceMode.Dynamic)
-            throw new UserFriendlyException("非动态节点");
-        switch (step.InstancePolicy)
-        {
-            case EDynamicPolicy.OrgUpCenterTop:
-            case EDynamicPolicy.OrgUp:
-            case EDynamicPolicy.OrgDownCenterTop:
-            case EDynamicPolicy.OrgDown:
-                return step.DynamicShouldTerminal();
-            default:
-                throw new ArgumentOutOfRangeException();
-        }
-    }
-
-    private bool DynamicShouldTerminal(StepDefine currentStepDefine, int currentOrgLevel)
-    {
-        if (currentStepDefine.InstanceMode is not EInstanceMode.Dynamic)
-            throw new UserFriendlyException("非动态节点");
-        switch (currentStepDefine.InstancePolicy)
-        {
-            case EDynamicPolicy.OrgUpCenterTop:
-            case EDynamicPolicy.OrgUp:
-            case EDynamicPolicy.OrgDownCenterTop:
-            case EDynamicPolicy.OrgDown:
-                return currentStepDefine.TerminalDynamicMark == currentOrgLevel.ToString();
-            default:
-                throw new ArgumentOutOfRangeException();
-        }
-    }
-
     private NextStepOption GetCsEndStepByPrev(List<WorkflowStep> steps, WorkflowStep step)
     {
         var prevStep = steps.FirstOrDefault(d => d.Id == step.PrevStepId);
@@ -1089,9 +1064,8 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
     /// <summary>
     /// 查询下一节点办理对象类型(user or org)及实际办理对象
     /// </summary>
-    public async Task<FlowAssignInfo> GetNextStepFlowAssignInfoAsync(Workflow workflow, StepDefine currentStepDefine,
-        WorkflowStep currentStep,
-        StepDefine nextStepDefine, BasicWorkflowDto dto, CancellationToken cancellationToken)
+    public async Task<FlowAssignInfo> GetNextStepFlowAssignInfoAsync(Workflow workflow, WorkflowStep currentStep,
+      BasicWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic, CancellationToken cancellationToken)
     {
         if (nextStepDefine.StepType is EStepType.End) return new();
 
@@ -1140,8 +1114,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             }
         }
 
-        if (currentStep.InstanceMode is EInstanceMode.Dynamic &&
-            !DynamicShouldTerminal(currentStepDefine, _sessionContext.OrgLevel))
+        if (isNextDynamic)
             return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign);
 
         return await GetNextStepFlowAssignInfoByDefineAsync(nextStepDefine, isStartCountersign, handlers,

+ 90 - 0
src/Hotline.Share/Dtos/Order/OrderBiDto.cs

@@ -182,4 +182,94 @@ namespace Hotline.Share.Dtos.Order
 
 		public int SumCount { get; set; }
     }
+
+
+	public class VisitAndOrgSatisfactionStatisticsDto
+	{
+		public string OrgName { get; set; }
+
+		public string OrgCode { get; set; }
+
+		public EOrgType OrgType { get; set; }
+
+        /// <summary>
+        /// 总数
+        /// </summary>
+        public int TotalSumCount { get; set; }
+
+		/// <summary>
+		/// 总满意度
+		/// </summary>
+		public double TotalSumRate { get; set; }
+
+		/// <summary>
+		/// 非常满意数
+		/// </summary>
+		public int VerySatisfiedCount { get; set; }
+
+		/// <summary>
+		/// 非常满意率
+		/// </summary>
+		public double VerySatisfiedRate { get; set; }
+
+		/// <summary>
+		/// 满意数
+		/// </summary>
+		public int SatisfiedCount { get; set; }
+
+		/// <summary>
+		/// 满意率
+		/// </summary>
+		public double SatisfiedRate { get; set; }
+
+		/// <summary>
+		/// 一般满意数
+		/// </summary>
+		public int GeneralSatisfiedCount { get; set; }
+
+		/// <summary>
+		/// 一般满意率
+		/// </summary>
+		public double GeneralSatisfiedRate { get; set; }
+
+		/// <summary>
+		/// 不满意数
+		/// </summary>
+		public int NoSatisfiedCount { get; set; }
+
+		/// <summary>
+		/// 不满意率
+		/// </summary>
+		public  double NoSatisfiedRate { get; set; }
+
+		/// <summary>
+		/// 非常不满意数
+		/// </summary>
+		public int VeryNoSatisfiedCount { get; set; }
+
+		/// <summary>
+		/// 非常不满意率
+		/// </summary>
+		public double VeryNoSatisfiedRate { get; set; }
+
+		/// <summary>
+		/// 未做评价数
+		/// </summary>
+		public int NoEvaluateCount { get; set; }
+
+		/// <summary>
+		/// 未做评价率
+		/// </summary>
+		public double NoEvaluateRate { get; set; }
+
+		/// <summary>
+		/// 未接通数
+		/// </summary>
+		public int NoPutThroughCount { get; set; }
+
+		/// <summary>
+		/// 未接通率
+		/// </summary>
+		public double NoPutThroughRate { get; set; }
+    }
 }

+ 7 - 2
src/Hotline.Share/Dtos/Order/OrderDto.cs

@@ -428,14 +428,19 @@ namespace Hotline.Share.Dtos.Order
 
         public EExpiredStatus? CalculateExpiredState()
         {
+            DateTime? dateTime = DateTime.Now;
+            if (Status>= EOrderStatus.Filed)
+            {
+                dateTime = ActualHandleTime;
+            }
             //ExpiredStatus
             if (ExpiredTime.HasValue)
             {
-                if (DateTime.Now< NearlyExpiredTime)
+                if (dateTime < NearlyExpiredTime)
                 {
                     return EExpiredStatus.Normal;
                 }
-                else if (DateTime.Now> NearlyExpiredTime && DateTime.Now < ExpiredTime)
+                else if (dateTime > NearlyExpiredTime && dateTime < ExpiredTime)
                 {
                     return EExpiredStatus.GoingToExpired;
                 }

+ 19 - 14
src/Hotline/FlowEngine/Workflows/IWorkflowDomainService.cs

@@ -23,7 +23,7 @@ namespace Hotline.FlowEngine.Workflows
         /// 进行流程的开始节点
         /// </summary>
         Task StartAsync(Workflow workflow, WorkflowStep startStep, BasicWorkflowDto dto, StepDefine firstStepDefine,
-            FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken);
+            bool isNextDynamic, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken);
 
         /// <summary>
         /// 查询工作流
@@ -48,12 +48,12 @@ namespace Hotline.FlowEngine.Workflows
         /// 办理(流转至下一节点)
         /// </summary>
         Task NextAsync(Workflow workflow, WorkflowStep currentStep, NextWorkflowDto dto, StepDefine nextStepDefine,
-            FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken);
+            bool isNextDynamic, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken);
 
-		/// <summary>
-		/// 退回(返回前一节点)
-		/// </summary>
-		/// <returns></returns>
+        /// <summary>
+        /// 退回(返回前一节点)
+        /// </summary>
+        /// <returns></returns>
         Task PreviousAsync(Workflow workflow, PreviousWorkflowDto dto, User operater, CancellationToken cancellationToken);
 
         /// <summary>
@@ -141,14 +141,14 @@ namespace Hotline.FlowEngine.Workflows
         /// </summary>
         Task EndCountersignAsync(string workflowId, CancellationToken cancellationToken);
 
-		/// <summary>
-		/// 查询未完成节点
-		/// </summary>
-		/// <param name="steps"></param>
-		/// <param name="orgCode"></param>
-		/// <param name="userId"></param>
-		/// <returns></returns>
-		WorkflowStep GetUnHandleStep(List<WorkflowStep> steps, string orgCode, string userId);
+        /// <summary>
+        /// 查询未完成节点
+        /// </summary>
+        /// <param name="steps"></param>
+        /// <param name="orgCode"></param>
+        /// <param name="userId"></param>
+        /// <returns></returns>
+        WorkflowStep GetUnHandleStep(List<WorkflowStep> steps, string orgCode, string userId);
 
         /// <summary>
         /// 检查当前办理节点是否为开始节点
@@ -159,5 +159,10 @@ namespace Hotline.FlowEngine.Workflows
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
         Task<bool> CheckCurrentIsStartStepAsync(string workflowId, string userId, string orgId, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 检查动态节点是否该终止
+        /// </summary>
+        bool DynamicShouldTerminal(StepDefine currentStepDefine, int currentOrgLevel);
     }
 }

+ 89 - 30
src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs

@@ -100,13 +100,12 @@ namespace Hotline.FlowEngine.Workflows
         /// 流程开始
         /// </summary>
         public async Task StartAsync(Workflow workflow, WorkflowStep startStep, BasicWorkflowDto dto,
-            StepDefine firstStepDefine, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken)
+            StepDefine firstStepDefine, bool isNextDynamic, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken)
         {
             //1. 创建first节点 (和trace)2.办理开始节点 
 
             //firststeps
-            var firstSteps = await CreateNextStepsAsync(workflow, firstStepDefine, startStep, dto, flowAssignInfo,
-                cancellationToken);
+            var firstSteps = await CreateNextStepsAsync(workflow, startStep, dto, firstStepDefine, isNextDynamic, flowAssignInfo, cancellationToken);
             if (firstSteps.Any())
                 workflow.Steps.AddRange(firstSteps);
 
@@ -273,7 +272,7 @@ namespace Hotline.FlowEngine.Workflows
         /// 办理(流转至下一节点)
         /// </summary>
         public async Task NextAsync(Workflow workflow, WorkflowStep currentStep, NextWorkflowDto dto,
-            StepDefine nextStepDefine, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken)
+            StepDefine nextStepDefine, bool isNextDynamic, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken)
         {
             ValidatePermission(workflow, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId);
             CheckWhetherRunnable(workflow.Status);
@@ -404,8 +403,8 @@ namespace Hotline.FlowEngine.Workflows
             ////    workflow.CenterToOrg(CalculateExpiredTime(workflow.WorkflowDefinition.Code));//todo 过期时间
 
             //创建下一/N个节点(会签汇总节点:会签未全部办理时不创建,最后一个会签办理节点创建会签汇总节点)
-            var nextSteps = await CreateNextStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo,
-                cancellationToken);
+            var nextSteps = await CreateNextStepsAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic,
+                flowAssignInfo, cancellationToken);
 
             //赋值当前节点的下级办理节点
             if (dto.IsStartCountersign
@@ -900,9 +899,8 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 创建下1/N个节点
         /// </summary>
-        private async Task<List<WorkflowStep>> CreateNextStepsAsync(Workflow workflow, StepDefine nextStepDefine,
-            WorkflowStep currentStep, BasicWorkflowDto dto, FlowAssignInfo flowAssignInfo,
-            CancellationToken cancellationToken)
+        private async Task<List<WorkflowStep>> CreateNextStepsAsync(Workflow workflow, WorkflowStep currentStep, BasicWorkflowDto dto,
+            StepDefine nextStepDefine, bool isNextDynamic, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken)
         {
             List<WorkflowStep> nextSteps = new();
             if (currentStep.IsInCountersign())
@@ -915,13 +913,13 @@ namespace Hotline.FlowEngine.Workflows
                     {
                         if (dto.IsStartCountersign)
                         {
-                            //todo 依据会签策略创建会签下一级节点
+                            //依据会签策略创建会签下一级节点
                             nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
                                 flowAssignInfo.FlowAssignType, cancellationToken);
                         }
                         else
                         {
-                            //todo 创建普通节点(根据配置)
+                            //创建普通节点(根据配置)
                             nextSteps = await CreateConfigStepsAsync(workflow, nextStepDefine, currentStep, dto,
                                 flowAssignInfo, EWorkflowTraceStatus.Normal, cancellationToken);
                         }
@@ -948,7 +946,7 @@ namespace Hotline.FlowEngine.Workflows
                     }
                     else
                     {
-                        //todo 依据会签策略创建会签下一级节点
+                        //依据会签策略创建会签下一级节点
                         nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
                             flowAssignInfo.FlowAssignType, cancellationToken);
                     }
@@ -956,20 +954,19 @@ namespace Hotline.FlowEngine.Workflows
             }
             else if (dto.IsStartCountersign) //top
             {
-                //todo 依据会签策略创建会签下一级节点
+                //依据会签策略创建会签下一级节点
                 nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
                     flowAssignInfo.FlowAssignType, cancellationToken);
             }
-            else if (currentStep.InstanceMode is EInstanceMode.Dynamic && !currentStep.DynamicShouldTerminal())
+            else if (isNextDynamic)
             {
-                //todo 创建动态下一级节点
-                nextSteps = await CreateStepsAsync(workflow, nextStepDefine, currentStep, dto,
-                    flowAssignInfo.FlowAssignType, dto.NextHandlers, null, EWorkflowStepStatus.WaitForAccept,
-                    ECountersignPosition.None, false, EWorkflowTraceStatus.Normal, cancellationToken);
+                //创建动态下一级节点
+                nextSteps = await CreateDynamicStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo,
+                    cancellationToken);
             }
             else
             {
-                //todo 创建普通节点(根据配置)
+                //创建普通节点(根据配置)
                 nextSteps = await CreateConfigStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo,
                     EWorkflowTraceStatus.Normal, cancellationToken);
             }
@@ -977,6 +974,29 @@ namespace Hotline.FlowEngine.Workflows
             return nextSteps;
         }
 
+        private async Task<List<WorkflowStep>> CreateDynamicStepsAsync(
+            Workflow workflow,
+            StepDefine nextStepDefine,
+            WorkflowStep prevStep,
+            BasicWorkflowDto dto,
+            FlowAssignInfo flowAssignInfo,
+            CancellationToken cancellationToken)
+        {
+            var handlerType = nextStepDefine.InstancePolicy switch
+            {
+                EDynamicPolicy.OrgUpCenterTop => EHandlerType.OrgLevel,
+                EDynamicPolicy.OrgUp => EHandlerType.OrgLevel,
+                EDynamicPolicy.OrgDownCenterTop => EHandlerType.OrgLevel,
+                EDynamicPolicy.OrgDown => EHandlerType.OrgLevel,
+                null => throw new ArgumentOutOfRangeException(),
+                _ => throw new ArgumentOutOfRangeException()
+            };
+
+            return await CreateStepsAsync(workflow, nextStepDefine, prevStep, dto,
+                flowAssignInfo.FlowAssignType, dto.NextHandlers, null, EWorkflowStepStatus.WaitForAccept,
+                ECountersignPosition.None, false, EWorkflowTraceStatus.Normal, handlerType, cancellationToken);
+        }
+
         private Task<List<WorkflowStep>> CreateCountersignStepsAsync(
             Workflow workflow,
             StepDefine stepDefine,
@@ -989,10 +1009,20 @@ namespace Hotline.FlowEngine.Workflows
         {
             var countersignId = prevStep.IsStartCountersign ? prevStep.StartCountersignId : prevStep.CountersignId;
 
+            var handlerType = stepDefine.CountersignPolicy switch
+            {
+                EDynamicPolicy.OrgUpCenterTop => EHandlerType.OrgLevel,
+                EDynamicPolicy.OrgUp => EHandlerType.OrgLevel,
+                EDynamicPolicy.OrgDownCenterTop => EHandlerType.OrgLevel,
+                EDynamicPolicy.OrgDown => EHandlerType.OrgLevel,
+                null => throw new ArgumentOutOfRangeException(),
+                _ => throw new ArgumentOutOfRangeException()
+            };
+
             return CreateStepsAsync(workflow, stepDefine, prevStep, dto, flowAssignType, dto.NextHandlers,
                 countersignId,
                 EWorkflowStepStatus.WaitForAccept, prevStep.GetNextStepCountersignPosition(),
-                false, EWorkflowTraceStatus.Normal, cancellationToken);
+                false, EWorkflowTraceStatus.Normal, handlerType, cancellationToken);
         }
 
         /// <summary>
@@ -1441,7 +1471,7 @@ namespace Hotline.FlowEngine.Workflows
                 : (await CreateStepsAsync(workflow, targetStepDefine, targetPrevStep, dto,
                     flowAssignInfo.FlowAssignType, dto.NextHandlers,
                     null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, true, traceStatus,
-                    cancellationToken)).First();
+                    null, cancellationToken)).First();
 
 
             //更新当前办理节点信息
@@ -1538,7 +1568,7 @@ namespace Hotline.FlowEngine.Workflows
 
             return await CreateStepsAsync(workflow, stepDefine, prevStep, dto, flowAssignInfo.FlowAssignType, handlers,
                 null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, true, traceStatus,
-                cancellationToken);
+                null, cancellationToken);
         }
 
         private async Task<List<WorkflowStep>> CreateStepsAsync(
@@ -1554,7 +1584,8 @@ namespace Hotline.FlowEngine.Workflows
             //DateTime expiredTime,
             bool isOrigin,
             EWorkflowTraceStatus traceStatus,
-            CancellationToken cancellationToken
+            EHandlerType? handlerType = null,
+            CancellationToken cancellationToken = default
         )
         {
             //var countersignId = prevStep.IsStartCountersign
@@ -1568,7 +1599,7 @@ namespace Hotline.FlowEngine.Workflows
                 {
                     var step = CreateStep(stepDefine, prevStep, workflow.Id, flowAssignType, new List<Kv> { handler },
                         dto.NextStepCode, dto.NextMainHandler, countersignId,
-                        stepStatus, csPosition, workflow.ExpiredTime, dto.NextStepName, isOrigin);
+                        stepStatus, csPosition, workflow.ExpiredTime, dto.NextStepName, isOrigin, handlerType);
 
                     steps.Add(step);
                 }
@@ -1577,7 +1608,7 @@ namespace Hotline.FlowEngine.Workflows
             {
                 var step = CreateStep(stepDefine, prevStep, workflow.Id, flowAssignType, handlers,
                     dto.NextStepCode, dto.NextMainHandler, countersignId,
-                    stepStatus, csPosition, workflow.ExpiredTime, dto.NextStepName, isOrigin);
+                    stepStatus, csPosition, workflow.ExpiredTime, dto.NextStepName, isOrigin, handlerType);
 
                 steps.Add(step);
             }
@@ -1617,10 +1648,34 @@ namespace Hotline.FlowEngine.Workflows
         public async Task<bool> CheckCurrentIsStartStepAsync(string workflowId, string userId, string orgId, CancellationToken cancellationToken)
         {
             var workflow = await GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: cancellationToken);
-            //var currentStep = FindCurrentStepWaitForHandle(workflow, userId, orgId);
-            //if (currentStep.StepType is EStepType.End)
-            //    throw new UserFriendlyException("结束节点无需办理");
-            return workflow.Steps.Count == 1;
+            var currentStep = FindCurrentStepWaitForHandle(workflow, userId, orgId);
+            return workflow.Steps.Count == 1 && currentStep.StepType is EStepType.Start && currentStep.IsOrigin;
+        }
+
+        /// <summary>
+        /// 检查动态节点是否该终止
+        /// </summary>
+        public bool DynamicShouldTerminal(StepDefine currentStepDefine, int currentOrgLevel)
+        {
+            if (currentStepDefine.InstanceMode is not EInstanceMode.Dynamic)
+                throw new UserFriendlyException("非动态节点");
+            switch (currentStepDefine.InstancePolicy)
+            {
+                case EDynamicPolicy.OrgUpCenterTop:
+                case EDynamicPolicy.OrgUp:
+                    if (!int.TryParse(currentStepDefine.TerminalDynamicMark, out var tMark))
+                        throw new UserFriendlyException(
+                            $"TerminalDynamicMark parse to int failed, tMark: {currentStepDefine.TerminalDynamicMark}");
+                    return currentOrgLevel <= tMark;
+                case EDynamicPolicy.OrgDownCenterTop:
+                case EDynamicPolicy.OrgDown:
+                    if (!int.TryParse(currentStepDefine.TerminalDynamicMark, out var tMark1))
+                        throw new UserFriendlyException(
+                            $"TerminalDynamicMark parse to int failed, tMark: {currentStepDefine.TerminalDynamicMark}");
+                    return currentOrgLevel >= tMark1;
+                default:
+                    throw new ArgumentOutOfRangeException();
+            }
         }
 
         private WorkflowStep? GetStep(List<WorkflowStep> steps, string orgCode, string userId,
@@ -1641,7 +1696,8 @@ namespace Hotline.FlowEngine.Workflows
             ECountersignPosition countersignPosition,
             DateTime expiredTime,
             string stepName,
-            bool isOrigin
+            bool isOrigin,
+            EHandlerType? handlerType = null//动态节点依据动态策略判断
         )
         {
             if (!handlers.Any())
@@ -1665,6 +1721,9 @@ namespace Hotline.FlowEngine.Workflows
             step.IsOrigin = isOrigin;
             step.Name = stepName;
 
+            if (handlerType.HasValue)
+                step.HandlerType = handlerType.Value;
+
             return step;
         }
 

+ 10 - 3
src/Hotline/Settings/TimeLimits/TimeLimitDomainService.cs

@@ -493,9 +493,16 @@ namespace Hotline.Settings.TimeLimits
                                 }
                                 else if(totalWorkMinutes < workMinutes && totalWorkMinutes != 0)
                                 {
-                                    startTime = startTime.AddDays(day);
-                                    startTime = DateTime.Parse(startTime.ToShortDateString() + " " + workTimeWorkDay.SettingValue[0] + ":00").AddMinutes(totalWorkMinutes);
-                                    totalWorkMinutes = 0;
+                                    if (startTime.AddMinutes(totalWorkMinutes)> DateTime.Parse(startTime.ToShortDateString() + " " + workTimeWorkDay.SettingValue[1] + ":00"))
+                                    {
+                                        startTime = startTime.AddDays(day);
+                                        startTime = DateTime.Parse(startTime.ToShortDateString() + " " + workTimeWorkDay.SettingValue[0] + ":00").AddMinutes(totalWorkMinutes);
+                                        totalWorkMinutes = 0;
+                                    }
+                                    else
+                                    {
+                                        startTime = startTime.AddMinutes(totalWorkMinutes);
+                                    }
                                 }
                                 day = 1;
                             }