自定义工作流 之 模型设计与实现
2013-07-23 08:31
477 查看
背景
在上篇文章(Workflow:自定义工作流 之 模型选择)介绍了模型的选择,这篇文章就介绍一下模型的设计与实现。有些朋友会希望在这里看到:数据库、持久化或审批人角色处理等代码,我是领域驱动设计(DDD)的爱好者,因此很长一段时间内您是看不到这些代码的,我觉得这些不是模型的核心。
模型设计
概念模型
模型规则如下
1、系统有活动(方块或圆形)和路由(线条)组成,每种类型的活动支持不同的路由规则。
2、方块代表人工活动,人工活动只能路由到一个目标节点,可以定义多个路由,但是只有一个路由会执行,这让模型支持:顺序和判定。
顺序执行
判定执行
3、圆形代表并行活动,Split(分流)和Join(合流)必须成对出现,Split会导致多个活动并行执行,Join会合并这些并行执行的活动,这让模式支持:并行。
并行执行
设计模型
工作流中涉及两块设计模型,一、定义模型(流程本身);二、实例模型(流程的运行实例)。定义模型
实例模型
我想类图就没有啥解释的,这些类图非常直观的反映了概念模型,让我们直接去看实现。
实现
活动基类这里定义活动可以Enter(进入)和Exit(离开),当然前提是他们CanEnter(能进入)和CanExit(能离开),如果他们Enter或Exit了,会触发OnEnter或OnExit。
Enter方法
internal void Enter(WorkflowContext context, ActivityInstance activityInstance) { activityInstance.SetStateToEntering(context); if (!this.CanEnter(context, activityInstance)) { return; } activityInstance.SetStateToEntered(context); this.OnEnter(context, activityInstance); }
Exit方法
internal void Exit(WorkflowContext context, ActivityInstance activityInstance) { activityInstance.SetStateToExiting(context); if (!this.CanExit(context, activityInstance)) { return; } activityInstance.SetStateToExited(context); this.OnExit(context, activityInstance); if (this.IsFinal) { context.WorkflowInstance.SetStateToCompleted(context); } }
人工活动
/// <summary> /// 代表了流程中的一个人工节点。 /// </summary> public class ManualActivity : Activity { /// <inheritdoc /> protected override bool CanEnter(WorkflowContext context, ActivityInstance activityInstance) { return true; } /// <inheritdoc /> protected override void OnEnter(WorkflowContext context, ActivityInstance activityInstance) { } /// <inheritdoc /> protected override bool CanExit(WorkflowContext context, ActivityInstance activityInstance) { return true; } /// <inheritdoc /> protected override void OnExit(WorkflowContext context, ActivityInstance activityInstance) { var routers = context .WorkflowInstance .Workflow .GetRoutersByFromId(this.Id); foreach (var router in routers) { //遇到第一个能执行的路由,进执行路由。 if (router.GetCondition().CanRoute(context, activityInstance, router)) { router.Route(context, activityInstance); break; } } } }
这里可以看到,人工活动实现了:顺序和判定模式,您没能看到:会签、审批人规则等,这些会在后面增加进去,目前不是重点。
路由方法的代码
这里除了分流令牌的创建,其它逻辑还是比较好理解的:创建目标活动实例,创建路由实例,执行目标活动(toActivity.Enter)。
脚本路由条件
/// <summary> /// 脚本路由条件。 /// </summary> public sealed class ScriptRouterCondition : IRouterCondition { private readonly string _serializedConditionContent; /// <summary> /// 构造方法。 /// </summary> public ScriptRouterCondition(string serializedConditionContent) { _serializedConditionContent = serializedConditionContent; } /// <inheritdoc /> public bool CanRoute(WorkflowContext context, ActivityInstance fromActivityInstance, Router router) { var engine = new ScriptEngine(); foreach (var item in context.Agrs) { engine.SetGlobalValue(item.Key, item.Value); } return (bool)engine.Evaluate(_serializedConditionContent); } /// <inheritdoc /> public string SerializedToString() { return _serializedConditionContent; } }
测试
using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Collections.Generic; using System.Linq; using Happy.BasicModule.Activities.Domain.Workflows; using Happy.BasicModule.Activities.Domain.WorkflowInstances; namespace Happy.BasicModule.Activities.Test { [TestClass] public class ConditionWorkflowTest { [TestMethod] public void Condition_Test() { var workflow = new Workflow { Id = Guid.NewGuid() }; workflow.AddActivity(new ManualActivity { Id = Guid.NewGuid(), DisplayName = "A", IsFinal = false, Name = "A" }, true); workflow.AddActivity(new ManualActivity { Id = Guid.NewGuid(), DisplayName = "B1", IsFinal = true, Name = "B1" }); workflow.AddActivity(new ManualActivity { Id = Guid.NewGuid(), DisplayName = "B2", IsFinal = true, Name = "B2" }); var routerA_B1 = new Router { Id = Guid.NewGuid(), DisplayName = "A->B1", FromId = workflow["A"].Id, ToId = workflow["B1"].Id }; routerA_B1.SetCondition(new ScriptRouterCondition("性别==\"男\"")); workflow.AddRouter(routerA_B1); var routerA_B2 = new Router { Id = Guid.NewGuid(), DisplayName = "A->B2", FromId = workflow["A"].Id, ToId = workflow["B2"].Id }; routerA_B2.SetCondition(new ScriptRouterCondition("性别==\"女\"")); workflow.AddRouter(routerA_B2); var instance = new WorkflowInstance(workflow); var args = new Dictionary<string, object> { { "性别", "女" } }; instance.Run(args); var activityInstances = instance.GetActivityInstances(); var routerInstances = instance.GetRouterInstances(); Assert.AreEqual(1, activityInstances.Length); Assert.AreEqual(ActivityState.Entered, activityInstances[0].State); Assert.AreEqual(WorkflowState.Running, instance.State); Assert.AreEqual(0, routerInstances.Length); instance.Resume(activityInstances[0].Id, args); activityInstances = instance.GetActivityInstances(); routerInstances = instance.GetRouterInstances(); Assert.AreEqual(2, activityInstances.Length); Assert.AreEqual(ActivityState.Exited, activityInstances[0].State); Assert.AreEqual(ActivityState.Entered, activityInstances[1].State); Assert.AreEqual(WorkflowState.Running, instance.State); Assert.AreEqual(1, routerInstances.Length); instance.Resume(activityInstances[1].Id, args); activityInstances = instance.GetActivityInstances(); routerInstances = instance.GetRouterInstances(); Assert.AreEqual(2, activityInstances.Length); Assert.AreEqual(ActivityState.Exited, activityInstances[0].State); Assert.AreEqual(ActivityState.Exited, activityInstances[1].State); Assert.AreEqual(WorkflowState.Completed, instance.State); Assert.AreEqual(1, routerInstances.Length); Assert.AreEqual("B2", workflow[activityInstances[1].ActivityId].Name); } } }
备注
如果去掉分流和合流,流程还是比较容易处理的,分流和合流的思想会在下一篇文章重点介绍。相关文章推荐
- 自定义工作流 之 模型设计与实现
- WF4实现工作流驳回流转模型的几种设计方案
- WF4实现工作流驳回流转模型的几种设计方案
- 【翻译】eXpressAppFramework QuickStart 业务模型设计(四)—— 实现自定义业务类
- (纯转载)一个真正符合中国国情的工作流设计参考(包括PHP实现)
- iPhone 6 / 6 Plus 出现后,如何改进工作流以实现一份设计稿支持多个尺寸
- iPhone 6/6 Plus 出现后,如何改进工作流以实现一份设计稿支持多个尺寸?
- Android UI设计之<二>自定义SwitchButton开关,实现类似IOS中UISwitch的动画效果
- 一个真正符合中国国情的工作流设计参考(包括PHP实现)
- 工作流模型设计 (引)
- 可自管理的分布式工作流(workflow)引擎的设计与实现 (2-2)
- 一个真正符合中国国情的工作流设计参考(包括PHP实现)
- 一套完整自定义工作流的实现
- 【android开发】自定义数字软键盘的设计与实现(2)
- 如何实现自定义工作流WF4
- 一套完整自定义工作流的实现
- Samba 文件服务器用户复杂权限模型设计和实现 v0.1b
- 模块管理常规功能自定义系统的设计与实现(41--终级阶段 综合查询[8]分类汇总)
- Samba 文件服务器用户复杂权限模型设计和实现
- 模块管理常规功能自定义系统的设计与实现(56--开源开发测试版发布 )