背景
在上篇文章(Workflow:自定義工作流 之 模型選擇)介紹了模型的選擇,這篇文章就介紹一下模型的設計與實現。
有些朋友會希望在這里看到:數據庫、持久化或審批人角色處理等代碼,我是領域驅動設計(DDD)的愛好者,因此很長一段時間內您是看不到這些代碼的,我覺得這些不是模型的核心。
模型設計
概念模型
模型規則如下
1、系統有活動(方塊或圓形)和路由(線條)組成,每種類型的活動支持不同的路由規則。
2、方塊代表人工活動,人工活動只能路由到一個目標節點,可以定義多個路由,但是只有一個路由會執行,這讓模型支持:順序和判定。
順序執行
判定執行
3、圓形代表並行活動,Split(分流)和Join(合流)必須成對出現,Split會導致多個活動並行執行,Join會合並這些並行執行的活動,這讓模式支持:並行。
並行執行
設計模型
工作流中涉及兩塊設計模型,一、定義模型(流程本身);二、實例模型(流程的運行實例)。
定義模型
實例模型
我想類圖就沒有啥解釋的,這些類圖非常直觀的反映了概念模型,讓我們直接去看實現。
實現
活動基類
這里定義活動可以Enter(進入)和Exit(離開),當然前提是他們CanEnter(能進入)和CanExit(能離開),如果他們Enter或Exit了,會觸發OnEnter或OnExit。
Enter方法
1 internal void Enter(WorkflowContext context, ActivityInstance activityInstance) 2 { 3 activityInstance.SetStateToEntering(context); 4 5 if (!this.CanEnter(context, activityInstance)) 6 { 7 return; 8 } 9 10 activityInstance.SetStateToEntered(context); 11 12 this.OnEnter(context, activityInstance); 13 }
Exit方法
1 internal void Exit(WorkflowContext context, ActivityInstance activityInstance) 2 { 3 activityInstance.SetStateToExiting(context); 4 5 if (!this.CanExit(context, activityInstance)) 6 { 7 return; 8 } 9 10 activityInstance.SetStateToExited(context); 11 12 this.OnExit(context, activityInstance); 13 14 if (this.IsFinal) 15 { 16 context.WorkflowInstance.SetStateToCompleted(context); 17 } 18 }
人工活動
1 /// <summary> 2 /// 代表了流程中的一個人工節點。 3 /// </summary> 4 public class ManualActivity : Activity 5 { 6 /// <inheritdoc /> 7 protected override bool CanEnter(WorkflowContext context, ActivityInstance activityInstance) 8 { 9 return true; 10 } 11 12 /// <inheritdoc /> 13 protected override void OnEnter(WorkflowContext context, ActivityInstance activityInstance) 14 { 15 16 } 17 18 /// <inheritdoc /> 19 protected override bool CanExit(WorkflowContext context, ActivityInstance activityInstance) 20 { 21 return true; 22 } 23 24 /// <inheritdoc /> 25 protected override void OnExit(WorkflowContext context, ActivityInstance activityInstance) 26 { 27 var routers = context 28 .WorkflowInstance 29 .Workflow 30 .GetRoutersByFromId(this.Id); 31 32 foreach (var router in routers) 33 { 34 //遇到第一個能執行的路由,進執行路由。 35 if (router.GetCondition().CanRoute(context, activityInstance, router)) 36 { 37 router.Route(context, activityInstance); 38 39 break; 40 } 41 } 42 } 43 }
這里可以看到,人工活動實現了:順序和判定模式,您沒能看到:會簽、審批人規則等,這些會在后面增加進去,目前不是重點。
路由方法的代碼
這里除了分流令牌的創建,其它邏輯還是比較好理解的:創建目標活動實例,創建路由實例,執行目標活動(toActivity.Enter)。
腳本路由條件
1 /// <summary> 2 /// 腳本路由條件。 3 /// </summary> 4 public sealed class ScriptRouterCondition : IRouterCondition 5 { 6 private readonly string _serializedConditionContent; 7 8 /// <summary> 9 /// 構造方法。 10 /// </summary> 11 public ScriptRouterCondition(string serializedConditionContent) 12 { 13 _serializedConditionContent = serializedConditionContent; 14 } 15 16 /// <inheritdoc /> 17 public bool CanRoute(WorkflowContext context, ActivityInstance fromActivityInstance, Router router) 18 { 19 var engine = new ScriptEngine(); 20 21 foreach (var item in context.Agrs) 22 { 23 engine.SetGlobalValue(item.Key, item.Value); 24 } 25 26 return (bool)engine.Evaluate(_serializedConditionContent); 27 } 28 29 /// <inheritdoc /> 30 public string SerializedToString() 31 { 32 return _serializedConditionContent; 33 } 34 }
測試
1 using System; 2 using Microsoft.VisualStudio.TestTools.UnitTesting; 3 using System.Collections.Generic; 4 using System.Linq; 5 6 using Happy.BasicModule.Activities.Domain.Workflows; 7 using Happy.BasicModule.Activities.Domain.WorkflowInstances; 8 9 namespace Happy.BasicModule.Activities.Test 10 { 11 [TestClass] 12 public class ConditionWorkflowTest 13 { 14 [TestMethod] 15 public void Condition_Test() 16 { 17 var workflow = new Workflow 18 { 19 Id = Guid.NewGuid() 20 }; 21 22 workflow.AddActivity(new ManualActivity 23 { 24 Id = Guid.NewGuid(), 25 DisplayName = "A", 26 IsFinal = false, 27 Name = "A" 28 }, true); 29 workflow.AddActivity(new ManualActivity 30 { 31 Id = Guid.NewGuid(), 32 DisplayName = "B1", 33 IsFinal = true, 34 Name = "B1" 35 }); 36 workflow.AddActivity(new ManualActivity 37 { 38 Id = Guid.NewGuid(), 39 DisplayName = "B2", 40 IsFinal = true, 41 Name = "B2" 42 }); 43 44 var routerA_B1 = new Router 45 { 46 Id = Guid.NewGuid(), 47 DisplayName = "A->B1", 48 FromId = workflow["A"].Id, 49 ToId = workflow["B1"].Id 50 }; 51 routerA_B1.SetCondition(new ScriptRouterCondition("性別==\"男\"")); 52 workflow.AddRouter(routerA_B1); 53 54 var routerA_B2 = new Router 55 { 56 Id = Guid.NewGuid(), 57 DisplayName = "A->B2", 58 FromId = workflow["A"].Id, 59 ToId = workflow["B2"].Id 60 }; 61 routerA_B2.SetCondition(new ScriptRouterCondition("性別==\"女\"")); 62 workflow.AddRouter(routerA_B2); 63 64 65 var instance = new WorkflowInstance(workflow); 66 var args = new Dictionary<string, object> 67 { 68 { "性別", "女" } 69 }; 70 71 72 instance.Run(args); 73 var activityInstances = instance.GetActivityInstances(); 74 var routerInstances = instance.GetRouterInstances(); 75 76 Assert.AreEqual(1, activityInstances.Length); 77 Assert.AreEqual(ActivityState.Entered, activityInstances[0].State); 78 Assert.AreEqual(WorkflowState.Running, instance.State); 79 Assert.AreEqual(0, routerInstances.Length); 80 81 82 instance.Resume(activityInstances[0].Id, args); 83 activityInstances = instance.GetActivityInstances(); 84 routerInstances = instance.GetRouterInstances(); 85 86 Assert.AreEqual(2, activityInstances.Length); 87 Assert.AreEqual(ActivityState.Exited, activityInstances[0].State); 88 Assert.AreEqual(ActivityState.Entered, activityInstances[1].State); 89 Assert.AreEqual(WorkflowState.Running, instance.State); 90 Assert.AreEqual(1, routerInstances.Length); 91 92 93 instance.Resume(activityInstances[1].Id, args); 94 activityInstances = instance.GetActivityInstances(); 95 routerInstances = instance.GetRouterInstances(); 96 97 Assert.AreEqual(2, activityInstances.Length); 98 Assert.AreEqual(ActivityState.Exited, activityInstances[0].State); 99 Assert.AreEqual(ActivityState.Exited, activityInstances[1].State); 100 Assert.AreEqual(WorkflowState.Completed, instance.State); 101 Assert.AreEqual(1, routerInstances.Length); 102 Assert.AreEqual("B2", workflow[activityInstances[1].ActivityId].Name); 103 } 104 } 105 }
備注
如果去掉分流和合流,流程還是比較容易處理的,分流和合流的思想會在下一篇文章重點介紹。