RuoYi-Vue工作流构建:可视化流程设计

RuoYi-Vue工作流构建:可视化流程设计

【免费下载链接】RuoYi-Vue 🎉 基于SpringBoot,Spring Security,JWT,Vue & Element 的前后端分离权限管理系统,同时提供了 Vue3 的版本 【免费下载链接】RuoYi-Vue 项目地址: https://gitcode.com/yangzongzhuan/RuoYi-Vue

引言:企业级工作流的迫切需求

在现代企业信息化建设中,业务流程管理(BPM)已成为提升组织效率的核心引擎。传统的纸质审批流程耗时耗力,信息传递不畅,流程监控困难,严重制约了企业的数字化转型进程。RuoYi-Vue作为一款优秀的前后端分离快速开发框架,为企业提供了构建可视化工作流系统的完整解决方案。

通过本文,您将掌握:

  • RuoYi-Vue工作流架构设计与实现原理
  • 可视化流程设计器的集成与应用
  • 基于BPMN 2.0标准的流程建模方法
  • 动态表单与流程引擎的无缝集成
  • 实时流程监控与性能优化策略

工作流技术栈选型与架构设计

核心组件选择

mermaid

技术选型对比表

技术方案优势适用场景集成复杂度
Flowable功能全面,社区活跃复杂业务流程中等
Activiti成熟稳定,文档丰富传统企业应用
Camunda性能优异,扩展性强高并发场景
自研引擎定制灵活,轻量级简单审批流

可视化流程设计器集成

BPMN.js集成配置

// 在ruoyi-ui中集成BPMN设计器
import BpmnModeler from 'bpmn-js/lib/Modeler'
import 'bpmn-js/dist/assets/diagram-js.css'
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css'

export default {
  data() {
    return {
      bpmnModeler: null,
      canvas: null,
      currentXML: ''
    }
  },
  mounted() {
    this.initBpmnModeler()
  },
  methods: {
    initBpmnModeler() {
      this.bpmnModeler = new BpmnModeler({
        container: '#canvas',
        keyboard: { bindTo: document },
        additionalModules: [
          // 自定义模块扩展
        ]
      })
      
      this.loadSampleDiagram()
    },
    
    async loadSampleDiagram() {
      try {
        const { data } = await this.axios.get('/api/bpmn/template')
        const result = await this.bpmnModeler.importXML(data)
        const { warnings } = result
        if (warnings.length) {
          console.warn('BPMN导入警告:', warnings)
        }
        this.canvas = this.bpmnModeler.get('canvas')
        this.canvas.zoom('fit-viewport')
      } catch (err) {
        console.error('BPMN导入错误:', err)
      }
    },
    
    async saveDiagram() {
      try {
        const { xml } = await this.bpmnModeler.saveXML({ format: true })
        this.currentXML = xml
        await this.axios.post('/api/bpmn/deploy', {
          bpmnXml: xml,
          processName: this.processName
        })
        this.$message.success('流程部署成功')
      } catch (error) {
        this.$message.error('流程保存失败')
      }
    }
  }
}

自定义Palette扩展

// 自定义工具栏组件
export default {
  methods: {
    createCustomPalette() {
      const palette = this.bpmnModeler.get('palette')
      
      palette.registerProvider('custom', function() {
        return {
          getPaletteEntries: function(element) {
            return {
              'custom-task': {
                group: 'activity',
                className: 'bpmn-icon-task custom-task',
                title: '自定义任务',
                action: {
                  dragstart: function(event) {
                    // 拖拽开始处理
                  },
                  click: function(event) {
                    // 点击处理
                  }
                }
              }
            }
          }
        }
      })
    }
  }
}

后端流程引擎实现

Flowable集成配置

// Spring Boot配置类
@Configuration
public class FlowableConfig {

    @Bean
    public SpringProcessEngineConfiguration processEngineConfiguration(
            DataSource dataSource, PlatformTransactionManager transactionManager) {
        
        SpringProcessEngineConfiguration config = new SpringProcessEngineConfiguration();
        config.setDataSource(dataSource);
        config.setTransactionManager(transactionManager);
        config.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
        config.setAsyncExecutorActivate(true);
        config.setHistoryLevel(HistoryLevel.AUDIT);
        
        // 自定义行为扩展
        config.setActivityBehaviorFactory(new CustomActivityBehaviorFactory());
        
        return config;
    }

    @Bean
    public ProcessEngine processEngine(SpringProcessEngineConfiguration config) {
        return config.buildProcessEngine();
    }

    @Bean
    public RepositoryService repositoryService(ProcessEngine processEngine) {
        return processEngine.getRepositoryService();
    }

    @Bean
    public RuntimeService runtimeService(ProcessEngine processEngine) {
        return processEngine.getRuntimeService();
    }

    @Bean
    public TaskService taskService(ProcessEngine processEngine) {
        return processEngine.getTaskService();
    }
}

流程部署服务

@Service
public class BpmnDeploymentService {

    @Autowired
    private RepositoryService repositoryService;

    /**
     * 部署BPMN流程定义
     */
    public Deployment deployProcess(String processName, String bpmnXml) {
        Deployment deployment = repositoryService.createDeployment()
                .name(processName)
                .addString(processName + ".bpmn20.xml", bpmnXml)
                .deploy();
        
        // 记录部署日志
        log.info("流程部署成功: {}, 部署ID: {}", processName, deployment.getId());
        return deployment;
    }

    /**
     * 启动流程实例
     */
    public ProcessInstance startProcess(String processDefinitionKey, 
                                       String businessKey, 
                                       Map<String, Object> variables) {
        return runtimeService.startProcessInstanceByKey(
            processDefinitionKey, businessKey, variables);
    }

    /**
     * 查询用户任务
     */
    public List<Task> getUserTasks(String userId) {
        return taskService.createTaskQuery()
                .taskAssignee(userId)
                .orderByTaskCreateTime().desc()
                .list();
    }
}

动态表单系统设计

表单JSON Schema定义

{
  "formId": "leave_approval",
  "formName": "请假审批表单",
  "version": "1.0",
  "fields": [
    {
      "fieldId": "leaveType",
      "fieldName": "请假类型",
      "fieldType": "select",
      "required": true,
      "options": [
        {"label": "年假", "value": "annual"},
        {"label": "病假", "value": "sick"},
        {"label": "事假", "value": "personal"}
      ],
      "rules": [{ "required": true, "message": "请选择请假类型" }]
    },
    {
      "fieldId": "startDate",
      "fieldName": "开始时间",
      "fieldType": "datetime",
      "required": true
    },
    {
      "fieldId": "endDate", 
      "fieldName": "结束时间",
      "fieldType": "datetime",
      "required": true
    },
    {
      "fieldId": "reason",
      "fieldName": "请假事由",
      "fieldType": "textarea",
      "required": true,
      "maxLength": 500
    }
  ],
  "layout": {
    "type": "grid",
    "columns": 2,
    "gutter": 20
  }
}

Vue动态表单渲染器

<template>
  <el-form :model="formData" :rules="formRules" ref="dynamicForm">
    <el-row :gutter="formConfig.layout.gutter">
      <el-col 
        v-for="field in formConfig.fields" 
        :key="field.fieldId"
        :span="field.span || 24/formConfig.layout.columns">
        
        <el-form-item 
          :label="field.fieldName" 
          :prop="field.fieldId"
          :required="field.required">
          
          <component
            :is="getFieldComponent(field.fieldType)"
            v-model="formData[field.fieldId]"
            v-bind="getFieldProps(field)"
            :placeholder="`请输入${field.fieldName}`">
            
            <template v-if="field.fieldType === 'select'">
              <el-option
                v-for="option in field.options"
                :key="option.value"
                :label="option.label"
                :value="option.value">
              </el-option>
            </template>
            
          </component>
        </el-form-item>
      </el-col>
    </el-row>
  </el-form>
</template>

<script>
export default {
  props: {
    formConfig: {
      type: Object,
      required: true
    },
    formData: {
      type: Object,
      default: () => ({})
    }
  },
  computed: {
    formRules() {
      const rules = {}
      this.formConfig.fields.forEach(field => {
        if (field.rules) {
          rules[field.fieldId] = field.rules
        } else if (field.required) {
          rules[field.fieldId] = [
            { required: true, message: `${field.fieldName}不能为空` }
          ]
        }
      })
      return rules
    }
  },
  methods: {
    getFieldComponent(fieldType) {
      const componentMap = {
        'input': 'el-input',
        'textarea': 'el-input',
        'select': 'el-select',
        'datetime': 'el-date-picker',
        'number': 'el-input-number'
      }
      return componentMap[fieldType] || 'el-input'
    },
    
    getFieldProps(field) {
      const props = {}
      switch (field.fieldType) {
        case 'textarea':
          props.type = 'textarea'
          props.rows = 4
          break
        case 'datetime':
          props.type = 'datetime'
          break
        case 'number':
          props.type = 'number'
          break
      }
      return { ...field, ...props }
    }
  }
}
</script>

流程节点与业务逻辑集成

自定义任务监听器

@Component
public class CustomTaskListener implements TaskListener {

    @Autowired
    private SysUserService userService;
    
    @Autowired
    private MessageService messageService;

    @Override
    public void notify(DelegateTask delegateTask) {
        String eventName = delegateTask.getEventName();
        String taskDefinitionKey = delegateTask.getTaskDefinitionKey();
        
        switch (eventName) {
            case EVENTNAME_CREATE:
                handleTaskCreate(delegateTask, taskDefinitionKey);
                break;
            case EVENTNAME_ASSIGNMENT:
                handleTaskAssignment(delegateTask);
                break;
            case EVENTNAME_COMPLETE:
                handleTaskComplete(delegateTask);
                break;
        }
    }

    private void handleTaskCreate(DelegateTask delegateTask, String taskKey) {
        // 根据任务Key设置处理人
        if ("manager_approval".equals(taskKey)) {
            String departmentId = (String) delegateTask.getVariable("departmentId");
            List<SysUser> managers = userService.selectManagersByDept(departmentId);
            if (!managers.isEmpty()) {
                delegateTask.setAssignee(managers.get(0).getUserName());
            }
        }
        
        // 发送任务通知
        messageService.sendTaskNotification(
            delegateTask.getAssignee(),
            delegateTask.getName(),
            delegateTask.getProcessInstanceId()
        );
    }

    private void handleTaskAssignment(DelegateTask delegateTask) {
        // 任务分配处理逻辑
        log.info("任务分配给: {}", delegateTask.getAssignee());
    }

    private void handleTaskComplete(DelegateTask delegateTask) {
        // 任务完成处理逻辑
        Map<String, Object> variables = delegateTask.getVariables();
        log.info("任务完成: {}, 变量: {}", delegateTask.getName(), variables);
    }
}

服务任务实现

@Component
public class ApprovalServiceTask implements JavaDelegate {

    @Autowired
    private ApprovalRecordService approvalRecordService;

    @Override
    public void execute(DelegateExecution execution) {
        String businessKey = execution.getProcessInstanceBusinessKey();
        String taskName = execution.getCurrentActivityName();
        String assignee = (String) execution.getVariable("approver");
        
        // 记录审批操作
        ApprovalRecord record = new ApprovalRecord();
        record.setBusinessKey(businessKey);
        record.setTaskName(taskName);
        record.setApprover(assignee);
        record.setApprovalTime(new Date());
        record.setApprovalResult((String) execution.getVariable("approvalResult"));
        record.setComments((String) execution.getVariable("comments"));
        
        approvalRecordService.insertApprovalRecord(record);
        
        // 更新业务状态
        updateBusinessStatus(execution, record.getApprovalResult());
    }

    private void updateBusinessStatus(DelegateExecution execution, String result) {
        String businessKey = execution.getProcessInstanceBusinessKey();
        String status = "APPROVED".equals(result) ? "approved" : "rejected";
        
        // 根据业务类型更新不同表的状态
        String businessType = (String) execution.getVariable("businessType");
        switch (businessType) {
            case "leave":
                leaveService.updateStatus(businessKey, status);
                break;
            case "expense":
                expenseService.updateStatus(businessKey, status);
                break;
            case "purchase":
                purchaseService.updateStatus(businessKey, status);
                break;
        }
    }
}

高级特性与最佳实践

流程版本控制策略

@Service
public class ProcessVersionService {

    @Autowired
    private RepositoryService repositoryService;

    /**
     * 获取流程定义的最新版本
     */
    public ProcessDefinition getLatestProcessDefinition(String processKey) {
        return repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(processKey)
                .latestVersion()
                .singleResult();
    }

    /**
     * 流程版本迁移
     */
    public void migrateProcessInstances(String oldProcessDefinitionId, 
                                      String newProcessDefinitionId) {
        List<ProcessInstance> instances = runtimeService
                .createProcessInstanceQuery()
                .processDefinitionId(oldProcessDefinitionId)
                .list();

        for (ProcessInstance instance : instances) {
            runtimeService.createProcessInstanceMigrationBuilder()
                    .migrateToProcessDefinition(newProcessDefinitionId)
                    .migrate(instance.getId());
        }
    }
}

性能优化配置

# application-flowable.yml
flowable:
  async-executor:
    enabled: true
    core-pool-size: 10
    max-pool-size: 50
    queue-size: 100
    seconds-to-wait-on-shutdown: 60
  history-level: audit
  database-schema-update: true
  job-executor-activate: true
  mail-server:
    default-from: noreply@company.com
    host: smtp.company.com
    port: 587
    username: ${MAIL_USERNAME}
    password: ${MAIL_PASSWORD}

监控与运维

流程实例监控看板

<template>
  <div class="process-monitor">
    <el-row :gutter="20">
      <el-col :span="6" v-for="metric in metrics" :key="metric.title">
        <el-card>
          <div class="metric-card">
            <div class="metric-value">{{ metric.value }}</div>
            <div class="metric-title">{{ metric.title }}</div>
            <div class="metric-trend" :class="metric.trend">
              <i :class="trendIcon(metric.trend)"></i>
              {{ metric.rate }}%
            </div>
          </div>
        </el-card>
      </el-col>
    </el-row>

    <el-table :data="processInstances" style="width: 100%">
      <el-table-column prop="processDefinitionName" label="流程名称"></el-table-column>
      <el-table-column prop="businessKey" label="业务编号"></el-table-column>
      <el-table-column prop="startTime" label="开始时间"></el-table-column>
      <el-table-column prop="currentActivity" label="当前环节"></el-table-column>
      <el-table-column prop="duration" label="耗时"></el-table-column>
      <el-table-column label="操作">
        <template #default="scope">
          <el-button @click="viewDetails(scope.row)" type="text">详情</el-button>
          <el-button @click="terminateProcess(scope.row)" type="text" v-if="scope.row.suspended">终止</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
export default {
  data() {
    return {
      metrics: [
        { title: '运行中流程', value: 128, trend: 'up', rate: 12 },
        { title: '已完成流程', value: 456, trend: 'up', rate: 8 },
        { title: '异常流程', value: 5, trend: 'down', rate: -20 },
        { title: '平均耗时', value: '2.5h', trend: 'down', rate: -5 }
      ],
      processInstances: []
    }
  },
  mounted() {
    this.loadProcessInstances()
    this.startAutoRefresh()
  },
  methods: {
    async loadProcessInstances() {
      const { data } = await this.axios.get('/api/process/instances')
      this.processInstances = data
    },
    
    startAutoRefresh() {
      setInterval(() => {
        this.loadProcessInstances()
      }, 30000) // 30秒刷新一次
    },

【免费下载链接】RuoYi-Vue 🎉 基于SpringBoot,Spring Security,JWT,Vue & Element 的前后端分离权限管理系统,同时提供了 Vue3 的版本 【免费下载链接】RuoYi-Vue 项目地址: https://gitcode.com/yangzongzhuan/RuoYi-Vue

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值