Skip to main content

Workflow Migration: From Activiti6.0 to Camunda7.8 Filling the Road

Mr.ChenAbout 33 minarticleJava

​​​​

Preface

When I first came into contact with the workflow engine ten years ago, all I knew was Activiti. I didn’t know if there were any other workflow engines. Although the Chinese manuals and tutorials were very lacking, after continuous exploration, I used it more and more smoothly. , has been used well. Recently, I was building a workflow service platform, and I found a lot of open source frameworks for the front-end drawing process. I have not found Activiti’s exclusive drawing framework. Later, I found bpmn-js, but found that the drawn process could not run on Activiti. Check it out Only to find out that the panel plug-in used is exclusive to Camunda. In fact, even bpmn-js was developed by the Camunda community. Immediately became interested in Camunda, read a lot of articles comparing Camunda, Flowable, and Activiti on the Internet, and found that the Camunda community is the most active now. With the idea of trying it out, I pulled a branch on the project and started the Camunda migration Work.

Why Camunda7.8?

The latest version is actually Camunda7.11, the function has been done very well, you can find the official rest interface library, webapp library in the maven library, and then cooperate with bpmn-js, you can realize the workflow without writing code at all manage. But after the project is deployed, it can't be started at all, so I know that it is not compatible with the springboot version. I use springboot1.5.9, and camunda7.8 and above are all based on springboot2.0, so I can only use version 7.8.

Paste my pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion><groupId>cn.jia</groupId>
  <artifactId>jia-api-admin</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging><name>jia-api-admin</name>
  <description>Jia服务平台管理后台</description><parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.9.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
  </parent><properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <spring-cloud.version>Edgware.SR3</spring-cloud.version>
  </properties><dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.session</groupId>
      <artifactId>spring-session-data-redis</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-configuration-processor</artifactId>
      <optional>true</optional>
      <exclusions>
        <exclusion>
          <groupId>com.vaadin.external.google</groupId>
          <artifactId>android-json</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
    </dependency>
    <!--<dependency>
      <groupId>org.activiti</groupId>
      <artifactId>activiti-spring-boot-starter-basic</artifactId>
      <version>6.0.0</version>
    </dependency>-->
    <dependency>
      <groupId>org.camunda.bpm.springboot</groupId>
      <artifactId>camunda-bpm-spring-boot-starter</artifactId>
      <version>2.3.0</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>1.3.1</version>
    </dependency>
    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper-spring-boot-starter</artifactId>
      <version>1.1.1</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.6</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      <exclusions>
        <exclusion>
          <groupId>com.vaadin.external.google</groupId>
          <artifactId>android-json</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <!--<scope>provided</scope>-->
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <scope>provided</scope>
    </dependency>
    <!-- json工具类 -->
    <dependency>
      <groupId>org.json</groupId>
      <artifactId>json</artifactId>
    </dependency>
  </dependencies><distributionManagement>
    <snapshotRepository>
      <id>nexus-snapshots</id>
      <url>http://maven.wydiy.com/repository/maven-snapshots/</url>
    </snapshotRepository>
    <repository>
      <id>nexus-releases</id>
      <url>http://maven.wydiy.com/repository/maven-releases/</url>
    </repository>
  </distributionManagement><build>
    <finalName>jia-api-admin</finalName>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <configuration>
          <skipTests>true</skipTests>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

Database Migration

In fact, there are already migration tutorials on the Internet, and the official website of Camunda also provides migration methods and scripts, but that is migrating from Activiti5.21 to Camunda7.5, so we can only try to solve the version problem. The first is the migration of the database. If it is a brand new project, you don’t need to worry about anything. The table will be created automatically when the project starts, but I used Activiti6. Well, Camunda is separated from Activiti, retaining most of Activiti's tables, and the structure is very similar, and Camunda also put migration scripts on github. I found the mysql script, and found that the mysql script had a grammatical error, so I had to fix it myself, and at the same time push the script to github. I didn’t expect the author to pay attention, because the last update was three years ago. Who would want the author? I actually asked me the reason for the push. After I explained the reason, I merged my script to the master. It was really a surprise. The author is also a very responsible person. However, that is not enough. There are still many differences between version 7.8 and version 7.5. There is no way but to compare my library with the initialization library of 7.8 one by one to sort out the latest scripts.

create table ACT_RU_INCIDENT (
  ID_ varchar(64) not null,
  INCIDENT_TIMESTAMP_ timestamp not null,
  INCIDENT_MSG_ varchar(4000),
  INCIDENT_TYPE_ varchar(255) not null,
  EXECUTION_ID_ varchar(64),
  ACTIVITY_ID_ varchar(255),
  PROC_INST_ID_ varchar(64),
  PROC_DEF_ID_ varchar(64),
  CAUSE_INCIDENT_ID_ varchar(64),
  ROOT_CAUSE_INCIDENT_ID_ varchar(64),
  CONFIGURATION_ varchar(255),
  primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;create index ACT_IDX_INC_CONFIGURATION on ACT_RU_INCIDENT(CONFIGURATION_);alter table ACT_RU_INCIDENT
    add constraint ACT_FK_INC_EXE
    foreign key (EXECUTION_ID_)
    references ACT_RU_EXECUTION (ID_);alter table ACT_RU_INCIDENT
    add constraint ACT_FK_INC_PROCINST
    foreign key (PROC_INST_ID_)
    references ACT_RU_EXECUTION (ID_);alter table ACT_RU_INCIDENT
    add constraint ACT_FK_INC_PROCDEF
    foreign key (PROC_DEF_ID_)
    references ACT_RE_PROCDEF (ID_);alter table ACT_RU_INCIDENT
    add constraint ACT_FK_INC_CAUSE
    foreign key (CAUSE_INCIDENT_ID_)
    references ACT_RU_INCIDENT (ID_) on delete cascade on update cascade;alter table ACT_RU_INCIDENT
    add constraint ACT_FK_INC_RCAUSE
    foreign key (ROOT_CAUSE_INCIDENT_ID_)
    references ACT_RU_INCIDENT (ID_) on delete cascade on update cascade;
​
​
​
/** add ACT_INST_ID_ column to execution table */
alter table ACT_RU_EXECUTION
    add ACT_INST_ID_ nvarchar(64);
​
​
/** populate ACT_INST_ID_ from history *//** get from history for active activity instances */
UPDATE
    ACT_RU_EXECUTION E
SET
    ACT_INST_ID_  = (
        SELECT
            MAX(ID_)
        FROM
            ACT_HI_ACTINST HAI
        WHERE
            HAI.EXECUTION_ID_ = E.ID_
        AND
            HAI.END_TIME_ is null
    )
WHERE
    E.ACT_INST_ID_ is null
AND
    E.ACT_ID_ is not null;/** remaining executions use execution id as activity instance id */
UPDATE
    ACT_RU_EXECUTION
SET
    ACT_INST_ID_  = ID_
WHERE
    ACT_INST_ID_ is null;/** add SUSPENSION_STATE_ column to task table */
-- alter table ACT_RU_TASK
--    add SUSPENSION_STATE_ integer;UPDATE ACT_RU_TASK T
INNER JOIN ACT_RU_EXECUTION E
  ON T.EXECUTION_ID_ = E.ID_
SET T.SUSPENSION_STATE_ = E.SUSPENSION_STATE_;UPDATE ACT_RU_TASK
SET SUSPENSION_STATE_ = 1
WHERE SUSPENSION_STATE_ is null;/** add authorizations **/create table ACT_RU_AUTHORIZATION (
  ID_ varchar(64) not null,
  REV_ integer not null,
  TYPE_ integer not null,
  GROUP_ID_ varchar(255),
  USER_ID_ varchar(255),
  RESOURCE_TYPE_ integer not null,
  RESOURCE_ID_ varchar(64),
  PERMS_ integer,
  primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;alter table ACT_RU_AUTHORIZATION
    add constraint ACT_UNIQ_AUTH_USER
    unique (USER_ID_,TYPE_,RESOURCE_TYPE_,RESOURCE_ID_);alter table ACT_RU_AUTHORIZATION
    add constraint ACT_UNIQ_AUTH_GROUP
    unique (GROUP_ID_,TYPE_,RESOURCE_TYPE_,RESOURCE_ID_);/** add deployment ID to job table  **/
alter table ACT_RU_JOB
    add DEPLOYMENT_ID_ varchar(64);/** add parent act inst ID */
alter table ACT_HI_ACTINST
    add PARENT_ACT_INST_ID_ varchar(64);-- add new column to historic activity instance table --
alter table ACT_HI_ACTINST
    add ACT_INST_STATE_ integer;-- add follow-up date to tasks --
alter table ACT_RU_TASK
    add FOLLOW_UP_DATE_ datetime;
alter table ACT_HI_TASKINST
    add FOLLOW_UP_DATE_ datetime;-- add JOBDEF table --
create table ACT_RU_JOBDEF (
    ID_ varchar(64) NOT NULL,
    REV_ integer,
    PROC_DEF_ID_ varchar(64) NOT NULL,
    PROC_DEF_KEY_ varchar(255) NOT NULL,
    ACT_ID_ varchar(255) NOT NULL,
    JOB_TYPE_ varchar(255) NOT NULL,
    JOB_CONFIGURATION_ varchar(255),
    SUSPENSION_STATE_ integer,
    primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;-- add new columns to job table --
alter table ACT_RU_JOB
    add PROCESS_DEF_ID_ varchar(64);alter table ACT_RU_JOB
    add PROCESS_DEF_KEY_ varchar(64);alter table ACT_RU_JOB
    add SUSPENSION_STATE_ integer;alter table ACT_RU_JOB
    add JOB_DEF_ID_ varchar(64);-- update job table with values from execution table --UPDATE
    ACT_RU_JOB J
SET
    PROCESS_DEF_ID_  = (
        SELECT
            PI.PROC_DEF_ID_
        FROM
            ACT_RU_EXECUTION PI
        WHERE
            PI.ID_ = J.PROCESS_INSTANCE_ID_
    ),
    SUSPENSION_STATE_  = (
        SELECT
            PI.SUSPENSION_STATE_
        FROM
            ACT_RU_EXECUTION PI
        WHERE
            PI.ID_ = J.PROCESS_INSTANCE_ID_
    );UPDATE
    ACT_RU_JOB J
SET
    PROCESS_DEF_KEY_  = (
        SELECT
            PD.KEY_
        FROM
            ACT_RE_PROCDEF PD
        WHERE
            PD.ID_ = J.PROCESS_DEF_ID_
    );-- add Hist OP Log table --create table ACT_HI_OP_LOG (
    ID_ varchar(64) not null,
    PROC_DEF_ID_ varchar(64),
    PROC_INST_ID_ varchar(64),
    EXECUTION_ID_ varchar(64),
    TASK_ID_ varchar(64),
    USER_ID_ varchar(255),
    TIMESTAMP_ timestamp not null,
    OPERATION_TYPE_ varchar(64),
    OPERATION_ID_ varchar(64),
    ENTITY_TYPE_ varchar(30),
    PROPERTY_ varchar(64),
    ORG_VALUE_ varchar(4000),
    NEW_VALUE_ varchar(4000),
    primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;-- add new column to ACT_HI_VARINST --alter table ACT_HI_VARINST
    add ACT_INST_ID_ varchar(64);alter table ACT_HI_DETAIL
    add VAR_INST_ID_ varchar(64);alter table ACT_HI_TASKINST
    add ACT_INST_ID_ varchar(64);-- set cached entity state to 63 on all executions --UPDATE
    ACT_RU_EXECUTION
SET
    CACHED_ENT_STATE_ = 63;-- add new table ACT_HI_INCIDENT --create table ACT_HI_INCIDENT (
  ID_ varchar(64) not null,
  PROC_DEF_ID_ varchar(64),
  PROC_INST_ID_ varchar(64),
  EXECUTION_ID_ varchar(64),
  CREATE_TIME_ timestamp not null,
  END_TIME_ timestamp null,
  INCIDENT_MSG_ varchar(4000),
  INCIDENT_TYPE_ varchar(255) not null,
  ACTIVITY_ID_ varchar(255),
  CAUSE_INCIDENT_ID_ varchar(64),
  ROOT_CAUSE_INCIDENT_ID_ varchar(64),
  CONFIGURATION_ varchar(255),
  INCIDENT_STATE_ integer,
  primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;-- update ACT_RU_VARIABLE table ---- add new column --ALTER TABLE ACT_RU_VARIABLE
    add VAR_SCOPE_ varchar(64);-- migrate execution variables --UPDATE
  ACT_RU_VARIABLE V
​
SET
  VAR_SCOPE_ = V.EXECUTION_ID_
​
WHERE
  V.EXECUTION_ID_ is not null AND
  V.TASK_ID_ is null;-- migrate task variables --UPDATE
  ACT_RU_VARIABLE V
​
SET
  VAR_SCOPE_ = V.TASK_ID_
​
WHERE
  V.TASK_ID_ is not null;-- set VAR_SCOPE_ not null --ALTER TABLE ACT_RU_VARIABLE
    MODIFY VAR_SCOPE_ varchar(64) NOT NULL;-- add unique constraint --
ALTER TABLE ACT_RU_VARIABLE
    ADD CONSTRAINT ACT_UNIQ_VARIABLE
    UNIQUE (VAR_SCOPE_, NAME_);
-- indexes for deadlock problems - https://app.camunda.com/jira/browse/CAM-2567 --
create index ACT_IDX_INC_CAUSEINCID on ACT_RU_INCIDENT(CAUSE_INCIDENT_ID_);
create index ACT_IDX_INC_EXID on ACT_RU_INCIDENT(EXECUTION_ID_);
create index ACT_IDX_INC_PROCDEFID on ACT_RU_INCIDENT(PROC_DEF_ID_);
create index ACT_IDX_INC_PROCINSTID on ACT_RU_INCIDENT(PROC_INST_ID_);
create index ACT_IDX_INC_ROOTCAUSEINCID on ACT_RU_INCIDENT(ROOT_CAUSE_INCIDENT_ID_);-- add deployment.lock row to property table --
INSERT INTO ACT_GE_PROPERTY
  VALUES ('deployment.lock', '0', 1);-- add revision column to incident table --
ALTER TABLE ACT_RU_INCIDENT
  ADD REV_ INTEGER;-- set initial incident revision to 1 --
UPDATE
  ACT_RU_INCIDENT
SET
  REV_ = 1;-- set incident revision column to not null --
ALTER TABLE ACT_RU_INCIDENT
  MODIFY REV_ INTEGER NOT NULL;-- case managementALTER TABLE ACT_RU_VARIABLE
  ADD CASE_EXECUTION_ID_ varchar(64);ALTER TABLE ACT_RU_VARIABLE
  ADD CASE_INST_ID_ varchar(64);ALTER TABLE ACT_RU_TASK
  ADD CASE_EXECUTION_ID_ varchar(64);ALTER TABLE ACT_RU_TASK
  ADD CASE_INST_ID_ varchar(64);ALTER TABLE ACT_RU_TASK
  ADD CASE_DEF_ID_ varchar(64);ALTER TABLE ACT_RU_EXECUTION
  ADD SUPER_CASE_EXEC_ varchar(64);ALTER TABLE ACT_RU_EXECUTION
  ADD CASE_INST_ID_ varchar(64);ALTER TABLE ACT_HI_OP_LOG
  ADD CASE_EXECUTION_ID_ varchar(64);ALTER TABLE ACT_HI_OP_LOG
  ADD CASE_INST_ID_ varchar(64);ALTER TABLE ACT_HI_OP_LOG
  ADD CASE_DEF_ID_ varchar(64);ALTER TABLE ACT_HI_OP_LOG
  ADD PROC_DEF_KEY_ varchar(255);ALTER TABLE ACT_HI_PROCINST
  ADD CASE_INST_ID_ varchar(64);ALTER TABLE ACT_HI_TASKINST
  ADD CASE_EXECUTION_ID_ varchar(64);ALTER TABLE ACT_HI_TASKINST
  ADD CASE_INST_ID_ varchar(64);ALTER TABLE ACT_HI_TASKINST
  ADD CASE_DEF_ID_ varchar(64);-- create case definition table --
create table ACT_RE_CASE_DEF (
    ID_ varchar(64) not null,
    REV_ integer,
    CATEGORY_ varchar(255),
    NAME_ varchar(255),
    KEY_ varchar(255) not null,
    VERSION_ integer not null,
    DEPLOYMENT_ID_ varchar(64),
    RESOURCE_NAME_ varchar(4000),
    DGRM_RESOURCE_NAME_ varchar(4000),
    primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;-- create case execution table --
create table ACT_RU_CASE_EXECUTION (
    ID_ varchar(64) NOT NULL,
    REV_ integer,
    CASE_INST_ID_ varchar(64),
    SUPER_CASE_EXEC_ varchar(64),
    BUSINESS_KEY_ varchar(255),
    PARENT_ID_ varchar(64),
    CASE_DEF_ID_ varchar(64),
    ACT_ID_ varchar(255),
    PREV_STATE_ integer,
    CURRENT_STATE_ integer,
    primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;-- create case sentry part table --create table ACT_RU_CASE_SENTRY_PART (
    ID_ varchar(64) NOT NULL,
    REV_ integer,
    CASE_INST_ID_ varchar(64),
    CASE_EXEC_ID_ varchar(64),
    SENTRY_ID_ varchar(255),
    TYPE_ varchar(255),
    SOURCE_CASE_EXEC_ID_ varchar(64),
    STANDARD_EVENT_ varchar(255),
    SATISFIED_ boolean,
    primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;-- create unique constraint on ACT_RE_CASE_DEF --
alter table ACT_RE_CASE_DEF
    add constraint ACT_UNIQ_CASE_DEF
    unique (KEY_,VERSION_);-- create index on business key --
create index ACT_IDX_CASE_EXEC_BUSKEY on ACT_RU_CASE_EXECUTION(BUSINESS_KEY_);-- create foreign key constraints on ACT_RU_CASE_EXECUTION --
alter table ACT_RU_CASE_EXECUTION
    add constraint ACT_FK_CASE_EXE_CASE_INST
    foreign key (CASE_INST_ID_)
    references ACT_RU_CASE_EXECUTION(ID_) on delete cascade on update cascade;alter table ACT_RU_CASE_EXECUTION
    add constraint ACT_FK_CASE_EXE_PARENT
    foreign key (PARENT_ID_)
    references ACT_RU_CASE_EXECUTION(ID_);alter table ACT_RU_CASE_EXECUTION
    add constraint ACT_FK_CASE_EXE_CASE_DEF
    foreign key (CASE_DEF_ID_)
    references ACT_RE_CASE_DEF(ID_);-- create foreign key constraints on ACT_RU_VARIABLE --
alter table ACT_RU_VARIABLE
    add constraint ACT_FK_VAR_CASE_EXE
    foreign key (CASE_EXECUTION_ID_)
    references ACT_RU_CASE_EXECUTION(ID_);alter table ACT_RU_VARIABLE
    add constraint ACT_FK_VAR_CASE_INST
    foreign key (CASE_INST_ID_)
    references ACT_RU_CASE_EXECUTION(ID_);-- create foreign key constraints on ACT_RU_TASK --
alter table ACT_RU_TASK
    add constraint ACT_FK_TASK_CASE_EXE
    foreign key (CASE_EXECUTION_ID_)
    references ACT_RU_CASE_EXECUTION(ID_);alter table ACT_RU_TASK
  add constraint ACT_FK_TASK_CASE_DEF
  foreign key (CASE_DEF_ID_)
  references ACT_RE_CASE_DEF(ID_);-- create foreign key constraints on ACT_RU_CASE_SENTRY_PART --
alter table ACT_RU_CASE_SENTRY_PART
    add constraint ACT_FK_CASE_SENTRY_CASE_INST
    foreign key (CASE_INST_ID_)
    references ACT_RU_CASE_EXECUTION(ID_);alter table ACT_RU_CASE_SENTRY_PART
    add constraint ACT_FK_CASE_SENTRY_CASE_EXEC
    foreign key (CASE_EXEC_ID_)
    references ACT_RU_CASE_EXECUTION(ID_);-- create filter table
create table ACT_RU_FILTER (
  ID_ varchar(64) not null,
  REV_ integer not null,
  RESOURCE_TYPE_ varchar(255) not null,
  NAME_ varchar(255) not null,
  OWNER_ varchar(255),
  QUERY_ LONGTEXT not null,
  PROPERTIES_ LONGTEXT,
  primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;-- add index to improve job executor performance
create index ACT_IDX_JOB_PROCINST on ACT_RU_JOB(PROCESS_INSTANCE_ID_);-- create historic case instance/activity table and indexes --
create table ACT_HI_CASEINST (
    ID_ varchar(64) not null,
    CASE_INST_ID_ varchar(64) not null,
    BUSINESS_KEY_ varchar(255),
    CASE_DEF_ID_ varchar(64) not null,
    CREATE_TIME_ datetime not null,
    CLOSE_TIME_ datetime,
    DURATION_ bigint,
    STATE_ integer,
    CREATE_USER_ID_ varchar(255),
    SUPER_CASE_INSTANCE_ID_ varchar(64),
    primary key (ID_),
    unique (CASE_INST_ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;create table ACT_HI_CASEACTINST (
    ID_ varchar(64) not null,
    PARENT_ACT_INST_ID_ varchar(64),
    CASE_DEF_ID_ varchar(64) not null,
    CASE_INST_ID_ varchar(64) not null,
    CASE_ACT_ID_ varchar(255) not null,
    TASK_ID_ varchar(64),
    CALL_PROC_INST_ID_ varchar(64),
    CALL_CASE_INST_ID_ varchar(64),
    CASE_ACT_NAME_ varchar(255),
    CASE_ACT_TYPE_ varchar(255),
    CREATE_TIME_ datetime not null,
    END_TIME_ datetime,
    DURATION_ bigint,
    STATE_ integer,
    primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;create index ACT_IDX_HI_CAS_I_CLOSE on ACT_HI_CASEINST(CLOSE_TIME_);
create index ACT_IDX_HI_CAS_I_BUSKEY on ACT_HI_CASEINST(BUSINESS_KEY_);
create index ACT_IDX_HI_CAS_A_I_CREATE on ACT_HI_CASEACTINST(CREATE_TIME_);
create index ACT_IDX_HI_CAS_A_I_END on ACT_HI_CASEACTINST(END_TIME_);
create index ACT_IDX_HI_CAS_A_I_COMP on ACT_HI_CASEACTINST(CASE_ACT_ID_, END_TIME_, ID_);
create index ACT_IDX_HI_CAS_A_I_CASEINST on ACT_HI_CASEACTINST(CASE_INST_ID_, CASE_ACT_ID_);create index ACT_IDX_TASK_ASSIGNEE on ACT_RU_TASK(ASSIGNEE_);-- add case instance/execution to historic variable instance and detail --
alter table ACT_HI_VARINST
  add CASE_INST_ID_ varchar(64);alter table ACT_HI_VARINST
  add CASE_EXECUTION_ID_ varchar(64);alter table ACT_HI_DETAIL
  add CASE_INST_ID_ varchar(64);alter table ACT_HI_DETAIL
  add CASE_EXECUTION_ID_ varchar(64);create index ACT_IDX_HI_DETAIL_CASE_INST on ACT_HI_DETAIL(CASE_INST_ID_);
create index ACT_IDX_HI_DETAIL_CASE_EXEC on ACT_HI_DETAIL(CASE_EXECUTION_ID_);
create index ACT_IDX_HI_CASEVAR_CASE_INST on ACT_HI_VARINST(CASE_INST_ID_);-- indexes to improve deployment
-- create index ACT_IDX_BYTEARRAY_NAME on ACT_GE_BYTEARRAY(NAME_);
-- create index ACT_IDX_DEPLOYMENT_NAME on ACT_RE_DEPLOYMENT(NAME_);
-- create index ACT_IDX_JOBDEF_PROC_DEF_ID ON ACT_RU_JOBDEF(PROC_DEF_ID_);
-- create index ACT_IDX_JOB_HANDLER_TYPE ON ACT_RU_JOB(HANDLER_TYPE_);
-- create index ACT_IDX_EVENT_SUBSCR_EVT_NAME ON ACT_RU_EVENT_SUBSCR(EVENT_NAME_);
-- create index ACT_IDX_PROCDEF_DEPLOYMENT_ID ON ACT_RE_PROCDEF(DEPLOYMENT_ID_);-- case management --ALTER TABLE ACT_RU_CASE_EXECUTION
  ADD SUPER_EXEC_ varchar(64);ALTER TABLE ACT_RU_CASE_EXECUTION
  ADD REQUIRED_ boolean;-- history --ALTER TABLE ACT_HI_ACTINST
  ADD CALL_CASE_INST_ID_ varchar(64);ALTER TABLE ACT_HI_PROCINST
  ADD SUPER_CASE_INSTANCE_ID_ varchar(64);ALTER TABLE ACT_HI_CASEINST
  ADD SUPER_PROCESS_INSTANCE_ID_ varchar(64);ALTER TABLE ACT_HI_CASEACTINST
  ADD REQUIRED_ boolean;ALTER TABLE ACT_HI_OP_LOG
  ADD JOB_ID_ varchar(64);ALTER TABLE ACT_HI_OP_LOG
  ADD JOB_DEF_ID_ varchar(64);create table ACT_HI_JOB_LOG (
    ID_ varchar(64) not null,
    TIMESTAMP_ timestamp not null,
    JOB_ID_ varchar(64) not null,
    JOB_DUEDATE_ timestamp NULL,
    JOB_RETRIES_ integer,
    JOB_EXCEPTION_MSG_ varchar(4000),
    JOB_EXCEPTION_STACK_ID_ varchar(64),
    JOB_STATE_ integer,
    JOB_DEF_ID_ varchar(64),
    JOB_DEF_TYPE_ varchar(255),
    JOB_DEF_CONFIGURATION_ varchar(255),
    ACT_ID_ varchar(64),
    EXECUTION_ID_ varchar(64),
    PROCESS_INSTANCE_ID_ varchar(64),
    PROCESS_DEF_ID_ varchar(64),
    PROCESS_DEF_KEY_ varchar(255),
    DEPLOYMENT_ID_ varchar(64),
    SEQUENCE_COUNTER_ bigint,
    primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;create index ACT_IDX_HI_JOB_LOG_PROCINST on ACT_HI_JOB_LOG(PROCESS_INSTANCE_ID_);
create index ACT_IDX_HI_JOB_LOG_PROCDEF on ACT_HI_JOB_LOG(PROCESS_DEF_ID_);-- history: add columns PROC_DEF_KEY_, PROC_DEF_ID_, CASE_DEF_KEY_, CASE_DEF_ID_ --ALTER TABLE ACT_HI_PROCINST
  ADD PROC_DEF_KEY_ varchar(255);ALTER TABLE ACT_HI_ACTINST
  ADD PROC_DEF_KEY_ varchar(255);ALTER TABLE ACT_HI_TASKINST
  ADD PROC_DEF_KEY_ varchar(255);ALTER TABLE ACT_HI_TASKINST
  ADD CASE_DEF_KEY_ varchar(255);ALTER TABLE ACT_HI_VARINST
  ADD PROC_DEF_KEY_ varchar(255);ALTER TABLE ACT_HI_VARINST
  ADD PROC_DEF_ID_ varchar(64);ALTER TABLE ACT_HI_VARINST
  ADD CASE_DEF_KEY_ varchar(255);ALTER TABLE ACT_HI_VARINST
  ADD CASE_DEF_ID_ varchar(64);ALTER TABLE ACT_HI_DETAIL
  ADD PROC_DEF_KEY_ varchar(255);ALTER TABLE ACT_HI_DETAIL
  ADD PROC_DEF_ID_ varchar(64);ALTER TABLE ACT_HI_DETAIL
  ADD CASE_DEF_KEY_ varchar(255);ALTER TABLE ACT_HI_DETAIL
  ADD CASE_DEF_ID_ varchar(64);ALTER TABLE ACT_HI_INCIDENT
  ADD PROC_DEF_KEY_ varchar(255);-- sequence counterALTER TABLE ACT_RU_EXECUTION
  ADD SEQUENCE_COUNTER_ bigint;ALTER TABLE ACT_HI_ACTINST
  ADD SEQUENCE_COUNTER_ bigint;ALTER TABLE ACT_RU_VARIABLE
  ADD SEQUENCE_COUNTER_ bigint;ALTER TABLE ACT_HI_DETAIL
  ADD SEQUENCE_COUNTER_ bigint;ALTER TABLE ACT_RU_JOB
  ADD SEQUENCE_COUNTER_ bigint;-- AUTHORIZATION ---- add grant authorizations for group camunda-admin:
INSERT INTO
  ACT_RU_AUTHORIZATION (ID_, TYPE_, GROUP_ID_, RESOURCE_TYPE_, RESOURCE_ID_, PERMS_, REV_)
VALUES
  ('camunda-admin-grant-process-definition', 1, 'camunda-admin', 6, '*', 2147483647, 1),
  ('camunda-admin-grant-task', 1, 'camunda-admin', 7, '*', 2147483647, 1),
  ('camunda-admin-grant-process-instance', 1, 'camunda-admin', 8, '*', 2147483647, 1),
  ('camunda-admin-grant-deployment', 1, 'camunda-admin', 9, '*', 2147483647, 1);-- add global grant authorizations for new authorization resources:
-- DEPLOYMENT
-- PROCESS_DEFINITION
-- PROCESS_INSTANCE
-- TASK
-- with ALL permissionsINSERT INTO
  ACT_RU_AUTHORIZATION (ID_, TYPE_, USER_ID_, RESOURCE_TYPE_, RESOURCE_ID_, PERMS_, REV_)
VALUES
  ('global-grant-process-definition', 0, '*', 6, '*', 2147483647, 1),
  ('global-grant-task', 0, '*', 7, '*', 2147483647, 1),
  ('global-grant-process-instance', 0, '*', 8, '*', 2147483647, 1),
  ('global-grant-deployment', 0, '*', 9, '*', 2147483647, 1);-- variables --ALTER TABLE ACT_RU_VARIABLE
  ADD IS_CONCURRENT_LOCAL_ TINYINT;-- metrics --create table ACT_RU_METER_LOG (
  ID_ varchar(64) not null,
  NAME_ varchar(64) not null,
  VALUE_ bigint,
  TIMESTAMP_ timestamp not null,
  primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;create index ACT_IDX_METER_LOG on ACT_RU_METER_LOG(NAME_,TIMESTAMP_);
alter table ACT_HI_JOB_LOG
  modify ACT_ID_ varchar(255);
-- index for deadlock problem - https://app.camunda.com/jira/browse/CAM-4440 --
create index ACT_IDX_AUTH_RESOURCE_ID on ACT_RU_AUTHORIZATION(RESOURCE_ID_);
-- indexes to improve deployment
create index ACT_IDX_BYTEARRAY_NAME on ACT_GE_BYTEARRAY(NAME_);
create index ACT_IDX_DEPLOYMENT_NAME on ACT_RE_DEPLOYMENT(NAME_);
create index ACT_IDX_JOBDEF_PROC_DEF_ID ON ACT_RU_JOBDEF(PROC_DEF_ID_);
create index ACT_IDX_JOB_HANDLER_TYPE ON ACT_RU_JOB(HANDLER_TYPE_);
create index ACT_IDX_EVENT_SUBSCR_EVT_NAME ON ACT_RU_EVENT_SUBSCR(EVENT_NAME_);
create index ACT_IDX_PROCDEF_DEPLOYMENT_ID ON ACT_RE_PROCDEF(DEPLOYMENT_ID_);
-- INCREASE process def key column size https://app.camunda.com/jira/browse/CAM-4328 --
alter table ACT_RU_JOB
  modify PROCESS_DEF_KEY_ varchar(255);-- https://app.camunda.com/jira/browse/CAM-5364 --
create index ACT_IDX_AUTH_GROUP_ID on ACT_RU_AUTHORIZATION(GROUP_ID_);
-- metrics --ALTER TABLE ACT_RU_METER_LOG
  ADD REPORTER_ varchar(255);-- job prioritization --ALTER TABLE ACT_RU_JOB
  ADD PRIORITY_ bigint NOT NULL
  DEFAULT 0;ALTER TABLE ACT_RU_JOBDEF
  ADD JOB_PRIORITY_ bigint;ALTER TABLE ACT_HI_JOB_LOG
  ADD JOB_PRIORITY_ bigint NOT NULL
  DEFAULT 0;-- create decision definition table --
create table ACT_RE_DECISION_DEF (
    ID_ varchar(64) not null,
    REV_ integer,
    CATEGORY_ varchar(255),
    NAME_ varchar(255),
    KEY_ varchar(255) not null,
    VERSION_ integer not null,
    DEPLOYMENT_ID_ varchar(64),
    RESOURCE_NAME_ varchar(4000),
    DGRM_RESOURCE_NAME_ varchar(4000),
    primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;-- create unique constraint on ACT_RE_DECISION_DEF --
alter table ACT_RE_DECISION_DEF
    add constraint ACT_UNIQ_DECISION_DEF
    unique (KEY_,VERSION_);-- case sentry part source --ALTER TABLE ACT_RU_CASE_SENTRY_PART
  ADD SOURCE_ varchar(255);-- create history decision instance table --
create table ACT_HI_DECINST (
    ID_ varchar(64) NOT NULL,
    DEC_DEF_ID_ varchar(64) NOT NULL,
    DEC_DEF_KEY_ varchar(255) NOT NULL,
    DEC_DEF_NAME_ varchar(255),
    PROC_DEF_KEY_ varchar(255),
    PROC_DEF_ID_ varchar(64),
    PROC_INST_ID_ varchar(64),
    CASE_DEF_KEY_ varchar(255),
    CASE_DEF_ID_ varchar(64),
    CASE_INST_ID_ varchar(64),
    ACT_INST_ID_ varchar(64),
    ACT_ID_ varchar(255),
    EVAL_TIME_ datetime not null,
    COLLECT_VALUE_ double,
    primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;-- create history decision input table --
create table ACT_HI_DEC_IN (
    ID_ varchar(64) NOT NULL,
    DEC_INST_ID_ varchar(64) NOT NULL,
    CLAUSE_ID_ varchar(64) NOT NULL,
    CLAUSE_NAME_ varchar(255),
    VAR_TYPE_ varchar(100),
    BYTEARRAY_ID_ varchar(64),
    DOUBLE_ double,
    LONG_ bigint,
    TEXT_ varchar(4000),
    TEXT2_ varchar(4000),
    primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;-- create history decision output table --
create table ACT_HI_DEC_OUT (
    ID_ varchar(64) NOT NULL,
    DEC_INST_ID_ varchar(64) NOT NULL,
    CLAUSE_ID_ varchar(64) NOT NULL,
    CLAUSE_NAME_ varchar(255),
    RULE_ID_ varchar(64) NOT NULL,
    RULE_ORDER_ integer,
    VAR_NAME_ varchar(255),
    VAR_TYPE_ varchar(100),
    BYTEARRAY_ID_ varchar(64),
    DOUBLE_ double,
    LONG_ bigint,
    TEXT_ varchar(4000),
    TEXT2_ varchar(4000),
    primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;-- create indexes for historic decision tables
create index ACT_IDX_HI_DEC_INST_ID on ACT_HI_DECINST(DEC_DEF_ID_);
create index ACT_IDX_HI_DEC_INST_KEY on ACT_HI_DECINST(DEC_DEF_KEY_);
create index ACT_IDX_HI_DEC_INST_PI on ACT_HI_DECINST(PROC_INST_ID_);
create index ACT_IDX_HI_DEC_INST_CI on ACT_HI_DECINST(CASE_INST_ID_);
create index ACT_IDX_HI_DEC_INST_ACT on ACT_HI_DECINST(ACT_ID_);
create index ACT_IDX_HI_DEC_INST_ACT_INST on ACT_HI_DECINST(ACT_INST_ID_);
create index ACT_IDX_HI_DEC_INST_TIME on ACT_HI_DECINST(EVAL_TIME_);create index ACT_IDX_HI_DEC_IN_INST on ACT_HI_DEC_IN(DEC_INST_ID_);
create index ACT_IDX_HI_DEC_IN_CLAUSE on ACT_HI_DEC_IN(DEC_INST_ID_, CLAUSE_ID_);create index ACT_IDX_HI_DEC_OUT_INST on ACT_HI_DEC_OUT(DEC_INST_ID_);
create index ACT_IDX_HI_DEC_OUT_RULE on ACT_HI_DEC_OUT(RULE_ORDER_, CLAUSE_ID_);-- add grant authorization for group camunda-admin:
INSERT INTO
  ACT_RU_AUTHORIZATION (ID_, TYPE_, GROUP_ID_, RESOURCE_TYPE_, RESOURCE_ID_, PERMS_, REV_)
VALUES
  ('camunda-admin-grant-decision-definition', 1, 'camunda-admin', 10, '*', 2147483647, 1);-- external tasks --create table ACT_RU_EXT_TASK (
  ID_ varchar(64) not null,
  REV_ integer not null,
  WORKER_ID_ varchar(255),
  TOPIC_NAME_ varchar(255),
  RETRIES_ integer,
  ERROR_MSG_ varchar(4000),
  LOCK_EXP_TIME_ timestamp NULL,
  SUSPENSION_STATE_ integer,
  EXECUTION_ID_ varchar(64),
  PROC_INST_ID_ varchar(64),
  PROC_DEF_ID_ varchar(64),
  PROC_DEF_KEY_ varchar(255),
  ACT_ID_ varchar(255),
  ACT_INST_ID_ varchar(64),
  primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;alter table ACT_RU_EXT_TASK
    add constraint ACT_FK_EXT_TASK_EXE
    foreign key (EXECUTION_ID_)
    references ACT_RU_EXECUTION (ID_);create index ACT_IDX_EXT_TASK_TOPIC on ACT_RU_EXT_TASK(TOPIC_NAME_);-- deployment --ALTER TABLE ACT_RE_DEPLOYMENT
  ADD SOURCE_ varchar(255);ALTER TABLE ACT_HI_OP_LOG
  ADD DEPLOYMENT_ID_ varchar(64);-- job suspension stateALTER TABLE ACT_RU_JOB
  MODIFY COLUMN SUSPENSION_STATE_ integer
  DEFAULT 1;-- relevant for jobs created in Camunda 7.0
UPDATE ACT_RU_JOB
  SET SUSPENSION_STATE_ = 1
  WHERE SUSPENSION_STATE_ IS NULL;ALTER TABLE ACT_RU_JOB
  MODIFY COLUMN SUSPENSION_STATE_ integer
  NOT NULL DEFAULT 1;
-- index to improve historic activity instance query - https://app.camunda.com/jira/browse/CAM-5257 --
create index ACT_IDX_HI_ACT_INST_STATS on ACT_HI_ACTINST(PROC_DEF_ID_, ACT_ID_, END_TIME_, ACT_INST_STATE_);
-- index to prevent deadlock on fk constraint - https://app.camunda.com/jira/browse/CAM-5440 --
create index ACT_IDX_EXT_TASK_EXEC on ACT_RU_EXT_TASK(EXECUTION_ID_);
-- https://app.camunda.com/jira/browse/CAM-5364 --
-- create index ACT_IDX_AUTH_GROUP_ID on ACT_RU_AUTHORIZATION(GROUP_ID_);
-- INCREASE process def key column size https://app.camunda.com/jira/browse/CAM-4328 --
alter table ACT_RU_JOB
  modify PROCESS_DEF_KEY_ varchar(255);-- semantic version --ALTER TABLE ACT_RE_PROCDEF
  ADD VERSION_TAG_ varchar(64);create index ACT_IDX_PROCDEF_VER_TAG on ACT_RE_PROCDEF(VERSION_TAG_);-- AUTHORIZATION ---- add grant authorizations for group camunda-admin:
INSERT INTO
  ACT_RU_AUTHORIZATION (ID_, TYPE_, GROUP_ID_, RESOURCE_TYPE_, RESOURCE_ID_, PERMS_, REV_)
VALUES
  ('camunda-admin-grant-tenant', 1, 'camunda-admin', 11, '*', 2147483647, 1),
  ('camunda-admin-grant-tenant-membership', 1, 'camunda-admin', 12, '*', 2147483647, 1),
  ('camunda-admin-grant-batch', 1, 'camunda-admin', 13, '*', 2147483647, 1);-- tenant id ---- ALTER TABLE ACT_RE_DEPLOYMENT
--  ADD TENANT_ID_ varchar(64);create index ACT_IDX_DEPLOYMENT_TENANT_ID on ACT_RE_DEPLOYMENT(TENANT_ID_);-- ALTER TABLE ACT_RE_PROCDEF
--  ADD TENANT_ID_ varchar(64);ALTER TABLE ACT_RE_PROCDEF
   DROP INDEX ACT_UNIQ_PROCDEF;create index ACT_IDX_PROCDEF_TENANT_ID ON ACT_RE_PROCDEF(TENANT_ID_);-- ALTER TABLE ACT_RU_EXECUTION
--  ADD TENANT_ID_ varchar(64);create index ACT_IDX_EXEC_TENANT_ID on ACT_RU_EXECUTION(TENANT_ID_);-- ALTER TABLE ACT_RU_TASK
--  ADD TENANT_ID_ varchar(64);create index ACT_IDX_TASK_TENANT_ID on ACT_RU_TASK(TENANT_ID_);ALTER TABLE ACT_RU_VARIABLE
  ADD TENANT_ID_ varchar(64);create index ACT_IDX_VARIABLE_TENANT_ID on ACT_RU_VARIABLE(TENANT_ID_);-- ALTER TABLE ACT_RU_EVENT_SUBSCR
--  ADD TENANT_ID_ varchar(64);create index ACT_IDX_EVENT_SUBSCR_TENANT_ID on ACT_RU_EVENT_SUBSCR(TENANT_ID_);-- ALTER TABLE ACT_RU_JOB
--  ADD TENANT_ID_ varchar(64);create index ACT_IDX_JOB_TENANT_ID on ACT_RU_JOB(TENANT_ID_);ALTER TABLE ACT_RU_JOBDEF
  ADD TENANT_ID_ varchar(64);create index ACT_IDX_JOBDEF_TENANT_ID on ACT_RU_JOBDEF(TENANT_ID_);ALTER TABLE ACT_RU_INCIDENT
  ADD TENANT_ID_ varchar(64);ALTER TABLE ACT_RU_IDENTITYLINK
  ADD TENANT_ID_ varchar(64);create index ACT_IDX_INC_TENANT_ID on ACT_RU_INCIDENT(TENANT_ID_);ALTER TABLE ACT_RU_EXT_TASK
  ADD TENANT_ID_ varchar(64);create index ACT_IDX_EXT_TASK_TENANT_ID on ACT_RU_EXT_TASK(TENANT_ID_);ALTER TABLE ACT_RE_DECISION_DEF
       DROP INDEX ACT_UNIQ_DECISION_DEF;ALTER TABLE ACT_RE_DECISION_DEF
  ADD TENANT_ID_ varchar(64);create index ACT_IDX_DEC_DEF_TENANT_ID on ACT_RE_DECISION_DEF(TENANT_ID_);ALTER TABLE ACT_RE_CASE_DEF
       DROP INDEX ACT_UNIQ_CASE_DEF;ALTER TABLE ACT_RE_CASE_DEF
  ADD TENANT_ID_ varchar(64);create index ACT_IDX_CASE_DEF_TENANT_ID on ACT_RE_CASE_DEF(TENANT_ID_);ALTER TABLE ACT_GE_BYTEARRAY
  ADD TENANT_ID_ varchar(64);ALTER TABLE ACT_RU_CASE_EXECUTION
  ADD TENANT_ID_ varchar(64);create index ACT_IDX_CASE_EXEC_TENANT_ID on ACT_RU_CASE_EXECUTION(TENANT_ID_);ALTER TABLE ACT_RU_CASE_SENTRY_PART
  ADD TENANT_ID_ varchar(64);-- user on historic decision instance --ALTER TABLE ACT_HI_DECINST
  ADD USER_ID_ varchar(255);-- tenant id on history ---- ALTER TABLE ACT_HI_PROCINST
--  ADD TENANT_ID_ varchar(64);create index ACT_IDX_HI_PRO_INST_TENANT_ID on ACT_HI_PROCINST(TENANT_ID_);-- ALTER TABLE ACT_HI_ACTINST
--  ADD TENANT_ID_ varchar(64);create index ACT_IDX_HI_ACT_INST_TENANT_ID on ACT_HI_ACTINST(TENANT_ID_);-- ALTER TABLE ACT_HI_TASKINST
--  ADD TENANT_ID_ varchar(64);create index ACT_IDX_HI_TASK_INST_TENANT_ID on ACT_HI_TASKINST(TENANT_ID_);ALTER TABLE ACT_HI_VARINST
  ADD TENANT_ID_ varchar(64);create index ACT_IDX_HI_VAR_INST_TENANT_ID on ACT_HI_VARINST(TENANT_ID_);ALTER TABLE ACT_HI_DETAIL
  ADD TENANT_ID_ varchar(64);create index ACT_IDX_HI_DETAIL_TENANT_ID on ACT_HI_DETAIL(TENANT_ID_);ALTER TABLE ACT_HI_INCIDENT
  ADD TENANT_ID_ varchar(64);create index ACT_IDX_HI_INCIDENT_TENANT_ID on ACT_HI_INCIDENT(TENANT_ID_);ALTER TABLE ACT_HI_JOB_LOG
  ADD TENANT_ID_ varchar(64);create index ACT_IDX_HI_JOB_LOG_TENANT_ID on ACT_HI_JOB_LOG(TENANT_ID_);ALTER TABLE ACT_HI_COMMENT
  ADD TENANT_ID_ varchar(64);ALTER TABLE ACT_HI_ATTACHMENT
  ADD TENANT_ID_ varchar(64);ALTER TABLE ACT_HI_OP_LOG
  ADD TENANT_ID_ varchar(64);ALTER TABLE ACT_HI_DEC_IN
  ADD TENANT_ID_ varchar(64);ALTER TABLE ACT_HI_DEC_OUT
  ADD TENANT_ID_ varchar(64);ALTER TABLE ACT_HI_DECINST
  ADD TENANT_ID_ varchar(64);create index ACT_IDX_HI_DEC_INST_TENANT_ID on ACT_HI_DECINST(TENANT_ID_);ALTER TABLE ACT_HI_CASEINST
  ADD TENANT_ID_ varchar(64);create index ACT_IDX_HI_CAS_I_TENANT_ID on ACT_HI_CASEINST(TENANT_ID_);ALTER TABLE ACT_HI_CASEACTINST
  ADD TENANT_ID_ varchar(64);create index ACT_IDX_HI_CAS_A_I_TENANT_ID on ACT_HI_CASEACTINST(TENANT_ID_);-- add tenant tablecreate table ACT_ID_TENANT (
    ID_ varchar(64),
    REV_ integer,
    NAME_ varchar(255),
    primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;create table ACT_ID_TENANT_MEMBER (
    ID_ varchar(64) not null,
    TENANT_ID_ varchar(64) not null,
    USER_ID_ varchar(64),
    GROUP_ID_ varchar(64),
    primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;alter table ACT_ID_TENANT_MEMBER
    add constraint ACT_UNIQ_TENANT_MEMB_USER
    unique (TENANT_ID_, USER_ID_);alter table ACT_ID_TENANT_MEMBER
    add constraint ACT_UNIQ_TENANT_MEMB_GROUP
    unique (TENANT_ID_, GROUP_ID_);alter table ACT_ID_TENANT_MEMBER
    add constraint ACT_FK_TENANT_MEMB
    foreign key (TENANT_ID_)
    references ACT_ID_TENANT (ID_);alter table ACT_ID_TENANT_MEMBER
    add constraint ACT_FK_TENANT_MEMB_USER
    foreign key (USER_ID_)
    references ACT_ID_USER (ID_);alter table ACT_ID_TENANT_MEMBER
    add constraint ACT_FK_TENANT_MEMB_GROUP
    foreign key (GROUP_ID_)
    references ACT_ID_GROUP (ID_);--  BATCH ---- remove not null from job definition table --
alter table ACT_RU_JOBDEF
  modify PROC_DEF_ID_ varchar(64),
  modify PROC_DEF_KEY_ varchar(255),
  modify ACT_ID_ varchar(255);create table ACT_RU_BATCH (
  ID_ varchar(64) not null,
  REV_ integer not null,
  TYPE_ varchar(255),
  TOTAL_JOBS_ integer,
  JOBS_CREATED_ integer,
  JOBS_PER_SEED_ integer,
  INVOCATIONS_PER_JOB_ integer,
  SEED_JOB_DEF_ID_ varchar(64),
  BATCH_JOB_DEF_ID_ varchar(64),
  MONITOR_JOB_DEF_ID_ varchar(64),
  SUSPENSION_STATE_ integer,
  CONFIGURATION_ varchar(255),
  TENANT_ID_ varchar(64),
  primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;create table ACT_HI_BATCH (
    ID_ varchar(64) not null,
    TYPE_ varchar(255),
    TOTAL_JOBS_ integer,
    JOBS_PER_SEED_ integer,
    INVOCATIONS_PER_JOB_ integer,
    SEED_JOB_DEF_ID_ varchar(64),
    MONITOR_JOB_DEF_ID_ varchar(64),
    BATCH_JOB_DEF_ID_ varchar(64),
    TENANT_ID_  varchar(64),
    START_TIME_ datetime not null,
    END_TIME_ datetime,
    primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;-- create table ACT_HI_IDENTITYLINK (
--    ID_ varchar(64) not null,
--    TIMESTAMP_ timestamp not null,
--    TYPE_ varchar(255),
--    USER_ID_ varchar(255),
--    GROUP_ID_ varchar(255),
--    TASK_ID_ varchar(64),
--    PROC_DEF_ID_ varchar(64),
--    OPERATION_TYPE_ varchar(64),
--    ASSIGNER_ID_ varchar(64),
--    PROC_DEF_KEY_ varchar(255),
--    TENANT_ID_ varchar(64),
--    primary key (ID_)
-- ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;
--
-- create index ACT_IDX_HI_IDENT_LNK_USER on ACT_HI_IDENTITYLINK(USER_ID_);
create index ACT_IDX_HI_IDENT_LNK_GROUP on ACT_HI_IDENTITYLINK(GROUP_ID_);
-- create index ACT_IDX_HI_IDENT_LNK_TENANT_ID on ACT_HI_IDENTITYLINK(TENANT_ID_);create index ACT_IDX_JOB_JOB_DEF_ID on ACT_RU_JOB(JOB_DEF_ID_);
create index ACT_IDX_HI_JOB_LOG_JOB_DEF_ID on ACT_HI_JOB_LOG(JOB_DEF_ID_);create index ACT_IDX_BATCH_SEED_JOB_DEF ON ACT_RU_BATCH(SEED_JOB_DEF_ID_);
alter table ACT_RU_BATCH
    add constraint ACT_FK_BATCH_SEED_JOB_DEF
    foreign key (SEED_JOB_DEF_ID_)
    references ACT_RU_JOBDEF (ID_);create index ACT_IDX_BATCH_MONITOR_JOB_DEF ON ACT_RU_BATCH(MONITOR_JOB_DEF_ID_);
alter table ACT_RU_BATCH
    add constraint ACT_FK_BATCH_MONITOR_JOB_DEF
    foreign key (MONITOR_JOB_DEF_ID_)
    references ACT_RU_JOBDEF (ID_);create index ACT_IDX_BATCH_JOB_DEF ON ACT_RU_BATCH(BATCH_JOB_DEF_ID_);
alter table ACT_RU_BATCH
    add constraint ACT_FK_BATCH_JOB_DEF
    foreign key (BATCH_JOB_DEF_ID_)
    references ACT_RU_JOBDEF (ID_);-- TASK PRIORITY --ALTER TABLE ACT_RU_EXT_TASK
  ADD PRIORITY_ bigint NOT NULL DEFAULT 0;create index ACT_IDX_EXT_TASK_PRIORITY ON ACT_RU_EXT_TASK(PRIORITY_);
​
​
-- HI OP PROC INDECIES --create index ACT_IDX_HI_OP_LOG_PROCINST on ACT_HI_OP_LOG(PROC_INST_ID_);
create index ACT_IDX_HI_OP_LOG_PROCDEF on ACT_HI_OP_LOG(PROC_DEF_ID_);-- JOB_DEF_ID_ on INCIDENTS --
ALTER TABLE ACT_RU_INCIDENT
  ADD JOB_DEF_ID_ varchar(64);create index ACT_IDX_INC_JOB_DEF on ACT_RU_INCIDENT(JOB_DEF_ID_);
alter table ACT_RU_INCIDENT
    add constraint ACT_FK_INC_JOB_DEF
    foreign key (JOB_DEF_ID_)
    references ACT_RU_JOBDEF (ID_);ALTER TABLE ACT_HI_INCIDENT
  ADD JOB_DEF_ID_ varchar(64);-- BATCH_ID_ on ACT_HI_OP_LOG --
ALTER TABLE ACT_HI_OP_LOG
  ADD BATCH_ID_ varchar(64);-- alter table ACT_HI_IDENTITYLINK to match Camundas version, note that some data may not be filled for old instances! 
alter table ACT_HI_IDENTITYLINK add column (
    ASSIGNER_ID_ varchar(64),
    OPERATION_TYPE_ varchar(64),
    PROC_DEF_ID_ varchar(64),
    PROC_DEF_KEY_ varchar(255),
    TENANT_ID_ varchar(64),
    TIMESTAMP_ timestamp);
--    drop PROC_INST_ID_, -- done in drop scriptcreate index ACT_IDX_HI_IDENT_LNK_TENANT_ID on ACT_HI_IDENTITYLINK(TENANT_ID_);-- changes in attributes
alter table ACT_HI_ACTINST      MODIFY COLUMN ASSIGNEE_ varchar(64);alter table ACT_HI_ACTINST      MODIFY COLUMN TENANT_ID_ varchar(64);
alter table ACT_HI_PROCINST     MODIFY COLUMN TENANT_ID_ varchar(64);
alter table ACT_HI_TASKINST     MODIFY COLUMN TENANT_ID_ varchar(64);
alter table ACT_RE_DEPLOYMENT   MODIFY COLUMN TENANT_ID_ varchar(64);
alter table ACT_RE_PROCDEF      MODIFY COLUMN TENANT_ID_ varchar(64);
alter table ACT_RU_EVENT_SUBSCR MODIFY COLUMN TENANT_ID_ varchar(64);
alter table ACT_RU_EXECUTION    MODIFY COLUMN TENANT_ID_ varchar(64);
alter table ACT_RU_JOB          MODIFY COLUMN TENANT_ID_ varchar(64);
alter table ACT_RU_TASK         MODIFY COLUMN TENANT_ID_ varchar(64);-- delete users and groups, as you have to re-create them (hashed passwords, different group types, required authorizations)
delete from ACT_ID_MEMBERSHIP;
delete from ACT_ID_TENANT_MEMBER;
delete from ACT_ID_INFO;
delete from ACT_ID_USER;
delete from ACT_ID_GROUP;-- drop tables not used in Camunda
drop table if exists  ACT_EVT_LOG cascade; 
drop table if exists  ACT_PROCDEF_INFO cascade;
drop table if exists  ACT_RE_MODEL cascade;-- columns not used in camunda                                                               
alter table ACT_HI_IDENTITYLINK DROP COLUMN PROC_INST_ID_;alter table ACT_HI_TASKINST     DROP COLUMN CATEGORY_;
alter table ACT_RE_DEPLOYMENT   DROP COLUMN CATEGORY_;
alter table ACT_RU_TASK         DROP COLUMN CATEGORY_;
alter table ACT_HI_TASKINST     DROP COLUMN CLAIM_TIME_;
alter table ACT_HI_VARINST      DROP COLUMN CREATE_TIME_;
alter table ACT_RE_PROCDEF      DROP COLUMN DESCRIPTION_;
alter table ACT_HI_VARINST      DROP COLUMN LAST_UPDATED_TIME_;
alter table ACT_RU_EXECUTION    DROP COLUMN LOCK_TIME_;
alter table ACT_HI_PROCINST     DROP COLUMN NAME_;
alter table ACT_RU_EXECUTION    DROP COLUMN NAME_;
ALTER TABLE ACT_RU_IDENTITYLINK DROP FOREIGN KEY ACT_FK_IDL_PROCINST;
alter table ACT_RU_IDENTITYLINK DROP COLUMN PROC_INST_ID_;
alter table ACT_RU_EVENT_SUBSCR DROP COLUMN PROC_DEF_ID_;
alter table ACT_RU_JOB          DROP FOREIGN KEY ACT_FK_JOB_PROC_DEF;
alter table ACT_RU_JOB          DROP COLUMN PROC_DEF_ID_;
alter table ACT_HI_ATTACHMENT   DROP COLUMN TIME_;
alter table ACT_HI_TASKINST     DROP COLUMN FORM_KEY_;
alter table ACT_RU_TASK         DROP COLUMN FORM_KEY_;
alter table ACT_RE_PROCDEF      DROP COLUMN HAS_GRAPHICAL_NOTATION_;-- drop indexes not used in Camunda
ALTER TABLE act_hi_actinst drop index ACT_IDX_HI_ACT_INST_EXEC;
ALTER TABLE act_hi_identitylink drop index ACT_IDX_HI_IDENT_LNK_TASK;
ALTER TABLE act_hi_varinst drop index ACT_IDX_HI_PROCVAR_TASK_ID;
ALTER TABLE act_hi_taskinst drop index ACT_IDX_HI_TASK_INST_PROCINST;ALTER TABLE act_hi_actinst
DROP COLUMN DELETE_REASON_,
ADD INDEX ACT_IDX_HI_ACT_INST_COMP (ID_, EXECUTION_ID_, ACT_ID_, END_TIME_) ,
ADD INDEX ACT_IDX_HI_ACT_INST_PROC_DEF_KEY (PROC_DEF_KEY_) ;ALTER TABLE act_hi_attachment
ADD INDEX ACT_IDX_HI_ATTACHMENT_CONTENT (CONTENT_ID_) ,
ADD INDEX ACT_IDX_HI_ATTACHMENT_PROCINST (PROC_INST_ID_) ,
ADD INDEX ACT_IDX_HI_ATTACHMENT_TASK (TASK_ID_) ;ALTER TABLE act_hi_comment
ADD INDEX ACT_IDX_HI_COMMENT_TASK (TASK_ID_) ,
ADD INDEX ACT_IDX_HI_COMMENT_PROCINST (PROC_INST_ID_) ;ALTER TABLE act_hi_decinst
ADD COLUMN ROOT_DEC_INST_ID_  varchar(64) NULL AFTER USER_ID_,
ADD COLUMN DEC_REQ_ID_  varchar(64) NULL AFTER ROOT_DEC_INST_ID_,
ADD COLUMN DEC_REQ_KEY_  varchar(255) NULL AFTER DEC_REQ_ID_,
ADD INDEX ACT_IDX_HI_DEC_INST_ROOT_ID (ROOT_DEC_INST_ID_) ,
ADD INDEX ACT_IDX_HI_DEC_INST_REQ_ID (DEC_REQ_ID_) ,
ADD INDEX ACT_IDX_HI_DEC_INST_REQ_KEY (DEC_REQ_KEY_) ;ALTER TABLE act_hi_detail
ADD COLUMN OPERATION_ID_  varchar(64) NULL AFTER TENANT_ID_,
ADD INDEX ACT_IDX_HI_DETAIL_PROC_DEF_KEY (PROC_DEF_KEY_) ,
ADD INDEX ACT_IDX_HI_DETAIL_BYTEAR (BYTEARRAY_ID_) ;CREATE TABLE act_hi_ext_task_log (
  ID_ varchar(64) COLLATE utf8_bin NOT NULL,
  TIMESTAMP_ timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  EXT_TASK_ID_ varchar(64) COLLATE utf8_bin NOT NULL,
  RETRIES_ int(11) DEFAULT NULL,
  TOPIC_NAME_ varchar(255) COLLATE utf8_bin DEFAULT NULL,
  WORKER_ID_ varchar(255) COLLATE utf8_bin DEFAULT NULL,
  PRIORITY_ bigint(20) NOT NULL DEFAULT '0',
  ERROR_MSG_ varchar(4000) COLLATE utf8_bin DEFAULT NULL,
  ERROR_DETAILS_ID_ varchar(64) COLLATE utf8_bin DEFAULT NULL,
  ACT_ID_ varchar(255) COLLATE utf8_bin DEFAULT NULL,
  ACT_INST_ID_ varchar(64) COLLATE utf8_bin DEFAULT NULL,
  EXECUTION_ID_ varchar(64) COLLATE utf8_bin DEFAULT NULL,
  PROC_INST_ID_ varchar(64) COLLATE utf8_bin DEFAULT NULL,
  PROC_DEF_ID_ varchar(64) COLLATE utf8_bin DEFAULT NULL,
  PROC_DEF_KEY_ varchar(255) COLLATE utf8_bin DEFAULT NULL,
  TENANT_ID_ varchar(64) COLLATE utf8_bin DEFAULT NULL,
  STATE_ int(11) DEFAULT NULL,
  REV_ int(11) DEFAULT NULL,
  PRIMARY KEY (ID_),
  KEY ACT_HI_EXT_TASK_LOG_PROCINST (PROC_INST_ID_),
  KEY ACT_HI_EXT_TASK_LOG_PROCDEF (PROC_DEF_ID_),
  KEY ACT_HI_EXT_TASK_LOG_PROC_DEF_KEY (PROC_DEF_KEY_),
  KEY ACT_HI_EXT_TASK_LOG_TENANT_ID (TENANT_ID_),
  KEY ACT_IDX_HI_EXTTASKLOG_ERRORDET (ERROR_DETAILS_ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;ALTER TABLE act_hi_identitylink
ADD INDEX ACT_IDX_HI_IDENT_LNK_PROC_DEF_KEY (PROC_DEF_KEY_) ,
ADD INDEX ACT_IDX_HI_IDENT_LINK_TASK (TASK_ID_) ;ALTER TABLE act_hi_incident
ADD INDEX ACT_IDX_HI_INCIDENT_PROC_DEF_KEY (PROC_DEF_KEY_) ,
ADD INDEX ACT_IDX_HI_INCIDENT_PROCINST (PROC_INST_ID_) ;ALTER TABLE act_hi_job_log
ADD INDEX ACT_IDX_HI_JOB_LOG_PROC_DEF_KEY (PROCESS_DEF_KEY_) ,
ADD INDEX ACT_IDX_HI_JOB_LOG_EX_STACK (JOB_EXCEPTION_STACK_ID_) ;ALTER TABLE act_hi_procinst
ADD COLUMN STATE_  varchar(255) NULL AFTER PROC_DEF_KEY_,
ADD INDEX ACT_IDX_HI_PRO_INST_PROC_DEF_KEY (PROC_DEF_KEY_) ;ALTER TABLE act_hi_taskinst
ADD INDEX ACT_IDX_HI_TASK_INST_PROC_DEF_KEY (PROC_DEF_KEY_) ,
ADD INDEX ACT_IDX_HI_TASKINST_PROCINST (PROC_INST_ID_) ,
ADD INDEX ACT_IDX_HI_TASKINSTID_PROCINST (ID_, PROC_INST_ID_) ;ALTER TABLE act_hi_varinst
ADD COLUMN STATE_  varchar(20) NULL AFTER TENANT_ID_,
ADD INDEX ACT_IDX_HI_VAR_INST_PROC_DEF_KEY (PROC_DEF_KEY_) ,
ADD INDEX ACT_IDX_HI_VARINST_BYTEAR (BYTEARRAY_ID_) ;ALTER TABLE act_re_case_def
ADD COLUMN HISTORY_TTL_  int(11) NULL AFTER TENANT_ID_;CREATE TABLE act_re_decision_req_def (
  ID_ varchar(64) COLLATE utf8_bin NOT NULL,
  REV_ int(11) DEFAULT NULL,
  CATEGORY_ varchar(255) COLLATE utf8_bin DEFAULT NULL,
  NAME_ varchar(255) COLLATE utf8_bin DEFAULT NULL,
  KEY_ varchar(255) COLLATE utf8_bin NOT NULL,
  VERSION_ int(11) NOT NULL,
  DEPLOYMENT_ID_ varchar(64) COLLATE utf8_bin DEFAULT NULL,
  RESOURCE_NAME_ varchar(4000) COLLATE utf8_bin DEFAULT NULL,
  DGRM_RESOURCE_NAME_ varchar(4000) COLLATE utf8_bin DEFAULT NULL,
  TENANT_ID_ varchar(64) COLLATE utf8_bin DEFAULT NULL,
  PRIMARY KEY (ID_),
  KEY ACT_IDX_DEC_REQ_DEF_TENANT_ID (TENANT_ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;ALTER TABLE act_re_decision_def
ADD COLUMN DEC_REQ_ID_  varchar(64) NULL AFTER DGRM_RESOURCE_NAME_,
ADD COLUMN DEC_REQ_KEY_  varchar(255) NULL AFTER DEC_REQ_ID_,
ADD COLUMN HISTORY_TTL_  int(11) NULL AFTER TENANT_ID_,
ADD COLUMN VERSION_TAG_  varchar(64) NULL AFTER HISTORY_TTL_,
ADD INDEX ACT_IDX_DEC_DEF_REQ_ID (DEC_REQ_ID_) ;ALTER TABLE act_re_decision_def ADD CONSTRAINT ACT_FK_DEC_REQ FOREIGN KEY (DEC_REQ_ID_) REFERENCES act_re_decision_req_def (ID_);ALTER TABLE act_re_deployment
DROP COLUMN KEY_,
DROP COLUMN ENGINE_VERSION_,
MODIFY COLUMN DEPLOY_TIME_  timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER TENANT_ID_;ALTER TABLE act_re_procdef
DROP COLUMN ENGINE_VERSION_,
ADD COLUMN HISTORY_TTL_  int(11) NULL AFTER VERSION_TAG_;ALTER TABLE act_ru_case_sentry_part
ADD COLUMN VARIABLE_EVENT_  varchar(255) NULL AFTER SOURCE_,
ADD COLUMN VARIABLE_NAME_  varchar(255) NULL AFTER VARIABLE_EVENT_;DROP TABLE IF EXISTS act_ru_deadletter_job;ALTER TABLE act_ru_execution
DROP COLUMN ROOT_PROC_INST_ID_,
DROP COLUMN IS_MI_ROOT_,
DROP COLUMN START_TIME_,
DROP COLUMN START_USER_ID_,
DROP COLUMN IS_COUNT_ENABLED_,
DROP COLUMN EVT_SUBSCR_COUNT_,
DROP COLUMN TASK_COUNT_,
DROP COLUMN JOB_COUNT_,
DROP COLUMN TIMER_JOB_COUNT_,
DROP COLUMN SUSP_JOB_COUNT_,
DROP COLUMN DEADLETTER_JOB_COUNT_,
DROP COLUMN VAR_COUNT_,
DROP COLUMN ID_LINK_COUNT_,
DROP INDEX ACT_IDC_EXEC_ROOT;ALTER TABLE act_ru_ext_task
ADD COLUMN ERROR_DETAILS_ID_  varchar(64) NULL AFTER ERROR_MSG_,
ADD INDEX ACT_IDX_EXT_TASK_ERR_DETAILS (ERROR_DETAILS_ID_) ;ALTER TABLE act_ru_ext_task ADD CONSTRAINT ACT_FK_EXT_TASK_ERROR_DETAILS FOREIGN KEY (ERROR_DETAILS_ID_) REFERENCES act_ge_bytearray (ID_);ALTER TABLE act_ru_job DROP FOREIGN KEY ACT_FK_JOB_EXECUTION;ALTER TABLE act_ru_job DROP FOREIGN KEY ACT_FK_JOB_PROCESS_INSTANCE;ALTER TABLE act_ru_meter_log
ADD COLUMN MILLISECONDS_  bigint(20) NULL DEFAULT 0 AFTER REPORTER_,
ADD INDEX ACT_IDX_METER_LOG_MS (MILLISECONDS_) ,
ADD INDEX ACT_IDX_METER_LOG_NAME_MS (NAME_, MILLISECONDS_) ,
ADD INDEX ACT_IDX_METER_LOG_REPORT (NAME_, REPORTER_, MILLISECONDS_) ,
ADD INDEX ACT_IDX_METER_LOG_TIME (TIMESTAMP_) ;DROP TABLE IF EXISTS act_ru_suspended_job;ALTER TABLE act_ru_task
DROP COLUMN CLAIM_TIME_,
MODIFY COLUMN CREATE_TIME_  timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER PRIORITY_;DROP TABLE IF EXISTS act_ru_timer_job;

Code Migration

As mentioned above, if it’s a simple new project, you don’t need to write any code at all, and you can run it by citing a few maven libraries, but it’s impossible to replace all my original rest interfaces with official rest interfaces, which would be too much work , the cost is too high, so I have to solve it one by one according to the error reminder of the IDE. I won’t talk about the process, and post the modified Service class.

package cn.jia.workflow.service;import cn.jia.workflow.entity.DeploymentExample;
import cn.jia.workflow.entity.ProcessDefinitionExample;
import cn.jia.workflow.entity.ProcessInstanceExample;
import cn.jia.workflow.entity.TaskExample;
import com.github.pagehelper.Page;
import org.camunda.bpm.engine.history.HistoricProcessInstance;
import org.camunda.bpm.engine.history.HistoricTaskInstance;
import org.camunda.bpm.engine.history.HistoricVariableInstance;
import org.camunda.bpm.engine.repository.Deployment;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Comment;
import org.camunda.bpm.engine.task.Task;import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;public interface WorkflowService {/**
   * 部署工作流
   * @param deployment
   * @param inputStream
   */
  void deployProcess(Deployment deployment, InputStream inputStream);
  
  void deployProcess(Deployment deployment, ZipInputStream zipInputStream);
  
  /**
   * 获取工作流列表
   * @return
   */
  List<Deployment> getDeployment();
  
  /**
   * 分页获取工作流列表
   * @param pageNo
   * @param pageSize
   * @return
   */
  Page<Deployment> getDeployment(DeploymentExample example, int pageNo, int pageSize);
  
  /**
   * 根据ID获取工作流
   * @param deploymentId
   * @return
   */
  Deployment getDeploymentById(String deploymentId);
  
  /**
   * 获取工作流部署资源列表
   * @param deploymentId
   * @return
   */
  List<String> getDeploymentResourceNames(String deploymentId);
  
  /**
   * 删除工作流
   * @param deploymentId
   */
  void deleteDeployment(String deploymentId);
  
  /**
   * 获取工作流定义列表
   * @param example
   * @param pageNo
   * @param pageSize
   * @return
   */
  Page<ProcessDefinition> getProcessDefinition(ProcessDefinitionExample example, int pageNo, int pageSize);
  
  /**
   * 获取工作流定义信息
   * @param processDefinitionId
   * @return
   */
  ProcessDefinition getProcessDefinitionById(String processDefinitionId);
  
  /**
   * 获取工作流图解内容
   * @param processDefinitionId
   * @return
   */
  InputStream getProcessDiagram(String processDefinitionId);
  
  /**
   * 获取工作流定义内容
   * @param deploymentId
   * @param resourceName
   * @return
   */
  InputStream getResourceAsStream(String deploymentId, String resourceName);
  
  /**
   * 激活工作流
   * @param processDefinitionId
   */
  void activateProcessDefinition(String processDefinitionId);
  
  /**
   * 挂起工作流
   * @param processDefinitionId
   */
  void suspendProcessDefinition(String processDefinitionId);/**
   * 开始任务
   * @param processDefinitionKey
   * @param businessKey
   * @param variables
   */
  void startProcess(String processDefinitionKey, String businessKey, Map<String, Object> variables);/**
   * 获得某个人的任务别表
   * @param assignee
   * @return
   */
  List<Task> getTasks(String assignee);
  
  /**
   * 分页显示某个人的任务列表
   * @param example
   * @param pageNo
   * @param pageSize
   * @return
   */
  Page<Task> getTasks(TaskExample example, int pageNo, int pageSize);
  
  /**
   * 根据业务编号查找当前用户的最新任务
   * @param businessKey
   * @return
   */
  Task getTaskByBusinessKey(String businessKey, String assignee);
  
  /**
   * 根据业务编号查找最新任务列表
   * @param businessKey
   * @return
   */
  List<Task> getTaskByBusinessKey(String businessKey);
  
  /**
   * 根据任务ID获取任务信息
   * @param taskId
   * @return
   */
  Task getTaskById(String taskId);
  
  /**
   * 根据实例ID获取
   * @param processInstanceId
   * @return
   */
  List<Task> getTaskByProcessInstanceId(String processInstanceId);/**
   * 完成任务
   * @param taskId
   * @param variables
   */
  void completeTasks(String taskId, Map<String, Object> variables);
  
  /**
   * 删除流程实例
   * @param processInstanceId
   * @param deleteReason
   */
  void deleteProcessInstance(String processInstanceId, String deleteReason);
  
  /**
   * 委托受理人
   * @param taskId 任务ID
   * @param userId 被委托人
   */
  void delegateTask(String taskId, String userId);
  
  /**
   * 任务认领
   * @param taskId 任务ID
   * @param userId 认领人ID
   */
  void claimTask(String taskId, String userId);
  
  /**
   * 设置受理人
   * @param taskId 任务ID
   * @param userId 受理人ID
   */
  void setAssignee(String taskId, String userId);/**
   * 获取任务审批人列表
   * @param taskId
   * @return
   */
  List<String> getCandidate(String taskId);
  
  /**
   * 获取历史审批列表
   * @param assignee
   * @return
   */
  List<HistoricTaskInstance> getHistorys(String assignee);
  
  /**
   * 分页显示历史审批列表
   * @param example
   * @param pageNo
   * @param pageSize
   * @return
   */
  Page<HistoricTaskInstance> getHistorys(TaskExample example, int pageNo, int pageSize);
  
  /**
   * 根据业务编码分页显示历史审批列表
   * @param businessKey
   * @param pageNo
   * @param pageSize
   * @return
   */
  Page<HistoricTaskInstance> getHistorysByBusinessKey(String businessKey, int pageNo, int pageSize);
  
  /**
   * 获取历史实例列表
   * @param applicant
   * @return
   */
  List<HistoricProcessInstance> getHistoricProcessInstances(String applicant);
  
  /**
   * 分页显示历史实例列表
   * @param example
   * @param pageNo
   * @param pageSize
   * @return
   */
  Page<HistoricProcessInstance> getHistoricProcessInstances(ProcessInstanceExample example, int pageNo, int pageSize);
  
  /**
     * 根据Task中的流程实例的ID,来获取对应的流程实例
     * @param task 流程中的任务
     * @return
     */
  ProcessInstance getProcessInstanceByTask(Task task);
    
    /**
     * 根据历史Task中的流程实例的ID,来获取对应的历史流程实例
     * @param instanceId
     * @return
     */
  HistoricProcessInstance getHistoricProcessInstanceById(String instanceId);
    
    /**
     * 根据taskId获取变量值
     * @param taskId
     * @param variableName
     * @param variableClass
     * @return
     */
  <T> T getProcessVariables(String taskId, String variableName, Class<T> variableClass);
    
    /**
     * 根据taskId获取变量值
     * @param taskId
     * @param variableName
     * @return
     */
  Object getProcessVariables(String taskId, String variableName);
    
    /**
     * 根据实例ID获取历史变量值
     * @param processInstanceId
     * @param variableName
     * @param variableClass
     * @return
     */
  <T> T getHistoricVariable(String processInstanceId, String variableName, Class<T> variableClass);
    
    /**
     * 根据实例ID获取历史变量值
     * @param processInstanceId
     * @param variableName
     * @return
     */
  Object getHistoricVariable(String processInstanceId, String variableName);
    
    /**
     * 获取任务变量列表
     * @param processInstanceId
     * @param taskId
     * @return
     */
  List<HistoricVariableInstance> getHistoricVariables(String processInstanceId, String taskId);
  
  /**
   * 获取实例变量列表
   * @param processInstanceId
   * @return
   */
  List<HistoricVariableInstance> getHistoricVariables(String processInstanceId);
  
  /**
   * 获取任务的指定变量
   * @param taskId
   * @param variableName
   * @return
   */
  Object getTaskVariable(String taskId, String variableName);
  
  /**
   * 获取任务所有变量
   * @param taskId
   * @return
   */
  Map<String, Object> getTaskVariables(String taskId);
  
  /**
   * 设置流程变量
   * @param taskId
   * @param variables
   */
  void setProcessVariables(String taskId, Map<String, Object> variables);
  
  /**
   * 设置流程变量
   * @param taskId
   * @param variableName
   * @param value
   */
  void setProcessVariable(String taskId, String variableName, Object value);
  
  /**
   * 设置任务变量
   * @param taskId
   * @param variables
   */
  void setTaskVariables(String taskId, Map<String, Object> variables);
  
  /**
   * 设置任务变量
   * @param taskId
   * @param variableName
   * @param value
   */
  void setTaskVariable(String taskId, String variableName, Object value);
  
  /**
   * 获取任务审批批注列表
   * @param taskId
   * @return
   */
  List<Comment> getTaskComments(String taskId);
  
  /**
   * 获取任务审批批注信息
   * @param taskId
   * @return
   */
  String getTaskComment(String taskId);
  
  /**
   * 添加任务批注
   * @param taskId
   * @param message
   */
  void addComment(String taskId, String message);
  
  /**
   * 获取实例流程图
   * @param instanceId
   * @return
   */
  InputStream getInstanceDiagram(String instanceId);
}
package cn.jia.workflow.service.impl;import cn.jia.core.common.EsSecurityHandler;
import cn.jia.core.util.StringUtils;
import cn.jia.workflow.entity.DeploymentExample;
import cn.jia.workflow.entity.ProcessDefinitionExample;
import cn.jia.workflow.entity.ProcessInstanceExample;
import cn.jia.workflow.entity.TaskExample;
import cn.jia.workflow.service.WorkflowService;
import com.github.pagehelper.Page;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.history.*;
import org.camunda.bpm.engine.repository.Deployment;
import org.camunda.bpm.engine.repository.DeploymentQuery;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.repository.ProcessDefinitionQuery;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Comment;
import org.camunda.bpm.engine.task.IdentityLink;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.engine.task.TaskQuery;
import org.camunda.bpm.model.bpmn.Bpmn;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.instance.Process;
import org.camunda.bpm.model.bpmn.instance.*;
import org.camunda.bpm.model.bpmn.instance.bpmndi.*;
import org.camunda.bpm.model.bpmn.instance.dc.Bounds;
import org.camunda.bpm.model.bpmn.instance.di.Waypoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;@Service
public class WorkflowServiceImpl implements WorkflowService {@Autowired
  private RepositoryService repositoryService;
  @Autowired
  private RuntimeService runtimeService;
  @Autowired
  private TaskService taskService;
  @Autowired
  private HistoryService historyService;
  @Autowired
  private IdentityService identityService;
  
  @Override
  public void deployProcess(Deployment deployment, InputStream inputStream) {
    String clientId = EsSecurityHandler.clientId();
    repositoryService.createDeployment().addInputStream(deployment.getName()+".bpmn", inputStream).name(deployment.getName()).tenantId(clientId).deploy();
  }
  
  @Override
  public void deployProcess(Deployment deployment, ZipInputStream zipInputStream) {
    String clientId = EsSecurityHandler.clientId();
    repositoryService.createDeployment().addZipInputStream(zipInputStream).name(deployment.getName()).tenantId(clientId).deploy();
  }
  
  @Override
  public List<Deployment> getDeployment() {
    return repositoryService.createDeploymentQuery().tenantIdIn(EsSecurityHandler.clientId()).list();
  }
  
  @Override
  public Page<Deployment> getDeployment(DeploymentExample example, int pageNo, int pageSize) {
    Page<Deployment> page = new Page<>(pageNo, pageSize);
    DeploymentQuery query = repositoryService.createDeploymentQuery().tenantIdIn(EsSecurityHandler.clientId());
    if(example != null){
      if(example.getName() != null){
        query.deploymentNameLike("%" + example.getName() + "%");
      }
    }
    query.orderByDeploymentTime().desc();
    page.setTotal(query.count());
    page.addAll(query.listPage((pageNo - 1) * pageSize, pageSize));
    return page;
  }
  
  @Override
  public Deployment getDeploymentById(String deploymentId) {
    return repositoryService.createDeploymentQuery().tenantIdIn(EsSecurityHandler.clientId()).deploymentId(deploymentId).singleResult();
  }
  
  @Override
  public List<String> getDeploymentResourceNames(String deploymentId) {
    return repositoryService.getDeploymentResourceNames(deploymentId);
  }
  
  @Override
  public InputStream getResourceAsStream(String deploymentId, String resourceName) {
    return repositoryService.getResourceAsStream(deploymentId, resourceName);
  }
  
  @Override
  public void deleteDeployment(String deploymentId) {
    repositoryService.deleteDeployment(deploymentId);
  }
  
  @Override
  public Page<ProcessDefinition> getProcessDefinition(ProcessDefinitionExample example, int pageNo, int pageSize) {
    Page<ProcessDefinition> page = new Page<>(pageNo, pageSize);
    ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery().tenantIdIn(EsSecurityHandler.clientId());
    if(StringUtils.isNotEmpty(example.getKey())) {
      query.processDefinitionKeyLike("%" + example.getKey() + "%");
    }
    if(StringUtils.isNotEmpty(example.getDeploymentId())) {
      query.deploymentId(example.getDeploymentId());
    }
    if(StringUtils.isNotEmpty(example.getCategory())){
      query.processDefinitionCategoryLike("%" + example.getCategory() + "%");
    }
    if(StringUtils.isNotEmpty(example.getName())){
      query.processDefinitionNameLike("%" + example.getName() + "%");
    }
    if(StringUtils.isNotEmpty(example.getResourceName())){
      query.processDefinitionResourceNameLike("%" + example.getResourceName() + "%");
    }
    query.orderByDeploymentId().desc();
​
    page.setTotal(query.count());
    page.addAll(query.listPage((pageNo - 1) * pageSize, pageSize));
    return page;
  }
  
  @Override
  public ProcessDefinition getProcessDefinitionById(String processDefinitionId) {
    return repositoryService.getProcessDefinition(processDefinitionId);
  }
  
  @Override
  public InputStream getProcessDiagram(String processDefinitionId) {
    return repositoryService.getProcessDiagram(processDefinitionId);
  }
  
  @Override
  public void activateProcessDefinition(String processDefinitionId) {
    repositoryService.activateProcessDefinitionById(processDefinitionId);
  }
  
  @Override
  public void suspendProcessDefinition(String processDefinitionId) {
    repositoryService.suspendProcessDefinitionById(processDefinitionId);
  }@Override
  public void startProcess(String processDefinitionKey, String businessKey, Map<String, Object> variables) {
    identityService.setAuthentication(String.valueOf(variables.get("applicant")), null, Collections.singletonList(EsSecurityHandler.clientId()));
    runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, variables);
  }@Override
  public List<Task> getTasks(String assignee) {
    return taskService.createTaskQuery().or().taskAssignee(assignee).taskCandidateUser(assignee).endOr().tenantIdIn(EsSecurityHandler.clientId()).list();
  }
  
  @Override
  public Page<Task> getTasks(TaskExample example, int pageNo, int pageSize) {
    Page<Task> page = new Page<>(pageNo, pageSize);
    TaskQuery query = taskService.createTaskQuery();
    if(example != null) {
      if(StringUtils.isNotEmpty(example.getAssignee())) {
        query.taskAssignee(example.getAssignee());
      }
      if(StringUtils.isNotEmpty(example.getCandidateUser())) {
        query.taskCandidateUser(example.getCandidateUser());
      }
      if(StringUtils.isNotEmpty(example.getCandidateOrAssigned())) {
        query.or().taskAssignee(example.getCandidateOrAssigned()).taskCandidateUser(example.getCandidateOrAssigned()).endOr();
      }
      if(StringUtils.isNotEmpty(example.getDefinitionKey())) {
        query.processDefinitionKey(example.getDefinitionKey());
      }
      if(StringUtils.isNotEmpty(example.getDefinitionName())) {
        query.processDefinitionNameLike("%" + example.getDefinitionName() + "%");
      }
      if(StringUtils.isNotEmpty(example.getBusinessKey())) {
        query.processInstanceBusinessKeyLike("%" + example.getBusinessKey() + "%");
      }
      if(StringUtils.isNotEmpty(example.getProcessInstanceId())) {
        query.processInstanceId(example.getProcessInstanceId());
      }
      if(StringUtils.isNotEmpty(example.getApplicant())) {
        query.processVariableValueLike("applicant", "%" + example.getApplicant() + "%");
      }
    }
    query.tenantIdIn(EsSecurityHandler.clientId()).orderByTaskCreateTime().desc();
    page.setTotal(query.count());
    page.addAll(query.listPage((pageNo - 1) * pageSize, pageSize));
    return page;
  }
  
  @Override
  public Task getTaskByBusinessKey(String businessKey, String assignee) {
    return taskService.createTaskQuery().processInstanceBusinessKey(businessKey).or().taskAssignee(assignee).taskCandidateUser(assignee).endOr().tenantIdIn(EsSecurityHandler.clientId()).singleResult();
  }
  
  @Override
  public List<Task> getTaskByBusinessKey(String businessKey) {
    return taskService.createTaskQuery().processInstanceBusinessKey(businessKey).tenantIdIn(EsSecurityHandler.clientId()).list();
  }
  
  @Override
  public Task getTaskById(String taskId) {
    return taskService.createTaskQuery().taskId(taskId).tenantIdIn(EsSecurityHandler.clientId()).singleResult();
  }
  
  @Override
  public List<Task> getTaskByProcessInstanceId(String processInstanceId) {
    return taskService.createTaskQuery().processInstanceId(processInstanceId).tenantIdIn(EsSecurityHandler.clientId()).list();
  }@Override
  public void completeTasks(String taskId, Map<String, Object> variables) {
    taskService.complete(taskId, variables);
  }
  
  @Override
  public void deleteProcessInstance(String processInstanceId, String deleteReason) {
        runtimeService.deleteProcessInstance(processInstanceId, deleteReason);
  }
  
  @Override
  public void delegateTask(String taskId, String userId) {
    taskService.delegateTask(taskId, userId);
  }
  
  @Override
  public void claimTask(String taskId, String userId) {
    taskService.claim(taskId, userId);
  }
  
  @Override
  public void setAssignee(String taskId, String userId) {
    taskService.setAssignee(taskId, userId);
  }@Override
  public List<String> getCandidate(String taskId) {
    List<String> candidate = new ArrayList<>();
    List<IdentityLink> identityLinks = taskService.getIdentityLinksForTask(taskId);
    for(IdentityLink id : identityLinks){
      if("candidate".equals(id.getType())){
        candidate.add(id.getUserId());
      }
    }
    return candidate;
  }@Override
  public List<HistoricTaskInstance> getHistorys(String assignee) {
    return historyService.createHistoricTaskInstanceQuery().taskAssignee(assignee).tenantIdIn(EsSecurityHandler.clientId()).finished().list();
  }
  
  @Override
  public Page<HistoricTaskInstance> getHistorys(TaskExample example, int pageNo, int pageSize) {
    Page<HistoricTaskInstance> page = new Page<>(pageNo, pageSize);
    HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery();
    if(example != null) {
      if(StringUtils.isNotEmpty(example.getAssignee())) {
        query = query.taskAssignee(example.getAssignee());
      }
      if(StringUtils.isNotEmpty(example.getDefinitionKey())) {
        query.processDefinitionKey(example.getDefinitionKey());
      }
      if(StringUtils.isNotEmpty(example.getDefinitionName())) {
        query.processDefinitionName(example.getDefinitionName());
      }
      if(StringUtils.isNotEmpty(example.getBusinessKey())) {
        query.processInstanceBusinessKeyLike("%" + example.getBusinessKey() + "%");
      }
      if(StringUtils.isNotEmpty(example.getProcessInstanceId())) {
        query.processInstanceId(example.getProcessInstanceId());
      }
      if(StringUtils.isNotEmpty(example.getApplicant())) {
        query.processVariableValueEquals("applicant", example.getApplicant());
      }
    }
    
    query = query.tenantIdIn(EsSecurityHandler.clientId()).finished().orderByTaskDueDate().desc();
    page.setTotal(query.count());
    page.addAll(query.listPage((pageNo - 1) * pageSize, pageSize));
    return page;
  }
  
  @Override
  public Page<HistoricTaskInstance> getHistorysByBusinessKey(String businessKey, int pageNo, int pageSize) {
    Page<HistoricTaskInstance> page = new Page<>(pageNo, pageSize);
    HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery().processInstanceBusinessKey(businessKey).tenantIdIn(EsSecurityHandler.clientId()).finished().orderByHistoricTaskInstanceEndTime().asc();
    page.setTotal(query.count());
    page.addAll(query.listPage((pageNo - 1) * pageSize, pageSize));
    return page;
  }
  
  @Override
  public List<HistoricProcessInstance> getHistoricProcessInstances(String applicant) {
    return historyService.createHistoricProcessInstanceQuery().startedBy(applicant).tenantIdIn(EsSecurityHandler.clientId()).list();
  }
  
  @Override
  public Page<HistoricProcessInstance> getHistoricProcessInstances(ProcessInstanceExample example, int pageNo, int pageSize) {
    Page<HistoricProcessInstance> page = new Page<>(pageNo, pageSize);
    HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery();
    if(example != null) {
      if(StringUtils.isNotEmpty(example.getApplicant())) {
        query.startedBy(example.getApplicant());
      }
      if(StringUtils.isNotEmpty(example.getDefinitionKey())) {
        query.processDefinitionKey(example.getDefinitionKey());
      }
      if(StringUtils.isNotEmpty(example.getDefinitionName())) {
        query.processDefinitionName(example.getDefinitionName());
      }
      if(example.getStartedBefore() != null) {
        query.startedBefore(example.getStartedBefore());
      }
      if(example.getStartedAfter() != null) {
        query.startedAfter(example.getStartedAfter());
      }
      if(example.getFinishedBefore() != null) {
        query.finishedBefore(example.getFinishedBefore());
      }
      if(example.getFinishedAfter() != null) {
        query.finishedAfter(example.getFinishedAfter());
      }
      if(StringUtils.isNotEmpty(example.getBusinessKey())) {
        query.processInstanceBusinessKey(example.getBusinessKey());
      }
    }
    
    query.tenantIdIn(EsSecurityHandler.clientId()).orderByProcessInstanceStartTime().desc();
    page.setTotal(query.count());
    page.addAll(query.listPage((pageNo - 1) * pageSize, pageSize));
    return page;
  }
  
  @Override
    public ProcessInstance getProcessInstanceByTask(Task task) {
        //得到当前任务的流程
    return runtimeService.createProcessInstanceQuery().tenantIdIn(EsSecurityHandler.clientId())
        .processInstanceId(task.getProcessInstanceId()).singleResult();
    }
  
  @Override
    public HistoricProcessInstance getHistoricProcessInstanceById(String instanceId) {
        //得到当前任务的流程
    return historyService.createHistoricProcessInstanceQuery().tenantIdIn(EsSecurityHandler.clientId())
                .processInstanceId(instanceId).singleResult();
    }
  
  @Override
  @SuppressWarnings("unchecked")
  public <T> T getProcessVariables(String taskId, String variableName, Class<T> variableClass) {
    return (T)taskService.getVariable(taskId, variableName);
  }
  
  @Override
  public Object getProcessVariables(String taskId, String variableName) {
    return taskService.getVariable(taskId, variableName);
  }
  
  @SuppressWarnings("unchecked")
  @Override
  public <T> T getHistoricVariable(String processInstanceId, String variableName, Class<T> variableClass) {
    HistoricVariableInstance instance = historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).variableName(variableName).singleResult();
    if(instance != null && instance.getValue() != null) {
      return (T) instance.getValue();
    }else {
      return null;
    }
  }
  
  @Override
  public Object getHistoricVariable(String processInstanceId, String variableName) {
    HistoricVariableInstance instance = historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).variableName(variableName).singleResult();
    if(instance != null && instance.getValue() != null) {
      return instance.getValue();
    }else {
      return null;
    }
  }
  
  @Override
  public List<HistoricVariableInstance> getHistoricVariables(String processInstanceId, String taskId) {
    return historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).taskIdIn(taskId).list();
  }
  
  @Override
  public List<HistoricVariableInstance> getHistoricVariables(String processInstanceId) {
    return historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).list();
  }
  
  @Override
  public Object getTaskVariable(String taskId, String variableName) {
    return taskService.getVariable(taskId, variableName);
  }
  
  @Override
  public Map<String, Object> getTaskVariables(String taskId) {
    return taskService.getVariables(taskId);
  }@Override
  public void setProcessVariables(String taskId, Map<String, Object> variables) {
    taskService.setVariables(taskId, variables);
  }
  
  @Override
  public void setProcessVariable(String taskId, String variableName, Object value) {
    taskService.setVariable(taskId, variableName, value);
  }
  
  @Override
  public void setTaskVariables(String taskId, Map<String, Object> variables) {
    taskService.setVariablesLocal(taskId, variables);
  }
  
  @Override
  public void setTaskVariable(String taskId, String variableName, Object value) {
    taskService.setVariableLocal(taskId, variableName, value);
  }
  
  @Override
  public List<Comment> getTaskComments(String taskId) {
    return taskService.getTaskComments(taskId);
  }
  
  @Override
  public String getTaskComment(String taskId) {
    List<String> comment = new ArrayList<>();
    List<Comment> commentList = taskService.getTaskComments(taskId);
    for(Comment c : commentList) {
      comment.add(c.getFullMessage());
    }
    return String.join(",", comment);
  }
  
  @Override
  public void addComment(String taskId, String message) {
    Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
    taskService.createComment(taskId, task.getProcessInstanceId(), message);
  }
  
  @Override
  public InputStream getInstanceDiagram(String instanceId) {
    return null;
//        try {
//            // 获取历史流程实例
//            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(instanceId).singleResult();
//
//            // 获取流程中已经执行的节点,按照执行先后顺序排序
//            List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(instanceId).orderByHistoricActivityInstanceId().asc().list();
//
//            // 构造已执行的节点ID集合
//            List<String> executedActivityIdList = new ArrayList<>();
//            for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
//                executedActivityIdList.add(activityInstance.getActivityId());
//            }
//
//            // 获取bpmnModel
//            BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
//            // 获取流程已发生流转的线ID集合
//            List<String> flowIds = this.getExecutedFlows(bpmnModel, historicActivityInstanceList);
//
//            // 使用默认配置获得流程图表生成器,并生成追踪图片字符流
//            ProcessDiagramGenerator processDiagramGenerator = new DefaultProcessDiagramGenerator();
//      return processDiagramGenerator.generateDiagram(bpmnModel, "png", executedActivityIdList, flowIds, "宋体", "微软雅黑", "黑体", null, 2.0);
//        } catch (Exception e) {
//            e.printStackTrace();
//            return null;
//        }
    }
  
//  private List<String> getExecutedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {
//        // 流转线ID集合
//        List<String> flowIdList = new ArrayList<>();
//        // 全部活动实例
//        List<FlowNode> historicFlowNodeList = new LinkedList<>();
//        // 已完成的历史活动节点
//        List<HistoricActivityInstance> finishedActivityInstanceList = new LinkedList<>();
//        for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
//            historicFlowNodeList.add((FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true));
//            if (historicActivityInstance.getEndTime() != null) {
//                finishedActivityInstanceList.add(historicActivityInstance);
//            }
//        }
//
//        // 遍历已完成的活动实例,从每个实例的outgoingFlows中找到已执行的
//        FlowNode currentFlowNode;
//        for (HistoricActivityInstance currentActivityInstance : finishedActivityInstanceList) {
//            // 获得当前活动对应的节点信息及outgoingFlows信息
//            currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
//            List<SequenceFlow> sequenceFlowList = currentFlowNode.getOutgoingFlows();
//
//            /*
//              遍历outgoingFlows并找到已已流转的
//              满足如下条件认为已已流转:
//              1.当前节点是并行网关或包含网关,则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转
//              2.当前节点是以上两种类型之外的,通过outgoingFlows查找到的时间最近的流转节点视为有效流转
//             */
//            FlowNode targetFlowNode;
//            if (BpmsActivityTypeEnum.PARALLEL_GATEWAY.getType().equals(currentActivityInstance.getActivityType())
//                    || BpmsActivityTypeEnum.INCLUSIVE_GATEWAY.getType().equals(currentActivityInstance.getActivityType())) {
//                // 遍历历史活动节点,找到匹配Flow目标节点的
//                for (SequenceFlow sequenceFlow : sequenceFlowList) {
//                    targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true);
//                    if (historicFlowNodeList.contains(targetFlowNode)) {
//                        flowIdList.add(sequenceFlow.getId());
//                    }
//                }
//            } else {
//                List<Map<String, String>> tempMapList = new LinkedList<>();
//                // 遍历历史活动节点,找到匹配Flow目标节点的
//                for (SequenceFlow sequenceFlow : sequenceFlowList) {
//                    for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
//                        if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
//                            tempMapList.add(DataUtil.toMap("flowId", sequenceFlow.getId(), "activityStartTime", String.valueOf(historicActivityInstance.getStartTime().getTime())));
//                        }
//                    }
//                }
//
//                // 遍历匹配的集合,取得开始时间最早的一个
//                long earliestStamp = 0L;
//                String flowId = null;
//                for (Map<String, String> map : tempMapList) {
//                    long activityStartTime = Long.valueOf(map.get("activityStartTime"));
//                    if (earliestStamp == 0 || earliestStamp >= activityStartTime) {
//                        earliestStamp = activityStartTime;
//                        flowId = map.get("flowId");
//                    }
//                }
//                flowIdList.add(flowId);
//            }
//        }
//        return flowIdList;
//    }
​
​
  public void generateProcess() throws IOException {
    BpmnModelInstance modelInstance = Bpmn.createEmptyModel();
    Definitions definitions = modelInstance.newInstance(Definitions.class);
    definitions.setTargetNamespace("http://camunda.org/examples");
    modelInstance.setDefinitions(definitions);// create the process
    Process process = modelInstance.newInstance(Process.class);
    process.setAttributeValue("id", "process-one-task", true);
    definitions.addChildElement(process);BpmnDiagram diagram = modelInstance.newInstance(BpmnDiagram.class);
    BpmnPlane plane = modelInstance.newInstance(BpmnPlane.class);
    plane.setBpmnElement(process);
    diagram.setBpmnPlane(plane);
    definitions.addChildElement(diagram);// create start event, user task and end event
    StartEvent startEvent = createElement(modelInstance, process, "start", "Di generation wanted",
        StartEvent.class, plane, 15, 15, 50, 50, true);UserTask userTask = createElement(modelInstance, process, "userTask", "Generate Model with DI",
        UserTask.class, plane, 100, 0, 80, 100, false);createSequenceFlow(modelInstance, process, startEvent, userTask, plane, 65, 40, 100, 40);EndEvent endEvent = createElement(modelInstance, process, "end", "DI generation completed",
        EndEvent.class, plane, 250, 15, 50, 50, true);createSequenceFlow(modelInstance, process, userTask, endEvent, plane, 200, 40, 250, 40);// validate and write model to file
    Bpmn.validateModel(modelInstance);
    File file = File.createTempFile("bpmn-model-api-", ".bpmn");
    Bpmn.writeModelToFile(file, modelInstance);}private <T extends BpmnModelElementInstance> T createElement(BpmnModelInstance modelInstance, BpmnModelElementInstance parentElement,
                                 String id, String name, Class<T> elementClass, BpmnPlane plane,
                                 double x, double y, double heigth, double width, boolean withLabel) {
    T element = modelInstance.newInstance(elementClass);
    element.setAttributeValue("id", id, true);
    element.setAttributeValue("name", name, false);
    parentElement.addChildElement(element);BpmnShape bpmnShape = modelInstance.newInstance(BpmnShape.class);
    bpmnShape.setBpmnElement((BaseElement) element);Bounds bounds = modelInstance.newInstance(Bounds.class);
    bounds.setX(x);
    bounds.setY(y);
    bounds.setHeight(heigth);
    bounds.setWidth(width);
    bpmnShape.setBounds(bounds);if (withLabel) {
      BpmnLabel bpmnLabel = modelInstance.newInstance(BpmnLabel.class);
      Bounds labelBounds = modelInstance.newInstance(Bounds.class);
      labelBounds.setX(x);
      labelBounds.setY(y + heigth);
      labelBounds.setHeight(heigth);
      labelBounds.setWidth(width);
      bpmnLabel.addChildElement(labelBounds);
      bpmnShape.addChildElement(bpmnLabel);
    }
    plane.addChildElement(bpmnShape);return element;
  }private SequenceFlow createSequenceFlow(BpmnModelInstance modelInstance, Process process, FlowNode from, FlowNode to, BpmnPlane plane,
                      int... waypoints) {
    String identifier = from.getId() + "-" + to.getId();
    SequenceFlow sequenceFlow = modelInstance.newInstance(SequenceFlow.class);
    sequenceFlow.setAttributeValue("id", identifier, true);
    process.addChildElement(sequenceFlow);
    sequenceFlow.setSource(from);
    from.getOutgoing().add(sequenceFlow);
    sequenceFlow.setTarget(to);
    to.getIncoming().add(sequenceFlow);BpmnEdge bpmnEdge = modelInstance.newInstance(BpmnEdge.class);
    bpmnEdge.setBpmnElement(sequenceFlow);
    for (int i = 0; i < waypoints.length / 2; i++) {
      double waypointX = waypoints[i*2];
      double waypointY = waypoints[i*2+1];
      Waypoint wp = modelInstance.newInstance(Waypoint.class);
      wp.setX(waypointX);
      wp.setY(waypointY);
      bpmnEdge.addChildElement(wp);
    }
    plane.addChildElement(bpmnEdge);return sequenceFlow;
  }}

​Paste another Listener that I often use

package cn.jia.workflow.listener;import cn.jia.core.common.EsSecurityHandler;
import cn.jia.core.entity.JSONResult;
import cn.jia.core.util.*;
import cn.jia.workflow.common.ErrorConstants;
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.delegate.*;
import org.camunda.bpm.engine.impl.el.JuelExpression;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;import java.util.Map;/**
 * Rest接口调用通用方法
 * @author chcbz
 * @date 2018年9月11日 下午3:37:55
 */
@Slf4j
@Service
public class RestListener implements ExecutionListener, TaskListener {
  
  private static final long serialVersionUID = -507889727531125820L;
  
  private JuelExpression url;
  private JuelExpression method;
  private JuelExpression params;
  @Autowired
  @Qualifier("singleRestTemplate")
  private RestTemplate restTemplate;
  @Autowired
  private TaskService taskService;@Override
  public void notify(DelegateTask delegateTask) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
//    OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)SecurityContextHolder.getContext().getAuthentication().getDetails();
//    headers.set("Authorization", "Bearer " + details.getTokenValue());
    String rqurl = fixedValueToString(url, delegateTask);
    String random = DataUtil.getRandom(false, 16);
    rqurl = HttpUtil.addUrlValue(rqurl, "nonce", random);
    String signature = MD5Util.str2Base32MD5(MD5Util.str2Base32MD5(random)+EsSecurityHandler.clientId());
    rqurl = HttpUtil.addUrlValue(rqurl, "signature", signature);
    String businessKey = String.valueOf(taskService.getVariable(delegateTask.getId(), "businessKey"));
    rqurl = HttpUtil.addUrlValue(rqurl, "businessKey", businessKey);
    String assignee = delegateTask.getAssignee();
    rqurl = HttpUtil.addUrlValue(rqurl, "assignee", assignee);String paramsStr = fixedValueToString(params, delegateTask);
    if(StringUtils.isNotEmpty(paramsStr)){
      Map<String, Object> paramsMap = JSONUtil.jsonToMap(paramsStr);
      if(paramsMap != null){
        for (String paramsKey : paramsMap.keySet()) {
          rqurl = HttpUtil.addUrlValue(rqurl, paramsKey, String.valueOf(paramsMap.get(paramsKey)));
        }
      }}//    String candidate = "";
//    Set<IdentityLink> candidates = delegateTask.getCandidates();
//    for(IdentityLink id : candidates){
//      candidate += "," + id.getUserId();
//    }
//    rqurl = HttpUtil.addUrlValue(rqurl, "candidate", StringUtils.isEmpty(candidate) ? "" : candidate.substring(1));
    log.info("======================rqurl: " + rqurl);
    log.info("======================method: " + fixedValueToString(method, delegateTask));
    log.info("======================params: " + paramsStr);
    HttpEntity<String> entity = new HttpEntity<>(fixedValueToString(params, delegateTask), headers);
    JSONResult<?> result = restTemplate.exchange(rqurl, HttpMethod.resolve(fixedValueToString(method, delegateTask)), entity, JSONResult.class).getBody();
    if(!ErrorConstants.SUCCESS.equals(result.getCode())) {
      log.error(result.getMsg());
    }
  }@Override
  public void notify(DelegateExecution execution) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
//    OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)SecurityContextHolder.getContext().getAuthentication().getDetails();
//    headers.set("Authorization", "Bearer " + details.getTokenValue());
    String rqurl = fixedValueToString(url, execution);
    String random = DataUtil.getRandom(false, 16);
    rqurl = HttpUtil.addUrlValue(rqurl, "nonce", random);
    String signature = MD5Util.str2Base32MD5(MD5Util.str2Base32MD5(random)+EsSecurityHandler.clientId());
    rqurl = HttpUtil.addUrlValue(rqurl, "signature", signature);
    String businessKey = execution.getBusinessKey();
    rqurl = HttpUtil.addUrlValue(rqurl, "businessKey", businessKey);
    HttpEntity<String> entity = new HttpEntity<>(fixedValueToString(params, execution), headers);
    JSONResult<?> result = restTemplate.exchange(rqurl, HttpMethod.resolve(fixedValueToString(method, execution)), entity, JSONResult.class).getBody();
    if(!ErrorConstants.SUCCESS.equals(result.getCode())) {
      log.error(result.getMsg());
    }
  }
  
  private String fixedValueToString(Expression fixedValue, VariableScope variableScope) {
    if(fixedValue == null) {
      return null;
    }
    return fixedValue.getValue(variableScope).toString();
  }}

Post another BPMN

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://www.camunda.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="m1570518626053" name="" targetNamespace="http://bpmn.io/schema/bpmn">
  <process id="carbook" name="商品车预订" processType="None" isClosed="false" isExecutable="true">
    <startEvent id="startevent1" name="Start" />
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="Vehicle_reservation_approval" />
    <userTask id="Vehicle_reservation_approval" name="仓库管理员审批" camunda:candidateUsers="${reviewers}">
      <extensionElements>
        <camunda:taskListener delegateExpression="${restListener}" event="create">
          <camunda:field name="url">
            <camunda:expression>https://api.mydomain.com/car/notify</camunda:expression>
          </camunda:field>
          <camunda:field name="method">
            <camunda:expression>GET</camunda:expression>
          </camunda:field>
          <camunda:field name="params">
            <camunda:expression>{"candidate":"${reviewers}","type":"Vehicle_reservation_approval"}</camunda:expression>
          </camunda:field>
        </camunda:taskListener>
      </extensionElements>
    </userTask>
    <exclusiveGateway id="reviewResult" name="审核结果" />
    <sequenceFlow id="flow3" sourceRef="Vehicle_reservation_approval" targetRef="reviewResult" />
    <endEvent id="endevent1" name="End">
      <incoming>flow4</incoming>
    </endEvent>
    <sequenceFlow id="pass" name="同意" sourceRef="reviewResult" targetRef="endevent1">
      <extensionElements>
        <camunda:executionListener delegateExpression="${restListener}" event="take">
          <camunda:field name="url">
            <camunda:expression>https://api.mydomain.com/car/complete/${businessKey}</camunda:expression>
          </camunda:field>
          <camunda:field name="method">
            <camunda:expression>GET</camunda:expression>
          </camunda:field>
          <camunda:field name="params">
            <camunda:expression>{}</camunda:expression>
          </camunda:field>
        </camunda:executionListener>
      </extensionElements>
      <conditionExpression xsi:type="tFormalExpression">${pass}</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow4" name="不同意" sourceRef="reviewResult" targetRef="endevent1">
      <extensionElements>
        <camunda:executionListener delegateExpression="${restListener}" event="take">
          <camunda:field name="url">
            <camunda:expression>https://api.mydomain.com/car/reject/${businessKey}</camunda:expression>
          </camunda:field>
          <camunda:field name="method">
            <camunda:expression>GET</camunda:expression>
          </camunda:field>
          <camunda:field name="params">
            <camunda:expression>{}</camunda:expression>
          </camunda:field>
        </camunda:executionListener>
      </extensionElements>
    </sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="Diagram-_1" name="New Diagram" documentation="background=#3C3F41;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0">
    <bpmndi:BPMNPlane bpmnElement="carbook">
      <bpmndi:BPMNShape id="Shape-startevent1" bpmnElement="startevent1">
        <omgdc:Bounds x="209" y="297" width="32" height="32" />
        <bpmndi:BPMNLabel>
          <omgdc:Bounds x="-107" y="-4" width="25" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-Vehicle_reservation_approval" bpmnElement="Vehicle_reservation_approval">
        <omgdc:Bounds x="343" y="285" width="105" height="55" />
        <bpmndi:BPMNLabel>
          <omgdc:Bounds x="0" y="0" width="105" height="55" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-reviewResult" bpmnElement="reviewResult" isMarkerVisible="true">
        <omgdc:Bounds x="539" y="300" width="32" height="32" />
        <bpmndi:BPMNLabel>
          <omgdc:Bounds x="-37" y="2" width="45" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Shape-endevent1" bpmnElement="endevent1">
        <omgdc:Bounds x="655" y="298" width="32" height="32" />
        <bpmndi:BPMNLabel>
          <omgdc:Bounds x="6" y="-3" width="20" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="BPMNEdge_flow1" bpmnElement="flow1" sourceElement="startevent1" targetElement="Vehicle_reservation_approval">
        <omgdi:waypoint x="241" y="313" />
        <omgdi:waypoint x="343" y="313" />
        <bpmndi:BPMNLabel>
          <omgdc:Bounds x="-1" y="-1" width="-1" height="-1" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="BPMNEdge_flow3" bpmnElement="flow3" sourceElement="Vehicle_reservation_approval" targetElement="reviewResult">
        <omgdi:waypoint x="448" y="315" />
        <omgdi:waypoint x="540" y="315" />
        <bpmndi:BPMNLabel>
          <omgdc:Bounds x="-1" y="-1" width="-1" height="-1" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="BPMNEdge_pass" bpmnElement="pass" sourceElement="reviewResult" targetElement="endevent1">
        <omgdi:waypoint x="570" y="315" />
        <omgdi:waypoint x="655" y="315" />
        <bpmndi:BPMNLabel>
          <omgdc:Bounds x="581" y="291" width="23" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="BPMNEdge_flow4" bpmnElement="flow4" sourceElement="Shape-reviewResult" targetElement="Shape-endevent1">
        <omgdi:waypoint x="555" y="332" />
        <omgdi:waypoint x="555" y="386" />
        <omgdi:waypoint x="671" y="386" />
        <omgdi:waypoint x="671" y="330" />
        <bpmndi:BPMNLabel>
          <omgdc:Bounds x="561" y="348" width="34" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

at last

So far, the project has been able to run normally, and the migration work has been completed, but there is still a problem that has not been resolved, that is, the instance flow chart cannot be automatically generated, and the official did not give a corresponding example, which is left for subsequent processing.

I put the project code on github for your reference, but because it uses springcloud and relies on some private jar packages, it cannot run alone.

GitHub - chcbz/jia-api-workflow: Jia framework workflow service

Reference article: