2017.2.28 activiti实战--第七章--Spring容器集成应用实例(五)普通表单
学习资料:《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容器集成应用实例(五)普通表单的更多相关文章
- 2017.2.21 activiti实战--第七章--Activiti与spring集成(一)配置文件
学习资料:<Activiti实战> 第七章 Activiti与容器集成 本章讲解activiti-spring可以做的事情,如何与现有系统集成,包含bean的注入.统一事务管理等. 7.1 ...
- 2017.2.28 activiti实战--第六章--任务表单(二)外置表单
学习资料:<Activiti实战> 第六章 任务表单(二)外置表单 6.3 外置表单 考虑到动态表单的缺点(见上节),外置表单使用的更多. 外置表单的特点: 页面的原样显示 字段值的自动填 ...
- 2017.2.28 activiti实战--第六章--任务表单(一)动态表单
学习资料:<Activiti实战> 第六章 任务表单(一)动态表单 内容概览:本章要完成一个OA(协同办公系统)的请假流程的设计,从实用的角度,讲解如何将activiti与业务紧密相连. ...
- 2017.2.28 activiti实战--第五章--用户与组及部署管理(三)部署流程及资源读取
学习资料:<Activiti实战> 第五章 用户与组及部署管理(三)部署流程及资源读取 内容概览:如何利用API读取已经部署的资源,比如读取流程定义的XML文件,或流程对应的图片文件. 以 ...
- 2017.2.28 activiti实战--第五章--用户与组及部署管理(二)部署流程资源
学习资料:<Activiti实战> 第五章 用户与组及部署管理(二)部署流程资源 内容概览:讲解流程资源的读取与部署. 5.2 部署流程资源 5.2.1 流程资源 流程资源常用的有以下几种 ...
- 2017.2.21 activiti实战--第十三章--流量数据查询与跟踪(一)查询接口介绍及运行时数据查询
学习资料:<Activiti实战> 第十三章 流量数据查询与跟踪 本章讲解运行时与历史数据的查询方法.主要包含三种:标准查询,Native查询,CustomSql查询. 13.1 Quer ...
- 2017.2.22 activiti实战--第六章--任务表单
学习资料:<Activiti实战> 第六章 任务表单 本章将一步步完成一个协同办公系统(OA)的请假流程的设计,讲解如何将Activiti和实际业务联系起来. 首先讲解动态表单与外置表单的 ...
- 2017.2.20 activiti实战--第二章--搭建Activiti开发环境及简单示例(二)简单示例
学习资料:<Activiti实战> 第一章 搭建Activiti开发环境及简单示例 2.5 简单流程图及其执行过程 (1)leave.bpmn 后缀名必须是bpmn.安装了activiti ...
- 2017.2.20 activiti实战--第二章--搭建Activiti开发环境及简单示例(一)搭建开发环境
学习资料:<Activiti实战> 第一章 认识Activiti 2.1 下载Activiti 官网:http://activiti.org/download.html 进入下载页后,可以 ...
随机推荐
- 精通CSS高级Web标准解决方案(7、布局)
7.1 让设计居中 7.1.1 使用自动空白边让设计居中 <body> <div id="wrapper"> </div> </body& ...
- Leetcode 655.输出二叉树
输出二叉树 在一个 m*n 的二维字符串数组中输出二叉树,并遵守以下规则: 行数 m 应当等于给定二叉树的高度. 列数 n 应当总是奇数. 根节点的值(以字符串格式给出)应当放在可放置的第一行正中间. ...
- 【两种方式 Service References和 web References 】手把手教你引入webservice 服务
1.对于一个webservie服务我们如何引入到自己的项目中去呢 第一种方法[Service References]:鼠标移到属性上 右键添加服务引用 然后在地址栏输入webservice 地址 点击 ...
- html5中checkbox的选中状态的设置与获取
获取checkbox是否选中: $("#checkbox").is(":checked"); 获得的值为true或false. 设置checkbox是否选中: ...
- Rust学习资源和路线
Rust学习资源和路线 来源 https://rust-lang-cn.org/article/23 学习资源 The Rust Programming Language 堪称Rust的"T ...
- [SDOI2016][bzoj4514] 数字配对 [费用流]
题面 传送门 思路 一个数字能且只能匹配一次 这引导我们思考:一次代表什么?代表用到一定上限(b数组)就不能再用,同时每用一次会产生价值(c数组) 上限?价值?网络流! 把一次匹配设为一点流量,那产生 ...
- git - 搭建最简单的git server
以下操作都在 centos7 下进行,但同样适用于centos 6. 1. 安装git-core yum -y install git 添加git用户,用于启动管理git仓库 useradd git ...
- Python 错误类型及解决方法
SyntaxError: invalid syntax 表示“语法错误:不正确的语法” 检查代码的缩进,代码格式是否正确,Python的缩进一般为四个空格,tab键尽量不要用. In ...
- [CODEVS1051]接龙游戏
题目描述 给出了N个单词,已经按长度排好了序.如果某单词i是某单词j的前缀,i->j算一次接龙(两个相同的单词不能算接龙). 你的任务是:对于输入的单词,找出最长的龙. 输入描述 Input D ...
- css 之 position定位
position属性一共有4个值,分别是static.absolute.relative.fixed. static为默认值,指块保持在原本应该在的位置上,即该值没有任何移动的效果. absolute ...