Activiti workflow development guide (springboot 2.x iview admin vue front and back end separation model designer dynamic data permission permission button display spring

Activiti workflow development guide (springboot 2.x iview admin vue front and back end separation model designer dynamic data permission permission button display spring


Integrate Activiti 5.22. Considering that there are many documents, the new version (the model designer has changed a lot) or Flowable was not selected.

http://localhost:8888

Several common interfaces

  1. RepositoryService: Provides a series of APIs for managing process deployment and process definition
  2. RuntimeService: Manage and control process instances when the process is running
  3. TaskService: Manage process tasks, such as task reminders, task completion, and task creation
  4. IdentityService: Provides an API to manage process role data, which includes user groups, users, and the relationships between them
  5. HistoryService: Operate on the historical data of the process, including query and delete these historical data

25 tables

1. act_ge_ general data table, ge is the abbreviation of general 2. act_hi_ historical data table, hi is the abbreviation of history, corresponding to the HistoryService interface 3. act_id_ identity data table, id is the abbreviation of identity, corresponding to the IdentityService interface 4. act_re_ process storage table , Re is the abbreviation of repository, corresponding to the RepositoryService interface, storing static data such as process deployment and process definitions 5. act_ru_ runtime data table, ru is the abbreviation of runtime, corresponding to the RuntimeService interface and TaskService interface, storing dynamic data such as process instances and user tasks

XBoot Extension Basic Development Guide

  • General process state table t_act_bussiness, where the table_idfield stores the associated form ID

  • The backend only needs to develop the corresponding form addition, deletion and modification interface, and store it in a single table. For example t_leave, the only thing that needs to be noted is that the new interface (when adding new data) needs to be associated with the business act_buniesstable and add information such as process and table ID to it. Refer toLeaveController

  • The front desk only needs to develop the corresponding single form page (the corresponding button can be displayed by routing parameters), refer to leave.vue(jump with the name of the route configured in the menu), and remember src/router/router.jsto add the route

  • Finally, remember to configure the corresponding process information in the system

    • Add the corresponding business table name in the data dictionary "business table", such as "t_leave"; add the corresponding front-end form component routing name in the "business form routing", such as "leave"
    • In the process management, edit and fill in the information related to the newly developed form. The main function of the business table is to associate and delete the corresponding form data when the user deletes the application. The form route name is used to jump to display the form page just developed on the front end

  • The process node approver can be set according to the role, department head, and personnel. After setting, it is checked by default, or sign (anyone approves, the process enters the next step, that is, first come, first review)

  • How to implement countersignature?

    • Please add an approval node!

    [Image upload failed...(image-5946f1-1556169186896)]

  • How to set up a branch?

    • Note: Branch settings are not supported. To ensure the simplicity of the workflow, only start, end, task nodes and one-way connections are supported. For branches, please allow users to dynamically select the next approver.

If the Chinese characters in the flowchart cannot be displayed after deployment, it is because there is no corresponding Chinese font in the server environment jdk, and Baidu can install it (the configuration file has been configured with Microsoft Yahei, if the default Song Ti is not configured)

Binding listener example

Common business requirements need to complete an approval process to change the original data state, notify the approval result message or perform other operations, and bind a listener to the end node. Once the end indicates that the process is all passed, the custom business is triggered.

  • Draw a flowchart and fill in the defined listener class
  • Listener sample code
@Slf4j
public class MyListener implements ExecutionListener {

    @Override
    public void notify(DelegateExecution delegateExecution) throws Exception {

        // ID ( tableId )
        String tableId = (String) delegateExecution.getVariable("tableId");
        log.info(tableId);
        LeaveService leaveService = SpringContextUtil.getBean(LeaveService.class);
        Leave leave = leaveService.get(tableId);
        ... ...
    }
}
 

Example of using the initiation process component

  • Front-end vue example, pay attention to the operation buttons that can only be displayed in each state
<template>
    <process-start
        ref="processStart"
        @on-submit="submitedProcess"
        @on-loading="processLoading=true"
        @on-loaded="processLoading=false"
   />
    <process-cancel ref="processCancel" @on-submit="submitedProcess"/>
</template>

<script>
...
import processStart from '../../../views/my-components/xboot/process-start'
import processCancel from '../../../views/my-components/xboot/process-cancel'

export default {
  name: 'demo',
  components: {
    processStart,
    processCancel
  },
  data () {
    return {
      ...
      columns: [
       // 
        {
          title: ' ',
          key: 'status',
          sortable: true,
          minWidth: 110,
          fixed: 'right',
          render: (h, params) => {
            let text = ' ',
              color = ''
            if (params.row.status === 0) {
              text = ' '
              color = 'default'
            } else if (params.row.status === 1) {
              text = ' '
              color = 'orange'
            } else if (params.row.status === 2) {
              text = ' '
              color = 'blue'
            }
            return h('div', [
              h(
                'Tag',
                {
                  props: {
                    color: color,
                  },
                },
                text,
              ),
            ])
          },
        },
        {
          title: ' ',
          key: 'result',
          sortable: true,
          minWidth: 110,
          fixed: 'right',
          render: (h, params) => {
            let text = ' ',
              color = ''
            if (params.row.result == 0) {
              text = ' '
              color = 'default'
            } else if (params.row.result == 1) {
              text = ' '
              color = 'orange'
            } else if (params.row.result == 2) {
              text = ' '
              color = 'green'
            } else if (params.row.result == 3) {
              text = ' '
              color = 'red'
            }
            return h('div', [
              h(
                'Tag',
                {
                  props: {
                    color: color,
                  },
                },
                text,
              ),
            ])
          },
        },
        {
          title: ' ',
          key: 'action',
          align: 'center',
          fixed: 'right',
          width: 260,
          render: (h, params) => {
            let result = params.row.result
            if (result == 0) {
              return h('div', [
                h(
                  'Button',
                  {
                    props: {
                      type: 'primary',
                      size: 'small',
                    },
                    style: {
                      marginRight: '5px',
                    },
                    on: {
                      click: () => {
                        this.showProcess(params.row)
                      },
                    },
                  },
                  ' ',
                ),
                h(
                  'Button',
                  {
                    props: {
                      size: 'small',
                      icon: 'ios-create-outline',
                    },
                    style: {
                      marginRight: '5px',
                    },
                    on: {
                      click: () => {
                        this.edit(params.row)
                      },
                    },
                  },
                  ' ',
                ),
                h(
                  'Button',
                  {
                    props: {
                      type: 'error',
                      size: 'small',
                      icon: 'md-trash',
                    },
                    on: {
                      click: () => {
                        this.remove(params.row)
                      },
                    },
                  },
                  ' ',
                ),
              ])
            }
            if (result == 1) {
              return h('div', [
                h(
                  'Button',
                  {
                    props: {
                      size: 'small',
                      type: 'warning',
                    },
                    style: {
                      marginRight: '5px',
                    },
                    on: {
                      click: () => {
                        this.cancelProcess(params.row)
                      },
                    },
                  },
                  ' ',
                ),
                h(
                  'Button',
                  {
                    props: {
                      size: 'small',
                    },
                    style: {
                      marginRight: '5px',
                    },
                    on: {
                      click: () => {
                        this.history(params.row)
                      },
                    },
                  },
                  ' ',
                ),
              ])
            }
            if (result == 2) {
                h(
                  'Button',
                  {
                    props: {
                      size: 'small',
                    },
                    style: {
                      marginRight: '5px',
                    },
                    on: {
                      click: () => {
                        this.history(params.row)
                      },
                    },
                  },
                  ' ',
                ),
              ])
            }
            if (result === 3) {
              return h('div', [
                h(
                  'Button',
                  {
                    props: {
                      type: 'primary',
                      size: 'small',
                    },
                    style: {
                      marginRight: '5px',
                    },
                    on: {
                      click: () => {
                        this.showProcess(params.row)
                      },
                    },
                  },
                  ' ',
                ),
                h(
                  'Button',
                  {
                    props: {
                      size: 'small',
                      icon: 'ios-create-outline',
                    },
                    style: {
                      marginRight: '5px',
                    },
                    on: {
                      click: () => {
                        this.edit(params.row)
                      },
                    },
                  },
                  ' ',
                ),
              ])
            }
          },
        },
      ]
    }
  },
  methods: {
    ...
    showProcess (v) {
     // key 
      this.$refs.processStart.show('demand', v.actBusinessId)
    },
    cancelProcess (v) {
     // 
      this.$refs.processCancel.show(v.actBusinessId, v.procInstId)
    },
    submitedProcess () {
     // 
      this.getDataList()
    },
    history (v) {
     // 
      if (!v.procInstId) {
        this.$Message.error(' ID ')
        return
      }
      let query = { id: v.procInstId, backRoute: this.$route.name }
      this.$router.push({
        name: 'historic_detail',
        query: query,
      })
    }}
  }
}
 
  • Back-end business interface, pay attention to the difference from the LeaveController example in the "Workflow-My Application" example, there is no need to pass in the process definition ID, the ActBussines table and the business table are paired with each other to record the ID of the other party for easy query
    @RequestMapping(value = "/add",method = RequestMethod.POST)
    @ApiOperation(value = " ")
    public Result<Object> add(@ModelAttribute Demand demand){

        Demand d = demandService.save(demand);
        // 
        String userId = securityUtil.getCurrUser().getId();
        ActBusiness actBusiness = new ActBusiness();
        actBusiness.setUserId(userId);
        // ID
        actBusiness.setTableId(d.getId());
        ActBusiness a = actBusinessService.save(actBusiness);
        // ID
        d.setActBusinessId(a.getId());
        demandService.update(d);
        return new ResultUtil<Object>().setSuccessMsg(" ");
    }
 
  • Effect preview