学习资料:《Activiti实战》

第七章  Spring容器集成应用实例(五)普通表单

第六章中介绍了动态表单、外置表单。这里讲解第三种表单:普通表单。

普通表单的特点:

 把表单内容写在表现层(JSP、JSF、HTML)文件中
一个用户任务对应一个页面
业务数据和流程数据分离
适用于业务相对固定但复杂、流程相对固定但表现层变化多的情况

因为普通表单中,业务数据和流程数据是分离的,所以存在统一事务管理的问题。要保证Activiti和业务数据操作在同一个事务中执行。前面集成Spring时的事务管理器的配置可表明这点。本节基于第六章的主要针请假流程,对数据和表单都分离的情况下,采用普通表单实现该功能。

本节采用Spring、SpringMVC和Hibernete。

7.5.1 业务建模

(1)表结构

(2)其他

DAO和Manger用来针对请假实体的CRUD,Leave-workflowService用来处理流程相关操作。

7.5.2 启动流程

(1)部署流程

在启动流程之前,先部署流程。

第六章已经实现过这个页面,不再提。总之,点击浏览,选择文件,然后submit,将chapter7/leave.bpmn和leave.png打包部署。部署完毕之后,在该列表页面中,会出现一个新的processDefinition记录。

(2)jsp

普通表单使用时,需要将表单内容保存在一个单独的文件中。这里采用jsp文件格式。

表单设计如下:

 1     <form action="${ctx }/chapter7/leave/start" class="form-horizontal" method="post" onsubmit="beforeSend()">
2 <input type="hidden" name="startTime" />
3 <input type="hidden" name="endTime" />
4 <fieldset>
5 <legend><small>请假申请</small></legend>
6 <div id="messageBox" class="alert alert-error input-large controls" style="display:none">输入有误,请先更正。</div>
7 <div class="control-group">
8 <label for="loginName" class="control-label">请假类型:</label>
9 <div class="controls">
10 <select id="leaveType" name="leaveType" class="required">
11 <option>公休</option>
12 <option>病假</option>
13 <option>调休</option>
14 <option>事假</option>
15 <option>婚假</option>
16 </select>
17 </div>
18 </div>
19 <div class="control-group">
20 <label for="name" class="control-label">开始时间:</label>
21 <div class="controls">
22 <input type="text" id="startDate" class="datepicker input-small" data-date-format="yyyy-mm-dd" />
23 <input type="text" id="startTime" class="time input-small" />
24 </div>
25 </div>
26 <div class="control-group">
27 <label for="plainPassword" class="control-label">结束时间:</label>
28 <div class="controls">
29 <input type="text" id="endDate" class="datepicker input-small" data-date-format="yyyy-mm-dd" />
30 <input type="text" id="endTime" class="time input-small" />
31 </div>
32 </div>
33 <div class="control-group">
34 <label for="groupList" class="control-label">请假原因:</label>
35 <div class="controls">
36 <textarea name="reason"></textarea>
37 </div>
38 </div>
39 <div class="form-actions">
40 <button type="submit" class="btn"><i class="icon-play"></i>启动流程</button>
41 </div>
42 </fieldset>
43 </form>

form

webapp/WEB-INF/views/chapter7/leave/leaveApply.jsp如下:

 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<%@ include file="/common/global.jsp"%>
<%@ include file="/common/meta.jsp" %>
<%@ include file="/common/include-base-styles.jsp" %>
<link rel="stylesheet" href="${ctx}/js/common/plugins/timepicker.css">
<title>请假申请</title>
<script type="text/javascript" src="${ctx }/js/common/jquery.js"></script>
<script type="text/javascript" src="${ctx }/js/common/bootstrap.min.js"></script>
<script type="text/javascript" src="${ctx }/js/common/bootstrap-datepicker.js"></script>
<script type="text/javascript" src="${ctx }/js/common/plugins/bootstrap-timepicker.js"></script>
<script type="text/javascript">
$(function() {
$('.datepicker').datepicker();
$('.time').timepicker({
minuteStep: 10,
showMeridian: false
});
}); function beforeSend() {
$('input[name=startTime]').val($('#startDate').val() + ' ' + $('#startTime').val());
$('input[name=endTime]').val($('#endDate').val() + ' ' + $('#endTime').val());
}
</script>
</head>
<body>
<c:if test="${not empty message}">
<div id="message" class="alert alert-success">${message}</div>
<!-- 自动隐藏提示信息 -->
<script type="text/javascript">
setTimeout(function() {
$('#message').hide('slow');
}, 5000);
</script>
</c:if>
<form>
<!--表单的代码放在这里-->
</form>
</body>
</html>

(3)Controller

在上面的流程定义列表页面中,增加一个字段"操作",里面包含两个button,一个是启动,一个是删除。

当点击"启动时",应该要显示表单,并且点击申请时,流程启动。

 @Controller
@RequestMapping(value = "/chapter7/leave")
public class LeaveController { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired
private LeaveManager leaveManager; @Autowired
private LeaveWorkflowService leaveService; @Autowired
private TaskService taskService; @Autowired
private RuntimeService runtimeService; @RequestMapping(value = {"apply", ""})
public String createForm(Model model) {
model.addAttribute("leave", new Leave());
return "/chapter7/leave/leave-apply";
} /**
* 启动请假流程
*/
@RequestMapping(value = "start", method = RequestMethod.POST)
public String startWorkflow(Leave leave, RedirectAttributes redirectAttributes, HttpSession session) {
try {
User user = UserUtil.getUserFromSession(session);
Map<String, Object> variables = new HashMap<String, Object>();
ProcessInstance processInstance = leaveService.startWorkflow(leave, user.getId(), variables);
redirectAttributes.addFlashAttribute("message", "流程已启动,流程ID:" + processInstance.getId());
} catch (ActivitiException e) {
if (e.getMessage().indexOf("no processes deployed with key") != -1) {
logger.warn("没有部署流程!", e);
redirectAttributes.addFlashAttribute("error", "没有部署请假流程");
} else {
logger.error("启动请假流程失败:", e);
redirectAttributes.addFlashAttribute("error", "系统内部错误!");
}
} catch (Exception e) {
logger.error("启动请假流程失败:", e);
redirectAttributes.addFlashAttribute("error", "系统内部错误!");
}
return "redirect:/chapter7/leave/apply";
} ...
}

(4)Service

前面提过,这里要实现业务和流程数据的分离。即业务数据存在一个表里,流程数据存在另一个表。但是又要将二者联系起来。

这里采取的办法是:

1 业务表:增加一个字段process_instance_id,方便从业务层面查询流程数据。
2 流程表:用entity的ID作为processDefinitionKey,方便从流程层面查询业务数据。

可以从代码中看出来,这里不只是单纯的启动流程。还进行了数据的存储与关联。

 @Service
@Transactional
public class LeaveWorkflowService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired
LeaveManager leaveManager; @Autowired
private IdentityService identityService; @Autowired
private RuntimeService runtimeService; @Autowired
private TaskService taskService; @Autowired
private RepositoryService repositoryService; /**
* 保存请假实体并启动流程
*/
public ProcessInstance startWorkflow(Leave entity, String userId, Map<String, Object> variables) {
if (entity.getId() == null) {
entity.setApplyTime(new Date());
entity.setUserId(userId);
}
leaveManager.save(entity);//持久化请假实体
String businessKey = entity.getId().toString();//实体保存后的ID,作为流程中的业务key

// 用来设置启动流程的人员ID,引擎会自动把用户ID保存到activiti:initiator中
identityService.setAuthenticatedUserId(userId); ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leave", businessKey, variables);//将业务主ID设置为流程实例的key
String processInstanceId = processInstance.getId();
entity.setProcessInstanceId(processInstanceId);// 将流程实例的ID保存至业务表
logger.debug("start process of {key={}, bkey={}, pid={}, variables={}}", new Object[]{"leave", businessKey, processInstanceId, variables});
leaveManager.save(entity);
return processInstance;
} ...
}

 7.5.3 任务读取

2017.2.28 activiti实战--第七章--Spring容器集成应用实例(五)普通表单的更多相关文章

  1. 2017.2.21 activiti实战--第七章--Activiti与spring集成(一)配置文件

    学习资料:<Activiti实战> 第七章 Activiti与容器集成 本章讲解activiti-spring可以做的事情,如何与现有系统集成,包含bean的注入.统一事务管理等. 7.1 ...

  2. 2017.2.28 activiti实战--第六章--任务表单(二)外置表单

    学习资料:<Activiti实战> 第六章 任务表单(二)外置表单 6.3 外置表单 考虑到动态表单的缺点(见上节),外置表单使用的更多. 外置表单的特点: 页面的原样显示 字段值的自动填 ...

  3. 2017.2.28 activiti实战--第六章--任务表单(一)动态表单

    学习资料:<Activiti实战> 第六章 任务表单(一)动态表单 内容概览:本章要完成一个OA(协同办公系统)的请假流程的设计,从实用的角度,讲解如何将activiti与业务紧密相连. ...

  4. 2017.2.28 activiti实战--第五章--用户与组及部署管理(三)部署流程及资源读取

    学习资料:<Activiti实战> 第五章 用户与组及部署管理(三)部署流程及资源读取 内容概览:如何利用API读取已经部署的资源,比如读取流程定义的XML文件,或流程对应的图片文件. 以 ...

  5. 2017.2.28 activiti实战--第五章--用户与组及部署管理(二)部署流程资源

    学习资料:<Activiti实战> 第五章 用户与组及部署管理(二)部署流程资源 内容概览:讲解流程资源的读取与部署. 5.2 部署流程资源 5.2.1 流程资源 流程资源常用的有以下几种 ...

  6. 2017.2.21 activiti实战--第十三章--流量数据查询与跟踪(一)查询接口介绍及运行时数据查询

    学习资料:<Activiti实战> 第十三章 流量数据查询与跟踪 本章讲解运行时与历史数据的查询方法.主要包含三种:标准查询,Native查询,CustomSql查询. 13.1 Quer ...

  7. 2017.2.22 activiti实战--第六章--任务表单

    学习资料:<Activiti实战> 第六章 任务表单 本章将一步步完成一个协同办公系统(OA)的请假流程的设计,讲解如何将Activiti和实际业务联系起来. 首先讲解动态表单与外置表单的 ...

  8. 2017.2.20 activiti实战--第二章--搭建Activiti开发环境及简单示例(二)简单示例

    学习资料:<Activiti实战> 第一章 搭建Activiti开发环境及简单示例 2.5 简单流程图及其执行过程 (1)leave.bpmn 后缀名必须是bpmn.安装了activiti ...

  9. 2017.2.20 activiti实战--第二章--搭建Activiti开发环境及简单示例(一)搭建开发环境

    学习资料:<Activiti实战> 第一章 认识Activiti 2.1 下载Activiti 官网:http://activiti.org/download.html 进入下载页后,可以 ...

随机推荐

  1. Leetcode 543.二叉树的直径

    二叉树的直径 给定一棵二叉树,你需要计算它的直径长度.一棵二叉树的直径长度是任意两个结点路径长度中的最大值.这条路径可能穿过根结点. 示例 :给定二叉树 1 / \ 2 3 / \ 4 5 返回 3, ...

  2. 数据结构算法——链表的各种操作,C++和Python

    时隔已久,一直没更新博客,感觉很愧疚呀. 先贴上所有的代码.这个是用C++写的 #include "stdafx.h" //Author:Albert //Date: 2018.1 ...

  3. sqlserver执行时间和自行效率

    SET STATISTICS PROFILE ON --SET STATISTICS IO ON --SET STATISTICS TIME ON declare @dtm datetime SQL语 ...

  4. springmvc支持跨域的请求(复制)

    Spring MVC 新增跨域支持 发表于2017/5/8 22:01:24  48人阅读 分类: SpringMVC Spring MVC 4.2 增加 CORS 支持 跨站 HTTP 请求(Cro ...

  5. [Python]Pandas简单入门(转)

    本篇文章转自 https://colab.research.google.com/notebooks/mlcc/intro_to_pandas.ipynb?hl=zh-cn#scrollTo=zCOn ...

  6. 【bzoj1097】[POI2007]旅游景点atr 状压dp+堆优化Dijkstra

    题目描述 FGD想从成都去上海旅游.在旅途中他希望经过一些城市并在那里欣赏风景,品尝风味小吃或者做其他的有趣的事情.经过这些城市的顺序不是完全随意的,比如说FGD不希望在刚吃过一顿大餐之后立刻去下一个 ...

  7. Sabota?

    Sabota? 题目描述 某个公司有n个人, 上下级关系构成了一个有根树.其中有个人是叛徒(这个人不知道是谁).对于一个人, 如果他下属(直接或者间接, 不包括他自己)中叛徒占的比例超过x,那么这个人 ...

  8. [暑假集训--数论]poj1595 Prime Cuts

    A prime number is a counting number (1, 2, 3, ...) that is evenly divisible only by 1 and itself. In ...

  9. POJ3311 Hie with the Pie

    The Pizazz Pizzeria prides itself in delivering pizzas to its customers as fast as possible. Unfortu ...

  10. codechef AUG17 T5 Chef And Fibonacci Array

    Chef has an array A = (A1, A2, ..., AN), which has N integers in it initially. Chef found that for i ...