update:这个例子是不当的,更合适的例子请自己上javaeye上搜索个帖子。源码俺早就丢了,请不要再发邮件给我,谢谢。
osworkflow扩展非常容易,跟我们的应用结合起来使用也很容易。假设一个请假流程:员工请假,需要经过部门经理和人力资源部经理两人共同审批,只有当两人都许可时才通过,任一人驳回就失效,也就是一个and split和and Join流程,并且我们附加一个要求,当发送请假请求、许可和驳回这几个操作时都将发送一条消息给相应的用户。
流程定义文件如下:
<?
xml version="1.0" encoding="UTF-8"
?>
<!
DOCTYPE workflow PUBLIC "-//OpenSymphony Group//DTD OSWorkflow 2.7//EN" "http://www.opensymphony.com/osworkflow/workflow_2_7.dtd"
>
<
workflow
>
<
initial-actions
>
<
action
id
="0"
name
="开始"
>
<
pre-functions
>
<
function
type
="class"
>
<
arg
name
="class.name"
>
com.opensymphony.workflow.util.Caller
</
arg
>
</
function
>
</
pre-functions
>
<
results
>
<
unconditional-result
old-status
="Finished"
status
="Underway"
step
="1"
owner
="${caller}"
/>
</
results
>
</
action
>
</
initial-actions
>
<
steps
>
<
step
id
="1"
name
="填假单"
>
<
external-permissions
>
<
permission
name
="permA"
>
<
restrict-to
>
<
conditions
type
="AND"
>
<
condition
type
="class"
>
<!--
流程处于Underway状态(流程已经启动)
-->
<
arg
name
="class.name"
>
com.opensymphony.workflow.util.StatusCondition
</
arg
>
<
arg
name
="status"
>
Underway
</
arg
>
</
condition
>
<
condition
type
="class"
>
<
arg
name
="class.name"
>
com.opensymphony.workflow.util.AllowOwnerOnlyCondition
</
arg
>
</
condition
>
</
conditions
>
</
restrict-to
>
</
permission
>
</
external-permissions
>
<
actions
>
<
action
id
="1"
name
="送出"
>
<
restrict-to
>
<
conditions
type
="AND"
>
<
condition
type
="class"
>
<!--
流程处于Underway状态(流程已经启动)
-->
<
arg
name
="class.name"
>
com.opensymphony.workflow.util.StatusCondition
</
arg
>
<
arg
name
="status"
>
Underway
</
arg
>
</
condition
>
<
condition
type
="class"
>
<
arg
name
="class.name"
>
com.opensymphony.workflow.util.AllowOwnerOnlyCondition
</
arg
>
</
condition
>
</
conditions
>
</
restrict-to
>
<
pre-functions
>
<
function
type
="class"
>
<
arg
name
="class.name"
>
com.opensymphony.workflow.util.Caller
</
arg
>
</
function
>
</
pre-functions
>
<
results
>
<
unconditional-result
old-status
="Finished"
split
="1"
status
="Queued"
>
<
post-functions
>
<
function
type
="class"
>
<
arg
name
="class.name"
>
net.rubyeye.leavesys.service.workflow.SendRemindInfFunction
</
arg
>
<
arg
name
="groupName"
>
AND (GROUPNAME='dept_manager' or GROUPNAME='comp_manager')
</
arg
>
<
arg
name
="content"
>
you have leavemsg to check!please check it!
</
arg
>
</
function
>
</
post-functions
>
</
unconditional-result
>
</
results
>
</
action
>
</
actions
>
</
step
>
<
step
id
="2"
name
="部门经理批假单"
>
<
actions
>
<
action
id
="2"
name
="准许"
>
<
restrict-to
>
<
conditions
>
<
condition
type
="class"
>
<
arg
name
="class.name"
>
com.opensymphony.workflow.util.OSUserGroupCondition
</
arg
>
<
arg
name
="group"
>
dept_manager
</
arg
>
</
condition
>
</
conditions
>
</
restrict-to
>
<
pre-functions
>
<
function
type
="class"
>
<
arg
name
="class.name"
>
com.opensymphony.workflow.util.Caller
</
arg
>
</
function
>
<
function
type
="beanshell"
>
<
arg
name
="script"
>
propertySet.setString("action1", "success");
</
arg
>
</
function
>
</
pre-functions
>
<
results
>
<
unconditional-result
old-status
="Finished"
status
="Queued"
join
="1"
owner
="${caller}"
/>
</
results
>
</
action
>
<
action
id
="3"
name
="驳回"
>
<
restrict-to
>
<
conditions
>
<
condition
type
="class"
>
<
arg
name
="class.name"
>
com.opensymphony.workflow.util.OSUserGroupCondition
</
arg
>
<
arg
name
="group"
>
dept_manager
</
arg
>
</
condition
>
</
conditions
>
</
restrict-to
>
<
pre-functions
>
<
function
type
="class"
>
<
arg
name
="class.name"
>
com.opensymphony.workflow.util.Caller
</
arg
>
</
function
>
<
function
type
="beanshell"
>
<
arg
name
="script"
>
propertySet.setString("action1", "fail");
</
arg
>
</
function
>
</
pre-functions
>
<
results
>
<
unconditional-result
old-status
="Finished"
status
="Queued"
join
="2"
owner
="${caller}"
/>
</
results
>
</
action
>
</
actions
>
</
step
>
<
step
id
="3"
name
="公司经理批假单"
>
<
actions
>
<
action
id
="4"
name
="准许"
>
<
restrict-to
>
<
conditions
>
<
condition
type
="class"
>
<
arg
name
="class.name"
>
com.opensymphony.workflow.util.OSUserGroupCondition
</
arg
>
<
arg
name
="group"
>
comp_manager
</
arg
>
</
condition
>
</
conditions
>
</
restrict-to
>
<
pre-functions
>
<
function
type
="class"
>
<
arg
name
="class.name"
>
com.opensymphony.workflow.util.Caller
</
arg
>
</
function
>
<
function
type
="beanshell"
>
<
arg
name
="script"
>
propertySet.setString("action2", "success");
</
arg
>
</
function
>
</
pre-functions
>
<
results
>
<
unconditional-result
old-status
="Finished"
status
="Queued"
join
="1"
owner
="${caller}"
/>
</
results
>
</
action
>
<
action
id
="5"
name
="驳回"
>
<
restrict-to
>
<
conditions
>
<
condition
type
="class"
>
<
arg
name
="class.name"
>
com.opensymphony.workflow.util.OSUserGroupCondition
</
arg
>
<
arg
name
="group"
>
dept_manager
</
arg
>
</
condition
>
</
conditions
>
</
restrict-to
>
<
pre-functions
>
<
function
type
="class"
>
<
arg
name
="class.name"
>
com.opensymphony.workflow.util.Caller
</
arg
>
</
function
>
<
function
type
="beanshell"
>
<
arg
name
="script"
>
propertySet.setString("action2", "fail");
</
arg
>
</
function
>
</
pre-functions
>
<
results
>
<
unconditional-result
old-status
="Finished"
status
="Queued"
join
="2"
owner
="${caller}"
/>
</
results
>
</
action
>
</
actions
>
</
step
>
<
step
id
="4"
name
="停止"
/>
</
steps
>
<
splits
>
<
split
id
="1"
>
<
unconditional-result
old-status
="Finished"
status
="Queued"
step
="2"
/>
<
unconditional-result
old-status
="Finished"
status
="Queued"
step
="3"
/>
</
split
>
</
splits
>
<
joins
>
<
join
id
="1"
>
<
conditions
type
="AND"
>
<
condition
type
="beanshell"
>
<
arg
name
="script"
>
<![CDATA[
"Finished".equals(jn.getStep(2).getStatus()) && "Finished".equals(jn.getStep(3).getStatus())&&"success".equals(propertySet.getString("action1"))&& "success".equals(propertySet.getString("action2"))
]]>
</
arg
>
</
condition
>
</
conditions
>
<
unconditional-result
old-status
="Finished"
status
="Queued"
step
="4"
/>
</
join
>
<
join
id
="2"
>
<
conditions
type
="OR"
>
<
condition
type
="beanshell"
>
<
arg
name
="script"
>
<![CDATA[
"Finished".equals(jn.getStep(2).getStatus()) &&"fail".equals(propertySet.getString("action1"))
]]>
</
arg
>
</
condition
>
<
condition
type
="beanshell"
>
<
arg
name
="script"
>
<![CDATA[
"Finished".equals(jn.getStep(3).getStatus())&&"fail".equals(propertySet.getString("action2"))
]]>
</
arg
>
</
condition
>
</
conditions
>
<
unconditional-result
old-status
="Finished"
step
="4"
status
="Queued"
>
<
post-functions
>
<
function
type
="class"
>
<
arg
name
="class.name"
>
net.rubyeye.leavesys.service.workflow.SendRemindInfFunction
</
arg
>
<
arg
name
="groupName"
>
AND GROUPNAME='employee'
</
arg
>
<
arg
name
="content"
>
you leveamsg is fail!!!
</
arg
>
</
function
>
</
post-functions
>
</
unconditional-result
>
</
join
>
</
joins
>
</
workflow
>
请注意,我们在许可或者通过的时候
propertySet.setString("action2",......),
propertySet.setString("action3",......)
,然后在join点判断,如果两个都是success,流程结束;如果一个是fail,就发送一个消息给员工。 发送消息的function像这样:
package
net.rubyeye.leavesys.service.workflow;
import
java.sql.SQLException;
import
java.util.Map;
import
net.rubyeye.leavesys.domain.RemindInf;
import
net.rubyeye.leavesys.service.ManagerFactory;
import
com.opensymphony.module.propertyset.PropertySet;
import
com.opensymphony.workflow.FunctionProvider;
import
com.opensymphony.workflow.WorkflowException;
public
class
SendRemindInfFunction
implements
FunctionProvider {
public
void
execute(Map transientVars, Map args, PropertySet ps)
throws
WorkflowException { String groupName
=
(String) args.get(
"
groupName
"
); String content
=
(String) args.get(
"
content
"
); RemindInf remindInf
=
new
RemindInf(); remindInf.setContent(content);
try
{ ManagerFactory.getRemindService().addRemindInfByGroupName( groupName, remindInf); }
catch
(SQLException e) { e.printStackTrace(); }
catch
(Exception ex) { ex.printStackTrace(); } } }
文章转自庄周梦蝶 ,原文发布时间5.17
得到两个参数groupName和content(消息内容),调用业务对象发送消息。 完整代码下载在《LeaveSystem》 代码用到了自己过去写的一个MVC框架和持久层,对此有兴趣的参考这三篇文章:《设计自己的MVC框架》《设计模式之事务处理》《使用Annotation设计持久层》 如果仅仅是想了解osworkflow的应用,建议您跑下流程,读读相关几个业务类(LeaveServiceImpl.java,SendRemindInfFunction.java,service包下)即可。解压缩后的文件可以直接导入myeclipse工程,部署在tomcat下,数据库用的是oracle。跑起来以后可以用3个用户登录,test是雇员组,dennis是部门经理组,jordan是公司经理,都不需要密码。写的比较简单,只是实验性质,见谅。 我认为使用osworkflow,只要了解了它的表结构和主要原理,根据你的业务需要结合几张主要表(os_wfentry,os_currentstep,os_historystep等)合理设计数据库和业务流程,可以省去过去为每个业务流程对象创建的一大堆flag(标志,目前的流程状态)的累赘,充分利用工作流的威力。比如为部门经理和人力资源部经理显示不同的需要审批的假单列表,只要结合os_historystep表进行联合查询,部门经理的应该是执行了未执行acion2,step在3的;而人力资源部经理得到的同样是step在3,action未执行3的。 手痒痒,很想把去年为一家公司写的绩效考核系统改写一下,当时设计的一个contract对象拥有7,8个flag来标志合约状态(直接上级审核,人力资源评价,KPI评价等),搞的非常混乱,而且流程写死在代码里,如果以后要改变考核流程,只有重新写过一套。不过那家公司是国有企业,每年的固定的预算费用一定要花掉,反正大家一起赚国家的钱嘛。