Procházet zdrojové kódy

Merge branch 'dev' into dev_dss

Dun.Jason před 1 rokem
rodič
revize
b78dd11c12

+ 76 - 58
src/Hotline.Api/Controllers/Bi/BiOrderController.cs

@@ -40,6 +40,7 @@ namespace Hotline.Api.Controllers.Bi
         private readonly IRepository<OrderDelay> _orderDelayRepository;
         private readonly IMapper _mapper;
         private readonly IRepository<WorkflowCountersign> _workflowCountersignRepository;
+        private readonly IRepository<WorkflowStepHandler> _workflowStepHandleRepository;
         private readonly IRepository<OrderSpecial> _orderSpecialRepository;
         private readonly IRepository<OrderVisit> _orderVisitRepository;
         private readonly IRepository<TrCallRecord> _trCallRecordRepository;
@@ -104,7 +105,7 @@ namespace Hotline.Api.Controllers.Bi
         {
             dto.EndTime = dto.EndTime.AddDays(1).AddSeconds(-1);
 
-            var (total,items) = await _orderRepository.Queryable()
+            var (total, items) = await _orderRepository.Queryable()
             .Where(x => x.CreationTime >= dto.StartTime && x.CreationTime <= dto.EndTime)
             .WhereIF(dto.QueryType == 1, x => x.Status >= EOrderStatus.Filed && x.ExpiredTime < x.FiledTime) //业务已办超期
             //.WhereIF(dto.QueryType== 2,) //会签已办超期
@@ -134,11 +135,11 @@ namespace Hotline.Api.Controllers.Bi
 
 
             var queryOrder = _systemOrganizeRepository.Queryable()
-                .LeftJoin<Order>((x,o)=> x.Id == o.ActualHandleOrgCode)
+                .LeftJoin<Order>((x, o) => x.Id == o.ActualHandleOrgCode)
                 .WhereIF(dto.StartTime.HasValue, (x, o) => o.CreationTime >= dto.StartTime)
                 .WhereIF(dto.EndTime.HasValue, (x, o) => o.CreationTime <= dto.EndTime)
                 .WhereIF(IsCenter == false, (x, o) => o.ActualHandleOrgCode == _sessionContext.RequiredOrgId)
-                .GroupBy((x,o) => new { x.Id, x.Name })
+                .GroupBy((x, o) => new { x.Id, x.Name })
                 .Select((x, o) => new OrderBiOrgDataListVo
                 {
                     OrgName = x.Name,
@@ -807,9 +808,9 @@ namespace Hotline.Api.Controllers.Bi
             if (IsCenter && list != null)
             {
                 data = await list.GroupBy(x => new
-                 {
-                     VisitOrgCode = x.VisitOrgCode.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("6"))
-                 })
+                {
+                    VisitOrgCode = x.VisitOrgCode.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("6"))
+                })
                 .Select(x => new VisitAndOrgSatisfactionStatisticsDto()
                 {
                     OrgCode = x.VisitOrgCode.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("6")),
@@ -843,9 +844,9 @@ namespace Hotline.Api.Controllers.Bi
             else
             {
                 data = await list.GroupBy(x => new
-                 {
-                     x.VisitOrgCode
-                 })
+                {
+                    x.VisitOrgCode
+                })
                 .Select(x => new VisitAndOrgSatisfactionStatisticsDto()
                 {
                     OrgCode = x.VisitOrgCode,
@@ -876,7 +877,7 @@ namespace Hotline.Api.Controllers.Bi
                 })
                 .ToListAsync();
             }
-                
+
 
             var countySumModel = new VisitAndOrgSatisfactionStatisticsDto()
             {
@@ -942,8 +943,8 @@ namespace Hotline.Api.Controllers.Bi
             var list = await _systemOrganizeRepository.Queryable().Where(x => x.Id.StartsWith(OrgCode))
                 .LeftJoin<OrderVisitDetail>((x, it) => x.Id == it.VisitOrgCode)
                 .Where((x, it) => it.OrderVisit.VisitTime >= StartDate && it.OrderVisit.VisitTime <= EndDate && it.VisitTarget == EVisitTarget.Org && it.OrderVisit.VisitState == EVisitState.Visited)
-                .WhereIF(OrgCode == "001", (x,it) => it.VisitOrgCode == OrgCode)
-                .WhereIF(OrgCode != "001", (x,it) => it.VisitOrgCode.StartsWith(OrgCode))
+                .WhereIF(OrgCode == "001", (x, it) => it.VisitOrgCode == OrgCode)
+                .WhereIF(OrgCode != "001", (x, it) => it.VisitOrgCode.StartsWith(OrgCode))
                 .WhereIF(!string.IsNullOrEmpty(LineNum), (x, it) => it.OrderVisit.Order.CallRecord.Gateway.Contains(LineNum))
                 .WhereIF(IsCenter == false, (x, it) => it.VisitOrgCode.StartsWith(_sessionContext.OrgId))
                  .GroupBy((x, it) => new
@@ -1542,7 +1543,7 @@ namespace Hotline.Api.Controllers.Bi
 
 
 
-            //会签
+            //会签(已办超期、待办超期)
             var queryCountersign = _workflowCountersignRepository.Queryable()
                 .LeftJoin<WorkflowCountersignMember>((x, o) => x.Id == o.WorkflowCountersignId)
                  .Where(x => x.CreationTime >= StartDate && x.CreationTime <= EndDate)
@@ -1556,7 +1557,24 @@ namespace Hotline.Api.Controllers.Bi
                     DelayWait = SqlFunc.AggregateSum(SqlFunc.IIF(!o.IsHandled, 1, 0)),
                 }).MergeTable();
 
-
+            _workflowStepHandleRepository.Queryable()
+                .Where(d => d.WorkflowStep.ModuleCode == WorkflowModuleConsts.OrderHandle &&
+                            d.WorkflowStep.CountersignPosition == ECountersignPosition.Multi &&
+                            d.WorkflowStep.CountersignPosition == ECountersignPosition.Single &&
+                            d.WorkflowStep.CreationTime >= StartDate && d.WorkflowStep.CreationTime <= EndDate)
+                .GroupBy(d => new { d.OrgId, d.OrgName })
+                .Select(d => new DepartmentalProcessingStatisticsDataDto
+                {
+                    OrgCode = d.OrgId,
+                    HQYBOverdue =
+                        SqlFunc.AggregateSum(SqlFunc.IIF(
+                            d.WorkflowStep.Status == EWorkflowStepStatus.Handled && d.WorkflowStep.HandleTime > d.WorkflowStep.StepExpiredTime, 1, 0)),
+                    HQZBOverdue =
+                        SqlFunc.AggregateSum(SqlFunc.IIF(
+                            d.WorkflowStep.Status != EWorkflowStepStatus.Handled && DateTime.Now >= d.WorkflowStep.StepExpiredTime, 1, 0)),
+                    DelayEnd = SqlFunc.AggregateSum(SqlFunc.IIF(d.WorkflowStep.Status == EWorkflowStepStatus.Handled, 1, 0)),
+                    DelayWait = SqlFunc.AggregateSum(SqlFunc.IIF(d.WorkflowStep.Status != EWorkflowStepStatus.Handled, 1, 0)),
+                });
 
 
             var queryPush = query.LeftJoin(queryPublish, (it, o) => it.Id == o.Id).Where(it => it.OrgCode != null);
@@ -1613,7 +1631,7 @@ namespace Hotline.Api.Controllers.Bi
                    .Where((p, o) => p.OverTime >= dto.StartDate && p.OverTime <= dto.EndDate)
                    .Where((p, o) => p.CallOrderType == ECallOrderType.Order)
                    .Where((p, o) => p.ExternalId != null && o.Id != null)
-                   .Where((p,o)=>p.CallDirection== ECallDirection.In)
+                   .Where((p, o) => p.CallDirection == ECallDirection.In)
                     .WhereIF(!string.IsNullOrEmpty(dto.PhoneNum), (p, o) => p.CPN == dto.PhoneNum)
                    .Select((p, o) => new
                    {
@@ -1852,26 +1870,26 @@ namespace Hotline.Api.Controllers.Bi
             return rsp;
         }
 
-		/// <summary>
-		/// 派单量统计
-		/// </summary>
-		/// <param name="dto"></param>
-		/// <returns></returns>
-		[HttpGet("send_order_report")]
-		public async Task<object> SendOrderReport([FromQuery] QuerySendOrderRequest dto)
+        /// <summary>
+        /// 派单量统计
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpGet("send_order_report")]
+        public async Task<object> SendOrderReport([FromQuery] QuerySendOrderRequest dto)
         {
             if (!dto.StartTime.HasValue || !dto.EndTime.HasValue)
                 throw UserFriendlyException.SameMessage("请选择时间!");
             dto.EndTime = dto.EndTime.Value.AddDays(1).AddSeconds(-1);
-			var items = await _workflowTraceRepository.Queryable()
+            var items = await _workflowTraceRepository.Queryable()
                 .LeftJoin<Workflow>((x, w) => x.WorkflowId == w.Id)
                 .LeftJoin<WorkflowStepHandler>((x, w, wsh) => x.StepId == wsh.WorkflowStepId)
                 .InnerJoin<SchedulingUser>((x, w, wsh, su) => wsh.UserId == su.UserId)
                 .Where((x, w, wsh, su) => w.ModuleCode == "OrderHandle" && x.BusinessType == EBusinessType.Send)
-                .Where((x, w, wsh, su) =>x.CreationTime >= dto.StartTime.Value)
+                .Where((x, w, wsh, su) => x.CreationTime >= dto.StartTime.Value)
                 .Where((x, w, wsh, su) => x.CreationTime <= dto.EndTime.Value)
-                .WhereIF(!string.IsNullOrEmpty(dto.UserName),(x, w, wsh, su) => su.UserName == dto.UserName)
-				.GroupBy((x, w, wsh, su) => new { su.UserId, su.UserName})
+                .WhereIF(!string.IsNullOrEmpty(dto.UserName), (x, w, wsh, su) => su.UserName == dto.UserName)
+                .GroupBy((x, w, wsh, su) => new { su.UserId, su.UserName })
                 .Having((x, w, wsh, su) => SqlFunc.AggregateCount(x.WorkflowId) == 1)
                 .Select((x, w, wsh, su) => new BiOrderSendVo
                 {
@@ -1881,40 +1899,40 @@ namespace Hotline.Api.Controllers.Bi
                     NoSendOrderNum = SqlFunc.AggregateSum(SqlFunc.IIF(x.HandlerId == null || x.HandlerId == "", 1, 0)),
                     ReSendOrderNum = 0,
                 }).ToListAsync();
-            var items2 =  await _workflowTraceRepository.Queryable()
-	            .LeftJoin<Workflow>((x, w) => x.WorkflowId == w.Id)
-	            .LeftJoin<WorkflowStepHandler>((x, w, wsh) => x.StepId == wsh.WorkflowStepId)
-	            .InnerJoin<SchedulingUser>((x, w, wsh, su) => wsh.UserId == su.UserId)
-	            .Where((x, w, wsh, su) => w.ModuleCode == "OrderHandle" && x.BusinessType == EBusinessType.Send)
-	            .Where((x, w, wsh, su) => x.CreationTime >= dto.StartTime.Value)
-	            .Where((x, w, wsh, su) => x.CreationTime <= dto.EndTime.Value)
-	            .WhereIF(!string.IsNullOrEmpty(dto.UserName), (x, w, wsh, su) => su.UserName == dto.UserName)
-				.GroupBy((x, w, wsh, su) => new { su.UserId, su.UserName })
-	            .Having((x, w, wsh, su) => SqlFunc.AggregateCount(x.WorkflowId) > 1)
-	            .Select((x, w, wsh, su) => new BiOrderSendVo
-	            {
-		            UserId = su.UserId,
-		            UserName = su.UserName,
-		            SendOrderNum = 0,
-		            NoSendOrderNum = 0,
-		            ReSendOrderNum = SqlFunc.AggregateSum(SqlFunc.IIF(true, 1, 0)),
-				}).ToListAsync();
+            var items2 = await _workflowTraceRepository.Queryable()
+                .LeftJoin<Workflow>((x, w) => x.WorkflowId == w.Id)
+                .LeftJoin<WorkflowStepHandler>((x, w, wsh) => x.StepId == wsh.WorkflowStepId)
+                .InnerJoin<SchedulingUser>((x, w, wsh, su) => wsh.UserId == su.UserId)
+                .Where((x, w, wsh, su) => w.ModuleCode == "OrderHandle" && x.BusinessType == EBusinessType.Send)
+                .Where((x, w, wsh, su) => x.CreationTime >= dto.StartTime.Value)
+                .Where((x, w, wsh, su) => x.CreationTime <= dto.EndTime.Value)
+                .WhereIF(!string.IsNullOrEmpty(dto.UserName), (x, w, wsh, su) => su.UserName == dto.UserName)
+                .GroupBy((x, w, wsh, su) => new { su.UserId, su.UserName })
+                .Having((x, w, wsh, su) => SqlFunc.AggregateCount(x.WorkflowId) > 1)
+                .Select((x, w, wsh, su) => new BiOrderSendVo
+                {
+                    UserId = su.UserId,
+                    UserName = su.UserName,
+                    SendOrderNum = 0,
+                    NoSendOrderNum = 0,
+                    ReSendOrderNum = SqlFunc.AggregateSum(SqlFunc.IIF(true, 1, 0)),
+                }).ToListAsync();
 
             var res = (from t1 in items
-	            join t2 in items2 on t1.UserId equals t2.UserId into t1_t2
-	            from item in t1_t2.DefaultIfEmpty()
-	            select new
-	            {
-		            UserId = t1.UserId,
-		            UserName = t1.UserName,
-		            SendOrderNum = t1.SendOrderNum,
-		            NoSendOrderNum = t1.NoSendOrderNum,
-                    ReSendOrderNum = t1_t2.Select(x => x.NoSendOrderNum).FirstOrDefault(),
-                    ChainRate = t1_t2.Select(x => x.NoSendOrderNum).FirstOrDefault() > 0 ?
-                              ((double.Parse(t1.SendOrderNum.ToString()) - double.Parse(t1_t2.Select(x => x.NoSendOrderNum).FirstOrDefault().ToString())) / double.Parse(t1.SendOrderNum.ToString()) * 100).ToString("F2") + "%" : "100.00%",
-                }).ToList();
-
-			return res; 
+                       join t2 in items2 on t1.UserId equals t2.UserId into t1_t2
+                       from item in t1_t2.DefaultIfEmpty()
+                       select new
+                       {
+                           UserId = t1.UserId,
+                           UserName = t1.UserName,
+                           SendOrderNum = t1.SendOrderNum,
+                           NoSendOrderNum = t1.NoSendOrderNum,
+                           ReSendOrderNum = t1_t2.Select(x => x.ReSendOrderNum).FirstOrDefault(),
+                           ChainRate = t1_t2.Select(x => x.ReSendOrderNum).FirstOrDefault() > 0 ?
+                                     ((double.Parse(t1.SendOrderNum.ToString()) - double.Parse(t1_t2.Select(x => x.ReSendOrderNum).FirstOrDefault().ToString())) / double.Parse(t1.SendOrderNum.ToString()) * 100).ToString("F2") + "%" : "100.00%",
+                       }).ToList();
+
+            return res;
         }
 
 

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

@@ -1453,15 +1453,15 @@ public class OrderController : BaseController
 			.WhereIF(!string.IsNullOrEmpty(dto.AcceptType), x => x.OrderVisit.Order!.AcceptTypeCode! == dto.AcceptType!)
             .WhereIF(dto.CounterSignType.HasValue ,x=> x.OrderVisit.Order!.CounterSignType == dto.CounterSignType)
             .WhereIF(!string.IsNullOrEmpty(dto.OrgLevelOneName), x => x.OrderVisit.Order!.OrgLevelOneName!.Contains(dto.OrgLevelOneName!))
-            .WhereIF(!string.IsNullOrEmpty(dto.ActualHandleStepName), x => x.OrderVisit.Order!.ActualHandleStepName!.Contains(dto.ActualHandleStepName!))
+            .WhereIF(!string.IsNullOrEmpty(dto.ActualHandleOrgName), x => x.OrderVisit.Order!.ActualHandleOrgName!.Contains(dto.ActualHandleOrgName!))
             .WhereIF(dto.ActualHandleTime.HasValue && dto.EndActualHandleTime.HasValue, x => x.OrderVisit.Order!.ActualHandleTime >= dto.ActualHandleTime && x.OrderVisit.Order!.ActualHandleTime <= dto.EndActualHandleTime)
             .WhereIF(dto.FiledTime.HasValue && dto.EndFiledTime.HasValue, x => x.OrderVisit.Order!.FiledTime == dto.FiledTime && x.OrderVisit.Order!.FiledTime <= dto.EndFiledTime)
             .WhereIF(dto.CreationTime.HasValue && dto.EndCreationTime.HasValue, x => x.OrderVisit.Order!.CreationTime == dto.CreationTime && x.OrderVisit.Order!.CreationTime <= dto.EndCreationTime)
             .WhereIF(dto.VisitTime.HasValue && dto.EndVisitTime.HasValue, x => x.OrderVisit.VisitTime == dto.VisitTime && x.OrderVisit.VisitTime <= dto.EndVisitTime)
             .WhereIF(!string.IsNullOrEmpty(dto.VisitOrgName), x => x.VisitOrgName!.Contains(dto.VisitOrgName!))
-            .WhereIF(!string.IsNullOrEmpty(dto.OrgProcessingResults), x => SqlFunc.JsonListObjectAny(x.OrgProcessingResults, "Key", dto.OrgProcessingResults))
+            .WhereIF(!string.IsNullOrEmpty(dto.OrgProcessingResults), x => SqlFunc.JsonField(x.OrgProcessingResults, "Key")== dto.OrgProcessingResults)
             .WhereIF(!string.IsNullOrEmpty(dto.OrgHandledAttitude), x => SqlFunc.JsonListObjectAny(x.OrgHandledAttitude, "Key", dto.OrgHandledAttitude))
-            .WhereIF(!string.IsNullOrEmpty(dto.OrgNoSatisfiedReason), x => SqlFunc.JsonListObjectAny(x.OrgNoSatisfiedReason, "Key", dto.OrgNoSatisfiedReason))
+            .WhereIF(!string.IsNullOrEmpty(dto.OrgNoSatisfiedReason), x => SqlFunc.JsonField(x.OrgNoSatisfiedReason, "Key") == dto.OrgNoSatisfiedReason)
 			.Where((x, s) => x.OrderVisit.VisitState != EVisitState.None && x.OrderVisit.IsCanHandle);
 		if (_sessionContext.OrgId != null && !_sessionContext.OrgIsCenter)
         {
@@ -2510,7 +2510,19 @@ public class OrderController : BaseController
                 NearlyExpiredTime = timeResult.NearlyExpiredTime
             };
         }
-        else
+        else if (dto.Workflow.FlowDirection == EFlowDirection.CenterToCenter)
+        {
+	        var timeResult = _timeLimitDomainService.CalcEndTime(DateTime.Now, ETimeType.WorkDay, 1, 0);
+	        expiredTimeConfig = new ExpiredTimeWithConfig
+	        {
+		        Count = 1,
+		        TimeType = ETimeType.WorkDay,
+		        TimeText = "1个工作日",
+		        ExpiredTime = timeResult.EndTime,
+		        NearlyExpiredTime = timeResult.NearlyExpiredTime
+	        };
+        }
+		else
         {
             //期满时间
             expiredTimeConfig = _timeLimitDomainService.CalcExpiredTime(DateTime.Now, EFlowDirection.CenterToOrg, dto.Data.AcceptTypeCode);
@@ -3406,18 +3418,20 @@ public class OrderController : BaseController
 				NextHandlers = dto.NextHandlers,
 				Opinion = dto.Cause
 			};
-			var expiredTime = _timeLimitDomainService.CalcEndTime(DateTime.Now,
-				ETimeType.WorkDay,
-				dto.TimeLimit.Value, order.AcceptTypeCode);
+            DateTime endTime = order.ExpiredTime!.Value;
 			// 计算期满时间
 			if (dto.AlterTime)
             {
+	            var expiredTime = _timeLimitDomainService.CalcEndTime(DateTime.Now,
+		            ETimeType.WorkDay,
+		            dto.TimeLimit.Value, order.AcceptTypeCode);
+                endTime = expiredTime.EndTime;
 				await _orderRepository.Updateable().SetColumns(o => new Order() { ExpiredTime = expiredTime.EndTime, NearlyExpiredTime = expiredTime.NearlyExpiredTime })
                     .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
 				var orderDto = _mapper.Map<OrderDto>(order);
 	            await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto,cancellationToken: HttpContext.RequestAborted);
             }
-            await _workflowApplication.RecallAsync(recall, expiredTime.EndTime, HttpContext.RequestAborted);
+            await _workflowApplication.RecallAsync(recall, endTime, HttpContext.RequestAborted);
 			var publish = await _orderPublishRepository.GetAsync(x => x.OrderId == dto.OrderId);
 			if (publish != null)
 			{

+ 1 - 1
src/Hotline.Application/FlowEngine/WorkflowApplication.cs

@@ -181,7 +181,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         //starttrace
         var startTrace = _mapper.Map<WorkflowTrace>(startStep);
         startTrace.StepId = startStep.Id;
-        startTrace.Status = EWorkflowTraceStatus.Normal;
+        startTrace.TraceType = EWorkflowTraceType.Normal;
         _mapper.Map(dto, startTrace);
         await _workflowTraceRepository.AddAsync(startTrace, cancellationToken);
         workflow.Traces.Add(startTrace);

+ 6 - 8
src/Hotline.Application/Mappers/WorkflowMapperConfigs.cs

@@ -41,10 +41,11 @@ public class WorkflowMapperConfigs : IRegister
             ;
 
         config.ForType<WorkflowStep, WorkflowTrace>()
-            .Ignore(d => d.Id)
             .Ignore(d => d.ParentId)
-            .Ignore(d => d.Status)
-            .Map(d => d.StepId, s => s.Id);
+            .Ignore(d => d.TraceType)
+            .Map(d => d.StepId, s => s.Id)
+            .AfterMapping((s, d) => d.Id = s.Id)
+            ;
 
         config.ForType<WorkflowSupplement, WorkflowSupplementDto>()
             .Map(d => d.CreationTime, x => x.CreationTime)
@@ -79,15 +80,12 @@ public class WorkflowMapperConfigs : IRegister
 
         config.ForType<Workflow, WorkflowStep>()
             .Map(d => d.WorkflowId, s => s.Id)
+            .Map(d => d.ExternalId, s => s.ExternalId)
             .Map(d => d.ModuleId, s => s.ModuleId)
             .Map(d => d.ModuleCode, s => s.ModuleCode)
             .Map(d => d.ModuleName, s => s.ModuleName)
             .IgnoreNonMapped(true)
             ;
-
-        config.ForType<BasicWorkflowDto, WorkflowStep>()
-            .Ignore(d => d.BusinessType)
-            .Ignore(d => d.HandlerType)
-            ;
+        
     }
 }

+ 2 - 2
src/Hotline.Share/Dtos/FlowEngine/Workflow/WorkflowTraceDto.cs

@@ -17,9 +17,9 @@ public class WorkflowTraceDto : StepBasicDto
     /// <summary>
     /// 流转记录状态
     /// </summary>
-    public EWorkflowTraceStatus Status { get; set; }
+    public EWorkflowTraceType Type { get; set; }
     
-    public string StatusText => Status.GetDescription();
+    public string StatusText => Type.GetDescription();
 
     /// <summary>
     /// 过期时间(生成流转记录时取值当前workflow的过期时间)

+ 1 - 1
src/Hotline.Share/Dtos/Order/QueryOrderDto.cs

@@ -567,7 +567,7 @@ namespace Hotline.Share.Dtos.Order
         /// <summary>
         /// 实际办理节点名称(会签状态此字段保存最外层会签发起节点名称)
         /// </summary>
-        public string? ActualHandleStepName { get; set; }
+        public string? ActualHandleOrgName { get; set; }
 
         /// <summary>
         /// 实际办理时间

+ 1 - 1
src/Hotline.Share/Enums/FlowEngine/EWorkflowTraceStatus.cs → src/Hotline.Share/Enums/FlowEngine/EWorkflowTraceType.cs

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
 
 namespace Hotline.Share.Enums.FlowEngine
 {
-    public enum EWorkflowTraceStatus
+    public enum EWorkflowTraceType
     {
         ///// <summary>
         ///// 流程开始

+ 86 - 1
src/Hotline/FlowEngine/Workflows/StepBasicEntity.cs

@@ -1,5 +1,7 @@
 using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.File;
+using Hotline.Share.Dtos.FlowEngine.Definition;
+using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.Order;
 using SqlSugar;
@@ -12,7 +14,7 @@ public abstract class StepBasicEntity : CreationEntity
 {
     public string WorkflowId { get; set; }
 
-    public string ExternalId { get; set; }
+    public string? ExternalId { get; set; }
 
     #region 业务模块(冗余)
 
@@ -52,6 +54,89 @@ public abstract class StepBasicEntity : CreationEntity
     /// </summary>
     public EExpiredStatus? ExpiredStatus { get; set; }
 
+    /// <summary>
+    /// 配置下一步节点 & 谁被选中
+    /// </summary>
+    [SugarColumn(ColumnDataType = "json", IsJson = true)]
+    public List<StepSimple>? NextSteps { get; set; } = new();
+
+    /// <summary>
+    /// 前一级节点Id,会签汇总节点无此字段(因可能有多个上级来源)
+    /// </summary>
+    public string? PrevStepId { get; set; }
+
+    public string? PrevStepCode { get; set; }
+
+    /// <summary>
+    /// 主办
+    /// </summary>
+    [SugarColumn(DefaultValue = "f")]
+    public bool IsMain { get; set; }
+
+    /// <summary>
+    /// 原生节点(区别动态生成)
+    /// </summary>
+    [SugarColumn(DefaultValue = "f")]
+    public bool IsOrigin { get; set; }
+
+    /// <summary>
+    /// 节点办理状态
+    /// </summary>
+    public EWorkflowStepStatus Status { get; set; }
+
+    #region 会签
+
+    /// <summary>
+    /// 会签id(或外层会签的id)
+    /// </summary>
+    public string? CountersignId { get; set; }
+
+    /// <summary>
+    /// 节点处于会签流程中的位置(区别直接办理会签和会签内非会签节点)
+    /// outer属于特殊会签
+    /// 最顶层的发起会签节点为none
+    /// </summary>
+    [SugarColumn(DefaultValue = "0")]
+    public ECountersignPosition CountersignPosition { get; set; }
+
+    /// <summary>
+    /// 会签直属办理节点(弃用)
+    /// </summary>
+    [SugarColumn(ColumnDataType = "json", IsJson = true)]
+    public List<CountersignStep>? CountersignSteps { get; set; } = new();
+
+    #region 发起会签节点特有
+
+    /// <summary>
+    /// 发起会签生成会签Id(不发起会签节点无此字段)
+    /// </summary>
+    public string? StartCountersignId { get; set; }
+
+    /// <summary>
+    /// 发起的会签是否汇总
+    /// </summary>
+    [SugarColumn(DefaultValue = "f")]
+    public bool IsStartedCountersignEnd { get; set; }
+
+    #endregion
+
+    #region 会签汇总节点特有
+
+    /// <summary>
+    /// 是否为会签汇总节点
+    /// </summary>
+    [SugarColumn(DefaultValue = "f")]
+    public bool IsCountersignEndStep { get; set; }
+
+    /// <summary>
+    /// 开启会签节点id(会签汇总节点对应会签发起节点id)
+    /// </summary>
+    public string? CountersignStartStepId { get; set; }
+
+    #endregion
+
+    #endregion
+    
     #region 接办
 
     /// <summary>

+ 51 - 42
src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs

@@ -324,7 +324,7 @@ namespace Hotline.FlowEngine.Workflows
                 currentStep.FileJson = await _fileRepository.AddFileAsync(dto.Files, workflow.ExternalId,
                     currentStep.Id, cancellationToken);
 
-            var isStartCountersign = currentStep.IsInCountersign() || dto.IsStartCountersign;
+            var isStartCountersign = (currentStep.IsInCountersign() && !dto.BackToCountersignEnd) || dto.IsStartCountersign;
 
             var counterSignType = GetCounterSignType(currentStep.BusinessType);
 
@@ -365,6 +365,17 @@ namespace Hotline.FlowEngine.Workflows
             await HandleStepAsync(currentStep, workflow, dto, flowAssignInfo.FlowAssignType,
                 counterSignType, expiredTime, cancellationToken);
 
+            //创建会签数据
+            if (isStartCountersign)
+            {
+                var exists = workflow.Countersigns.Any(d =>
+                    !d.IsCompleted() && d.StarterId == _sessionContext.RequiredUserId);
+                if (exists)
+                    throw new UserFriendlyException("该用户在当前流程存在未结束会签");
+                await StartCountersignAsync(workflow, currentStep, dto, flowAssignInfo.FlowAssignType,
+                    counterSignType, expiredTime, cancellationToken);
+            }
+
             currentStep.IsActualHandled = CheckIsActualHandle(workflow, currentStep, nextStepDefine, dto);
 
             _mapper.Map(dto, workflow);
@@ -445,17 +456,6 @@ namespace Hotline.FlowEngine.Workflows
                 return;
             }
 
-            //创建会签数据
-            if (isStartCountersign)
-            {
-                var exists = workflow.Countersigns.Any(d =>
-                    !d.IsCompleted() && d.StarterId == _sessionContext.RequiredUserId);
-                if (exists)
-                    throw new UserFriendlyException("该用户在当前流程存在未结束会签");
-                await StartCountersignAsync(workflow, currentStep, dto, flowAssignInfo.FlowAssignType,
-                    counterSignType, expiredTime, cancellationToken);
-            }
-
             //创建下一/N个节点(会签汇总节点:会签未全部办理时不创建,最后一个会签办理节点创建会签汇总节点)
             var nextSteps = await CreateNextStepsAsync(workflow, currentStep, dto,
                 nextStepDefine, isNextDynamic, flowAssignInfo, expiredTime, stepHandlers, isStartCountersign,
@@ -560,8 +560,8 @@ namespace Hotline.FlowEngine.Workflows
             var trace = await PreviousTraceAsync(workflow.Id, dto, currentStep, cancellationToken);
 
             //复制上一个节点为待接办
-            var newPrevStep =
-                await DuplicateStepWithTraceAsync(workflow, prevStep, EWorkflowTraceStatus.Previous, cancellationToken);
+            var newPrevStep = await DuplicateStepWithTraceAsync(workflow, prevStep, EWorkflowTraceType.Previous,
+                cancellationToken);
 
             //remove workflow.steps
             await _workflowStepRepository.RemoveRangeAsync(removeSteps, cancellationToken);
@@ -750,7 +750,7 @@ namespace Hotline.FlowEngine.Workflows
             await RecallTraceAsync(workflow.Id, dto.Opinion, cancellationToken);
 
             var isOrgToCenter = await RecallAsync(workflow, dto, flowAssignInfo, targetStepDefine, targetStep,
-                EWorkflowTraceStatus.Recall, stepHandlers, expiredTime, cancellationToken);
+                EWorkflowTraceType.Recall, stepHandlers, expiredTime, cancellationToken);
 
             workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
 
@@ -780,7 +780,7 @@ namespace Hotline.FlowEngine.Workflows
                 workflow.SetStatusRunnable();
 
             var newStartStep =
-                await DuplicateStepWithTraceAsync(workflow, startStep, EWorkflowTraceStatus.Recall, cancellationToken);
+                await DuplicateStepWithTraceAsync(workflow, startStep, EWorkflowTraceType.Recall, cancellationToken);
 
             //更新当前办理节点信息
             //workflow.UpdateWorkflowCurrentStepInfo(false,
@@ -1238,7 +1238,7 @@ namespace Hotline.FlowEngine.Workflows
         }
 
         private async Task<WorkflowStep> CreateStartStepAsync(Workflow workflow, StepDefine startStepDefine,
-            BasicWorkflowDto dto, List<Kv> handles, List<WorkflowStepHandler> stepHandlers, EWorkflowTraceStatus traceStatus,
+            BasicWorkflowDto dto, List<Kv> handles, List<WorkflowStepHandler> stepHandlers, EWorkflowTraceType traceType,
             DateTime? expiredTime, CancellationToken cancellationToken)
         {
             var startStep = CreateStartStep(workflow, startStepDefine, dto, handles, stepHandlers, expiredTime);
@@ -1246,7 +1246,7 @@ namespace Hotline.FlowEngine.Workflows
             await _workflowStepRepository.AddNav(startStep)
                 .Include(d => d.StepHandlers)
                 .ExecuteCommandAsync();
-            await CreateTraceAsync(workflow, startStep, traceStatus, cancellationToken);
+            await CreateTraceAsync(workflow, startStep, traceType, cancellationToken);
             return startStep;
         }
 
@@ -1291,18 +1291,27 @@ namespace Hotline.FlowEngine.Workflows
                         {
                             //创建普通节点(根据配置)
                             nextSteps = await CreateConfigStepsAsync(workflow, nextStepDefine, currentStep, dto,
-                                flowAssignInfo, EWorkflowTraceStatus.Normal, expiredTime, stepHandlers, cancellationToken);
+                                flowAssignInfo, EWorkflowTraceType.Normal, expiredTime, stepHandlers, cancellationToken);
                         }
                     }
                     else
                     {
-                        // csStartStep.prev
-                        var csStartStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
-                        if (csStartStep is null)
-                            throw new UserFriendlyException("未查询到会签节点");
+                        if (dto.BackToCountersignEnd)
+                        {
+                            // csStartStep.prev
+                            var csStartStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
+                            if (csStartStep is null)
+                                throw new UserFriendlyException("未查询到会签节点");
 
-                        nextSteps = await CreateCsEndStepsByTargetPrevAsync(workflow, csStartStep, dto, stepHandlers,
-                            expiredTime, cancellationToken);
+                            nextSteps = await CreateCsEndStepsByTargetPrevAsync(workflow, csStartStep, dto, stepHandlers,
+                                expiredTime, cancellationToken);
+                        }
+                        else
+                        {
+                            //依据会签策略创建会签下一级节点
+                            nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
+                                flowAssignInfo.FlowAssignType, expiredTime, stepHandlers, isStartCountersign, cancellationToken);
+                        }
                     }
                 }
                 else
@@ -1337,7 +1346,7 @@ namespace Hotline.FlowEngine.Workflows
             {
                 //创建普通节点(根据配置)
                 nextSteps = await CreateConfigStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo,
-                    EWorkflowTraceStatus.Normal, expiredTime, stepHandlers, cancellationToken);
+                    EWorkflowTraceType.Normal, expiredTime, stepHandlers, cancellationToken);
             }
 
             return nextSteps;
@@ -1365,7 +1374,7 @@ namespace Hotline.FlowEngine.Workflows
 
             return await CreateStepsAsync(workflow, nextStepDefine, prevStep, dto, dto.IsStartCountersign,
                 flowAssignInfo.FlowAssignType, dto.NextHandlers, stepHandlers, null, EWorkflowStepStatus.WaitForAccept,
-                ECountersignPosition.None, false, EWorkflowTraceStatus.Normal, handlerType, expiredTime,
+                ECountersignPosition.None, false, EWorkflowTraceType.Normal, handlerType, expiredTime,
                 cancellationToken: cancellationToken);
         }
 
@@ -1400,7 +1409,7 @@ namespace Hotline.FlowEngine.Workflows
 
             return CreateStepsAsync(workflow, stepDefine, prevStep, dto, isStartCountersign, flowAssignType, dto.NextHandlers,
                 stepHandlers, countersignId, EWorkflowStepStatus.WaitForAccept, nextStepCountersignPosition,
-                false, EWorkflowTraceStatus.Normal, handlerType, expiredTime, cancellationToken: cancellationToken);
+                false, EWorkflowTraceType.Normal, handlerType, expiredTime, cancellationToken: cancellationToken);
         }
 
         /// <summary>
@@ -1424,7 +1433,7 @@ namespace Hotline.FlowEngine.Workflows
                 nextSteps = new List<WorkflowStep> { countersignEndStep };
 
                 //create trace
-                await CreateTraceAsync(workflow, countersignEndStep, EWorkflowTraceStatus.Normal, cancellationToken);
+                await CreateTraceAsync(workflow, countersignEndStep, EWorkflowTraceType.Normal, cancellationToken);
 
                 await _mediator.Publish(new CountersignEndAssigned(workflow), cancellationToken);
             }
@@ -1577,7 +1586,7 @@ namespace Hotline.FlowEngine.Workflows
         /// 复制一个节点为待接办
         /// </summary>
         private async Task<WorkflowStep> DuplicateStepWithTraceAsync(Workflow workflow, WorkflowStep step,
-            EWorkflowTraceStatus traceStatus, CancellationToken cancellationToken)
+            EWorkflowTraceType traceType, CancellationToken cancellationToken)
         {
             var newStep = _mapper.Map<WorkflowStep>(step);
             newStep.Reset();
@@ -1591,9 +1600,9 @@ namespace Hotline.FlowEngine.Workflows
             newStep.StartCountersignId = step.StartCountersignId;
             newStep.CountersignId = step.CountersignId;
             newStep.IsStartedCountersignEnd = step.IsStartedCountersignEnd;
-            //await _workflowStepRepository.AddAsync(newStep, cancellationToken);
+            await _workflowStepRepository.AddAsync(newStep, cancellationToken);
 
-            await CreateTraceAsync(workflow, newStep, traceStatus, cancellationToken);
+            await CreateTraceAsync(workflow, newStep, traceType, cancellationToken);
 
             return newStep;
         }
@@ -1720,11 +1729,11 @@ namespace Hotline.FlowEngine.Workflows
         }
 
         private async Task CreateTraceAsync(Workflow workflow, WorkflowStep step,
-            EWorkflowTraceStatus traceStatus = EWorkflowTraceStatus.Normal,
+            EWorkflowTraceType traceType = EWorkflowTraceType.Normal,
             CancellationToken cancellationToken = default)
         {
             var trace = _mapper.Map<WorkflowTrace>(step);
-            trace.Status = traceStatus;
+            trace.TraceType = traceType;
 
             if (step.IsInCountersign())
             {
@@ -1765,7 +1774,7 @@ namespace Hotline.FlowEngine.Workflows
         }
 
         private async Task<bool> RecallAsync(Workflow workflow, BasicWorkflowDto dto, FlowAssignInfo flowAssignInfo,
-            StepDefine targetStepDefine, WorkflowStep targetStep, EWorkflowTraceStatus traceStatus,
+            StepDefine targetStepDefine, WorkflowStep targetStep, EWorkflowTraceType traceType,
             List<WorkflowStepHandler> stepHandlers, DateTime? expiredTime, CancellationToken cancellationToken)
         {
             var targetIsStartStep = targetStepDefine.StepType is EStepType.Start;
@@ -1796,11 +1805,11 @@ namespace Hotline.FlowEngine.Workflows
                 workflow.SetStatusRunnable();
 
             var targetStepNew = targetIsStartStep
-                ? await CreateStartStepAsync(workflow, targetStepDefine, dto, dto.NextHandlers, stepHandlers, traceStatus, expiredTime,
+                ? await CreateStartStepAsync(workflow, targetStepDefine, dto, dto.NextHandlers, stepHandlers, traceType, expiredTime,
                     cancellationToken)
                 : (await CreateStepsAsync(workflow, targetStepDefine, targetPrevStep, dto, false,
                     flowAssignInfo.FlowAssignType, dto.NextHandlers, stepHandlers,
-                    null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, true, traceStatus,
+                    null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, true, traceType,
                     null, expiredTime, cancellationToken: cancellationToken)).First();
 
 
@@ -1882,7 +1891,7 @@ namespace Hotline.FlowEngine.Workflows
             WorkflowStep prevStep,
             BasicWorkflowDto dto,
             FlowAssignInfo flowAssignInfo,
-            EWorkflowTraceStatus traceStatus,
+            EWorkflowTraceType traceType,
             DateTime? expiredTime,
             List<WorkflowStepHandler> stepHandlers,
             CancellationToken cancellationToken)
@@ -1909,7 +1918,7 @@ namespace Hotline.FlowEngine.Workflows
             return await CreateStepsAsync(workflow, stepDefine, prevStep, dto, dto.IsStartCountersign,
                 flowAssignInfo.FlowAssignType, handlers, stepHandlers, null,
                 EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None,
-                true, traceStatus, null, expiredTime, cancellationToken);
+                true, traceType, null, expiredTime, cancellationToken);
         }
 
         private async Task<List<WorkflowStep>> CreateStepsAsync(
@@ -1925,7 +1934,7 @@ namespace Hotline.FlowEngine.Workflows
             EWorkflowStepStatus stepStatus,
             ECountersignPosition csPosition,
             bool isOrigin,
-            EWorkflowTraceStatus traceStatus,
+            EWorkflowTraceType traceType,
             EHandlerType? handlerType = null,
             DateTime? expiredTime = null,
             CancellationToken cancellationToken = default
@@ -1964,7 +1973,7 @@ namespace Hotline.FlowEngine.Workflows
             //create traces todo add range traces
             foreach (var step in steps)
             {
-                await CreateTraceAsync(workflow, step, traceStatus, cancellationToken);
+                await CreateTraceAsync(workflow, step, traceType, cancellationToken);
             }
 
             return steps;
@@ -2076,7 +2085,7 @@ namespace Hotline.FlowEngine.Workflows
 
                 //cp会签发起节点变为待办节点
                 //1. create terminal trace 2. 撤回至startStep
-                var newStep = await DuplicateStepWithTraceAsync(workflow, startCountersignStep, EWorkflowTraceStatus.Normal,
+                var newStep = await DuplicateStepWithTraceAsync(workflow, startCountersignStep, EWorkflowTraceType.Normal,
                     cancellationToken);
 
                 //当topcsStep结束cs时,实际办理节点应该更新为newStep

+ 0 - 78
src/Hotline/FlowEngine/Workflows/WorkflowStep.cs

@@ -10,84 +10,6 @@ namespace Hotline.FlowEngine.Workflows;
 
 public class WorkflowStep : StepBasicEntity
 {
-    /// <summary>
-    /// 配置下一步节点 & 谁被选中
-    /// </summary>
-    [SugarColumn(ColumnDataType = "json", IsJson = true)]
-    public List<StepSimple> NextSteps { get; set; } = new();
-
-    /// <summary>
-    /// 前一级节点Id,会签汇总节点无此字段(因可能有多个上级来源)
-    /// </summary>
-    public string? PrevStepId { get; set; }
-
-    public string? PrevStepCode { get; set; }
-
-    /// <summary>
-    /// 主办
-    /// </summary>
-    public bool IsMain { get; set; }
-
-    /// <summary>
-    /// 原生节点(区别动态生成)
-    /// </summary>
-    public bool IsOrigin { get; set; }
-
-    /// <summary>
-    /// 节点办理状态
-    /// </summary>
-    public EWorkflowStepStatus Status { get; set; }
-
-    #region 会签
-
-    /// <summary>
-    /// 会签id(或外层会签的id)
-    /// </summary>
-    public string? CountersignId { get; set; }
-
-    /// <summary>
-    /// 节点处于会签流程中的位置(区别直接办理会签和会签内非会签节点)
-    /// outer属于特殊会签
-    /// 最顶层的发起会签节点为none
-    /// </summary>
-    public ECountersignPosition CountersignPosition { get; set; }
-
-    /// <summary>
-    /// 会签直属办理节点(弃用)
-    /// </summary>
-    [SugarColumn(ColumnDataType = "json", IsJson = true)]
-    public List<CountersignStep>? CountersignSteps { get; set; } = new();
-
-    #region 发起会签节点特有
-
-    /// <summary>
-    /// 发起会签生成会签Id(不发起会签节点无此字段)
-    /// </summary>
-    public string? StartCountersignId { get; set; }
-
-    /// <summary>
-    /// 发起的会签是否汇总
-    /// </summary>
-    public bool IsStartedCountersignEnd { get; set; }
-
-    #endregion
-
-    #region 会签汇总节点特有
-
-    /// <summary>
-    /// 是否为会签汇总节点
-    /// </summary>
-    public bool IsCountersignEndStep { get; set; }
-
-    /// <summary>
-    /// 开启会签节点id(会签汇总节点对应会签发起节点id)
-    /// </summary>
-    public string? CountersignStartStepId { get; set; }
-
-    #endregion
-
-    #endregion
-
     [Navigate(NavigateType.ManyToOne, nameof(WorkflowId))]
     public Workflow Workflow { get; set; }
 

+ 4 - 4
src/Hotline/FlowEngine/Workflows/WorkflowTrace.cs

@@ -15,14 +15,14 @@ public class WorkflowTrace : StepBasicEntity
     public string StepId { get; set; }
 
     /// <summary>
-    /// 流转记录状态
+    /// 会签从属关系
     /// </summary>
-    public EWorkflowTraceStatus Status { get; set; }
+    public string? ParentId { get; set; }
 
     /// <summary>
-    /// 会签从属关系
+    /// 流转记录状态
     /// </summary>
-    public string? ParentId { get; set; }
+    public EWorkflowTraceType? TraceType { get; set; }
 
     /// <summary>
     /// 会签流转记录