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 进入下载页后,可以 ...
随机推荐
- pb8.0 mssqlserver 新建数据库连接问题
将ntwdblib.DLL复制到Sybase\Shared\PowerBuilder目录下 unable to load the requested database interface,无法创建数据 ...
- [oldboy-django][4python面试]有关csrf跨站伪造请求攻击
1 csrf定义 - csrf定义:Cross Site Request Forgery,跨站请求伪造 举例来说: 网站A伪造了一个图片链接: <a href="http://www. ...
- ABP 未能加载文件或程序集“System.ComponentModel.Annota, Version=4.2.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc”或它的某一个依赖项。找到的程序集清单定义与程序集引用不匹配。
切换System.ComponentModel.Annotations版本到4.4.1 重新编译即可
- iOS属性文字NSAttributedString
它本身是一个Foundation框架的类, 但如果要使用它主要用到了UIKit框架中的NSAttributedString中的一些常量字符串 ----------------------------- ...
- POJ 3321:Apple Tree(dfs序+树状数组)
题目大意:对树进行m次操作,有两类操作,一种是改变一个点的权值(将0变为1,1变为0),另一种为查询以x为根节点的子树点权值之和,开始时所有点权值为1. 分析: 对树进行dfs,将树变为序列,记录每个 ...
- 【bzoj4012】[HNOI2015]开店 动态点分治+STL-vector
题目描述 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的想法当然非常好啦,但是她们也发现她们面临着一个问题 ...
- poj 1743 Musical Theme 后缀自动机/后缀数组/后缀树
题目大意 直接用了hzwer的题意 题意:有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题."主题&qu ...
- EL与OGNL
EL表达式: >>单纯在jsp页面中出现,是在四个作用域中取值,page,request,session,application. >>如果在struts环境中,它除了有在上 ...
- 2017-2018 ACM-ICPC, NEERC, Southern Subregional Contest A E F G H I K M
// 深夜补水题,清早(雾)写水文 A. Automatic Door 题意 \(n(n\leq 1e9)\)个\(employee\)和\(m(m\leq 1e5)\)个\(client\)要进门, ...
- 前端优化 -- Combo Handler
Combo Handler来合并CSS/JS文件 背景 Combo Handler是Yahoo!开发的一个Apache模块,它实现了开发人员简单方便地通过URL来合并JavaScript和CSS文件, ...