1,背景
工作流思想在上世紀60年代就有人提出過;70年代就有人開始嘗試,但是由於當時許多的限制,工作流一直沒有成功的被實現;80年代才出現第一批成功的工作流系統;90年代工作流技術走向了第一個發展高峰期;90年代後至今工作流出現了很多版本,但是主旨還是不變的,為了使我們的工作變得更加高效。
通俗點,我們經常使用的OA系統上。關於一個材料的申報,又或者個人的請假。這些流程就屬於工作流(工作審批流)。其中對於審批人和各個節點是可以動態操作的。
工作流可以透過資料庫設計的形式實現,也可以使用第三方的框架Elsa,Workflow Core。本文使用第二種,並詳細的介紹一下程式碼實現和json實現工作流程。
2,安裝
使用Nuget包管理工具安裝以下的包:
Workflow Core 3.10.0 核心包不解釋
WorkflowCore.DSL 3.10.0 json或者yaml注入需要
WorkflowCore.Persistence.MySQL 持久化
3,文件
JSON / YAML Definitions - Workflow Core (workflow-core.readthedocs.io)
WorkFlowCore 載入Json檔案 - 程式設計程式碼 (cscoder.cn)
4,例子
1,建立專案
控制檯或者WebApi專案均可,這裡拿WebApi專案舉例。(版本用的是.net 6)
2,新增工步
工步檔案需要繼承StepBody。現在新增一個Hello.cs和Goodbye的工步,程式碼如下:
using WorkflowCore.Interface; using WorkflowCore.Models; namespace WorkFlowCore_Test.WorkFlows.HelloWord.Steps { public class Hello : StepBody { public override ExecutionResult Run(IStepExecutionContext context) { Console.WriteLine("Hello World!"); return ExecutionResult.Next(); } } }
using WorkflowCore.Interface; using WorkflowCore.Models; namespace WorkFlowCore_Test.WorkFlows.HelloWord.Steps { public class Goodbye : StepBody { public override ExecutionResult Run(IStepExecutionContext context) { Console.WriteLine("Goodbye World!"); return ExecutionResult.Next(); } } }
3,新增工作流檔案
工作流檔案需要繼承IWorkflow。現在開始新增:
using WorkflowCore.Interface; using WorkFlowCore_Test.WorkFlows.HelloWord.Steps; namespace WorkFlowCore_Test.WorkFlows.HelloWord { public class HelloWorldWorkflow : IWorkflow { public string Id => "HelloWorld"; public int Version => 1; public void Build(IWorkflowBuilder<object> builder) { builder .StartWith<Hello>() .Then<Goodbye>(); } } }
4,配置和啟動
基本資料都準備好了之後,在專案啟動檔案進行一下配置。程式碼如下:
using Microsoft.AspNetCore.Mvc; using System.Xml.Linq; using WorkflowCore.Interface; using WorkflowCore.Services; using WorkflowCore.Services.DefinitionStorage; using WorkFlowCore_Test.Utils; using WorkFlowCore_Test.WorkFlows.AskForLeave.Model; using WorkFlowCore_Test.WorkFlows.AskForLeave; using WorkFlowCore_Test.WorkFlows.HelloWord; using WorkFlowCore_Test.WorkFlows.IfStatement; using WorkFlowCore_Test.WorkFlows.IfStatement.Model; using WorkFlowCore_Test.WorkFlows.Json.Steps; using WorkFlowCore_Test.WorkFlows.SimpleDecision; namespace WorkFlowCore_Test { public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddAuthorization(); builder.Services.AddControllers(); builder.Services.AddLogging(); // WorkflowCore需要用到logging service //持久化 builder.Services.AddWorkflow(cfg => { cfg.UseMySQL("server=127.0.0.1;Database=Workflow;Uid=root;Pwd=xunpai123.", true, true); }); builder.Services.AddWorkflowDSL();//用來注入json和yaml var app = builder.Build(); //核心注入方法 UseWorkflow(app); app.UseAuthorization(); app.MapControllers(); app.Run(); } public static void UseWorkflow(WebApplication app) { var host = app.Services.GetService<IWorkflowHost>(); #region 工步註冊 //c#程式碼註冊 host?.RegisterWorkflow<HelloWorldWorkflow>(); //json註冊 #endregion host?.Start(); // 透過DI獲取IHostApplicationLifetime例項 var applicationLifetime = app.Services.GetService(typeof(IHostApplicationLifetime)) as IHostApplicationLifetime; applicationLifetime?.ApplicationStopping.Register(() => { host?.Stop(); }); } } }
這個時候我們新增api控制器進行呼叫這個helloworld的流程。程式碼如下:
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Diagnostics; using System.Net.Http.Json; using System.Text.Json.Nodes; using WorkflowCore.Interface; using WorkflowCore.Models; using WorkflowCore.Services; using WorkflowCore.Services.DefinitionStorage; using WorkFlowCore_Test.Utils; using WorkFlowCore_Test.WorkFlows.AskForLeave.Model; using WorkFlowCore_Test.WorkFlows.SimpleDecision; namespace WorkFlowCore_Test.Controllers { [Route("api/[controller]")] [ApiController] public class TestApiController : ControllerBase { private IWorkflowController WorkflowService; private IWorkflowHost WorkflowHost; public TestApiController(IWorkflowController workflowService, IWorkflowHost workflowHost) { WorkflowService = workflowService; WorkflowHost = workflowHost; } [Route("WorkFlowCoreTestDemo")] [HttpGet] public string WorkFlowCoreTestDemo() { //var initialData = new LeaveRequestData //{ // RequestId = "11111", // RequestMsg = "開始請假" //}; //WorkflowHost.StartWorkflow("LeaveRequestWorkflow", 1, initialData); WorkflowService.StartWorkflow("HelloWorld"); return "true"; } } }
5,效果展示
啟動後呼叫我們的控制器方法,然後觀看console 的變化,如圖:
6,請假單流程
請假單的流程建立的話需要用到等待的函式,等待管理員進行稽核後進行下一步的操作。請假單的流程程式碼如下:
public class LeaveRequestData { public string RequestId { get; set; } public bool ManagerApproved { get; set; } public bool HRApproved { get; set; } public string RequestMsg { get; set; } }
using WorkflowCore.Interface; using WorkFlowCore_Test.WorkFlows.AskForLeave.Model; using WorkFlowCore_Test.WorkFlows.AskForLeave.Steps; using WorkFlowCore_Test.WorkFlows.SimpleDecision.Steps; namespace WorkFlowCore_Test.WorkFlows.AskForLeave { public class AskForLeaveWorkflow : IWorkflow<LeaveRequestData> { public string Id => "LeaveRequestWorkflow"; public int Version => 1; public void Build(IWorkflowBuilder<LeaveRequestData> builder) { builder.StartWith(context => Console.WriteLine("提交請假申請")) .Activity("manage-audit", (data) => data.RequestMsg) .Output(data => data.ManagerApproved, step => step.Result) .Then<ManagerApprovalStep>().Input(step => step.AuditDataInfo, data => data) .If(data => data.ManagerApproved) .Do(then => then.StartWith(context => Console.WriteLine("經理稽核"))); } } }
然後建立好之後,在啟動檔案進行註冊一下:
host?.RegisterWorkflow<AskForLeaveWorkflow, LeaveRequestData>();
好了,接下來我們就可以用介面的形式進行模擬了。首先我們新增一個介面用來模擬申請請求程式碼如下:
[Route("WorkFlowCoreTestDemo")] [HttpGet] public string WorkFlowCoreTestDemo() { var initialData = new LeaveRequestData { RequestId = "11111", RequestMsg = "開始請假" }; WorkflowHost.StartWorkflow("LeaveRequestWorkflow", 1, initialData); //WorkflowService.StartWorkflow("HelloWorld"); return "true"; }
然後建立一個管理員的審批方法,用來稽核透過或者拒絕。程式碼如下:
[Route("WorkFlowCoreTestDemo4")] [HttpGet] public async Task<string> WorkFlowCoreTestDemo4() { //獲取待決活動 var approval = await WorkflowHost.GetPendingActivity("manage-audit", "worker1"); if (approval != null) { Console.WriteLine("需要批准的" + approval.Parameters); //提交活動成功 await WorkflowHost.SubmitActivitySuccess(approval.Token, true);//true,代表審批透過。邏輯驗證在步驟裡面 } return "請假工作流"; }
好了,這樣就形成閉環了。大家可以在postman上請求測試一下,觀察console的變化了。
7,json注入
最後就是json注入的方式了,這種方式也是目前我比較傾向的方式,和前端對接就很方便的。前端按照固定的格式給後端傳入json資料,後端根據json資料解析工步進行開啟工作流。
首先需要建立一個json檔案,json檔案程式碼如下:
{ "Id": "HelloWorldJson", "Version": 1, "Steps": [ { "Id": "Hello", "StepType": "", "NextStepId": "Bye" }, { "Id": "Bye", "StepType": "" } ] }
然後在啟動檔案配置,注入一下json工作流:
//json註冊 var loader = app.Services.GetService<IDefinitionLoader>(); var json = System.IO.File.ReadAllText("WorkFlows/Json/JsonYaml.json");// 假設檔案位於程式執行目錄 // 【json注入時候,stepType寫這個程式集的類名】獲取並列印全限定名,包括程式集名稱 //Type myClassType = typeof(HelloWorldStep); //string fullAssemblyClassName = myClassType.AssemblyQualifiedName; loader?.LoadDefinition(json, Deserializers.Json);
到這裡就可以了,建立一個api方法來呼叫看看:
[Route("WorkFlowCoreTestDemo6")] [HttpGet] public string WorkFlowCoreTestDemo6() { WorkflowHost.StartWorkflow("HelloWorldJson", 1, null); return ""; }
到這裡就可以實現json注入工作流了。不過細心的朋友就發現了,我給的json檔案中的step的type為什麼是空的呢?是因為StepType取的是專案程式集工步的類名稱,因為我的電腦專案和你們的專案不一樣,所以這裡沒有寫。大家可以自行百度搜搜看,我這裡還是放一下我的json例子吧。
{ "Id": "HelloWorldJson", "Version": 1, "Steps": [ { "Id": "Hello", "StepType": "WorkFlowCore_Test.WorkFlows.Json.Steps.HelloWorldStep, WorkFlowCore-Test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "NextStepId": "Bye" }, { "Id": "Bye", "StepType": "WorkFlowCore_Test.WorkFlows.Json.Steps.HelloWorldStep, WorkFlowCore-Test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" } ] }
5,結語
例子備註寫的比較多,就不多贅述了。有問題的話就留言吧,感謝觀看~