浏览代码

Merge branch 'master' into tangj-knowledge

xf 2 年之前
父节点
当前提交
e1491c9b8c

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

@@ -202,7 +202,7 @@ public class OrderController : BaseController
         dto.DefinitionCode = definition.Code;
         dto.DefinitionCode = definition.Code;
         dto.Title = order.Title;
         dto.Title = order.Title;
         var workflowId = await _workflowApplication.StartWorkflowAsync(dto, HttpContext.RequestAborted);
         var workflowId = await _workflowApplication.StartWorkflowAsync(dto, HttpContext.RequestAborted);
-        order.StartFlow(workflowId);
+        order.StartManageFlow(workflowId);
         await _orderRepository.UpdateAsync(order, HttpContext.RequestAborted);
         await _orderRepository.UpdateAsync(order, HttpContext.RequestAborted);
     }
     }
 
 

+ 16 - 2
src/Hotline.Api/Controllers/WorkflowController.cs

@@ -249,7 +249,7 @@ public class WorkflowController : BaseController
     /// 办理节点
     /// 办理节点
     /// </summary>
     /// </summary>
     [HttpPost("next")]
     [HttpPost("next")]
-    public async Task NextAsync([FromBody]NextWorkflowDto dto)
+    public async Task NextAsync([FromBody] NextWorkflowDto dto)
     {
     {
         await _workflowApplication.NextAsync(dto, HttpContext.RequestAborted);
         await _workflowApplication.NextAsync(dto, HttpContext.RequestAborted);
     }
     }
@@ -298,7 +298,8 @@ public class WorkflowController : BaseController
     public async Task<IReadOnlyList<StepDefineDto>> GetJumpOptions(string workflowId)
     public async Task<IReadOnlyList<StepDefineDto>> GetJumpOptions(string workflowId)
     {
     {
         var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, true, true, cancellationToken: HttpContext.RequestAborted);
         var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, true, true, cancellationToken: HttpContext.RequestAborted);
-        return _mapper.Map<IReadOnlyList<StepDefineDto>>(workflow.Definition.Steps);
+        var steps = workflow.Definition.Steps.Where(d => d.StepType != EStepType.Start && d.StepType != EStepType.End);
+        return _mapper.Map<IReadOnlyList<StepDefineDto>>(steps);
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -354,6 +355,19 @@ public class WorkflowController : BaseController
         }
         }
     }
     }
 
 
+    /// <summary>
+    /// 查询流程流转记录
+    /// </summary>
+    /// <param name="workflowId"></param>
+    /// <returns></returns>
+    [HttpGet("{workflowId}/traces")]
+    public async Task<WorkflowDto> GetWorkflowTraces(string workflowId)
+    {
+        var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withTraces: true,
+            cancellationToken: HttpContext.RequestAborted);
+        return _mapper.Map<WorkflowDto>(workflow);
+    }
+
     [HttpGet("base-data")]
     [HttpGet("base-data")]
     public dynamic BaseData()
     public dynamic BaseData()
     {
     {

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

@@ -86,9 +86,11 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
     {
     {
         var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, true, true, cancellationToken: cancellationToken);
         var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, true, true, cancellationToken: cancellationToken);
         var nextStepBoxDefine = _workflowDomainService.GetStepBoxDefine(workflow.Definition, dto.NextStepCode);
         var nextStepBoxDefine = _workflowDomainService.GetStepBoxDefine(workflow.Definition, dto.NextStepCode);
-        var isOutOfCallCenter =
-            await CheckIfFlowOutOfCallCenterAsync(nextStepBoxDefine, dto.NextMainHandler, cancellationToken);
-        var isStartCountersign = nextStepBoxDefine.IsStartCountersign(dto.NextHandlers.Count);
+        //下一节点为结束节点时,无办理人等参数,只有办理意见即可
+        var isOutOfCallCenter = nextStepBoxDefine.StepType is not EStepType.End 
+                                && await CheckIfFlowOutOfCallCenterAsync(nextStepBoxDefine, dto.NextMainHandler, cancellationToken);
+        var isStartCountersign = nextStepBoxDefine.StepType is not EStepType.End 
+                                 && nextStepBoxDefine.IsStartCountersign(dto.NextHandlers.Count);
         await _workflowDomainService.NextAsync(workflow, dto, nextStepBoxDefine, isOutOfCallCenter, isStartCountersign, cancellationToken);
         await _workflowDomainService.NextAsync(workflow, dto, nextStepBoxDefine, isOutOfCallCenter, isStartCountersign, cancellationToken);
 
 
         //更新接办部门(详情页面展示)
         //更新接办部门(详情页面展示)
@@ -159,44 +161,52 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 Name = nextStepDefine.Name,
                 Name = nextStepDefine.Name,
             };
             };
 
 
-            switch (nextStepDefine.HandlerType)
+            if (nextStepDefine.StepType != EStepType.End)
             {
             {
-                case EHandlerType.AssignUser:
-                    var users = await _userRepository.QueryAsync(d =>
-                        nextStepDefine.HandlerClassifies.Select(d => d.Id).Contains(d.Id));
-                    options.NextSteps = users.Select(d => new KeyValuePair<string, string>(d.Id, d.Name)).ToList();
-                    break;
-                case EHandlerType.AssignOrg:
-                    var orgs = await _organizeRepository.QueryAsync(d =>
-                        nextStepDefine.HandlerClassifies.Select(d => d.Id).Contains(d.OrgCode));
-                    options.NextSteps = orgs.Select(d => new KeyValuePair<string, string>(d.OrgCode, d.OrgName)).ToList();
-                    break;
-                case EHandlerType.Role:
-                    var roles = await _roleRepository.Queryable().Includes(d => d.Accounts, d => d.User)
-                        .Where(d => nextStepDefine.HandlerClassifies.Select(d => d.Id).Contains(d.Name)).ToListAsync();
-                    var users1 = roles.SelectMany(d => d.Accounts).Select(d => d.User);
-                    if (nextStepDefine.OnlySelfOrg ?? false)
-                        users1 = users1.Where(d => d.OrgCode == _sessionContext.RequiredOrgCode);
-                    options.NextSteps = users1.Select(d => new KeyValuePair<string, string>(d.Id, d.Name)).ToList();
-                    break;
-                case EHandlerType.OrgLevel:
-                    //当前操作人所属部门的下级部门并且属于配置orgLevel的部门
-                    var levels = nextStepDefine.HandlerClassifies.Select(d => d.Id).Select(d => int.Parse(d));
-                    var orgs1 = await _organizeRepository.QueryAsync(d =>
-                        d.IsEnable && d.OrgCode.StartsWith(_sessionContext.RequiredOrgCode) &&
-                        levels.Contains(d.OrgLevel));
-                    options.NextSteps = orgs1.Select(d => new KeyValuePair<string, string>(d.OrgCode, d.OrgName)).ToList();
-                    break;
-                case EHandlerType.OrgType:
-                    var types = nextStepDefine.HandlerClassifies.Select(d => d.Id).Select(d => Enum.Parse<EOrgType>(d));
-                    var org2 = await _organizeRepository.QueryAsync(d =>
-                        d.IsEnable && d.OrgCode.StartsWith(_sessionContext.RequiredOrgCode) &&
-                        types.Contains(d.OrgType));
-                    options.NextSteps = org2.Select(d => new KeyValuePair<string, string>(d.OrgCode, d.OrgName)).ToList();
-                    break;
+                switch (nextStepDefine.HandlerType)
+                {
+                    case EHandlerType.AssignUser:
+                        var users = await _userRepository.QueryAsync(d =>
+                            nextStepDefine.HandlerClassifies.Select(d => d.Id).Contains(d.Id));
+                        options.NextSteps = users.Select(d => new KeyValuePair<string, string>(d.Id, d.Name)).ToList();
+                        break;
+                    case EHandlerType.AssignOrg:
+                        var orgs = await _organizeRepository.QueryAsync(d =>
+                            nextStepDefine.HandlerClassifies.Select(d => d.Id).Contains(d.OrgCode));
+                        options.NextSteps = orgs.Select(d => new KeyValuePair<string, string>(d.OrgCode, d.OrgName))
+                            .ToList();
+                        break;
+                    case EHandlerType.Role:
+                        var roles = await _roleRepository.Queryable().Includes(d => d.Accounts, d => d.User)
+                            .Where(d => nextStepDefine.HandlerClassifies.Select(d => d.Id).Contains(d.Name))
+                            .ToListAsync();
+                        var users1 = roles.SelectMany(d => d.Accounts).Select(d => d.User);
+                        if (nextStepDefine.OnlySelfOrg ?? false)
+                            users1 = users1.Where(d => d.OrgCode == _sessionContext.RequiredOrgCode);
+                        options.NextSteps = users1.Select(d => new KeyValuePair<string, string>(d.Id, d.Name)).ToList();
+                        break;
+                    case EHandlerType.OrgLevel:
+                        //当前操作人所属部门的下级部门并且属于配置orgLevel的部门
+                        var levels = nextStepDefine.HandlerClassifies.Select(d => d.Id).Select(d => int.Parse(d));
+                        var orgs1 = await _organizeRepository.QueryAsync(d =>
+                            d.IsEnable && d.OrgCode.StartsWith(_sessionContext.RequiredOrgCode) &&
+                            levels.Contains(d.OrgLevel));
+                        options.NextSteps = orgs1.Select(d => new KeyValuePair<string, string>(d.OrgCode, d.OrgName))
+                            .ToList();
+                        break;
+                    case EHandlerType.OrgType:
+                        var types = nextStepDefine.HandlerClassifies.Select(d => d.Id)
+                            .Select(d => Enum.Parse<EOrgType>(d));
+                        var org2 = await _organizeRepository.QueryAsync(d =>
+                            d.IsEnable && d.OrgCode.StartsWith(_sessionContext.RequiredOrgCode) &&
+                            types.Contains(d.OrgType));
+                        options.NextSteps = org2.Select(d => new KeyValuePair<string, string>(d.OrgCode, d.OrgName))
+                            .ToList();
+                        break;
 
 
-                default:
-                    throw new ArgumentOutOfRangeException();
+                    default:
+                        throw new ArgumentOutOfRangeException();
+                }
             }
             }
 
 
             items.Add(options);
             items.Add(options);

+ 37 - 0
src/Hotline.Application/Handlers/FlowEngine/AcceptWorkflowHandler.cs

@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Hotline.FlowEngine.Notifications;
+using Hotline.Orders;
+using Hotline.Settings;
+using MediatR;
+
+namespace Hotline.Application.Handlers.FlowEngine
+{
+    public class AcceptWorkflowHandler : INotificationHandler<AcceptWorkflowNotify>
+    {
+        private readonly IOrderDomainService _orderDomainService;
+
+        public AcceptWorkflowHandler(IOrderDomainService orderDomainService)
+        {
+            _orderDomainService = orderDomainService;
+        }
+
+        /// <summary>Handles a notification</summary>
+        /// <param name="notification">The notification</param>
+        /// <param name="cancellationToken">Cancellation token</param>
+        public async Task Handle(AcceptWorkflowNotify notification, CancellationToken cancellationToken)
+        {
+            var workflow = notification.Workflow;
+
+            switch (workflow.ModuleCode)
+            {
+                case WorkflowModuleConsts.OrderManage:
+                    await _orderDomainService.AcceptAsync(notification.Workflow.Id, cancellationToken);
+                    break;
+            }
+        }
+    }
+}

+ 1 - 1
src/Hotline.Application/Handlers/FlowEngine/NextStepHandler.cs

@@ -37,7 +37,7 @@ public class NextStepHandler : INotificationHandler<NextStepNotify>
         switch (workflow.ModuleCode)
         switch (workflow.ModuleCode)
         {
         {
             case WorkflowModuleConsts.OrderManage:
             case WorkflowModuleConsts.OrderManage:
-                await _orderDomainService.OrderManageAsync(EOrderStatus.WaitForSign,
+                await _orderDomainService.ManageFlowNextAsync(
                     assignMode, notification.IsCountersignEnd, notification.IsCountersignStart,
                     assignMode, notification.IsCountersignEnd, notification.IsCountersignStart,
                     workflow.Id, workflow.CurrentStepTime, workflow.CurrentStepName, workflow.ExpiredTime,
                     workflow.Id, workflow.CurrentStepTime, workflow.CurrentStepName, workflow.ExpiredTime,
                     cancellationToken);
                     cancellationToken);

+ 29 - 0
src/Hotline.Application/Handlers/FlowEngine/OrderFinalManageHandler.cs

@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Hotline.FlowEngine.Notifications;
+using Hotline.Orders;
+using MediatR;
+
+namespace Hotline.Application.Handlers.FlowEngine
+{
+    public class OrderFinalManageHandler : INotificationHandler<OrderFinalManageNotify>
+    {
+        private readonly IOrderDomainService _orderDomainService;
+
+        public OrderFinalManageHandler(IOrderDomainService orderDomainService)
+        {
+            _orderDomainService = orderDomainService;
+        }
+
+        /// <summary>Handles a notification</summary>
+        /// <param name="notification">The notification</param>
+        /// <param name="cancellationToken">Cancellation token</param>
+        public async Task Handle(OrderFinalManageNotify notification, CancellationToken cancellationToken)
+        {
+            await _orderDomainService.FinalManageAsync(notification.Workflow.Id, cancellationToken);
+        }
+    }
+}

+ 23 - 0
src/Hotline.Application/Handlers/FlowEngine/OrderRecallFinalManageHandler.cs

@@ -0,0 +1,23 @@
+using Hotline.FlowEngine.Notifications;
+using Hotline.Orders;
+using MediatR;
+
+namespace Hotline.Application.Handlers.FlowEngine;
+
+public class OrderRecallFinalManageHandler : INotificationHandler<OrderRecallFinalManageNotify>
+{
+    private readonly IOrderDomainService _orderDomainService;
+
+    public OrderRecallFinalManageHandler(IOrderDomainService orderDomainService)
+    {
+        _orderDomainService = orderDomainService;
+    }
+
+    /// <summary>Handles a notification</summary>
+    /// <param name="notification">The notification</param>
+    /// <param name="cancellationToken">Cancellation token</param>
+    public async Task Handle(OrderRecallFinalManageNotify notification, CancellationToken cancellationToken)
+    {
+        await _orderDomainService.RecallFinalManageAsync(notification.Workflow.Id, cancellationToken);
+    }
+}

+ 1 - 1
src/Hotline.Application/Handlers/FlowEngine/StartWorkflowHandler.cs

@@ -43,7 +43,7 @@ namespace Hotline.Application.Handlers.FlowEngine
             switch (workflow.ModuleCode)
             switch (workflow.ModuleCode)
             {
             {
                 case WorkflowModuleConsts.OrderManage:
                 case WorkflowModuleConsts.OrderManage:
-                    await _orderDomainService.OrderManageAsync(EOrderStatus.WaitForSign, assignMode,
+                    await _orderDomainService.ManageFlowNextAsync(assignMode,
                         false, notification.IsCountersignStart,
                         false, notification.IsCountersignStart,
                         workflow.Id, workflow.CurrentStepTime, workflow.CurrentStepName, workflow.ExpiredTime, cancellationToken);
                         workflow.Id, workflow.CurrentStepTime, workflow.CurrentStepName, workflow.ExpiredTime, cancellationToken);
                     break;
                     break;

+ 4 - 0
src/Hotline.Application/Mappers/MapperConfigs.cs

@@ -49,6 +49,7 @@ namespace Hotline.Application.Mappers
 
 
             config.ForType<WorkflowStep, WorkflowTrace>()
             config.ForType<WorkflowStep, WorkflowTrace>()
                 .Ignore(d => d.Id)
                 .Ignore(d => d.Id)
+                .Ignore(d=>d.ParentId)
                 .Map(d => d.StepId, s => s.Id);
                 .Map(d => d.StepId, s => s.Id);
 
 
             config.ForType<WorkflowSupplement, WorkflowSupplementDto>()
             config.ForType<WorkflowSupplement, WorkflowSupplementDto>()
@@ -59,6 +60,9 @@ namespace Hotline.Application.Mappers
                 .Map(d => d.AssignOrgs, s => string.Join(',', s.Assigns.Select(d => d.OrgName)))
                 .Map(d => d.AssignOrgs, s => string.Join(',', s.Assigns.Select(d => d.OrgName)))
                 ;
                 ;
 
 
+            config.ForType<BasicWorkflowDto, WorkflowStep>()
+                .IgnoreNullValues(true);
+
             #endregion
             #endregion
 
 
             #region Knowledge
             #region Knowledge

+ 2 - 1
src/Hotline.CacheManager/SysDicDataCacheManager.cs

@@ -6,10 +6,11 @@ using System.Linq;
 using System.Text;
 using System.Text;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using XF.Domain.Cache;
 using XF.Domain.Cache;
+using XF.Domain.Dependency;
 
 
 namespace Hotline.CacheManager
 namespace Hotline.CacheManager
 {
 {
-    public class SysDicDataCacheManager : ISysDicDataCacheManager
+    public class SysDicDataCacheManager : ISysDicDataCacheManager, IScopeDependency
     {
     {
         private readonly ISysDicDataRepository _sysDicDataRepository;
         private readonly ISysDicDataRepository _sysDicDataRepository;
         private readonly ITypedCache<IReadOnlyList<SysDicData>> _cacheSysDicData;
         private readonly ITypedCache<IReadOnlyList<SysDicData>> _cacheSysDicData;

+ 1 - 1
src/Hotline.Repository.SqlSugar/Extensions/SqlSugarStartupExtensions.cs

@@ -172,7 +172,7 @@ namespace Hotline.Repository.SqlSugar.Extensions
             /***写AOP等方法***/
             /***写AOP等方法***/
             db.Aop.OnLogExecuting = (sql, pars) =>
             db.Aop.OnLogExecuting = (sql, pars) =>
             {
             {
-                Log.Information(sql);
+                //Log.Information(sql);
             };
             };
             db.Aop.OnError = (exp) =>//SQL报错
             db.Aop.OnError = (exp) =>//SQL报错
             {
             {

+ 10 - 0
src/Hotline.Share/Dtos/Order/OrderDto.cs

@@ -38,6 +38,16 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         /// </summary>
         public DateTime? ExpiredTime { get; set; }
         public DateTime? ExpiredTime { get; set; }
 
 
+        /// <summary>
+        /// 工单办理完成时间,实际办理部门的办理时间(汇总节点的上一个非汇总节点办理完成时间,开启会签时清空该字段)
+        /// </summary>
+        public DateTime? CompleteTime { get; set; }
+
+        /// <summary>
+        /// 归档时间(暂为流程结束时间,因流程结束自动归档)
+        /// </summary>
+        public DateTime? FiledTime { get; set; }
+
         /// <summary>
         /// <summary>
         /// 过期状态
         /// 过期状态
         /// </summary>
         /// </summary>

+ 7 - 2
src/Hotline.Share/Dtos/Settings/TimeLimitDto.cs

@@ -76,14 +76,19 @@ namespace Hotline.Share.Dtos.Settings
     public class CombinationParamModel
     public class CombinationParamModel
     {
     {
         /// <summary>
         /// <summary>
-        /// 组合参数ID
+        /// 参数类型Code
         /// </summary>
         /// </summary>
-        public string Id { get; set; }
+        public string Code { get; set; }
 
 
         /// <summary>
         /// <summary>
         /// 组合参数名称
         /// 组合参数名称
         /// </summary>
         /// </summary>
         public string ParamName { get; set; }
         public string ParamName { get; set; }
+
+        /// <summary>
+        /// 组合参数值
+        /// </summary>
+        public string ParamValue { get; set; }
     }
     }
 
 
 
 

+ 1 - 1
src/Hotline.Share/Enums/Order/EOrderStatus.cs

@@ -35,7 +35,7 @@ public enum EOrderStatus
     Return = 40,
     Return = 40,
 
 
     /// <summary>
     /// <summary>
-    /// 办理完成
+    /// 办理完成(最终办结)
     /// </summary>
     /// </summary>
     [Description("办理完成")]
     [Description("办理完成")]
     Completed = 50,
     Completed = 50,

+ 13 - 1
src/Hotline/FlowEngine/Notifications/WorkflowNotify.cs

@@ -15,8 +15,20 @@ public record AcceptWorkflowNotify(Workflow Workflow) : INotification;
 
 
 public record CountersignEndAssigned(Workflow Workflow) : INotification;
 public record CountersignEndAssigned(Workflow Workflow) : INotification;
 
 
+public record CountersignStartAssigned(Workflow Workflow) : INotification;
+
 public record PreviousStepNotify(PreviousWorkflowDto Data) : INotification;
 public record PreviousStepNotify(PreviousWorkflowDto Data) : INotification;
 
 
 public record EndWorkflowNotify(Workflow Workflow) : INotification;
 public record EndWorkflowNotify(Workflow Workflow) : INotification;
 
 
-public record TerminalWorkflowNotify(Workflow Workflow) : INotification;
+public record TerminalWorkflowNotify(Workflow Workflow) : INotification;
+
+/// <summary>
+/// 工单最终办理通知(工单的实际办理部门办理完成时发出)
+/// </summary>
+public record OrderFinalManageNotify(Workflow Workflow) : INotification;
+
+/// <summary>
+/// 工单撤销最终办理通知
+/// </summary>
+public record OrderRecallFinalManageNotify(Workflow Workflow) : INotification;

+ 8 - 0
src/Hotline/FlowEngine/Workflows/Workflow.cs

@@ -29,6 +29,9 @@ public class Workflow : CreationEntity
     /// </summary>
     /// </summary>
     public DateTime ExpiredTime { get; set; }
     public DateTime ExpiredTime { get; set; }
 
 
+    /// <summary>
+    /// 流程流转到end节点的时间(配置模板的end节点前一个节点办理完成时间)
+    /// </summary>
     public DateTime? CompleteTime { get; set; }
     public DateTime? CompleteTime { get; set; }
 
 
     public EWorkflowStatus Status { get; set; }
     public EWorkflowStatus Status { get; set; }
@@ -186,5 +189,10 @@ public class Workflow : CreationEntity
     /// </summary>
     /// </summary>
     public void CloseCountersignStatus() => CurrentCountersignStepCode = null;
     public void CloseCountersignStatus() => CurrentCountersignStepCode = null;
 
 
+    /// <summary>
+    /// 重置最终办理意见
+    /// </summary>
+    public void ResetOption() => Opinion = "办理中...";
+
     #endregion
     #endregion
 }
 }

+ 11 - 3
src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs

@@ -257,7 +257,7 @@ namespace Hotline.FlowEngine.Workflows
                 workflow.Complete();
                 workflow.Complete();
                 await _workflowRepository.UpdateAsync(workflow, cancellationToken);
                 await _workflowRepository.UpdateAsync(workflow, cancellationToken);
 
 
-                _mediator.Publish(new EndWorkflowNotify(workflow), cancellationToken);
+                await _mediator.Publish(new EndWorkflowNotify(workflow), cancellationToken);
                 return;
                 return;
             }
             }
 
 
@@ -269,10 +269,18 @@ namespace Hotline.FlowEngine.Workflows
                 workflow.AssignTime = DateTime.Now;
                 workflow.AssignTime = DateTime.Now;
             }
             }
 
 
-            //最终办理意见
-            if (nextStepBoxDefine.StepType is not EStepType.Normal)
+            //最终办理意见与时间处理
+            if (currentStep.StepType is EStepType.Normal && nextStepBoxDefine.StepType is EStepType.CountersignEnd)
             {
             {
+                //最终办理
                 workflow.Opinion = dto.Opinion;
                 workflow.Opinion = dto.Opinion;
+                await _mediator.Publish(new OrderFinalManageNotify(workflow), cancellationToken);
+            }
+            else if (currentStep.StepType is EStepType.CountersignEnd && nextStepBoxDefine.StepType is EStepType.Normal)
+            {
+                //汇总以后又重新指派到非汇总节点办理
+                workflow.ResetOption();
+                await _mediator.Publish(new OrderRecallFinalManageNotify(workflow), cancellationToken);
             }
             }
 
 
             //创建下一节点
             //创建下一节点

+ 34 - 3
src/Hotline/Orders/IOrderDomainService.cs

@@ -16,12 +16,43 @@ namespace Hotline.Orders
         /// <summary>
         /// <summary>
         /// 归档
         /// 归档
         /// </summary>
         /// </summary>
-        Task FileAsync(string orderId, CancellationToken cancellationToken);
+        Task FileAsync(string workflowId, CancellationToken cancellationToken);
 
 
         /// <summary>
         /// <summary>
-        /// 工单办理(每个节点都会触发)
+        /// 接办工单(查看详情视为接办)
         /// </summary>
         /// </summary>
-        Task OrderManageAsync(EOrderStatus status, FlowAssignMode assignMode, bool isCountersignEnd, bool isCountersignStart,
+        /// <param name="workflowId"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        Task AcceptAsync(string workflowId, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 工单办理流程流转(每个节点办理都会触发)
+        /// </summary>
+        Task ManageFlowNextAsync(FlowAssignMode assignMode, bool isCountersignEnd, bool isCountersignStart,
             string workflowId, DateTime? currentStepTime, string? CurrentStepName, DateTime expiredTime, CancellationToken cancellationToken);
             string workflowId, DateTime? currentStepTime, string? CurrentStepName, DateTime expiredTime, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 工单最终办理(此完结流程并未结束)
+        /// </summary>
+        /// <remarks>
+        /// 需求:工单实际办理部门办理完成视为工单最终办理完结,
+        /// 实际办理部门指:1.第一个汇总节点的前一个非汇总节点代表的办理部门 2.汇总以后如果再次开启会签需将工单回退到未办理完结的状态重新等待下一个汇总节点
+        /// </remarks>
+        /// <returns></returns>
+        Task FinalManageAsync(string workflowId, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 撤回最终办理(汇总以后又重新指派到非汇总汇总节点办理的场景)
+        /// </summary>
+        /// <returns></returns>
+        Task RecallFinalManageAsync(string workflowId, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 工单办理流程流转到结束节点
+        /// </summary>
+        /// <param name="workflowId"></param>
+        /// <returns></returns>
+        Task ManageFlowEndAsync(string workflowId, CancellationToken cancellationToken);
     }
     }
 }
 }

+ 76 - 1
src/Hotline/Orders/Order.cs

@@ -204,6 +204,16 @@ namespace Hotline.Orders
         /// </summary>
         /// </summary>
         public DateTime? ExpiredTime { get; set; }
         public DateTime? ExpiredTime { get; set; }
 
 
+        /// <summary>
+        /// 工单办理完成时间,实际办理部门的办理时间(汇总节点的上一个非汇总节点办理完成时间,开启会签时清空该字段)
+        /// </summary>
+        public DateTime? CompleteTime { get; set; }
+
+        /// <summary>
+        /// 归档时间(暂为流程结束时间,因流程结束自动归档)
+        /// </summary>
+        public DateTime? FiledTime { get; set; }
+
         /// <summary>
         /// <summary>
         /// 过期状态 //todo 延迟消息更新此字段
         /// 过期状态 //todo 延迟消息更新此字段
         /// </summary>
         /// </summary>
@@ -250,12 +260,77 @@ namespace Hotline.Orders
             StartTime = DateTime.Now;
             StartTime = DateTime.Now;
         }
         }
 
 
-        public void StartFlow(string workflowId)
+        /// <summary>
+        /// 开始工单办理流程
+        /// </summary>
+        /// <param name="workflowId"></param>
+        public void StartManageFlow(string workflowId)
         {
         {
             WorkflowId = workflowId;
             WorkflowId = workflowId;
             Status = EOrderStatus.WaitForSign;
             Status = EOrderStatus.WaitForSign;
         }
         }
 
 
+        /// <summary>
+        /// 接办
+        /// </summary>
+        public void Accept()
+        {
+            Status = EOrderStatus.Handling;
+        }
+
+        /// <summary>
+        /// 工单办理流程流转(每次办理都会调用该方法)
+        /// </summary>
+        public void ManageFlow(
+            bool isCountersignStart,
+            bool isCountersignEnd, 
+            DateTime? currentStepTime, 
+            string? currentStepName, 
+            DateTime expiredTime)
+        {
+            //1.如果order未处于会签中,则判断是否发起会签(isstartCountersign) 2.如果处于会签中,则判断会签是否结束(isCountersignEnd)
+            if (Status is EOrderStatus.Countersigning && isCountersignEnd)
+            {
+                Status = EOrderStatus.WaitForSign;
+            }
+            else if (Status is not EOrderStatus.Countersigning && isCountersignStart)
+            {
+                Status = EOrderStatus.Countersigning;
+            }
+
+            CurrentStepTime = currentStepTime;
+            CurrentStepName = currentStepName;
+            ExpiredTime = expiredTime;
+            Status = EOrderStatus.WaitForSign;
+        }
+
+        /// <summary>
+        /// 最终办理
+        /// </summary>
+        public void FinalManage()
+        {
+            Status = EOrderStatus.Completed;
+            CompleteTime = DateTime.Now;
+        }
+
+        /// <summary>
+        /// 撤回最终办理
+        /// </summary>
+        public void RecallFinalManage()
+        {
+            Status = EOrderStatus.WaitForSign;
+            CompleteTime = null;
+        }
+
+        /// <summary>
+        /// 归档
+        /// </summary>
+        public void Filed()
+        {
+            Status = EOrderStatus.Filed;
+            FiledTime = DateTime.Now;
+        }
+
         #endregion
         #endregion
     }
     }
 }
 }

+ 71 - 24
src/Hotline/Orders/OrderDomainService.cs

@@ -28,9 +28,9 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
 
 
     public async Task<string> AddAsync(Order order, CancellationToken cancellationToken)
     public async Task<string> AddAsync(Order order, CancellationToken cancellationToken)
     {
     {
-        if (order.AcceptType == EAcceptType.Complain && order.OrderComplain == null)
+        if (order.OrderType == EOrderType.MarketSupervisionBy12315 && order.AcceptType == EAcceptType.Complain && order.OrderComplain == null)
             throw UserFriendlyException.SameMessage("非法投诉参数");
             throw UserFriendlyException.SameMessage("非法投诉参数");
-        if (order.AcceptType == EAcceptType.Report && order.OrderReport == null)
+        if (order.OrderType == EOrderType.MarketSupervisionBy12315 && order.AcceptType == EAcceptType.Report && order.OrderReport == null)
             throw UserFriendlyException.SameMessage("非法举报参数");
             throw UserFriendlyException.SameMessage("非法举报参数");
 
 
         order.Init(_sessionContext.RequiredUserId);
         order.Init(_sessionContext.RequiredUserId);
@@ -42,53 +42,100 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
     /// <summary>
     /// <summary>
     /// 归档
     /// 归档
     /// </summary>
     /// </summary>
-    public async Task FileAsync(string orderId, CancellationToken cancellationToken)
+    public async Task FileAsync(string workflowId, CancellationToken cancellationToken)
     {
     {
-        var order = await _orderRepository.GetAsync(orderId, cancellationToken);
+        var order = await _orderRepository.GetAsync(d => d.WorkflowId == workflowId, cancellationToken);
         if (order == null)
         if (order == null)
-            throw UserFriendlyException.SameMessage("无效的工单编号");
+            throw new UserFriendlyException($"无效工单流程编号, workflowId: {workflowId}", "无效工单编号");
+        if (order.Status is EOrderStatus.Filed) return;
         order.Status = EOrderStatus.Filed;
         order.Status = EOrderStatus.Filed;
         await _orderRepository.UpdateAsync(order, cancellationToken);
         await _orderRepository.UpdateAsync(order, cancellationToken);
     }
     }
 
 
+    /// <summary>
+    /// 接办工单(查看详情视为接办)
+    /// </summary>
+    /// <param name="workflowId"></param>
+    /// <param name="cancellationToken"></param>
+    /// <returns></returns>
+    public async Task AcceptAsync(string workflowId, CancellationToken cancellationToken)
+    {
+        var order = await GetOrderAsync(workflowId, cancellationToken);
+        order.Accept();
+        await _orderRepository.UpdateAsync(order, cancellationToken);
+    }
+
     /// <summary>
     /// <summary>
     /// 工单办理(每个节点都会触发)
     /// 工单办理(每个节点都会触发)
     /// </summary>
     /// </summary>
-    public async Task OrderManageAsync(EOrderStatus status, FlowAssignMode assignMode,
+    public async Task ManageFlowNextAsync(FlowAssignMode assignMode,
         bool isCountersignEnd, bool isCountersignStart,
         bool isCountersignEnd, bool isCountersignStart,
         string workflowId, DateTime? currentStepTime, string? CurrentStepName, DateTime expiredTime,
         string workflowId, DateTime? currentStepTime, string? CurrentStepName, DateTime expiredTime,
         CancellationToken cancellationToken)
         CancellationToken cancellationToken)
     {
     {
-        var order = await _orderRepository.GetAsync(d => d.WorkflowId == workflowId, cancellationToken);
-        if (order == null)
-            throw new UserFriendlyException($"无效工单流程编号, workflowId: {workflowId}", "无效工单编号");
-        if (order.Status is EOrderStatus.Filed) return;
+        var order = await GetOrderAsync(workflowId, cancellationToken);
 
 
         //更新指派信息
         //更新指派信息
         order.Assign(assignMode.FlowAssignType, assignMode.Handlers);
         order.Assign(assignMode.FlowAssignType, assignMode.Handlers);
 
 
-        //1.如果order未处于会签中,则判断是否发起会签(isstartCountersign) 2.如果处于会签中,则判断会签是否结束(isCountersignEnd)
-        if (order.Status is EOrderStatus.Countersigning && isCountersignEnd)
-        {
-            order.Status = EOrderStatus.WaitForSign;
-        }
-        else if (order.Status is not EOrderStatus.Countersigning && isCountersignStart)
-        {
-            order.Status = EOrderStatus.Countersigning;
-        }
+        //更新流转信息
+        order.ManageFlow(isCountersignStart, isCountersignEnd, currentStepTime, CurrentStepName, expiredTime);
+
+        await _orderRepository.UpdateAsync(order, cancellationToken);
+
+        //todo push msg to next step handler
+    }
 
 
-        order.CurrentStepTime = currentStepTime;
-        order.CurrentStepName = CurrentStepName;
-        order.ExpiredTime = expiredTime;
-        order.Status = status;
+    /// <summary>
+    /// 工单最终办理(此完结流程并未结束)
+    /// </summary>
+    /// <remarks>
+    /// 需求:工单实际办理部门办理完成视为工单最终办理完结,
+    /// 实际办理部门指:1.第一个汇总节点的前一个非汇总节点代表的办理部门 2.汇总以后如果再次开启会签需将工单回退到未办理完结的状态重新等待下一个汇总节点
+    /// </remarks>
+    /// <returns></returns>
+    public async Task FinalManageAsync(string workflowId, CancellationToken cancellationToken)
+    {
+        var order = await GetOrderAsync(workflowId, cancellationToken);
+        order.FinalManage();
+        await _orderRepository.UpdateAsync(order, cancellationToken);
+    }
 
 
+    /// <summary>
+    /// 取消最终办理(汇总以后又重新指派到非汇总汇总节点办理的场景)
+    /// </summary>
+    /// <returns></returns>
+    public async Task RecallFinalManageAsync(string workflowId, CancellationToken cancellationToken)
+    {
+        var order = await GetOrderAsync(workflowId, cancellationToken);
+        order.RecallFinalManage();
         await _orderRepository.UpdateAsync(order, cancellationToken);
         await _orderRepository.UpdateAsync(order, cancellationToken);
+    }
 
 
-        //todo push msg
+    /// <summary>
+    /// 工单办理流程流转到结束节点
+    /// </summary>
+    /// <param name="workflowId"></param>
+    /// <returns></returns>
+    public async Task ManageFlowEndAsync(string workflowId, CancellationToken cancellationToken)
+    {
+        var order = await GetOrderAsync(workflowId, cancellationToken);
+        order.Filed();
+        await _orderRepository.UpdateAsync(order, cancellationToken);
     }
     }
 
 
     #region private
     #region private
 
 
+    private async Task<Order> GetOrderAsync(string workflowId, CancellationToken cancellationToken)
+    {
+        var order = await _orderRepository.GetAsync(d => d.WorkflowId == workflowId, cancellationToken);
+        if (order == null)
+            throw new UserFriendlyException($"无效工单流程编号, workflowId: {workflowId}", "无效工单编号");
+        if (order.Status is EOrderStatus.Filed)
+            throw UserFriendlyException.SameMessage("工单已归档");
+        return order;
+    }
+
     private string GenerateNewOrderNo()
     private string GenerateNewOrderNo()
     {
     {
         var today = DateTime.Today;
         var today = DateTime.Today;

+ 2 - 2
src/Hotline/Settings/CommonOpinions/CommonOpinionTypeConsts.cs

@@ -28,9 +28,9 @@
         public const string Seat = "Seat";
         public const string Seat = "Seat";
 
 
         /// <summary>
         /// <summary>
-        /// 流转常用意见
+        /// 工单流转常用意见
         /// </summary>
         /// </summary>
-        public const string Circulation = "Circulation";
+        public const string OrderCirculation = "OrderCirculation";
 
 
         /// <summary>
         /// <summary>
         /// 回访常用意见
         /// 回访常用意见

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

@@ -28,13 +28,20 @@ namespace Hotline.Settings.TimeLimits
         }
         }
 
 
 
 
-        //public async Task<(string, int)> Get(string WorkflowCode,DateTime BeginTime,Dictionary<string,string> param)
+        //public async Task Get(string WorkflowCode, DateTime BeginTime, Dictionary<string, string> param)
         //{
         //{
         //    //1>> 取出对应已启用的配置
         //    //1>> 取出对应已启用的配置
         //    var model = await _timeLimitRepository.GetAsync(x => x.WorkflowCode == WorkflowCode && x.TimeLimitState == Share.Enums.Settings.ETimeLimitState.Enable);
         //    var model = await _timeLimitRepository.GetAsync(x => x.WorkflowCode == WorkflowCode && x.TimeLimitState == Share.Enums.Settings.ETimeLimitState.Enable);
-        //    if (model!=null)
+        //    if (model != null)
         //    {
         //    {
-
+        //        //组合逻辑
+        //        if (model.Combination.Count > 0)
+        //        {
+        //            foreach (var item in model.Combination)
+        //            {
+        //                item.CombinationParam.
+        //            }
+        //        }
         //    }
         //    }
         //}
         //}