新增activiti处理页面与接口

This commit is contained in:
WenG 2021-07-11 23:43:38 +08:00
parent ca7893f017
commit d6d5507c85
6 changed files with 655 additions and 40 deletions

View File

@ -0,0 +1,105 @@
package com.cm.inspection.controller.apis.activiti;
import com.cm.common.annotation.CheckRequestBodyAnnotation;
import com.cm.common.base.AbstractController;
import com.cm.common.constants.ISystemConstant;
import com.cm.common.exception.SearchException;
import com.cm.common.pojo.ListPage;
import com.cm.common.result.ErrorResult;
import com.cm.common.result.SuccessResult;
import com.cm.common.result.SuccessResultData;
import com.cm.common.result.SuccessResultList;
import com.cm.inspection.pojo.dtos.check.CheckDTO;
import com.cm.inspection.pojo.vos.activiti.ActivitiVO;
import com.cm.inspection.service.activiti.IActivitiService;
import io.swagger.annotations.*;
import org.activiti.engine.repository.Model;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.management.ObjectName;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* When you feel like quitting. Think about why you started
* 当你想要放弃的时候想想当初你为何开始
*
* @ClassName: ActivitiController
* @Description: activiti
* @Author: WangGeng
* @Date: 2021/7/11 16:36
* @Version: 1.0
**/
@Api(tags = ISystemConstant.API_TAGS_SYSTEM_PREFIX + "流程引擎接口")
@RestController
@RequestMapping(ISystemConstant.API_PREFIX + "/activiti")
public class ActivitiController extends AbstractController {
@Autowired
private IActivitiService activitiService;
@ApiOperation(value = "创建模型", notes = "创建模型接口")
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@PostMapping("create")
@CheckRequestBodyAnnotation
public SuccessResultData create(@RequestBody ActivitiVO activitiVO) throws UnsupportedEncodingException {
return new SuccessResultData<>(activitiService.create(activitiVO));
}
@ApiOperation(value = "删除模型", notes = "删除模型接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "modelId", value = "模型ID", paramType = "path")
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@DeleteMapping("delete/{modelId}")
public SuccessResult delete(@PathVariable("modelId") String modelId) {
activitiService.delete(modelId);
return new SuccessResult();
}
@ApiOperation(value = "发布模型", notes = "发布模型接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "modelId", value = "模型ID", paramType = "path")
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@PutMapping("publish/{modelId}")
public SuccessResult publish(@PathVariable("modelId") String modelId) throws IOException {
activitiService.publish(modelId);
return new SuccessResult();
}
@ApiOperation(value = "撤销发布模型", notes = "撤销发布模型接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "modelId", value = "模型ID", paramType = "path")
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@PutMapping("revoke-publish/{modelId}")
public SuccessResult revokePublish(@PathVariable("modelId") String modelId) {
activitiService.revokePublish(modelId);
return new SuccessResult();
}
@ApiOperation(value = "模型列表", notes = "模型列表接口")
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@GetMapping("list")
public List<Model> list() {
return activitiService.list();
}
@ApiOperation(value = "模型分页列表", notes = "模型分页列表接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "page", value = "当前页码", paramType = "query", dataType = "Integer", defaultValue = "1"),
@ApiImplicitParam(name = "rows", value = "显示数量", paramType = "query", dataType = "Integer", defaultValue = "20"),
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@GetMapping("listpage")
public SuccessResultList<List<Model>> listPage(ListPage page) {
return activitiService.listPage(page.getPage(), page.getRows());
}
}

View File

@ -0,0 +1,65 @@
package com.cm.inspection.pojo.vos.activiti;
import com.cm.common.annotation.CheckEmptyAnnotation;
import com.cm.common.annotation.CheckNumberAnnotation;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
/**
* When you feel like quitting. Think about why you started
* 当你想要放弃的时候想想当初你为何开始
*
* @ClassName: ActivitiVO
* @Description: activiti
* @Author: WangGeng
* @Date: 2021/7/11 22:04
* @Version: 1.0
**/
@ApiModel
public class ActivitiVO {
@ApiModelProperty(name = "modelName", value = "模型名称")
@CheckEmptyAnnotation(name = "模型名称")
private String modelName;
@ApiModelProperty(name = "modelKey", value = "模型key")
@CheckEmptyAnnotation(name = "模型key")
private String modelKey;
@ApiModelProperty(name = "modelDescription", value = "模型描述")
private String modelDescription;
@ApiModelProperty(name = "modelVersion", value = "模型版本")
@CheckNumberAnnotation(name = "模型版本")
private Integer modelVersion;
public String getModelName() {
return modelName == null ? "" : modelName.trim();
}
public void setModelName(String modelName) {
this.modelName = modelName;
}
public String getModelKey() {
return modelKey == null ? "" : modelKey.trim();
}
public void setModelKey(String modelKey) {
this.modelKey = modelKey;
}
public String getModelDescription() {
return modelDescription == null ? "" : modelDescription.trim();
}
public void setModelDescription(String modelDescription) {
this.modelDescription = modelDescription;
}
public Integer getModelVersion() {
return modelVersion;
}
public void setModelVersion(Integer modelVersion) {
this.modelVersion = modelVersion;
}
}

View File

@ -0,0 +1,73 @@
package com.cm.inspection.service.activiti;
import com.cm.common.pojo.ListPage;
import com.cm.common.result.SuccessResultList;
import com.cm.inspection.pojo.vos.activiti.ActivitiVO;
import org.activiti.engine.repository.Model;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
/**
* When you feel like quitting. Think about why you started
* 当你想要放弃的时候想想当初你为何开始
*
* @ClassName: IActivitiService
* @Description: activiti
* @Author: WangGeng
* @Date: 2021/7/11 16:46
* @Version: 1.0
**/
public interface IActivitiService {
/**
* 新建模型
*
* @param activitiVO
* @return
* @throws UnsupportedEncodingException
*/
String create(ActivitiVO activitiVO) throws UnsupportedEncodingException;
/**
* 删除模型
*
* @param modelId 模型ID
*/
void delete(String modelId);
/**
* 发布模型
*
* @param modelId
*/
void publish(String modelId) throws IOException;
/**
* 撤销发布模型
*
* @param modelId
*/
void revokePublish(String modelId);
/**
* 模型列表
*
* @return
*/
List<Model> list();
/**
* 模型分页列表
*
* @param page
* @param rows
* @return
*/
SuccessResultList<List<Model>> listPage(int page, int rows);
}

View File

@ -0,0 +1,158 @@
package com.cm.inspection.service.activiti.impl;
import com.alibaba.fastjson.JSONObject;
import com.cm.common.base.AbstractService;
import com.cm.common.constants.ISystemConstant;
import com.cm.common.exception.RemoveException;
import com.cm.common.exception.SearchException;
import com.cm.common.pojo.ListPage;
import com.cm.common.result.SuccessResultList;
import com.cm.inspection.pojo.vos.activiti.ActivitiVO;
import com.cm.inspection.service.activiti.IActivitiService;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ModelQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
/**
* When you feel like quitting. Think about why you started
* 当你想要放弃的时候想想当初你为何开始
*
* @ClassName: ActivitiServiceImpl
* @Description: activiti
* @Author: WangGeng
* @Date: 2021/7/11 16:46
* @Version: 1.0
**/
@Service
public class ActivitiServiceImpl extends AbstractService implements IActivitiService {
@Autowired
private RepositoryService repositoryService;
@Autowired
private HistoryService historyService;
@Autowired
private RuntimeService runtimeService;
@Override
public String create(ActivitiVO activitiVO) throws UnsupportedEncodingException {
Model model = repositoryService.newModel();
model.setName(activitiVO.getModelName());
model.setKey(activitiVO.getModelKey());
model.setMetaInfo(createModelMetaInfo(activitiVO));
LOG.debug("保存模型");
repositoryService.saveModel(model);
addModelEditorSource(model.getId());
return model.getId();
}
@Override
public void delete(String modelId) {
Model model = repositoryService.getModel(modelId);
if (model == null) {
throw new SearchException("模型不存在");
}
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processDefinitionKey(model.getKey()).singleResult();
// 删除模型实例
if (processInstance != null) {
runtimeService.deleteProcessInstance(processInstance.getId(), "");
historyService.deleteHistoricProcessInstance(processInstance.getId());
}
//删除模型模型
repositoryService.deleteModel(modelId);
}
@Override
public void publish(String modelId) throws IOException {
Model model = repositoryService.getModel(modelId);
if (model == null) {
throw new SearchException("模型不存在");
}
byte[] modelEditorSource = repositoryService.getModelEditorSource(model.getId());
if (modelEditorSource == null) {
throw new SearchException("模型数据不存在");
}
JsonNode modelNode = new ObjectMapper().readTree(modelEditorSource);
BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(modelNode);
Deployment deployment = repositoryService.createDeployment()
.name(model.getName())
.addBpmnModel(model.getKey() + ".bpmn20.xml", bpmnModel)
.deploy();
model.setDeploymentId(deployment.getId());
repositoryService.saveModel(model);
}
@Override
public void revokePublish(String modelId) {
Model modelData = repositoryService.getModel(modelId);
if (modelData == null) {
throw new SearchException("模型不存在");
}
/**
* 参数不加true:为普通删除如果当前规则下有正在执行的流程则抛异常
* 参数加true:为级联删除,会删除和当前规则相关的所有信息包括历史
*/
repositoryService.deleteDeployment(modelData.getDeploymentId(), true);
}
@Override
public List<Model> list() {
return repositoryService.createModelQuery().list();
}
@Override
public SuccessResultList<List<Model>> listPage(int page, int rows) {
ModelQuery modelQuery = repositoryService.createModelQuery();
List<Model> models = modelQuery.listPage(page - 1, rows);
return new SuccessResultList<>(models, page, modelQuery.count());
}
/**
* 创建模型信息
*
* @param activitiVO
* @return
*/
private String createModelMetaInfo(ActivitiVO activitiVO) {
JSONObject metaInfo = new JSONObject();
metaInfo.put(ModelDataJsonConstants.MODEL_NAME, activitiVO.getModelName());
metaInfo.put(ModelDataJsonConstants.MODEL_DESCRIPTION, activitiVO.getModelDescription());
metaInfo.put(ModelDataJsonConstants.MODEL_REVISION, activitiVO.getModelVersion());
return metaInfo.toString();
}
/**
* 添加模型时完善ModelEditorSource
*
* @param modelId
*/
private void addModelEditorSource(String modelId) throws UnsupportedEncodingException {
LOG.info("添加模型时完善ModelEditorSource入参模型ID{}", modelId);
JSONObject editorObject = new JSONObject();
editorObject.put("id", "canvas");
editorObject.put("resourceId", "canvas");
JSONObject stencilSetObject = new JSONObject();
stencilSetObject.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
editorObject.put("stencilset", stencilSetObject);
repositoryService.addModelEditorSource(modelId, editorObject.toString().getBytes(ISystemConstant.CHARSET_UTF8));
}
}

View File

@ -0,0 +1,214 @@
<!doctype html>
<html lang="en">
<head>
<base href="/inspection/">
<meta charset="utf-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
<link rel="stylesheet" href="assets/fonts/font-awesome/css/font-awesome.css"/>
<link rel="stylesheet" href="assets/layuiadmin/layui/css/layui.css" media="all">
<link rel="stylesheet" href="assets/layuiadmin/style/admin.css" media="all">
</head>
<body>
<div class="layui-fluid layui-anim layui-anim-fadein">
<div class="layui-row">
<div class="layui-col-md12">
<div class="layui-card">
<div class="layui-card-body">
<table class="layui-hide" id="dataTable" lay-filter="dataTable"></table>
<!-- 表头按钮组 -->
<script type="text/html" id="headerToolBar">
<div class="layui-btn-group">
<button type="button" class="layui-btn layui-btn-sm" lay-event="saveEvent">
<i class="fa fa-lg fa-plus"></i> 新增
</button>
<button type="button" class="layui-btn layui-btn-normal layui-btn-sm" lay-event="updateEvent">
<i class="fa fa-lg fa-edit"></i> 编辑
</button>
<button type="button" class="layui-btn layui-btn-danger layui-btn-sm" lay-event="removeEvent">
<i class="fa fa-lg fa-trash"></i> 删除
</button>
</div>
</script>
</div>
</div>
</div>
</div>
</div>
<script src="assets/layuiadmin/layui/layui.js"></script>
<script src="assets/js/vendor/viewer/viewer.min.js"></script>
<script>
layui.config({
base: 'assets/layuiadmin/'
}).extend({
index: 'lib/index'
}).use(['index', 'table', 'laydate', 'common'], function() {
var $ = layui.$;
var $win = $(window);
var table = layui.table;
var admin = layui.admin;
var laydate = layui.laydate;
var common = layui.common;
var resizeTimeout = null;
var tableUrl = 'api/activiti/listpage';
// 初始化表格
function initTable() {
table.render({
elem: '#dataTable',
id: 'dataTable',
url: top.restAjax.path(tableUrl, []),
width: admin.screen() > 1 ? '100%' : '',
height: $win.height() - 50,
limit: 20,
limits: [20, 40, 60, 80, 100, 200],
toolbar: '#headerToolBar',
request: {
pageName: 'page',
limitName: 'rows'
},
cols: [[
{type:'checkbox', fixed: 'left'},
{field:'rowNum', width:80, title: '序号', fixed: 'left', align:'center', templet: '<span>{{d.LAY_INDEX}}</span>'},
{field:'name', width:150, title: '模型名称', align:'center',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
return '-';
}
return rowData;
}
},
{field:'key', width:150, title: '模型key', align:'center',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
return '-';
}
return rowData;
}
},
{field:'version', width:80, title: '版本', align:'center',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
return '-';
}
return rowData;
}
},
{field: 'option', width: 80, title: '操作', align:'center',
templet: function(row) {
var rowData = row[this.field];
return '<button class="layui-btn layui-btn-normal layui-btn-sm" lay-event="operationEvent">部署</button>';
}
},
]],
page: true,
parseData: function(data) {
return {
'code': 0,
'msg': '',
'count': data.total,
'data': data.rows
};
}
});
}
// 重载表格
function reloadTable(currentPage) {
table.reload('dataTable', {
url: top.restAjax.path(tableUrl, []),
where: {},
page: {
curr: currentPage
},
height: $win.height() - 50,
});
}
// 删除
function removeData(ids) {
top.dialog.msg(top.dataMessage.delete, {
time: 0,
btn: [top.dataMessage.button.yes, top.dataMessage.button.no],
shade: 0.3,
yes: function (index) {
top.dialog.close(index);
var layIndex;
top.restAjax.delete(top.restAjax.path('api/activiti/delete/{ids}', [ids]), {}, null, function (code, data) {
top.dialog.msg(top.dataMessage.deleteSuccess, {time: 1000});
reloadTable();
}, function (code, data) {
top.dialog.msg(data.msg);
}, function () {
layIndex = top.dialog.msg(top.dataMessage.deleting, {icon: 16, time: 0, shade: 0.3});
}, function () {
top.dialog.close(layIndex);
});
}
});
}
initTable();
// 事件 - 页面变化
$win.on('resize', function() {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(function() {
reloadTable();
}, 500);
});
// 事件 - 搜索
$(document).on('click', '#search', function() {
reloadTable(1);
});
// 事件 - 增删改
table.on('toolbar(dataTable)', function(obj) {
var layEvent = obj.event;
var checkStatus = table.checkStatus('dataTable');
var checkDatas = checkStatus.data;
if(layEvent === 'saveEvent') {
layer.open({
type: 2,
title: false,
closeBtn: 0,
area: ['100%', '100%'],
shadeClose: true,
anim: 2,
content: top.restAjax.path('route/activiti/create.html', []),
end: function() {
reloadTable();
}
});
} else if(layEvent === 'updateEvent') {
if(checkDatas.length === 0) {
top.dialog.msg(top.dataMessage.table.selectEdit);
} else if(checkDatas.length > 1) {
top.dialog.msg(top.dataMessage.table.selectOneEdit);
} else {
layer.open({
type: 2,
title: false,
closeBtn: 0,
area: ['100%', '100%'],
shadeClose: true,
anim: 2,
content: top.restAjax.path('route/activiti/update.html?modelId={modelId}', [checkDatas[0].id]),
end: function() {
reloadTable();
}
});
}
} else if(layEvent === 'removeEvent') {
if(checkDatas.length === 0) {
top.dialog.msg(top.dataMessage.table.selectDelete);
} else if(checkDatas.length > 1) {
top.dialog.msg(top.dataMessage.table.selectOneEdit);
} else {
removeData(checkDatas[0].id);
}
}
});
});
</script>
</body>
</html>

View File

@ -1,48 +1,48 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<base th:href="${#request.getContextPath() + '/'} ">
<meta charset="UTF-8">
<title>工作流模型列表</title>
<link rel="stylesheet" href="/act/js/bootstrap-3.3.7-dist/bootstrap.min.css">
<script src="/act/js/jquery-3.4.1.min.js"></script>
<script src="/act/js/bootstrap-3.3.7-dist/bootstrap.min.js"></script>
<base th:href="${#request.getContextPath() + '/'} ">
<meta charset="UTF-8">
<title>工作流模型列表</title>
<link rel="stylesheet" href="/act/js/bootstrap-3.3.7-dist/bootstrap.min.css">
<script src="/act/js/jquery-3.4.1.min.js"></script>
<script src="/act/js/bootstrap-3.3.7-dist/bootstrap.min.js"></script>
</head>
<body>
<div class="panel panel-default" style="margin: 10px" >
<div class="panel-heading">工作流管理</div>
<div class="panel-body">
<a href="act/create" target="_blank">
<button type="button" class="btn btn-default" aria-label="Left Align">
创建模型
</button>
</a>
</div>
<table class="table">
<thead>
<tr>
<th width="10%">模型编号</th>
<th width="10%">版本</th>
<th width="20%">模型key</th>
<th width="30%">模型名称</th>
<th width="30%">操作</th>
</tr>
</thead>
<tbody>
<tr th:each="actModel,actStat:${actList}">
<th scope="row" th:text="${actModel.id}"></th>
<td th:text="${actModel.version}"></td>
<td th:text="${actModel.key}"></td>
<td th:text="${actModel.name}"></td>
<td>
<a th:href="@{'act/publish?modelId='+${actModel.id}}" >部署</a>
<a th:href="@{'act/revokePublish?modelId='+${actModel.id}}">撤销</a>
<a th:href="@{'act/editor?modelId='+${actModel.id}}">编辑</a>
<a th:href="@{'act/delete?modelId='+${actModel.id}}">删除</a>
</td>
</tr>
</tbody>
</table>
<div class="panel panel-default" style="margin: 10px">
<div class="panel-heading">工作流管理</div>
<div class="panel-body">
<a href="act/create" target="_blank">
<button type="button" class="btn btn-default" aria-label="Left Align">
创建模型
</button>
</a>
</div>
<table class="table">
<thead>
<tr>
<th width="10%">模型编号</th>
<th width="10%">版本</th>
<th width="20%">模型key</th>
<th width="30%">模型名称</th>
<th width="30%">操作</th>
</tr>
</thead>
<tbody>
<tr th:each="actModel,actStat:${actList}">
<th scope="row" th:text="${actModel.id}"></th>
<td th:text="${actModel.version}"></td>
<td th:text="${actModel.key}"></td>
<td th:text="${actModel.name}"></td>
<td>
<a th:href="@{'act/publish?modelId='+${actModel.id}}">部署</a>
<a th:href="@{'act/revokePublish?modelId='+${actModel.id}}">撤销</a>
<a th:href="@{'act/editor?modelId='+${actModel.id}}">编辑</a>
<a th:href="@{'act/delete?modelId='+${actModel.id}}">删除</a>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>