ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

泛微流程引擎自定义函数实战:从配置到开发的进阶指南

泛微流程引擎自定义函数实战:从配置到开发的进阶指南 在泛微系统的流程引擎里折腾久了你会发现“自定义函数”这四个字是实施工程师和业务部门之间最关键的那道桥。业务说“合同金额超过100万的要自动转法务审批低于100万走普通审批就行”标准流程设计器里你也能拼出这个条件可一旦涉及跨表取数、自动计算金额大写、审批通过后回写ERP甚至节点上动态调整审批人纯配置就开始不够用了。这篇文章我想把这几年在泛微流程引擎里用自定义函数的经验完整整理一遍。它不是什么官方文档复刻而是实战项目里真正跑过、踩过、修过的内容。适合谁看一类是刚接手泛微二次开发的实施/开发人员另一类是公司里负责流程梳理但总被“函数能不能实现”问住的流程负责人。看完你应该能回答三个问题函数到底写在哪里、怎么让它生效、出问题怎么排。需要声明一点泛微的产品线很多版本迭代也快E-cology 8.x/9.x等各版本界面和入口有差异本文讲的是流程引擎里通用的函数应用思路和机制具体菜单名称以你手上的环境为准。1. 先搞清楚流程引擎里的自定义函数到底长什么样1.1 三个落点对应三种完全不同的函数我在培训同事时喜欢用一个类比流程引擎是一辆自动驾驶的车表单是车厢审批人是乘客而自定义函数就是车里的传感器和方向盘——它们负责在特定时机感知数据、做出判断、改变方向。泛微的流程引擎里自定义函数实际上分布在三个完全不同的位置很多新手分不清导致“函数写了为什么没反应”这个问题反复出现。表单层的函数作用于字段负责计算、校验、联动。比如根据输入金额自动生成大写、根据请假类型联动默认审批人、提交时校验明细表不能为空。这类函数平时写在表单设计的字段事件或字段默认值里部分版本还支持独立维护函数库。路由条件层的函数作用于流程走向负责判断“走这条线还是那条线”。泛微流程路径上支持条件表达式表达式里除了比较运算符还可以调用已经定义好的函数把返回值作为判断依据。节点执行层的函数作用于流程动作负责在节点进入、离开、审批通过等时机执行一段额外逻辑。典型的是审批通过后自动更新业务表、调用WebService通知外部系统、生成待办待阅等。这类函数往往以SQL、Java类或脚本形式存在。这三个落点决定了函数的调用时机、可用的上下文变量和出错后的排查方式。表单函数错了是“字段值不对”路由函数错了是“流程走错了路”节点执行函数错了是“该干的事没干”。排错入口完全不一样。1.2 业务场景速查下面列一些我在真实项目里遇到过的需求可以帮你快速判断“这个需求要不要动用自定义函数”需求类型典型描述用不用函数放在哪一层字段自动计算单价×数量总价需要表单层格式转换数字转中文大写需要表单层跨表单带值从付款单带出合同金额可能需要路由/入口层流程自动分流金额超阈值走法务需要路由条件层审批后回写更新台账状态需要节点执行层外部系统同步推送金蝶、SAP等需要节点执行层复杂人员判断按条件动态选审批人需要节点执行层普通条件判断数字大小、字段等于不需要流程引擎原生支持-这张表不是标准答案但它代表了我评估需求时的第一反应先想清楚函数要放在流程的哪个环节再动手写。1.3 哪些问题不该用自定义函数解决有一条经验很重要不要什么需求都上函数。泛微流程引擎本身已经提供了很多“不用写代码”的能力比如流程关口、节点操作按钮、条件判断大于/小于/等于、表单的级联下拉、字段回填等。能用配置解决的配置优先函数是用来补标准功能空缺的不是用来替代标准功能的。我见过最典型的反面案例是有人用一个很大的脚本在流程节点里轮询数据库只是为了实现“上个节点审批通过后把状态字段改成已审批”。其实这种需求用泛微节点的“节点操作”配置加一条更新语句就能完成完全不需要动用自定义函数。函数越多后续维护成本越高。这个原则贯穿全文。2. 从第一个函数开始写表单字段的自动计算与联动2.1 一个标准“申请单”里函数放哪最合适先拿最常用的表单层函数举例。假设要做的是一张“合同付款申请单”下面有合同金额和付款金额两个字段。业务要求付款金额不能超过合同金额的90%超过就提示且不允许提交。这类规则一般放表单字段的事件里。泛微的表单支持在字段上配置“JS事件”比如失焦、变更来写前端函数也支持在字段默认值里放后端函数来做计算。前端函数的优点是即时反馈用户输入完就提示缺点是依赖浏览器环境容易被绕过。后端函数的优点是提交到服务器计算可靠但要等保存/提交动作触发。我的习惯是能前端实时反馈的校验放前端必须作为流程判断依据的取值逻辑放后端。因为流程路由条件读取的是后端已经存储的字段值前端函数的计算结果如果不回写数据库路由条件一样取不到。2.2 写一个金额转大写函数前端示例金额转中文大写是报销、合同、付款单里极高频率的需求。在泛微表单里我一般写一个JS函数挂在金额字段的失焦事件上再把计算结果回填到“金额大写”字段。function numToChinese(num) { if (isNaN(num) || num ) { return ; } var strOutput ; var strUnit 仟佰拾亿仟佰拾万仟佰拾元角分; var numStr Math.round(num * 100).toString(); var zeroCount 0; for (var i 0; i strUnit.length; i) { var digit numStr.charAt(numStr.length - 1 - i); if (digit || digit 0) { zeroCount; if (i 6 zeroCount 4) { strOutput 万 strOutput; } if (i 10 zeroCount 4) { strOutput 亿 strOutput; } if (i 0) { strOutput 整 strOutput; } } else { if (zeroCount 0) { strOutput 零 strOutput; } strOutput 零壹贰叁肆伍陆柒捌玖.charAt(parseInt(digit)) strUnit.charAt(i) strOutput; zeroCount 0; } } return strOutput; }写这个函数最麻烦的不是算法本身而是边界值0怎么处理、中间有0怎么处理、整数部分为0时是否显示“零元”。我在项目里就直接把一份经过大量测试的完整算法脚本放到函数库里所有表单复用而不是每张表单重写一遍。2.3 主表字段和明细表字段联动最容易踩的取值坑接着就是各类函数里出错率最高的部分主表字段的取值和明细表字段的取值。泛微的表单通常分为主表单条记录和明细表多条记录比如合同下面的付款计划。写函数时从明细表取数必须通过明细表对象逐行遍历而不是像主表字段那样直接取值。常见错误是拿主表字段ID去取明细数据取出来永远是空。前端的写法大致是var table document.getElementById(detailTableId); // 明细表对象 var rows table.rows; var total 0; for (var i 1; i rows.length; i) { var amountCell rows[i].cells[2].getElementsByTagName(input)[0]; total parseFloat(amountCell.value || 0); }后端的写法要通过泛微的明细表数据模型取拿到明细行集合再遍历。这里特别提醒后端明细表的字段标识和界面上的显示名不一样必须查表单字段标识否则遍历到的行老是null。这两个坑我踩过不止一次尤其是“看起来取了值实际上取的是第一个单元格的标题”这种问题不打印出来根本发现不了。3. 流程拐弯靠函数合同超阈值自动进入法务审批3.1 需求到底难在哪把最典型的场景拉出来展开一遍合同金额超过100万流程自动多一个“法务审批”节点不超过100万就直接走部门负责人审批。如果金额是在表单上直接填的配置完全可以实现流程设计里画两个出口法务那条线加一个条件“合同金额1000000”。可现实里合同金额往往是从OA里的合同台账带出来的或者是从其他系统对接过来的它可能不是一个表单字段而是一个外部数据源的值。这时候就要在路由条件里用函数。常见的做法是在流程入口或表单保存时通过函数把合同金额从台账或外部接口取出来写入流程主表的一个隐藏字段然后路由条件判断这个隐藏字段。这个隐藏字段就是函数计算的“落地结果”。3.2 路由条件里的函数怎么配置泛微流程设计器里路径的属性中可以设置“条件”。条件有两种形态一是基于字段的简单比较二是基于表达式的条件函数。表达式里可以写类似if (字段的值 1000000) { return true; } else { return false; }不同版本对语法要求不一样有的用Groovy类脚本有的用BeanShell有的直接用可视化的“函数”下拉框选择已经定义好的函数。配置前务必认真看当前环境的帮助文档这里我只说通用思路条件函数的入参是流程主表数据对象你可以通过它读取所有字段值。返回值必须是boolean流程引擎根据true/false决定走哪条路径。函数内尽量避免复杂计算路由条件会被频繁调用性能直接影响流程流转速度。3.3 一条稳妥的调试链路我每次改完路由条件都会在测试流程里走过一遍完整闭环并刻意做三组测试用例金额刚好等于100万金额100.01万金额99.99万这三组用例能暴露绝大多数边界问题比如临界值的比较符写错写成、单位没换算成统一单位等。调试时不要只盯着流程走到哪了还要看函数的实际返回值。可以在函数里临时记录一条日志把入参和结果打出来跑完流程后查看。我在现场环境里的做法更土也更有效在表单上加一个“调试用隐藏字段”函数算出的结果直接赋给这个字段走完流程后把这个字段显示出来业务方一眼就能看到函数到底返回了什么。3.4 常见反例为什么流程总是走错分支我经常在代码Review时看到这样的反例在路由条件里调一个外部接口。网络一抖动流程直接卡住审批人全都进不去。条件函数里写死了某个字段结果表单结构一调整函数没同步改流程全部走到默认路径没有任何报错。函数里读取的是“当前节点字段值”但该字段在流程流转过程中被后面节点修改了导致路由条件结果不稳定。这些都是需求层面看起来很合理、实现后却很脆弱的写法。经验是路由条件函数只做死的逻辑判断不做实时数据获取要获取外部数据提前在入口节点把它算好、落到字段里判断前后字段值保持稳定。4. 节点操作中调用外部系统函数当“桥”用4.1 场景审批通过后把数据同步给金蝶/ERP审批类流程对接ERP是特别常见的需求。我在项目里做过一个典型的集成合同在泛微走完审批审批通过后需要把合同编号、供应商、金额、税率这些字段同步给金蝶/ERP系统作为财务做账的依据。这个需求在泛微流程引擎里有多种实现方式其中一种就是节点执行函数。在“合同审批”流程的最后一个节点比如“归档”上配置一个执行函数在节点动作提交后触发同步逻辑。另外很多人搜索时会看到“泛微oa系统单点登录金蝶”那个是登录层级的集成解决的是“从一个系统免登录跳转到另一个系统”的问题而这里讲的是“流程数据推送到业务系统”两者层次完全不同不要混在一起设计。4.2 用Java类/BeanShell写同步逻辑泛微节点操作支持调用Java类也可以写BeanShell一种轻量级Java脚本。实际项目里我更多是把复杂逻辑封装成一个Java类放到服务器classpath里然后在流程节点上配置对这个类的调用。大致思路public class ContractSyncToKingdee { public String sync(String requestId) { // 1. 根据流程requestId取流程主表数据 // 2. 通过泛微的数据接口解析合同字段 // 3. 调用金蝶WebService接口把数据传过去 // 4. 记录同步结果日志失败时返回错误信息 } }流程节点上配置“执行方式”为“Java类调用”把类名和方法名填上并指定触发时机比如“节点归档时”“审批通过时”。这里最关键的是要理解执行时机审批人点“同意”后动作提交成功才会触发节点执行函数。如果函数报错流程本身可能已经走完也可能被回滚取决于异常处理策略。所以设计时要明确“同步失败怎么办”。4.3 同步失败的兜底设计这一步是真正考验项目的部分。外部系统不可能一直稳定函数在流程里执行时一旦网络超时要么重试要么把失败的记录留痕通过定时任务或手工按钮补偿。我一般会做一张“外部系统同步日志表”函数里每次调用都往日志表写一条记录状态为“待同步/成功/失败”。跑批时扫描“失败”的记录尝试重推。这样即使接口挂了也不会丢数据。当然日志表本身可以放在泛微内置数据库中也可以放到对接方提供的中间表里看项目架构。4.4 性能与使用周期别把函数当成万能接口节点执行函数也是函数但它运行在流程服务器上占用的也是流程引擎的线程。如果函数里同步数据、调用外部接口耗时超过几秒审批人点完“同意”后页面会一直转圈体验极差。如果外部系统慢甚至会把流程引擎的线程池拖垮影响整站流程。遇到这种情况我的建议是不要同步调用而是改成异步流程节点执行函数只负责写一张“需要同步的任务表”并立即返回后台有一个定时任务去消费这张表慢慢同步到金蝶。这样流程流转速度不受外部系统影响稳定性也高。这个模式还有一个好处接口方升级或临时维护时不会阻断泛微这边的审批流。5. 踩坑复盘那些年自定义函数“不生效”的原因5.1 函数写对了流程就是不跑作用域与触发时机这是排名第一的新手困惑。写了一个表单默认值函数结果新建流程时字段是空的写了一个路由条件函数流程却永远走默认路径。排查后多数是作用域和触发时机的问题。表单默认值函数只对“新建单据”生效对已有的历史单据不会重建默认值流程入口处给字段赋值的方法和在节点上改变字段值的方法也不一样。函数里面的“当前节点”概念和“全局数据”概念经常被混淆。排查时的关键动作是确认函数在哪个动作点保存、点提交、点同意、点撤回之后才执行再用临时日志去验证。5.2 数据对不上类型、精度、空值三座大山另一个高发问题是函数返回的数据看起来对实际用起来不对。金额字段尤甚。泛微表单里的金额字段底层类型可能是浮点型也可能是字符串比较时如果不做类型转换会出现“1000000”这样的字符串比较导致“1000000 999999”这种反直觉的结果。我的处理习惯后端取值时统一用BigDecimal或double处理并显式格式化。空值当成0还是当成非法必须先在函数里约定好否则关联逻辑毫无安全感。前后端传输数字时不要直接用字符串拼接尤其是金额精度丢失在财务上是没法接受的。5.3 死循环与无限触发函数要设计成幂等还有一类坑是函数自己在“喂”自己。比如A字段的联动函数改了B字段B字段的联动函数又改了A字段两个函数互相触发最后页面卡死或者流程数据错乱。解决这个问题的核心思路是幂等一个函数无论被触发多少次最终数据结果都一致不要因为一次触发就把数据累加一次。我见过最痛的一次是明细表合计字段被设成了“每次保存就累加”结果用户多点了两次保存金额直接翻了三倍。所以写函数前先问自己这个函数如果重复执行十次结果会不会变会变就要加防止重复触发的条件比如用操作类型判断是新增还是修改或者用状态位标记是否已计算。5.4 一条可复用的排查链路最后把排查链路整理成一套可以照做的流程你遇到“函数不生效”时按这个顺序走基本能定位八成问题先在流程里放一个空的节点用测试单据跑一遍确认业务数据本身有没有问题。在函数入口和出口各加一行日志打印入参和返回结果跑完后看日志。确认执行时机是在提交前还是提交后是在节点进入时还是节点离开时。确认数据是否被后续节点覆盖查数据库里主表字段的实际值。把函数返回值直接暴露到表单一个隐藏字段上跑完看这个字段的值便于业务人员反馈。这套方法不依赖调试器在客户现场的测试环境里最好用。现场环境往往没有开发工具能依赖的就是日志、数据库和那条测试单据。做流程引擎自定义函数这几年我最大的体会是函数本身不难难的是想清楚它该在哪一层、什么时机执行、失败之后怎么办。泛微系统给了一个很宽的口子但你用得越克制流程越稳定。项目里真正出问题的地方几乎都不是“函数不会写”而是“函数放错了位置”或者“没考虑重复执行和失败兜底”。最后给刚上手的朋友一个很实际的建议在你们公司的泛微环境里单独建一个“函数测试流程”专门用来验证那些你新写的函数脚本没有作用域和边界问题后再复制到正式流程里。测试流程不审批、不通知、不影响任何业务数据但它能陪你避免很多生产事故。这个习惯我坚持了快四年值得你试试。
返回列表