学习资料:《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. 11 JVM 垃圾回收(上)

    引用计数法和可达性分析 垃圾回收,就是将已经分配出去的,但却不在使用的内存回收回来,以便再次分配.在 Java 虚拟机语境下,垃圾指的是死亡的对象所占据的堆空间.下面就总结一下如何如何辨别一个对象是否 ...

  2. 精通CSS高级Web标准解决方案(4、对链接应用样式)

    4.1 简单的链接样式 锚可以作为内部引用,也可以作为外部链接,应该区分对待. 伪类选择器: :link 用来寻找没有访问过的链接 :visited 用来寻找已经访问过的链接 a:link{color ...

  3. STL之string使用简介

    声明一个C++字符串 string类的构造函数和析构函数如下: string s; //生成一个空字符串s string s(str) //拷贝构造函数 生成str的复制品 string s(str, ...

  4. [报错处理]Could not find a version that satisfies the requirement xml (from versions)

    安装xml库发生报错 pip3 install xml Collecting xml Could not find a version that satisfies the requirement x ...

  5. OgnlValueStack 源码

    /* * Copyright 2002-2006,2009 The Apache Software Foundation. * * Licensed under the Apache License, ...

  6. 洛谷P2866 [USACO06NOV]糟糕的一天Bad Hair Day

    P2866 [USACO06NOV]糟糕的一天Bad Hair Day 75通过 153提交 题目提供者洛谷OnlineJudge 标签USACO2006云端 难度普及/提高- 时空限制1s / 12 ...

  7. 数位DP毕业题

    原题 题意 给一个 $64$ 位的二进制数,求小于这个数的回文二进制数的数量. 题解 加强版 题意 同上,但允许一个数最多有 $k$ 位不是回文(即把任意 $k$ 位取反后这个数是一个回文数),这种数 ...

  8. Python之时间:calender模块(日历)

    import calendar 1.星期 (1)calendar.day_name 星期的全称 print calendar.day_name for i in calendar.day_name: ...

  9. FileInputStream/FileOutputStream的应用

    这是一对继承于InputStream和OutputStream的类,用于本地文件读写(二进制格式读写并且是顺序读写,读和写要分别创建出不同的文件流对象): 本地文件读写编程的基本过程为: ①  生成文 ...

  10. shell文本过滤编程(一):grep和正则表达式【转】

    转自:http://blog.csdn.net/shallnet/article/details/38799739 版权声明:本文为博主原创文章,未经博主允许不得转载.如果您觉得文章对您有用,请点击文 ...