尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

OA系统源码如何设计审批流程:从流程引擎到避坑实践

OA系统源码如何设计审批流程:从流程引擎到避坑实践 简介这套OA系统源码基于.NET技术栈同时提供Web端和MobileWeb移动端界面核心价值在于可自行设计审批流程适应请假、报销、采购等不同企业内部审批场景。资源共2000个文件压缩包43.54MB以C#源代码619个cs、ASPX页面498个aspx、JavaScript脚本、CSS样式及GIF/PNG图片素材为主同时包含数据库文件、DLL组件和项目工程文件构成一套可直接运行学习的完整项目。目前已有6931人浏览学习适合需要搭建或定制办公自动化系统的开发团队、企业IT人员也适合想了解.NET分层架构的中级开发者。通过源码可以清晰看到TMOA.BLL业务逻辑层、TMOA.DBUtility数据访问层、TMOA.Common公共模块之间的协作方式理解审批流程从数据表设计、后端逻辑处理到前端操作页面的完整实现链路MobileWeb目录则展示了移动端审批适配方案方便在桌面与手机端同步处理工作事务为二次开发和企业内部推广提供实用参考。1. 一套OA系统源码最值钱的部分是让你改得动审批流程网上能下载到的OA系统源码很多能跑起来的也不少。但大多数人在完成部署、打开登录页的那一刻就踩进同一个坑公司要调整请假审批规则想把「部门主管审批」改成「部门主管审批后超过三天的还要总监会签」。源代码里翻了半小时发现这个逻辑要么写死在代码里要么只能在数据库里硬改改完还不敢重启。这时候你才会真正明白一套完整的OA系统源码值钱的部分从来不是界面也不是考勤打卡而是审批流程能不能自己再设计。这篇文章不介绍某个具体项目的下载渠道只围绕「一套完整OA系统源码可自己设计审批流程」这条主线讲清楚这类源码从选型、拆解到落地再到避坑的完整路径。读完你会知道怎么识别一套源码的流程引擎是否合格、怎么在现有源码上扩展自己的审批设计能力以及上手后最先会翻车的几个位置。2. 流程引擎是源码的智能中枢先认清三种主流实现再动手很多人在第一次拿到一套OA源码时习惯先看菜单和界面这是倒过来的。审批流程的设计能力几乎完全由源码里那个看不见的流程引擎决定。先看懂它属于哪一类实现你才知道后期的每一步改动会在哪个文件、哪张表里发生。2.1 从源码里找出流程引擎的三条识别线拿到一套不熟悉的OA源码我通常不会去读所有代码而是用三条线快速锁定它的流程引擎。第一条线是看流程定义是否存在数据库表里比如表名类似于 flow_def、wf_process、approval_process如果这类表存在并且有内容说明流程定义是可配置的数据如果翻遍代码找不到这类表那流程大概率写死在 Java 或 PHP 的业务文件里后期每次调整都要动代码。第二条线是看前端有没有流程设计器也就是那种可以用拖拽方式画审批节点、连线的页面。如果源码里只有审批列表没有设计器页面说明这套源码的流程方向是「改代码」而不是「配流程」。第三条线是看审批人规则怎么表达例如是否有候选角色、部门主管、发起人自选这类选项这直接决定了流程能不能适配公司的复杂人事结构。2.2 三种流程设计的实现方式对比我把见过和实测过的OA源码按实现方式分成三大类每一类的可扩展性和风险差别极大。实现方式流程定义存在哪设计入口改流程成本适用场景状态机硬编码代码中的枚举与 switch 分支无每次改动都要发版流程极简单且几乎不变表驱动配置数据库流程表与节点表后台管理页发版或配置后重启中小团队流程变化不频繁自研/开源工作流引擎单独的流程定义表、节点表、实例表可视化设计器配置后即时生效多组织、多流程、频繁调整场景如果是表驱动你要重点把流程定义表、节点表、条件表的字段吃透因为后期「自己设计审批流程」的本质就是往这几张表里写数据。如果是集成了工作流引擎比如基于活动工作流组件或流程引擎方案二次封装的你需要从上到下梳理清楚三套数据模型流程定义、流程实例、待办任务。这里有一个典型的误区以为源码里带有「审批」页面就等同于支持自定义审批流。很多带审批页面的源码背后只是把单条记录的字符串状态从 0 改成 1 再改成 2比如新建申请是状态A、部门主管审批后改成状态B、再经财务审批改成状态C。这种假工作流无法处理会签、分支、驳回、按条件选审批人。所以我在 2.1 里强调设计器不是加分项而是分水岭。没有可视化设计器的源码就算它再完整也只是勉强够用而已。2.3 源码到手后先验证的三个关键设计能力第一发布一个流程后旧流程还能不能继续跑。很多源码支持建流程但一改流程就把所有进行中的单子也都切换到了新规则这是灾难性的。第二能不能配置「或签」与「会签」。所谓或签指多个人中只要有一个人审批通过即可进入下一节点会签则要求所有人都通过才算通过。这两类节点是审批流里最常用的源码不支持的话后面会非常难搞。第三驳回时能不能指定退回的节点也就是支持驳回到发起人或者驳回到某一中间环节而不是只能驳回到发起人。我会把这三件事记在小本子上一套源码能不能留在候选名单里就靠它们过第一轮筛选。因为这三件事全部塞进「可自己设计审批流程」这句话的实际含义里自己设计不只是画一条流程图而是能控制流程在运行中遇到分叉、驳回、多人协作时的行为。如果这套源码只能画出一张静态图那它离「可设计」这三个字还很远。3. 把审批流程设计落地四张核心表、发起接口与常用节点实现一段可靠的落地是从表结构开始的。数据模型不正确界面做得再好看流程一旦复杂后面维护的人想哭都找不到地方哭。我建议你拿到源码后把和流程相关的表以及接口对应的字段全部打印出来再对照这一章节的通用设计去对比差异。3.1 流程定义表和节点表用四张表撑起审批模板无论源码用哪种语言写的流程设计这套底子基本是相通的。以我习惯的简化模型为例最核心的是四张表流程定义表、流程节点表、流程实例表、审批任务表。CREATE TABLE oa_flow_def ( id BIGINT PRIMARY KEY AUTO_INCREMENT, flow_code VARCHAR(64) NOT NULL COMMENT 流程编码全局唯一, flow_name VARCHAR(128) NOT NULL COMMENT 流程名称, form_code VARCHAR(64) NOT NULL COMMENT 绑定的表单编码, version INT NOT NULL DEFAULT 1 COMMENT 版本号, status TINYINT NOT NULL DEFAULT 0 COMMENT 状态0草稿 1已发布 2已停用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) COMMENT流程定义表; CREATE TABLE oa_flow_node ( id BIGINT PRIMARY KEY AUTO_INCREMENT, flow_def_id BIGINT NOT NULL COMMENT 所属流程定义ID, node_key VARCHAR(64) NOT NULL COMMENT 节点唯一标识如 start、approve_dept, node_name VARCHAR(128) NOT NULL COMMENT 节点名称如部门主管审批, node_type VARCHAR(32) NOT NULL COMMENT 节点类型start/approve/cc/end, approver_type VARCHAR(32) COMMENT 审批人类型role、user、department_manager、self_select, approver_value VARCHAR(255) COMMENT 审批人具体值角色编码或用户ID, strategy VARCHAR(16) DEFAULT pass_all COMMENT 多人节点的通过策略pass_all会签 pass_one或签, branch_expression VARCHAR(255) COMMENT 条件分支表达式如 $days 3, next_node_key VARCHAR(64) COMMENT 默认下一节点, reject_target VARCHAR(64) COMMENT 驳回时退回到哪个节点, sort_no INT DEFAULT 0 COMMENT 排序号, UNIQUE KEY uk_def_node (flow_def_id, node_key) ) COMMENT流程节点表;这两张表建好一个流程模板的骨架就出来了。流程定义表负责描述「流程是什么版本、绑定了哪个表单」节点表则描述「流程经过哪些环节、每个环节谁审批、多人时怎么算通过」。字段里需要留意的有 node_key 和 next_node_key它们共同决定了流转路径。很多早期源码用数字作为节点标识会导致驳回和条件分支的代码逻辑非常绕我用字符串做主键一样的逻辑标识维护时一眼就能看出某个节点的功能。3.2 发起审批构建流程实例并跑通第一步流程定义是模板流程实例是每一次具体申请产生的运行记录。一次请假发起的动作核心操作是读取模板、生成一条实例记录、根据首个节点生成审批任务。def create_flow_instance(flow_code: str, form_data: dict, initiator_id: int): # 锁定已发布的最新版流程定义避免拿到草稿版本 flow_def get_flow_def_by_code(flow_code, status1) if not flow_def: raise BizException(流程未发布或已停用) # 写入流程实例主记录 instance_id insert_flow_instance(flow_def.id, flow_def.version, form_data, initiator_id) # 找到开始节点之后的第一个真实审批节点 first_node get_first_approve_node(flow_def.id) # 为该审批节点生成待办任务 create_task(instance_id, first_node, approver_user_ids(first_node, form_data)) return instance_id这段代码里最关键的是最后一步 approver_user_ids 函数根据节点配置的 approver_type 去动态解析审批人。如果审批人类型是角色就查该角色下所有成员是部门主管就去查表单里填报人所属部门的第一负责人是发起人自选则需要从表单值中读取用户选择。顺序不能反过来否则在「部门主管审批」这个场景下发起的单子无法正常找到审批人。参数说明flow_code 是流程编码比如 leave_days_03initiator_id 是发起人用户IDform_data 是提交的表单数据供条件表达式判断使用分支条件是否走某一节点全靠这个 Dict 的取值。3.3 审批过程会签、或签、条件分支和驳回的通用算法流程引擎里最见功底的地方不在发起而在审批完成那一刻要往哪个方向走。这里有一个通用函数几乎所有流程源码都会用到我称它为「寻路函数」当前节点上一任务完成后找出下一个节点同时判断流程是终结还是继续。def get_next_approve_node(current_node, form_data, task_records): any_pass, all_pass calc_node_pass(current_node, task_records) if current_node.strategy pass_all and not all_pass: return None, False # 会签未全部通过节点继续等待 if current_node.strategy pass_one and not any_pass: return None, False # 或签未出现第一个通过同样继续等待 # 进入分支判断同一层挂多个条件节点时按表达式优先级取首个满足的 for next_node in get_next_nodes(current_node.flow_def_id, current_node.id): if next_node.branch_expression: if eval_expression(next_node.branch_expression, form_data): return next_node, True else: return next_node, True return None, True # 没有下一节点流程结束归档先解释一下 calc_node_pass它统计该审批节点下任务记录里通过数量与总数。一种实现是为了避免并发重复按任务ID去重否则同一审批人重复点击提交时人数是对的但记录重复了。然后看分支表达式 eval_expression常见的条件是请假天数、金额这类表单里的数值字段。表达式写成 $days 3 这样的字符串引擎解析时把 $days 替换成 form_data 里的值再计算。驳回的逻辑我单独处理。每个节点都允许多个审批节点记录 reject_target如果驳回到发起人就直接终止当前实例否则把任务重新生成到指定节点并把当前节点状态置为已驳回。有些团队在实现时把驳回做成特殊的状态字段我没采用因为状态字段一多就容易和正常流转混淆直接生成新任务最简单。3.4 可运行的完整实例请假的3天分界线怎么配现在写一个具体的实操过程。完整的 OA 源码通常会自带一些演示流程但那个只用来展示真正要投入运营时得学会在这套引擎上重新自己设计一条流程。以请假流程为例业务要求是请假天数小于等于3天部门主管审批后直接到人事备案请假天数大于3天部门主管审批后还要总监审批。节点配置上就要在部门主管节点旁的下一层挂两个分支子节点也可以挂在同一个层级里用分支表达式区分。节点 key类型审批人下一节点/条件策略start开始无approve_dept无approve_dept审批部门主管branch_gte3$days3否则 approve_hr单人branch_gte3条件分支无approve_director无approve_director审批总监approve_hr单人approve_hr审批人事角色end单人end结束无无无配置完成后再走一单测试申请事假2天部门主管审批完成后待办列表应出现人事备案申请事假5天部门主管审批完成后待办列表应出现总监审批。整套配置过程里不需要写一行代码只需要在流程设计器里拖动节点、填字段、保存发布这就是「自己设计审批流程」在用户侧的真实样貌。如果源码里配置一套流程需要修改 PHP 或 Java 文件那么它更接近表驱动而不是可视化设计你要考虑是否用 2.2 的知识去补充一个配置界面。4. 避坑审批流程设计最容易翻车的五个真实场景再顺手的源码真正跑起来时也会暴露问题。这五个坑我在实践里基本都踩过而且每条都不冷门。记录成「现象-原因-解决」的形式方便你排查时直接对号入座。4.1 现象改完流程旧单子全部找不到审批人了源码里配置了新版本的请假流程把原「部门主管审批-人事备案」改成了「部门主管审批-总监审批-人事备案」。第二天一批在途的老请假单进入总监审批节点时直接报错说找不到下一节点。原因这套源码的流程实例没有保存节点快照运行中的实例始终实时读取最新流程定义。流程定义一变旧实例的下一节点对新版本来说可能已不存在或者节点连接关系对不上。解决给流程实例增加一个字段 current_version在发起时固定实例使用的流程版本。审批流转时必须用实例上的版本号去取节点信息而不是查最新版本的流程定义。我用一个通用做法流程实例表里加 flow_version 字段并在审批时的 get_next_nodes 查询中带上该字段条件。SELECT * FROM oa_flow_node WHERE flow_def_id #{flowDefId} AND flow_version #{instance.version} ORDER BY node_seq;4.2 现象条件分支经常走到错误审批人那里请假5天的单子按规则应该走总监审批结果直接跳过了总监、落到人事备案。表单里明明填了 5 天但是判断时始终取不到正确的数值。原因分支表达式里写的字段是 days表单传入的字段名是 leave_days表达式引擎取不到值时做了降级处理默认走 else 分支。这种问题多表驱动配置场景中最容易出现因为表达式是字符串配置的运行期间不会有人提醒你字段名拼错。解决在表达式引擎里加入严格校验和体检功能。解析表达式之前先从表单字段表中查询所有可用字段名如果引用的键不存在直接抛出异常而不是静默降级。同时在流程发布前增加一次自检用一组测试数据跑一遍整个流程分支确保所有路径都被覆盖。4.3 现象审批人连点两次通过同一个节点通过了两次电脑卡顿、审批人双击了审批按钮结果系统生成了两条通过记录。会签节点本来 3 个人现在 4 条记录流程判断时永远无法进入下一步。原因前端没有做请求防重后端也没有在任务表上做幂等约束。核心原因是代码里的「插入审批记录」和「更新任务状态」两步操作被分在不同的数据库事务中执行或者事务隔离级别不够导致了重复提交。解决在审批任务表上建立唯一约束保证同一审批人对同一个任务只能有一条有效记录然后以这个唯一键作为插入前置条件后端接口设计上也要增加一个 request_id 参数每次打开审批页面生成一次提交时校验 request_id 是否已用过。具体的数据库层防重语句参考如下。ALTER TABLE oa_flow_task ADD UNIQUE KEY uk_task_user (instance_id, node_key, approver_id);在有这条唯一约束的前提下插入时用 INSERT IGNORE 或者先查后插重复点击只会有一条记录生效流程引擎读取到的通过人数始终准确。4.4 现象会签节点驳回到发起人再重新提交时审批人列表变少了一个需要三位审批人会签的节点审批被驳回到了发起人处。发起人修改表单重新提交以后会签节点只剩一位审批人了另外两个人的待办任务没有重新生成。原因驳回操作将节点状态重置为待审批但只重新生成了当前处理人的那一条待办没有重新读取节点配置里的完整审批人列表。会签任务在首次生成时按审批人列表批量插入驳回后没有对应做批量重建。解决统一封装一个重发任务的函数在驳回到任意非发起人节点时调用。该函数做的事情很简单把该节点下所有已有任务先标记为取消再根据当前表单数据重新解析审批人列表整体重建待办。注意重建时要保留「已提交过的审批记录」用于历史追溯不要物理删除。def rebuild_node_tasks(instance_id, node_key, form_data): cancel_all_tasks(instance_id, node_key) users resolve_approver_users(node_key, form_data) for user in users: create_task(instance_id, node_key, user)4.5 现象多实例并发审批时已经同意的意见被后续驳回操作覆盖一个节点有三个审批人A 已经同意B 审核时选择了驳回。结果 A 的同意意见被系统清掉流程直接回到发起人但业务侧已经误以为 A 那关通过了。原因很多源码里的驳回实现是「把流程实例状态置为驳回并把所有同节点任务全部标记为作废」没有区分应该取消哪些任务、保留哪些记录。正确逻辑应该是驳回操作的目标是让流程回到某个节点而不是清空所有历史意见。解决驳回动作只作用于「当前待办」和「需要重新审批的节点」对于已经通过的兄弟节点记录保留原状。实现上把作废范围限定为当前节点内状态为待办的任务而已完成任务的状态不变。判断依据是任务表的 status 字段只在 status0待办时置为取消status1已通过的记录不处理。5. 进阶实战流程效率优化、设计器体验与验证清单流程引擎跑稳了下一步不是停止而是给自己增加一些能让系统更好用的能力。以下三个点是「一套OA系统源码可自己设计审批流程」在真实办公环境里最容易被上级和同事夸奖的地方。5.1 高频优化节点超时提醒与自动催办我的习惯是在流程实例表里增加 expected_finish_time 字段同时配置每个节点允许的处理时限。核心逻辑是靠定时任务每分钟扫描一次待办表把当前时间大于 expect_time 且状态仍为待办的任务捞出来统一推送钉钉或企业微信通知。为避免重复提醒加一个 last_remind_time 字段控制同一任务每 8 小时才提醒一次。def scan_timeout_tasks(): tasks find_overdue_tasks() for task in tasks: if task.last_remind_time is None or (now() - task.last_remind_time).hours 8: send_remind(task.approver_id, task.task_name) update_last_remind_time(task.id)这套逻辑改动量不大但收益非常可观。它把审批推着走的体验从被动变成主动。5.2 验证清单新流程在上线前跑一遍这些检查每设计完一个新流程我建议按下面这张表过一周验收而不是直接丢给用户使用。检查项通过标准分支覆盖测试每个分支条件分别发起一单确认走对应路径会签/或签验证三人会签时分别测试全部通过、一人驳回、一票否决驳回再提交驳回到发起人/中间节点后重新提交能完整走通版本升级验证旧流程在途单不因新版本发布而中断并发压力测试同一任务连续提交多次只产生一条有效结果每次做新流程我都不跳过这套清单因为流程设计的绝大部分 bug都出现在边界条件和组合场景。单独测试每个节点时一切都很正常一旦把分支和驳回串联起来问题就全冒出来了。最终还有个个人习惯想分享我在生产环境的流程设计器上方始终保留一行醒目提示——「优先为每个节点填写清晰的 node_key不要用数字 ID」。因为当流程数量超过 30 个之后你维护的不再是流程图而是一堆节点之间错综复杂的关系清晰命名的收益会成倍放大。被莫名其妙的数字节点坑过两次之后我痛定思痛制定了这条规则。希望这个细节和你在这篇文章里读到的所有避坑经验一样能帮你省掉未来大量排查时间。希望你也能做出一套真正能让业务顺畅运转的OA流程系统。本文还有配套的精品资源点击获取
返回列表