xf пре 2 година
родитељ
комит
7bb780661d

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

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

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

@@ -298,7 +298,8 @@ public class WorkflowController : BaseController
     public async Task<IReadOnlyList<StepDefineDto>> GetJumpOptions(string workflowId)
     {
         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>

+ 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)
         {
             case WorkflowModuleConsts.OrderManage:
-                await _orderDomainService.OrderManageAsync(EOrderStatus.WaitForSign,
+                await _orderDomainService.ManageFlowNextAsync(
                     assignMode, notification.IsCountersignEnd, notification.IsCountersignStart,
                     workflow.Id, workflow.CurrentStepTime, workflow.CurrentStepName, workflow.ExpiredTime,
                     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)
             {
                 case WorkflowModuleConsts.OrderManage:
-                    await _orderDomainService.OrderManageAsync(EOrderStatus.WaitForSign, assignMode,
+                    await _orderDomainService.ManageFlowNextAsync(assignMode,
                         false, notification.IsCountersignStart,
                         workflow.Id, workflow.CurrentStepTime, workflow.CurrentStepName, workflow.ExpiredTime, cancellationToken);
                     break;

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

@@ -35,7 +35,7 @@ public enum EOrderStatus
     Return = 40,
 
     /// <summary>
-    /// 办理完成
+    /// 办理完成(最终办结)
     /// </summary>
     [Description("办理完成")]
     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 CountersignStartAssigned(Workflow Workflow) : INotification;
+
 public record PreviousStepNotify(PreviousWorkflowDto Data) : 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>
     public DateTime ExpiredTime { get; set; }
 
+    /// <summary>
+    /// 流程流转到end节点的时间(配置模板的end节点前一个节点办理完成时间)
+    /// </summary>
     public DateTime? CompleteTime { get; set; }
 
     public EWorkflowStatus Status { get; set; }
@@ -186,5 +189,10 @@ public class Workflow : CreationEntity
     /// </summary>
     public void CloseCountersignStatus() => CurrentCountersignStepCode = null;
 
+    /// <summary>
+    /// 重置最终办理意见
+    /// </summary>
+    public void ResetOption() => Opinion = "办理中...";
+
     #endregion
 }

+ 10 - 2
src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs

@@ -269,10 +269,18 @@ namespace Hotline.FlowEngine.Workflows
                 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;
+                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>
-        Task FileAsync(string orderId, CancellationToken cancellationToken);
+        Task FileAsync(string workflowId, CancellationToken cancellationToken);
 
         /// <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);
+
+        /// <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>
         public DateTime? ExpiredTime { get; set; }
 
+        /// <summary>
+        /// 工单办理完成时间,实际办理部门的办理时间(汇总节点的上一个非汇总节点办理完成时间,开启会签时清空该字段)
+        /// </summary>
+        public DateTime? CompleteTime { get; set; }
+
+        /// <summary>
+        /// 归档时间(暂为流程结束时间,因流程结束自动归档)
+        /// </summary>
+        public DateTime? FiledTime { get; set; }
+
         /// <summary>
         /// 过期状态 //todo 延迟消息更新此字段
         /// </summary>
@@ -250,12 +260,77 @@ namespace Hotline.Orders
             StartTime = DateTime.Now;
         }
 
-        public void StartFlow(string workflowId)
+        /// <summary>
+        /// 开始工单办理流程
+        /// </summary>
+        /// <param name="workflowId"></param>
+        public void StartManageFlow(string workflowId)
         {
             WorkflowId = workflowId;
             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
     }
 }

+ 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)
     {
-        if (order.AcceptType == EAcceptType.Complain && order.OrderComplain == null)
+        if (order.OrderType == EOrderType.MarketSupervisionBy12315 && order.AcceptType == EAcceptType.Complain && order.OrderComplain == null)
             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("非法举报参数");
 
         order.Init(_sessionContext.RequiredUserId);
@@ -42,53 +42,100 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
     /// <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)
-            throw UserFriendlyException.SameMessage("无效的工单编号");
+            throw new UserFriendlyException($"无效工单流程编号, workflowId: {workflowId}", "无效工单编号");
+        if (order.Status is EOrderStatus.Filed) return;
         order.Status = EOrderStatus.Filed;
         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>
-    public async Task OrderManageAsync(EOrderStatus status, FlowAssignMode assignMode,
+    public async Task ManageFlowNextAsync(FlowAssignMode assignMode,
         bool isCountersignEnd, bool isCountersignStart,
         string workflowId, DateTime? currentStepTime, string? CurrentStepName, DateTime expiredTime,
         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);
 
-        //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);
+    }
 
-        //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
 
+    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()
     {
         var today = DateTime.Today;