转:传智播客--Jbpm(五)
1. 组任务和个人任务
组任务 拾取
个人任务 审批
2. 增加2个实体
请假单:LeaveForm, 审批信息:ApproveInfo;
2者是1对多的关系;
LeaveForm:
id:long;
creator:User;
processName:String;
processInstanceId : long;(不使用外键关联,会导致级联删除)
creationTime:Date;
status:int
approveInfos : Set<ApproveInfo> ;
-----------------------
days:int;
reason : String;
ApproveInfo:
id : long;
leaveForm : LeaveForm;
workItem : long;(任务实例,变量命名, TaskInstance)
-----------------------
approval : boolean;
comment : String;
approver : User;
approveTime : Date;
用户与请假单是一对多关系;
用户与审批信息室一对多关系;
增加报销表单:
发现与请假表单的属性很接近;
所以抽取成父类:业务表单
请假表单和报销表单继承业务表单
请假表单:days:int, reason:String
所以修改审批表单关联的业务表单属性
bizForm : BizForm
取代
leaveForm : LeaveForm;
练习:
继承关系的映射文件的写法;
问题:
long id;
long processInstanceId;
(1) 流程变量与临时变量;
流程变量需要被保存到数据库中;
class that are persistable with hibernate;
(2) 流程定义的删除功能;
删除流程定义,就删除了所有使用该流程定义的流程实例;(jbpm设置的)
并删除这些流程实例中的所有任务实例;
流程实例的删除不能【删除业务表单/解除关系】;
这样导致外键约束失败;
所以processInstanceId不与流程实例在数据库关系上关联;
但它保存的还是真实的processInstanceId;
这样如果该流程实例没有被删除,则通过processInstanceId可以查找到流程实例;
如果该流程实例被删除,就不能被查到;
3. 根据分析,写WorkflowAction extends BaseAction;
applyUI(), apply() -- 表单填写与提交
listWorkitems() -- 列车当前用户的工作项(任务实例)列表;
takeWorkitem() -- 拾取任务
approveUI -- 审批页面
approve -- 审批
首先以请假为例;写出上述代码;
(1) 获取当前登录用户:
BaseAction.getCurrentUser();
(2) 显示任务列表;
任务名字,所属流程,发起人,发起时间,相关操作;
action中的变量使用
Map<TaskInstance, BizForm> tasks;
<c:forEach item="${tasks}" var="entry">
<c:set var="workitem" value="{entry.key}" scope="page">
<c:set var="bizForm" value="{entry.value}" scope="page">
<fmt:formateDate var="" pattern="" />
<c:remove var="workitem" scope="page">
<c:remove var="bizForm" scope="page">
(3)登录页面的处理
放在一个小窗口中,登录成功后
-- 提示登录成功;
-- 刷新主页面:
window.opener.document.location.reload(true);
-- 关闭自己;
问题:登录上去后,不能显示用户名,可能与缓存有关;
(4) 组任务与个人任务的区分
${workitem.actorId eq null} 拾取
${workitem.actorId ne null} 审批
来源:(http://blog.sina.com.cn/s/blog_5ecfe46a0100dos6.html) - 传智播客--Jbpm(五)_hhl_bj_新浪博客
(5) 拾取任务;
takeWorkItem(long workItemId, Strint actorId);
ti = jbpm.getTaskInstance(workItemId);
ti.setActorId(actorId);
(6) 如何强制用户登录?
父类:BaseAction.execute
a. 判断当前用户是否登录;如果未登录,则重定向到
response.sendRedirect(request.getContextPath("/")+"/user.do?method=loginUI");
return null;
b. 判断当前的方法是否为login, loginUI,避免死循环;
requestParameter("method");
methodName.startsWith("login");
c. 其他 return super.execute(...);
(7) 拾取与审批操作中的细节:
拾取或审批成功后,与登录的处理方法接近;
(8) 审批的业务
保存审批信息;
如果审批不通过,流程结束,业务表单为审批未通过状态;
如果审批通过,流程继续执行;
如果本次审批时最后一个环节,业务表单为审批通过状态;
// 审批部通过
ti.getProcessInstance().end();
bizForm.setStatus(BizForm.STATUS_DISAPPROVED);
// 如何知道下一个结点是结束结点:通过时间
if (ti.getProcessInstance().getEnded() )
bizForm.setStatus(BizForm.STATUS_APPROVED);
(9) 任务执行与传播的问题:
部门经理审批未通过时,任务还继续走向总经理审批;
但流程已经结束(从数据库中可以查看);
-- 审批不通过
t1.start();
ti.setSignalling(false); // 增加这样的代码,是否发信号往下执行,默认为true;
ti.end();
-- 审批通过
t1.start();
t1.end();
(10) 请假的流程有很多代码可以重用,结合报销流程,实现代码重用;
分析代码重用
-- 包含页面
<c:import url="">
-- Action中很多方法实现重用,但是不同的流程跳转的页面地址不一样;
所以写一个工具类,通过流程名来获取跳转页面名;
new ActionForward(FormViewUtils.getView(bizForm.getProcessName()));
(11) 流程实例监控
2个方法:正在执行的流程实例列表;查看流程实例图片;
getSession().createQuery(
"from ProcessInstance pi where pi.end is null").list();
ProcessInstance : 需要在Hibernate-mapping auto-import="true / false"
auto-import="false" : 引用时需要使用全名
org.jbpm.graph.exe.ProcessInstance
查询时可用使用hql语句,修改删除时要使用它自带的api;
图片高亮的思路:
用流程图作为背景图片;
用一个div,它的大小与正在执行的节点相等,修改它的颜色即可;
<html:img action="/pd?method=viewPdImage&id=${pi.processDefinition.id}"
style="position: absolute; left:0; top:0;"
/>
如何找到结点的坐标信息;
// 获取结点名
String nodeName = pi.getRootToken().getNode().getName();
//
InputStream in = pi.getProcessDefinition().getFileDefinition.getInputStream("");
SAXReader saxReader = new SAXReader();
Document doc = saxReader.read(in);
in.close();
Element root = doc.getRootElement();
for (Iterator it = root.elementIterator("node"); it.hasNext();) {
Element e = it.next();
if ( e.getName().equals(nodeName) ) {
int x = Integer.parseInt( e.attribute("x").getValue());
int y = Integer.parseInt( e.attribute("y").getValue());
int width = Integer.parseInt( e.attribute("width").getValue());
int height = Integer.parseInt( e.attribute("height").getValue());
rect = new Rectangle(x, y, width, height);
}
request.setAttribute("rect", rect);
<div style="border:red; position:absolute; left:{rect.x};
top:${rect.y}; width:${rect.width}; height:${rect.height}>
div生成,但是位置信息为空,应该是Action中的rect为null;
可能是结点没有找到;
dom4j, css
多个Token的情况;
fox, join结点
也就是查找所有的结点;
pi.findAllToken();
List<Rectangle> positions = new ArrayList<Rectangle>();
for (Object obj : pi.findAllTokens()) {
Token token = (Token) obj;
String nodeName = token.getNode().getName();
Rectangele position = findPosition(root, nodeName);
positions.add(position);
}