Files
hzdyjs/.cursor/cursorrules-workflow.md
T
zlwu 45b5d58ae4 feat: 完整的Warm-Flow工作流框架文档体系建设
- 新增工作流框架开发指南文档 (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天标准开发周期的高效开发流程
2025-07-06 17:54:37 +08:00

23 KiB
Raw Blame History

Warm-Flow 工作流开发规范

🎯 概述

本规范基于 warm-flow 工作流框架,适用于所有工作流相关的开发任务。warm-flow 是 dromara 社区开源的轻量级工作流引擎,与 Spring Boot 和若依框架无缝集成。


🔧 技术栈与配置

核心依赖

<!-- warm-flow核心依赖 -->
<dependency>
    <groupId>org.dromara.warm</groupId>
    <artifactId>warm-flow-mybatis-plus-sb3-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.dromara.warm</groupId>
    <artifactId>warm-flow-plugin-ui-sb-web</artifactId>
</dependency>

配置开关

# application.yml
warm-flow:
    enabled: true # 设置为 true 时,启用工作流功能

条件开启注解

// 所有工作流相关类必须添加此注解
@ConditionalOnEnable
@Service / @Controller / @Component
public class WorkflowRelatedClass {
    // 实现逻辑
}

🏗️ 后端开发规范

1. 实体类规范

1.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;  // 工作流状态字段(必需)
}

1.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;
}

⚠️ 重要提醒: 所有工作流业务 VO 类必须包含四个基础字段,否则会出现 Could not set property 'createBy' 错误。

2. Controller 规范

@ConditionalOnEnable  // 🔴 必须添加
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/业务模块")
public class WorkflowController extends BaseController {

    private final IWorkflowService workflowService;

    // 🔴 所有接口必须添加权限注解
    @SaCheckPermission("workflow:业务模块:list")
    @GetMapping("/list")
    public TableDataInfo<WorkflowVo> list(WorkflowBo bo, PageQuery pageQuery) {
        return workflowService.queryPageList(bo, pageQuery);
    }

    @SaCheckPermission("workflow:业务模块:add")
    @PostMapping()
    public R<WorkflowVo> add(@Validated(AddGroup.class) @RequestBody WorkflowBo bo) {
        return R.ok(workflowService.insertByBo(bo));
    }

    @SaCheckPermission("workflow:业务模块:edit")
    @PutMapping()
    public R<WorkflowVo> edit(@Validated(EditGroup.class) @RequestBody WorkflowBo bo) {
        return R.ok(workflowService.updateByBo(bo));
    }

    @SaCheckPermission("workflow:业务模块:remove")
    @DeleteMapping("/{ids}")
    public R<Void> remove(@PathVariable Long[] ids) {
        return toAjax(workflowService.deleteWithValidByIds(List.of(ids)));
    }
}

3. Service 规范

@ConditionalOnEnable  // 🔴 必须添加
@Service
@RequiredArgsConstructor
public class WorkflowServiceImpl implements IWorkflowService {

    private final WorkflowMapper baseMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)  // 🔴 重要操作需要事务
    public WorkflowVo insertByBo(WorkflowBo bo) {
        Workflow entity = BeanUtil.toBean(bo, Workflow.class);
        // 🔴 新建时必须设置状态为草稿
        entity.setStatus("draft");
        validEntityBeforeSave(entity);
        boolean flag = baseMapper.insert(entity) > 0;
        if (flag) {
            bo.setId(entity.getId());
            return BeanUtil.toBean(entity, WorkflowVo.class);
        }
        return null;
    }

    // 其他CRUD方法...
}

4. 数据库规范

4.1 业务表必需字段

-- 所有工作流业务表必须包含以下字段
CREATE TABLE workflow_business (
    id                  BIGINT(20)      NOT NULL    PRIMARY KEY     COMMENT '主键ID',

    -- 业务字段
    -- ...

    -- 🔴 工作流状态字段(必需)
    status              VARCHAR(20)     DEFAULT 'draft'             COMMENT '流程状态',

    -- 🔴 标准基础字段(必需)
    tenant_id           VARCHAR(20)     DEFAULT '000000'            COMMENT '租户编号',
    create_dept         BIGINT(20)      DEFAULT NULL                COMMENT '创建部门',
    create_by           BIGINT(20)      DEFAULT NULL                COMMENT '创建者',
    create_time         DATETIME        DEFAULT CURRENT_TIMESTAMP   COMMENT '创建时间',
    update_by           BIGINT(20)      DEFAULT NULL                COMMENT '更新者',
    update_time         DATETIME        DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    deleted             TINYINT(1)      DEFAULT 0                   COMMENT '删除标志'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='工作流业务表';

4.2 工作流状态值规范

状态值 状态名称 说明 可用操作
draft 草稿 新建但未提交 编辑、删除、提交
waiting 待审批 流程进行中 查看、撤销
complete 已完成 流程正常结束 查看
cancel 已取消 申请人撤销 编辑、删除、重新提交
back 已退回 审批人退回 编辑、删除、重新提交
invalid 已作废 管理员作废 查看
terminate 已终止 流程异常终止 查看

🎨 前端开发规范

1. 页面结构规范

1.1 列表页面结构

<template>
    <div class="p-2">
        <!-- 搜索表单 -->
        <transition
            :enter-active-class="proxy?.animate.searchAnimate.enter"
            :leave-active-class="proxy?.animate.searchAnimate.leave"
        >
            <div v-show="showSearch" class="search">
                <el-form ref="queryFormRef" :model="queryParams" :inline="true">
                    <!-- 搜索字段 -->
                    <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>
        </transition>

        <!-- 数据表格 -->
        <el-card shadow="never">
            <template #header>
                <el-row :gutter="10" class="mb8">
                    <el-col :span="1.5">
                        <el-button
                            v-hasPermi="['workflow:业务:add']"
                            type="primary"
                            plain
                            icon="Plus"
                            @click="handleAdd"
                        >
                            新增
                        </el-button>
                    </el-col>
                </el-row>
            </template>

            <el-table v-loading="loading" border :data="dataList">
                <el-table-column type="selection" width="55" align="center" />

                <!-- 业务字段 -->

                <!-- 🔴 工作流状态列(必需) -->
                <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-row :gutter="10" class="mb8">
                            <el-col
                                :span="1.5"
                                v-if="['draft', 'cancel', 'back'].includes(scope.row.status)"
                            >
                                <el-button
                                    type="primary"
                                    size="small"
                                    icon="Edit"
                                    @click="handleUpdate(scope.row)"
                                >
                                    修改
                                </el-button>
                            </el-col>
                            <el-col
                                :span="1.5"
                                v-if="['draft', 'cancel', 'back'].includes(scope.row.status)"
                            >
                                <el-button
                                    type="primary"
                                    size="small"
                                    icon="Delete"
                                    @click="handleDelete(scope.row)"
                                >
                                    删除
                                </el-button>
                            </el-col>
                        </el-row>
                        <el-row :gutter="10" class="mb8">
                            <el-col :span="1.5">
                                <el-button
                                    type="primary"
                                    size="small"
                                    icon="View"
                                    @click="handleView(scope.row)"
                                >
                                    查看
                                </el-button>
                            </el-col>
                            <el-col :span="1.5" v-if="scope.row.status === 'waiting'">
                                <el-button
                                    size="small"
                                    type="primary"
                                    icon="Notification"
                                    @click="handleCancel(scope.row.id)"
                                >
                                    撤销
                                </el-button>
                            </el-col>
                        </el-row>
                    </template>
                </el-table-column>
            </el-table>
        </el-card>
    </div>
</template>

<script setup lang="ts">
import { list业务, del业务 } from '@/api/workflow/业务模块';
import { cancelProcessApply } from '@/api/workflow/instance';

const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));

// 组件逻辑...
</script>

1.2 编辑页面结构(🔴 标准模板)

<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" style="height: 78vh; overflow-y: auto">
            <el-form
                ref="formRef"
                v-loading="loading"
                :disabled="routeParams.type === 'view'"
                :model="form"
                :rules="rules"
                label-width="80px"
            >
                <!-- 业务字段 -->
            </el-form>
        </el-card>

        <!-- 🔴 3. 工作流组件(必需) -->
        <submitVerify
            ref="submitVerifyRef"
            :task-variables="taskVariables"
            @submit-callback="submitCallback"
        />
        <approvalRecord ref="approvalRecordRef" />

        <!-- 4. 流程选择对话框 -->
        <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 SubmitVerify from '@/components/Process/submitVerify.vue';
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
import ApprovalButton from '@/components/Process/approvalButton.vue';

// 🔴 必需的API引入
import { add业务, get业务, update业务 } from '@/api/workflow/业务模块';
import { startWorkFlow } from '@/api/workflow/task';

const { proxy } = getCurrentInstance() as ComponentInternalInstance;

// 🔴 必需的ref引用
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();

// 🔴 必需的变量定义
const buttonLoading = ref(false);
const loading = ref(true);
const routeParams = ref<Record<string, any>>({});
const taskVariables = ref<Record<string, any>>({});

// 🔴 流程定义选项(根据实际业务配置)
const flowCodeOptions = [{ value: 'workflow_code', label: '工作流名称' }];

// 🔴 必需的事件处理函数
const submitForm = async (status: string) => {
    try {
        // 1. 表单验证
        await formRef.value?.validate();

        // 2. 保存业务数据
        buttonLoading.value = true;
        let res;
        if (form.value.id) {
            res = await update业务(form.value);
        } else {
            res = await add业务(form.value);
        }
        form.value = res.data;

        // 3. 处理不同状态
        if (status === 'draft') {
            proxy?.$modal.msgSuccess('暂存成功');
            proxy.$tab.closePage(proxy.$route);
            proxy.$router.go(-1);
            return;
        }

        // 4. 启动工作流
        if (!flowCode.value) {
            dialogVisible.visible = true;
            return;
        }

        await handleStartWorkFlow(res.data);
    } catch (error) {
        console.error('提交失败:', error);
    } finally {
        buttonLoading.value = false;
    }
};

const handleStartWorkFlow = async (data) => {
    const submitFormData = {
        flowCode: flowCode.value,
        businessId: data.id,
        variables: taskVariables.value,
    };

    const resp = await startWorkFlow(submitFormData);
    submitVerifyRef.value.openDialog(resp.data.taskId);
};

const approvalVerifyOpen = () => {
    submitVerifyRef.value.openDialog(routeParams.value.taskId);
};

const handleApprovalRecord = () => {
    approvalRecordRef.value.init(form.value.id);
};

const submitCallback = async () => {
    await proxy.$tab.closePage(proxy.$route);
    proxy.$router.go(-1);
};

// 🔴 页面初始化
onMounted(() => {
    nextTick(async () => {
        routeParams.value = proxy.$route.query;
        // 初始化逻辑
        if (['update', 'view', 'approval'].includes(routeParams.value.type)) {
            getInfo();
        }
    });
});
</script>

2. API 接口规范

2.1 类型定义

// api/workflow/业务模块/types.ts
export interface WorkflowForm extends BaseEntity {
    id?: string | number;
    // 业务字段
    status?: string; // 🔴 工作流状态字段
}

export interface WorkflowVO extends BaseEntity {
    id?: string | number;
    // 业务字段
    status?: string; // 🔴 工作流状态字段
}

export interface WorkflowQuery extends PageQuery {
    // 查询字段
    status?: string;
}

2.2 接口定义

// api/workflow/业务模块/index.ts
import request from '@/utils/request';
import { WorkflowQuery, WorkflowForm, WorkflowVO } from './types';

export const list业务 = (query: WorkflowQuery): AxiosPromise<WorkflowVO[]> => {
    return request({
        url: '/workflow/业务模块/list',
        method: 'get',
        params: query,
    });
};

export const get业务 = (id: string | number): AxiosPromise<WorkflowVO> => {
    return request({
        url: `/workflow/业务模块/${id}`,
        method: 'get',
    });
};

export const add业务 = (data: WorkflowForm): AxiosPromise<WorkflowVO> => {
    return request({
        url: '/workflow/业务模块',
        method: 'post',
        data: data,
    });
};

export const update业务 = (data: WorkflowForm): AxiosPromise<WorkflowVO> => {
    return request({
        url: '/workflow/业务模块',
        method: 'put',
        data: data,
    });
};

export const del业务 = (id: string | number | Array<string | number>): AxiosPromise<void> => {
    return request({
        url: `/workflow/业务模块/${id}`,
        method: 'delete',
    });
};

3. 路由配置规范

// router配置
{
  path: '/workflow/业务模块',
  component: Layout,
  redirect: '/workflow/业务模块/index',
  name: 'Workflow业务模块',
  meta: {
    title: '业务模块管理',
    icon: 'workflow'
  },
  children: [
    {
      path: 'index',
      component: () => import('@/views/workflow/业务模块/index.vue'),
      name: 'Workflow业务模块List',
      meta: { title: '业务模块列表', activeMenu: '/workflow/业务模块/index' }
    },
    {
      path: 'edit',
      component: () => import('@/views/workflow/业务模块/edit.vue'),
      name: 'Workflow业务模块Edit',
      hidden: true,
      meta: { title: '业务模块编辑', activeMenu: '/workflow/业务模块/index' }
    }
  ]
}

🔍 工作流核心功能

1. 流程启动

// 启动工作流标准代码
const handleStartWorkFlow = async (businessData: any) => {
    const submitFormData = {
        flowCode: 'your_flow_code', // 🔴 流程定义编码
        businessId: businessData.id, // 🔴 业务数据ID
        variables: {
            // 🔴 流程变量
            // 条件判断变量
            amount: businessData.amount,
            // 会签用户列表
            userList: ['1', '3', '4'],
            // 其他业务变量
            ...otherVariables,
        },
    };

    try {
        const resp = await startWorkFlow(submitFormData);
        // 打开提交确认对话框
        submitVerifyRef.value.openDialog(resp.data.taskId);
    } catch (error) {
        console.error('启动工作流失败:', error);
    }
};

2. 任务办理

// 在 submitVerify 组件中自动处理
// 支持的操作:
// - 提交审批
// - 委托 (delegateTask)
// - 转办 (transferTask)
// - 加签 (addSignature)
// - 减签 (reductionSignature)
// - 终止 (terminationTask)
// - 退回 (backProcess)

3. 流程撤销

// 申请人撤销流程
const handleCancelProcessApply = async (businessId: string) => {
    await proxy?.$modal.confirm('是否确认撤销当前单据?');

    const data = {
        businessId: businessId,
        message: '申请人撤销流程!',
    };

    await cancelProcessApply(data);
    proxy?.$modal.msgSuccess('撤销成功');
    getList(); // 刷新列表
};

📊 常用工作流模式

1. 普通线性流程

// 流程编码:simple_approval
// 特点:线性审批,一步一步进行
const taskVariables = {
    // 基本业务数据即可
    businessData: formData,
};

2. 排他网关流程

// 流程编码:exclusive_gateway
// 特点:根据条件选择不同审批分支
const taskVariables = {
    amount: formData.amount, // 金额条件
    urgentLevel: formData.urgent, // 紧急程度
    applicantLevel: currentUser.level, // 申请人级别
};

3. 并行网关流程

// 流程编码:parallel_gateway
// 特点:多个分支并行执行
const taskVariables = {
    parallelBranches: ['finance', 'hr', 'legal'], // 并行分支
    businessData: formData,
};

4. 会签流程

// 流程编码:countersign
// 特点:多人会签,所有人都需要审批
const taskVariables = {
    userList: ['1', '3', '4', '5'], // 会签用户列表
    signType: 'all', // all-全部同意 any-任一同意
    businessData: formData,
};

🔒 权限配置规范

1. 菜单权限配置

在系统管理 → 菜单管理中配置:

父菜单:工作流管理
├── 业务模块管理
    ├── 菜单权限:workflow:业务模块:list
    ├── 按钮权限:
        ├── 新增:workflow:业务模块:add
        ├── 修改:workflow:业务模块:edit
        ├── 删除:workflow:业务模块:remove
        ├── 查询:workflow:业务模块:query
        └── 导出:workflow:业务模块:export

2. 工作流权限配置

工作流通用权限:
├── 任务管理:workflow:task:list
├── 实例管理:workflow:instance:list
├── 定义管理:workflow:definition:list
└── 审批权限:workflow:approval:handle

⚠️ 常见问题与解决方案

1. MyBatis 映射错误

问题: Could not set property 'createBy'

解决: 确保 VO 类包含完整的基础字段:

// 🔴 必须包含的四个基础字段
private Long createBy;
private Date createTime;
private Long updateBy;
private Date updateTime;

2. 工作流功能不显示

问题: 工作流相关功能无法使用

检查清单:

  • 配置文件:warm-flow.enabled=true
  • 类注解:@ConditionalOnEnable
  • 权限注解:@SaCheckPermission
  • 组件引入路径正确

3. 流程启动失败

常见原因:

  • 流程定义未发布
  • flowCode 不存在
  • 业务数据状态非 'draft'
  • 权限不足

4. 日期格式问题

解决: 统一使用标准格式

<el-date-picker
    v-model="dateRange"
    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)]"
/>

📝 代码生成模板

快速生成工作流业务模块

  1. 数据库表: 按照规范创建业务表
  2. 后端代码: 使用代码生成器,添加工作流注解
  3. 前端代码: 复制标准模板,修改业务字段
  4. 权限配置: 在菜单管理中配置权限
  5. 流程定义: 在流程设计器中创建流程

📚 参考文档


规范版本: v1.0
最后更新: 2025-01-09
适用范围: 所有基于 warm-flow 的工作流开发任务