YbSoftwareFactory 代码生成插件【十六】:Web 下灵活、强大的审批流程实现(含流程控制组件、流程设计器和表单设计器)
程序=数据结构+算法,而企业级的软件=数据+流程,流程往往千差万别,客户自身有时都搞不清楚,随时变化的情况更是家常便饭,抛开功能等不谈,需求变化很大程度上就是流程的变化,流程的变化会给开发工作造成很大麻烦。而本审批流程具有较强的通用性,同时也有很大的灵活性,虽然无法100%的解决各种非常个性化的审批流程,但至少也能解决其中80%以上的较为通用的流程了。本文将就分享部分心得!
本审批流程从实现上来说由流程设计器、流程控制组件和表单设计器三大部分组成。下面将分别进行描述:
1. 流程设计器
流程设计器基于 Web,使用了JQuery UI、EasyUI、Bootstrape、Knockout.js等等前端框架,可随意设计符合自己的流程,首先看看效果图:

目前的流程库支持5种类型的活动,其中并行活动表示其前面的活动需进行并行审批,其入口事件都执行通过后才允许执行当前活动;分支活动主要用来判定后续到底哪些活动需要进一步执行,分支活动由系统自动根据所设条件自动执行,条件的声明采用 C# 的标准语法;每种活动均设有抵达入口和出口的外部方法接口,可在后台编写好代码后再在界面上进行配置绑定,目的是提供诸如发送邮件通知等类似的接口功能;同时还能随意自定义任何活动上的审批事件,例如保存、上报、回退、驳回、通过、结束等等,如下是事件的参数设置界面:

在此不得不提一下knockout.js,进行属性的绑定和界面的更新实在是太方便了,但就是声明 ViewModel 的定义太麻烦。因此本人此处还用到了其 knockout.mapping 插件,通过 ko.mapping.fromJS(data, mapping, this) 一句代码就可直接把 Json 数据转换为 ViewModel,也能一句代码就能把 ViewModel 转换回要保存的 Json 数据,如果你使用knockout.js 的话,简直没有不使用该插件的道理,可让你从声明 ViewModel 的繁杂体力活中解脱出来。如下是工作流定义从 WebApi 加载后绑定至界面所需的 ViewModel 的创建代码:
var ViewModel = function(data) {
var self = this;
ko.mapping.fromJS(data, mapping, this);
self.StartCount = ko.computed(function() {
var total = 0;
$.each(self.ActivityDefinitions(), function() {
if (this.ActivityType() == 0) total++;
});
return total;
});
self.FinishCount = ko.computed(function() {
var total = 0;
$.each(self.ActivityDefinitions(), function() {
if (this.ActivityType() == 4) total++;
});
return total;
});
self.refresh = function(item) {
ko.mapping.fromJS(item, self);
};}
2. 流程控制组件
流程控制组件主要完成流程的跳转控制,数据的加载和保存等。流程控制组件和前面文章中提到过的底层组件一样,使用了 Provider 模式。因为需要在界面上直接配置自定义跳转执行条件,而在C#中目前还没有类似于 Javascript 的 eval 方法。为实现该功能,需要对字符串脚本进行动态编译,目前.NET 下支持C# 脚本的工具还是很多的,在此先后用过CS-SCRIPT,Javascript.NET和Roslyn,但都没成功。
CS-SCRIPT 很好用,但.NET 4.0 下的版本有问题,虽然是 .NET 4.0 的,但实际上还得安装.NET 4.5,当时在本机测试没任何问题,一旦部署至服务器上就会报错,提示不能加载 System.Runtime.CompilerServices 类型之类的错误,最后发现是其依赖的 Mono.CSharp.dll 的问题;最终的原因也搞明白了个大概,是.NET 4.0 下安装了 .NET 4.5 后会修改默认的 .NET 4.0 底层框架,也就是说,这是升级至 .NET 4.5 带来的兼容性问题,导致在安装了 .NET 4.5 的环境下所生成的针对.NET 4.0 的 Mono.CSharp.dll 组件无法在没安装.NET 4.5 的环境下运行。因为目前很多的服务器环境还是 Windows Server 2003,还没法安装.NET 4.5,很是蛋疼,只有放弃。
Javascript.NET 的最大的特点是使用非常简单,使用JS兼容的语法,但无法传递Dynamic类型的参数。
最后又使用了Roslyn,和Javascript.NET一样,.NET 4.0 版本的貌似不支持 dynamic 类型参数,反正我是没测试成功,估计.NET 4.5 的版本倒是支持,但因很多部署环境还是Windows Server 2003,因此最后还是放弃了。最后没折,只有自己实现了,通过 CSharpCodeProvider 动态编译技术实现了一个Eval方法,一番折腾后发现其实是很简单的,效果也还不错,该 Eval 的代码如下,在此粘出代码共享下劳动成果:
1 /// <summary>
2 /// 动态编译,获取条件表达式的执行结果
3 /// </summary>
4 /// <param name="expression">判断条件表达式</param>
5 /// <param name="objectInstance">对象实例,各个属性和Form表单对应</param>
6 /// <param name="activityInstance">当前执行的活动实例对象</param>
7 /// <returns>编译并执行条件表达式后返回的结果</returns>
8 private object Eval(string expression, dynamic objectInstance, ActivityInstance activityInstance)
9 {
var codeProvider = new CSharpCodeProvider();
var cpt = new CompilerParameters();
//引用程序集
cpt.ReferencedAssemblies.Add("system.core.dll");
cpt.ReferencedAssemblies.Add("system.dll");
cpt.ReferencedAssemblies.Add("Microsoft.CSharp.dll");
//获取对象实例的类型
var type = (Type)objectInstance.GetType();
//获取活动实例对应的程序集路径
var path = activityInstance.GetType().Assembly.Location;
cpt.ReferencedAssemblies.Add(path);
if (type.Assembly.Location != path)
{
cpt.ReferencedAssemblies.Add(type.Assembly.Location);
}
cpt.CompilerOptions = "/t:library";
cpt.GenerateInMemory = true;
var builder = new StringBuilder("");
builder.Append("using System;\n");
builder.Append("using System.Dynamic;\n");
builder.Append("using Yb.Workflow.Provider;\n");
var ns = type.Namespace;
if (ns != "Yb.Workflow.Provider")
{
builder.Append(string.Format("using {0};\n", ns));
cpt.ReferencedAssemblies.Add(activityInstance.GetType().Assembly.Location);
}
builder.Append("namespace CSCodeEvaler{ \n");
builder.Append("public class CSCodeEvaler{ \n");
builder.Append("public bool EvalCode(dynamic objectInstance,ActivityInstance activityInstance){\n");
builder.Append("return " + expression + "; \n");
builder.Append("} \n");
builder.Append("} \n");
builder.Append("}\n");
//在内存中编译
var cr = codeProvider.CompileAssemblyFromSource(cpt, builder.ToString());
if (cr.Errors.Count > )
{
throw new InvalidExpressionException(
string.Format("Error ({0}) evaluating: {1}",
cr.Errors[].ErrorText, expression));
}
var a = cr.CompiledAssembly;
object instance = a.CreateInstance("CSCodeEvaler.CSCodeEvaler");
Type t = instance.GetType();
var mi = t.GetMethod("EvalCode");
//反射调用方法的执行结果
object result = mi.Invoke(instance, new object[] { objectInstance, activityInstance });
return result;
}
3. 表单设计器
表单设计器将在下个版本中集成,当前还只能在流程定义中手动敲入已编辑好的表单内容。表单内容的持久化和加载使用了前面提到过的 ExtensionDataApi,因为支持 .NET 4.0 下的 dynamic 属性,同时提供了一个名为 Properties 的字典来管理所有的属性名和属性值,和 MVC 下的 Request.Form.AllKeys 简直就是绝配,因此可非常方便地和表单界面进行集成。换句话说,你只管设计好表单界面即可,具体表单数据的保存和加载完全可以交由 ExtensionDataApi 来完成。
如需进一步了解,可点击:http://pjdemo.yellbuy.com/
YbSoftwareFactory 代码生成插件【十六】:Web 下灵活、强大的审批流程实现(含流程控制组件、流程设计器和表单设计器)的更多相关文章
- YbSoftwareFactory 代码生成插件【十四】:通过 DynamicLinq 简单实现 N-Tier 部署下的服务端数据库通用分页
YbSoftwareFactory 的 YbRapidSolution for WinForm 插件使用CSLA.NET作为业务层,CSLA.NET的一个强大的特性是支持 N-Tiers 部署.只需非 ...
- YbSoftwareFactory 代码生成插件【十五】:Show 一下最新的动态属性扩展功能与键值生成器功能
YbSoftwareFactory 各种插件的基础类库中又新增了两个方便易用的功能:动态属性扩展与键值生成器,本章将分别介绍这两个非常方便的组件. 一.动态属性扩展 在实际的开发过程中,你肯定会遇到数 ...
- 基于Extjs的web表单设计器 第六节——界面框架设计
基于Extjs的web表单设计器 基于Extjs的web表单设计器 第一节 基于Extjs的web表单设计器 第二节——表单控件设计 基于Extjs的web表单设计器 第三节——控件拖放 基于Extj ...
- 基于Extjs的web表单设计器 第七节——取数公式设计之取数公式的使用
基于Extjs的web表单设计器 基于Extjs的web表单设计器 第一节 基于Extjs的web表单设计器 第二节——表单控件设计 基于Extjs的web表单设计器 第三节——控件拖放 基于Extj ...
- 基于Extjs的web表单设计器 第五节——数据库设计
这里列出表单设计器系列的内容,6.7.8节的内容应该在春节后才有时间出了.因为这周末就请假回老家了,准备我的结婚大事.在此提前祝大家春节快乐! 基于Extjs的web表单设计器 基于Extjs的web ...
- 基于Extjs的web表单设计器 第三节——控件拖放
看过之前设计器截图的朋友应该有印象,可能会发觉我们的设计器UI设计布局其实类似Visual studio 的设计界面,采用的是左.中.右三个区域布局.左侧为控件区域.中间为表单的画布设区域.右侧为属性 ...
- Unit01: Web概述 、 HTML概述 、 文本处理 、 图像和超链接 、 表格 、 表单
Unit01: Web概述 . HTML概述 . 文本处理 . 图像和超链接 . 表格 . 表单 demo1.html <!-- 声明网页的版本(文档类型) --> <!doctyp ...
- .net web 开发平台- 表单设计器 一(web版)
如今为了适应需求的不断变化,动态表单设计器应运而生.它主要是为了满足界面的不断变化和提高开发速度.比如:一些页面客户可能也无法确定页面的终于布局,控件的位置,在哪种情况下显示或不显示等可能须要随时改动 ...
- 基于Extjs的web表单设计器 第一节
前面一节介绍了表单设计器的背景和最终的大概样式,本节主要介绍表单设计器的需求及功能设计. 在讲需求之前先明确几个常用的概念: 主表或者卡片表——具有多行多列的一个区域的控件块,如下图所示. 明细表—— ...
随机推荐
- 使用WWW获取本地文件夹的XML配置文件
Unity3D读取本地文件可以使用Resources.Load来读取放在Resources文件夹下的文件,如果不是放在该文件夹下,则可以通过WWW类来读取. 譬如读取xml的配置文件. /// < ...
- USACO翻译:USACO 2014 JAN三题(2)
USACO 2014 JAN 一.题目概览 中文题目名称 队伍平衡 滑雪录像 滑雪场建设 英文题目名称 bteams recording skicourse 可执行文件名 bteams recordi ...
- [sourceTree]这是一个无效的源路径
解决方法:工具 ——>选项 ——> git, 启用git 就可以了.
- MongoDB的配置、启动、关闭
MongoDB 是一个基于分布式文件存储的数据库.由 C++ 语言编写.旨在为 WEB 应用提供可扩展的高性能数据存储解决方案. MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系 ...
- Public DNS (公共域名解析服务)
114DNS 服务IP:114.114.114.114, 114.114.115.115 拦截 钓鱼病毒木马网站:114.114.114.119, 114.114.115.119 拦截 色情网站:11 ...
- JAVA算法
一.归并排序 归并排序 (merge sort) 是一类与插入排序.交换排序.选择排序不同的另一种排序方法.归并的含义是将两个或两个以上的有序表合并成一个新的有序表.归并排序有多路归并排序.两路归并 ...
- [翻译]类型双关不好玩:C中使用指针重新解释是坏的
原文地址 Type punning isn't funny: Using pointers to recast in C is bad. C语言中一个重新解释(reinterpret)数据类型的技巧有 ...
- 基于FormsAuthentication的用户、角色身份认证
一般情况下,在我们做访问权限管理的时候,会把用户的正确登录后的基本信息保存在Session中,以后用户每次请求页面或接口数据的时候,拿到 Session中存储的用户基本信息,查看比较他有没有登录和能否 ...
- WindowsService 安装 cmd
步骤: 1.运行--〉cmd:打开cmd命令框 2.在命令行里定位到InstallUtil.exe所在的位置 InstallUtil.exe 默认的安装位置是在C:/Windows/Microsoft ...
- idea使用心得(4)-踩过的坑
1.非法的表达式开始 / 需要';' / 未结束的字符串文字 表现形式: 原因/解决: 这个一定是文件编码问题:依次检查setting中的file Encodings 中的IDE ...