首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
NC65二次开发必备:常用API、查询保存审批流与避坑指南
📅 2026/10/14 16:04:23
✍️ 爱科研究院
👁 阅读 3,247
简介NC65开发常见API详解是一份面向用友NC65平台开发者的入门参考聚焦表单、报表、界面交互中高频使用的接口方法。内容覆盖表体行列获取、默认值设置、编辑公式、小数位控制、按钮可用性管理、查询面板取值、单据继承关系等18类常见开发场景每个场景均配有代码片段适合新手对照练习也能作为日常开发速查手册。资源为PDF电子文档共1个文件压缩包大小193KB便于快速下载与本地查阅。目前已有582人学习下载受到NC65入门开发者的认可。文档以场景化方式组织从取数、显示、编辑到打印、弹窗、数据清空均有示例并补充了界面数据访问、导入导出、list/map/set操作等进阶技巧可帮助开发者缩短API摸索时间减少踩坑提升NC65单据与报表开发效率。1. NC65开发的迷茫期先知道 API 藏在哪、管什么事很多新手第一次拿到 NC65 工程时面对几百个jar包和一堆以nc.*开头的类第一反应是懵的查数据该调谁、保存单据该调谁、审批流又该碰哪个类。这个阶段最消耗人的不是业务逻辑而是“不知道标准做法在哪”。NC65 开发常见 API 这条路径实际上就是在帮新手把两个问题一次解决掉一是知道常用操作对应哪几个核心服务接口二是拿到能直接改着用的代码骨架。这篇文章按查询、保存、审批流、单据转换、避坑、最小工程骨架的顺序走一遍覆盖的是日常开发里重复率最高的操作。适合正在做 NC65 二次开发、急需跑通第一个功能的初学者也适合刚接手遗留项目、想快速看懂老代码里都在调哪些 API 的熟手。2. 查数据与存数据查询 API、保存 API 怎么选、怎么调NC65 的数据操作有两条路一条是走平台封装好的服务接口另一条是拿JDBC或SqlCaution自己拼 SQL。新手最容易踩的第一个坑就是两条路混着用或者干脆自己写 SQL结果换来一坨权限过滤、多语、数据权限全部失效的代码。这一章把查询和保存的标准打法讲清楚。2.1 查询 API优先用 QueryDAO 而不是手拼 SQLNC65 里最常用的查询入口是平台提供的查询服务。它的好处是自动拼接组织权限、数据权限和业务状态的过滤条件你只要描述“我要查哪个表、查哪些字段、按什么条件过滤”就行。我一般会通过NCLocator拿到查询服务实例然后构造查询条件对象去执行。下面是一段常见的按条件查询仓库档案的示例代码// 1. 通过服务定位器拿到查询服务 QueryDAO queryDAO NCLocator.getInstance().lookup(QueryDAO.class); // 2. 构造查询条件表名、要查的字段、过滤条件 QueryConditionVO qc new QueryConditionVO(); qc.setTableName(bd_stordoc); qc.setSelectItems(new String[] { pk_stordoc, code, name, pk_group }); // 3. 添加过滤条件组织主键等于某值 ConditionVO cond new ConditionVO(); cond.setHead(pk_group, , 0001A1100000000000Z5); qc.setConditionValues(new ConditionVO[] { cond }); // 4. 执行查询返回 VO 数组 Object[] result queryDAO.query(qc); if (result ! null result.length 0) { // result 里的元素是动态 VO可以用 getAttributeValue 取字段 }这段代码中有几个参数值得注意。setTableName填的是物理表名而不是单据编码或 VO 名写错表名不会得到友好提示只会返回空数组。setSelectItems决定返回对象里带哪些字段缺了哪个字段后面取值就是null不会报错所以排查时先看这里。setConditionValues接收一个ConditionVO数组代表多个条件是“且”的关系如果希望“或”要单独处理条件组对象。如果查询条件涉及时间范围或模糊匹配可以把操作符换成/或者用平台提供的like关键字。这里最需要注意的是不要自己拼接WHERE字符串塞给底层 SQL因为条件值里的单引号、特殊字符可能会导致查询异常或被过滤掉。把条件交给ConditionVO平台会负责转义。2.2 保存 API用标准保存服务别自己写 update保存单据时新手经常犯的错是拿到 VO 后直接调save()或者自己发 SQL。这样做在简单的单表上能跑通但一遇到主表子表一起保存、业务状态要联动、审批单不允许重复保存这些场景就翻车。标准做法是调用平台提供的保存服务。// 1. 构建一个标准的业务 VO比如库存单据 BillVO billVO new BillVO(); // 2. 填充表头字段 billVO.setCode(20250607001); billVO.setPkOrg(1001A1100000000002); billVO.setBillmaker(u00001); // 3. 创建子表行并加到 billVO 的子表集合中 BillVOItem item new BillVOItem(); item.setMaterial(1002A1100000000007); item.setNum(10); billVO.setItems(new BillVOItem[] { item }); // 4. 通过保存服务执行保存 BillSave billSave NCLocator.getInstance().lookup(BillSave.class); billSave.save(billVO);这里需要解释为什么不能直接save()。BillSave在保存过程中会触发平台内置的后台校验比如必填项检查、编码重复判断、子表合法性校验还会在保存成功后抛出业务事件这些事件是后续动作链的基础。如果你绕过它所有监听这些事件的扩展逻辑都不会执行表是写进去了但整条业务链是断的。BillSave的参数并不是只用VO有些版本接口还需要传入操作类型比如新增还是修改。我的习惯是先调用setPk判断主键是否为空如果为空走新增逻辑否则走修改逻辑再交给BillSave处理。不要主动去调update()或insert()把“该走哪条路”的判断交给平台你只负责把 VO 组装好。2.3 参数、编码规则这些小 API 也是刚需除了查询和保存业务开发里还有两个高频小 API编码规则和系统参数。不做二次开发的人可能永远不知道它们的存在但做单据时几乎躲不开。// 1. 获得编码规则实例 BillCodeManage codeManager NCLocator.getInstance().lookup(BillCodeManage.class); // 2. 按单据类型生成编码传 VO 进去用于取组织等信息 String billCode codeManager.getBillCode(34xx, billVO);getBillCode的第一个参数是单据类型编码第二个参数是业务 VO。编码规则里如果配置了“按组织独立流水”那这个 VO 里必须携带组织主键字段不然生成出来的编码可能跨组织重号。系统参数的读取用的是类似 NCLocator 的方式拿到参数服务后按参数编码取值常用于控制某些按钮是否显示、某些字段是否必填。这一层 API 的价值在于让代码不依赖硬编码。比如页面上有个“是否允许超量入库”的开关你从参数表里查出来判断以后业务改了参数代码不用动。3. 审批流与单据转换NC65 业务开发最容易卡壳的两个 API 场景查询和保存是单表操作审批流和单据转换则是跨模块操作也是新手接手 NC65 项目后最难受的两个地方。审批流涉及状态机的流转单据转换涉及类型之间的字段映射两者恰好是 API 使用不规范就会出静默问题的地方。3.1 审批流 API提交、驳回、撤回的调用姿势NC65 的审批动作并不是简单地把状态字段改成“已审批”。标准写法是调用审批服务由平台统一处理待办、历史记录和操作日志。// 1. 锁定当前单据 billVO.setStatus(2); // 常见状态值2 表示提交审批具体以单据类型为准 // 2. 调用审批服务提交 Pub_Approve approveService NCLocator.getInstance().lookup(Pub_Approve.class); approveService.approve(approve, billVO); // 3. 驳回场景 approveService.approve(unapprove, billVO);这里最容易翻车的是状态值的含义。不同单据的“提交审批”状态码可能不同有的用 0 表示自由态2 表示审批中3 表示审批通过。你最好先查一下目标单据类型的后台枚举或标准动作脚本再写状态赋值别想当然地照抄别处代码。调用审批服务后平台会校验当前状态是否允许该动作如果不允许会直接抛业务异常。多级审批和会签逻辑不要自己写也不要试图通过改状态字段去模拟。正确做法是配置审批流然后代码里只发提交动作。每一次提交之后平台会按照流程定义找到下一步审批人。你在代码里能做的只是“发起”和“响应结果”而不是自己串流程。项目里有一类经典问题就是实现人在代码里强行把待办清掉结果流程记录和单据状态不一致后面追溯全部对不上。3.2 单据转换 API一张单变另一张单的拷贝逻辑采购订单转采购入库单、销售订单转发货单这些动作在 NC65 里由单据转换服务负责。转换的本质是根据配置好的字段映射规则把源 VO 的字段值拷贝到目标 VO然后生成目标单。// 1. 拿到转换服务 IBillTransformService transformService NCLocator.getInstance().lookup(IBillTransformService.class); // 2. 执行转换参数分别是转换类型、源 VO、目标类名 Object targetVO transformService.transform(1, sourceVO, nc.vo.scm.po.POBILLVO);如果转换后目标 VO 里有一批字段是空的先检查转换类型编号是否配错。NC65 里同一个源单可以配置多种不同的转换规则比如“订单转采购”和“订单转退货”用的是不同的转换类型编号在数据库的映射配置里查一下就能看到差异。不要靠猜。字段没带过去还有一个常见原因源 VO 的子表行没有正确设置关联主键。转换服务靠主键关系把源表头和源表体对应起来如果子表行的主表主键为空转换时只能匹配到表头子表体就丢了。3.3 操作日志和消息 API别忽视那些“看不见的接口”业务单据提交后页面上会显示操作历史“创建、提交、审批通过”这些记录并不是框架自动完成的而是代码通过日志服务写入的。如果你在扩展开发中自定义了按钮动作却没有记录操作日志用户会看到单据状态变了但历史里一片空白排查时非常被动。这个服务的调用方式很简单构造一个操作日志 VO填入动作编码、对象主键、操作人、操作时间然后调用保存日志的接口。不用做复杂封装直接调用即可。但有一点值得提醒操作日志的“动作编码”要和单据类型内置的动作编码保持一致否则前端展示时无法正确映射成中文按钮名称。消息 API 用于在流程节点上给审批人发待办消息。如果你的二次开发里没有主动调用消息服务再大的单据也只会在当前登录人这边看到别人根本不会在待办中心里收到提醒。这块服务的标准操作是拿目标用户主键和消息模板编码再填几个业务字段调用发送消息接口。新手往往在“单据已经提交了但审批人看不到”这个问题上浪费大半天其实只是少调了一个消息服务。4. NC65 API 避坑实录5 个反复折腾的新手问题这一章不是概念讲解是从真实开发里踩出来的记录。每一条都按“现象 → 原因 → 解决”的顺序写代码跑不通、结果对不上、逻辑不触发的时候优先对照这里。4.1 查询返回结果为空SQL 在数据库里却能查出数据现象把查询条件抄到数据库客户端里执行明明有数据换成平台 API 查出来却是空数组。原因绝大多数是条件里的组织主键有问题。NC65 的查询服务会自动追加数据权限过滤当前登录人看不到其他组织的单据是正常的。另一种可能是表名填错比如把视图名当成表名传给了setTableName。解决先用管理员身份登录跑一遍排除权限干扰。然后把ConditionVO里的条件逐个去掉定位到具体哪个条件把结果集过滤掉了。如果怀疑是表名问题就在工程里搜一下目标 VO 对应的物理表名不要凭记忆写。4.2 保存成功但单据列表里看不到现象调用BillSave没有报错回到单据列表却查不到新单。原因常见原因是保存时没有正确写入组织主键或单据状态。列表查询默认会按当前登录组织过滤如果你的 VO 里组织主键是空字符串保存服务不会报错但查询端把它过滤掉了。还有一类情况是保存的是草稿单列表入口只显示已提交单据。解决在保存前把组织主键、单据状态、制单人都显式赋值。保存后立即用刚生成的pk走一遍 2.1 节的查询方法确认数据落库且过滤条件能匹配。如果列表还是看不到就查列表查询的过滤条件看是不是状态被写死了。4.3 保存后触发的事件没有执行现象代码里监听了保存事件希望在保存后额外写一张日志表但日志表永远是空的。原因大概率是保存时没有走BillSave而是直接用 VO 的save()绕过了事件发布。还有一种可能是监听器注册的类名与单据类型不匹配事件发布出去但没找到对应监听者。解决先确认保存动作确实走了标准服务再检查监听配置。在监听方法第一行打日志看有没有进入。如果日志都没打印说明监听器根本没被加载如果打印了但没写库再检查日志表连接或事务是否被外层回滚。4.4 审批流提交后状态是“自由态”审批人也没收到待办现象调了审批服务不报错单据状态没变审批人也没有待办。原因这个现象通常是 VO 里没带主键或状态值没有匹配到有效动作。审批服务拿到 VO 后先用主键找到持久态数据再判断当前状态是否允许提交。主键为空时它无法判断要审批哪张单所以静默返回。解决在提交前打印 VO 的pk值确保非空。再确认状态码与单据类型的动作配置匹配不要把别处单据的状态码照搬过来。最后检查当前用户是否是制单人如果当前用户与该单的制单人不是同一个审批流配置里可能不允许提交。4.5 单据转换后子表数据少了一半现象订单两张子表转成下游单据后只保留了一张子表的内容另一张全丢。原因转换配置里没有给那张子表配置映射关系平台只拷贝配置过的字段未配置的直接不处理。不是代码性能问题也不是丢数据是规则没配全。解决到转换映射配置界面查看目标 VO 各子表的分录映射。对照源 VO 的子表标识、目标 VO 的子表标识逐个检查。少哪条配哪条配完重新执行转换。如果是代码里直接调transform的检查传入的源 VO 子表是否都被正确填充且与表头关联。5. 一套最小可跑的 API 代码骨架从登录用户到保存单据前几章把 API 分开讲了这一章把它们串成一个完整的最小流程。新手可以照着这个骨架改把“查询、生成编码、组装 VO、保存、记录日志”一次跑通。以新增一张库存单据为例。5.1 从登录上下文取当前用户与组织NC65 后端代码通常会从当前线程上下文里取登录信息不要自己从请求参数里传用户和组织。下面这段用于在服务方法内部获取会话信息。// 1. 拿到当前登录用户上下文 LoginContext context LoginContext.getInstance(); // 2. 从上下文取用户主键、组织和语言 String userId context.getPkUser(); String pkOrg context.getPkOrg(); String locale context.getLocaleStr().toString();getPkOrg拿到的是当前登录人默认组织如果业务要求跨组织操作需要显式传入目标组织主键。开发中常犯的错是把userId当成用户编码用实际上很多接口要的是pk主键两者长相类似但完全不同。5.2 一步到位编码、VO、保存、日志这段代码会走一遍完整流程// 1. 取上下文信息 LoginContext context LoginContext.getInstance(); String userId context.getPkUser(); String pkOrg context.getPkOrg(); // 2. 生成单据编码 BillCodeManage codeManager NCLocator.getInstance().lookup(BillCodeManage.class); String billCode codeManager.getBillCode(34xx, null); // 3. 组装 VO BillVO billVO new BillVO(); billVO.setCode(billCode); billVO.setPkOrg(pkOrg); billVO.setBillmaker(userId); billVO.setStatus(0); // 新增状态具体以单据类型为准 BillVOItem item new BillVOItem(); item.setMaterial(1002A1100000000007); item.setNum(10); billVO.setItems(new BillVOItem[] { item }); // 4. 保存 BillSave billSave NCLocator.getInstance().lookup(BillSave.class); billSave.save(billVO); // 5. 记录操作日志 OpLogVO logVO new OpLogVO(); logVO.setBillCode(billCode); logVO.setActionCode(CREATE); logVO.setOperator(userId); opLogService.saveLog(logVO);第 2 步中getBillCode传的null不太好如果编码规则依赖组织特征段这里应该传一个带pkOrg的轻量 VO否则生成结果可能不带组织前缀。第 3 步的状态值0是常见实现里的新增状态不同单据类型可能不一样务必以目标单据的实际枚举为准。第 5 步日志的actionCode要和单据类型内置动作一致这样前端按钮历史才能正常显示中文。5.3 跑通后怎么验证写对了跑通标准不是“没报错”而是四个点同时满足。第一数据库单据主表新增一条记录字段值完整。第二子表有对应数据且与主表能通过主键关联。第三单据列表或详情页能查到这张单。第四操作历史里有“创建”记录。如果前三点满足但第四点缺失说明日志 API 没调对对照 5.2 节的第 5 步排查。如果第四点之外有问题先回查查询 API 的过滤条件和数据权限。还有一条血泪经验不要只看返回 VO 里的字段要直接查库确认落库值因为某些持久化框架只做部分字段写入回显 VO 是缓存里的旧值。6. 比背 API 更有效的方法看懂标准功能生成的日志与断点定位新手最容易陷入的一个误区是把 API 当死记硬背的东西今天记住了一个查询接口下个版本类名变了就不会写。我在 NC65 开发里的一个靠得住的方法是不背 API而是把平台标准功能当成“参考答案”。思路是新建一个同类型单据在前台走一遍完整流程同时在后端打开调试断点看平台自己调用了哪些服务、传了什么参数、按什么顺序执行。这些调用链比任何文档都准确因为它是当前版本正在跑的代码。具体操作也不复杂。在 IDE 里对BillSave的save方法打上断点然后在前台保存一张标准单据断点命中后查看调用栈就能一路看到参数来源和校验逻辑。用同样的办法看QueryDAO的查询方法能直观地看到平台拼接了哪些过滤条件。经验之谈第一次打断点看查询执行比看十遍接口文档都管用。另外一个判断 API 用没用对的方法是看标准动作日志。后台日志中接口调用都有轨迹出错时优先看日志里的业务异常信息而不是只看堆栈最底下那几行。很多潜规则异常都包装在BusinessException里真正的业务错误提示就在最外层。我现在的习惯是每接到一个新的单据类型先花半小时走一遍标准前台操作并抓日志再写自己的扩展代码。这半小时省下的排查时间远不止半小时。学会让平台自己告诉你正确写法比自己硬猜强太多。希望帮到你。本文还有配套的精品资源点击获取
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/10/14 16:04:23
中间件安全实战:Tomcat/Nginx/Weblogic/Shiro——“Web服务器“为什么会成为突破口
2026/10/14 16:04:23
无人自助拼豆系统从需求拆解到上线运行全流程开发实战指南
2026/10/14 16:04:23
无人自助拼豆系统核心功能实现实战 从零到一落地开发全流程指南
2026/10/14 17:09:52
从零搭建业务系统:Doctrine、配置、文件上传与令牌鉴权实战
2026/10/14 17:09:52
Unity边缘检测原理与工业级Shader实现指南
2026/10/14 17:09:52
面向对象数据库:解决对象-关系阻抗失配的原生方案
2026/10/14 17:09:52
涛的天道观【其一百零一】文明层次的技术突破点
2026/10/14 17:09:52
Python 基础学习总结:从语法入门
2026/10/14 17:04:51
安全工程论文降重:哪些必须原样保留,哪些可以放心改
2026/10/14 0:02:01
Oracle 19c Windows客户端安装配置与连接排错指南
2026/10/14 0:02:01
React Native截图优化:解决Android兼容性问题
2026/10/14 0:02:01
金属锈蚀速率计算模型与防护方案优化实践
2026/10/13 12:25:09
流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南
2026/10/13 18:15:04
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别
2026/10/14 14:52:59
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容
2026/10/12 19:53:23
我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
2026/10/11 21:41:11
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026/10/11 23:43:10
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)