Skip to content

工作流演示开发文档

1. 概述

梵医云工作流模块基于 Flowable 6 实现,提供完整的业务流程管理(Business Process Management)功能。本文档将详细介绍工作流的演示开发流程,帮助开发者快速上手工作流功能。

2. 工作流核心概念

2.1 核心组件

组件描述对应类
流程模型工作流的设计蓝图,包含流程定义和表单配置BpmModel
流程定义已部署的流程模型,可用于创建流程实例ProcessDefinition
流程实例流程的具体执行实例,对应一次业务流程的运行ProcessInstance
流程任务流程中的具体任务,需要用户或系统处理Task
流程活动流程执行过程中的各种活动,如开始、结束、用户任务等Activity
流程表单与流程关联的表单,用于收集和展示数据BpmFormDO

2.2 任务分配策略

工作流支持 7 种任务分配规则:

策略描述实现类
用户分配直接指定具体用户BpmTaskCandidateUserStrategy
角色分配基于用户角色分配BpmTaskCandidateRoleStrategy
部门领导分配给部门领导BpmTaskCandidateDeptLeaderStrategy
部门成员分配给部门所有成员BpmTaskCandidateDeptMemberStrategy
岗位分配基于用户岗位分配BpmTaskCandidatePostStrategy
分组分配基于用户分组分配BpmTaskCandidateGroupStrategy
表达式分配基于表达式动态计算BpmTaskCandidateExpressionStrategy
发起人选择由流程发起人选择审批人BpmTaskCandidateStartUserSelectStrategy

3. 工作流演示开发步骤

3.1 步骤一:创建流程模型

3.1.1 在线设计流程

  1. 访问流程模型管理:进入后台管理系统,点击「工作流程」→「流程模型」
  2. 创建新模型:点击「新建」按钮,填写模型名称和描述
  3. 设计流程图
    • 拖拽左侧组件到画布
    • 连接各个节点形成流程
    • 配置每个节点的属性

3.1.2 配置用户任务

  1. 选择用户任务节点
  2. 配置任务属性
    • 任务名称:如「部门领导审批」
    • 分配类型:选择合适的任务分配策略
    • 审批人:根据分配类型设置具体值

3.1.3 关联流程表单

  1. 进入流程表单管理:点击「工作流程」→「流程表单」
  2. 创建新表单
    • 拖拽表单元素生成表单
    • 配置表单字段属性
    • 保存表单
  3. 关联表单:在流程模型设计时,为用户任务节点关联对应的表单

3.2 步骤二:部署流程定义

  1. 发布流程模型:在流程模型列表中,点击「发布」按钮
  2. 验证部署:进入「流程定义」页面,查看已部署的流程

3.3 步骤三:创建流程实例

3.3.1 前端发起流程

vue
<template>
  <el-button type="primary" @click="startProcess">发起流程</el-button>
</template>

<script>
export default {
  methods: {
    async startProcess() {
      const formData = {
        // 表单数据
      };
      const response = await this.$http.post('/bpm/process-instance/create', {
        processDefinitionId: '流程定义ID',
        variables: formData,
        businessKey: '业务标识'
      });
      if (response.code === 0) {
        this.$message.success('流程发起成功');
      }
    }
  }
};
</script>

3.3.2 后端创建流程实例

java
@Service
public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService {

    @Override
    public String createProcessInstance(BpmProcessInstanceCreateReqVO createReqVO) {
        // 1. 获取流程定义
        ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(
                createReqVO.getProcessDefinitionId());
        
        // 2. 构建流程变量
        Map<String, Object> variables = createReqVO.getVariables();
        
        // 3. 创建流程实例
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(
                createReqVO.getProcessDefinitionId(),
                createReqVO.getBusinessKey(),
                variables
        );
        
        // 4. 保存流程实例信息
        // ...
        
        return processInstance.getId();
    }
}

3.4 步骤四:处理流程任务

3.4.1 待办任务列表

前端代码

vue
<template>
  <el-table :data="taskList">
    <el-table-column prop="name" label="任务名称" />
    <el-table-column prop="processInstanceName" label="流程名称" />
    <el-table-column label="操作">
      <template slot-scope="scope">
        <el-button @click="approveTask(scope.row.id, true)">通过</el-button>
        <el-button @click="approveTask(scope.row.id, false)">拒绝</el-button>
      </template>
    </el-table-column>
  </el-table>
</template>

<script>
export default {
  data() {
    return {
      taskList: []
    };
  },
  mounted() {
    this.loadTasks();
  },
  methods: {
    async loadTasks() {
      const response = await this.$http.get('/bpm/task/page');
      if (response.code === 0) {
        this.taskList = response.data.list;
      }
    },
    async approveTask(taskId, approved) {
      const response = await this.$http.post('/bpm/task/approve', {
        id: taskId,
        approved: approved,
        reason: approved ? '同意' : '不同意'
      });
      if (response.code === 0) {
        this.$message.success('审批成功');
        this.loadTasks();
      }
    }
  }
};
</script>

3.4.2 任务审批处理

后端代码

java
@Service
public class BpmTaskServiceImpl implements BpmTaskService {

    @Override
    public void approveTask(String id, BpmTaskApproveReqVO reqVO) {
        // 1. 获取任务
        Task task = taskService.createTaskQuery().taskId(id).singleResult();
        
        // 2. 验证任务状态
        // ...
        
        // 3. 设置任务变量
        Map<String, Object> variables = new HashMap<>();
        variables.put(BpmConstants.TASK_VARIABLE_STATUS, reqVO.getApproved() ? "APPROVED" : "REJECTED");
        variables.put(BpmConstants.TASK_VARIABLE_REASON, reqVO.getReason());
        
        // 4. 完成任务
        taskService.complete(id, variables);
        
        // 5. 处理审批结果
        // ...
    }
}

3.5 步骤五:流程状态跟踪

3.5.1 流程实例详情

前端代码

vue
<template>
  <div>
    <h3>流程详情</h3>
    <el-descriptions :column="2">
      <el-descriptions-item label="流程名称">{{ processInstance.name }}</el-descriptions-item>
      <el-descriptions-item label="状态">{{ getStatusText(processInstance.status) }}</el-descriptions-item>
      <el-descriptions-item label="发起人">{{ processInstance.startUser }}</el-descriptions-item>
      <el-descriptions-item label="发起时间">{{ formatDate(processInstance.startTime) }}</el-descriptions-item>
    </el-descriptions>
    
    <h3>审批 timeline</h3>
    <el-timeline>
      <el-timeline-item
        v-for="activity in activities"
        :key="activity.id"
        :timestamp="formatDate(activity.startTime)"
        :type="activity.type === 'approve' ? 'success' : 'warning'"
      >
        {{ activity.name }} - {{ activity.assignee }}
        <div v-if="activity.reason">{{ activity.reason }}</div>
      </el-timeline-item>
    </el-timeline>
  </div>
</template>

<script>
export default {
  data() {
    return {
      processInstance: {},
      activities: []
    };
  },
  mounted() {
    this.loadProcessInstance();
    this.loadActivities();
  },
  methods: {
    async loadProcessInstance() {
      const response = await this.$http.get(`/bpm/process-instance/get?id=${this.$route.params.id}`);
      if (response.code === 0) {
        this.processInstance = response.data;
      }
    },
    async loadActivities() {
      const response = await this.$http.get(`/bpm/activity/list?processInstanceId=${this.$route.params.id}`);
      if (response.code === 0) {
        this.activities = response.data;
      }
    },
    getStatusText(status) {
      const statusMap = {
        'RUNNING': '运行中',
        'COMPLETED': '已完成',
        'CANCELLED': '已取消'
      };
      return statusMap[status] || status;
    },
    formatDate(date) {
      return this.$moment(date).format('YYYY-MM-DD HH:mm:ss');
    }
  }
};
</script>

4. 工作流演示示例

4.1 OA 请假流程

4.1.1 流程设计

  1. 创建流程模型

    • 流程名称:OA 请假流程
    • 流程标识:oa_leave
    • 流程分类:OA 流程
  2. 设计流程图

    • 开始事件 → 填写请假表单 → 部门领导审批 → 人事审批 → 结束事件
  3. 配置表单

    • 请假类型:下拉选择(事假、病假、年假等)
    • 请假开始时间:日期时间选择器
    • 请假结束时间:日期时间选择器
    • 请假天数:计算字段
    • 请假原因:文本域
  4. 配置任务分配

    • 部门领导审批:部门领导策略
    • 人事审批:角色分配(人事专员)

4.1.2 代码实现

流程发起

java
@RestController
@RequestMapping("/bpm/oa/leave")
public class BpmOALeaveController {

    @PostMapping("/create")
    public CommonResult<Long> createBpmOALeave(@Valid @RequestBody BpmOALeaveCreateReqVO createReqVO) {
        return success(bpmOALeaveService.createBpmOALeave(createReqVO));
    }
}

流程服务

java
@Service
public class BpmOALeaveServiceImpl implements BpmOALeaveService {

    @Override
    public Long createBpmOALeave(BpmOALeaveCreateReqVO createReqVO) {
        // 1. 计算请假天数
        long days = DateUtils.betweenDays(createReqVO.getStartTime(), createReqVO.getEndTime()) + 1;
        
        // 2. 保存请假记录
        BpmOALeaveDO leave = BpmOALeaveConvert.INSTANCE.convert(createReqVO);
        leave.setDays(days);
        leaveMapper.insert(leave);
        
        // 3. 发起流程
        BpmProcessInstanceCreateReqDTO processInstanceCreateReqDTO = new BpmProcessInstanceCreateReqDTO();
        processInstanceCreateReqDTO.setProcessDefinitionId(createReqVO.getProcessDefinitionId());
        processInstanceCreateReqDTO.setBusinessKey(leave.getId().toString());
        
        Map<String, Object> variables = new HashMap<>();
        variables.put("leaveId", leave.getId());
        variables.put("leaveType", createReqVO.getType());
        variables.put("startTime", createReqVO.getStartTime());
        variables.put("endTime", createReqVO.getEndTime());
        variables.put("days", days);
        variables.put("reason", createReqVO.getReason());
        processInstanceCreateReqDTO.setVariables(variables);
        
        String processInstanceId = bpmProcessInstanceApi.createProcessInstance(processInstanceCreateReqDTO);
        
        // 4. 更新请假记录的流程实例ID
        leave.setProcessInstanceId(processInstanceId);
        leaveMapper.updateById(leave);
        
        return leave.getId();
    }
}

4.2 采购审批流程

4.2.1 流程设计

  1. 创建流程模型

    • 流程名称:采购审批流程
    • 流程标识:purchase_approval
    • 流程分类:业务流程
  2. 设计流程图

    • 开始事件 → 填写采购申请 → 部门领导审批 → 财务审批 → 总经理审批 → 结束事件
  3. 配置表单

    • 采购物品:文本框
    • 采购数量:数字输入框
    • 采购金额:数字输入框
    • 采购原因:文本域
    • 供应商信息:文本框
  4. 配置任务分配

    • 部门领导审批:部门领导策略
    • 财务审批:角色分配(财务专员)
    • 总经理审批:条件网关(金额 > 10000 时需要)

5. 工作流高级功能

5.1 流程监听器

流程监听器可以在流程执行的不同阶段触发自定义逻辑:

java
public class BpmProcessInstanceEventListener {

    @EventListener
    public void onProcessStarted(ProcessStartedEvent event) {
        // 流程开始时的处理逻辑
        log.info("Process started: {}", event.getProcessInstanceId());
    }

    @EventListener
    public void onProcessCompleted(ProcessCompletedEvent event) {
        // 流程完成时的处理逻辑
        log.info("Process completed: {}", event.getProcessInstanceId());
    }

    @EventListener
    public void onTaskCreated(TaskCreatedEvent event) {
        // 任务创建时的处理逻辑
        log.info("Task created: {}", event.getTask().getId());
        // 发送任务通知
        bpmMessageService.sendTaskCreatedMessage(event.getTask());
    }

    @EventListener
    public void onTaskCompleted(TaskCompletedEvent event) {
        // 任务完成时的处理逻辑
        log.info("Task completed: {}", event.getTask().getId());
    }
}

5.2 自定义任务分配策略

如果内置的任务分配策略不能满足需求,可以自定义策略:

java
@Component
public class CustomTaskCandidateStrategy implements BpmTaskCandidateStrategy {

    @Override
    public List<Long> calculateCandidates(UserTask userTask, BpmTaskCandidateStrategyContext context) {
        // 自定义任务分配逻辑
        // 例如:基于业务规则计算审批人
        return Collections.singletonList(1L); // 返回审批人ID列表
    }

    @Override
    public String getType() {
        return "custom"; // 策略类型标识
    }
}

5.3 流程变量管理

流程变量用于在流程执行过程中传递数据:

java
// 设置流程变量
runtimeService.setVariable(processInstanceId, "key", "value");

// 获取流程变量
Object value = runtimeService.getVariable(processInstanceId, "key");

// 获取所有流程变量
Map<String, Object> variables = runtimeService.getVariables(processInstanceId);

6. 工作流最佳实践

6.1 设计原则

  1. 流程清晰:流程图应该清晰易懂,避免过于复杂的分支逻辑
  2. 表单简洁:表单字段应该简洁明了,只包含必要的信息
  3. 权限明确:任务分配策略应该明确,避免审批人不明确的情况
  4. 业务关联:流程应该与业务系统紧密关联,通过 businessKey 建立联系
  5. 异常处理:考虑流程执行过程中的异常情况,如审批人离职等

6.2 性能优化

  1. 流程设计优化

    • 避免过长的流程链
    • 合理使用子流程
    • 减少不必要的网关
  2. 数据库优化

    • 为 Flowable 表添加索引
    • 定期清理历史数据
    • 使用合适的数据库连接池配置
  3. 代码优化

    • 避免在流程监听器中执行耗时操作
    • 使用异步任务处理复杂逻辑
    • 合理使用缓存减少数据库查询

6.3 常见问题

问题原因解决方案
流程无法启动流程定义未激活检查流程定义状态,确保已激活
任务分配错误任务分配策略配置错误检查任务分配策略配置,确保正确设置审批人
流程卡住网关条件不满足检查网关条件表达式,确保流程能够正常流转
性能问题流程实例过多定期清理历史流程实例,优化流程设计

7. 开发工具和调试

7.1 开发工具

  • Flowable Modeler:在线流程设计器
  • Flowable Admin:流程管理控制台
  • Flowable IDM:身份管理系统

7.2 调试技巧

  1. 查看流程实例状态

    • 通过 runtimeService.getProcessInstance(processInstanceId) 获取流程实例
    • 通过 historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult() 获取历史流程实例
  2. 查看任务状态

    • 通过 taskService.createTaskQuery().processInstanceId(processInstanceId).list() 获取任务列表
    • 通过 historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).list() 获取历史任务
  3. 查看流程变量

    • 通过 runtimeService.getVariables(processInstanceId) 获取流程变量
    • 通过 historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).list() 获取历史变量
  4. 查看流程日志

    • 配置 Flowable 日志级别为 DEBUG
    • 查看应用服务器日志

8. 总结

工作流是梵医云系统的重要组成部分,基于 Flowable 6 实现了完整的业务流程管理功能。本文档介绍了工作流的核心概念、开发步骤、演示示例和最佳实践,希望能够帮助开发者快速上手工作流开发。

通过合理的流程设计和代码实现,可以构建高效、可靠的业务流程系统,提高企业的运营效率和管理水平。

9. 参考资料