Activiti 之Model 模型管理需求:
实现Model 检索、新增、编辑、删除、导出和部署功能。
Activiti 之Model 模型管理页面操作:
Model 首页:
Model 检索:
Model 新增:
?Model 编辑:
简单设计转正流程,记得点击保存按钮。
?Model 导出:
?
Model 删除:?
5001 流程模型已经被删除。
?Model 部署:?
功能说明:依据流程模型定义发起一次流程实例,由于该功能涉及用户和用户组设置、表单设计。这里仅仅实现在控制台后端输出流程实例Id。
选择模型定义,点击部署:
请教流程实例Id输出:
SpringBoot 之Activiti Model 模型管理后台
SpringBoot程序入口排除Activiti自带SpringSecurity 安全框架校验
package com.zzg;
import org.activiti.spring.boot.SecurityAutoConfiguration;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(exclude={SecurityAutoConfiguration.class})
@MapperScan("com.zzg.mapper")
public class OAApplication {
public static void main(String[] args) {
SpringApplication.run(OAApplication.class, args);
}
}
SpringBoot 引入前端开发框架LayUI
在layUI 开源网站中,下载LayUI。下载地址:Layui - 经典开源模块化前端 UI 框架
在本项目的resource/static文件夹中,引入layui 框架。
SpringBoot 配置SpringMVC 资源路径映射
package com.zzg.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class SpringMVCConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");super.addResourceHandlers(registry);
}
}
SpringBoot 添加流程模型控制器(ModelController)
package com.zzg.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.zzg.common.vo.PageList;
import com.zzg.common.vo.Resp;
import com.zzg.common.vo.enums.CodeMsgEnum;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ModelQuery;
import org.activiti.engine.repository.ProcessDefinition;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;
import java.util.Map;
@RequestMapping("/model")
@RestController
public class ModelController {
private static Logger logger= LoggerFactory.getLogger(ActivitiController.class);
/**
* 流程存储服务
*/
@Autowired
private RepositoryService repositoryService;
@Autowired
private ObjectMapper objectMapper;
/**
* 模型默认首页
* @return
*/
@GetMapping("/index")
public ModelAndView index() {
return new ModelAndView("redirect:/model.html");
}
/**
* 模型分页查询
* @param request
* @return
*/
@GetMapping("/page")
public PageList getModels(HttpServletRequest request){
String pageParame = request.getParameter("page");
String sizeParame = request.getParameter("size");
String modelName = request.getParameter("name");
Integer page = StringUtils.isNotEmpty(pageParame) ? Integer.valueOf(pageParame) : 1;
Integer size = StringUtils.isNotEmpty(sizeParame) ? Integer.valueOf(sizeParame) : 10;
// 获取模型查询实例
ModelQuery modelQuery = repositoryService.createModelQuery();
// 根据模型名称模糊查询
Integer total;
List<Model> models;
if (StringUtils.isNotEmpty(modelName)) {
// 获取模型总数
total = (int) modelQuery.modelNameLike("%" + modelName + "%").count();
// 获取列表数据
models = modelQuery
.modelNameLike("%" + modelName + "%")
.orderByLastUpdateTime()
.desc()
.listPage((page - 1) * size, size);
} else {
// 获取模型总数
total = (int) modelQuery.count();
// 获取列表数据
models = modelQuery
.orderByLastUpdateTime()
.desc()
.listPage((page - 1) * size, size);
}
//封装到pageList对象中
PageList pageList = new PageList(page,total, size);
pageList.setList(models);
return pageList;
}
/**
* 模型删除
* @param request
*/
@GetMapping("/delete")
public Resp<String> delete(HttpServletRequest request){
String processId = request.getParameter("processId");
System.out.println("processId is:" + processId);
// if(StringUtils.isNotEmpty(processId)){
// return Resp.ERROR(CodeMsgEnum.ERROR);
// }
Model model = repositoryService.getModel(processId);
if(model!=null){
System.out.println("指定模型存在");
repositoryService.deleteModel(processId);
}
return Resp.OK_WITHOUT_DATA();
}
/**
* 新增模型
* @param map
* @return
* @throws UnsupportedEncodingException
*/
@PostMapping(value = "/insert", produces = "application/json;charset=utf-8")
public Resp<String> insert(@RequestBody Map map) throws UnsupportedEncodingException {
System.out.println("model 参数接受:" + map);
//初始化一个空模型
Model model = repositoryService.newModel();
//设置一些默认信息,可以用参数接收
int revision = 1;
String key = String.valueOf(map.get("processKey"));
String name =String.valueOf(map.get("processName"));;
String description = String.valueOf(map.get("processDesc"));
//ModelEditorSource
ObjectNode editorNode = objectMapper.createObjectNode();
editorNode.put("id", "canvas");
editorNode.put("resourceId", "canvas");
ObjectNode stencilSetNode = objectMapper.createObjectNode();
stencilSetNode.put("namespace","http://b3mn.org/stencilset/bpmn2.0#");
editorNode.put("stencilset" , stencilSetNode);
ObjectNode modelNode = objectMapper.createObjectNode();
modelNode.put(ModelDataJsonConstants.MODEL_NAME, name);
modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
modelNode.put(ModelDataJsonConstants.MODEL_REVISION, revision);
model.setName(name);
model.setKey(key);
model.setMetaInfo(modelNode.toString());
repositoryService.saveModel(model);
String id = model.getId();
repositoryService.addModelEditorSource(id, editorNode.toString().getBytes("utf-8"));
// return new ModelAndView("redirect:/modeler.html?modelId=" + id);
return Resp.OK(id);
}
/**
* 根据Model部署流程
*/
@PostMapping(value = "deploy/{modelId}")
public Resp<String> deploy(@PathVariable("modelId") String modelId) {
try {
// 获取模型
Model model = repositoryService.getModel(modelId);
ObjectNode objectNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(model.getId()));
BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(objectNode);
String processName = model.getName()+".bpmn20.xml";
byte[] bytes = new BpmnXMLConverter().convertToXML(bpmnModel);
// 部署流程
Deployment deployment = repositoryService
.createDeployment().name(model.getName())
.addString(processName, new String(bytes,"UTF-8"))
.deploy();
System.out.println("流程部署id----"+deployment.getId());
return Resp.OK(deployment.getId());
} catch (Exception e) {
logger.error("根据模型部署流程失败:modelId={}", modelId, e);
return Resp.ERROR(CodeMsgEnum.ERROR);
}
}
/**
* 导出指定模型xml 文件
* @param modelId
* @param response
*/
@GetMapping(value = "export/{modelId}")
public void export(@PathVariable String modelId, HttpServletResponse response) {
try {
Model modelData = repositoryService.getModel(modelId);
BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
JsonNode editorNode = new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);
ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);
OutputStream outputStream = response.getOutputStream();
IOUtils.copy(in, outputStream);
String filename = bpmnModel.getMainProcess().getId() + ".bpmn.xml";
response.setHeader("content-type", "application/octet-stream");
response.setContentType("application/octet-stream;charset=UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "utf-8"));
outputStream.flush();
outputStream.close();
} catch (Exception e) {
logger.error("导出model的xml文件失败:{}", e.getMessage(), e);
}
}
}
SpringBoot 添加流程模型管理页面
在项目中的resource/static文件夹中添加流程模型管理页面(model.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1">
<title>**系统 - Layui</title>
<link rel="stylesheet" href="layui/css/layui.css">
</head>
<body class="layui-layout-body">
<div class="layui-layout layui-layout-admin">
<div class="layui-header">
<div class="layui-logo">** 系统</div>
<ul class="layui-nav layui-layout-right">
<li class="layui-nav-item"><a href="javascript:;"> 超级管理员 </a>
<dl class="layui-nav-child">
<dd>
<a href="">基本资料</a>
</dd>
<dd>
<a href="">安全设置</a>
</dd>
</dl></li>
<li class="layui-nav-item"><a href="">退了</a></li>
</ul>
</div>
<div class="layui-side layui-bg-black">
<div class="layui-side-scroll">
<!-- 左侧导航区域(可配合layui已有的垂直导航) -->
<ul class="layui-nav layui-nav-tree" lay-filter="test">
<li class="layui-nav-item layui-nav-itemed"><a class=""
href="javascript:;">流程管理</a>
<dl class="layui-nav-child">
<dd>
<a href="javascript:;">流程定义</a>
</dd>
<dd>
<a href="javascript:;">表单定义</a>
</dd>
</dl></li>
<li class="layui-nav-item"><a href="javascript:;">用户管理</a>
<dl class="layui-nav-child">
<dd>
<a href="javascript:;">查询用户</a>
</dd>
<dd>
<a href="javascript:;">新增用户</a>
</dd>
</dl></li>
<li class="layui-nav-item"><a href="javascript:;">借阅信息</a>
<dl class="layui-nav-child">
<dd>
<a href="javascript:;">所有记录</a>
</dd>
<dd>
<a href="javascript:;">个人记录</a>
</dd>
</dl></li>
<li class="layui-nav-item"><a href="">帮助</a></li>
</ul>
</div>
</div>
<div class="layui-body">
<!-- 内容主体区域 -->
<div style="padding: 15px;">
<div class="demoTable">
流程名称:
<div class="layui-inline">
<input class="layui-input" name="id" id="demoReload" autocomplete="off">
</div>
<button class="layui-btn" data-type="reload">搜索</button>
<button class="layui-btn" data-type="add">新增</button>
</div>
<table id="tb-book" lay-filter="tb-book"></table>
<script type="text/html" id="barDemo">
<a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
<a class="layui-btn layui-btn-xs" lay-event="deploy">部署</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
<a class="layui-btn layui-btn-warm layui-btn-xs" lay-event="export">导出</a>
</script>
<!-- 编辑弹出层-->
<script type="text/html" id="edit_form">
<div class="layui-col-md10" style="margin-left: 35px;margin-top: 20px">
<form class="layui-form layui-form-pane" lay-filter="edit_form" action="">
<div class="layui-form-item">
<label class="layui-form-label">流程Key</label>
<div class="layui-input-block">
<input type="text" name="processKey" required lay-verify="required" placeholder="请输入流程Key"
autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">流程名称</label>
<div class="layui-input-block">
<input type="text" name="processName" required lay-verify="required" placeholder="请输入流程Key"
autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">流程描述</label>
<div class="layui-input-block">
<input type="text" name="processDesc" required lay-verify="required" placeholder="请输入流程Key"
autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item" style="margin-top: 20px">
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="formDemo">立即提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</form>
</div>
</script>
</div>
</div>
<div class="layui-footer">
<!-- 底部固定区域 -->
? layui.com - **系统
</div>
</div>
<script src="layui/layui.js"></script>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script>
layui.use(['element','table', 'form'], function(){
element = layui.element;
table = layui.table;
form = layui.form;
//第一个实例
table.render({
elem: '#tb-book'
,height: 312
,url: 'http://localhost:8080/model/page'
,where: {page: '1', size: '10'}
,page: true //开启分页
,cols: [[ //表头
{field: 'id', title: 'ID', sort: true, fixed: 'left'}
,{field: 'name', title: '流程名称', }
,{field: 'key', title: '流程Key', }
,{field: 'category', title: '流程分类', }
,{field: 'createTime', title: '创建时间', }
,{fixed: 'right', title:'操作', toolbar: '#barDemo', width:260}
]]
,parseData: function (res) { //将原始数据解析成 table 组件所规定的数据
console.log(res)
return {
"code": 0, //解析接口状态
"count": res.totalData, //解析数据长度
"data": res.list //解析数据列表
};
}
});
// 表格数据重载
var $ = layui.$, active = {
reload: function(){
var demoReload = $('#demoReload');
console.log('----'+ demoReload.val())
//执行重载
table.reload('tb-book', {
page: {
curr: 1 //重新从第 1 页开始
}
,where: {
name: demoReload.val()
}
});
},
add: function(){
layer.open({
type: 1,
title: '新增流程',
area: ['420px', '330px'],
content: $('#edit_form').html()
});
form.on('submit(formDemo)',function(messge){
console.log(messge);
var str={
"processDesc":messge.field.processDesc,
"processKey":messge.field.processKey,
"processName":messge.field.processName
};
$.ajax({
url:"http://localhost:8080/model/insert",
type:"POST",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: JSON.stringify(str),
success:function (msg) {
console.log("成功消息:" + msg);
if (msg.code === 200) {
layer.msg("新增成功", {icon: 6});
layer.closeAll();
// 跳转至流程设计界面
window.location.href ="http://localhost:8080/modeler.html?modelId=" + msg.data
} else {
layer.msg("新增失败", {icon: 5});
}
}
})
return false;//阻止表单跳转,网页url不显示提交的参数。
})
}
};
$('.demoTable .layui-btn').on('click', function(){
var type = $(this).data('type');
active[type] ? active[type].call(this) : '';
});
//监听工具条
table.on('tool(tb-book)', function(obj){
console.log(obj);
var data = obj.data;
if(obj.event === 'deploy') {
$.ajax({
url:"http://localhost:8080/model/deploy/" + data.id,
type:"POST",
success:function (msg) {
console.log(msg);
// var returnCode = msg.returnValue//取得返回数据(Sting类型的字符串)的信息进行取值判断
if (msg.code === 200) {
//layer.closeAll('loading');
//layer.load(2);
layer.msg("模型部署成功:" + msg.data, {icon: 6});
layer.closeAll();
// 加载层 - 风格
} else {
layer.msg("模型部署失败", {icon: 5});
}
}
})
} else if(obj.event === 'del'){
layer.confirm('真的删除行么', function(index){
obj.del();
layer.close(index);
console.log('id is:', data.id)
$.ajax({
url:"http://localhost:8080/model/delete?processId="+data.id,
success:function (msg) {
console.log(msg);
// var returnCode = msg.returnValue//取得返回数据(Sting类型的字符串)的信息进行取值判断
if (msg) {
//layer.closeAll('loading');
//layer.load(2);
layer.msg("修改成功", {icon: 6});
layer.closeAll();
// 加载层 - 风格
} else {
layer.msg("新增失败", {icon: 5});
}
}
})
});
} else if(obj.event === 'edit'){
console.log("点击编辑")
window.location.href ="http://localhost:8080/modeler.html?modelId=" + data.id
} else if(obj.event ==='export'){
console.log("点击导出")
window.location.href ="http://localhost:8080/model/export/" + data.id
}
});
});
</script>
</body>
</html>
?GitHub地址:https://github.com/zhouzhiwengang/SpringBoot-Project.git
|