├── example
├── backend
│ ├── src
│ │ └── main
│ │ │ ├── resources
│ │ │ └── application.yml
│ │ │ └── java
│ │ │ └── cn
│ │ │ └── lzgabel
│ │ │ └── camunda
│ │ │ └── demo
│ │ │ ├── controller
│ │ │ ├── DeployRequest.java
│ │ │ └── WorkflowController.java
│ │ │ └── CamundaBpmnGenerateApplication.java
│ ├── .gitignore
│ └── pom.xml
└── frontend
│ ├── public
│ ├── favicon.ico
│ ├── data.json
│ ├── data字段注释.js
│ ├── departments324.json
│ ├── index.html
│ ├── departments150.json
│ ├── departments.json
│ ├── departments0.json
│ ├── conditions.json
│ ├── conditions字段注释.js
│ ├── employees.json
│ └── roles.json
│ ├── src
│ ├── assets
│ │ ├── logo.png
│ │ └── images
│ │ │ ├── cancel.png
│ │ │ ├── loading.gif
│ │ │ ├── add-close.png
│ │ │ ├── check_box.png
│ │ │ ├── icon_file.png
│ │ │ ├── icon_role.png
│ │ │ ├── jiaojiao.png
│ │ │ ├── add-close1.png
│ │ │ ├── icon_people.png
│ │ │ ├── list_search.png
│ │ │ ├── next_level.png
│ │ │ └── next_level_active.png
│ ├── router.js
│ ├── plugins
│ │ ├── element.js
│ │ ├── axios.js
│ │ └── preload.js
│ ├── main.js
│ ├── components
│ │ ├── dialog
│ │ │ ├── errorDialog.vue
│ │ │ ├── mixins.js
│ │ │ ├── roleDialog.vue
│ │ │ ├── employeesDialog.vue
│ │ │ └── employeesRoleDialog.vue
│ │ ├── drawer
│ │ │ ├── promoterDrawer.vue
│ │ │ ├── copyerDrawer.vue
│ │ │ └── approverDrawer.vue
│ │ └── addNode.vue
│ ├── store.js
│ ├── css
│ │ ├── dialog.css
│ │ └── override-element-ui.css
│ ├── App.vue
│ └── views
│ │ └── setting.vue
│ ├── babel.config.js
│ ├── .gitignore
│ ├── LICENSE
│ ├── vue.config.js
│ ├── package.json
│ ├── pom.xml
│ └── README.md
├── static
└── img
│ └── img.jpeg
├── .editorconfig
├── converter
├── src
│ └── main
│ │ └── java
│ │ └── cn
│ │ └── lzgabel
│ │ └── camunda
│ │ └── converter
│ │ ├── bean
│ │ ├── Process.java
│ │ ├── event
│ │ │ ├── start
│ │ │ │ ├── NoneEndEventDefinition.java
│ │ │ │ ├── NoneStartEventDefinition.java
│ │ │ │ ├── EndEventDefinition.java
│ │ │ │ ├── MessageStartEventDefinition.java
│ │ │ │ ├── EventType.java
│ │ │ │ ├── TimerDefinitionType.java
│ │ │ │ ├── TimerStartEventDefinition.java
│ │ │ │ └── StartEventDefinition.java
│ │ │ ├── intermediate
│ │ │ │ ├── MessageIntermediateCatchEventDefinition.java
│ │ │ │ ├── TimerIntermediateCatchEventDefinition.java
│ │ │ │ └── IntermediateCatchEventDefinition.java
│ │ │ └── EventDefinition.java
│ │ ├── gateway
│ │ │ ├── ExclusiveGatewayDefinition.java
│ │ │ ├── InclusiveGatewayDefinition.java
│ │ │ ├── ParallelGatewayDefinition.java
│ │ │ ├── BranchNode.java
│ │ │ └── GatewayDefinition.java
│ │ ├── DecisionRefBindingType.java
│ │ ├── task
│ │ │ ├── ManualTaskDefinition.java
│ │ │ ├── ReceiveTaskDefinition.java
│ │ │ ├── ScriptTaskDefinition.java
│ │ │ ├── ServiceTaskDefinition.java
│ │ │ ├── UserTaskDefinition.java
│ │ │ └── BusinessRuleTaskDefinition.java
│ │ ├── subprocess
│ │ │ ├── CallActivityDefinition.java
│ │ │ └── SubProcessDefinition.java
│ │ ├── listener
│ │ │ ├── TaskListener.java
│ │ │ └── ExecutionListener.java
│ │ ├── ProcessDefinition.java
│ │ ├── BpmnElementType.java
│ │ └── BaseDefinition.java
│ │ ├── processing
│ │ ├── event
│ │ │ ├── EndEventProcessor.java
│ │ │ ├── IntermediateCatchEventProcessor.java
│ │ │ └── StartEventProcessor.java
│ │ ├── task
│ │ │ ├── ManualTaskProcessor.java
│ │ │ ├── ReceiveTaskProcessor.java
│ │ │ ├── ScriptTaskProcessor.java
│ │ │ ├── ServiceTaskProcessor.java
│ │ │ ├── BusinessRuleTaskProcessor.java
│ │ │ └── UserTaskProcessor.java
│ │ ├── container
│ │ │ ├── CallActivityProcessor.java
│ │ │ └── SubProcessProcessor.java
│ │ ├── gateway
│ │ │ ├── ParallelGatewayProcessor.java
│ │ │ ├── AbstractGatewayProcessor.java
│ │ │ ├── ExclusiveGatewayProcessor.java
│ │ │ └── InclusiveGatewayProcessor.java
│ │ ├── BpmnElementProcessors.java
│ │ └── BpmnElementProcessor.java
│ │ └── BpmnBuilder.java
├── .gitignore
└── pom.xml
├── .gitignore
├── pom.xml
├── README.md
└── LICENSE
/example/backend/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 |
2 | # demo
3 |
--------------------------------------------------------------------------------
/static/img/img.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzgabel/camunda-bpmn-converter/HEAD/static/img/img.jpeg
--------------------------------------------------------------------------------
/example/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzgabel/camunda-bpmn-converter/HEAD/example/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/example/frontend/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzgabel/camunda-bpmn-converter/HEAD/example/frontend/src/assets/logo.png
--------------------------------------------------------------------------------
/example/frontend/src/assets/images/cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzgabel/camunda-bpmn-converter/HEAD/example/frontend/src/assets/images/cancel.png
--------------------------------------------------------------------------------
/example/frontend/src/assets/images/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzgabel/camunda-bpmn-converter/HEAD/example/frontend/src/assets/images/loading.gif
--------------------------------------------------------------------------------
/example/frontend/src/assets/images/add-close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzgabel/camunda-bpmn-converter/HEAD/example/frontend/src/assets/images/add-close.png
--------------------------------------------------------------------------------
/example/frontend/src/assets/images/check_box.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzgabel/camunda-bpmn-converter/HEAD/example/frontend/src/assets/images/check_box.png
--------------------------------------------------------------------------------
/example/frontend/src/assets/images/icon_file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzgabel/camunda-bpmn-converter/HEAD/example/frontend/src/assets/images/icon_file.png
--------------------------------------------------------------------------------
/example/frontend/src/assets/images/icon_role.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzgabel/camunda-bpmn-converter/HEAD/example/frontend/src/assets/images/icon_role.png
--------------------------------------------------------------------------------
/example/frontend/src/assets/images/jiaojiao.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzgabel/camunda-bpmn-converter/HEAD/example/frontend/src/assets/images/jiaojiao.png
--------------------------------------------------------------------------------
/example/frontend/src/assets/images/add-close1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzgabel/camunda-bpmn-converter/HEAD/example/frontend/src/assets/images/add-close1.png
--------------------------------------------------------------------------------
/example/frontend/src/assets/images/icon_people.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzgabel/camunda-bpmn-converter/HEAD/example/frontend/src/assets/images/icon_people.png
--------------------------------------------------------------------------------
/example/frontend/src/assets/images/list_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzgabel/camunda-bpmn-converter/HEAD/example/frontend/src/assets/images/list_search.png
--------------------------------------------------------------------------------
/example/frontend/src/assets/images/next_level.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzgabel/camunda-bpmn-converter/HEAD/example/frontend/src/assets/images/next_level.png
--------------------------------------------------------------------------------
/example/frontend/src/assets/images/next_level_active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lzgabel/camunda-bpmn-converter/HEAD/example/frontend/src/assets/images/next_level_active.png
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | indent_style = space
3 | charset = utf-8
4 | end_of_line = lf
5 | trim_trailing_whitespace = true
6 | insert_final_newline = true
7 |
8 | [*.xml]
9 | indent_size = 2
10 |
11 | [*.html]
12 | indent_size = 2
13 |
14 | [*.md]
15 | indent_size = 2
16 |
17 |
--------------------------------------------------------------------------------
/example/frontend/src/router.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 |
4 | Vue.use(Router)
5 |
6 | export default new Router({
7 | routes: [{
8 | path: '/',
9 | component: (resolve) => {
10 | require([`@/views/setting`], resolve)
11 | }
12 | }]
13 | })
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/Process.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean;
2 |
3 | import lombok.Data;
4 |
5 | /**
6 | * 〈功能简述〉
7 | * 〈〉
8 | *
9 | * @author lizhi
10 | * @since 1.0.0
11 | */
12 | @Data
13 | public class Process {
14 |
15 | /** 流程id */
16 | private String processId;
17 |
18 | /** 流程名称 */
19 | private String name;
20 | }
21 |
--------------------------------------------------------------------------------
/example/frontend/babel.config.js:
--------------------------------------------------------------------------------
1 | const plugins = [
2 | [
3 | "component",
4 | {
5 | "libraryName": "element-ui",
6 | "styleLibraryName": "theme-chalk"
7 | }
8 | ]
9 | ]
10 | if (process.env.NODE_ENV == 'production') {
11 | plugins.push('transform-remove-console')
12 | }
13 | module.exports = {
14 | "presets": [
15 | "@vue/app"
16 | ],
17 | "plugins": plugins
18 | }
--------------------------------------------------------------------------------
/example/frontend/public/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "code": "200",
3 | "msg": "success",
4 | "data": {
5 | "process":{
6 | "processId":"process-id",
7 | "name":"自动生成"
8 | },
9 | "processNode": {
10 | "id": "start",
11 | "nodeType": "start",
12 | "nodeName": "发起人",
13 | "type": 0,
14 | "nextNode": null,
15 | "branchNodes": []
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**
5 | !**/src/test/**
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 |
30 | ### VS Code ###
31 | .vscode/
32 |
--------------------------------------------------------------------------------
/example/backend/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**
5 | !**/src/test/**
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 |
30 | node_modules
31 | ### VS Code ###
32 | .vscode/
33 |
--------------------------------------------------------------------------------
/example/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**
5 | !**/src/test/**
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 |
30 | ### VS Code ###
31 | .vscode/
32 | node_modules
33 |
--------------------------------------------------------------------------------
/example/frontend/public/data字段注释.js:
--------------------------------------------------------------------------------
1 | export default {
2 | "code": "200",
3 | "msg": "success",
4 | "data": {
5 | "process":{
6 | "processId":"process-id",
7 | "name":"自动生成"
8 | },
9 | "processNode": {
10 | "id": "start", // demo 中仅仅作为【发起人】初始节点展示, 最终给后端时使用的是 processNode.nextNode 给到后端
11 | "nodeType": "start",
12 | "nodeName": "发起人",
13 | "type": 0,
14 | "nextNode": null,
15 | "branchNodes": []
16 | }
17 | }
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/event/start/NoneEndEventDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.event.start;
2 |
3 | import lombok.Data;
4 | import lombok.NoArgsConstructor;
5 | import lombok.experimental.SuperBuilder;
6 |
7 | /**
8 | * 〈功能简述〉
9 | * 〈〉
10 | *
11 | * @author lizhi
12 | * @since 1.0.0
13 | */
14 | @Data
15 | @SuperBuilder
16 | @NoArgsConstructor
17 | public class NoneEndEventDefinition extends EndEventDefinition {
18 |
19 | @Override
20 | public String getEventType() {
21 | return EventType.NONE.value();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/event/start/NoneStartEventDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.event.start;
2 |
3 | import lombok.Data;
4 | import lombok.NoArgsConstructor;
5 | import lombok.experimental.SuperBuilder;
6 |
7 | /**
8 | * 〈功能简述〉
9 | * 〈〉
10 | *
11 | * @author lizhi
12 | * @since 1.0.0
13 | */
14 | @Data
15 | @SuperBuilder
16 | @NoArgsConstructor
17 | public class NoneStartEventDefinition extends StartEventDefinition {
18 |
19 | @Override
20 | public String getEventType() {
21 | return EventType.NONE.value();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example/frontend/public/departments324.json:
--------------------------------------------------------------------------------
1 | {
2 | "code": "200",
3 | "msg": "操作成功",
4 | "data": {
5 | "childDepartments": [],
6 | "employees": [{
7 | "id": "53128133",
8 | "employeeName": "测试3",
9 | "isLeave": "0",
10 | "open": "false"
11 | }],
12 | "titleDepartments": [{
13 | "departmentId": "324",
14 | "departmentKey": "ZNBN",
15 | "departmentName": "法务部",
16 | "departmentNames": "法务部",
17 | "id": "324",
18 | "parentId": "0"
19 | }]
20 | }
21 | }
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/event/start/EndEventDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.event.start;
2 |
3 | import cn.lzgabel.camunda.converter.bean.event.EventDefinition;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 | import lombok.experimental.SuperBuilder;
7 |
8 | /**
9 | * 〈功能简述〉
10 | * 〈〉
11 | *
12 | * @author lizhi
13 | * @since 1.0.0
14 | */
15 | @Data
16 | @SuperBuilder
17 | @NoArgsConstructor
18 | public class EndEventDefinition extends EventDefinition {
19 |
20 | @Override
21 | public String getNodeType() {
22 | return "endEvent";
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/example/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | 审批设置
10 |
11 |
12 |
13 |
14 | We're sorry but ydc_espace doesn't work properly without JavaScript enabled. Please enable it to
15 | continue.
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/example/backend/src/main/java/cn/lzgabel/camunda/demo/controller/DeployRequest.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.demo.controller;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.Process;
5 | import com.alibaba.fastjson.JSON;
6 | import lombok.Data;
7 |
8 | /**
9 | * 〈功能简述〉
10 | * 〈process流程定义〉
11 | *
12 | * @author lizhi
13 | * @since 1.0.0
14 | */
15 | @Data
16 | public class DeployRequest {
17 |
18 | private Process process;
19 |
20 | private BaseDefinition processNode;
21 |
22 | @Override
23 | public String toString() {
24 | return JSON.toJSONString(this);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/gateway/ExclusiveGatewayDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.gateway;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BpmnElementType;
4 | import lombok.NoArgsConstructor;
5 | import lombok.experimental.SuperBuilder;
6 |
7 | /**
8 | * 〈功能简述〉
9 | * 〈排他网关定义〉
10 | *
11 | * @author lizhi
12 | * @since 1.0.0
13 | */
14 | @SuperBuilder
15 | @NoArgsConstructor
16 | public class ExclusiveGatewayDefinition extends GatewayDefinition {
17 |
18 | @Override
19 | public String getNodeType() {
20 | return BpmnElementType.EXCLUSIVE_GATEWAY.getElementTypeName().get();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/gateway/InclusiveGatewayDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.gateway;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BpmnElementType;
4 | import lombok.NoArgsConstructor;
5 | import lombok.experimental.SuperBuilder;
6 |
7 | /**
8 | * 〈功能简述〉
9 | * 〈包容网关〉
10 | *
11 | * @author lizhi
12 | * @since 1.0.0
13 | */
14 | @SuperBuilder
15 | @NoArgsConstructor
16 | public class InclusiveGatewayDefinition extends GatewayDefinition {
17 |
18 | @Override
19 | public String getNodeType() {
20 | return BpmnElementType.INCLUSIVE_GATEWAY.getElementTypeName().get();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/converter/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | !**/src/main/**/target/
4 | !**/src/test/**/target/
5 |
6 | ### IntelliJ IDEA ###
7 | .idea/modules.xml
8 | .idea/jarRepositories.xml
9 | .idea/compiler.xml
10 | .idea/libraries/
11 | *.iws
12 | *.iml
13 | *.ipr
14 |
15 | ### Eclipse ###
16 | .apt_generated
17 | .classpath
18 | .factorypath
19 | .project
20 | .settings
21 | .springBeans
22 | .sts4-cache
23 |
24 | ### NetBeans ###
25 | /nbproject/private/
26 | /nbbuild/
27 | /dist/
28 | /nbdist/
29 | /.nb-gradle/
30 | build/
31 | !**/src/main/**/build/
32 | !**/src/test/**/build/
33 |
34 | ### VS Code ###
35 | .vscode/
36 |
37 | ### Mac OS ###
38 | .DS_Store
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/DecisionRefBindingType.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean;
2 |
3 | /**
4 | * 〈功能简述〉
5 | * 〈〉
6 | *
7 | * @author lizhi
8 | * @since 1.0.0
9 | */
10 | public enum DecisionRefBindingType {
11 | LATEST("latest"),
12 |
13 | VERSION("version"),
14 |
15 | VERSION_TAG("versionTag");
16 |
17 | private final String value;
18 |
19 | DecisionRefBindingType(final String value) {
20 | this.value = value;
21 | }
22 |
23 | public boolean isEquals(String value) {
24 | return this.value.equals(value);
25 | }
26 |
27 | public String value() {
28 | return this.value;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/event/start/MessageStartEventDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.event.start;
2 |
3 | import lombok.Data;
4 | import lombok.NoArgsConstructor;
5 | import lombok.NonNull;
6 | import lombok.experimental.SuperBuilder;
7 |
8 | /**
9 | * 〈功能简述〉
10 | * 〈〉
11 | *
12 | * @author lizhi
13 | * @since 1.0.0
14 | */
15 | @Data
16 | @SuperBuilder
17 | @NoArgsConstructor
18 | public class MessageStartEventDefinition extends StartEventDefinition {
19 |
20 | @NonNull private String messageName;
21 |
22 | @Override
23 | public String getEventType() {
24 | return EventType.MESSAGE.value();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/gateway/ParallelGatewayDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.gateway;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BpmnElementType;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 | import lombok.experimental.SuperBuilder;
7 |
8 | /**
9 | * 〈功能简述〉
10 | * 〈并行网关〉
11 | *
12 | * @author lizhi
13 | * @since 1.0.0
14 | */
15 | @Data
16 | @SuperBuilder
17 | @NoArgsConstructor
18 | public class ParallelGatewayDefinition extends GatewayDefinition {
19 |
20 | @Override
21 | public String getNodeType() {
22 | return BpmnElementType.PARALLEL_GATEWAY.getElementTypeName().get();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/event/start/EventType.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.event.start;
2 |
3 | /**
4 | * 〈功能简述〉
5 | * 〈〉
6 | *
7 | * @author lizhi
8 | * @since 1.0.0
9 | */
10 | public enum EventType {
11 |
12 | /** none event */
13 | NONE("none"),
14 |
15 | /** timer event */
16 | TIMER("timer"),
17 |
18 | /** message event */
19 | MESSAGE("message");
20 |
21 | private String value;
22 |
23 | EventType(String value) {
24 | this.value = value;
25 | }
26 |
27 | public String value() {
28 | return value;
29 | }
30 |
31 | public boolean isEqual(String value) {
32 | return this.value.equals(value);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/example/backend/src/main/java/cn/lzgabel/camunda/demo/CamundaBpmnGenerateApplication.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.demo;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.context.annotation.ComponentScan;
6 |
7 | /**
8 | * 〈功能简述〉
9 | * 〈〉
10 | *
11 | * @author lizhi
12 | * @date 2021-08-21
13 | * @since 1.0.0
14 | */
15 | @SpringBootApplication
16 | @ComponentScan(basePackages = "cn.lzgabel")
17 | public class CamundaBpmnGenerateApplication {
18 |
19 | public static void main(String[] args) {
20 | SpringApplication.run(CamundaBpmnGenerateApplication.class, args);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/event/intermediate/MessageIntermediateCatchEventDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.event.intermediate;
2 |
3 | import lombok.Data;
4 | import lombok.NoArgsConstructor;
5 | import lombok.NonNull;
6 | import lombok.experimental.SuperBuilder;
7 |
8 | /**
9 | * 〈功能简述〉
10 | * 〈〉
11 | *
12 | * @author lizhi
13 | * @since 1.0.0
14 | */
15 | @Data
16 | @SuperBuilder
17 | @NoArgsConstructor
18 | public class MessageIntermediateCatchEventDefinition extends IntermediateCatchEventDefinition {
19 |
20 | @NonNull private String messageName;
21 |
22 | @Override
23 | public String getEventType() {
24 | return "message";
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/event/intermediate/TimerIntermediateCatchEventDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.event.intermediate;
2 |
3 | import lombok.Data;
4 | import lombok.NoArgsConstructor;
5 | import lombok.NonNull;
6 | import lombok.experimental.SuperBuilder;
7 |
8 | /**
9 | * 〈功能简述〉
10 | * 〈〉
11 | *
12 | * @author lizhi
13 | * @since 1.0.0
14 | */
15 | @Data
16 | @SuperBuilder
17 | @NoArgsConstructor
18 | public class TimerIntermediateCatchEventDefinition extends IntermediateCatchEventDefinition {
19 |
20 | @NonNull private String timerDefinition;
21 |
22 | @Override
23 | public String getEventType() {
24 | return "timer";
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/event/EventDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.event;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.event.start.EventType;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 | import lombok.experimental.SuperBuilder;
8 |
9 | /**
10 | * 〈功能简述〉
11 | * 〈〉
12 | *
13 | * @author lizhi
14 | * @since 1.0.0
15 | */
16 | @Data
17 | @SuperBuilder
18 | @NoArgsConstructor
19 | public abstract class EventDefinition extends BaseDefinition {
20 |
21 | private String eventType;
22 |
23 | public String getEventType() {
24 | return EventType.NONE.value();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/task/ManualTaskDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.task;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.BpmnElementType;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 | import lombok.experimental.SuperBuilder;
8 |
9 | /**
10 | * 〈功能简述〉
11 | * 〈〉
12 | *
13 | * @author lizhi
14 | * @since 1.0.0
15 | */
16 | @Data
17 | @SuperBuilder
18 | @NoArgsConstructor
19 | public class ManualTaskDefinition extends BaseDefinition {
20 |
21 | @Override
22 | public String getNodeType() {
23 | return BpmnElementType.MANUAL_TASK.getElementTypeName().get();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/event/start/TimerDefinitionType.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.event.start;
2 |
3 | /**
4 | * 〈功能简述〉
5 | * 〈〉
6 | *
7 | * @author lizhi
8 | * @since 1.0.0
9 | */
10 | public enum TimerDefinitionType {
11 |
12 | /** date */
13 | DATE("date"),
14 |
15 | /** cycle */
16 | CYCLE("cycle"),
17 |
18 | /** duration */
19 | DURATION("duration");
20 |
21 | private String value;
22 |
23 | TimerDefinitionType(String value) {
24 | this.value = value;
25 | }
26 |
27 | public boolean isEqual(String value) {
28 | return this.value.equals(value);
29 | }
30 |
31 | public String value() {
32 | return this.value;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/gateway/BranchNode.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.gateway;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import com.fasterxml.jackson.annotation.JsonProperty;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 | import lombok.experimental.SuperBuilder;
8 |
9 | /**
10 | * 〈功能简述〉
11 | * 〈分支节点〉
12 | *
13 | * @author lizhi
14 | * @since 1.0.0
15 | */
16 | @Data
17 | @SuperBuilder
18 | @NoArgsConstructor
19 | public class BranchNode {
20 |
21 | @JsonProperty("isDefault")
22 | private boolean isDefault;
23 |
24 | private String nodeName;
25 |
26 | private String conditionExpression;
27 |
28 | private BaseDefinition nextNode;
29 | }
30 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/task/ReceiveTaskDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.task;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.BpmnElementType;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 | import lombok.experimental.SuperBuilder;
8 |
9 | /**
10 | * 〈功能简述〉
11 | * 〈接收任务定义〉
12 | *
13 | * @author lizhi
14 | * @since 1.0.0
15 | */
16 | @Data
17 | @SuperBuilder
18 | @NoArgsConstructor
19 | public class ReceiveTaskDefinition extends BaseDefinition {
20 |
21 | private String messageName;
22 |
23 | @Override
24 | public String getNodeType() {
25 | return BpmnElementType.RECEIVE_TASK.getElementTypeName().get();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/example/frontend/public/departments150.json:
--------------------------------------------------------------------------------
1 | {
2 | "code": "200",
3 | "msg": "操作成功",
4 | "data": {
5 | "childDepartments": [],
6 | "employees": [{
7 | "id": "53128112",
8 | "employeeName": "测试1",
9 | "isLeave": "0",
10 | "open": "false"
11 | },{
12 | "id": "53128113",
13 | "employeeName": "测试2",
14 | "isLeave": "0",
15 | "open": "false"
16 | }],
17 | "titleDepartments": [{
18 | "departmentId": "150",
19 | "departmentKey": "RLXZB_V2",
20 | "departmentName": "人力行政部",
21 | "departmentNames": "人力行政部",
22 | "id": "150",
23 | "parentId": "0"
24 | }]
25 | }
26 | }
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/event/start/TimerStartEventDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.event.start;
2 |
3 | import lombok.Data;
4 | import lombok.NoArgsConstructor;
5 | import lombok.NonNull;
6 | import lombok.experimental.SuperBuilder;
7 |
8 | /**
9 | * 〈功能简述〉
10 | * 〈〉
11 | *
12 | * @author lizhi
13 | * @since 1.0.0
14 | */
15 | @Data
16 | @NoArgsConstructor
17 | @SuperBuilder
18 | public class TimerStartEventDefinition extends StartEventDefinition {
19 |
20 | /** {@link TimerDefinitionType} */
21 | @NonNull private String timerDefinitionType;
22 |
23 | @NonNull private String timerDefinition;
24 |
25 | @Override
26 | public String getEventType() {
27 | return EventType.TIMER.value();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/example/frontend/public/departments.json:
--------------------------------------------------------------------------------
1 | {
2 | "code": "200",
3 | "msg": "操作成功",
4 | "data": {
5 | "childDepartments": [{
6 | "departmentKey": "RLXZB_V2",
7 | "departmentName": "人力行政部",
8 | "id": "150",
9 | "parentId": "0",
10 | "departmentNames": "人力行政部"
11 | }, {
12 | "departmentKey": "ZNBN",
13 | "departmentName": "法务部",
14 | "id": "324",
15 | "parentId": "0",
16 | "departmentNames": "法务部"
17 | }],
18 | "employees": [{
19 | "id": "53128111",
20 | "employeeName": "亚nan",
21 | "isLeave": "0",
22 | "open": "false"
23 | }],
24 | "titleDepartments": []
25 | }
26 | }
--------------------------------------------------------------------------------
/example/frontend/public/departments0.json:
--------------------------------------------------------------------------------
1 | {
2 | "code": "200",
3 | "msg": "操作成功",
4 | "data": {
5 | "childDepartments": [{
6 | "departmentKey": "RLXZB_V2",
7 | "departmentName": "人力行政部",
8 | "id": "150",
9 | "parentId": "0",
10 | "departmentNames": "人力行政部"
11 | }, {
12 | "departmentKey": "ZNBN",
13 | "departmentName": "法务部",
14 | "id": "324",
15 | "parentId": "0",
16 | "departmentNames": "法务部"
17 | }],
18 | "employees": [{
19 | "id": "53128111",
20 | "employeeName": "亚nan",
21 | "isLeave": "0",
22 | "open": "false"
23 | }],
24 | "titleDepartments": []
25 | }
26 | }
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/subprocess/CallActivityDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.subprocess;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.BpmnElementType;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 | import lombok.NonNull;
8 | import lombok.experimental.SuperBuilder;
9 |
10 | /**
11 | * 〈功能简述〉
12 | * 〈调用活动定义〉
13 | *
14 | * @author lizhi
15 | * @since 1.0.0
16 | */
17 | @Data
18 | @SuperBuilder
19 | @NoArgsConstructor
20 | public class CallActivityDefinition extends BaseDefinition {
21 |
22 | @NonNull private String calledElement;
23 |
24 | @Override
25 | public String getNodeType() {
26 | return BpmnElementType.CALL_ACTIVITY.getElementTypeName().get();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/subprocess/SubProcessDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.subprocess;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.BpmnElementType;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 | import lombok.NonNull;
8 | import lombok.experimental.SuperBuilder;
9 |
10 | /**
11 | * 〈功能简述〉
12 | * 〈子流程定义〉
13 | *
14 | * @author lizhi
15 | * @since 1.0.0
16 | */
17 | @Data
18 | @SuperBuilder
19 | @NoArgsConstructor
20 | public class SubProcessDefinition extends BaseDefinition {
21 |
22 | @NonNull private BaseDefinition childNode;
23 |
24 | @Override
25 | public String getNodeType() {
26 | return BpmnElementType.SUB_PROCESS.getElementTypeName().get();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/listener/TaskListener.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.listener;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.Accessors;
5 |
6 | /**
7 | * 〈功能简述〉
8 | * 〈〉
9 | *
10 | * @author lizhi
11 | * @date 2023/3/2
12 | * @since 1.0.0
13 | */
14 | @Data
15 | @Accessors(chain = true)
16 | public class TaskListener {
17 |
18 | private String eventType = "create";
19 |
20 | private String expression;
21 |
22 | private String delegateExpression;
23 |
24 | private String javaClass;
25 |
26 | public boolean isClass() {
27 | return javaClass != null;
28 | }
29 |
30 | public boolean isDelegateExpression() {
31 | return delegateExpression != null;
32 | }
33 |
34 | public boolean isExpression() {
35 | return expression != null;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/example/frontend/public/conditions.json:
--------------------------------------------------------------------------------
1 | {
2 | "code": "200",
3 | "msg": "操作成功",
4 | "data": [
5 | {
6 | "columnId": "1090",
7 | "showType": "1",
8 | "showName": "预计采购总费用",
9 | "columnName": "planProcurementTotalMoney",
10 | "columnType": "Double",
11 | "fixedDownBoxValue": ""
12 | },
13 | {
14 | "columnId": "1092",
15 | "showType": "3",
16 | "showName": "采购类型",
17 | "columnName": "procurementType",
18 | "columnType": "String",
19 | "fixedDownBoxValue": "{\"1\":{\"key\":\"1\",\"value\":\"新开园区集中采购\",\"column\":\"\",\"type\":\"1\"}}"
20 | },
21 | {
22 | "columnId": "1093",
23 | "showType": "1",
24 | "showName": "园区面积",
25 | "columnName": "parkArea",
26 | "columnType": "Double",
27 | "fixedDownBoxValue": ""
28 | }
29 | ]
30 | }
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/task/ScriptTaskDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.task;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.BpmnElementType;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 | import lombok.experimental.SuperBuilder;
8 |
9 | /**
10 | * 〈功能简述〉
11 | * 〈脚本任务定义〉
12 | *
13 | * @author lizhi
14 | * @since 1.0.0
15 | */
16 | @Data
17 | @SuperBuilder
18 | @NoArgsConstructor
19 | public class ScriptTaskDefinition extends BaseDefinition {
20 |
21 | private String scriptFormat;
22 |
23 | private String scriptText;
24 |
25 | private String resultVariable;
26 |
27 | @Override
28 | public String getNodeType() {
29 | return BpmnElementType.SCRIPT_TASK.getElementTypeName().get();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/example/frontend/src/plugins/element.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import {
3 | Button, Popover,
4 | Dialog, Drawer,
5 | Tree, Radio,
6 | RadioGroup, Checkbox,
7 | CheckboxGroup, Breadcrumb,
8 | BreadcrumbItem, Message,
9 | Tabs,
10 | TabPane,
11 | } from 'element-ui'
12 | import 'element-ui/lib/theme-chalk/index.css'
13 | Vue.use(Button)
14 | Vue.use(Popover)
15 | Vue.use(Dialog)
16 | Vue.use(Drawer)
17 | Vue.use(Tree)
18 | Vue.use(Radio)
19 | Vue.use(RadioGroup)
20 | Vue.use(Checkbox)
21 | Vue.use(CheckboxGroup)
22 | Vue.use(Breadcrumb)
23 | Vue.use(BreadcrumbItem)
24 | Vue.use(Tabs)
25 | Vue.use(TabPane)
26 |
27 | Vue.prototype.$message = {
28 | error(msg) {
29 | Message.closeAll();
30 | Message.error(msg)
31 | },
32 | success(msg) {
33 | Message.closeAll();
34 | Message.success(msg)
35 | }
36 | }
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/task/ServiceTaskDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.task;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.BpmnElementType;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 | import lombok.experimental.SuperBuilder;
8 |
9 | /**
10 | * 〈功能简述〉
11 | * 〈服务任务定义〉
12 | *
13 | * @author lizhi
14 | * @since 1.0.0
15 | */
16 | @Data
17 | @SuperBuilder
18 | @NoArgsConstructor
19 | public class ServiceTaskDefinition extends BaseDefinition {
20 |
21 | private String topic;
22 |
23 | private String expression;
24 |
25 | private String resultVariable;
26 |
27 | private String delegateExpression;
28 |
29 | private String javaClass;
30 |
31 | @Override
32 | public String getNodeType() {
33 | return BpmnElementType.SERVICE_TASK.getElementTypeName().get();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/processing/event/EndEventProcessor.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.processing.event;
2 |
3 | import cn.lzgabel.camunda.converter.bean.event.start.EndEventDefinition;
4 | import cn.lzgabel.camunda.converter.processing.BpmnElementProcessor;
5 | import org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder;
6 | import org.camunda.bpm.model.bpmn.builder.EndEventBuilder;
7 |
8 | /**
9 | * 〈功能简述〉
10 | * 〈EndEvent节点类型详情设置〉
11 | *
12 | * @author lizhi
13 | * @since 1.0.0
14 | */
15 | public class EndEventProcessor
16 | implements BpmnElementProcessor {
17 |
18 | @Override
19 | public String onComplete(AbstractFlowNodeBuilder builder, EndEventDefinition flowNode) {
20 | final EndEventBuilder endEventBuilder = (EndEventBuilder) createInstance(builder, flowNode);
21 | return endEventBuilder.getElement().getId();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/listener/ExecutionListener.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.listener;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.Accessors;
5 | import org.apache.commons.lang3.StringUtils;
6 |
7 | /**
8 | * 〈功能简述〉
9 | * 〈〉
10 | *
11 | * @author lizhi
12 | * @date 2022/10/26
13 | * @since 1.0.0
14 | */
15 | @Data
16 | @Accessors(chain = true)
17 | public class ExecutionListener {
18 |
19 | private String eventType = "start";
20 |
21 | private String expression;
22 |
23 | private String delegateExpression;
24 |
25 | private String javaClass;
26 |
27 | public boolean isClass() {
28 | return StringUtils.isNotBlank(javaClass);
29 | }
30 |
31 | public boolean isDelegateExpression() {
32 | return StringUtils.isNotBlank(delegateExpression);
33 | }
34 |
35 | public boolean isExpression() {
36 | return StringUtils.isNotBlank(expression);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/task/UserTaskDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.task;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.BpmnElementType;
5 | import cn.lzgabel.camunda.converter.bean.listener.TaskListener;
6 | import java.util.List;
7 | import lombok.Data;
8 | import lombok.NoArgsConstructor;
9 | import lombok.experimental.SuperBuilder;
10 |
11 | /**
12 | * 〈功能简述〉
13 | * 〈用户任务定义〉
14 | *
15 | * @author lizhi
16 | * @since 1.0.0
17 | */
18 | @Data
19 | @SuperBuilder
20 | @NoArgsConstructor
21 | public class UserTaskDefinition extends BaseDefinition {
22 |
23 | private String assignee;
24 |
25 | private String candidateUsers;
26 |
27 | private String candidateGroups;
28 |
29 | private List taskListeners;
30 |
31 | @Override
32 | public String getNodeType() {
33 | return BpmnElementType.USER_TASK.getElementTypeName().get();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/example/frontend/public/conditions字段注释.js:
--------------------------------------------------------------------------------
1 | export default {
2 | "code": "200",
3 | "msg": "操作成功",
4 | "data": [
5 | {
6 | //条件id columnId == 0 为发起人
7 | "columnId": "1090",
8 | //columnType == "String" && showType == "3"为多选 columnType == "Double"为区间
9 | "showType": "1",
10 | "showName": "预计采购总费用",
11 | //columnName 条件自定义字段
12 | "columnName": "planProcurementTotalMoney",
13 | "columnType": "Double",
14 | //fixedDownBoxValue 匹配 columnType == "String" && showType == "3"时子选项内容
15 | "fixedDownBoxValue": ""
16 | },
17 | {
18 | "columnId": "1092",
19 | "showType": "3",
20 | "showName": "采购类型",
21 | "columnName": "procurementType",
22 | "columnType": "String",
23 | "fixedDownBoxValue": "{\"1\":{\"key\":\"1\",\"value\":\"新开园区集中采购\",\"column\":\"\",\"type\":\"1\"}}"
24 | },
25 | {
26 | "columnId": "1093",
27 | "showType": "1",
28 | "showName": "园区面积",
29 | "columnName": "parkArea",
30 | "columnType": "Double",
31 | "fixedDownBoxValue": ""
32 | }
33 | ]
34 | }
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/task/BusinessRuleTaskDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.task;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.BpmnElementType;
5 | import cn.lzgabel.camunda.converter.bean.DecisionRefBindingType;
6 | import lombok.Data;
7 | import lombok.NoArgsConstructor;
8 | import lombok.experimental.SuperBuilder;
9 |
10 | /**
11 | * 〈功能简述〉
12 | * 〈业务规则定义〉
13 | *
14 | * @author lizhi
15 | * @since 1.0.0
16 | */
17 | @Data
18 | @SuperBuilder
19 | @NoArgsConstructor
20 | public class BusinessRuleTaskDefinition extends BaseDefinition {
21 |
22 | private String decisionRef;
23 |
24 | private String decisionRefBinding = DecisionRefBindingType.LATEST.value();
25 |
26 | private String decisionRefVersion;
27 |
28 | private String decisionRefVersionTag;
29 |
30 | private String decisionRefTenantId;
31 |
32 | private String resultVariable;
33 |
34 | @Override
35 | public String getNodeType() {
36 | return BpmnElementType.BUSINESS_RULE_TASK.getElementTypeName().get();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/gateway/GatewayDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.gateway;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import com.google.common.collect.Lists;
5 | import java.util.List;
6 | import lombok.Data;
7 | import lombok.NoArgsConstructor;
8 | import lombok.experimental.SuperBuilder;
9 |
10 | /**
11 | * 〈功能简述〉
12 | * 〈网关数据定义〉
13 | *
14 | * @author lizhi
15 | * @since 1.0.0
16 | */
17 | @Data
18 | @SuperBuilder
19 | @NoArgsConstructor
20 | public abstract class GatewayDefinition extends BaseDefinition {
21 |
22 | /** 分支节点 */
23 | private List branchNodes;
24 |
25 | public abstract static class GatewayDefinitionBuilder<
26 | C extends GatewayDefinition, B extends GatewayDefinition.GatewayDefinitionBuilder>
27 | extends BaseDefinitionBuilder {
28 |
29 | public GatewayDefinitionBuilder() {
30 | branchNodes = Lists.newArrayList();
31 | }
32 |
33 | public B branchNode(BranchNode branchNode) {
34 | branchNodes.add(branchNode);
35 | return self();
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/example/frontend/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Stavin Li
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/example/backend/src/main/java/cn/lzgabel/camunda/demo/controller/WorkflowController.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.demo.controller;
2 |
3 | import cn.lzgabel.camunda.converter.BpmnBuilder;
4 | import java.io.ByteArrayOutputStream;
5 | import org.camunda.bpm.model.bpmn.Bpmn;
6 | import org.camunda.bpm.model.bpmn.BpmnModelInstance;
7 | import org.springframework.http.MediaType;
8 | import org.springframework.web.bind.annotation.*;
9 |
10 | /**
11 | * 〈功能简述〉
12 | * 〈〉
13 | *
14 | * @author lizhi
15 | * @date 2021/8/21
16 | * @since 1.0.0
17 | */
18 | @RestController
19 | @RequestMapping
20 | public class WorkflowController {
21 |
22 | @GetMapping("/ok")
23 | public String ok() {
24 | return "ok";
25 | }
26 |
27 | @PostMapping(value = "/deploy", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
28 | @CrossOrigin
29 | public byte[] deploy(@RequestBody DeployRequest request) {
30 |
31 | BpmnModelInstance modelInstance = BpmnBuilder.build(request.toString());
32 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
33 | Bpmn.writeModelToStream(outputStream, modelInstance);
34 | return outputStream.toByteArray();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/event/intermediate/IntermediateCatchEventDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.event.intermediate;
2 |
3 | import cn.lzgabel.camunda.converter.bean.event.EventDefinition;
4 | import com.fasterxml.jackson.annotation.JsonSubTypes;
5 | import com.fasterxml.jackson.annotation.JsonTypeInfo;
6 | import lombok.Data;
7 | import lombok.NoArgsConstructor;
8 | import lombok.experimental.SuperBuilder;
9 |
10 | /**
11 | * 〈功能简述〉
12 | * 〈〉
13 | *
14 | * @author lizhi
15 | * @since 1.0.0
16 | */
17 | @Data
18 | @SuperBuilder
19 | @NoArgsConstructor
20 | @JsonTypeInfo(
21 | use = JsonTypeInfo.Id.NAME,
22 | include = JsonTypeInfo.As.EXISTING_PROPERTY,
23 | property = "eventType",
24 | visible = true)
25 | @JsonSubTypes({
26 | @JsonSubTypes.Type(value = TimerIntermediateCatchEventDefinition.class, name = "timer"),
27 | @JsonSubTypes.Type(value = MessageIntermediateCatchEventDefinition.class, name = "message")
28 | })
29 | public class IntermediateCatchEventDefinition extends EventDefinition {
30 |
31 | @Override
32 | public String getNodeType() {
33 | return "intermediateCatchEvent";
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/event/start/StartEventDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean.event.start;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BpmnElementType;
4 | import cn.lzgabel.camunda.converter.bean.event.EventDefinition;
5 | import com.fasterxml.jackson.annotation.JsonSubTypes;
6 | import com.fasterxml.jackson.annotation.JsonTypeInfo;
7 | import lombok.Data;
8 | import lombok.NoArgsConstructor;
9 | import lombok.experimental.SuperBuilder;
10 |
11 | /**
12 | * 〈功能简述〉
13 | * 〈〉
14 | *
15 | * @author lizhi
16 | * @since 1.0.0
17 | */
18 | @Data
19 | @SuperBuilder
20 | @NoArgsConstructor
21 | @JsonTypeInfo(
22 | use = JsonTypeInfo.Id.NAME,
23 | include = JsonTypeInfo.As.EXISTING_PROPERTY,
24 | property = "eventType",
25 | visible = true)
26 | @JsonSubTypes({
27 | @JsonSubTypes.Type(value = TimerStartEventDefinition.class, name = "timer"),
28 | @JsonSubTypes.Type(value = NoneStartEventDefinition.class, name = "none"),
29 | @JsonSubTypes.Type(value = MessageStartEventDefinition.class, name = "message")
30 | })
31 | public abstract class StartEventDefinition extends EventDefinition {
32 |
33 | @Override
34 | public String getNodeType() {
35 | return BpmnElementType.START_EVENT.getElementTypeName().get();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/example/frontend/public/employees.json:
--------------------------------------------------------------------------------
1 | {
2 | "code": "200",
3 | "msg": "操作成功",
4 | "data": {
5 | "navigatepageNums": ["1"],
6 | "startRow": "1",
7 | "hasNextPage": "false",
8 | "prePage": "0",
9 | "nextPage": "0",
10 | "endRow": "3",
11 | "pageSize": "30",
12 | "list": [{
13 | "departmentName": "招商事业部",
14 | "employeeName": "111111",
15 | "employeeDepartmentId": "121",
16 | "id": "5312",
17 | "departmentNames": "招商事业部"
18 | }, {
19 | "departmentName": "111",
20 | "employeeName": "东亚-测试",
21 | "employeeDepartmentId": "139",
22 | "id": "2515638",
23 | "departmentNames": "111"
24 | }, {
25 | "departmentName": "招商事业部",
26 | "employeeName": "1111",
27 | "employeeDepartmentId": "121",
28 | "id": "2515746",
29 | "departmentNames": "招商事业部"
30 | }],
31 | "pageNum": "1",
32 | "navigatePages": "8",
33 | "navigateFirstPage": "1",
34 | "total": "3",
35 | "pages": "1",
36 | "size": "3",
37 | "isLastPage": "true",
38 | "hasPreviousPage": "false",
39 | "navigateLastPage": "1",
40 | "isFirstPage": "true"
41 | }
42 | }
--------------------------------------------------------------------------------
/example/frontend/src/main.js:
--------------------------------------------------------------------------------
1 | import 'babel-polyfill'
2 | import Vue from 'vue'
3 | import './plugins/axios'
4 | import App from './App.vue'
5 | import router from './router'
6 | import store from './store'
7 | import './plugins/element.js'
8 | import './css/override-element-ui.css'
9 | import func from './plugins/preload.js'
10 | import nodeWrap from '@/components/nodeWrap'
11 | Vue.component('nodeWrap', nodeWrap); //初始化组件
12 | import addNode from '@/components/addNode'
13 | Vue.component('addNode', addNode); //初始化组件
14 | Vue.prototype.$func = func;
15 | Vue.config.productionTip = false;
16 | Vue.directive('enterNumber', {
17 | bind: function(el, { value = 2 }) {
18 | el = el.nodeName == "INPUT" ? el : el.children[0]
19 | var RegStr = value == 0 ? `^[\\+\\-]?\\d+\\d{0,0}` : `^[\\+\\-]?\\d+\\.?\\d{0,${value}}`;
20 | el.addEventListener('keyup', function() {
21 | el.value = el.value.match(new RegExp(RegStr, 'g'));
22 | el.dispatchEvent(new Event('input'))
23 | })
24 | }
25 | });
26 |
27 | Vue.directive('focus', {
28 | // 当被绑定的元素插入到 DOM 中时……
29 | inserted: function(el) {
30 | el.focus()
31 | }
32 | });
33 | new Vue({
34 | router,
35 | store,
36 | components: {
37 | nodeWrap,
38 | addNode,
39 | },
40 | render: h => h(App)
41 | }).$mount('#app')
--------------------------------------------------------------------------------
/example/frontend/src/components/dialog/errorDialog.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
当前无法发布
6 |
7 |
8 |
以下内容不完善,需进行修改
9 |
10 |
11 |
流程设计
12 |
{{item.name}} 未选择{{item.type}}
13 |
14 |
15 |
16 |
17 |
18 |
22 |
23 |
24 |
25 |
43 |
44 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/processing/task/ManualTaskProcessor.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.processing.task;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.task.ManualTaskDefinition;
5 | import cn.lzgabel.camunda.converter.processing.BpmnElementProcessor;
6 | import java.lang.reflect.InvocationTargetException;
7 | import java.util.Objects;
8 | import org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder;
9 | import org.camunda.bpm.model.bpmn.builder.ManualTaskBuilder;
10 |
11 | /**
12 | * 〈功能简述〉
13 | * 〈ManualTask节点类型详情设置〉
14 | *
15 | * @author lizhi
16 | * @since 1.0.0
17 | */
18 | public class ManualTaskProcessor
19 | implements BpmnElementProcessor {
20 |
21 | @Override
22 | public String onComplete(AbstractFlowNodeBuilder flowNodeBuilder, ManualTaskDefinition flowNode)
23 | throws InvocationTargetException, IllegalAccessException {
24 | final ManualTaskBuilder manualTaskBuilder =
25 | (ManualTaskBuilder) createInstance(flowNodeBuilder, flowNode);
26 | String id = manualTaskBuilder.getElement().getId();
27 |
28 | // 如果当前任务还有后续任务,则遍历创建后续任务
29 | BaseDefinition nextNode = flowNode.getNextNode();
30 | if (Objects.nonNull(nextNode)) {
31 | return onCreate(moveToNode(flowNodeBuilder, id), nextNode);
32 | } else {
33 | return id;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/example/frontend/vue.config.js:
--------------------------------------------------------------------------------
1 | const CompressionWebpackPlugin = require('compression-webpack-plugin'),
2 | productionGzipExtensions = ['js', 'css'];
3 | let path = require('path')
4 |
5 | function resolve(dir) {
6 | return path.join(__dirname, dir)
7 | }
8 | module.exports = {
9 | lintOnSave: false,
10 | productionSourceMap: false,
11 | publicPath: process.env.NODE_ENV === "production" ? '/' : '/',
12 | devServer: {
13 | disableHostCheck: true,
14 | https: false, // https:{type:Boolean}
15 | port: 6969,
16 | },
17 | chainWebpack: config => {
18 | config.resolve.alias.set('@', resolve('src'));
19 | config.performance.set('hints', false);
20 | config.output.filename('[name].[hash].js').end();
21 | },
22 | configureWebpack: config => {
23 | //配置参数详解https://www.webpackjs.com/plugins/compression-webpack-plugin/
24 | if (process.env.NODE_ENV !== 'development') { // 非开发环境
25 | config.plugins.push(new CompressionWebpackPlugin({
26 | algorithm: 'gzip', //algorithm: 可以是 function(buf, callback) 或者字符串。对于字符串来说依照 zlib 的算法(或者 zopfli 的算法)。默认值是 "gzip"。
27 | test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'), //test: 所有匹配该正则的资源都会被处理。默认值是全部资源。
28 | threshold: 10240, //threshold: 只有大小大于该值的资源会被处理。单位是 bytes。默认值是 0。
29 | minRatio: 0.8 //minRatio: 只有压缩率小于这个值的资源才会被处理。默认值是 0.8。
30 | }))
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/example/frontend/src/store.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | Vue.use(Vuex)
4 |
5 | export default new Vuex.Store({
6 | state: {
7 | promoterDrawer: false,
8 | flowPermission: [],
9 | approverDrawer: false,
10 | approverConfig:{},
11 | copyerDrawer: false,
12 | copyerConfig:{},
13 | conditionDrawer: false,
14 | conditionsConfig:{
15 | branchNodes: [],
16 | },
17 | },
18 | mutations: {
19 | updatePromoter(status,promoterDrawer){
20 | status.promoterDrawer = promoterDrawer
21 | },
22 | updateFlowPermission(status,flowPermission){
23 | status.flowPermission = flowPermission
24 | },
25 | updateApprover(status,approverDrawer){
26 | status.approverDrawer = approverDrawer
27 | },
28 | updateApproverConfig(status,approverConfig){
29 | status.approverConfig = approverConfig
30 | },
31 | updateCopyer(status,copyerDrawer){
32 | status.copyerDrawer = copyerDrawer
33 | },
34 | updateCopyerConfig(status,copyerConfig){
35 | status.copyerConfig = copyerConfig
36 | },
37 | updateCondition(status,conditionDrawer){
38 | status.conditionDrawer = conditionDrawer
39 | },
40 | updateConditionsConfig(status,conditionsConfig){
41 | status.conditionsConfig = conditionsConfig
42 | },
43 | },
44 | actions: {}
45 | })
46 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/processing/container/CallActivityProcessor.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.processing.container;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.subprocess.CallActivityDefinition;
5 | import cn.lzgabel.camunda.converter.processing.BpmnElementProcessor;
6 | import java.lang.reflect.InvocationTargetException;
7 | import java.util.Objects;
8 | import org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder;
9 | import org.camunda.bpm.model.bpmn.builder.CallActivityBuilder;
10 |
11 | /**
12 | * 〈功能简述〉
13 | * 〈CallActivity节点类型详情设置〉
14 | *
15 | * @author lizhi
16 | * @since 1.0.0
17 | */
18 | public class CallActivityProcessor
19 | implements BpmnElementProcessor {
20 |
21 | @Override
22 | public String onComplete(AbstractFlowNodeBuilder flowNodeBuilder, CallActivityDefinition flowNode)
23 | throws InvocationTargetException, IllegalAccessException {
24 | final CallActivityBuilder callActivityBuilder =
25 | (CallActivityBuilder) createInstance(flowNodeBuilder, flowNode);
26 |
27 | callActivityBuilder.calledElement(flowNode.getCalledElement());
28 | String id = callActivityBuilder.getElement().getId();
29 | BaseDefinition childNode = flowNode.getNextNode();
30 | if (Objects.nonNull(childNode)) {
31 | return onCreate(moveToNode(callActivityBuilder, id), childNode);
32 | }
33 | return id;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/example/frontend/src/components/dialog/mixins.js:
--------------------------------------------------------------------------------
1 | export default {
2 | data(){
3 | return {
4 | visibleDialog: false,
5 | searchVal: "",
6 | activeName: "1",
7 | departments: {},
8 | roles: [],
9 | }
10 | },
11 | methods:{
12 | getRoleList() {
13 | this.$axios.get(`${process.env.BASE_URL}roles.json`).then(res => {
14 | this.roles = res.data.list;
15 | })
16 | },
17 | getDepartmentList(parentId = 0) {
18 | this.$axios.get(`${process.env.BASE_URL}departments.json?parentId=${parentId}`).then(res => {
19 | this.departments = res.data;
20 | })
21 | },
22 | getDebounceData(event, type = 1) {
23 | this.$func.debounce(function () {
24 | if (event.target.value) {
25 | if (type == 1) {
26 | this.departments.childDepartments = [];
27 | this.$axios.get(`${process.env.BASE_URL}employees.json?searchName=${event.target.value}&pageNum=1&pageSize=30`).then(res => {
28 | this.departments.employees = res.data.list
29 | })
30 | } else {
31 | this.$axios.get(`${process.env.BASE_URL}roles.json?searchName=${event.target.value}&pageNum=1&pageSize=30`).then(res => {
32 | this.roles = res.data.list
33 | })
34 | }
35 | } else {
36 | type == 1 ? this.getDepartmentList() : this.getRoleList();
37 | }
38 | }.bind(this))()
39 | },
40 | }
41 | }
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/processing/task/ReceiveTaskProcessor.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.processing.task;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.task.ReceiveTaskDefinition;
5 | import cn.lzgabel.camunda.converter.processing.BpmnElementProcessor;
6 | import java.lang.reflect.InvocationTargetException;
7 | import java.util.Objects;
8 | import org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder;
9 | import org.camunda.bpm.model.bpmn.builder.ReceiveTaskBuilder;
10 |
11 | /**
12 | * 〈功能简述〉
13 | * 〈ReceiveTask节点类型详情设置〉
14 | *
15 | * @author lizhi
16 | * @since 1.0.0
17 | */
18 | public class ReceiveTaskProcessor
19 | implements BpmnElementProcessor {
20 |
21 | @Override
22 | public String onComplete(AbstractFlowNodeBuilder flowNodeBuilder, ReceiveTaskDefinition flowNode)
23 | throws InvocationTargetException, IllegalAccessException {
24 | String messageName = flowNode.getMessageName();
25 |
26 | // 创建 ReceiveTask
27 | final ReceiveTaskBuilder receiveTaskBuilder =
28 | (ReceiveTaskBuilder) createInstance(flowNodeBuilder, flowNode);
29 | receiveTaskBuilder.message(messageName);
30 |
31 | String id = receiveTaskBuilder.getElement().getId();
32 |
33 | // 如果当前任务还有后续任务,则遍历创建后续任务
34 | BaseDefinition nextNode = flowNode.getNextNode();
35 | if (Objects.nonNull(nextNode)) {
36 | return onCreate(moveToNode(flowNodeBuilder, id), nextNode);
37 | } else {
38 | return id;
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/processing/task/ScriptTaskProcessor.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.processing.task;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.task.ScriptTaskDefinition;
5 | import cn.lzgabel.camunda.converter.processing.BpmnElementProcessor;
6 | import java.lang.reflect.InvocationTargetException;
7 | import java.util.Objects;
8 | import org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder;
9 | import org.camunda.bpm.model.bpmn.builder.ScriptTaskBuilder;
10 |
11 | /**
12 | * 〈功能简述〉
13 | * 〈ScriptTask节点类型详情设置〉
14 | *
15 | * @author lizhi
16 | * @since 1.0.0
17 | */
18 | public class ScriptTaskProcessor
19 | implements BpmnElementProcessor {
20 |
21 | @Override
22 | public String onComplete(AbstractFlowNodeBuilder flowNodeBuilder, ScriptTaskDefinition flowNode)
23 | throws InvocationTargetException, IllegalAccessException {
24 | String scriptFormat = flowNode.getScriptFormat();
25 | String scriptText = flowNode.getScriptText();
26 | String resultVariable = flowNode.getResultVariable();
27 |
28 | // 创建 ScriptTask
29 | final ScriptTaskBuilder scriptTaskBuilder =
30 | (ScriptTaskBuilder) createInstance(flowNodeBuilder, flowNode);
31 |
32 | scriptTaskBuilder
33 | .scriptFormat(scriptFormat)
34 | .camundaResultVariable(resultVariable)
35 | .scriptText(scriptText);
36 | String id = scriptTaskBuilder.getElement().getId();
37 |
38 | // 如果当前任务还有后续任务,则遍历创建后续任务
39 | BaseDefinition nextNode = flowNode.getNextNode();
40 | if (Objects.nonNull(nextNode)) {
41 | return onCreate(moveToNode(flowNodeBuilder, id), nextNode);
42 | } else {
43 | return id;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/example/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ydc_espace",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "dev": "vue-cli-service serve",
8 | "test": "vue-cli-service build --mode test",
9 | "build": "vue-cli-service build",
10 | "lint": "vue-cli-service lint"
11 | },
12 | "dependencies": {
13 | "core-js": "^2.6.5",
14 | "element-ui": "^2.12.0",
15 | "vue": "^2.6.10",
16 | "vue-router": "^3.0.3",
17 | "vuex": "^3.0.1"
18 | },
19 | "devDependencies": {
20 | "@vue/cli-plugin-babel": "^3.0.3",
21 | "@vue/cli-plugin-eslint": "^3.0.3",
22 | "@vue/cli-service": "^3.0.3",
23 | "axios": "^0.21.1",
24 | "babel-eslint": "^10.0.1",
25 | "babel-plugin-component": "^1.1.1",
26 | "babel-plugin-transform-remove-console": "^6.9.4",
27 | "babel-polyfill": "^6.26.0",
28 | "compression-webpack-plugin": "^3.0.0",
29 | "eslint": "^5.16.0",
30 | "eslint-plugin-vue": "^5.0.0",
31 | "less": "^2.7.2",
32 | "less-loader": "^4.0.6",
33 | "vue-cli-plugin-axios": "0.0.4",
34 | "vue-cli-plugin-element": "^1.0.1",
35 | "vue-template-compiler": "^2.6.10"
36 | },
37 | "eslintConfig": {
38 | "root": true,
39 | "env": {
40 | "node": true
41 | },
42 | "extends": [
43 | "plugin:vue/essential",
44 | "eslint:recommended"
45 | ],
46 | "rules": {},
47 | "parserOptions": {
48 | "parser": "babel-eslint"
49 | }
50 | },
51 | "postcss": {
52 | "plugins": {
53 | "autoprefixer": {}
54 | }
55 | },
56 | "browserslist": [
57 | "> 1%",
58 | "last 2 versions"
59 | ]
60 | }
61 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/ProcessDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean;
2 |
3 | import com.fasterxml.jackson.databind.DeserializationFeature;
4 | import com.fasterxml.jackson.databind.ObjectMapper;
5 | import java.io.IOException;
6 | import lombok.Data;
7 | import lombok.NoArgsConstructor;
8 | import lombok.NonNull;
9 | import lombok.SneakyThrows;
10 | import lombok.experimental.SuperBuilder;
11 |
12 | /**
13 | * 〈功能简述〉
14 | * 〈process流程定义〉
15 | *
16 | * @author lizhi
17 | * @since 1.0.0
18 | */
19 | @Data
20 | @SuperBuilder
21 | @NoArgsConstructor
22 | public class ProcessDefinition {
23 |
24 | private Process process;
25 |
26 | private BaseDefinition processNode;
27 |
28 | public abstract static class ProcessDefinitionBuilder<
29 | C extends ProcessDefinition, B extends ProcessDefinition.ProcessDefinitionBuilder> {
30 |
31 | public ProcessDefinitionBuilder() {
32 | process = new Process();
33 | }
34 |
35 | public B name(String name) {
36 | process.setName(name);
37 | return self();
38 | }
39 |
40 | public B processId(@NonNull String processId) {
41 | process.setProcessId(processId);
42 | return self();
43 | }
44 |
45 | public B processNode(@NonNull BaseDefinition processNode) {
46 | this.processNode = processNode;
47 | return self();
48 | }
49 | }
50 |
51 | public static ProcessDefinition of(String json) {
52 | ObjectMapper mapper =
53 | new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
54 | ProcessDefinition result;
55 | try {
56 | result = mapper.readValue(json, ProcessDefinition.class);
57 | } catch (IOException e) {
58 | throw new RuntimeException(e.getMessage(), e.getCause());
59 | }
60 | return result;
61 | }
62 |
63 | @SneakyThrows
64 | @Override
65 | public String toString() {
66 | return new ObjectMapper().writeValueAsString(this);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/processing/event/IntermediateCatchEventProcessor.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.processing.event;
2 |
3 | import cn.lzgabel.camunda.converter.bean.event.intermediate.IntermediateCatchEventDefinition;
4 | import cn.lzgabel.camunda.converter.bean.event.intermediate.MessageIntermediateCatchEventDefinition;
5 | import cn.lzgabel.camunda.converter.bean.event.intermediate.TimerIntermediateCatchEventDefinition;
6 | import cn.lzgabel.camunda.converter.bean.event.start.EventType;
7 | import cn.lzgabel.camunda.converter.processing.BpmnElementProcessor;
8 | import org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder;
9 | import org.camunda.bpm.model.bpmn.builder.IntermediateCatchEventBuilder;
10 |
11 | /**
12 | * 〈功能简述〉
13 | * 〈IntermediateCatchEvent节点类型详情设置〉
14 | *
15 | * @author lizhi
16 | * @since 1.0.0
17 | */
18 | public class IntermediateCatchEventProcessor
19 | implements BpmnElementProcessor {
20 |
21 | @Override
22 | public String onComplete(
23 | AbstractFlowNodeBuilder flowNodeBuilder, IntermediateCatchEventDefinition flowNode) {
24 | String eventType = flowNode.getEventType();
25 | final IntermediateCatchEventBuilder intermediateCatchEventBuilder =
26 | (IntermediateCatchEventBuilder) createInstance(flowNodeBuilder, flowNode);
27 |
28 | if (EventType.TIMER.isEqual(eventType)) {
29 | TimerIntermediateCatchEventDefinition timer =
30 | (TimerIntermediateCatchEventDefinition) flowNode;
31 | String timerDefinition = timer.getTimerDefinition();
32 | return intermediateCatchEventBuilder.timerWithDuration(timerDefinition).getElement().getId();
33 | } else if (EventType.MESSAGE.isEqual(eventType)) {
34 | MessageIntermediateCatchEventDefinition message =
35 | (MessageIntermediateCatchEventDefinition) flowNode;
36 | String messageName = message.getMessageName();
37 | return intermediateCatchEventBuilder.message(messageName).getElement().getId();
38 | }
39 | return null;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 |
7 | org.springframework.boot
8 | spring-boot-starter-parent
9 | 2.7.11
10 |
11 |
12 | parent
13 | cn.lzgabel
14 | camunda-bpmn-converter-parent
15 | 1.0-SNAPSHOT
16 | pom
17 |
18 |
19 |
20 | v16.5.0
21 | 8.1.2
22 | 2.7.11
23 |
24 |
25 |
26 |
27 |
28 | org.springframework.boot
29 | spring-boot-dependencies
30 | ${version.spring-boot}
31 | pom
32 | import
33 |
34 |
35 |
36 | com.fasterxml.jackson
37 | jackson-bom
38 | 2.13.3
39 |
40 |
41 |
42 | cn.lzgabel
43 | camunda-bpmn-converter
44 | 1.0-SNAPSHOT
45 |
46 |
47 |
48 | cn.lzgabel
49 | workflow-frontend
50 | 1.0-SNAPSHOT
51 |
52 |
53 |
54 | org.projectlombok
55 | lombok
56 | 1.18.22
57 |
58 |
59 |
60 |
61 |
62 | example/backend
63 | example/frontend
64 | converter
65 |
66 |
67 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/processing/container/SubProcessProcessor.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.processing.container;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.subprocess.SubProcessDefinition;
5 | import cn.lzgabel.camunda.converter.processing.BpmnElementProcessor;
6 | import java.lang.reflect.InvocationTargetException;
7 | import java.util.Objects;
8 | import org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder;
9 | import org.camunda.bpm.model.bpmn.builder.EmbeddedSubProcessBuilder;
10 | import org.camunda.bpm.model.bpmn.builder.StartEventBuilder;
11 | import org.camunda.bpm.model.bpmn.builder.SubProcessBuilder;
12 |
13 | /**
14 | * 〈功能简述〉
15 | * 〈SubProcess节点类型详情设置〉
16 | *
17 | * @author lizhi
18 | * @since 1.0.0
19 | */
20 | public class SubProcessProcessor
21 | implements BpmnElementProcessor {
22 |
23 | @Override
24 | public String onComplete(AbstractFlowNodeBuilder flowNodeBuilder, SubProcessDefinition flowNode)
25 | throws InvocationTargetException, IllegalAccessException {
26 | final SubProcessBuilder subProcessBuilder =
27 | (SubProcessBuilder) createInstance(flowNodeBuilder, flowNode);
28 | EmbeddedSubProcessBuilder embeddedSubProcessBuilder = subProcessBuilder.embeddedSubProcess();
29 |
30 | // 子流程内部创建开始
31 | StartEventBuilder startEventBuilder = embeddedSubProcessBuilder.startEvent();
32 | subProcessBuilder.getElement().setName(flowNode.getNodeName());
33 | String lastNode = startEventBuilder.getElement().getId();
34 | // 创建子流程节点
35 | BaseDefinition childNode = flowNode.getChildNode();
36 | if (Objects.nonNull(childNode)) {
37 | lastNode =
38 | onCreate(
39 | moveToNode(subProcessBuilder, startEventBuilder.getElement().getId()), childNode);
40 | }
41 | // 子流程内部创建结束
42 | moveToNode(startEventBuilder, lastNode).endEvent();
43 |
44 | // 如果当前任务还有后续任务,则遍历创建后续任务
45 | String id = subProcessBuilder.getElement().getId();
46 | BaseDefinition nextNode = flowNode.getNextNode();
47 | if (Objects.nonNull(nextNode)) {
48 | return onCreate(moveToNode(subProcessBuilder, id), nextNode);
49 | }
50 | return subProcessBuilder.getElement().getId();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/example/frontend/public/roles.json:
--------------------------------------------------------------------------------
1 | {
2 | "code": "200",
3 | "msg": "操作成功",
4 | "data": {
5 | "navigatepageNums": [],
6 | "startRow": "1",
7 | "hasNextPage": "false",
8 | "prePage": "0",
9 | "nextPage": "0",
10 | "endRow": "8",
11 | "pageSize": "0",
12 | "list": [{
13 | "code": "SHGLY",
14 | "roleId": "1",
15 | "scope": "1",
16 | "roleName": "审核管理员",
17 | "description": "初始化内置审批角色",
18 | "status": "1"
19 | }, {
20 | "code": "",
21 | "roleId": "2",
22 | "scope": "1",
23 | "roleName": "招商事业部",
24 | "description": "",
25 | "status": "1"
26 | }, {
27 | "code": "",
28 | "roleId": "3",
29 | "scope": "1",
30 | "roleName": "互联网部门",
31 | "description": "",
32 | "status": "1"
33 | }, {
34 | "code": "",
35 | "roleId": "4",
36 | "scope": "1",
37 | "roleName": "销售部",
38 | "description": "",
39 | "status": "1"
40 | }, {
41 | "code": "",
42 | "roleId": "5",
43 | "scope": "1",
44 | "roleName": "战区一",
45 | "description": "",
46 | "status": "1"
47 | }, {
48 | "code": "",
49 | "roleId": "6",
50 | "scope": "1",
51 | "roleName": "战区二",
52 | "description": "",
53 | "status": "1"
54 | }, {
55 | "code": "",
56 | "roleId": "7",
57 | "scope": "1",
58 | "roleName": "JAVA开发",
59 | "description": "",
60 | "status": "1"
61 | }, {
62 | "code": "",
63 | "roleId": "8",
64 | "scope": "1",
65 | "roleName": "测试审批角色",
66 | "description": "",
67 | "status": "1"
68 | }],
69 | "pageNum": "1",
70 | "navigatePages": "8",
71 | "navigateFirstPage": "0",
72 | "total": "8",
73 | "pages": "0",
74 | "size": "8",
75 | "isLastPage": "true",
76 | "hasPreviousPage": "false",
77 | "navigateLastPage": "0",
78 | "isFirstPage": "true"
79 | }
80 | }
--------------------------------------------------------------------------------
/example/frontend/src/css/dialog.css:
--------------------------------------------------------------------------------
1 | .person_body {
2 | border: 1px solid #f5f5f5;
3 | height: 500px;
4 | }
5 | .tree_nav span {
6 | display: inline-block;
7 | padding-right: 10px;
8 | margin-right: 5px;
9 | max-width: 6em;
10 | color: #38adff;
11 | font-size: 12px;
12 | cursor: pointer;
13 | background: url(~@/assets/images/jiaojiao.png) no-repeat right center;
14 | }
15 | .tree_nav span:last-of-type {
16 | background: none;
17 | }
18 | .person_tree {
19 | padding: 10px 12px 0 8px;
20 | width: 280px;
21 | height: 100%;
22 | border-right: 1px solid #f5f5f5;
23 | }
24 | .person_tree input {
25 | padding-left: 22px;
26 | width: 210px;
27 | height: 30px;
28 | font-size: 12px;
29 | border-radius: 2px;
30 | border: 1px solid #d5dadf;
31 | background: url(~@/assets/images/list_search.png) no-repeat 10px center;
32 | background-size: 14px 14px;
33 | margin-bottom: 14px;
34 | }
35 | .person_tree ul,
36 | .has_selected ul {
37 | height: 420px;
38 | overflow-y: auto;
39 | }
40 | .person_tree li {
41 | padding: 5px 0;
42 | }
43 | .person_tree li i {
44 | float: right;
45 | padding-left: 24px;
46 | padding-right: 10px;
47 | color: #3195f8;
48 | font-size: 12px;
49 | cursor: pointer;
50 | background: url(~@/assets/images/next_level_active.png) no-repeat 10px
51 | center;
52 | border-left: 1px solid rgb(238, 238, 238);
53 | }
54 | .person_tree li a.active + i {
55 | color: rgb(197, 197, 197);
56 | background-image: url(~@/assets/images/next_level.png);
57 | pointer-events: none;
58 | }
59 | .person_tree img {
60 | width: 14px;
61 | vertical-align: middle;
62 | margin-right: 5px;
63 | }
64 |
65 | .has_selected {
66 | width: 276px;
67 | height: 100%;
68 | font-size: 12px;
69 | }
70 | .has_selected ul {
71 | height: 460px;
72 | }
73 | .has_selected p {
74 | padding-left: 19px;
75 | padding-right: 20px;
76 | line-height: 37px;
77 | border-bottom: 1px solid #f2f2f2;
78 | }
79 | .has_selected p a {
80 | float: right;
81 | }
82 | .has_selected ul li {
83 | margin: 11px 26px 13px 19px;
84 | line-height: 17px;
85 | }
86 | .has_selected li span {
87 | vertical-align: middle;
88 | }
89 | .has_selected li img:first-of-type {
90 | width: 14px;
91 | vertical-align: middle;
92 | margin-right: 5px;
93 | }
94 | .has_selected li img:last-of-type {
95 | float: right;
96 | margin-top: 2px;
97 | width: 14px;
98 | }
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/processing/task/ServiceTaskProcessor.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.processing.task;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.task.ServiceTaskDefinition;
5 | import cn.lzgabel.camunda.converter.processing.BpmnElementProcessor;
6 | import java.lang.reflect.InvocationTargetException;
7 | import java.util.Objects;
8 | import java.util.Optional;
9 | import org.apache.commons.lang3.StringUtils;
10 | import org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder;
11 | import org.camunda.bpm.model.bpmn.builder.ServiceTaskBuilder;
12 |
13 | /**
14 | * 〈功能简述〉
15 | * 〈ServiceTask节点类型详情设置〉
16 | *
17 | * @author lizhi
18 | * @since 1.0.0
19 | */
20 | public class ServiceTaskProcessor
21 | implements BpmnElementProcessor {
22 |
23 | @Override
24 | public String onComplete(AbstractFlowNodeBuilder flowNodeBuilder, ServiceTaskDefinition flowNode)
25 | throws InvocationTargetException, IllegalAccessException {
26 | final ServiceTaskBuilder serviceTaskBuilder =
27 | (ServiceTaskBuilder) createInstance(flowNodeBuilder, flowNode);
28 |
29 | // set implementation
30 | Optional.ofNullable(flowNode.getTopic())
31 | .filter(StringUtils::isNotBlank)
32 | .ifPresent(serviceTaskBuilder::camundaExternalTask);
33 |
34 | Optional.ofNullable(flowNode.getExpression())
35 | .filter(StringUtils::isNotBlank)
36 | .map(
37 | expression -> {
38 | serviceTaskBuilder.camundaExpression(expression);
39 | return flowNode.getResultVariable();
40 | })
41 | .filter(StringUtils::isNotBlank)
42 | .ifPresent(serviceTaskBuilder::camundaResultVariable);
43 |
44 | Optional.ofNullable(flowNode.getDelegateExpression())
45 | .filter(StringUtils::isNotBlank)
46 | .ifPresent(serviceTaskBuilder::camundaDelegateExpression);
47 |
48 | Optional.ofNullable(flowNode.getJavaClass())
49 | .filter(StringUtils::isNotBlank)
50 | .ifPresent(serviceTaskBuilder::camundaClass);
51 |
52 | String id = serviceTaskBuilder.getElement().getId();
53 | // 如果当前任务还有后续任务,则遍历创建后续任务
54 | BaseDefinition nextNode = flowNode.getNextNode();
55 | if (Objects.nonNull(nextNode)) {
56 | return onCreate(moveToNode(flowNodeBuilder, id), nextNode);
57 | } else {
58 | return id;
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/BpmnBuilder.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.BpmnElementType;
5 | import cn.lzgabel.camunda.converter.bean.Process;
6 | import cn.lzgabel.camunda.converter.bean.ProcessDefinition;
7 | import cn.lzgabel.camunda.converter.processing.BpmnElementProcessor;
8 | import cn.lzgabel.camunda.converter.processing.BpmnElementProcessors;
9 | import java.util.Optional;
10 | import org.apache.commons.lang3.StringUtils;
11 | import org.camunda.bpm.model.bpmn.Bpmn;
12 | import org.camunda.bpm.model.bpmn.BpmnModelInstance;
13 | import org.camunda.bpm.model.bpmn.builder.AbstractBaseElementBuilder;
14 | import org.camunda.bpm.model.bpmn.builder.ProcessBuilder;
15 | import org.camunda.bpm.model.bpmn.builder.StartEventBuilder;
16 |
17 | /**
18 | * 〈功能简述〉
19 | * 〈基于 json 格式自动生成 bpmn 文件〉
20 | *
21 | * @author lizhi
22 | * @since 1.0.0
23 | */
24 | public class BpmnBuilder {
25 |
26 | public static BpmnModelInstance build(String json) {
27 | return build(ProcessDefinition.of(json));
28 | }
29 |
30 | public static BpmnModelInstance build(ProcessDefinition processDefinition) {
31 | if (processDefinition == null) {
32 | return null;
33 | }
34 |
35 | try {
36 | ProcessBuilder executableProcess = Bpmn.createExecutableProcess();
37 |
38 | Process process = processDefinition.getProcess();
39 | Optional.ofNullable(process.getName())
40 | .filter(StringUtils::isNotBlank)
41 | .ifPresent(executableProcess::name);
42 |
43 | Optional.ofNullable(process.getProcessId())
44 | .filter(StringUtils::isNotBlank)
45 | .ifPresent(executableProcess::id);
46 |
47 | StartEventBuilder startEventBuilder = executableProcess.startEvent();
48 | BaseDefinition processNode = processDefinition.getProcessNode();
49 | BpmnElementProcessor processor =
50 | BpmnElementProcessors.getProcessor(BpmnElementType.START_EVENT);
51 | String lastNode = processor.onCreate(startEventBuilder, processNode);
52 | processor.moveToNode(startEventBuilder, lastNode).endEvent();
53 | BpmnModelInstance modelInstance = startEventBuilder.done();
54 | Bpmn.validateModel(modelInstance);
55 |
56 | return modelInstance;
57 | } catch (Exception e) {
58 | e.printStackTrace();
59 | throw new RuntimeException("创建失败: e=" + e.getMessage());
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/processing/task/BusinessRuleTaskProcessor.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.processing.task;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.DecisionRefBindingType;
5 | import cn.lzgabel.camunda.converter.bean.task.BusinessRuleTaskDefinition;
6 | import cn.lzgabel.camunda.converter.processing.BpmnElementProcessor;
7 | import java.lang.reflect.InvocationTargetException;
8 | import java.util.Objects;
9 | import org.apache.commons.lang3.StringUtils;
10 | import org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder;
11 | import org.camunda.bpm.model.bpmn.builder.BusinessRuleTaskBuilder;
12 |
13 | /**
14 | * 〈功能简述〉
15 | * 〈BusinessRuleTask节点类型详情设置〉
16 | *
17 | * @author lizhi
18 | * @since 1.0.0
19 | */
20 | public class BusinessRuleTaskProcessor
21 | implements BpmnElementProcessor {
22 |
23 | @Override
24 | public String onComplete(
25 | AbstractFlowNodeBuilder flowNodeBuilder, BusinessRuleTaskDefinition flowNode)
26 | throws InvocationTargetException, IllegalAccessException {
27 | String decisionRef = flowNode.getDecisionRef();
28 | String decisionRefBinding = flowNode.getDecisionRefBinding();
29 | String decisionRefTenantId = flowNode.getDecisionRefTenantId();
30 | String resultVariable = flowNode.getResultVariable();
31 |
32 | // 创建 businessRuleTask
33 | final BusinessRuleTaskBuilder businessRuleTaskBuilder =
34 | (BusinessRuleTaskBuilder) createInstance(flowNodeBuilder, flowNode);
35 | businessRuleTaskBuilder.camundaDecisionRef(decisionRef).camundaResultVariable(resultVariable);
36 |
37 | if (DecisionRefBindingType.VERSION.isEquals(decisionRefBinding)) {
38 | businessRuleTaskBuilder.camundaDecisionRefVersion(flowNode.getDecisionRefVersion());
39 | }
40 |
41 | if (DecisionRefBindingType.VERSION_TAG.isEquals(decisionRefBinding)) {
42 | businessRuleTaskBuilder.camundaDecisionRefVersionTag(flowNode.getDecisionRefVersionTag());
43 | }
44 |
45 | if (StringUtils.isNotBlank(decisionRefTenantId)) {
46 | businessRuleTaskBuilder.camundaDecisionRefTenantId(decisionRefTenantId);
47 | }
48 |
49 | String id = businessRuleTaskBuilder.getElement().getId();
50 |
51 | // 如果当前任务还有后续任务,则遍历创建后续任务
52 | BaseDefinition nextNode = flowNode.getNextNode();
53 | if (Objects.nonNull(nextNode)) {
54 | return onCreate(moveToNode(flowNodeBuilder, id), nextNode);
55 | } else {
56 | return id;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/processing/gateway/ParallelGatewayProcessor.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.processing.gateway;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.gateway.BranchNode;
5 | import cn.lzgabel.camunda.converter.bean.gateway.ParallelGatewayDefinition;
6 | import com.google.common.collect.Lists;
7 | import java.lang.reflect.InvocationTargetException;
8 | import java.util.Collections;
9 | import java.util.List;
10 | import java.util.Objects;
11 | import org.apache.commons.collections.CollectionUtils;
12 | import org.apache.commons.lang3.StringUtils;
13 | import org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder;
14 | import org.camunda.bpm.model.bpmn.builder.ParallelGatewayBuilder;
15 |
16 | /**
17 | * 〈功能简述〉
18 | * 〈ParallelGateway节点类型详情设置〉
19 | *
20 | * @author lizhi
21 | * @since 1.0.0
22 | */
23 | public class ParallelGatewayProcessor
24 | extends AbstractGatewayProcessor {
25 |
26 | @Override
27 | public String onComplete(
28 | AbstractFlowNodeBuilder flowNodeBuilder, ParallelGatewayDefinition flowNode)
29 | throws InvocationTargetException, IllegalAccessException {
30 | final ParallelGatewayBuilder parallelGatewayBuilder =
31 | (ParallelGatewayBuilder) createInstance(flowNodeBuilder, flowNode);
32 |
33 | List branchNodes = flowNode.getBranchNodes();
34 | if (CollectionUtils.isEmpty(branchNodes) && Objects.isNull(flowNode.getNextNode())) {
35 | return parallelGatewayBuilder.getElement().getId();
36 | }
37 |
38 | List incoming = Lists.newArrayListWithCapacity(branchNodes.size());
39 | for (BranchNode branchNode : branchNodes) {
40 | BaseDefinition childNode = branchNode.getNextNode();
41 | if (Objects.isNull(childNode)) {
42 | incoming.add(parallelGatewayBuilder.getElement().getId());
43 | continue;
44 | }
45 | String id =
46 | onCreate(
47 | moveToNode(parallelGatewayBuilder, parallelGatewayBuilder.getElement().getId()),
48 | childNode);
49 | if (StringUtils.isNotBlank(id)) {
50 | incoming.add(id);
51 | }
52 | }
53 |
54 | String id = parallelGatewayBuilder.getElement().getId();
55 | BaseDefinition nextNode = flowNode.getNextNode();
56 | if (Objects.nonNull(nextNode)) {
57 | nextNode.setIncoming(incoming);
58 | return merge(parallelGatewayBuilder, id, Collections.emptyList(), nextNode);
59 | }
60 | return id;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/example/frontend/src/css/override-element-ui.css:
--------------------------------------------------------------------------------
1 | .el-drawer__header button.el-drawer__close-btn {
2 | display: none;
3 | }
4 |
5 | .el-drawer__header {
6 | margin-bottom: 0;
7 | padding: 14px 0 14px 20px;
8 | /* border-bottom: 1px solid #f2f2f2; */
9 | color: #323232;
10 | font-size: 16px;
11 | }
12 |
13 | .demo-drawer__content {
14 | display: flex;
15 | flex-direction: column;
16 | height: 100%;
17 | }
18 |
19 | .drawer_content {
20 | flex: 1;
21 | }
22 |
23 | .demo-drawer__content>div {
24 | border-top: 1px solid #F2F2F2;
25 | }
26 |
27 | .el-button {
28 | min-width: 79px;
29 | padding: 8px 12px;
30 | font-size: 12px;
31 | border-radius: 2px;
32 | background: #46A6FE;
33 | }
34 |
35 | .el-button.el-button--default {
36 | color: #323232;
37 | background: #f2f2f2;
38 | }
39 |
40 | .demo-drawer__footer {
41 | padding: 10px 30px;
42 | border-top: 1px solid #F2F2F2;
43 | }
44 |
45 | .demo-drawer__footer .el-button {
46 | float: right;
47 | margin-right: 10px;
48 | }
49 |
50 | .el-dialog {
51 | width: 520px;
52 | border: 1px solid #DDE1E5;
53 | border-radius: 3px;
54 | }
55 |
56 | .el-dialog__header {
57 | padding: 0 0 0 20px;
58 | line-height: 50px;
59 | height: 50px;
60 | background: #fff;
61 | border-bottom: 1px solid #F2F2F2;
62 | }
63 |
64 | .el-dialog__header .el-dialog__title {
65 | font-size: 16px;
66 | line-height: 50px;
67 | color: #333333;
68 | }
69 |
70 | .el-dialog__header .el-dialog__headerbtn {
71 | height: 12px;
72 | width: 12px;
73 | }
74 |
75 | .el-dialog__header .el-icon-close {
76 | width: 12px;
77 | height: 12px;
78 | float: left;
79 | }
80 |
81 | .el-dialog__header .el-icon-close::before {
82 | display: block;
83 | width: 12px;
84 | height: 12px;
85 | background: url(~@/assets/images/add-close.png) no-repeat center;
86 | background-size: 100% 100%;
87 | content: "";
88 | }
89 |
90 | .el-dialog__footer {
91 | border-top: 1px solid #F2F2F2;
92 | padding-bottom: 10px;
93 | }
94 |
95 | .el-checkbox,
96 | .el-checkbox__input.is-checked+.el-checkbox__label,
97 | .el-radio,
98 | .el-radio__input.is-checked+.el-radio__label,
99 | .el-dialog__body,
100 | .el-tree {
101 | color: #333;
102 | }
103 |
104 | .el-radio__label, .el-checkbox__label {
105 | font-size: 12px;
106 | }
107 | .add-node-popover-body {
108 | display: flex;
109 | }
110 | .my-el-custom-spinner {
111 | display: inline-block;
112 | width: 80px;
113 | height: 80px;
114 | background: url(~@/assets/images/loading.gif) no-repeat center;
115 | }
--------------------------------------------------------------------------------
/example/frontend/src/components/drawer/promoterDrawer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
{{$func.arrToStr(flowPermission)?$func.arrToStr(flowPermission):'所有人'}}
6 |
添加/修改发起人
7 |
8 |
12 |
18 |
19 |
20 |
21 |
65 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/BpmnElementType.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean;
2 |
3 | import java.util.Arrays;
4 | import java.util.Optional;
5 | import org.camunda.bpm.model.bpmn.instance.*;
6 |
7 | /**
8 | * 〈功能简述〉
9 | * 〈〉
10 | *
11 | * @author lizhi
12 | * @since 1.0.0
13 | */
14 | public enum BpmnElementType {
15 | // Default
16 | UNSPECIFIED(null, null),
17 |
18 | // Containers
19 | SUB_PROCESS("subProcess", SubProcess.class),
20 | EVENT_SUB_PROCESS(null, null),
21 |
22 | // Events
23 | START_EVENT("startEvent", StartEvent.class),
24 | INTERMEDIATE_CATCH_EVENT("intermediateCatchEvent", IntermediateCatchEvent.class),
25 | INTERMEDIATE_THROW_EVENT("intermediateThrowEvent", IntermediateThrowEvent.class),
26 | BOUNDARY_EVENT("boundaryEvent", BoundaryEvent.class),
27 | END_EVENT("endEvent", EndEvent.class),
28 |
29 | // Tasks
30 | SERVICE_TASK("serviceTask", ServiceTask.class),
31 | RECEIVE_TASK("receiveTask", ReceiveTask.class),
32 | USER_TASK("userTask", UserTask.class),
33 | MANUAL_TASK("manualTask", ManualTask.class),
34 |
35 | // Gateways
36 | EXCLUSIVE_GATEWAY("exclusiveGateway", ExclusiveGateway.class),
37 | INCLUSIVE_GATEWAY("inclusiveGateway", InclusiveGateway.class),
38 | PARALLEL_GATEWAY("parallelGateway", ParallelGateway.class),
39 | EVENT_BASED_GATEWAY("eventBasedGateway", EventBasedGateway.class),
40 |
41 | // Other
42 | MULTI_INSTANCE_BODY(null, null),
43 | CALL_ACTIVITY("callActivity", CallActivity.class),
44 | BUSINESS_RULE_TASK("businessRuleTask", BusinessRuleTask.class),
45 | SCRIPT_TASK("scriptTask", ScriptTask.class),
46 | SEND_TASK("sendTask", SendTask.class);
47 |
48 | private final String elementTypeName;
49 | private final Class extends FlowNode> elementTypeClass;
50 |
51 | private BpmnElementType(
52 | final String elementTypeName, final Class extends FlowNode> elementTypeClass) {
53 | this.elementTypeName = elementTypeName;
54 | this.elementTypeClass = elementTypeClass;
55 | }
56 |
57 | public Optional getElementTypeName() {
58 | return Optional.ofNullable(this.elementTypeName);
59 | }
60 |
61 | public Optional> getElementTypeClass() {
62 | return Optional.ofNullable(this.elementTypeClass);
63 | }
64 |
65 | public static BpmnElementType bpmnElementTypeFor(final String elementTypeName) {
66 | return Arrays.stream(values())
67 | .filter(
68 | bpmnElementType ->
69 | bpmnElementType.elementTypeName != null
70 | && bpmnElementType.elementTypeName.equals(elementTypeName))
71 | .findFirst()
72 | .orElseThrow(
73 | () -> new RuntimeException("Unsupported BPMN element of type " + elementTypeName));
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/example/frontend/src/components/dialog/roleDialog.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 |
已选({{total}})
15 | 清空
16 |
17 |
18 |
19 |
20 | {{item.roleName}}
21 |
22 |
23 |
24 |
25 |
26 |
30 |
31 |
32 |
33 |
79 |
80 |
83 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/processing/event/StartEventProcessor.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.processing.event;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.BpmnElementType;
5 | import cn.lzgabel.camunda.converter.bean.event.start.*;
6 | import cn.lzgabel.camunda.converter.processing.BpmnElementProcessor;
7 | import cn.lzgabel.camunda.converter.processing.BpmnElementProcessors;
8 | import java.lang.reflect.InvocationTargetException;
9 | import java.util.Objects;
10 | import org.apache.commons.lang3.StringUtils;
11 | import org.camunda.bpm.model.bpmn.builder.StartEventBuilder;
12 |
13 | /**
14 | * 〈功能简述〉
15 | * 〈StartEvent节点类型详情设置〉
16 | *
17 | * @author lizhi
18 | * @since 1.0.0
19 | */
20 | public class StartEventProcessor
21 | implements BpmnElementProcessor {
22 |
23 | @Override
24 | public String onComplete(StartEventBuilder start, StartEventDefinition flowNode)
25 | throws InvocationTargetException, IllegalAccessException {
26 | // 事件类型 timer/message 默认:none
27 | String eventType = flowNode.getEventType();
28 | String nodeName = flowNode.getNodeName();
29 | start.name(nodeName);
30 | if (StringUtils.isNotBlank(eventType)) {
31 | if (EventType.TIMER.isEqual(eventType)) {
32 | TimerStartEventDefinition timer = (TimerStartEventDefinition) flowNode;
33 | // timer 定义类型: date/cycle/duration
34 | String timerDefinitionType = timer.getTimerDefinitionType();
35 | if (TimerDefinitionType.DATE.isEqual(timerDefinitionType)) {
36 | String timerDefinition = timer.getTimerDefinition();
37 | start.timerWithDate(timerDefinition);
38 | } else if (TimerDefinitionType.DURATION.isEqual(timerDefinitionType)) {
39 | String timerDefinition = timer.getTimerDefinition();
40 | start.timerWithDuration(timerDefinition);
41 | } else if (TimerDefinitionType.CYCLE.isEqual(timerDefinitionType)) {
42 | String timerDefinition = timer.getTimerDefinition();
43 | start.timerWithCycle(timerDefinition);
44 | }
45 | } else if (EventType.MESSAGE.isEqual(eventType)) {
46 | MessageStartEventDefinition message = (MessageStartEventDefinition) flowNode;
47 | String messageName = message.getMessageName();
48 | start.message(messageName);
49 | }
50 | }
51 |
52 | // create execution listener
53 | createExecutionListener(start, flowNode);
54 |
55 | String id = start.getElement().getId();
56 | BaseDefinition nextNode = flowNode.getNextNode();
57 | if (Objects.isNull(nextNode)) {
58 | return id;
59 | }
60 |
61 | BpmnElementType elementType = BpmnElementType.bpmnElementTypeFor(nextNode.getNodeType());
62 | return BpmnElementProcessors.getProcessor(elementType)
63 | .onComplete(moveToNode(start, id), nextNode);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/example/frontend/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 |
6 | cn.lzgabel
7 | camunda-bpmn-converter-parent
8 | 1.0-SNAPSHOT
9 | ../../pom.xml
10 |
11 |
12 | workflow-frontend
13 | 1.0-SNAPSHOT
14 | jar
15 |
16 |
17 |
18 |
19 | com.github.eirslett
20 | frontend-maven-plugin
21 | 1.12.1
22 |
23 | target
24 |
25 |
26 |
27 | install node and npm
28 |
29 | install-node-and-npm
30 |
31 |
32 | ${version.node}
33 | ${version.npm}
34 | https://npm.taobao.org/mirrors/node/
35 | https://registry.npm.taobao.org/npm/-/
36 |
37 |
38 |
39 |
40 | npm install
41 |
42 | npm
43 |
44 | generate-resources
45 |
46 | install
47 |
48 |
49 |
50 | npm build
51 |
52 | npm
53 |
54 | generate-resources
55 |
56 | run build
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | dist
66 | ${project.build.outputDirectory}/META-INF/resources
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/example/frontend/src/plugins/axios.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import Vue from 'vue';
4 | import axios from "axios";
5 | import router from '../router'
6 | import func from "./preload"
7 | import {
8 | Message,
9 | Loading
10 | } from 'element-ui'; //引入elm组件
11 | let needLoadingRequestCount = 0,
12 | loading;
13 |
14 |
15 | function showFullScreenLoading() {
16 | if (needLoadingRequestCount === 0) {
17 | startLoading()
18 | }
19 | needLoadingRequestCount++
20 | }
21 |
22 | function tryHideFullScreenLoading() {
23 | if (needLoadingRequestCount <= 0) return
24 | needLoadingRequestCount--
25 | if (needLoadingRequestCount === 0) {
26 | setTimeout(function() {
27 | endLoading()
28 | }, 200)
29 | }
30 | }
31 |
32 | function startLoading() {
33 | loading = Loading.service({
34 | lock: true,
35 | background: 'rgba(0, 0, 0, 0.2)',
36 | spinner: 'my-el-custom-spinner'
37 | })
38 | }
39 |
40 | function endLoading() {
41 | loading.close()
42 | }
43 |
44 | let config = {
45 | // baseURL: process.env.baseURL || process.env.apiUrl || ""
46 | timeout: 10 * 1000, // Timeout
47 | // withCredentials: true, // Check cross-site Access-Control
48 | };
49 | const _axios = axios.create(config);
50 |
51 | // 添加请求拦截器
52 | _axios.interceptors.request.use(config => { // 在发送请求之前做些什么
53 | if (config.loading !== false) {
54 | showFullScreenLoading()
55 | }
56 | return config;
57 | }, err => { // 对请求错误做些什么
58 | return Promise.reject(err);
59 | });
60 |
61 | // 添加响应拦截器
62 | _axios.interceptors.response.use(res => { // 对响应数据做点什么
63 | tryHideFullScreenLoading()
64 | if (res.data.code && res.data.code != 200) {
65 | if (res.data.msg == "登录用户ID不能为空") {
66 | func.clearCookie(["token", "uid"])
67 | window.location.href = "/login"
68 | }
69 | Message.error(res.data.msg);
70 | return;
71 | }
72 | return res.data;
73 | }, err => { // 对响应错误做点什么
74 | tryHideFullScreenLoading()
75 | if (err.message.includes('timeout') || err.response.status == 502) {
76 | router.push({
77 | path: '/500',
78 | query: {
79 | redirect: router.currentRoute.query.redirect ? router.currentRoute.query.redirect : router.currentRoute.fullPath
80 | }
81 | })
82 | }
83 | if (err.response.data.code && err.response.data.code != 200) {
84 | Message.error(err.response.data.msg);
85 | }
86 | return Promise.reject(err);
87 | });
88 |
89 | Plugin.install = function(Vue, options) {
90 | Vue.axios = _axios;
91 | window.axios = _axios;
92 | Object.defineProperties(Vue.prototype, {
93 | axios: {
94 | get() {
95 | return _axios;
96 | }
97 | },
98 | $axios: {
99 | get() {
100 | return _axios;
101 | }
102 | },
103 | });
104 | };
105 |
106 | Vue.use(Plugin)
107 |
108 | export default Plugin;
109 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/processing/BpmnElementProcessors.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.processing;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.BpmnElementType;
5 | import cn.lzgabel.camunda.converter.processing.container.CallActivityProcessor;
6 | import cn.lzgabel.camunda.converter.processing.container.SubProcessProcessor;
7 | import cn.lzgabel.camunda.converter.processing.event.EndEventProcessor;
8 | import cn.lzgabel.camunda.converter.processing.event.IntermediateCatchEventProcessor;
9 | import cn.lzgabel.camunda.converter.processing.event.StartEventProcessor;
10 | import cn.lzgabel.camunda.converter.processing.gateway.ExclusiveGatewayProcessor;
11 | import cn.lzgabel.camunda.converter.processing.gateway.InclusiveGatewayProcessor;
12 | import cn.lzgabel.camunda.converter.processing.gateway.ParallelGatewayProcessor;
13 | import cn.lzgabel.camunda.converter.processing.task.*;
14 | import java.util.EnumMap;
15 | import java.util.Map;
16 | import org.camunda.bpm.model.bpmn.builder.AbstractBaseElementBuilder;
17 |
18 | public final class BpmnElementProcessors {
19 |
20 | private static final Map> processors =
21 | new EnumMap<>(BpmnElementType.class);
22 |
23 | static {
24 | // tasks
25 | processors.put(BpmnElementType.BUSINESS_RULE_TASK, new BusinessRuleTaskProcessor());
26 | processors.put(BpmnElementType.SCRIPT_TASK, new ScriptTaskProcessor());
27 | processors.put(BpmnElementType.SERVICE_TASK, new ServiceTaskProcessor());
28 | processors.put(BpmnElementType.USER_TASK, new UserTaskProcessor());
29 | processors.put(BpmnElementType.RECEIVE_TASK, new ReceiveTaskProcessor());
30 | processors.put(BpmnElementType.MANUAL_TASK, new ManualTaskProcessor());
31 |
32 | // gateways
33 | processors.put(BpmnElementType.EXCLUSIVE_GATEWAY, new ExclusiveGatewayProcessor());
34 | processors.put(BpmnElementType.INCLUSIVE_GATEWAY, new InclusiveGatewayProcessor());
35 | processors.put(BpmnElementType.PARALLEL_GATEWAY, new ParallelGatewayProcessor());
36 |
37 | // containers
38 | processors.put(BpmnElementType.SUB_PROCESS, new SubProcessProcessor());
39 | processors.put(BpmnElementType.CALL_ACTIVITY, new CallActivityProcessor());
40 |
41 | // events
42 | processors.put(BpmnElementType.START_EVENT, new StartEventProcessor());
43 | processors.put(BpmnElementType.END_EVENT, new EndEventProcessor());
44 | processors.put(BpmnElementType.INTERMEDIATE_CATCH_EVENT, new IntermediateCatchEventProcessor());
45 | }
46 |
47 | public static
48 | BpmnElementProcessor getProcessor(final BpmnElementType bpmnElementType) {
49 |
50 | final BpmnElementProcessor processor = processors.get(bpmnElementType);
51 | if (processor == null) {
52 | throw new UnsupportedOperationException(
53 | String.format(
54 | "Expected to find a BPMN element processor for the BPMN element type '%s' but not found.",
55 | bpmnElementType));
56 | }
57 | return processor;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/example/frontend/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
191 |
--------------------------------------------------------------------------------
/example/frontend/src/components/drawer/copyerDrawer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
添加成员
6 |
7 | {{item.name}}
8 |
9 |
10 | 清除
11 |
12 |
13 | 允许发起人自选抄送人
14 |
15 |
16 |
20 |
25 |
26 |
27 |
28 |
78 |
79 |
92 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/processing/task/UserTaskProcessor.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.processing.task;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.listener.TaskListener;
5 | import cn.lzgabel.camunda.converter.bean.task.UserTaskDefinition;
6 | import cn.lzgabel.camunda.converter.processing.BpmnElementProcessor;
7 | import com.google.common.collect.Lists;
8 | import java.lang.reflect.InvocationTargetException;
9 | import java.util.List;
10 | import java.util.Objects;
11 | import org.apache.commons.collections.CollectionUtils;
12 | import org.apache.commons.lang3.StringUtils;
13 | import org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder;
14 | import org.camunda.bpm.model.bpmn.builder.UserTaskBuilder;
15 |
16 | /**
17 | * 〈功能简述〉
18 | * 〈UserTask节点类型详情设置〉
19 | *
20 | * @author lizhi
21 | * @since 1.0.0
22 | */
23 | public class UserTaskProcessor
24 | implements BpmnElementProcessor {
25 |
26 | private static final List EVENT_TYPES =
27 | Lists.newArrayList("create", "assignment", "complete", "delete", "update");
28 |
29 | @Override
30 | public String onComplete(AbstractFlowNodeBuilder flowNodeBuilder, UserTaskDefinition flowNode)
31 | throws InvocationTargetException, IllegalAccessException {
32 | String assignee = flowNode.getAssignee();
33 | String candidateUsers = flowNode.getCandidateUsers();
34 | String candidateGroups = flowNode.getCandidateGroups();
35 |
36 | final UserTaskBuilder userTaskBuilder =
37 | (UserTaskBuilder) createInstance(flowNodeBuilder, flowNode);
38 |
39 | if (StringUtils.isNotBlank(assignee)) {
40 | userTaskBuilder.camundaAssignee(assignee);
41 | }
42 |
43 | if (StringUtils.isNotBlank(candidateUsers)) {
44 | userTaskBuilder.camundaCandidateUsers(candidateUsers);
45 | }
46 |
47 | if (StringUtils.isNotBlank(candidateGroups)) {
48 | userTaskBuilder.camundaCandidateGroups(candidateGroups);
49 | }
50 |
51 | // create task listener
52 | createTaskListener(userTaskBuilder, flowNode);
53 |
54 | String id = userTaskBuilder.getElement().getId();
55 | // 如果当前任务还有后续任务,则遍历创建后续任务
56 | BaseDefinition nextNode = flowNode.getNextNode();
57 | if (Objects.nonNull(nextNode)) {
58 | return onCreate(moveToNode(flowNodeBuilder, id), nextNode);
59 | } else {
60 | return id;
61 | }
62 | }
63 |
64 | /**
65 | * 创建任务监听器
66 | *
67 | * @param userTaskBuilder builder
68 | * @param flowNode 当前节点
69 | */
70 | private void createTaskListener(UserTaskBuilder userTaskBuilder, UserTaskDefinition flowNode) {
71 | final List listeners = flowNode.getTaskListeners();
72 | if (CollectionUtils.isNotEmpty(listeners)) {
73 | listeners.forEach(
74 | listener -> {
75 | final String eventType = listener.getEventType();
76 | if (!EVENT_TYPES.contains(eventType)) {
77 | throw new IllegalArgumentException("Unsupported event of type " + eventType);
78 | }
79 |
80 | if (listener.isClass()) {
81 | userTaskBuilder.camundaTaskListenerClass(eventType, listener.getJavaClass());
82 | } else if (listener.isDelegateExpression()) {
83 | userTaskBuilder.camundaTaskListenerDelegateExpression(
84 | eventType, listener.getDelegateExpression());
85 | } else if (listener.isExpression()) {
86 | userTaskBuilder.camundaTaskListenerExpression(eventType, listener.getExpression());
87 | }
88 | });
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/processing/gateway/AbstractGatewayProcessor.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.processing.gateway;
2 |
3 | /**
4 | * 〈功能简述〉
5 | * 〈〉
6 | *
7 | * @author lizhi
8 | * @since 1.0.0
9 | */
10 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
11 | import cn.lzgabel.camunda.converter.bean.gateway.BranchNode;
12 | import cn.lzgabel.camunda.converter.processing.BpmnElementProcessor;
13 | import java.lang.reflect.InvocationTargetException;
14 | import java.util.List;
15 | import java.util.Objects;
16 | import org.apache.commons.lang3.StringUtils;
17 | import org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder;
18 | import org.camunda.bpm.model.bpmn.instance.ConditionExpression;
19 | import org.camunda.bpm.model.bpmn.instance.SequenceFlow;
20 | import org.camunda.bpm.model.xml.instance.ModelElementInstance;
21 |
22 | public abstract class AbstractGatewayProcessor<
23 | E extends BaseDefinition, T extends AbstractFlowNodeBuilder>
24 | implements BpmnElementProcessor {
25 |
26 | String merge(
27 | AbstractFlowNodeBuilder flowNodeBuilder,
28 | String fromId,
29 | List conditions,
30 | BaseDefinition flowNode)
31 | throws InvocationTargetException, IllegalAccessException {
32 | List incoming = flowNode.getIncoming();
33 |
34 | // ------------------------
35 | //
36 | // ---> serviceTask(current) --> serviceTask --
37 | // gateway --> --> gateway(merge) -->
38 | // serviceTask(nextNode)
39 | // --> serviceTask --> serviceTask --
40 | //
41 | //
42 |
43 | // 继续沿新分支,创建新的元素类型(参见 current 所在分支))
44 | if (incoming == null || incoming.size() == 0) {
45 | return onCreate(moveToNode(flowNodeBuilder, fromId), flowNode);
46 | } else {
47 | // 聚合节点所有入度边连接
48 | flowNode.setIncoming(incoming);
49 |
50 | // 1.0 先对聚合节点进行所有的边连接(参见 merge), 临时暂存聚合节点 nextNode 后续节点(参见 nextNode)
51 | BaseDefinition tempNextNode = flowNode.getNextNode();
52 | flowNode.setNextNode(null);
53 |
54 | // 创建聚合节点(参见 merge)
55 | String mergeId = onCreate(moveToNode(flowNodeBuilder, incoming.get(0)), flowNode);
56 | for (int i = 1; i < incoming.size(); i++) {
57 | moveToNode(flowNodeBuilder, incoming.get(i)).connectTo(mergeId);
58 | }
59 |
60 | accept(flowNodeBuilder, mergeId, conditions);
61 |
62 | // 1.1 边连接完成后,进行聚合节点 nextNode 后续节点创建
63 | if (Objects.nonNull(tempNextNode)) {
64 | return onCreate(moveToNode(flowNodeBuilder, mergeId), tempNextNode);
65 | } else {
66 | return mergeId;
67 | }
68 | }
69 | }
70 |
71 | /**
72 | * 排他网关: 针对分支条件中空分支场景 添加条件表达式
73 | *
74 | * @param flowNodeBuilder
75 | * @param mergeId
76 | * @param conditions
77 | */
78 | protected void accept(
79 | AbstractFlowNodeBuilder flowNodeBuilder, String mergeId, List conditions) {}
80 |
81 | protected void createConditionExpression(
82 | SequenceFlow sequenceFlow, AbstractFlowNodeBuilder flowNodeBuilder, BranchNode condition) {
83 | String nodeName = condition.getNodeName();
84 | String expression = condition.getConditionExpression();
85 | if (StringUtils.isBlank(sequenceFlow.getName()) && StringUtils.isNotBlank(nodeName)) {
86 | sequenceFlow.setName(nodeName);
87 | }
88 | // 设置条件表达式
89 | if (Objects.isNull(sequenceFlow.getConditionExpression())
90 | && StringUtils.isNotBlank(expression)) {
91 | ConditionExpression conditionExpression =
92 | createInstance(flowNodeBuilder, ConditionExpression.class);
93 | conditionExpression.setTextContent(expression);
94 | sequenceFlow.setConditionExpression(conditionExpression);
95 | }
96 | }
97 |
98 | private T createInstance(
99 | AbstractFlowNodeBuilder, ?> abstractFlowNodeBuilder, Class clazz) {
100 | return abstractFlowNodeBuilder.getElement().getModelInstance().newInstance(clazz);
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/bean/BaseDefinition.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.bean;
2 |
3 | import cn.lzgabel.camunda.converter.bean.event.intermediate.IntermediateCatchEventDefinition;
4 | import cn.lzgabel.camunda.converter.bean.event.start.EndEventDefinition;
5 | import cn.lzgabel.camunda.converter.bean.event.start.StartEventDefinition;
6 | import cn.lzgabel.camunda.converter.bean.gateway.ExclusiveGatewayDefinition;
7 | import cn.lzgabel.camunda.converter.bean.gateway.InclusiveGatewayDefinition;
8 | import cn.lzgabel.camunda.converter.bean.gateway.ParallelGatewayDefinition;
9 | import cn.lzgabel.camunda.converter.bean.listener.ExecutionListener;
10 | import cn.lzgabel.camunda.converter.bean.subprocess.CallActivityDefinition;
11 | import cn.lzgabel.camunda.converter.bean.subprocess.SubProcessDefinition;
12 | import cn.lzgabel.camunda.converter.bean.task.*;
13 | import com.fasterxml.jackson.annotation.JsonSubTypes;
14 | import com.fasterxml.jackson.annotation.JsonTypeInfo;
15 | import com.google.common.collect.Lists;
16 | import java.io.Serializable;
17 | import java.util.List;
18 | import java.util.function.Supplier;
19 | import lombok.Data;
20 | import lombok.NoArgsConstructor;
21 | import lombok.experimental.SuperBuilder;
22 |
23 | /**
24 | * 〈功能简述〉
25 | * 〈基础元素定义〉
26 | *
27 | * @author lizhi
28 | * @since 1.0.0
29 | */
30 | @Data
31 | @SuperBuilder
32 | @NoArgsConstructor
33 | @JsonTypeInfo(
34 | use = JsonTypeInfo.Id.NAME,
35 | include = JsonTypeInfo.As.EXISTING_PROPERTY,
36 | property = "nodeType",
37 | visible = true)
38 | @JsonSubTypes({
39 | // event
40 | @JsonSubTypes.Type(value = StartEventDefinition.class, name = "startEvent"),
41 | @JsonSubTypes.Type(value = EndEventDefinition.class, name = "endEvent"),
42 |
43 | // task
44 | @JsonSubTypes.Type(value = UserTaskDefinition.class, name = "userTask"),
45 | @JsonSubTypes.Type(value = ServiceTaskDefinition.class, name = "serviceTask"),
46 | @JsonSubTypes.Type(value = ScriptTaskDefinition.class, name = "scriptTask"),
47 | @JsonSubTypes.Type(value = ReceiveTaskDefinition.class, name = "receiveTask"),
48 | @JsonSubTypes.Type(value = ManualTaskDefinition.class, name = "manualTask"),
49 | @JsonSubTypes.Type(value = BusinessRuleTaskDefinition.class, name = "businessRuleTask"),
50 |
51 | // sub process
52 | @JsonSubTypes.Type(value = CallActivityDefinition.class, name = "callActivity"),
53 | @JsonSubTypes.Type(value = SubProcessDefinition.class, name = "subProcess"),
54 |
55 | // gateway
56 | @JsonSubTypes.Type(value = ParallelGatewayDefinition.class, name = "parallelGateway"),
57 | @JsonSubTypes.Type(value = ExclusiveGatewayDefinition.class, name = "exclusiveGateway"),
58 | @JsonSubTypes.Type(value = InclusiveGatewayDefinition.class, name = "inclusiveGateway"),
59 |
60 | // catch event
61 | @JsonSubTypes.Type(
62 | value = IntermediateCatchEventDefinition.class,
63 | name = "intermediateCatchEvent")
64 | })
65 | public abstract class BaseDefinition implements Serializable {
66 |
67 | /** 节点名称 */
68 | private String nodeName;
69 |
70 | /** 节点类型 */
71 | private String nodeType;
72 |
73 | /** 入度节点 */
74 | private List incoming;
75 |
76 | /** 后继节点 */
77 | private BaseDefinition nextNode;
78 |
79 | /** 执行监听器 */
80 | private List listeners = Lists.newArrayList();
81 |
82 | public abstract String getNodeType();
83 |
84 | public abstract static class BaseDefinitionBuilder<
85 | C extends BaseDefinition, B extends BaseDefinition.BaseDefinitionBuilder> {
86 |
87 | public BaseDefinitionBuilder() {
88 | this.listeners = Lists.newArrayList();
89 | }
90 |
91 | public B nodeNode(String nodeName) {
92 | this.nodeName = nodeName;
93 | return self();
94 | }
95 |
96 | public B nextNode(BaseDefinition nextNode) {
97 | this.nextNode = nextNode;
98 | return self();
99 | }
100 |
101 | public B listener(ExecutionListener listener) {
102 | this.listeners.add(listener);
103 | return self();
104 | }
105 |
106 | public B listener(Supplier supplier) {
107 | this.listeners.add(supplier.get());
108 | return self();
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Camunda BPMN Converter
5 |
6 | Convert json to bpmn for Camunda Platform.
7 |
8 |
9 |
10 |
11 | ### 1. 节点类型(nodeType)
12 | - serviceTask
13 | - service 任务节点
14 | - userTask
15 | - userTask 任务节点
16 | - parallelGateway
17 | - 并行节点
18 | - exclusiveGateway
19 | - 排他节点
20 |
21 | ### 2. 数据结构
22 |
23 | - serviceTask
24 | ```json
25 | {
26 | "nodeName":"审核人1",
27 | "nodeType":"serviceTask",
28 | "nextNode": null
29 | }
30 | ```
31 |
32 | - userTask
33 | ```json
34 | {
35 | "nodeName":"审核人1",
36 | "nodeType":"userTask",
37 | "assignee": "aaa",
38 | "candidateUsers": "bbb,ccc",
39 | "candidateGroups": "bbb,ddd",
40 | "nextNode": null
41 | }
42 | ```
43 |
44 | > 对于 parallelGateway/exclusiveGateway 类型,目前建议设置 nextNode 的 nodeType 类型一一对应
45 |
46 | - exclusive
47 | ```json
48 | {
49 | "nodeName":"排他",
50 | "nodeType":"exclusiveGateway",
51 | "nextNode":{
52 | "nodeName":"",
53 | "nodeType":"exclusiveGateway",
54 | "nextNode":null
55 | },
56 | "branchNodes":[
57 | {
58 | "nodeName":"条件1",
59 | "conditionExpression":"=id>1",
60 | "nextNode":{
61 | "nodeName":"审核人2.1",
62 | "nodeType":"serviceTask",
63 | "nextNode": null
64 | }
65 | },
66 | {
67 | "nodeName":"条件2",
68 | "conditionExpression":"=id<1",
69 | "nextNode":{
70 | "nodeName":"审核人2.2",
71 | "nodeType":"serviceTask",
72 | "nextNode":null
73 | }
74 | },
75 | {...}
76 | ]
77 | }
78 | ```
79 |
80 | - parallel
81 | ```json
82 | {
83 | "nodeName":"并行任务",
84 | "nodeType":"parallelGateway",
85 | "nextNode":{
86 | "nodeName":"",
87 | "nodeType":"parallelGateway",
88 | "nextNode":null
89 | },
90 | "branchNodes":[
91 | {
92 | "nextNode":{
93 | "nodeName":"审核人2.1",
94 | "nodeType":"serviceTask",
95 | "nextNode": null
96 | }
97 | },
98 | {
99 | "nextNode":{
100 | "nodeName":"审核人2.2",
101 | "nodeType":"serviceTask",
102 | "nextNode":null
103 | }
104 | },
105 | {
106 | "nextNode":{
107 | "nodeName":"审核人2.3",
108 | "nodeType":"serviceTask",
109 | "nextNode":null
110 | }
111 | },
112 | {...}
113 | ]
114 | }
115 | ```
116 |
117 | - 完整数据结构
118 | ```json
119 | {
120 | "process":{
121 | "processId":"work-flow-id",
122 | "name":"合同审批"
123 | },
124 | "processNode":{
125 | "nodeName":"审核人1",
126 | "nodeType":"serviceTask",
127 | "taskHeaders":{
128 | "a":"b",
129 | "e":"d"
130 | },
131 | "nextNode":{
132 | "nodeName":"排他",
133 | "nodeType":"exclusiveGateway",
134 | "nextNode":{
135 | "nodeName":"排他网关",
136 | "nodeType":"exclusiveGateway",
137 | "nextNode":null
138 | },
139 | "branchNodes":[
140 | {
141 | "nodeName":"条件1",
142 | "conditionExpression":"=id>1",
143 | "nextNode":{
144 | "nodeName":"审核人2.1",
145 | "nodeType":"userTask",
146 | "assignee": "aaa",
147 | "candidateGroups": "bbb",
148 | "nextNode":null
149 | }
150 | },
151 | {
152 | "nodeName":"条件2",
153 | "conditionExpression":"=id<1",
154 | "nextNode":{
155 | "nodeName":"审核人2.2",
156 | "nodeType":"serviceTask",
157 | "nextNode":null
158 | }
159 | }
160 | ]
161 | }
162 | }
163 | }
164 | ```
165 | ### 3. 示例
166 | * 启动服务
167 | * ``cd example && mvn clean package -DskipTests && mvn spring-boot:run``
168 | * http://localhost:8080
169 |
--------------------------------------------------------------------------------
/example/frontend/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ### workflow钉钉审批流程设置
4 |
5 | workflow钉钉审批流程设置,基于vue开发。QQ交流群:639251756
6 | 
7 | - [线上开源地址 https://github.com/StavinLi/Workflow](https://github.com/StavinLi/Workflow) github点个星吧!
8 | - [预览地址 http://stavinli.gitee.io/workflow/#/](http://stavinli.gitee.io/workflow/#/)
9 | -------------------
10 |
11 |
12 | #### 项目介绍
13 | - UI钉钉风格
14 | - 技术点
15 | 1. 组件自调用+递归处理,按树状结局处理审批流程问题
16 | - 主要功能点
17 | 2. 界面缩放
18 | 
19 | ```javascript
20 |
21 |
22 |
{{nowVal}}%
23 |
24 |
25 | ```
26 | 3. 节点设置(包括审批人、发起人、抄送人、条件设置)
27 | 
28 | ```javascript
29 |
30 |
31 |
32 |
33 |
34 | 指定成员
35 | 主管
36 | 发起人自选
37 | 发起人自己
38 | 连续多级主管
39 |
40 | ...
41 | ```
42 | 5. 节点新增
43 | 
44 | ```javascript
45 |
46 |
47 |
67 | ...
68 | ```
69 | 5.错误校验
70 | 
71 | ```javascript
72 | let {type,error,nodeName,branchNodes} = nextNode
73 | if (type == 1 || type == 2) {
74 | if (error) {
75 | this.tipList.push({ name: nodeName, type: ["","审核人","抄送人"][type] })
76 | }
77 | this.reErr(nextNode)
78 | } else if (type == 3) {
79 | this.reErr(nextNode)
80 | } else if (type == 4) {
81 | this.reErr(nextNode)
82 | for (var i = 0; i < branchNodes.length; i++) {
83 | if (branchNodes[i].error) {
84 | this.tipList.push({ name: branchNodes[i].nodeName, type: "条件" })
85 | }
86 | this.reErr(branchNodes[i])
87 | }
88 | }
89 | ```
90 | 6.模糊搜索匹配人员、职位、角色
91 | ```javascript
92 |
93 |
94 |
95 | ```
96 | #### 项目安装
97 |
98 | > git clone https://github.com/StavinLi/Workflow.git 点个赞吧!
99 |
100 | #### 项目运行
101 | > 1.环境依赖 `npm i`
102 |
103 | > 2.本地运行 `npm run serve`
104 |
105 | > 3.打包运行 `npm run build`
106 |
--------------------------------------------------------------------------------
/example/frontend/src/components/dialog/employeesDialog.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 天下
8 | {{item.departmentName}}
10 |
11 |
23 |
24 |
25 |
已选({{total}})
26 | 清空
27 |
28 |
29 |
30 |
31 |
32 | {{item.departmentName}}
33 |
34 |
35 |
36 |
37 |
38 | {{item.employeeName}}
39 |
40 |
41 |
42 |
43 |
44 |
48 |
49 |
50 |
51 |
103 |
104 |
107 |
--------------------------------------------------------------------------------
/example/backend/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 |
6 | cn.lzgabel
7 | camunda-bpmn-converter-parent
8 | 1.0-SNAPSHOT
9 | ../../pom.xml
10 |
11 |
12 | camunda-bpmn-converter-demo
13 | jar
14 |
15 | camunda-bpmn-converter-demo
16 |
17 |
18 | 17
19 | 1.7
20 | 2.22.4
21 | UTF-8
22 | UTF-8
23 |
24 | false
25 |
26 |
27 |
28 |
29 | org.springframework.boot
30 | spring-boot-starter-web
31 |
32 |
33 |
34 | cn.lzgabel
35 | camunda-bpmn-converter
36 |
37 |
38 |
39 | cn.lzgabel
40 | workflow-frontend
41 |
42 |
43 |
44 |
45 | camunda-bpmn-converter-demo
46 |
47 |
48 | false
49 | src/main/resources
50 |
51 |
52 |
53 |
54 |
55 | com.diffplug.spotless
56 | spotless-maven-plugin
57 | ${plugin.version.spotless}
58 |
59 |
60 |
61 |
62 | ${plugin.version.googlejavaformat}
63 |
64 |
65 |
66 |
67 |
68 |
69 | false
70 | true
71 | true
72 |
73 |
74 | ${skipChecks}
75 | ${skipChecks}
76 |
77 |
78 |
79 | com.google.googlejavaformat
80 | google-java-format
81 | ${plugin.version.googlejavaformat}
82 |
83 |
84 |
85 |
86 |
87 | apply
88 |
89 | process-sources
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | org.apache.maven.plugins
98 | maven-compiler-plugin
99 | 3.9.0
100 |
101 | 17
102 | 17
103 | UTF-8
104 |
105 |
106 |
107 |
108 | com.diffplug.spotless
109 | spotless-maven-plugin
110 | ${plugin.version.spotless}
111 |
112 |
113 |
114 | **/*.md
115 |
116 |
117 | **/target/**/*.md
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 | apply
126 |
127 | process-sources
128 |
129 |
130 |
131 |
132 |
133 | org.springframework.boot
134 | spring-boot-maven-plugin
135 |
136 |
137 |
138 |
139 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/processing/BpmnElementProcessor.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.processing;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.BpmnElementType;
5 | import java.lang.reflect.InvocationTargetException;
6 | import java.lang.reflect.Method;
7 | import java.util.Objects;
8 | import org.camunda.bpm.model.bpmn.builder.AbstractBaseElementBuilder;
9 | import org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder;
10 | import org.camunda.bpm.model.bpmn.instance.FlowNode;
11 |
12 | /**
13 | * 〈功能简述〉
14 | * 〈完成基于 JSON 格式转 BPMN 元素业务逻辑转换〉
15 | *
16 | * @author lizhi
17 | * @since 1.0.0
18 | */
19 | public interface BpmnElementProcessor<
20 | E extends BaseDefinition, T extends AbstractBaseElementBuilder> {
21 |
22 | /**
23 | * 创建新的节点
24 | *
25 | * @param flowNodeBuilder builder
26 | * @param flowNode 流程节点参数
27 | * @return 最后一个节点id
28 | * @throws InvocationTargetException invocationTargetException
29 | * @throws IllegalAccessException illegalAccessException
30 | */
31 | default String onCreate(AbstractFlowNodeBuilder flowNodeBuilder, BaseDefinition flowNode)
32 | throws InvocationTargetException, IllegalAccessException {
33 | String nodeType = flowNode.getNodeType();
34 | BpmnElementType elementType = BpmnElementType.bpmnElementTypeFor(nodeType);
35 | BpmnElementProcessor processor =
36 | BpmnElementProcessors.getProcessor(elementType);
37 | return processor.onComplete(flowNodeBuilder, flowNode);
38 | }
39 |
40 | /**
41 | * 完成当前节点详情设置
42 | *
43 | * @param flowNodeBuilder builder
44 | * @param flowNode 流程节点参数
45 | * @return 最后一个节点id
46 | * @throws InvocationTargetException invocationTargetException
47 | * @throws IllegalAccessException illegalAccessException
48 | */
49 | String onComplete(T flowNodeBuilder, E flowNode)
50 | throws InvocationTargetException, IllegalAccessException;
51 |
52 | /**
53 | * 循环向上转型, 获取对象的 DeclaredMethod
54 | *
55 | * @param object : 子类对象
56 | * @param methodName : 父类中的方法名
57 | * @param parameterTypes : 父类中的方法参数类型
58 | * @return 父类中的方法对象
59 | */
60 | default Method getDeclaredMethod(Object object, String methodName, Class>... parameterTypes) {
61 | Method method;
62 | for (Class> clazz = object.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
63 | try {
64 | method = clazz.getDeclaredMethod(methodName, parameterTypes);
65 | return method;
66 | } catch (Exception ignore) {
67 | }
68 | }
69 | return null;
70 | }
71 |
72 | /**
73 | * 移动到指定节点
74 | *
75 | * @param flowNodeBuilder builder
76 | * @param id 目标节点位移标识
77 | * @return 目标节点类型 builder
78 | */
79 | default AbstractFlowNodeBuilder, ?> moveToNode(
80 | AbstractFlowNodeBuilder, ?> flowNodeBuilder, String id) {
81 | return flowNodeBuilder.moveToNode(id);
82 | }
83 |
84 | /**
85 | * 创建指定类型实例
86 | *
87 | * @param flowNodeBuilder builder
88 | * @param flowNode 节点
89 | * @return 指定类型实例 builder
90 | */
91 | @SuppressWarnings("unchecked")
92 | default AbstractFlowNodeBuilder createInstance(
93 | AbstractFlowNodeBuilder, ?> flowNodeBuilder, BaseDefinition flowNode) {
94 | // 自动生成id
95 | Method createTarget = getDeclaredMethod(flowNodeBuilder, "createTarget", Class.class);
96 | // 手动传入id
97 | // Method createTarget = getDeclaredMethod(abstractFlowNodeBuilder, "createTarget", Class.class,
98 | // String.class);
99 | try {
100 | final var nodeType = flowNode.getNodeType();
101 | createTarget.setAccessible(true);
102 | Class extends FlowNode> clazz =
103 | BpmnElementType.bpmnElementTypeFor(nodeType)
104 | .getElementTypeClass()
105 | .orElseThrow(
106 | () -> new RuntimeException("Unsupported BPMN element of type " + nodeType));
107 |
108 | final var instance = clazz.cast(createTarget.invoke(flowNodeBuilder, clazz));
109 | instance.setName(flowNode.getNodeName());
110 | final var builder = instance.builder();
111 | // 创建监听器
112 | createExecutionListener(builder, flowNode);
113 | return builder;
114 | } catch (IllegalAccessException | InvocationTargetException e) {
115 | e.printStackTrace();
116 | return null;
117 | }
118 | }
119 |
120 | /**
121 | * 创建监听器
122 | *
123 | * @param flowNodeBuilder builder
124 | * @param flowNode 当前节点
125 | */
126 | default void createExecutionListener(
127 | AbstractFlowNodeBuilder, ?> flowNodeBuilder, BaseDefinition flowNode) {
128 | flowNode.getListeners().stream()
129 | .filter(Objects::nonNull)
130 | .forEach(
131 | listener -> {
132 | if (listener.isClass()) {
133 | flowNodeBuilder.camundaExecutionListenerClass(
134 | listener.getEventType(), listener.getJavaClass());
135 | } else if (listener.isDelegateExpression()) {
136 | flowNodeBuilder.camundaExecutionListenerDelegateExpression(
137 | listener.getEventType(), listener.getDelegateExpression());
138 | } else if (listener.isExpression()) {
139 | flowNodeBuilder.camundaExecutionListenerExpression(
140 | listener.getEventType(), listener.getExpression());
141 | }
142 | });
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/processing/gateway/ExclusiveGatewayProcessor.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.processing.gateway;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.gateway.BranchNode;
5 | import cn.lzgabel.camunda.converter.bean.gateway.ExclusiveGatewayDefinition;
6 | import com.google.common.collect.Lists;
7 | import java.lang.reflect.InvocationTargetException;
8 | import java.util.Collections;
9 | import java.util.List;
10 | import java.util.Objects;
11 | import java.util.stream.Collectors;
12 | import org.apache.commons.collections.CollectionUtils;
13 | import org.apache.commons.lang3.StringUtils;
14 | import org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder;
15 | import org.camunda.bpm.model.bpmn.builder.ExclusiveGatewayBuilder;
16 | import org.camunda.bpm.model.bpmn.instance.SequenceFlow;
17 |
18 | /**
19 | * 〈功能简述〉
20 | * 〈ExclusiveGateway节点类型详情设置〉
21 | *
22 | * @author lizhi
23 | * @since 1.0.0
24 | */
25 | public class ExclusiveGatewayProcessor
26 | extends AbstractGatewayProcessor {
27 |
28 | @Override
29 | public String onComplete(
30 | AbstractFlowNodeBuilder flowNodeBuilder, ExclusiveGatewayDefinition flowNode)
31 | throws InvocationTargetException, IllegalAccessException {
32 |
33 | final ExclusiveGatewayBuilder exclusiveGatewayBuilder =
34 | (ExclusiveGatewayBuilder) createInstance(flowNodeBuilder, flowNode);
35 |
36 | List branchNodes = flowNode.getBranchNodes();
37 | if (CollectionUtils.isEmpty(flowNode.getBranchNodes())
38 | && Objects.isNull(flowNode.getNextNode())) {
39 | return exclusiveGatewayBuilder.getElement().getId();
40 | }
41 | List incoming = Lists.newArrayListWithCapacity(branchNodes.size());
42 |
43 | // 不存在任务节点的情况(即空分支: 见 branchNode-2)
44 | List emptyNextNodeBranchNodes = Lists.newCopyOnWriteArrayList();
45 | for (BranchNode branchNode : branchNodes) {
46 | BaseDefinition nextNode = branchNode.getNextNode();
47 |
48 | String nodeName = branchNode.getNodeName();
49 | String expression = branchNode.getConditionExpression();
50 |
51 | // 记录分支条件中不存在任务节点的情况(即空分支: 见 branchNode-2)
52 | // ------------------------
53 | //
54 | // --(branchNode-1)--> serviceTask(current) --> serviceTask --
55 | // gateway --> --> gateway(merge)
56 | // --> serviceTask(nextNode)
57 | // --(branchNode-2)--> ----------------------------------- --
58 | //
59 | //
60 | if (Objects.isNull(nextNode)) {
61 | incoming.add(exclusiveGatewayBuilder.getElement().getId());
62 | BranchNode condition =
63 | BranchNode.builder().nodeName(nodeName).conditionExpression(expression).build();
64 | emptyNextNodeBranchNodes.add(condition);
65 | continue;
66 | }
67 |
68 | // 只生成一个任务,同时设置当前任务的条件
69 | nextNode.setIncoming(Collections.singletonList(exclusiveGatewayBuilder.getElement().getId()));
70 | String id =
71 | onCreate(
72 | moveToNode(exclusiveGatewayBuilder, exclusiveGatewayBuilder.getElement().getId()),
73 | nextNode);
74 | exclusiveGatewayBuilder.getElement().getOutgoing().stream()
75 | .forEach(
76 | sequenceFlow ->
77 | conditionExpression(sequenceFlow, exclusiveGatewayBuilder, branchNode));
78 | if (Objects.nonNull(id)) {
79 | incoming.add(id);
80 | }
81 | }
82 |
83 | String id = exclusiveGatewayBuilder.getElement().getId();
84 | BaseDefinition nextNode = flowNode.getNextNode();
85 | if (Objects.nonNull(nextNode)) {
86 | nextNode.setIncoming(incoming);
87 | return merge(exclusiveGatewayBuilder, id, emptyNextNodeBranchNodes, nextNode);
88 | }
89 | return id;
90 | }
91 |
92 | protected void accept(
93 | AbstractFlowNodeBuilder flowNodeBuilder, String mergeId, List conditions) {
94 | if (!(flowNodeBuilder instanceof ExclusiveGatewayBuilder)) {
95 | return;
96 | }
97 | // 针对分支条件中空分支场景 添加条件表达式
98 | ExclusiveGatewayBuilder exclusiveGatewayBuilder = (ExclusiveGatewayBuilder) flowNodeBuilder;
99 | if (CollectionUtils.isNotEmpty(conditions)) {
100 | List sequenceFlows =
101 | moveToNode(exclusiveGatewayBuilder, mergeId).getElement().getIncoming().stream()
102 | // 获取从源 gateway 到目标节点 未设置条件表达式的节点
103 | .filter(
104 | e ->
105 | StringUtils.equals(
106 | e.getSource().getId(), exclusiveGatewayBuilder.getElement().getId()))
107 | .collect(Collectors.toList());
108 |
109 | sequenceFlows.stream()
110 | .forEach(
111 | sequenceFlow -> {
112 | if (!conditions.isEmpty()) {
113 | BranchNode condition = conditions.get(0);
114 | conditionExpression(sequenceFlow, exclusiveGatewayBuilder, condition);
115 | conditions.remove(0);
116 | }
117 | });
118 | }
119 | }
120 |
121 | private void conditionExpression(
122 | SequenceFlow sequenceFlow,
123 | ExclusiveGatewayBuilder exclusiveGatewayBuilder,
124 | BranchNode condition) {
125 | if (condition.isDefault()) {
126 | exclusiveGatewayBuilder.defaultFlow(sequenceFlow);
127 | }
128 | createConditionExpression(sequenceFlow, exclusiveGatewayBuilder, condition);
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/converter/src/main/java/cn/lzgabel/camunda/converter/processing/gateway/InclusiveGatewayProcessor.java:
--------------------------------------------------------------------------------
1 | package cn.lzgabel.camunda.converter.processing.gateway;
2 |
3 | import cn.lzgabel.camunda.converter.bean.BaseDefinition;
4 | import cn.lzgabel.camunda.converter.bean.gateway.BranchNode;
5 | import cn.lzgabel.camunda.converter.bean.gateway.InclusiveGatewayDefinition;
6 | import com.google.common.collect.Lists;
7 | import java.lang.reflect.InvocationTargetException;
8 | import java.util.Collections;
9 | import java.util.List;
10 | import java.util.Objects;
11 | import java.util.stream.Collectors;
12 | import org.apache.commons.collections.CollectionUtils;
13 | import org.apache.commons.lang3.StringUtils;
14 | import org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder;
15 | import org.camunda.bpm.model.bpmn.builder.InclusiveGatewayBuilder;
16 | import org.camunda.bpm.model.bpmn.instance.SequenceFlow;
17 |
18 | /**
19 | * 〈功能简述〉
20 | * 〈InclusiveGateway节点类型详情设置〉
21 | *
22 | * @author lizhi
23 | * @since 1.0.0
24 | */
25 | public class InclusiveGatewayProcessor
26 | extends AbstractGatewayProcessor {
27 |
28 | @Override
29 | public String onComplete(
30 | AbstractFlowNodeBuilder flowNodeBuilder, InclusiveGatewayDefinition flowNode)
31 | throws InvocationTargetException, IllegalAccessException {
32 | final InclusiveGatewayBuilder inclusiveGatewayBuilder =
33 | (InclusiveGatewayBuilder) createInstance(flowNodeBuilder, flowNode);
34 |
35 | List branchNodes = flowNode.getBranchNodes();
36 | if (CollectionUtils.isEmpty(flowNode.getBranchNodes())
37 | && Objects.isNull(flowNode.getNextNode())) {
38 | return inclusiveGatewayBuilder.getElement().getId();
39 | }
40 | List incoming = Lists.newArrayListWithCapacity(branchNodes.size());
41 |
42 | // 不存在任务节点的情况(即空分支: 见 branchNode-2)
43 | List emptyNextNodeBranchNodes = Lists.newCopyOnWriteArrayList();
44 | for (BranchNode branchNode : branchNodes) {
45 | BaseDefinition nextNode = branchNode.getNextNode();
46 |
47 | String nodeName = branchNode.getNodeName();
48 | String expression = branchNode.getConditionExpression();
49 |
50 | // 记录分支条件中不存在任务节点的情况(即空分支: 见 branchNode-2)
51 | // ------------------------
52 | //
53 | // --(branchNode-1)--> serviceTask(current) --> serviceTask --
54 | // gateway --> --> gateway(merge)
55 | // --> serviceTask(nextNode)
56 | // --(branchNode-2)--> ----------------------------------- --
57 | //
58 | //
59 | if (Objects.isNull(nextNode)) {
60 | incoming.add(inclusiveGatewayBuilder.getElement().getId());
61 | BranchNode condition =
62 | BranchNode.builder().nodeName(nodeName).conditionExpression(expression).build();
63 | emptyNextNodeBranchNodes.add(condition);
64 | continue;
65 | }
66 |
67 | // 只生成一个任务,同时设置当前任务的条件
68 | nextNode.setIncoming(Collections.singletonList(inclusiveGatewayBuilder.getElement().getId()));
69 | String id =
70 | onCreate(
71 | moveToNode(inclusiveGatewayBuilder, inclusiveGatewayBuilder.getElement().getId()),
72 | nextNode);
73 | inclusiveGatewayBuilder.getElement().getOutgoing().stream()
74 | .forEach(
75 | sequenceFlow ->
76 | conditionExpression(sequenceFlow, inclusiveGatewayBuilder, branchNode));
77 | if (Objects.nonNull(id)) {
78 | incoming.add(id);
79 | }
80 | }
81 |
82 | String id = inclusiveGatewayBuilder.getElement().getId();
83 | BaseDefinition nextNode = flowNode.getNextNode();
84 | if (Objects.nonNull(nextNode)) {
85 | nextNode.setIncoming(incoming);
86 | return merge(inclusiveGatewayBuilder, id, emptyNextNodeBranchNodes, nextNode);
87 | }
88 | return id;
89 | }
90 |
91 | protected void accept(
92 | AbstractFlowNodeBuilder flowNodeBuilder, String mergeId, List conditions) {
93 | if (!(flowNodeBuilder instanceof InclusiveGatewayBuilder)) {
94 | return;
95 | }
96 | // 针对分支条件中空分支场景 添加条件表达式
97 | InclusiveGatewayBuilder inclusiveGatewayBuilder = (InclusiveGatewayBuilder) flowNodeBuilder;
98 | if (CollectionUtils.isNotEmpty(conditions)) {
99 | List sequenceFlows =
100 | moveToNode(inclusiveGatewayBuilder, mergeId).getElement().getIncoming().stream()
101 | // 获取从源 gateway 到目标节点 未设置条件表达式的节点
102 | .filter(
103 | e ->
104 | StringUtils.equals(
105 | e.getSource().getId(), inclusiveGatewayBuilder.getElement().getId()))
106 | .collect(Collectors.toList());
107 |
108 | sequenceFlows.stream()
109 | .forEach(
110 | sequenceFlow -> {
111 | if (!conditions.isEmpty()) {
112 | BranchNode condition = conditions.get(0);
113 | conditionExpression(sequenceFlow, inclusiveGatewayBuilder, condition);
114 | conditions.remove(0);
115 | }
116 | });
117 | }
118 | }
119 |
120 | private void conditionExpression(
121 | SequenceFlow sequenceFlow,
122 | InclusiveGatewayBuilder inclusiveGatewayBuilder,
123 | BranchNode condition) {
124 | if (condition.isDefault()) {
125 | inclusiveGatewayBuilder.defaultFlow(sequenceFlow);
126 | }
127 |
128 | createConditionExpression(sequenceFlow, inclusiveGatewayBuilder, condition);
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/converter/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 |
6 | cn.lzgabel
7 | camunda-bpmn-converter-parent
8 | 1.0-SNAPSHOT
9 | ../pom.xml
10 |
11 |
12 | camunda-bpmn-converter
13 | jar
14 |
15 | camunda-bpmn-converter
16 |
17 | Camunda BPMN Converter is a tool to convert JSON file format to BPMN file format
18 |
19 |
20 | 7.18.0
21 | 17
22 | 1.7
23 | 2.22.4
24 | UTF-8
25 | UTF-8
26 |
27 | false
28 |
29 |
30 |
31 |
32 | org.camunda.bpm.model
33 | camunda-bpmn-model
34 | ${camunda.version}
35 |
36 |
37 |
38 | org.apache.commons
39 | commons-lang3
40 | 3.10
41 |
42 |
43 |
44 | com.alibaba
45 | fastjson
46 | 1.2.83
47 |
48 |
49 |
50 | com.google.guava
51 | guava
52 | 30.1-jre
53 |
54 |
55 |
56 | org.projectlombok
57 | lombok
58 |
59 |
60 |
61 | commons-collections
62 | commons-collections
63 | 3.2.2
64 |
65 |
66 |
67 | junit
68 | junit
69 | 4.13.1
70 | test
71 |
72 |
73 |
74 | com.fasterxml.jackson.core
75 | jackson-core
76 |
77 |
78 |
79 | com.fasterxml.jackson.core
80 | jackson-annotations
81 |
82 |
83 |
84 | com.fasterxml.jackson.core
85 | jackson-databind
86 |
87 |
88 |
89 | cn.lzgabel.layouter
90 | bpmn-auto-layout
91 | 1.0.4
92 |
93 |
94 |
95 |
96 |
97 | camunda-bpmn-converter
98 |
99 |
100 |
101 | false
102 | src/main/resources
103 |
104 |
105 |
106 |
107 |
108 | com.diffplug.spotless
109 | spotless-maven-plugin
110 | ${plugin.version.spotless}
111 |
112 |
113 |
114 |
115 | ${plugin.version.googlejavaformat}
116 |
117 |
118 |
119 |
120 |
121 |
122 | false
123 | true
124 | true
125 |
126 |
127 | ${skipChecks}
128 | ${skipChecks}
129 |
130 |
131 |
132 | com.google.googlejavaformat
133 | google-java-format
134 | ${plugin.version.googlejavaformat}
135 |
136 |
137 |
138 |
139 |
140 | apply
141 |
142 | process-sources
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 | org.apache.maven.plugins
152 | maven-compiler-plugin
153 | 3.9.0
154 |
155 | 17
156 | 17
157 | UTF-8
158 |
159 |
160 |
161 |
162 | com.diffplug.spotless
163 | spotless-maven-plugin
164 | ${plugin.version.spotless}
165 |
166 |
167 |
168 | **/*.md
169 |
170 |
171 | **/target/**/*.md
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 | apply
180 |
181 | process-sources
182 |
183 |
184 |
185 |
186 |
187 |
188 |
--------------------------------------------------------------------------------
/example/frontend/src/components/dialog/employeesRoleDialog.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | 天下
12 | {{item.departmentName}}
14 |
15 |
27 |
33 |
34 |
35 |
已选({{total}})
36 | 清空
37 |
38 |
39 |
40 |
41 | {{item.roleName}}
42 |
43 |
44 |
45 |
46 |
47 | {{item.departmentName}}
48 |
49 |
50 |
51 |
52 |
53 | {{item.employeeName}}
54 |
55 |
56 |
57 |
58 |
59 |
63 |
64 |
65 |
66 |
134 |
135 |
138 |
--------------------------------------------------------------------------------
/example/frontend/src/plugins/preload.js:
--------------------------------------------------------------------------------
1 | function All() {}
2 | All.prototype = {
3 | timer: "",
4 | debounce(fn, delay = 500) {
5 | var _this = this;
6 | return function(arg) {
7 | //获取函数的作用域和变量
8 | let that = this;
9 | let args = arg;
10 | clearTimeout(_this.timer) // 清除定时器
11 | _this.timer = setTimeout(function() {
12 | fn.call(that, args)
13 | }, delay)
14 | }
15 | },
16 | setCookie(val) { //cookie设置[{key:value}]、获取key、清除['key1','key2']
17 | for (var i = 0, len = val.length; i < len; i++) {
18 | for (var key in val[i]) {
19 | document.cookie = key + '=' + encodeURIComponent(val[i][key]) + "; path=/";
20 | }
21 | }
22 | },
23 | getCookie(name) {
24 | var strCookie = document.cookie;
25 | var arrCookie = strCookie.split("; ");
26 | for (var i = 0, len = arrCookie.length; i < len; i++) {
27 | var arr = arrCookie[i].split("=");
28 | if (name == arr[0]) {
29 | return decodeURIComponent(arr[1]);
30 | }
31 | }
32 | },
33 | clearCookie(name) {
34 | var myDate = new Date();
35 | myDate.setTime(-1000); //设置时间
36 | for (var i = 0, len = name.length; i < len; i++) {
37 | document.cookie = "" + name[i] + "=''; path=/; expires=" + myDate.toGMTString();
38 | }
39 | },
40 | arrToStr(arr) {
41 | if (arr) {
42 | return arr.map(item => { return item.name }).toString()
43 | }
44 | },
45 | toggleClass(arr, elem, key = 'id') {
46 | return arr.some(item => { return item[key] == elem[key] });
47 | },
48 | toChecked(arr, elem, key = 'id') {
49 | var isIncludes = this.toggleClass(arr, elem, key);
50 | !isIncludes ? arr.push(elem) : this.removeEle(arr, elem, key);
51 | },
52 | removeEle(arr, elem, key = 'id') {
53 | var includesIndex;
54 | arr.map((item, index) => {
55 | if (item[key] == elem[key]) {
56 | includesIndex = index
57 | }
58 | });
59 | arr.splice(includesIndex, 1);
60 | },
61 | setApproverStr(processNode) {
62 | console.log(processNode)
63 | // if (processNode.settype == 1) {
64 | // if (processNode.nodeUserList.length == 1) {
65 | // return processNode.nodeUserList[0].name
66 | // } else if (processNode.nodeUserList.length > 1) {
67 | // if (processNode.examineMode == 1) {
68 | // return this.arrToStr(processNode.nodeUserList)
69 | // } else if (processNode.examineMode == 2) {
70 | // return processNode.nodeUserList.length + "人会签"
71 | // }
72 | // }
73 | // } else if (processNode.settype == 2) {
74 | // let level = processNode.directorLevel == 1 ? '直接主管' : '第' + processNode.directorLevel + '级主管'
75 | // if (processNode.examineMode == 1) {
76 | // return level
77 | // } else if (processNode.examineMode == 2) {
78 | // return level + "会签"
79 | // }
80 | // } else if (processNode.settype == 4) {
81 | // if (processNode.selectRange == 1) {
82 | // return "发起人自选"
83 | // } else {
84 | // if (processNode.nodeUserList.length > 0) {
85 | // if (processNode.selectRange == 2) {
86 | // return "发起人自选"
87 | // } else {
88 | // return '发起人从' + processNode.nodeUserList[0].name + '中自选'
89 | // }
90 | // } else {
91 | // return "";
92 | // }
93 | // }
94 | // } else if (processNode.settype == 5) {
95 | // return "发起人自己"
96 | // } else if (processNode.settype == 7) {
97 | // return '从直接主管到通讯录中级别最高的第' + processNode.examineEndDirectorLevel + '个层级主管'
98 | // }
99 | },
100 | dealStr(str, obj) {
101 | let arr = [];
102 | let list = str.split(",");
103 | for (var elem in obj) {
104 | list.map(item => {
105 | if (item == elem) {
106 | arr.push(obj[elem].value)
107 | }
108 | })
109 | }
110 | return arr.join("或")
111 | },
112 | conditionStr(processNode, index) {
113 | // var { conditionList, nodeUserList } = processNode.branchNodes[index];
114 | // if (conditionList.length == 0) {
115 | // return (index == processNode.branchNodes.length - 1) && processNode.branchNodes[0].conditionList.length != 0 ? '其他条件进入此流程' : '请设置条件'
116 | // } else {
117 | // let str = ""
118 | // for (var i = 0; i < conditionList.length; i++) {
119 | // var { columnId, columnType, showType, showName, optType, zdy1, opt1, zdy2, opt2, fixedDownBoxValue } = conditionList[i];
120 | // if (columnId == 0) {
121 | // if (nodeUserList.length != 0) {
122 | // str += '发起人属于:'
123 | // str += nodeUserList.map(item => { return item.name }).join("或") + " 并且 "
124 | // }
125 | // }
126 | // if (columnType == "String" && showType == "3") {
127 | // if (zdy1) {
128 | // str += showName + '属于:' + this.dealStr(zdy1, JSON.parse(fixedDownBoxValue)) + " 并且 "
129 | // }
130 | // }
131 | // if (columnType == "Double") {
132 | // if (optType != 6 && zdy1) {
133 | // var optTypeStr = ["", "<", ">", "≤", "=", "≥"][optType]
134 | // str += `${showName} ${optTypeStr} ${zdy1} 并且 `
135 | // } else if (optType == 6 && zdy1 && zdy2) {
136 | // str += `${zdy1} ${opt1} ${showName} ${opt2} ${zdy2} 并且 `
137 | // }
138 | // }
139 | // }
140 | // return str ? str.substring(0, str.length - 4) : '请设置条件'
141 | // }
142 | },
143 | copyerStr(processNode) {
144 | // if (processNode.nodeUserList.length != 0) {
145 | // return this.arrToStr(processNode.nodeUserList)
146 | // } else {
147 | // if (processNode.ccSelfSelectFlag == 1) {
148 | // return "发起人自选"
149 | // }
150 | // }
151 | },
152 | toggleStrClass(item, key) {
153 | let a = item.zdy1 ? item.zdy1.split(",") : []
154 | return a.some(item => { return item == key });
155 | },
156 | }
157 |
158 | export default new All();
159 |
--------------------------------------------------------------------------------
/example/frontend/src/views/setting.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
{{process.name}}
7 |
8 |
17 |
18 |
19 | 发 布
20 |
21 |
22 |
23 |
24 |
25 |
26 |
{{nowVal}}%
27 |
28 |
29 |
37 |
38 |
39 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
204 |
210 |
--------------------------------------------------------------------------------
/example/frontend/src/components/drawer/approverDrawer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 指定成员
8 | 主管
9 | 发起人自选
10 | 发起人自己
11 | 连续多级主管
12 |
13 |
添加/修改成员
14 |
15 | {{item.name}}
16 |
17 |
18 | 清除
19 |
20 |
21 |
22 |
23 | 发起人的:
24 |
25 | {{item==1?'直接':'第'+item+'级'}}主管
26 |
27 |
28 |
找不到主管时,由上级主管代审批
29 |
30 |
31 |
该审批节点设置“发起人自己”后,审批人默认为发起人
32 |
33 |
34 |
35 | 选一个人
36 | 选多个人
37 |
38 |
选择范围
39 |
40 | 全公司
41 | 指定成员
42 | 指定角色
43 |
44 |
添加/修改成员
45 |
添加/修改角色
46 |
47 | {{item.name}}
48 |
49 |
50 | 清除
51 |
52 |
53 |
54 |
审批终点
55 |
56 | 发起人的:
57 |
58 | {{item==1?'最高':'第'+item}}层级主管
59 |
60 |
61 |
62 |
63 |
多人审批时采用的审批方式
64 |
65 | 依次审批
66 |
67 | 会签(须所有审批人同意)
68 |
69 |
70 |
71 |
审批人为空时
72 |
73 | 自动审批通过/不允许发起
74 |
75 | 转交给审核管理员
76 |
77 |
78 |
79 |
83 |
88 |
93 |
94 |
95 |
96 |
171 |
226 |
--------------------------------------------------------------------------------
/example/frontend/src/components/addNode.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
138 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2021 lzgabel
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------