- 今天内容安排:
- 1、流程实例管理(查询流程实例、查看流程实例运行状态)
- 2、将bos系统中的用户和角色同步到activiti框架的用户表和组表中去
- 3、设计物流配送流程
- 4、启动物流配送流程
- 5、组任务操作(查询、拾取)
- 6、个人任务操作(查询、办理)
1、流程实例管理
1.1、查询流程实例
第一步:创建一个流程实例管理ProcessInstanceAction,提供list()方法,查询流程实例列表数据
// 注入Service
@Autowired
private RuntimeService runtimeService;
/**
* 查询流程实例(同步)
* @return
*/
public String list() {
// 创建流程实例查询对象
ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery();
query.orderByProcessInstanceId().desc(); // 根据流程实例id排序,降序
List<ProcessInstance> list = query.list(); // 执行查询,获得流程实例列表数据
// 将流程实例列表数据压入值栈中
ActionContext.getContext().getValueStack().set("list", list);
return "list";
}
第二步:配置struts.xml
<!-- 流程实例管理:配置processInstanceAction-->
<action name="processInstanceAction_*" class="processInstanceAction" method="{1}">
<result name="list">/WEB-INF/pages/workflow/processinstance.jsp</result>
</action>
第三步:提供processinstance.jsp页面,展示列表数据
<tbody>
<s:iterator value="list">
<tr>
<td>${id}</td><!-- 流程实例id -->
<td>${processDefinitionId}</td>
<td>${activityId}</td><!-- 流程id(任务默认id) -->
<td>
<div id="div${id}"></div>
<script type="text/javascript">
// 根据流程实例id查询流程变量
$.post("${pageContext.request.contextPath}/processInstanceAction_findData.action",{"id":'${id}'},function(data) {
$("#div${id}").html(data);
});
</script>
</td>
<td>
<a class="easyui-linkbutton" data-options="iconCls:'icon-search'" onclick="showPng('${id}');" href="#">查看流程图</a>
</td>
</tr>
</s:iterator>
</tbody>
第四步:在ProcessInstanceAction中提供findData()的方法,根据流程实例id查询对应的流程变量数据(ajax)
// 接收页面提交过来的参数:流程实例id
private String id;
public void setId(String id) {
this.id = id;
}
/**
* 根据流程实例id查询对应的流程变量数据(ajax)
* @return
* @throws IOException
*/
public String findData() throws IOException {
Map<String, Object> variables = runtimeService.getVariables(id);
ServletActionContext.getResponse().setContentType("text/html;charset=UTF-8");
ServletActionContext.getResponse().getWriter().print(variables.toString());
return "none";
}
浏览器效果如下图所示:
![](https://s1.ax2x.com/2018/10/19/5T2kUG.png)
1.2、查询流程实例运行状态
第一步:为“查看流程图”按钮绑定事件,注意:点击查看流程图按钮,实际上发起了2次请求
<script type="text/javascript">
function showPng(id) {
// 弹出新窗口
window.open("${pageContext.request.contextPath}/processInstanceAction_showPng.action?id=" + id);
}
</script>
第二步:在ProcessInstanceAction中提供showPng()方法,根据流程实例id获取部署id、图片名称、图片坐标
/**
* 根据流程实例id获取部署id、图片名称、图片坐标
*
* @return
*/
public String showPng() {
// 1、根据流程实例id创建流程实例查询对象
ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery();
query.processInstanceId(id); // 根据流程实例id过滤
ProcessInstance processInstance = query.singleResult(); // 得到流程实例对象
// 2、根据流程实例对象获取流程定义id
String processDefinitionId = processInstance.getProcessDefinitionId();
// 3、根据流程定义id创建流程定义查询对象 --> 根据流程定义id过滤 --> 得到流程定义对象(链式编程),注意:此方式只查询了流程定义表act_re_procdef
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
// 4、根据流程定义对象获取部署id
deploymentId = processDefinition.getDeploymentId();
// 5、根据流程定义对象获取图片名称
imageName = processDefinition.getDiagramResourceName();
// 把部署id和图片名称压入值栈后,在image.jsp页面就可以正常获取出来这两个参数,取出来之后该页面又请求新的地址,新的地址又把这两个参数提交过来,我们在Action中又需要接收(设置)这两个参数,是不是有些麻烦呢?
// 所以针对这种情况,我们需要把部署id和图片名称由局部变量提升为成员变量,并且提供getter和setter方法,终于这两个方法都能用着啦!getter方法用于页面EL表达式获取参数,setter方法用于接收页面提交回来的参数。
// 因为成员变量本身就在struts的值栈里面,因为struts框架本身将Action对象压入值栈中。
// 获取坐标
// 1、获取当前流程实例执行到哪个节点了
String activityId = processInstance.getActivityId(); // usertask1
// 2、加载bpmn(xml)文件,获得一个流程定义对象,注意:此方式不仅查询了流程定义表act_re_procdef,还查询了act_ge_bytearray
ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId);
// 使用流程定义对象的实现类,该实现类里面扩展了很多方法,其中就包括关于坐标的
// 3、根据activityId获取含有坐标信息的对象
ActivityImpl findActivity = processDefinitionEntity.findActivity(activityId);
int x = findActivity.getX();
int y = findActivity.getY();
int width = findActivity.getWidth();
int height = findActivity.getHeight();
// 将坐标信息压入值栈中
ActionContext.getContext().getValueStack().set("x", x);
ActionContext.getContext().getValueStack().set("y", y);
ActionContext.getContext().getValueStack().set("width", width);
ActionContext.getContext().getValueStack().set("height", height);
return "showPng";
}
第三步:配置struts.xml,跳转到image.jsp页面
<result name="showPng">/WEB-INF/pages/workflow/image.jsp</result>
第四步:提供image.jsp页面
<body>
<!-- 1.获取到规则流程图 -->
<img style="position: absolute;top: 0px;left: 0px;"
src="processInstanceAction_viewImage?deploymentId=${deploymentId}&imageName=${imageName}">
<!-- 2.根据当前活动的坐标,动态绘制DIV -->
<div style="position:absolute;border:1px solid red;top:${y-1}px;left:${x-1}px;${width}px;height:${height}px;">
</div>
</body>
第五步:在ProcessInstanceAction中提供viewImage()方法,根据部署id和图片名称获得对应的输入流
/**
* 根据部署id和图片名称获取对应的png输入流
* @return
*/
public String viewImage() {
// 获取png图片对应的输入流
InputStream pngStream = repositoryService.getResourceAsStream(deploymentId, imageName);
// 使用Struts框架提供的文件下载功能(文件下载结果集):通过输出流把服务端的资源写到客户端
// 先把输入流压入值栈,再到页面把流读出来即可
ActionContext.getContext().getValueStack().set("pngStream", pngStream);
return "viewImage";
}
第六步:配置struts.xml
<result name="viewImage" type="stream">
<param name="contentType">image/png</param>
<param name="inputName">pngStream</param><!-- 输入流压栈时起的名字 -->
<!--
<param name="contentDisposition">attachment;filename="abc.png"</param>
<param name="bufferSize">1024</param>
-->
</result>
浏览器效果如下图所示:
![](https://s1.ax2x.com/2018/10/19/5T2I93.png)
2、将bos系统中的用户和角色同步到activiti框架的用户表和组表中去
2.1、将角色同步到 act_id_group 表中去
修改RoleServiceImpl中的save()方法,如下图所示:
![](https://s1.ax2x.com/2018/10/19/5T2AJ6.png)
2.2、将用户同步到 act_id_user 表中去
注意:我们在添加用户的时候会选择角色,在activiti框架中对应的是用户表关联组表。
修改UserServiceImpl的save()方法,如下图所示:
![](https://s1.ax2x.com/2018/10/19/5T2tIp.png)
3、设计物流配送流程
- 流程定义的id:
- 使用排他网关:
- 使用组任务:
- 任务的id(对应Action中的方法名):
4、启动物流配送流程
准备工作:
- 修改工作单类
- 修改工作单对应的hbm映射文件
- 为了和我们之前的命名规则一致,修改
数据库表auth_function
中的“启动配送流程”中的page属性的值为workordermanageAction_list.action
4.1、查询工作单列表数据
第一步:在工作单管理Action中提供list()方法,查询流程状态start为0的工作单(即未启动的工作单)
/**
* 查询流程状态start为0的工作单(即未启动的工作单)
* @return
*/
public String list() {
List<Workordermanage> list = workordermanageService.findListNotStart();
ActionContext.getContext().getValueStack().set("list", list); // 将查询到的结果压栈
return "list";
}
Service层代码:
/**
* 查询流程状态start为0的工作单(即未启动的工作单)
*/
public List<Workordermanage> findListNotStart() {
// 创建离线条件查询对象
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Workordermanage.class);
// 向离线条件查询对象中封装查询条件
detachedCriteria.add(Restrictions.eq("start", "0"));
return workordermanageDao.findByCriteria(detachedCriteria );
}
第二步:配置struts.xml
<!-- 工作单管理:配置workordermanageAction-->
<action name="workordermanageAction_*" class="workordermanageAction" method="{1}">
<result name="list">/WEB-INF/pages/workflow/startransfer.jsp</result>
</action>
第三步:提供startransfer.jsp页面,展示工作单列表数据
![](https://s1.ax2x.com/2018/10/19/5T22Xa.png)
浏览器显示效果:
![](https://s1.ax2x.com/2018/10/19/5T27Zz.png)
4.2、根据key启动流程实例
第一步:修改列表页面中“启动按钮”绑定事件
<td>
<s:a action="workordermanageAction_start" cssClass="easyui-linkbutton" iconCls="icon-edit">启动
<s:param name="id" value="#workOrderManage.id"></s:param>
</s:a>
</td>
第二步:在工作单Action中提供start()方法,启动物流配送流程对应的流程实例
注意:“修改工作单中的start值为1,启动流程实例,设置流程变量”为多个数据库表的操作,所以尽量不要在Action中写这么复杂的逻辑,为什么呢?
答:建议写在service里面去,因为涉及到多个数据库表的操作,这样可以保证事务控制。
/**
* 启动物流配送流程
* @return
*/
public String start() {
// 获取工作单id
String id = model.getId();
// 注意:“修改工作单中的start值为1,启动流程实例,设置流程变量”为多个数据库表的操作,所以尽量不要在Action中写这么复杂的逻辑,为什么呢?
// 答:建议写在service里面去,因为涉及到多个数据库表的操作,这样可以保证事务控制。
// 启动流程实例
workordermanageService.start(id);
return "toList";
}
第三步:在工作单Service中提供start()方法,实现数据库操作
/**
* 修改工作单中的start值为1,设置流程变量,启动流程实例
*/
public void start(String workorderId) {
// 修改工作单中的start值为1
Workordermanage workordermanage = workordermanageDao.findById(workorderId);
workordermanage.setStart("1");
// 设置流程变量
String processDefinitionKey = "transfer"; // 流程定义key == 画图时给图起的id,这个不需要查询数据库,为已知数据
String businessKey = workorderId; // 业务主键 == 业务表(工作单)的主键值,因为实际当中携带的数据往往是业务表中数据 ==> 业务主键存在实例流程表中,工作流框架通过业务主键可以找到业务数据
Map<String, Object> variables = new HashMap<String, Object>(); // 流程变量
variables.put("业务数据", workordermanage); // 我们在流程变量里面也存一份业务数据,就是说,“业务数据”我可以存在业务主键中,也可以存在流程变量中
// 启动流程实例
runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, variables);
}
如下图所示:
![](https://s1.ax2x.com/2018/10/19/5T2RIS.png)
5、组任务操作
5.1、查询组任务
第一步:创建一个TaskAction类,提供查询组任务的方法findGroupTask()
@Autowired
private TaskService taskService;
/**
* 查询组任务
* @return
*/
public String findGroupTask() {
// 创建任务查询对象
TaskQuery query = taskService.createTaskQuery();
String candidateUser = BOSContext.getLoginUser().getId(); // 获取当前登录用户的id
query.taskCandidateUser(candidateUser); // 根据组任务过滤,即根据候选人过滤
List<Task> list = query.list(); // 执行查询,获得任务列表数据
// 将任务列表数据压入值栈中
ActionContext.getContext().getValueStack().set("list", list);
return "grouptasklist";
}
第二步:配置struts.xml
<!-- 任务管理:配置taskAction-->
<action name="taskAction_*" class="taskAction" method="{1}">
<result name="grouptasklist">/WEB-INF/pages/workflow/grouptask.jsp</result>
</action>
第三步:提供grouptask.jsp页面,展示任务列表数据
![](https://s1.ax2x.com/2018/10/19/5T2n9h.png)
第三步:在任务Action中,提供显示业务数据的方法showData()
// 接收页面提交过来的参数:任务id
private String taskId;
public void setTaskId(String taskId) {
this.taskId = taskId;
}
/**
* 根据任务id查询对应的流程变量数据(ajax)
* @return
* @throws IOException
*/
public String showData() throws IOException {
Map<String, Object> variables = taskService.getVariables(taskId);
ServletActionContext.getResponse().setContentType("text/html;charset=UTF-8");
ServletActionContext.getResponse().getWriter().print(variables.toString());
return "none";
}
浏览器显示效果:
![](https://s1.ax2x.com/2018/10/19/5T75W9.png)
5.2、拾取组任务
第一步:修改jsp页面中“拾取按钮”事件
<td>
<s:a action="taskAction_takeTask" namespace="/" cssClass="easyui-linkbutton">拾取
<s:param name="taskId" value="id"></s:param>
</s:a>
</td>
第二步:在TaskAction中提供拾取任务的方法takeTask()
/**
* 拾取组任务
* @return
*/
public String takeTask() {
String userId = BOSContext.getLoginUser().getId(); // 获取当前登录用户的id
taskService.claim(taskId, userId);
return "togrouptasklist";
}
第三步:配置struts.xml
<!-- 任务管理:配置taskAction-->
<action name="taskAction_*" class="taskAction" method="{1}">
<result name="grouptasklist">/WEB-INF/pages/workflow/grouptask.jsp</result>
<result name="togrouptasklist" type="redirectAction">
taskAction_findGroupTask
</result>
</action>
6、个人任务操作
6.1、查询个人任务
第一步:在TaskAction中提供findPersonalTask()方法,查询当前登录用户的个人任务
/**
* 查询个人任务
*/
public String findPersonalTask() {
// 创建任务查询对象
TaskQuery query = taskService.createTaskQuery();
String assignee = BOSContext.getLoginUser().getId(); // 获取当前登录用户的id
query.taskAssignee(assignee); // 根据个人任务过滤
List<Task> list = query.list(); // 执行查询,获得任务列表数据
// 将任务列表数据压入值栈中
ActionContext.getContext().getValueStack().set("list", list);
return "personaltasklist";
}
第二步:配置struts.xml
<result name="personaltasklist">/WEB-INF/pages/workflow/personaltask.jsp</result>
第三步:提供personaltask.jsp页面,展示个人任务列表数据
图片同查询组任务中的第三步。
浏览器显示效果:
![](https://s1.ax2x.com/2018/10/19/5T2qUN.png)
6.2、办理个人任务
6.2.1、办理审核工作单任务
第一步:修改personaltask.jsp页面中“办理任务”按钮的事件
![](https://s1.ax2x.com/2018/10/19/5T2wnu.png)
第二步:在TaskAction中提供checkWorkOrderManage()方法,办理审核工作单任务
// 接收页面提交过来的参数:check
// 审核结果:1 表示审核通过,0表示审核不通过
private Integer check;
public Integer getCheck() {
return check;
}
public void setCheck(Integer check) {
this.check = check;
}
/**
* 办理审核工作单任务(实际上是跳转到审核工作单页面 + 真正办理审核工作单任务)
*/
public String checkWorkOrderManage() {
// 根据传递过来的任务id获取任务对象(任务查询对象 --> 根据任务id过滤 --> 任务对象)
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
// 根据任务对象获取流程实例id
String processInstanceId = task.getProcessInstanceId();
// 根据流程实例id获取流程实例对象
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
String workorderId = processInstance.getBusinessKey(); // 业务主键 == 工作单id
// 根据工作单id获取工作单对象
Workordermanage workordermanage = workordermanageService.findId(workorderId);
if (check == null) {
// 说明需要跳转到审核页面
// 将工作单数据压入值栈中
ActionContext.getContext().getValueStack().set("map", workordermanage);
// 跳转到一个审核工作单页面,展示当前对应的工作单信息
return "check";
} else {
// 真正办理审核工作单任务
workordermanageService.checkWorkOrderManage(taskId, check, workorderId);
return "topersonaltasklist"; // 重新回显查询个人任务
}
}
第三步:如果是跳转到审核页面,配置struts.xml
<result name="check">/WEB-INF/pages/workflow/check.jsp</result>
第四步:提供check.jsp页面,展示审核工作单表单页面
![](https://s1.ax2x.com/2018/10/19/5T79cA.png)
第五步:在工作单Service中提供checkWorkOrderManage()方法,处理审核工作单任务
注意:spring配置文件中需要注入historyService。
// 注入service
@Autowired
private TaskService taskService;
// 注入service
@Autowired
private HistoryService historyService;
/**
* 办理审核工作单任务
*/
public void checkWorkOrderManage(String taskId, Integer check, String workorderId) {
// 如果审核不通过,修改工作单中的start值为0
Workordermanage workordermanage = workordermanageDao.findById(workorderId);
// 根据传递过来的任务id获取任务对象(任务查询对象 --> 根据任务id过滤 --> 任务对象)
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
// 根据任务对象获取流程实例id
String processInstanceId = task.getProcessInstanceId();
Map<String, Object> variables = new HashMap<String, Object>();
// 设置流程变量:check,check会在排他网关中使用
variables.put("check", check);
// 办理审核工作单任务
taskService.complete(taskId, variables);
if (check == 0) {
// 说明审核不通过
workordermanage.setStart("0");
// 删除历史流程实例数据
historyService.deleteHistoricProcessInstance(processInstanceId);
}
}
6.2.2、办理其他任务
我们简写了。
/**
* 办理出库任务
*/
public String outStore() {
taskService.complete(taskId);
return "topersonaltasklist";
}
/**
* 办理配送任务
*/
public String transferGoods() {
taskService.complete(taskId);
return "topersonaltasklist";
}
/**
* 办理签收任务
*/
public String receive() {
taskService.complete(taskId);
return "topersonaltasklist";
}