springboot2+activiti7+bpmn-js使用入门
搭建
# activiti配置 # 自动部署验证设置:true-开启(默认)、false-关闭 spring.activiti.check-process-definitions=false # 自动创建表 # spring.activiti.database-schema-update=drop-create spring.activiti.database-schema-update=false # 使用历史表 spring.activiti.db-history-used=true spring.activiti.history-level=full # 关闭自动部署 spring.activiti.deployment-mode=never-fail # 自动部署文件路径前后缀 # spring.activiti.process-definition-location-prefix=classpath:/process/ # spring.activiti.process-definition-location-suffixes=**.bpmn,**.bpmn20.xml
@SpringBootApplication(exclude = { org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class, org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration.class })
到此就可以读取指定目录下的流程配置文件使用流程了(我这里没用这种方式)。
表结构和组件
activiti工作流总共包含25张表,都已act_开头(activiti7与老版本的activiti表结构还是有些小变化的);其中:
ACT_GE_:通用,用在各种情况下,2张;
ACT_HI_:历史,8张,默认提供了4种历史级别:
none:不保存历史记录,性能最好;
activity:保存流程实例、任务、活动信息;
audit:默认,保存实例、任务、活动、表单属性;
full:保存完整的历史记录;
ACT_RE_:仓库,3张,保存一些静态信息,如流程定义、资源(图片,规则等);
ACT_RU_:运行,10张,保存流程实例、任务、变量等运行时数据,流程结束后会立即移除数据;
还有两张:ACT_EVT_LOG:存储事件处理日志;ACT_PROCDEF_INFO:流程定义信息;
(老版本的activiti少了4张ACT_RU_,多了4张ACT_ID_的表(用于管理用户和用户组,作用不大))
常用的流程服务组件有:
RepositoryService:提供管理流程定义和部署的API;
RuntimeService:在流程运行时对流程实例进行管理与控制;
TaskService:对流程任务进行管理,如:任务提醒、任务完成和创建;
ManagementService:提供对流程引擎进行管理和维护的服务;
HistoryService:对流程历史数据进行操作;
DynamicBpmnService:实现在不重新部署流程的情况下对流程模型进行部分修改;
流程设计器
依赖
首先需要搭建好vue+elementUI的开发环境;
安装:yarn add bpmn-js bpmn-js-properties-panel(注意bpmn-js的版本不能太高,不然整合activiti可能会报错,我这里使用的这个版本)
"bpmn-js": "~7.5.0", "bpmn-js-properties-panel": "~0.43.1",
整合activiti
{ "name": "Activiti", "uri": "http://activiti.org/bpmn", "prefix": "activiti", "xml": { "tagAlias": "lowerCase" }, "associations": [], "types": [ { "name": "Definitions", "isAbstract": true, "extends": [ "bpmn:Definitions" ], "properties": [ { "name": "diagramRelationId", "isAttr": true, "type": "String" } ] }, { "name": "InOutBinding", "superClass": [ "Element" ], "isAbstract": true, "properties": [ { "name": "source", "isAttr": true, "type": "String" }, { "name": "sourceExpression", "isAttr": true, "type": "String" }, { "name": "target", "isAttr": true, "type": "String" }, { "name": "businessKey", "isAttr": true, "type": "String" }, { "name": "local", "isAttr": true, "type": "Boolean", "default": false }, { "name": "variables", "isAttr": true, "type": "String" } ] }, { "name": "In", "superClass": [ "InOutBinding" ], "meta": { "allowedIn": [ "bpmn:CallActivity" ] } }, { "name": "Out", "superClass": [ "InOutBinding" ], "meta": { "allowedIn": [ "bpmn:CallActivity" ] } }, { "name": "AsyncCapable", "isAbstract": true, "extends": [ "bpmn:Activity", "bpmn:Gateway", "bpmn:Event" ], "properties": [ { "name": "async", "isAttr": true, "type": "Boolean", "default": false }, { "name": "asyncBefore", "isAttr": true, "type": "Boolean", "default": false }, { "name": "asyncAfter", "isAttr": true, "type": "Boolean", "default": false }, { "name": "exclusive", "isAttr": true, "type": "Boolean", "default": true } ] }, { "name": "JobPriorized", "isAbstract": true, "extends": [ "bpmn:Process", "activiti:AsyncCapable" ], "properties": [ { "name": "jobPriority", "isAttr": true, "type": "String" } ] }, { "name": "SignalEventDefinition", "isAbstract": true, "extends": [ "bpmn:SignalEventDefinition" ], "properties": [ { "name": "async", "isAttr": true, "type": "Boolean", "default": false } ] }, { "name": "ErrorEventDefinition", "isAbstract": true, "extends": [ "bpmn:ErrorEventDefinition" ], "properties": [ { "name": "errorCodeVariable", "isAttr": true, "type": "String" }, { "name": "errorMessageVariable", "isAttr": true, "type": "String" } ] }, { "name": "Error", "isAbstract": true, "extends": [ "bpmn:Error" ], "properties": [ { "name": "activiti:errorMessage", "isAttr": true, "type": "String" } ] }, { "name": "PotentialStarter", "superClass": [ "Element" ], "properties": [ { "name": "resourceAssignmentExpression", "type": "bpmn:ResourceAssignmentExpression" } ] }, { "name": "FormSupported", "isAbstract": true, "extends": [ "bpmn:StartEvent", "bpmn:UserTask" ], "properties": [ { "name": "formHandlerClass", "isAttr": true, "type": "String" }, { "name": "formKey", "isAttr": true, "type": "String" } ] }, { "name": "TemplateSupported", "isAbstract": true, "extends": [ "bpmn:Process", "bpmn:FlowElement" ], "properties": [ { "name": "modelerTemplate", "isAttr": true, "type": "String" } ] }, { "name": "Initiator", "isAbstract": true, "extends": [ "bpmn:StartEvent" ], "properties": [ { "name": "initiator", "isAttr": true, "type": "String" } ] }, { "name": "ScriptTask", "isAbstract": true, "extends": [ "bpmn:ScriptTask" ], "properties": [ { "name": "resultVariable", "isAttr": true, "type": "String" }, { "name": "resource", "isAttr": true, "type": "String" } ] }, { "name": "Process", "isAbstract": true, "extends": [ "bpmn:Process" ], "properties": [ { "name": "candidateStarterGroups", "isAttr": true, "type": "String" }, { "name": "candidateStarterUsers", "isAttr": true, "type": "String" }, { "name": "versionTag", "isAttr": true, "type": "String" }, { "name": "historyTimeToLive", "isAttr": true, "type": "String" }, { "name": "isStartableInTasklist", "isAttr": true, "type": "Boolean", "default": true }, { "name": "executionListener", "isAbstract": true, "type": "Expression" } ] }, { "name": "EscalationEventDefinition", "isAbstract": true, "extends": [ "bpmn:EscalationEventDefinition" ], "properties": [ { "name": "escalationCodeVariable", "isAttr": true, "type": "String" } ] }, { "name": "FormalExpression", "isAbstract": true, "extends": [ "bpmn:FormalExpression" ], "properties": [ { "name": "resource", "isAttr": true, "type": "String" } ] }, { "name": "multiinstance_type", "superClass": [ "Element" ] }, { "name": "multiinstance_condition", "superClass": [ "Element" ] }, { "name": "Assignable", "extends": [ "bpmn:UserTask" ], "properties": [ { "name": "assignee", "isAttr": true, "type": "String" }, { "name": "candidateUsers", "isAttr": true, "type": "String" }, { "name": "candidateGroups", "isAttr": true, "type": "String" }, { "name": "dueDate", "isAttr": true, "type": "String" }, { "name": "followUpDate", "isAttr": true, "type": "String" }, { "name": "priority", "isAttr": true, "type": "String" }, { "name": "multiinstance_condition", "isAttr": true, "type": "String" } ] }, { "name": "CallActivity", "extends": [ "bpmn:CallActivity" ], "properties": [ { "name": "calledElementBinding", "isAttr": true, "type": "String", "default": "latest" }, { "name": "calledElementVersion", "isAttr": true, "type": "String" }, { "name": "calledElementVersionTag", "isAttr": true, "type": "String" }, { "name": "calledElementTenantId", "isAttr": true, "type": "String" }, { "name": "caseRef", "isAttr": true, "type": "String" }, { "name": "caseBinding", "isAttr": true, "type": "String", "default": "latest" }, { "name": "caseVersion", "isAttr": true, "type": "String" }, { "name": "caseTenantId", "isAttr": true, "type": "String" }, { "name": "variableMappingClass", "isAttr": true, "type": "String" }, { "name": "variableMappingDelegateExpression", "isAttr": true, "type": "String" } ] }, { "name": "ServiceTaskLike", "extends": [ "bpmn:ServiceTask", "bpmn:BusinessRuleTask", "bpmn:SendTask", "bpmn:MessageEventDefinition" ], "properties": [ { "name": "expression", "isAttr": true, "type": "String" }, { "name": "class", "isAttr": true, "type": "String" }, { "name": "delegateExpression", "isAttr": true, "type": "String" }, { "name": "resultVariable", "isAttr": true, "type": "String" } ] }, { "name": "DmnCapable", "extends": [ "bpmn:BusinessRuleTask" ], "properties": [ { "name": "decisionRef", "isAttr": true, "type": "String" }, { "name": "decisionRefBinding", "isAttr": true, "type": "String", "default": "latest" }, { "name": "decisionRefVersion", "isAttr": true, "type": "String" }, { "name": "mapDecisionResult", "isAttr": true, "type": "String", "default": "resultList" }, { "name": "decisionRefTenantId", "isAttr": true, "type": "String" } ] }, { "name": "ExternalCapable", "extends": [ "activiti:ServiceTaskLike" ], "properties": [ { "name": "type", "isAttr": true, "type": "String" }, { "name": "topic", "isAttr": true, "type": "String" } ] }, { "name": "TaskPriorized", "extends": [ "bpmn:Process", "activiti:ExternalCapable" ], "properties": [ { "name": "taskPriority", "isAttr": true, "type": "String" } ] }, { "name": "Properties", "superClass": [ "Element" ], "meta": { "allowedIn": [ "*" ] }, "properties": [ { "name": "values", "type": "Property", "isMany": true } ] }, { "name": "Property", "superClass": [ "Element" ], "properties": [ { "name": "id", "type": "String", "isAttr": true }, { "name": "name", "type": "String", "isAttr": true }, { "name": "value", "type": "String", "isAttr": true } ] }, { "name": "Connector", "superClass": [ "Element" ], "meta": { "allowedIn": [ "activiti:ServiceTaskLike" ] }, "properties": [ { "name": "inputOutput", "type": "InputOutput" }, { "name": "connectorId", "type": "String" } ] }, { "name": "InputOutput", "superClass": [ "Element" ], "meta": { "allowedIn": [ "bpmn:FlowNode", "activiti:Connector" ] }, "properties": [ { "name": "inputOutput", "type": "InputOutput" }, { "name": "connectorId", "type": "String" }, { "name": "inputParameters", "isMany": true, "type": "InputParameter" }, { "name": "outputParameters", "isMany": true, "type": "OutputParameter" } ] }, { "name": "InputOutputParameter", "properties": [ { "name": "name", "isAttr": true, "type": "String" }, { "name": "value", "isBody": true, "type": "String" }, { "name": "definition", "type": "InputOutputParameterDefinition" } ] }, { "name": "InputOutputParameterDefinition", "isAbstract": true }, { "name": "List", "superClass": [ "InputOutputParameterDefinition" ], "properties": [ { "name": "items", "isMany": true, "type": "InputOutputParameterDefinition" } ] }, { "name": "Map", "superClass": [ "InputOutputParameterDefinition" ], "properties": [ { "name": "entries", "isMany": true, "type": "Entry" } ] }, { "name": "Entry", "properties": [ { "name": "key", "isAttr": true, "type": "String" }, { "name": "value", "isBody": true, "type": "String" }, { "name": "definition", "type": "InputOutputParameterDefinition" } ] }, { "name": "Value", "superClass": [ "InputOutputParameterDefinition" ], "properties": [ { "name": "id", "isAttr": true, "type": "String" }, { "name": "name", "isAttr": true, "type": "String" }, { "name": "value", "isBody": true, "type": "String" } ] }, { "name": "Script", "superClass": [ "InputOutputParameterDefinition" ], "properties": [ { "name": "scriptFormat", "isAttr": true, "type": "String" }, { "name": "resource", "isAttr": true, "type": "String" }, { "name": "value", "isBody": true, "type": "String" } ] }, { "name": "Field", "superClass": [ "Element" ], "meta": { "allowedIn": [ "activiti:ServiceTaskLike", "activiti:ExecutionListener", "activiti:TaskListener" ] }, "properties": [ { "name": "name", "isAttr": true, "type": "String" }, { "name": "expression", "type": "String" }, { "name": "stringValue", "isAttr": true, "type": "String" }, { "name": "string", "type": "String" } ] }, { "name": "InputParameter", "superClass": [ "InputOutputParameter" ] }, { "name": "OutputParameter", "superClass": [ "InputOutputParameter" ] }, { "name": "Collectable", "isAbstract": true, "extends": [ "bpmn:MultiInstanceLoopCharacteristics" ], "superClass": [ "activiti:AsyncCapable" ], "properties": [ { "name": "collection", "isAttr": true, "type": "String" }, { "name": "elementVariable", "isAttr": true, "type": "String" } ] }, { "name": "FailedJobRetryTimeCycle", "superClass": [ "Element" ], "meta": { "allowedIn": [ "activiti:AsyncCapable", "bpmn:MultiInstanceLoopCharacteristics" ] }, "properties": [ { "name": "body", "isBody": true, "type": "String" } ] }, { "name": "ExecutionListener", "superClass": [ "Element" ], "meta": { "allowedIn": [ "bpmn:Task", "bpmn:ServiceTask", "bpmn:UserTask", "bpmn:BusinessRuleTask", "bpmn:ScriptTask", "bpmn:ReceiveTask", "bpmn:ManualTask", "bpmn:ExclusiveGateway", "bpmn:SequenceFlow", "bpmn:ParallelGateway", "bpmn:InclusiveGateway", "bpmn:EventBasedGateway", "bpmn:StartEvent", "bpmn:IntermediateCatchEvent", "bpmn:IntermediateThrowEvent", "bpmn:EndEvent", "bpmn:BoundaryEvent", "bpmn:CallActivity", "bpmn:SubProcess", "bpmn:Process" ] }, "properties": [ { "name": "expression", "isAttr": true, "type": "String" }, { "name": "class", "isAttr": true, "type": "String" }, { "name": "delegateExpression", "isAttr": true, "type": "String" }, { "name": "event", "isAttr": true, "type": "String" }, { "name": "script", "type": "Script" }, { "name": "fields", "type": "Field", "isMany": true } ] }, { "name": "TaskListener", "superClass": [ "Element" ], "meta": { "allowedIn": [ "bpmn:UserTask" ] }, "properties": [ { "name": "expression", "isAttr": true, "type": "String" }, { "name": "class", "isAttr": true, "type": "String" }, { "name": "delegateExpression", "isAttr": true, "type": "String" }, { "name": "event", "isAttr": true, "type": "String" }, { "name": "script", "type": "Script" }, { "name": "fields", "type": "Field", "isMany": true } ] }, { "name": "FormProperty", "superClass": [ "Element" ], "meta": { "allowedIn": [ "bpmn:StartEvent", "bpmn:UserTask" ] }, "properties": [ { "name": "id", "type": "String", "isAttr": true }, { "name": "name", "type": "String", "isAttr": true }, { "name": "type", "type": "String", "isAttr": true }, { "name": "required", "type": "String", "isAttr": true }, { "name": "readable", "type": "String", "isAttr": true }, { "name": "writable", "type": "String", "isAttr": true }, { "name": "variable", "type": "String", "isAttr": true }, { "name": "expression", "type": "String", "isAttr": true }, { "name": "datePattern", "type": "String", "isAttr": true }, { "name": "default", "type": "String", "isAttr": true }, { "name": "values", "type": "Value", "isMany": true } ] }, { "name": "FormProperty", "superClass": [ "Element" ], "properties": [ { "name": "id", "type": "String", "isAttr": true }, { "name": "label", "type": "String", "isAttr": true }, { "name": "type", "type": "String", "isAttr": true }, { "name": "datePattern", "type": "String", "isAttr": true }, { "name": "defaultValue", "type": "String", "isAttr": true }, { "name": "properties", "type": "Properties" }, { "name": "validation", "type": "Validation" }, { "name": "values", "type": "Value", "isMany": true } ] }, { "name": "Validation", "superClass": [ "Element" ], "properties": [ { "name": "constraints", "type": "Constraint", "isMany": true } ] }, { "name": "Constraint", "superClass": [ "Element" ], "properties": [ { "name": "name", "type": "String", "isAttr": true }, { "name": "config", "type": "String", "isAttr": true } ] }, { "name": "ConditionalEventDefinition", "isAbstract": true, "extends": [ "bpmn:ConditionalEventDefinition" ], "properties": [ { "name": "variableName", "isAttr": true, "type": "String" }, { "name": "variableEvent", "isAttr": true, "type": "String" } ] } ], "emumerations": [] }
使用
<template> <div style="width: 100%; height: 100%"> <div ref="bpmn" style="width: 100%; height: 100%;"></div> <div ref="panel" class="panel"></div> <button @click="showSave = true" class="saveBtn" style="margin-left: -50px;">保存</button> <button @click="loadData" class="saveBtn" style="margin-left: 50px;">重置</button> <el-dialog title="保存流程图" :visible.sync="showSave"> <el-form :model="model"> <el-form-item label="key"> <el-input v-model="model.key" autocomplete="off"></el-input> </el-form-item> <el-form-item label="name"> <el-input v-model="model.name" autocomplete="off"></el-input> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="showSave = false">取 消</el-button> <el-button type="primary" @click="save">确 定</el-button> </div> </el-dialog> </div> </template> <script> // 这个只能预览 // import BpmnViewer from 'bpmn-js/lib/Viewer' import Modeler from 'bpmn-js/lib/Modeler' // 面板 import propertiesPanelModule from 'bpmn-js-properties-panel' import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/bpmn' // 适应activiti import activiti from '@/components/activiti/activiti.json' // 汉化 /// import customTranslate from '@/components/activiti/customTranslate.js' export default { data() { return { showSave: false, model: {id: '', key: '', name: '', xml: ''}, modeler: null } }, mounted() { this.modeler = new Modeler({ container: this.$refs.bpmn, propertiesPanel: {parent: this.$refs.panel}, additionalModules: [ propertiesPanelModule, propertiesProviderModule, /// {translate: ['value', customTranslate]} ], moddleExtensions: { activiti: activiti } }) this.loadData() }, methods: { loadData() { this.model.id = this.$route.query.id new Promise(resolve => { if (this.model.id) { this.$apis.act.act.getModel.req({id: this.model.id}).then(res => { if (res.data && res.data.metaInfo) { this.model = res.data } resolve(this.model.metaInfo) }) } else { resolve() } }).then(xml => { xml ? this.modeler.importXML(xml) : this.modeler.createDiagram() }) }, save() { this.modeler.saveXML().then(xml => { this.model.xml = xml.xml this.$apis.act.act.saveModel.req(this.model).then(res => { this.$message({type: 'success', message: '成功'}) this.$router.push({path: '/bpmn/modelEdit', query: {id: res.data.id}}) this.loadData() }) }) /*this.modeler.saveSVG().then(b => { console.log(b) })*/ }, } } </script> <style scoped> /* 基础样式 */ @import '~bpmn-js/dist/assets/diagram-js.css'; @import '~bpmn-js/dist/assets/bpmn-font/css/bpmn.css'; @import '~bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css'; @import '~bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'; /* 右侧面板样式 */ @import '~bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css'; .saveBtn { position: absolute; top: 80%; left: 50%; padding: 12px 16px; border-radius: 10px; font-size: 18px; cursor: pointer; } .panel { position: absolute; box-sizing: border-box; width: 400px; height: 100%; padding: 10px 15px; top: 80px; right: 15px; overflow: auto; } </style>
API
数据查询
保存流程设计
@PostMapping("/saveModel") @ApiOperation("保存流程") public R<String> saveModel(String id, String name, String xml) { Model model = repositoryService.getModel(id); if (model == null) { model = repositoryService.newModel(); } model.setMetaInfo(xml); model.setName(name); model.setKey("testKey"); //保存模型 repositoryService.saveModel(model); return R.success(model.getId()); }
部署
@GetMapping("/deploy") @ApiOperation("部署流程") public R<String> deploy(String modelId) { Model model = repositoryService.getModel(modelId); String deploymentId = model.getDeploymentId(); if (StringUtils.hasText(deploymentId)) { return R.error("流程已部署" + deploymentId); } // 部署流程 Deployment deployment = repositoryService.createDeployment().name(model.getName()).key(model.getKey()) .addString(model.getName() + ".bpmn20.xml", model.getMetaInfo()).deploy(); model.setDeploymentId(deployment.getId()); repositoryService.saveModel(model); return R.success(deployment.getId()); }
启动
@GetMapping("/start") @ApiOperation("启动流程") public R<String> start(String deploymentId, String businessKey) { ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() .deploymentId(deploymentId).active().latestVersion().singleResult(); List<ProcessInstance> list = runtimeService.createProcessInstanceQuery() .processDefinitionId(processDefinition.getId()).processInstanceBusinessKey(businessKey).list(); if (list != null && !list.isEmpty()) { return R.error("businessKey重复"); } // 启动流程 ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId(), businessKey); return R.success(processInstance.getId()); }
审批
@GetMapping("/audit") @ApiOperation("审核流程") public R audit(String processInstanceId) { Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult(); if (task == null) { return R.error("任务不存在"); } // 通过 taskService.complete(task.getId()); // 驳回 /// taskService.resolveTask(task.getId()); return R.success(); }
流程图
@GetMapping("/img") @ApiOperation("流程图") public R<String> img(String defId, String businessKey) throws IOException { BpmnModel bpmnModel = repositoryService.getBpmnModel(defId); List<String> activityIds = new ArrayList<>(); HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(businessKey).singleResult(); List<HistoricActivityInstance> list1 = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstance.getId()).list(); for (HistoricActivityInstance instance : list1) { activityIds.add(instance.getActivityId()); } ProcessDiagramGenerator imageGenerator = new DefaultProcessDiagramGenerator(); String activityFontName = "宋体"; String labelFontName = "宋体"; String annotationFontName = "宋体"; Process process = bpmnModel.getMainProcess(); List<String> flows = new ArrayList<>(); for (FlowElement flowElement : process.getFlowElements()) { boolean isExecute = (flowElement instanceof Activity || flowElement instanceof Event) && activityIds.contains(flowElement.getId()); if (isExecute) { flows.addAll(((FlowNode) flowElement).getIncomingFlows().stream().map(BaseElement::getId).collect(Collectors.toList())); } } InputStream is = imageGenerator.generateDiagram(bpmnModel, activityIds, flows, activityFontName, labelFontName, annotationFontName, true); byte[] bytes = new byte[is.available()]; int len = is.read(bytes); is.close(); String svgImg = new String(bytes, 0, len); return R.success(svgImg); }