- 新增工作流框架开发指南文档 (warm-flow工作流框架开发指南.md) 包含技术架构、核心组件、开发流程、请假工作流示例等完整内容 - 新增工作流开发规范 (.cursor/cursorrules-workflow.md) 整合到cursorrules体系,包含强制性VO类规范、状态管理、前后端开发模板 - 新增工作流开发最佳实践总结 (Warm-Flow工作流开发最佳实践总结.md) 基于请假工作流实践的快速开始模板、开发效率统计、常见问题解决方案 - 更新主配置文件 (.cursorrules) 添加工作流规范引用,确保Cursor AI在工作流开发时自动应用规范 - 新增变更记录 (code-adjust-record-2025-01-09-wuzilong.md) 详细记录本次工作流文档建设的所有变更内容 关键成果: - 建立了7种标准工作流状态管理体系 - 总结了6种常用工作流模式 (线性、排他网关、并行网关、会签等) - 提供了完整的前后端开发模板和最佳实践 - 解决了VO类基础字段规范等关键技术问题 - 形成了3.5天标准开发周期的高效开发流程
1091 lines
33 KiB
Markdown
1091 lines
33 KiB
Markdown
# Warm-Flow 工作流框架开发指南
|
|
|
|
## 📋 目录
|
|
|
|
- [1. 工作流框架概述](#1-工作流框架概述)
|
|
- [2. 技术架构](#2-技术架构)
|
|
- [3. 核心组件](#3-核心组件)
|
|
- [4. 工作流开发流程](#4-工作流开发流程)
|
|
- [5. 请假工作流示例](#5-请假工作流示例)
|
|
- [6. 前端组件使用](#6-前端组件使用)
|
|
- [7. 工作流状态管理](#7-工作流状态管理)
|
|
- [8. 最佳实践](#8-最佳实践)
|
|
- [9. 常见问题](#9-常见问题)
|
|
|
|
---
|
|
|
|
## 1. 工作流框架概述
|
|
|
|
### 1.1 框架介绍
|
|
|
|
本项目集成了 **warm-flow** 工作流框架,这是由 dromara 社区开源的轻量级工作流引擎,具有以下特点:
|
|
|
|
- 🚀 **轻量级**: 核心代码简洁,易于理解和扩展
|
|
- 🔧 **可配置**: 支持通过配置文件动态开启/关闭工作流功能
|
|
- 🎯 **易集成**: 与 Spring Boot 无缝集成,支持 MyBatis-Plus
|
|
- 📊 **可视化**: 内置流程设计器和流程图展示
|
|
- 🔐 **权限控制**: 集成 Sa-Token 权限认证
|
|
- 📧 **消息通知**: 支持站内信、邮件、短信通知
|
|
|
|
### 1.2 配置开关
|
|
|
|
工作流功能通过 `@ConditionalOnEnable` 注解控制,配置如下:
|
|
|
|
```yaml
|
|
# application.yml
|
|
warm-flow:
|
|
enabled: true # 设置为 true 时,启用工作流功能
|
|
```
|
|
|
|
---
|
|
|
|
## 2. 技术架构
|
|
|
|
### 2.1 技术栈
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ 前端层 (Vue 3) │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ - approvalButton (审批按钮组件) │
|
|
│ - submitVerify (提交审批组件) │
|
|
│ - approvalRecord (审批记录组件) │
|
|
│ - flowChart (流程图组件) │
|
|
└─────────────────────────────────────────────────────────┘
|
|
│
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ 后端层 (Spring Boot) │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ - FlwTaskController (任务管理控制器) │
|
|
│ - FlwInstanceController (流程实例控制器) │
|
|
│ - FlwDefinitionController(流程定义控制器) │
|
|
│ - TestLeaveController (请假业务控制器) │
|
|
└─────────────────────────────────────────────────────────┘
|
|
│
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ 服务层 (Service) │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ - IFlwTaskService (任务服务) │
|
|
│ - IFlwInstanceService (实例服务) │
|
|
│ - IFlwDefinitionService (定义服务) │
|
|
│ - IFlwCommonService (通用服务) │
|
|
│ - ITestLeaveService (请假业务服务) │
|
|
└─────────────────────────────────────────────────────────┘
|
|
│
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Warm-Flow 核心引擎 │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ - FlowEngine (工作流引擎) │
|
|
│ - DefService (流程定义服务) │
|
|
│ - InsService (流程实例服务) │
|
|
│ - TaskService (任务服务) │
|
|
│ - NodeService (节点服务) │
|
|
│ - HisTaskService (历史任务服务) │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### 2.2 数据库表结构
|
|
|
|
warm-flow 使用以下核心表:
|
|
|
|
```sql
|
|
-- 流程定义表
|
|
flow_definition -- 存储流程定义信息
|
|
flow_node -- 存储流程节点信息
|
|
flow_skip -- 存储节点间跳转关系
|
|
|
|
-- 流程实例表
|
|
flow_instance -- 存储流程实例信息
|
|
flow_task -- 存储当前待办任务
|
|
flow_his_task -- 存储历史任务记录
|
|
|
|
-- 扩展表
|
|
flow_category -- 流程分类表
|
|
flow_copy -- 抄送表
|
|
```
|
|
|
|
---
|
|
|
|
## 3. 核心组件
|
|
|
|
### 3.1 后端核心服务
|
|
|
|
#### 3.1.1 流程定义服务 (IFlwDefinitionService)
|
|
|
|
```java
|
|
public interface IFlwDefinitionService {
|
|
// 查询流程定义列表
|
|
TableDataInfo<FlowDefinitionVo> queryList(FlowDefinition flowDefinition, PageQuery pageQuery);
|
|
|
|
// 发布流程定义
|
|
boolean publish(Long id);
|
|
|
|
// 导入流程定义
|
|
boolean importJson(MultipartFile file, String category);
|
|
|
|
// 删除流程定义
|
|
boolean removeDef(List<Long> ids);
|
|
}
|
|
```
|
|
|
|
#### 3.1.2 流程实例服务 (IFlwInstanceService)
|
|
|
|
```java
|
|
public interface IFlwInstanceService {
|
|
// 查询运行中的流程实例
|
|
TableDataInfo<FlowInstanceVo> selectRunningInstanceList(FlowInstanceBo bo, PageQuery pageQuery);
|
|
|
|
// 查询已结束的流程实例
|
|
TableDataInfo<FlowInstanceVo> selectFinishInstanceList(FlowInstanceBo bo, PageQuery pageQuery);
|
|
|
|
// 撤销流程
|
|
boolean cancelProcessApply(FlowCancelBo bo);
|
|
|
|
// 作废流程
|
|
boolean invalid(FlowInvalidBo bo);
|
|
}
|
|
```
|
|
|
|
#### 3.1.3 任务服务 (IFlwTaskService)
|
|
|
|
```java
|
|
public interface IFlwTaskService {
|
|
// 启动工作流
|
|
StartProcessReturnDTO startWorkFlow(StartProcessBo startProcessBo);
|
|
|
|
// 办理任务
|
|
boolean completeTask(CompleteTaskBo completeTaskBo);
|
|
|
|
// 查询待办任务
|
|
TableDataInfo<FlowTaskVo> pageByTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery);
|
|
|
|
// 查询已办任务
|
|
TableDataInfo<FlowHisTaskVo> pageByTaskFinish(FlowTaskBo flowTaskBo, PageQuery pageQuery);
|
|
}
|
|
```
|
|
|
|
### 3.2 前端核心组件
|
|
|
|
#### 3.2.1 审批按钮组件 (approvalButton.vue)
|
|
|
|
```vue
|
|
<template>
|
|
<div style="display: flex; justify-content: space-between">
|
|
<div>
|
|
<el-button
|
|
v-if="submitButtonShow"
|
|
:loading="buttonLoading"
|
|
type="info"
|
|
@click="submitForm('draft')"
|
|
>
|
|
暂存
|
|
</el-button>
|
|
<el-button
|
|
v-if="submitButtonShow"
|
|
:loading="buttonLoading"
|
|
type="primary"
|
|
@click="submitForm('submit')"
|
|
>
|
|
提 交
|
|
</el-button>
|
|
<el-button
|
|
v-if="approvalButtonShow"
|
|
:loading="buttonLoading"
|
|
type="primary"
|
|
@click="approvalVerifyOpen"
|
|
>
|
|
审批
|
|
</el-button>
|
|
<el-button v-if="id && status !== 'draft'" type="primary" @click="handleApprovalRecord">
|
|
流程进度
|
|
</el-button>
|
|
</div>
|
|
<div>
|
|
<el-button style="float: right" @click="goBack()">返回</el-button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
```
|
|
|
|
**功能特点:**
|
|
|
|
- 根据页面类型和流程状态动态显示按钮
|
|
- 支持暂存、提交、审批、查看流程进度等操作
|
|
- 自动处理按钮的显示/隐藏逻辑
|
|
|
|
#### 3.2.2 提交审批组件 (submitVerify.vue)
|
|
|
|
```vue
|
|
<!-- 核心功能 -->
|
|
<el-form-item label="消息提醒">
|
|
<el-checkbox-group v-model="form.messageType">
|
|
<el-checkbox value="1" disabled>站内信</el-checkbox>
|
|
<el-checkbox value="2">邮件</el-checkbox>
|
|
<el-checkbox value="3">短信</el-checkbox>
|
|
</el-checkbox-group>
|
|
</el-form-item>
|
|
|
|
<el-form-item label="下一步审批人" v-if="nestNodeList?.length > 0">
|
|
<div v-for="item in nestNodeList" :key="item.nodeCode">
|
|
<span>【{{ item.nodeName }}】:</span>
|
|
<el-input placeholder="请选择审批人" readonly v-model="nickName[item.nodeCode]">
|
|
<template v-slot:append>
|
|
<el-button @click="choosePeople(item)" icon="search">选择</el-button>
|
|
</template>
|
|
</el-input>
|
|
</div>
|
|
</el-form-item>
|
|
```
|
|
|
|
**功能特点:**
|
|
|
|
- 支持消息通知配置(站内信、邮件、短信)
|
|
- 支持附件上传
|
|
- 支持抄送人员选择
|
|
- 支持下一步审批人选择
|
|
- 支持审批意见填写
|
|
- 支持多种操作:提交、委托、转办、加签、减签、终止、退回
|
|
|
|
#### 3.2.3 审批记录组件 (approvalRecord.vue)
|
|
|
|
```vue
|
|
<el-tabs v-model="tabActiveName">
|
|
<el-tab-pane label="流程图" name="image">
|
|
<flowChart :ins-id="insId" v-if="insId" />
|
|
</el-tab-pane>
|
|
<el-tab-pane label="审批信息" name="info">
|
|
<el-table :data="historyList" border>
|
|
<el-table-column prop="nodeName" label="任务名称" />
|
|
<el-table-column prop="approveName" label="办理人" />
|
|
<el-table-column prop="flowStatus" label="状态" />
|
|
<el-table-column prop="message" label="审批意见" />
|
|
<el-table-column prop="createTime" label="开始时间" />
|
|
<el-table-column prop="updateTime" label="结束时间" />
|
|
</el-table>
|
|
</el-tab-pane>
|
|
</el-tabs>
|
|
```
|
|
|
|
**功能特点:**
|
|
|
|
- 流程图可视化展示
|
|
- 详细的审批记录表格
|
|
- 支持附件下载
|
|
- 审批时长统计
|
|
|
|
---
|
|
|
|
## 4. 工作流开发流程
|
|
|
|
### 4.1 后端开发步骤
|
|
|
|
#### 步骤 1: 创建业务实体
|
|
|
|
```java
|
|
// 1. 创建业务Bo类
|
|
@Data
|
|
@EqualsAndHashCode(callSuper = true)
|
|
public class TestLeaveBo extends BaseEntity {
|
|
private Long id;
|
|
private String leaveType;
|
|
private String startDate;
|
|
private String endDate;
|
|
private Integer leaveDays;
|
|
private String remark;
|
|
private String status;
|
|
}
|
|
|
|
// 2. 创建业务Vo类
|
|
@Data
|
|
@ExcelIgnoreUnannotated
|
|
public class TestLeaveVo {
|
|
private Long id;
|
|
private String leaveType;
|
|
private String startDate;
|
|
private String endDate;
|
|
private Integer leaveDays;
|
|
private String remark;
|
|
private String status;
|
|
|
|
// 基础字段(重要:避免MyBatis映射错误)
|
|
private Long createBy;
|
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
|
private Date createTime;
|
|
private Long updateBy;
|
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
|
private Date updateTime;
|
|
}
|
|
```
|
|
|
|
#### 步骤 2: 创建 Service 接口和实现
|
|
|
|
```java
|
|
// Service接口
|
|
public interface ITestLeaveService {
|
|
TestLeaveVo queryById(Long id);
|
|
TableDataInfo<TestLeaveVo> queryPageList(TestLeaveBo bo, PageQuery pageQuery);
|
|
List<TestLeaveVo> queryList(TestLeaveBo bo);
|
|
TestLeaveVo insertByBo(TestLeaveBo bo);
|
|
TestLeaveVo updateByBo(TestLeaveBo bo);
|
|
Boolean deleteWithValidByIds(List<Long> ids);
|
|
}
|
|
|
|
// Service实现类
|
|
@Service
|
|
@RequiredArgsConstructor
|
|
public class TestLeaveServiceImpl implements ITestLeaveService {
|
|
private final TestLeaveMapper baseMapper;
|
|
|
|
@Override
|
|
public TestLeaveVo insertByBo(TestLeaveBo bo) {
|
|
TestLeave add = BeanUtil.toBean(bo, TestLeave.class);
|
|
// 设置默认状态为草稿
|
|
add.setStatus("draft");
|
|
validEntityBeforeSave(add);
|
|
boolean flag = baseMapper.insert(add) > 0;
|
|
if (flag) {
|
|
bo.setId(add.getId());
|
|
return BeanUtil.toBean(add, TestLeaveVo.class);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// 其他CRUD方法实现...
|
|
}
|
|
```
|
|
|
|
#### 步骤 3: 创建 Controller
|
|
|
|
```java
|
|
@ConditionalOnEnable // 工作流条件开启注解
|
|
@Validated
|
|
@RequiredArgsConstructor
|
|
@RestController
|
|
@RequestMapping("/workflow/leave")
|
|
public class TestLeaveController extends BaseController {
|
|
|
|
private final ITestLeaveService testLeaveService;
|
|
|
|
@SaCheckPermission("workflow:leave:list")
|
|
@GetMapping("/list")
|
|
public TableDataInfo<TestLeaveVo> list(TestLeaveBo bo, PageQuery pageQuery) {
|
|
return testLeaveService.queryPageList(bo, pageQuery);
|
|
}
|
|
|
|
@SaCheckPermission("workflow:leave:add")
|
|
@PostMapping()
|
|
public R<TestLeaveVo> add(@Validated(AddGroup.class) @RequestBody TestLeaveBo bo) {
|
|
return R.ok(testLeaveService.insertByBo(bo));
|
|
}
|
|
|
|
@SaCheckPermission("workflow:leave:edit")
|
|
@PutMapping()
|
|
public R<TestLeaveVo> edit(@Validated(EditGroup.class) @RequestBody TestLeaveBo bo) {
|
|
return R.ok(testLeaveService.updateByBo(bo));
|
|
}
|
|
|
|
// 其他CRUD接口...
|
|
}
|
|
```
|
|
|
|
### 4.2 前端开发步骤
|
|
|
|
#### 步骤 1: 创建 API 接口
|
|
|
|
```typescript
|
|
// api/workflow/leave/types.ts
|
|
export interface LeaveForm extends BaseEntity {
|
|
id?: string | number;
|
|
leaveType?: string;
|
|
startDate?: string;
|
|
endDate?: string;
|
|
leaveDays?: number;
|
|
remark?: string;
|
|
status?: string;
|
|
}
|
|
|
|
export interface LeaveVO extends BaseEntity {
|
|
id?: string | number;
|
|
leaveType?: string;
|
|
startDate?: string;
|
|
endDate?: string;
|
|
leaveDays?: number;
|
|
remark?: string;
|
|
status?: string;
|
|
}
|
|
|
|
// api/workflow/leave/index.ts
|
|
export const listLeave = (query: LeaveQuery): AxiosPromise<LeaveVO[]> => {
|
|
return request({
|
|
url: '/workflow/leave/list',
|
|
method: 'get',
|
|
params: query,
|
|
});
|
|
};
|
|
|
|
export const addLeave = (data: LeaveForm): AxiosPromise<LeaveVO> => {
|
|
return request({
|
|
url: '/workflow/leave',
|
|
method: 'post',
|
|
data: data,
|
|
});
|
|
};
|
|
```
|
|
|
|
#### 步骤 2: 创建列表页面
|
|
|
|
```vue
|
|
<!-- views/workflow/leave/index.vue -->
|
|
<template>
|
|
<div class="p-2">
|
|
<!-- 搜索表单 -->
|
|
<div v-show="showSearch" class="search">
|
|
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
|
<el-form-item label="请假天数" prop="startLeaveDays">
|
|
<el-input v-model="queryParams.startLeaveDays" placeholder="请输入请假天数" />
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
|
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
</div>
|
|
|
|
<!-- 数据表格 -->
|
|
<el-card shadow="never">
|
|
<template #header>
|
|
<el-row :gutter="10" class="mb8">
|
|
<el-col :span="1.5">
|
|
<el-button
|
|
v-hasPermi="['workflow:leave:add']"
|
|
type="primary"
|
|
plain
|
|
icon="Plus"
|
|
@click="handleAdd"
|
|
>
|
|
新增
|
|
</el-button>
|
|
</el-col>
|
|
</el-row>
|
|
</template>
|
|
|
|
<el-table v-loading="loading" border :data="leaveList">
|
|
<el-table-column type="selection" width="55" align="center" />
|
|
<el-table-column label="请假类型" align="center">
|
|
<template #default="scope">
|
|
<el-tag>{{ getLeaveTypeLabel(scope.row.leaveType) }}</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="请假天数" align="center" prop="leaveDays" />
|
|
<el-table-column label="流程状态" align="center">
|
|
<template #default="scope">
|
|
<dict-tag :options="wf_business_status" :value="scope.row.status" />
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="操作" align="center">
|
|
<template #default="scope">
|
|
<!-- 根据状态显示不同操作按钮 -->
|
|
<el-button
|
|
v-if="['draft', 'cancel', 'back'].includes(scope.row.status)"
|
|
type="primary"
|
|
size="small"
|
|
icon="Edit"
|
|
@click="handleUpdate(scope.row)"
|
|
>
|
|
修改
|
|
</el-button>
|
|
<el-button
|
|
type="primary"
|
|
size="small"
|
|
icon="View"
|
|
@click="handleView(scope.row)"
|
|
>
|
|
查看
|
|
</el-button>
|
|
<el-button
|
|
v-if="scope.row.status === 'waiting'"
|
|
size="small"
|
|
type="primary"
|
|
icon="Notification"
|
|
@click="handleCancel(scope.row.id)"
|
|
>
|
|
撤销
|
|
</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</el-card>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { listLeave, delLeave } from '@/api/workflow/leave';
|
|
import { cancelProcessApply } from '@/api/workflow/instance';
|
|
|
|
// 组件逻辑...
|
|
</script>
|
|
```
|
|
|
|
#### 步骤 3: 创建编辑页面
|
|
|
|
```vue
|
|
<!-- views/workflow/leave/leaveEdit.vue -->
|
|
<template>
|
|
<div class="p-2">
|
|
<!-- 审批按钮 -->
|
|
<el-card shadow="never">
|
|
<approvalButton
|
|
@submitForm="submitForm"
|
|
@approvalVerifyOpen="approvalVerifyOpen"
|
|
@handleApprovalRecord="handleApprovalRecord"
|
|
:buttonLoading="buttonLoading"
|
|
:id="form.id"
|
|
:status="form.status"
|
|
:pageType="routeParams.type"
|
|
/>
|
|
</el-card>
|
|
|
|
<!-- 表单内容 -->
|
|
<el-card shadow="never">
|
|
<el-form ref="leaveFormRef" :model="form" :rules="rules" label-width="80px">
|
|
<el-form-item label="请假类型" prop="leaveType">
|
|
<el-select v-model="form.leaveType" placeholder="请选择请假类型">
|
|
<el-option
|
|
v-for="item in options"
|
|
:key="item.value"
|
|
:label="item.label"
|
|
:value="item.value"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
|
|
<el-form-item label="请假时间" required>
|
|
<el-date-picker
|
|
v-model="leaveTime"
|
|
type="daterange"
|
|
value-format="YYYY-MM-DD HH:mm:ss"
|
|
@change="changeLeaveTime()"
|
|
/>
|
|
</el-form-item>
|
|
|
|
<el-form-item label="请假天数" prop="leaveDays">
|
|
<el-input v-model="form.leaveDays" disabled type="number" />
|
|
</el-form-item>
|
|
|
|
<el-form-item label="请假原因" prop="remark">
|
|
<el-input v-model="form.remark" type="textarea" :rows="3" />
|
|
</el-form-item>
|
|
</el-form>
|
|
</el-card>
|
|
|
|
<!-- 工作流组件 -->
|
|
<submitVerify
|
|
ref="submitVerifyRef"
|
|
:task-variables="taskVariables"
|
|
@submit-callback="submitCallback"
|
|
/>
|
|
<approvalRecord ref="approvalRecordRef" />
|
|
|
|
<!-- 流程选择对话框 -->
|
|
<el-dialog v-model="dialogVisible.visible" title="流程定义" width="500">
|
|
<el-select v-model="flowCode" placeholder="Select">
|
|
<el-option
|
|
v-for="item in flowCodeOptions"
|
|
:key="item.value"
|
|
:label="item.label"
|
|
:value="item.value"
|
|
/>
|
|
</el-select>
|
|
<template #footer>
|
|
<el-button @click="handleClose">取消</el-button>
|
|
<el-button type="primary" @click="submitFlow()">确认</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { addLeave, getLeave, updateLeave } from '@/api/workflow/leave';
|
|
import { startWorkFlow } from '@/api/workflow/task';
|
|
import SubmitVerify from '@/components/Process/submitVerify.vue';
|
|
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
|
|
import ApprovalButton from '@/components/Process/approvalButton.vue';
|
|
|
|
// 流程定义选项
|
|
const flowCodeOptions = [
|
|
{ value: 'leave1', label: '请假申请-普通' },
|
|
{ value: 'leave2', label: '请假申请-排他网关' },
|
|
{ value: 'leave3', label: '请假申请-并行网关' },
|
|
{ value: 'leave4', label: '请假申请-会签' },
|
|
{ value: 'leave5', label: '请假申请-并行会签网关' },
|
|
{ value: 'leave6', label: '请假申请-排他并行会签' },
|
|
];
|
|
|
|
// 提交表单
|
|
const submitForm = async (status: string) => {
|
|
// 验证表单
|
|
await leaveFormRef.value?.validate();
|
|
|
|
// 保存业务数据
|
|
let res;
|
|
if (form.value.id) {
|
|
res = await updateLeave(form.value);
|
|
} else {
|
|
res = await addLeave(form.value);
|
|
}
|
|
|
|
// 如果是暂存,直接返回
|
|
if (status === 'draft') {
|
|
proxy?.$modal.msgSuccess('暂存成功');
|
|
return;
|
|
}
|
|
|
|
// 如果是提交,启动工作流
|
|
if (!flowCode.value) {
|
|
dialogVisible.visible = true;
|
|
return;
|
|
}
|
|
|
|
await handleStartWorkFlow(res.data);
|
|
};
|
|
|
|
// 启动工作流
|
|
const handleStartWorkFlow = async (data: LeaveForm) => {
|
|
const submitFormData = {
|
|
flowCode: flowCode.value,
|
|
businessId: data.id,
|
|
variables: {
|
|
leaveDays: data.leaveDays,
|
|
userList: ['1', '3', '4'], // 会签用户列表
|
|
},
|
|
};
|
|
|
|
const resp = await startWorkFlow(submitFormData);
|
|
submitVerifyRef.value.openDialog(resp.data.taskId);
|
|
};
|
|
</script>
|
|
```
|
|
|
|
---
|
|
|
|
## 5. 请假工作流示例
|
|
|
|
### 5.1 工作流模式
|
|
|
|
系统提供 6 种不同的工作流模式:
|
|
|
|
| 流程编码 | 流程名称 | 特点说明 |
|
|
| -------- | --------------------- | ------------------------ |
|
|
| leave1 | 请假申请-普通 | 简单的线性审批流程 |
|
|
| leave2 | 请假申请-排他网关 | 根据条件选择不同分支 |
|
|
| leave3 | 请假申请-并行网关 | 多个分支并行执行 |
|
|
| leave4 | 请假申请-会签 | 多人会签,所有人都需审批 |
|
|
| leave5 | 请假申请-并行会签网关 | 并行网关+会签组合 |
|
|
| leave6 | 请假申请-排他并行会签 | 排他网关+并行会签组合 |
|
|
|
|
### 5.2 流程变量
|
|
|
|
```typescript
|
|
// 工作流变量配置
|
|
const taskVariables = {
|
|
// leave2/6 使用的流程变量(排他网关条件判断)
|
|
leaveDays: data.leaveDays,
|
|
|
|
// leave4/5 使用的流程变量(会签用户列表)
|
|
userList: ['1', '3', '4'],
|
|
};
|
|
```
|
|
|
|
### 5.3 状态流转
|
|
|
|
```
|
|
draft(草稿) → waiting(待审批) → complete(已完成)
|
|
↓ ↓
|
|
cancel(已取消) back(已退回)
|
|
```
|
|
|
|
---
|
|
|
|
## 6. 前端组件使用
|
|
|
|
### 6.1 工作流页面模板
|
|
|
|
```vue
|
|
<template>
|
|
<div class="p-2">
|
|
<!-- 1. 审批按钮组件 -->
|
|
<el-card shadow="never">
|
|
<approvalButton
|
|
@submitForm="submitForm"
|
|
@approvalVerifyOpen="approvalVerifyOpen"
|
|
@handleApprovalRecord="handleApprovalRecord"
|
|
:buttonLoading="buttonLoading"
|
|
:id="form.id"
|
|
:status="form.status"
|
|
:pageType="routeParams.type"
|
|
/>
|
|
</el-card>
|
|
|
|
<!-- 2. 业务表单 -->
|
|
<el-card shadow="never">
|
|
<el-form ref="formRef" :model="form" :rules="rules">
|
|
<!-- 业务字段 -->
|
|
</el-form>
|
|
</el-card>
|
|
|
|
<!-- 3. 工作流组件 -->
|
|
<submitVerify
|
|
ref="submitVerifyRef"
|
|
:task-variables="taskVariables"
|
|
@submit-callback="submitCallback"
|
|
/>
|
|
<approvalRecord ref="approvalRecordRef" />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import SubmitVerify from '@/components/Process/submitVerify.vue';
|
|
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
|
|
import ApprovalButton from '@/components/Process/approvalButton.vue';
|
|
|
|
// 必需的ref引用
|
|
const submitVerifyRef = ref();
|
|
const approvalRecordRef = ref();
|
|
|
|
// 必需的事件处理
|
|
const submitForm = (status) => {
|
|
/* 提交表单逻辑 */
|
|
};
|
|
const approvalVerifyOpen = () => {
|
|
/* 打开审批窗口 */
|
|
};
|
|
const handleApprovalRecord = () => {
|
|
/* 查看审批记录 */
|
|
};
|
|
const submitCallback = () => {
|
|
/* 提交回调 */
|
|
};
|
|
</script>
|
|
```
|
|
|
|
### 6.2 路由跳转工具
|
|
|
|
```typescript
|
|
// 使用工作流通用路由跳转
|
|
import workflowCommon from '@/api/workflow/workflowCommon';
|
|
|
|
const handleOpen = (row: FlowTaskVO) => {
|
|
const routerJumpVo = {
|
|
businessId: row.businessId,
|
|
taskId: row.id,
|
|
type: 'approval', // add/update/view/approval
|
|
formCustom: row.formCustom,
|
|
formPath: row.formPath,
|
|
};
|
|
workflowCommon.routerJump(routerJumpVo, proxy);
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## 7. 工作流状态管理
|
|
|
|
### 7.1 业务状态
|
|
|
|
| 状态值 | 状态名称 | 说明 | 可用操作 |
|
|
| --------- | -------- | ------------ | -------------------- |
|
|
| draft | 草稿 | 新建但未提交 | 编辑、删除、提交 |
|
|
| waiting | 待审批 | 流程进行中 | 查看、撤销 |
|
|
| complete | 已完成 | 流程正常结束 | 查看 |
|
|
| cancel | 已取消 | 申请人撤销 | 编辑、删除、重新提交 |
|
|
| back | 已退回 | 审批人退回 | 编辑、删除、重新提交 |
|
|
| invalid | 已作废 | 管理员作废 | 查看 |
|
|
| terminate | 已终止 | 流程异常终止 | 查看 |
|
|
|
|
### 7.2 任务状态
|
|
|
|
| 状态值 | 状态名称 | 说明 |
|
|
| -------- | -------- | ---------------- |
|
|
| waiting | 待办 | 等待当前用户处理 |
|
|
| complete | 已办 | 当前用户已处理 |
|
|
| reject | 已拒绝 | 当前用户拒绝 |
|
|
| delegate | 已委托 | 当前用户委托他人 |
|
|
| transfer | 已转办 | 当前用户转办他人 |
|
|
|
|
### 7.3 状态检查工具
|
|
|
|
```typescript
|
|
// 前端状态检查
|
|
const canEdit = computed(() => {
|
|
return ['draft', 'cancel', 'back'].includes(form.value.status);
|
|
});
|
|
|
|
const canCancel = computed(() => {
|
|
return form.value.status === 'waiting';
|
|
});
|
|
|
|
const showApprovalButton = computed(() => {
|
|
return routeParams.value.type === 'approval' && form.value.status === 'waiting';
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 8. 最佳实践
|
|
|
|
### 8.1 后端开发最佳实践
|
|
|
|
#### 1. VO 类基础字段规范
|
|
|
|
```java
|
|
// 所有工作流业务VO类必须包含统一基础字段
|
|
public class TestLeaveVo {
|
|
// 业务字段
|
|
private Long id;
|
|
private String leaveType;
|
|
// ...其他业务字段
|
|
|
|
// 基础字段(必须包含,避免MyBatis映射错误)
|
|
private Long createBy;
|
|
private Date createTime;
|
|
private Long updateBy;
|
|
private Date updateTime;
|
|
}
|
|
```
|
|
|
|
#### 2. 权限注解规范
|
|
|
|
```java
|
|
// 所有工作流接口必须添加权限检查
|
|
@SaCheckPermission("workflow:leave:list")
|
|
@GetMapping("/list")
|
|
public TableDataInfo<TestLeaveVo> list(TestLeaveBo bo, PageQuery pageQuery) {
|
|
return testLeaveService.queryPageList(bo, pageQuery);
|
|
}
|
|
```
|
|
|
|
#### 3. 条件开启注解
|
|
|
|
```java
|
|
// 所有工作流相关类必须添加条件开启注解
|
|
@ConditionalOnEnable
|
|
@Service
|
|
public class TestLeaveServiceImpl implements ITestLeaveService {
|
|
// 实现逻辑
|
|
}
|
|
```
|
|
|
|
#### 4. 事务管理
|
|
|
|
```java
|
|
// 涉及业务数据和工作流数据的操作需要事务保证
|
|
@Transactional(rollbackFor = Exception.class)
|
|
public TestLeaveVo insertByBo(TestLeaveBo bo) {
|
|
// 1. 保存业务数据
|
|
TestLeave entity = BeanUtil.toBean(bo, TestLeave.class);
|
|
entity.setStatus("draft");
|
|
baseMapper.insert(entity);
|
|
|
|
// 2. 如果需要同时操作工作流数据,确保在同一事务中
|
|
return BeanUtil.toBean(entity, TestLeaveVo.class);
|
|
}
|
|
```
|
|
|
|
### 8.2 前端开发最佳实践
|
|
|
|
#### 1. 组件引入规范
|
|
|
|
```vue
|
|
<script setup>
|
|
// 工作流组件统一引入
|
|
import SubmitVerify from '@/components/Process/submitVerify.vue';
|
|
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
|
|
import ApprovalButton from '@/components/Process/approvalButton.vue';
|
|
|
|
// API接口引入
|
|
import { addLeave, getLeave, updateLeave } from '@/api/workflow/leave';
|
|
import { startWorkFlow } from '@/api/workflow/task';
|
|
</script>
|
|
```
|
|
|
|
#### 2. 表单验证规范
|
|
|
|
```javascript
|
|
// 工作流表单必须包含的验证规则
|
|
const rules = {
|
|
// 业务字段验证
|
|
leaveType: [{ required: true, message: '请假类型不能为空', trigger: 'blur' }],
|
|
leaveDays: [{ required: true, message: '请假天数不能为空', trigger: 'blur' }],
|
|
|
|
// 如果有时间范围,需要自定义验证
|
|
leaveTime: [{ required: true, message: '请假时间不能为空', trigger: 'blur' }],
|
|
};
|
|
```
|
|
|
|
#### 3. 错误处理规范
|
|
|
|
```javascript
|
|
// 提交表单错误处理
|
|
const submitForm = async (status: string) => {
|
|
try {
|
|
// 表单验证
|
|
await leaveFormRef.value?.validate();
|
|
|
|
// 业务数据保存
|
|
buttonLoading.value = true;
|
|
const res = form.value.id ? await updateLeave(form.value) : await addLeave(form.value);
|
|
|
|
// 工作流处理
|
|
if (status === 'submit') {
|
|
await handleStartWorkFlow(res.data);
|
|
}
|
|
} catch (error) {
|
|
console.error('提交失败:', error);
|
|
proxy?.$modal.msgError('操作失败,请重试');
|
|
} finally {
|
|
buttonLoading.value = false;
|
|
}
|
|
};
|
|
```
|
|
|
|
### 8.3 性能优化
|
|
|
|
#### 1. 组件懒加载
|
|
|
|
```javascript
|
|
// 工作流组件按需加载
|
|
const SubmitVerify = defineAsyncComponent(() => import('@/components/Process/submitVerify.vue'));
|
|
const ApprovalRecord = defineAsyncComponent(() =>
|
|
import('@/components/Process/approvalRecord.vue'),
|
|
);
|
|
```
|
|
|
|
#### 2. 接口优化
|
|
|
|
```javascript
|
|
// 避免重复请求,使用缓存
|
|
const { data: taskInfo, loading } = useCachedRequest(() => getTask(taskId.value), {
|
|
cacheKey: `task_${taskId.value}`,
|
|
ttl: 5 * 60 * 1000, // 5分钟缓存
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 9. 常见问题
|
|
|
|
### 9.1 MyBatis 映射错误
|
|
|
|
**问题**: `Could not set property 'createBy'`
|
|
|
|
**原因**: VO 类缺少基础字段,但 SQL 查询包含这些字段
|
|
|
|
**解决**: 确保所有 VO 类包含统一的基础字段
|
|
|
|
```java
|
|
// 正确的VO类定义
|
|
public class TestLeaveVo {
|
|
// 业务字段
|
|
private Long id;
|
|
private String leaveType;
|
|
|
|
// 必须包含的基础字段
|
|
private Long createBy;
|
|
private Date createTime;
|
|
private Long updateBy;
|
|
private Date updateTime;
|
|
}
|
|
```
|
|
|
|
### 9.2 工作流组件不显示
|
|
|
|
**问题**: 审批按钮或其他工作流组件不显示
|
|
|
|
**原因**:
|
|
|
|
1. 没有添加 `@ConditionalOnEnable` 注解
|
|
2. 配置文件中 `warm-flow.enabled=false`
|
|
3. 组件引入路径错误
|
|
|
|
**解决**:
|
|
|
|
```yaml
|
|
# 确保配置正确
|
|
warm-flow:
|
|
enabled: true
|
|
```
|
|
|
|
```java
|
|
// 确保添加条件注解
|
|
@ConditionalOnEnable
|
|
@RestController
|
|
public class TestLeaveController {
|
|
// 控制器逻辑
|
|
}
|
|
```
|
|
|
|
### 9.3 权限问题
|
|
|
|
**问题**: 接口访问被拒绝
|
|
|
|
**原因**: 缺少权限注解或权限配置不正确
|
|
|
|
**解决**:
|
|
|
|
1. 添加权限注解:`@SaCheckPermission("workflow:leave:list")`
|
|
2. 在菜单管理中配置对应的权限标识
|
|
3. 为用户角色分配相应权限
|
|
|
|
### 9.4 流程启动失败
|
|
|
|
**问题**: 启动工作流时报错
|
|
|
|
**原因**:
|
|
|
|
1. 流程定义未发布
|
|
2. 流程编码不存在
|
|
3. 业务数据状态不正确
|
|
|
|
**解决**:
|
|
|
|
1. 检查流程定义是否已发布
|
|
2. 确认 `flowCode` 参数正确
|
|
3. 确保业务数据状态为 `draft`
|
|
|
|
### 9.5 日期格式问题
|
|
|
|
**问题**: 日期选择器数据格式不匹配
|
|
|
|
**原因**: 前端日期格式与后端不一致
|
|
|
|
**解决**:
|
|
|
|
```vue
|
|
<!-- 统一使用标准格式 -->
|
|
<el-date-picker
|
|
v-model="leaveTime"
|
|
type="daterange"
|
|
value-format="YYYY-MM-DD HH:mm:ss"
|
|
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
|
|
/>
|
|
```
|
|
|
|
---
|
|
|
|
## 10. 总结
|
|
|
|
warm-flow 工作流框架为本项目提供了完整的工作流解决方案,具有以下优势:
|
|
|
|
1. **轻量级**: 核心简洁,易于理解和维护
|
|
2. **可扩展**: 支持自定义业务逻辑和权限控制
|
|
3. **可视化**: 内置流程设计器和流程图展示
|
|
4. **完整性**: 提供完整的前后端组件和示例
|
|
|
|
通过请假工作流示例,开发者可以快速掌握工作流开发的完整流程,并应用到其他业务场景中。
|
|
|
|
---
|
|
|
|
**文档维护**: 本文档随项目发展持续更新
|
|
**最后更新**: 2025-01-09
|
|
**版本**: v1.0
|