该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间:2007-07-12
这个小业务系统的主要目的 演示如何基于OSWorkflow构建业务系统,即如何将原来程序内硬编码的业务过程抽取出来,放到底层的工作流引擎中,上层业务模块只进行具体业务动作的执行,同时演示着两层如何“集成”。 本系统演示三个方面:业务处理和OSWorkflow的基本“集成”、工作流引擎根据工作流定义调用业务处理逻辑、基于业务数据的工作流跳转 业务用例描述 核心工作流: 1、员工填写请假申请单,包括“请假原因”和“请假天数”,填写后进行提交; 2、部门主管对员工请假申请进行审批,同意员工请假; 3、人力资源主管对员工请假申请进行审批,同意员工请假; 4、系统发送邮件通知员工请假申请已获得批准; 5、用例结束; 备选工作流1: 在第一步中,如果员工请假天数小于等于3天,那么不需要部门主管审批,直接从第一步跳到第三步,又人力资源审批; 备选工作流2: 在第二步中,如果部门主管不同意请假申请,那么系统将给请假员工发送请假没有通过的邮件通知,用例结束; 备选工作流3: 在第三步中,如果人力资源主管不同意请假申请,那么系统将给请假员工发送请假没有通过的邮件通知,用例结束; 系统运行/开发环境 数据库:ms sqlserver2000 应用服务器:Jboss-4.0.2 开发工具:Jbuilder2006 系统搭建 1、创建数据库,我命名为osworkflow_app,也可命名为任意名字,只要在数据源配置中正确配置即可; 2、创建数据表并初始化用户及用户组数据,执行附件中的db_leaveApprove_20070712.sql即可; 3、在jboss下配置数据源,我的配置如附件mssql-ds.xml 4、将osworkflow及log4j的配置文件leave_apply.xml、osuser.xml、osworkflow.xml、propertyset.xml、workflows.xml、log4j.properties拷贝到工程目录src下面; 可直接将附件的leaveApprove.rar解到项目中,里面包括所有的源程序和资源包。 工作流定义文件说明 依据本业务需求的工作流定义文件如下 定义中包括6个step step1是员工请假申请的步骤,本步骤的action在执行时将回调业务方法类ApplyFunction,将申请单数据插入到数据库中,同时处理结果将根据请假申请天数dayCount进行判断,如果请假申请天数大于3天,将跳到step2让部门主管审批,如果不大于3天,将直接跳到step3让人力资源主管审批; step2 是部门主管审批,如果审批同意(opinion!=2)将跳到step3再由人力资源主管审批,如果审批不同意(opinion==2)将跳到step5自动发“申请未批准”邮件通知步骤; step3 是人力资源主管审批,如果审批同意(opinion!=2)将跳到step4自动发“申请批准”邮件通知步骤,如果审批不同意(opinion==2)将跳到step5自动发“申请未批准”邮件通知步骤; step4和step5都是自动发送邮件通知步骤,执行完之后跳到空步骤step6结束该工作流实例。 工作流定义文件 <?xml version="1.0" encoding="GBK"?> <!DOCTYPE workflow PUBLIC "-//OpenSymphony Group//DTD OSWorkflow 2.6//EN" "http://www.opensymphony.com/osworkflow/workflow_2_8.dtd"> <workflow> <initial-actions> <action id="100" name="启动请假申请工作流"> <results> <unconditional-result old-status="Finished" status="Underway" step="1"/> </results> </action> </initial-actions> <steps> <step id="1" name="请假申请"> <actions> <action id="1" name="提交需求申请"> <restrict-to> <conditions type="AND"> <condition type="class"> <arg name="class.name">com.opensymphony.workflow.util.OSUserGroupCondition</arg> <arg name="group">employee</arg> </condition> </conditions> </restrict-to> <pre-functions> <function type="class"> <arg name="class.name">com.qiny.leave.ApplyFunction</arg> </function> </pre-functions> <results> <result old-status="Finished" status="Underway" step="2" owner="manager1"> <conditions type="AND"> <condition type="beanshell"> <arg name="script"> propertySet.getInt("dayCount")>3 </arg> </condition> </conditions> <post-functions> <function type="beanshell"> <arg name="script"> System.out.println("步骤 1 提交需求申请 满足条件结果 需部门经理审批..."); </arg> </function> </post-functions> </result> <unconditional-result old-status="Finished" status="Underway" step="3" owner="hr1"/> </results> </action> </actions> </step> <step id="2" name="请假申请审核"> <actions> <action id="2" name="部门主管审批请假申请"> <restrict-to> <conditions type="AND"> <condition type="beanshell"> <arg name="script">true</arg> </condition> <condition type="class"> <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.OSUserGroupCondition</arg> <arg name="group">manager</arg> </condition> </conditions> </restrict-to> <pre-functions> <function type="class"> <arg name="class.name">com.qiny.leave.ApproveFunction</arg> </function> </pre-functions> <results> <result old-status="Finished" status="Underway" step="5"> <conditions type="AND"> <condition type="beanshell"> <arg name="script"> propertySet.getInt("opinion")==2 </arg> </condition> </conditions> <post-functions> <function type="beanshell"> <arg name="script"> System.out.println("步骤 2 请假申请审核 部门经理审批没有通过 ..."); </arg> </function> </post-functions> </result> <unconditional-result old-status="Finished" status="Underway" step="3" owner="hr1"/> </results> </action> </actions> </step> <step id="3" name="请假申请审核"> <actions> <action id="3" name="人力资源主管审批请假申请"> <pre-functions> <function type="class"> <arg name="class.name">com.qiny.leave.ApproveFunction</arg> </function> </pre-functions> <results> <result old-status="Finished" status="Underway" step="5"> <conditions type="AND"> <condition type="beanshell"> <arg name="script"> propertySet.getInt("opinion")==2 </arg> </condition> </conditions> </result> <unconditional-result old-status="Finished" status="Underway" step="5"/> </results> </action> </actions> </step> <step id="4" name="请假申请结果通知"> <actions> <action id="4" auto="true" name="请假申请获准邮件通知"> <pre-functions> <function type="beanshell"> <arg name="script"> System.out.println("步骤 4 自动动作 请假申请获准邮件通知 Send mail 祝贺你$$$$$$$"); </arg> </function> </pre-functions> <results> <unconditional-result old-status="Finished" status="Finished" step="6"/> </results> </action> </actions> </step> <step id="5" name="请假申请结果通知"> <actions> <action id="5" auto="true" name="请假申请没能获准邮件通知"> <pre-functions> <function type="beanshell"> <arg name="script"> System.out.println("步骤 5 自动动作 请假申请没能获准邮件通知 Send mail 很抱歉$$$$$$$"); </arg> </function> </pre-functions> <results> <unconditional-result old-status="Finished" status="Finished" step="6"/> </results> </action> </actions> </step> <step id="6" name="flow over"> </step> </steps> </workflow> 回家了,明天再对一些程序进行说明 可以下载附件,建立工程后执行,数据库中有三个用户:employee1表示员工,可以提交请假申请;manager1表示部门主管;hr1表示人力资源主管,密码都是test 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2007-07-13
希望楼主能把这个例子在eclipse下发布下,很多人都没装JBuilder.
|
|
返回顶楼 | |
发表时间:2007-07-13
这是eclipse3.2.2下的项目工程
|
|
返回顶楼 | |
发表时间:2007-07-13
还没用过这个...下一个试试 谢谢哈
|
|
返回顶楼 | |
发表时间:2007-07-13
工作流引擎调用业务逻辑
在基于工作流进行业务系统开发时,通常需要在某些事件发生时或某些情况下,由低层工作流引擎调用上层的业务处理逻辑,比如在示例业务需求中,当部门主管审批员工请假申请时,审批事件发生时需要将审批意见插入到数据库表中,并进入后续相应的步骤,这里的将审批意见插入数据库,可以在业务程序中审批处理代码中直接插入,也可以只让审批处理代码只负责调用工作流引擎的“审批处理”步骤的doAction,而在doAction的过程中由引擎调用插入数据的操作;又例如在某些动作执行后向用户发送邮件通知。 这样当采用由引擎调用业务处理后,就可以在不改变程序代码的情况下调整业务过程及处理方式,比如去掉插入审批意见到数据库、取消动作后发送邮件通知等。业务过程的可配置性大大提高。 在工作流管理系统参考模型中,工作流引擎调用业务逻辑就是指的Interface 3,即如下图所示 在示例中,由引擎调用业务处理是通过Function的回调实现的,这个Function需要实现com.opensymphony.workflow.FunctionProvider,然后实现其中的execute(Map transientVars, Map args, PropertySet ps)方法。参数说明: Map transientVars表示临时变量,这些变量可以通过doAction(long id, int actionId, Map inputs)的inputs传入,也可以通过initialize(String workflowName, int initialAction, Map inputs)的inputs传入,此外还包含一些全局变量,如entry、context、actionId等; Map args表示调用Function的属性,这些属性在过程定义文件xml中设定; PropertySet ps 持久变量,这些变量设置后将存储到数据库或其他存储介质,在本示例中通过在doAction中设置该变量,然后在流程跳转时根据该变量进行相应跳转,即实现业务流程的不同流向。 示例代码 在部门主管审批时调用的ApproveFunction,这个Function只是将审批结果插入数据库中 public void execute(Map transientVars, Map args, PropertySet ps) { logger.info("execute ......"); WorkflowEntry entry = (WorkflowEntry) transientVars.get("entry"); long wfid = entry.getId(); int actionId = ((Integer) transientVars.get("actionId")).intValue(); logger.info("execute actionId=" + actionId); int applyID = ((Integer) transientVars.get("applyID")).intValue(); String approver = (String) transientVars.get("approver"); int opinion = ((Integer) transientVars.get("opinion")).intValue(); LeaveApprove LeaveApprove = new LeaveApprove(applyID, approver, opinion); LeaveDAO leaveDao = new LeaveDAO(); leaveDao.addLeaveApprove(LeaveApprove); ps.setInt("opinion", opinion); logger.info("execute leaveDao has added leaveApprove!"); } 过程定义配置文件中相关的回调 <step id="2" name="请假申请审核"> <actions> <action id="2" name="部门主管审批请假申请"> <restrict-to> <conditions type="AND"> <condition type="beanshell"> <arg name="script">true</arg> </condition> <condition type="class"> <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.OSUserGroupCondition</arg> <arg name="group">manager</arg> </condition> </conditions> </restrict-to> <pre-functions> [color=red]<function type="class"> <arg name="class.name">com.qiny.leave.ApproveFunction</arg> </function>[/color] <function type="beanshell"> <arg name="script"> System.out.println("步骤 2 请假申请审核 pre-functions $$$$$$$"); </arg> </function> </pre-functions> <results> <result old-status="Finished" status="Underway" step="5"> <conditions type="AND"> <condition type="beanshell"> <arg name="script"> propertySet.getInt("opinion")==2 </arg> </condition> </conditions> <post-functions> <function type="beanshell"> <arg name="script"> System.out.println("步骤 2 请假申请审核 部门经理审批没有通过 ..."); </arg> </function> </post-functions> </result> <unconditional-result old-status="Finished" status="Underway" step="3" owner="hr1"/> </results> <post-functions> <function type="beanshell"> <arg name="script"> System.out.println("步骤 2 请假申请审核 post-functions ......."); </arg> </function> </post-functions> </action> </actions> </step> |
|
返回顶楼 | |
发表时间:2007-07-13
业务过程根据业务数据而变化
在上面的示例中,只有在员工请假申请的天数大于3天时,才需要部门主管审批,若不大于3天则直接由人力资源主管审批,这样业务过程就由业务数据决定,不同的数据有不同的过程。 这个实现是通过action的条件结果完成的,配置如下 <results> <result old-status="Finished" status="Underway" step="2" owner="manager1"> <conditions type="AND"> <condition type="beanshell"> <arg name="script"> propertySet.getInt("dayCount")>3 </arg> </condition> </conditions> <post-functions> <function type="beanshell"> <arg name="script"> System.out.println("步骤 1 提交需求申请 满足条件结果 需部门经理审批..."); </arg> </function> </post-functions> </result> <unconditional-result old-status="Finished" status="Underway" step="3" owner="hr1"/> </results> 这里当条件propertySet.getInt("dayCount")>3满足时,导向结果<result old-status="Finished" status="Underway" step="2" owner="manager1">,由部门主管审批 如果条件不满足,则导向默认结果unconditional-result ,由人力资源直接审批 这里的值(propertySet.getInt("dayCount"))从哪里来的呢?是从定义的pre-functions:com.qiny.leave.ApproveFunction来的,在ApproveFunction中我们将数据插入库后,调用了ps.setInt("dayCount", dayCount); |
|
返回顶楼 | |
发表时间:2007-07-15
为什么用JSQL这个驱动啊,找了好久都没找到,现在的SQLSERVER的驱动不行吗?
|
|
返回顶楼 | |
发表时间:2007-07-15
不明白作者在这里要说明什么,是分析业务流程还是说明OSWorkflow解决问题的方法,而且无论是你的配置文件还是代码从规范上都显得过于的粗糙,这样的代码还是最好不要拿出来的好。
|
|
返回顶楼 | |
发表时间:2007-07-15
killvin 写道 不明白作者在这里要说明什么,是分析业务流程还是说明OSWorkflow解决问题的方法,而且无论是你的配置文件还是代码从规范上都显得过于的粗糙,这样的代码还是最好不要拿出来的好。
正如标题,要说明的就是:基于开源工作流引擎OSWorkflow的业务系统实例,具体内容在上面说得都很明白 至于代码粗糙,因为程序的目标不在程序设计和编码规范上,而在用最简单的程序代码——尽量少的设计概念表达os的使用方法。 示例中jsp基本上都是从OSWorkflow的演示(参见osworkflow-2.8.0-example.war)而来;java代码除了一个dao和两个值对象,另两个就是Function的实现类,代码都非常的简单 |
|
返回顶楼 | |
发表时间:2007-07-15
说些题外话,看了很多的workflow,撇开实现好坏不管,就workflow的schema来说--这也是最重要的--还是jbpm最适合阅读和理解,更DSL些. 其它的开源实现都实在不宜阅读和理解.
|
|
返回顶楼 | |