背景
在上篇文章(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 }
备注
如果去掉分流和合流,流程还是比较容易处理的,分流和合流的思想会在下一篇文章重点介绍。