前情回顾与本节目标

在上一节中,我们完成了人事管理模块,实现了:

  • 员工档案
  • 入职管理
  • 离职管理

但随着系统扩展,离职、退费、开票、采购、请假等各种业务都需要审批,如果每个业务单独写一套审批逻辑,最终会导致 if/else 无限增长、页面爆炸、流程混乱、难以维护。因此,我们正式进入轻量级流程引擎的设计,核心思想是:业务表只负责业务数据,流程系统只负责流程流转,一套流程内核支撑所有审批业务。


本节核心目标

本节我们将实现:

本节核心目标:

  1. 四表流程模型:业务表 + 流程实例表 + 任务表 + 日志表
  2. 统一流程内核:createProcessInstance / completeTask / writeLog / getMyTasks
  3. 统一审批中心:我的待办 / 我已处理 / 我发起的 / 审批详情 / 流程时间线
  4. 离职审批完整案例:离职申请 → 部门经理审批 → HR审批 → 流程结束

第一步:数据建模

原来的审批模式我们将审批状态直接存储在业务表中,随着业务扩展(退费、采购、开票等),代码会充斥大量 if/else 判断,导致审批系统与业务严重耦合。企业级系统必须做"流程抽象",将业务与流程彻底解耦。

我们采用四表模型

业务表(真实业务)
        │
        │ 1:1
        ↓
流程实例表(流程主线)
        │
        │ 1:n
        ↓
任务表(待办中心)
        │
        │ 1:n
        ↓
日志表(审计轨迹)

设计原则

  • 业务表只存业务数据(金额、原因、附件等),不存审批人、审批历史
  • 流程实例表是流程主线,连接业务、任务、日志,记录流程当前状态
  • 任务表是待办中心,记录"谁现在要处理什么"
  • 日志表是审计轨迹,记录全过程操作,包含业务快照保证历史不可变

1.1 离职申请表(MBA_Resignations)

字段名称 字段标识 字段类型 说明
记录ID _id 文本 主键
离职单号 leave_no 文本 如:RS-20260501-001
关联员工 rel_employee_id 关联关系 关联员工表
离职类型 leave_type 枚举 1-主动离职、2-协商离职、3-辞退、4-试用期不合格
拟离职日期 plan_date 日期 计划离职日期
交接人 rel_handover_id 关联关系 工作交接人
离职原因 reason 多行文本 离职原因说明
业务状态 biz_status 枚举 1-草稿、2-审批中、3-已通过、4-已驳回、5-已取消
流程实例ID process_instance_id 文本 关联流程实例
创建人 created_by 关联关系 发起人
创建时间 created_at 日期时间 自动生成
更新时间 updated_at 日期时间 自动更新

1.2 流程实例表(MBA_ProcessInstances)

字段名称 字段标识 字段类型 说明
实例ID _id 文本 主键
流程KEY process_key 文本 如:resignation_process、refund_process
流程名称 process_name 文本 如:离职审批流程
业务表名 biz_table 文本 如:MBA_Resignations
业务记录ID biz_record_id 文本 关联业务表记录
业务编号 biz_no 文本 如:RS-20260501-001
审批标题 title 文本 审批标题(如:张三的离职申请)
发起人 starter_id 关联关系 关联用户表
当前节点 current_node 文本 如:dept_manager、hr_review
当前任务ID current_task_id 文本 当前待办任务ID
流程状态 status 枚举 running-运行中、approved-已通过、rejected-已驳回、cancel-已取消
开始时间 start_time 日期时间 流程发起时间
结束时间 end_time 日期时间 流程完成时间
创建时间 created_at 日期时间 自动生成

1.3 任务表(MBA_ProcessTasks)

字段名称 字段标识 字段类型 说明
任务ID _id 文本 主键
流程实例ID instance_id 关联关系 关联流程实例
节点KEY node_key 文本 如:dept_manager、hr_review
节点名称 node_name 文本 如:部门经理审批、HR审核
审批人 assignee_id 关联关系 关联用户表
任务类型 task_type 枚举 approve-审批、copy-抄送、read-阅知
任务状态 status 枚举 pending-待处理、done-已完成、reject-已驳回
审批动作 action 文本 approve-通过、reject-驳回、transfer-转交
审批意见 comment 多行文本 审批意见
接收时间 received_at 日期时间 任务接收时间
完成时间 completed_at 日期时间 任务完成时间
创建时间 created_at 日期时间 自动生成

1.4 日志表(MBA_ProcessLogs)

字段名称 字段标识 字段类型 说明
日志ID _id 文本 主键
流程实例ID instance_id 关联关系 关联流程实例
任务ID task_id 关联关系 来源任务
操作人 operator_id 关联关系 关联用户表
操作动作 action 文本 submit-提交、approve-通过、reject-驳回、transfer-转交、cancel-取消
来源节点 from_node 文本 操作前节点
去向节点 to_node 文本 操作后节点
审批意见 comment 多行文本 审批意见
创建时间 created_at 日期时间 操作时间

第二步:统一流程内核

抽象3个核心方法,所有审批业务共用,在全局方法里创建:

2.1 createProcessInstance - 创建流程实例

统一创建流程实例、第一个任务、写入日志。

export default async function createProcessInstance(params) {
  const {
    processKey, processName, bizTable, bizRecordId, bizNo, title,
    starterId, firstNode, firstNodeName, firstAssigneeId
  } = params;
  const now = Date.now();

  // 1. 创建流程实例
  const instanceRes = await $w.cloud.callDataSource({
    dataSourceName: 'MBA_ProcessInstances',
    methodName: 'wedaCreateV2',
    params: {
      data: {
        process_key: processKey, process_name: processName,
        biz_table: bizTable, biz_record_id: bizRecordId, biz_no: bizNo,
        title: title, starter_id: { _id: starterId },
        current_node: firstNode, status: 'running',
        start_time: now, created_at: now
      }
    }
  });
  const instanceId = instanceRes.id;

  // 2. 创建第一个任务
  const taskRes = await $w.cloud.callDataSource({
    dataSourceName: 'MBA_ProcessTasks',
    methodName: 'wedaCreateV2',
    params: {
      data: {
        instance_id: { _id: instanceId }, node_key: firstNode,
        node_name: firstNodeName, assignee_id: { _id: firstAssigneeId },
        task_type: 'approve', status: 'pending',
        received_at: now, created_at: now
      }
    }
  });

  // 3. 更新流程实例的当前任务ID
  await $w.cloud.callDataSource({
    dataSourceName: 'MBA_ProcessInstances',
    methodName: 'wedaUpdateV2',
    params: {
      filter: { where: { _id: { $eq: instanceId } } },
      data: { current_task_id: taskRes.id, updated_at: now }
    }
  });

  // 4. 写日志
  await app.common.writeLog({
    instanceId, operatorId: starterId, action: 'submit',
    fromNode: '', toNode: firstNode, comment: '提交申请', snapshot: {}
  });

  return { instanceId, taskId: taskRes.id };
}

2.2 completeTask - 完成任务流转

完成当前任务、创建下一任务、更新流程状态、写日志。

export default async function completeTask(params) {
  const { taskId, action, comment, nextNode, nextNodeName, nextAssigneeId, snapshot } = params;
  const now = Date.now();

  // 1. 查询任务信息
  const taskRes = await $w.cloud.callDataSource({
    dataSourceName: 'MBA_ProcessTasks',
    methodName: 'wedaGetItemV2',
    params: { filter: { where: { _id: { $eq: taskId } } }, select: { $master: true, instance_id: true } }
  });
  if (!taskRes) throw new Error('任务不存在');

  const task = taskRes;
  const instanceId = task.instance_id?._id || task.instance_id;
  const currentNode = task.node_key;
  const operatorId = $w.app.dataset.state.currentUser._id;

  // 2. 更新任务状态
  await $w.cloud.callDataSource({
    dataSourceName: 'MBA_ProcessTasks',
    methodName: 'wedaUpdateV2',
    params: {
      filter: { where: { _id: { $eq: taskId } } },
      data: { status: action === 'reject' ? 'reject' : 'done', action, comment: comment || '', completed_at: now, updated_at: now }
    }
  });

  // 3. 处理流程流转
  let toNode = '', newTaskId = null;
  if (action === 'approve') {
    if (nextNode) {
      const newTaskRes = await $w.cloud.callDataSource({
        dataSourceName: 'MBA_ProcessTasks',
        methodName: 'wedaCreateV2',
        params: {
          data: {
            instance_id: { _id: instanceId }, node_key: nextNode,
            node_name: nextNodeName, assignee_id: { _id: nextAssigneeId },
            task_type: 'approve', status: 'pending', received_at: now, created_at: now
          }
        }
      });
      newTaskId = newTaskRes.id; toNode = nextNode;
      await $w.cloud.callDataSource({
        dataSourceName: 'MBA_ProcessInstances',
        methodName: 'wedaUpdateV2',
        params: {
          filter: { where: { _id: { $eq: instanceId } } },
          data: { current_node: nextNode, current_task_id: newTaskId, updated_at: now }
        }
      });
    } else {
      toNode = 'end';
      await $w.cloud.callDataSource({
        dataSourceName: 'MBA_ProcessInstances',
        methodName: 'wedaUpdateV2',
        params: {
          filter: { where: { _id: { $eq: instanceId } } },
          data: { status: 'approved', current_node: 'end', current_task_id: '', end_time: now, updated_at: now }
        }
      });
    }
  } else if (action === 'reject') {
    toNode = 'rejected';
    await $w.cloud.callDataSource({
      dataSourceName: 'MBA_ProcessInstances',
      methodName: 'wedaUpdateV2',
      params: {
        filter: { where: { _id: { $eq: instanceId } } },
        data: { status: 'rejected', current_node: 'rejected', current_task_id: '', end_time: now, updated_at: now }
      }
    });
  }

  // 4. 写日志
  await app.common.writeLog({ instanceId, taskId, operatorId, action, fromNode: currentNode, toNode, comment: comment || '', snapshot: snapshot || {} });
  return { success: true, action, newTaskId, isEnd: action === 'reject' || !nextNode };
}

2.3 writeLog - 写入审计日志

统一记录所有操作,包含业务快照保证历史不可变。

export default async function writeLog(params) {
  const { instanceId, taskId, operatorId, action, fromNode, toNode, comment, snapshot } = params;
  await $w.cloud.callDataSource({
    dataSourceName: 'MBA_ProcessLogs',
    methodName: 'wedaCreateV2',
    params: {
      data: {
        instance_id: { _id: instanceId },
        task_id: taskId ? { _id: taskId } : undefined,
        operator_id: { _id: operatorId }, action,
        from_node: fromNode || '', to_node: toNode || '',
        comment: comment || '', snapshot: snapshot || {}, created_at: Date.now()
      }
    }
  });
}

第三歩:离职申请模块

3.1 创建页面

切换到布局设计,点击新建布局
在这里插入图片描述
选择左侧导航布局,将布局重命名为OA布局
在这里插入图片描述

点击创建页面图标,输入离职申请,选择OA布局
在这里插入图片描述
切换到布局管理,选择OA布局,添加平级菜单,添加"离职申请"菜单
在这里插入图片描述

3.2 离职列表搭建

在OA布局的内容插槽下添加布局组件,修改标题为离职申请
在这里插入图片描述
在布局内容里添加数据表格组件
在这里插入图片描述
选择离职申请数据模型,勾选所有场景
在这里插入图片描述
配置筛选条件
在这里插入图片描述

3.2 提交离职申请

点击提交后,系统依次执行:创建业务记录 → 创建流程实例 → 创建待办任务 → 写日志 → 更新员工状态为"离职中"。

export default async function submitResignation({ event, data }) {
  try {
    $w.utils.showLoading({ title: '提交中...' });
    const formData = $w.form2.value;
    const currentUserId = $w.app.dataset.state.currentUser._id||$w.query1.data._id;
     console.log(formData)
    // 验证必填
    if (!formData.rel_employee_id || !formData.leave_type || !formData.plan_date || !formData.rel_handover_id || !formData.reason) {
      $w.utils.hideLoading();
      return $w.utils.showToast({ title: '请填写完整信息', icon: 'error' });
    }

    // 1. 根据当前用户ID查询员工信息
    const starterRes = await $w.cloud.callDataSource({
      dataSourceName: 'MBA_Employees', 
      methodName: 'wedaGetRecordsV2',
      params: { filter: { where: { rel_user_id: { $eq: currentUserId } } }, 
      select: { $master: true } }
    });
    const starterEmployee = starterRes.records?.[0];
    if (!starterEmployee) {
      $w.utils.hideLoading();
      return $w.utils.showToast({ title: '未找到您的员工信息', icon: 'error' });
    }

    // 2. 创建业务记录
    const bizRes = await $w.cloud.callDataSource({
      dataSourceName: 'MBA_Resignations',
      methodName: 'wedaCreateV2',
      params: {
        data: {
          rel_employee_id: { _id: formData.rel_employee_id },
          leave_type: formData.leave_type, plan_date: formData.plan_date,
          rel_handover_id: { _id: formData.rel_handover_id }, reason: formData.reason,
          biz_status: '2', created_by: { _id: starterEmployee._id },
         
        }
      }
    });

    // 3. 查询获取自动生成的业务编号
    const bizDetailRes = await $w.cloud.callDataSource({
      dataSourceName: 'MBA_Resignations', 
      methodName: 'wedaGetItemV2',
      params: { filter: { where: { _id: { $eq: bizRes.id } } }, 
      select: { $master: true } }
    });
    const leaveNo = bizDetailRes.leave_no;

    // 4. 获取离职员工信息
    const empRes = await $w.cloud.callDataSource({
      dataSourceName: 'MBA_Employees', 
      methodName: 'wedaGetItemV2',
      params: { filter: { where: { _id: { $eq: formData.rel_employee_id } } }, 
      select: { $master: true } }
    });
    const empName = empRes?.name || '未知';

    // 5. 创建流程实例(调用公共方法)
    const processRes = await app.common.createProcessInstance({
      processKey: 'resignation_process', processName: '离职审批流程',
      bizTable: 'MBA_Resignations', bizRecordId: bizRes.id, bizNo: leaveNo,
      title: `${empName}的离职申请`, starterId: starterEmployee._id,
      firstNode: 'dept_manager', firstNodeName: '部门经理审批',
      firstAssigneeId: empRes?.rel_supervisor_id?._id || empRes?.rel_supervisor_id
    });

    // 6. 更新业务记录关联流程实例
    await $w.cloud.callDataSource({
      dataSourceName: 'MBA_Resignations', 
      methodName: 'wedaUpdateV2',
      params: { filter: { where: { _id: { $eq: bizRes.id } } }, 
      data: { process_instance_id:{_id:processRes.instanceId} } }
    });

    // 7. 更新员工状态为"离职中"
    await $w.cloud.callDataSource({
      dataSourceName: 'MBA_Employees', 
      methodName: 'wedaUpdateV2',
      params: { filter: { where: { _id: { $eq: formData.rel_employee_id } } }, 
      data: { status: '2' } }
    });

    // 8. 写日志(调用公共方法)
    await app.common.writeLog({
      instanceId: processRes.instanceId, taskId: processRes.taskId,
      operatorId: starterEmployee._id, action: 'submit',
      fromNode: '', toNode: 'dept_manager', comment: '提交离职申请', snapshot: {}
    });

    $w.utils.hideLoading();
    $w.utils.showToast({ title: '提交成功', icon: 'success' });
    $w.modal2.close({});
    $w.table1.refresh()
  } catch (error) {
    console.error('提交离职申请失败:', error);
    $w.utils.hideLoading();
    $w.utils.showToast({ title: '提交失败,请重试', icon: 'error' });
  }
}

修改表单的提交方法,改为调用我们的自定义方法
在这里插入图片描述


第四章:审批中心

4.1 创建页面

点击创建页面的图标,输入审批中心,布局选择OA布局
在这里插入图片描述
切换到页面布局,添加审批中心的菜单
在这里插入图片描述

4.2 搭建页面布局

在OA布局下添加布局组件,修改标题为审批中心
在这里插入图片描述

4.2 待办列表

添加数据表格组件,数据模型选择任务表
在这里插入图片描述
将操作列的按钮改为办理
在这里插入图片描述

4.3 办理审批

添加弹窗组件,修改弹窗标题,改为流程办理
在这里插入图片描述
里边添加表单容器,场景选择查看,数据模型选择离职申请表
在这里插入图片描述
添加单行输入组件,标题改为审批意见
在这里插入图片描述

底部按钮改为"通过"和"驳回"。
在这里插入图片描述
给操作列的办理按钮配置点击事件,打开弹窗,传入所在行数据
在这里插入图片描述
给表单容器绑定数据标识,绑定弹窗入参的业务标识字段
在这里插入图片描述

最终效果

员工在离职申请模块提交申请
在这里插入图片描述
审批人在审批中心处理离职申请
在这里插入图片描述


总结

本节完成了轻量级流程引擎的核心设计,通过四表模型(业务表、流程实例表、任务表、日志表)实现业务与流程解耦。统一流程内核(createProcessInstance、completeTask、writeLog、getMyTasks)让所有审批共享一套流程能力,新增审批类型无需修改流程内核,实现真正的平台化。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐