emap-flow
开发手册
1. 环境准备
1) 获取emap开发环境:参考EMAP开发手册
2) 通过EMAP开发工具获取emapflow流程应用:参考如emapAuth等应用获取方式,不再赘述,如图:
3) 引用emapflow应用接口包:参考emap开发手册关于“引用pub_classes”开发包,不再赘述
2. 流程开发与发布
PS:开发流程前请参考EMAP开发应用手册将基本的业务功能开发完毕,即确保除流程流转以外如信息录入、修改、统计查询等功能基本开发完毕。
1) 数据库配置
emapflow的数据源默认使用EMAP的默认数据源DB_EMAP_BIZ_BASE,这样流程服务在启动时会将流程系统表自动创建到该数据库中
PS:请确保系统启动正确无误
2) 访问流程控制台(待办任务、流程实例、流程定义、流程状态)
系统启动成功,利用账户登录系统后,可以访问如下地址进入流程控制台:http://localhost:8080/emap/sys/emapflow/tasks/queryMyTask.do(IP和端口可自定),如图:
3) 创建流程文件
在需要创建流程文件的应用文件夹“工作流”上右击新建“工作流”,填写必要的编号和名称。注意流程编号在一个应用下必须保证唯一,如图:
点击“确定”后工具会生成一个流程定义,如图:
4) 流程节点说明
a) 流程定义:鼠标点击画布空白区域时会显示流程本身的属性定义,除了名称可修改外其他都不可修改
监听:可以在流程启动时和流程结束时利用事件机制编写表达式以完成相关业务逻辑
流程启动事件(start):流程开始时会触发该事件,如图所示:
流程结束事件(end):流程结束时会触发该事件,如图所示:
b) 开始(StartEvent):流程开始节点,默认配置基本不用修改,如果需要修改也是可以的
c) 结束(EndEvent):流程结束节点,同“开始”节点
d) 文本注释及注释线:两者结合起来能够对各个节点进行注释说明,在流程中无功能作用,如图:
e) 泳池与泳道:将处理角色通过一个显示的方式组织起来,能够方便阅读,本身无功能作用,如图:
f) 人工任务:工作流的核心节点,主要配置说明:
i. 处理人:
ii. 处理命令按钮
iii. 处理表单
iv. 多实例(会签):人工任务的会签需要根据审批人员的个数进行计算,如图:
PS:如果会签环节之后有分支判断必须加上网关,如图:
g) 排他网关与线条:排他网关需与线条配合使用,配合线条的“条件”表达式完成流程的流转分支判断(注意只能满足一个线条条件,即该网关只能触发一个线条的任务),如图:
h) 并行网关与线条:线条不再需要配置条件,默认全部都会第一时间触发任务,如图:
i) 包容网关与线条:线条需要配置条件,与排他网关的区别是只要线条满足条件都会触发任务(不仅仅触发一个线条的任务),如图:
j) 信号抛出与捕获:流程与流程之间或流程内部通信的一种手段,如A流程在某个任务环节进行之前必须接收到B流程的某一步发出信号才能触发、或A流程内部某个任务环节进行之前必须接收到同样是A流程的某一步发出信号才能触发。
● 配置信号定义:配置信号的唯一标识符用以区分是什么信号,如图:
● 配置信号抛出:在某个流程环节抛出信号让其他相同信号定义的流程或环节可以捕获该信号,如图:
● 配置信号捕获:在某个流程环节捕获信号让流程能继续流转下去,如图:
k) 外部子流程:一个流程是可以发起启动其他流程定义,这样可以很好的将流程抽象并封装(如统一的付款流程)。注意主流程必须等待子流程结束才能继续执行,如图:
l) 邮件任务:可以给指定邮箱发送邮件,如图:
spring.xml中邮件服务器配置,如图:
m) 定时启动:可以指定时间启动流程,如图:
à时间设置,如下图所示:
à假设设置时间为流程启动之后的半分钟,如图:
n) 时间边界:对任务可以指定时间处理
à时间设置同“定时启动”的时间设置,不再赘述;
à假设设置时间为任务到达当前时间之后半分钟,如图:
当时间走过半分钟之后,引擎会立即将立即执行到“协助处理”,如图:
o) 时间捕获:可以在流程中设置时间将流程中断一段时间
à时间设置同“定时启动”的时间设置,不再赘述;
à常用corn表达式:
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
0 0 12 ? * WED 表示每个星期三中午12点
"0 0 12 * * ?" 每天中午12点触发
"0 15 10 ? * *" 每天上午10:15触发
"0 15 10 * * ?" 每天上午10:15触发
"0 15 10 * * ? *" 每天上午10:15触发
"0 15 10 * * ? 2005" 2005年的每天上午10:15触发
"0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发
"0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发
"0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
"0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发
"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发
"0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发
"0 15 10 15 * ?" 每月15日上午10:15触发
"0 15 10 L * ?" 每月最后一日的上午10:15触发
"0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发
5) 流程发布:创建好流程文件后需要将它发布到数据库中才能运行,如图:
然后登录到流程控制台的“流程定义”中就能看到刚刚发布的流程,如图:
3. 流程运行
1) 流程定义
列表展现了在一个EMAP底座上所有的流程定义,这些定义都是可发起可启动的,如图:
● 启动:发起一个流程,如启动“报销申请”发起报销的流程
● 挂起:被挂起的流程处于暂时“不可用”状态,流程不能再启动
● 激活:将被挂起的流程恢复到可启动状态,与“挂起”功能相反
● 删除:将整个流程定义删除掉(如果该流程定义存在尚未流转完毕的流程实例则不能删除,要么先“删除实例”,要么等待流程流转完毕)
● 终止实例:将所有该流程定义所发起的并且是正在运行的实例全部终止并保存历史记录
● 清除实例:删除所有该流程定义发起的流程,不保存历史记录
● 设计:在线设计并修改流程定义。如图:
2) 启动:流程启动,点击控制台“流程定义”,列出流程定义列表,点击“流程编号”这一列会显示该流程定义对应的流程图,如图:
点击“启动”展现业务表单,如图:
3) 提交:一旦流程启动后,换审批人登录后会接收到待办任务,点击“待办任务”后显示如图:
点击“处理”打开之前提交的业务申请表单,如图:
之后可以根据需要点击不同的按钮进行任务处理。
4) 流程实例
反映的是每一个流程(如报销审批)启动后整个流程的进行情况,用户可以根据自身在流程中所处角色(如报销的某一步审批人)直接跟踪该流程的进展情况,分为已结束、未结束流程实例,如图:
● 挂起:可以将该流程实例挂起,如果挂起一个流程实例那么该流程的所有进行中的任务不能再进行下去、处于中断状态
● 激活:将已挂起的流程实例恢复为可流转状态,与“挂起”功能相反
● 删除:将该流程实例直接删除,避免误申请或流程异常脏数据
● 退回:退回操作将流程退回至某个节点,目标节点可在设置按钮时设置(如果没有设置退回节点默认退回到第一个环节),如图所示:
退回后流程将从退回到的节点继续执行。
à退回前
à退回后
● 取回:在当前节点审批完成,下一节点尚未审批时,可在待办任务的“已完成”中进行取回,取回后,将在当前节点重新审批(如若出现“追回失败”提示则表示该任务不满足上述追回的条件),如图所示:
à取回前:
à取回后:
● 办结取回:对已经走完的流程,可执行办结取回,执行后将在保留历史审批记录的前提下使该流程重新流转。在“已完成”实例中可取回,如图所示:
执行办结取回后,流程实例变为未结束状态,办结取回后实例状态如下图:
● 加签减签:会签中可进行加签减签,添加/减少一个审批人(任务);按钮如图所示:
à加签前(会签中共2个审批人):
à加签后(会签中共3个审批人):
减签(退签)同理可知(不再赘述)。
● 选择下一步执行节点:流程在当前节点可手动选择下一步从某个节点继续执行
5) 流程状态
真实的反映一个流程实例的进展情况列表,如图:
● 进度图
真实的反映当前流程实例进展情况,一般配合下面的列表共同反映,如图:
绿色打钩图形表示已经执行完毕的任务环节,灰色的图形表示当前所处的流程任务环节
● 列表详情
详细反映每个任务环节执行人、开始时间、结束时间、耗时、意见等信息,如图:
列表以“开始时间”递增进行排序,记录自流程开始运转到结束的详细信息
● 流程图
反映当前流程实例在整个流程运转中所处的位置,它与上面的“进度图”不同的在于可以宏观的反映目前任务在整个流程定义中所处的环节,如图:
红色框表示目前所处的任务环节位置
4. 表达式
a) 示例
b)引擎内置的三个变量:execution、task、authenticatedUserId
c) 说明:
i. 所有自定义JavaBean变量均需实现序列化接口Serializable,引擎可以保存一个自定义的JavaBean对象到数据库,但必须保证该JavaBean实现了该接口,否则引擎报异常
ii. 上面三个内置变量仅能通过表达式直接获取
iii. 上图中的${serviceBean.confirm()}需要改为${appName_serviceBean.confirm()}
5. 事件处理
1) 处理命令(CommandEventListener):
以及实现接口时引擎会传递给方法的参数,如图:
2) 人工任务事件(略)
6. 注意事项
1) 流程引擎默认不支持在同一个数据库实例下初始化流程表(既不能在同一个schema下使用数据库多用户初始化表),如果需要支持多数据源在spring.xml中配置,如图:
2) 业务表必须存在WID唯一键(可以不是主键)
3) 业务表必须存在processinstanceid字段,其他字段可以根据需求自行增加
4) 暂不支持流程定义多版本,重新发布流程须将原有流程流转完毕;重新发布会强制替换原版本,如果原有流程没有流转完毕则发布不会生效
5) 会签用处理人集合作为变量(类型必须是集合)
6) 表单变量默认映射流程变量,并且是持久化的;获取表单变量的表达式是${formData.key}
7) 访问bean的表达式必须通过应用名称去获取:${appName_serviceBean.method()}
8) 用户组动态分配、且任务分配时引擎判断该用户组是否包含子用户组并分拆组存入处理人的任务分配;待办任务必须选择子用户组登录,否则会出现重复待办
9) 表单URL参数:instId-实例编号;nodeId-环节编号;taskId-任务编号;defKey-流程定义编号;bizkey-业务唯一键WID;flowComment-审批意见
10) “用户任务”环节选择“候选组”目前可以使用功能角色代替流程角色,引擎会在以后的版本会更新使用流程角色
11) “流程状态”按钮点击会弹出窗口,在Chrome下默认会被拦截,请去掉拦截即可显示当前流程运行状态
12) 引擎默认会在执行提交按钮时(如审批时)自动加入“同意”两字作为审批意见,如果想自定义审批意见只需将前端控件(如文本框)的name命名为“flow_comment”、然后将该控件的值在提交表单前传给流程引擎即可;
13) 只要有分支判断条件就必须在每个线条上加上条件,如图:
14) 部署上线后如果需要修改流程环节处理人,则须利用ODI方式将部门和教职工的数据分别导入到“教职工信息表”和“部门信息表”,如图所示:
如果没有同步这两张表数据则在流程环节上选择处理人的地方将无法显示部门和教职工信息,如图:
15)
7. 常用接口与方法
1. 获取流程某个人工任务环节的任务(常见的业务场景:班主任需要查询班主任的审批列表、辅导员需要查询辅导员的审批列表,包括待办、已办、全部任务)
➢ 在index.jsp中引入emapflow.js,如图:
<script src="<%= request.getContextPath() %>/sys/emapflow/public/js/emapflow.js"></script>
➢ 前端调用,如图:
代码粘贴如下:
var params = {
taskType : 'NOTEND', //未完成-NOTEND,已完成-ENDED,所有-ALL
nodeId : 'usertask2', //流程定义中人工环节的编号,必填
appName : 'student_app',//应用的名称,必填
module : 'modules', //模块名,可以没有,默认modules
page: 'xsxxdbwh', //回调动作的epg的编号,必填
action : 'T_PXXX_XSJBXX_QUERY' //回调的动作,必填
};
● taskType是查询待办、或已办、或待办和已办全部数据(未完成-NOTEND、已完成-ENDED、当前登录用户所有任务-ALL、全部用户所有任务-ALL_USER)
● nodeId是流程定义的环节编号,必填,写成常量即可(如果多个审批环节对应一个审批表单nodeId可以不传),如图:
● appName是下面查询数据的应用名称(引擎需要知道调用哪个应用的action),与下面的action动作结合起来就是调用“appName.action”
● module表示模块名,目前EMAP中默认是“modules”这一层目录名,该参数可以不传,默认引擎会加上“modules”
● action表示需要调用的业务表查询信息,具体写法见epg中配置动作的名称
● 还需加上固定的参数,如图:
其中params即为上面的参数params,其他的参数可以固定写死,代码粘贴如下:
url: _emapflow.getQueryTasksByNodeUrl(),
action: _emapflow.getQueryTasksByNodeAction(),
datamodel: _emapflow.getDataModel(params),
params : params, //参数
➢ 返回值:该动作的返回值来源于调用的appName.action,不同的是在查询结果中会自动加入当前的任务编号taskId,如下描述:
● 原生的appName.action返回值,如图:
● 引擎查询返回值,如图:
其中“queryUserTasksByNode”动作是引擎调用,可以不必关注;而业务数据JSON中带有taskId,在打开审批页面的时候就能taskId传给表单的工具栏toolbar标签从而能将toolbar标签加载出来
● 引擎查询返回流程实例状态值、当前环节编号、流程任务状态值,如图:
flowStatus:状态值
flowStatusName:状态值名称
PS:(1-审核中;2-已驳回;3-已完成;4-草稿;5-已终止;0-未知状态)
nodeId:人工任务环节编号
taskStatus:(0-已办;1-待办)
PS:状态值对应状态名称,状态名称可以进行自定义,通过修改ROOT目录下的emap.properties可完成自定义操作,要注意:状态值不能修改,只能改状态名称,且状态值与状态名称的对应顺序不能错位,必须按照修改前对应关系一一对应。状态名称可只进行部分修改,若只修改状态值1对应的状态名称则只有1状态对应的名称会变化,其余状态值对应名称依旧会是默认值。
➢ 修改的配置文件位置和内容如下图
例如,将上图配置文件中 1-XXXXX 修改为 1-审核,则有以下效果,可以看到“审核中”的状态变成了“审核”,两者都表示状态值为1的状态
2. 自定义获取用户、角色或用户组
➢ 如果自己实现获取用户和角色信息,需要实现接口ICustomUserAndGroup,如图所示:
➢ 在ROOT工程下emap.properties中需要注册自己的实现类
假设该自定义的实现类所在的应用名称是cszgl,实现类的beanId是CustomUserAndGroup(注意是beanId不是类名),那么配置为:identity_manager_implements = csgzl/CustomUserAndGroup,如下图:
➢ 如果只需众多方法中的某些方法,比如只实现getUserName,那么你需要这样去处理,如图所示:
默认代码片段(可拷贝):
private ICustomUserAndGroup getCustomUserAndGroup() {
AppBeanContainer<ICustomUserAndGroup> container = new AppBeanContainer<ICustomUserAndGroup>("emapflow",
"FlowCustomUserAndGroup", true);
return container.get();
}
➢
3. 自定义业务数据保存和更新
如果业务需要自己编写业务表的保存或更新,首先推荐EMAP的Java动作(EMAP底座必须在156版本以上);若不再使用任何EMAP的动作进行数据操作,那么需要继承并实现接口BusinessDataOperateInterface,如图所示:
假设实现类为com.wisedu.emapflow.output.BusinessDataOperateImpl,如图:
那么在按钮选择动作的地方需要配置为如下所示即可:
PS:无论是Java动作还是自定义保存的saveFormData()方法都必须返回string类型的WID值,否则流程数据会出现异常;而updateFormData()返回int类型值,该值对流程运行无影响。
4. 常用后端流程运行接口(详细参加《流程后端运行接口》),打开其中的“index.html”,显示如图:
5. 流程运行http接口
● 启动
a) 入参
参数 |
值 |
formData |
{"WID":"*********","SQR":"*******"}(表单数据) |
commandType |
start(固定值) |
action |
model:T_TEMP_JDSQ.ADD(业务表数据插入动作) |
execute |
do_start(固定值) |
defKey |
csgzl.test(流程定义编号) |
b) 出参
● 草稿
a) 入参
参数 |
值 |
formData |
{"WID":"*********","SQR":"*******"}(表单数据) |
commandType |
draft(固定值) |
action |
model:T_TEMP_JDSQ.SAVE(业务表数据保存动作) |
execute |
do_start(固定值) |
defKey |
csgzl.test(流程定义编号) |
b) 出参
● 提交
a) 入参
参数 |
值 |
formData |
{"WID":"*********","SQR":"*******"}(表单数据) |
commandType |
submit(固定值) |
action |
model:T_TEMP_JDSQ. MODIFY(业务表数据更新动作) |
execute |
do_submit(固定值) |
defKey |
csgzl.test(流程定义编号) |
taskId |
1005024(任务编号,可以提交多个任务编号、并以逗号分隔实现任务批量审批,如1005024, 1005025) |
b) 出参
● 退回
a) 入参
参数 |
值 |
formData |
{"WID":"*********","SQR":"*******"}(表单数据) |
commandType |
turnback(固定值) |
action |
model:T_TEMP_JDSQ. MODIFY(业务表数据更新动作) |
execute |
do_turnback(固定值) |
defKey |
csgzl.test(流程定义编号) |
taskId |
1005024(任务编号) |
backTO |
usertask1(流程人工环节编号) |
b) 出参
● 终止流程
a) 入参
参数 |
值 |
formData |
{"WID":"*********","SQR":"*******"}(表单数据) |
commandType |
termination(固定值) |
action |
model:T_TEMP_JDSQ. MODIFY(业务表数据更新动作) |
execute |
do_termination(固定值) |
defKey |
csgzl.test(流程定义编号) |
taskId |
1005024(任务编号) |
b) 出参
● 追回
a) 入参
参数 |
值 |
formData |
{"WID":"*********","SQR":"*******"}(表单数据) |
commandType |
callback(固定值) |
taskId |
1005024(任务编号) |
b) 出参
● 转办
c) 入参
参数 |
值 |
formData |
{"WID":"*********","SQR":"*******"}(表单数据) |
commandType |
changeAssignee(固定值) |
action |
model:T_TEMP_JDSQ. MODIFY(业务表数据更新动作) |
execute |
do_change_assignee(固定值) |
defKey |
csgzl.test(流程定义编号) |
taskId |
1005024(任务编号) |
assignee |
1003(被转人员的编号或工号) |
d) 出参
8. 示例集成
1. 应用名称:工作流示例(csgzl)
2. 主要关注点:
➢ 数据模型:T_TEMP_JDSQ,如图:
➢ 页面:index、jdsq_temp、xzsh_temp、scsh_temp,如图:
➢ 流程文件:测试(test),如图:
➢ 任务环节配置:审批按钮和任务处理人,如图:
➢ 登录系统,访问页面,如图:
➢ 发起申请开始流程,如图:
➢ 按角色审批流程(用角色‘GN001’登录,其他角色登录无法看到待办),如图:
点击“流程状态”,如图:
3. 改造步骤:
➢ 集成流程按钮toolbar:
1) 初始化(引用放入index.jsp中)
<link rel="stylesheet" href="http://res.wisedu.com/bower_components/element-ui/lib/theme-default/index.css"/>
<script src="http://res.wisedu.com/bower_components/vue2/vue.min.js"></script>
<script src="http://res.wisedu.com/bower_components/element-ui/lib/index.js"></script>
<script src="<%=request.getContextPath() %>/sys/emapflow/public/js/emap-h5tag.min.js "></script>
2) 改造表单按钮加载(一般在xxxxxSave.html中)
<div id="app">
<emap-flow-toolbar :flowcomment="flow_comment" :submitformdata="formdata" :defkey="defkey" @action_complete="action_complete" :taskid="taskid" :onverify="btnclick"></emap-flow-toolbar>
<div class="bh-clearfix"></div>
</div>
3) 改造表单按钮执行(一般在xxxxxSave.js中)
var flow = new Vue({
el : '#app',
data : function() {
return {
formdata:{},
flow_comment: "",
defkey:"student_app.myFlow",
taskid:""
};
},
methods : {
btnclick : function(type, result) {
debugger;
// this.formdata = $("#emapForm").emapForm("getValue");
return true;
},
action_complete : function(type, result) {
if(type === "processStatus"){
} else {
//重新加载数据
$.bhPaperPileDialog.hide({
close:function() {
// $('#emapdatatable').emapdatatable('reload');
}
});//关闭当前弹窗
}
}
}
});
PS:
l 注意上图中审批意见“flow_comment”的传值
l 注意流程按钮(如启动、提交等)的处理结果返回值在action_complete方法的result中
4) 参数说明
参数 |
说明 |
类型 |
默认值 |
可选值 |
defkey |
流程定义id |
String
|
|
|
taskid |
任务id |
String
|
系统自动生成 |
|
submitformdata |
表单数据 |
Object
|
|
|
onverify |
按钮点击时的前置校验 |
Function
|
|
|
onbtnrender |
自定义按钮渲染函数 |
Function
|
|
|
flowcomment |
流程备注 |
String
|
|
|
5) 事件
事件名称 |
说明 |
回调参数 |
action_complete |
按钮触发完毕后 |
按钮类型 , 参数
|
➢ 查询待办/已办/全部任务(一般在xxxxx.js文件中初始化表格_initTable方法处),具体方法请参见“7.常用接口与方法”中的“获取流程某个人工任务环节的任务”的方法,而这里只截图说明:
只需在查询任务列表的地方加入如上代码即可,参数说明参见“7.常用接口与方法”,不再赘述。
PS:如果多个审批环节对应一个审批表单只需要不传“nodeId”即可。