微搭低代码MBA 培训管理系统实战 41——审批中心

目录

  • 前情回顾与本节目标
  • 本节核心目标
  • 第一步:数据建模
    • [1.1 离职申请表(MBA_Resignations)](#1.1 离职申请表(MBA_Resignations))
    • [1.2 流程实例表(MBA_ProcessInstances)](#1.2 流程实例表(MBA_ProcessInstances))
    • [1.3 任务表(MBA_ProcessTasks)](#1.3 任务表(MBA_ProcessTasks))
    • [1.4 日志表(MBA_ProcessLogs)](#1.4 日志表(MBA_ProcessLogs))
  • 第二步:统一流程内核
    • [2.1 createProcessInstance - 创建流程实例](#2.1 createProcessInstance - 创建流程实例)
    • [2.2 completeTask - 完成任务流转](#2.2 completeTask - 完成任务流转)
    • [2.3 writeLog - 写入审计日志](#2.3 writeLog - 写入审计日志)
  • 第三歩:离职申请模块
    • [3.1 创建页面](#3.1 创建页面)
    • [3.2 离职列表搭建](#3.2 离职列表搭建)
    • [3.2 提交离职申请](#3.2 提交离职申请)
  • 第四章:审批中心
    • [4.1 创建页面](#4.1 创建页面)
    • [4.2 搭建页面布局](#4.2 搭建页面布局)
    • [4.2 待办列表](#4.2 待办列表)
    • [4.3 办理审批](#4.3 办理审批)
  • 最终效果
  • 总结

前情回顾与本节目标

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

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

但随着系统扩展,离职、退费、开票、采购、请假等各种业务都需要审批,如果每个业务单独写一套审批逻辑,最终会导致 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 - 创建流程实例

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

javascript 复制代码
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 - 完成任务流转

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

javascript 复制代码
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 - 写入审计日志

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

javascript 复制代码
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 提交离职申请

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

javascript 复制代码
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)让所有审批共享一套流程能力,新增审批类型无需修改流程内核,实现真正的平台化。

相关推荐
踩着两条虫2 小时前
「AI + 低代码」的可视化设计器
开发语言·前端·低代码·设计模式·架构
2601_957787587 小时前
全场景矩阵系统低代码平台:企业级业务快速定制与扩展技术实践
低代码·可视化开发·流程编排
Jeking21710 小时前
低代码平台表单设计器 unione form editor 组件介绍--随机输入组件
低代码·动态表单·表单设计·表单引擎·unione cloud
Jeking21711 小时前
低代码平台表单设计器 unione form editor 组件介绍--单选组件
低代码·动态表单·表单设计·表单引擎·unione cloud
UXbot14 小时前
支持移动端原型绘制的 AI 工具核心功能对比(2026):5 款主流平台能力横向评测
前端·低代码·ui·交互·原型模式·web app
IT研究所1 天前
AI 时代下的知识管理:从 Claude 的“复盘”能力看生成式 AI价值
大数据·运维·数据库·人工智能·科技·低代码·自然语言处理
Jeking2171 天前
低代码平台表单设计器 unione form editor 组件介绍--文件上传
低代码·动态表单·表单设计·表单引擎·unione cloud
Jeking2172 天前
低代码平台表单设计器 unione form editor 组件介绍--复选组件
低代码·动态表单·表单设计·表单引擎·unione cloud
Jeking2172 天前
低代码平台表单设计器 unione form editor 组件介绍--下拉树
低代码·动态表单·表单设计·表单引擎·unione cloud