资讯动态

用友UAP开发NC65主子表单据:从向导到发布的全链路实战

发布时间:2026/10/15 15:30:46 来源:尧图企业网站定制
简介用友UAP NC65环境下基于向导开发主子表单据的完整流程与配套源码包适合正在学习或初次接触企业级表单开发的新手。内容覆盖创建主表、创建子表、主子表关联、交互逻辑、源代码解析、测试调试与优化等核心环节以实操步骤为主线帮助读者快速建立从数据库设计到界面联动的整体认知。压缩包共126个文件大小约4.56MB其中包含37个Java源代码、37个编译后的class文件、16个SQL脚本以及properties、xml、bmf/bpf等配置与工程描述文件源码与数据库脚本配套齐全便于直接导入开发环境对照学习。已有1673人学习使用可作系统化自学或项目参考。通过阅读代码与执行文档可掌握用友UAP平台API调用、控件数据绑定、主子表联动刷新等关键技能为后续承担更复杂的NC65二次开发任务打下扎实基础。1. 用友 UAP 开发 NC65 主子表单据向导只是开始链路才是重点在 NC65 上做一张带明细行的主子表单据很多人第一反应是“跑一下 UAP 向导点几下就完事”结果要么菜单里找不到新单据要么保存时直接报主键为空要么子表数据怎么都存不进去。用友 UAP 开发 NC65 主子表单据真正的工作量不在向导而在后面的整条链路元数据建模、卡片布局、动作配置、权限分配、发布部署每一环断了前端都会以各种奇怪的方式翻车。这篇笔记按我实际做过的顺序把主子表单据从新建向导到发布验收的完整过程拆开写关键参数和踩坑点都放在对应位置适合刚接手 NC65 开发的实施工程师也适合被主子表折磨过、想系统查漏补缺的开发人员。2. 环境与向导建表之前必须确认的四件事跑向导本身并不难难的是跑之前没人告诉你的那几件事。环境选错、账号权限不够、中间件连接不上都会让向导生成之后的发布步骤变成黑匣子。我先说清我一般怎么确认环境再进向导。2.1 开发环境UAP 客户端、数据库账号与中间件的关系NC65 的主子表单据开发链路是UAP 客户端连数据库建表元数据发布写到 NC 中间件界面模板最终跑在 Web 端。所以开发环境至少要确认三样东西UAP 客户端版本要和 NC65 补丁版本对应数据库账号要有建表、建序列的权限中间件地址要能连通并且允许发布元数据。我一般会在正式建表之前先跑一段 SQL确认当前账号到底有没有建表权限避免向导走到一半报无权限错误还得回头找 DBA 开权限。-- 确认当前数据库账号的关键权限重点看 CREATE TABLE 和 CREATE SEQUENCE SELECT privilege FROM user_tab_privs WHERE privilege IN (CREATE TABLE, CREATE SEQUENCE, CREATE VIEW) ORDER BY privilege;这段 SQL 的逻辑是以当前登录的数据库用户身份查询它拥有的表权限和序列权限。正常开发库会返回 CREATE TABLE、CREATE SEQUENCE、CREATE VIEW 等记录如果返回为空说明当前账号是只读账号向导建表这一步过不去。注意权限核查一定要在开发库上做生产库账号通常不会给建表权限也不需要给。顺带确认一个容易忽略的参数序列。NC65 生成主子表时主键经常依赖序列来生成如果账号没有 CREATE SEQUENCE 权限发布后保存依然会失败。我习惯提前在库里建好专用序列并授权而不是依赖向导去碰运气这样能少一次隐藏的翻车。2.2 向导入口与主子结构选择先建模再建表UAP 客户端登录后在集成开发平台下新建实体会弹出选择实体结构的向导常见选项有单表实体、主子实体、树形实体。做主子表单据时选“主子实体”系统会要求先定义主实体单据头再定义子实体单据体并约定二者之间的关联字段。这个顺序不能反过来因为子表必须在主表基础上挂接。关联字段是这套机制的核心主表主键比如 pk_purchase会被子表引用为外键字段向导界面里一般叫做“从实体引用主实体主键”。这一步选对后续子表数据才能和主表挂上关系。选错或者不选生成出来的子表就是一张孤立的明细表保存时外键永远为空。注意向导阶段不要追求字段一步到位。先把主表主键、子表主键、子表外键、状态字段建好其它业务字段等实体生成后在元数据编辑器里统一增补改动成本比在向导里反复回退低得多。2.3 字段规划命名、类型与精度的三个约定UAP 的表结构最终会落到数据库命名不统一后续写 SQL、做报表、对接第三方系统都非常痛苦。我给自己定了三条约定现在基本每张主子表单据都按这个来。第一条是主键命名。主表主键统一叫 pk_业务模块名比如 pk_purchase子表主键叫 pk_业务模块名b也就是在主表主键后面加一个 b 后缀子表里的主表引用字段直接复制主表主键字段名。这样从子表 SQL 里一眼就能看出它属于哪张主表。第二条是数量与金额精度。金额字段用 NUMBER(16,2)单价允许小数多一位的用 NUMBER(16,6)数量用 NUMBER(16,4)。NC65 的很多报表模板对精度有默认预期乱用 INTEGER 会导致单价小数点后直接截断对账的时候对不上。第三条是状态和日期字段。状态字段统一用 SMALLINT0 表示未保存、1 表示已保存、2 表示已提交写动作脚本时这样的语义最好记日期字段要么统一 VARCHAR 存储带格式的字符串要么统一 DATE 类型最怕主表用 DATE、子表用 VARCHAR后面联查和统计会疯掉。2.4 向导生成后的产物自动生成的类与配置清单向导跑完不只是建了几张表它会同时生成一套标准化的开发产物。很多新手不知道这些东西是干什么用的发布失败也不知道从哪里查我列了一张清单方便对照。产物作用常见问题数据库表存储主表与子表数据字段类型不对需要手工改表持久化 VO 类主表和子表的数据结构映射增删字段后未同步更新BM 配置文件实体元数据的描述文件元数据发布不完整导致单据无法识别界面模板列表模板和卡片模板模板未发布时菜单打开空白功能注册记录把单据挂到菜单和权限体系未分配权限时菜单里看不到向导生成后我会先去查一遍实际建表结果确认表名和字段名跟自己规划的一致。-- 按表名模糊查询向导生成的表确认主表和子表都已落库 SELECT table_name FROM user_tables WHERE table_name LIKE PK_PURCHASE% ORDER BY table_name;参数说明把 PK_PURCHASE 换成你自己的主表前缀即可。返回结果里应该能看到主表和子表至少两张表。如果一张都没返回说明向导并没有真正执行建表或者当前数据库账号连接的是另一个库这是最常见的环境错位问题。3. 元数据与界面模板主子关联的灵魂在“主键引用”和“卡片布局”向导生成的实体只是一副骨架。主子表能不能在界面上正常增删改取决于元数据里的引用关系配没配好以及卡片模板里子表区域绑定的数据是否正确。这一章是整套开发里最容易踩坑的地方我按建模、卡片、列表三层展开。3.1 元数据建模子表如何引用主表主键实体生成后打开元数据编辑器能看到主实体和子实体两个对象。关键操作是选中子实体的主表引用字段把它的引用目标设为主实体的主键字段。这个引用关系不会创建数据库物理外键但会告诉运行框架保存子表数据时用主表主键去填充子表里的这个字段。我每次都会检查两个配置项子实体是否勾选了“引用主实体主键”主实体是否开启了“级联保存子实体”和“级联删除子实体”。级联删除默认是关闭的如果希望删除主表时连带清掉子表明细需要在这里显式打开。生产环境尽量谨慎用级联删除一旦误删后悔药不好找。下面这段 Java 代码结构对应元数据配置完成后的 VO理解它有助于排查后续保存逻辑的问题。// 主表 VO一个单据头对应一个 List 的明细 public class PurchaseMainVO { private String pkPurchase; // 主表主键 private String billNo; // 单据号 private Integer billState; // 单据状态0草稿 1保存 2提交 private ListPurchaseSubVO subList; // 子表数据集 } // 子表 VO每一行明细都持有主表主键作为外键 public class PurchaseSubVO { private String pkPurchaseb; // 子表主键 private String pkPurchase; // 引用主表主键外键字段 private String materialCode; // 物料编码 private BigDecimal quantity; // 数量 private BigDecimal price; // 单价 }这段代码的逻辑说明主表 VO 里的 subList 是子表数据在内存中的集合前端卡片把明细行装配到这个 List 里子表 VO 里的 pkPurchase 必须与主表 pkPurchase 值一致否则保存后子表记录找不到自己的单据头。参数说明billState 用 Integer 而不是 String避免和数字类型比较时报类型错quantity 和 price 用 BigDecimal不能用 double 存金额否则精度会在后续计算中出问题。3.2 卡片界面模板主表区、子表区的拖拽顺序元数据完成后进入界面模板设计器。新建卡片模板时系统会自动按主实体字段生成默认布局但这个默认布局基本都要手工调整。我的固定套路是上半部分放主表字段包括单据号、单据类型、制单人、业务日期、备注下半部分放一个表格区域表格区域绑定子实体数据集。子表区域绑定是整个卡片模板的关键。在模板设计器里选中子表表格控件数据源属性要选择子实体的数据集而不是主实体的数据集。这一步做错的表现是卡片打开后明细区域永远空白或者新增明细行时提示找不到数据源。卡片模板还有两个属性我每次都会确认一个是“主子联动”是否开启开启后选中主表记录自动切换对应明细另一个是“子表可编辑”状态如果发布后子表只能看不能改大概率是这个属性处于只读状态检查一下模板属性就能定位。3.3 列表模板与查询模板主表查询与子表过滤列表模板负责在菜单打开时展示单据列表。这里要明确一点列表展示的是主表字段子表明细不直接出现在列表里用户通过单击列表行打开卡片查看明细。所以列表模板上把主表的单据号、制单人、状态、业务日期拖进去就行不要尝试把子表字段拖到列表列上运行时会取不到值。查询模板则是列表页顶部的筛选区域。主子表场景下查询模板经常要增加按子表明细反查主表的条件比如按物料编码查单据物料编码属于子表字段查询时先过滤子实体再关联回主表。如果查询模板没有配置这层关联用户按物料编码查单据时就会发现查不到任何结果这个坑很隐蔽。3.4 模板发布与默认值设置模板设计完成并不代表生效。卡片模板和列表模板都要执行发布并且生成对应的功能节点编码。发布成功后还要在功能节点分配里把节点授权给测试角色否则用测试账号登录时菜单里依然找不到单据。模板上还可以给主表字段配默认值比如制单人默认当前登录用户、业务日期默认当前日期。这些默认值可以在字段属性的默认值窗口里直接写表达式也可以用脚本来算。我倾向于能用表达式就不写脚本表达式简单而且不容易干扰保存动作同时排错时一眼能看到默认值是什么。4. 动作与按钮保存、提交、按钮权限的配置链路到了这一章数据模型和界面都齐了但按钮还是死的。需要使用 UAP 的动作配置机制把界面按钮和业务动作绑定起来并处理保存时的级联逻辑。这里也是“保存成功但子表丢失”的重灾区我详细拆一下。4.1 动作配置新建、保存、提交的分工在界面模板里工具栏的每个按钮都要绑定一个动作。新建动作负责创建一张空的主表 VO保存动作负责把主表 VO 和子表 List 持久化提交动作负责将单据状态从已保存改成已提交并触发后续流程。动作和数据分开配置可以在动作集里统一管理不用在界面模板里逐个写。一个常见误区是把业务逻辑直接写在按钮的客户端事件里。UAP 更推荐的方式是服务端动作客户端按钮只负责触发。写在客户端看着省事但换一个界面模板就要重新配一遍而且不同 Web 端的执行行为可能不一致。我见过一个项目把校验逻辑写在按钮点击事件里结果换了个访问端之后校验完全失效最后花了一下午把逻辑全部迁回服务端。4.2 保存动作的关键参数级联保存与主键回填保存动作里最重要的两个参数是“是否级联保存子表”和“是否回填外键”。级联保存决定框架要不要遍历主表 VO 的 subList 并逐条插入子表外键回填决定子表插入时用哪个字段值填充 pkPurchase。有的单据保存后子表记录能查到但 pkPurchase 为空就是外键回填配置没生效。我还会在保存动作里挂一段后置脚本来处理主键和默认状态的赋值。这是这类脚本的典型写法注意它只处理内存对象不做数据库操作。// 保存动作的后置脚本负责主键、外键和状态补齐 public void beforeSave(PurchaseMainVO main) { if (main.getPkPurchase() null || main.getPkPurchase().isEmpty()) { main.setPkPurchase(uuid()); // 没有编码规则时用UUID兜底 } main.setBillState(1); // 状态置为已保存 ListPurchaseSubVO subs main.getSubList(); if (subs ! null) { for (PurchaseSubVO sub : subs) { sub.setPkPurchase(main.getPkPurchase()); // 外键回填 if (sub.getPkPurchaseb() null || sub.getPkPurchaseb().isEmpty()) { sub.setPkPurchaseb(uuid()); // 子表主键兜底 } } } }这段代码的逻辑说明先保证主表主键非空再遍历子表给每一行补外键最后统一修改状态。参数说明uuid() 是平台提供的 UUID 生成方法没有配置单号规则时用它兜底如果你的环境已经配置了单据号规则主键可以由编码规则自动生成脚本里就不需要再设一次避免单据号被覆盖。这里要提醒脚本里只做内存赋值不要在这里执行 update 或 insert 语句。UAP 保存动作会统一提交事务手工再执行一次插入容易造成同一单据写了两遍或者主外键对不上反而更难查。4.3 按钮权限与功能节点分配动作配好后按钮也不一定马上显示。NC65 的按钮显示受两个条件控制当前登录用户是否拥有按钮对应的权限码界面模板中按钮的可用状态是否满足条件。权限码在功能节点注册时生成需要在角色管理里勾选对应项。我对按钮权限的习惯是列表和卡片分开控制列表页允许普通用户查询卡片页的新增、删除、修改、提交按钮单独授权。这样测试时不会出现角色权限全给完、结果误删数据的尴尬也方便后续按岗位收缩权限范围。4.4 工具栏顺序与常用按钮组合主子表单据的典型按钮顺序是新增、修改、删除、保存、提交、撤销、刷新。其中“保存”和“提交”是两个独立动作提交动作执行后建议把保存按钮置灰防止用户反复提交同一张单据。置灰可以通过按钮的可用状态脚本实现也可以由单据状态字段直接控制。有一类问题我遇到很多按钮存在但点击后没有任何反应。这时打开浏览器开发者工具看请求如果请求根本没发出多半是按钮没有绑定成功动作如果请求发出但报错则要看服务端日志。判断这个边界能省掉一大半的排查时间别一上来就怀疑框架有问题。5. 避坑排查从“保存报错”到“列表空白”的五个实战问题下面这些问题全是实际开发里反复出现的。我按“现象、原因、解决”三个维度写每一条都是一个完整的小案例。5.1 环境与发布相关的问题现象元数据、界面模板、功能节点全部做了但用测试账号登录后菜单里根本找不到新发布的单据有些节点能找到但点击后报接口 404。原因主要是两个方向功能节点虽然注册了但没有执行发布或者角色菜单权限没有同步节点编码与界面模板编码不一致前端加载模板时按节点编码去找对应的列表模板和卡片模板编码对不上就报 404。解决按元数据发布、模板发布、功能节点分配、角色菜单授权这个顺序完整走一遍不要跳步然后核对功能节点注册里的模板编码与模板设计器里的模板编码是否完全一致包括大小写。编码这问题很小写错一个字母前端就会直接空白排查时最容易忽略。5.2 保存与数据相关的两个高频问题问题一保存主表成功子表一条都没存进去。现象是单据头能保存重新打开卡片后明细区为空。原因多半是保存动作里没有开启级联保存子表或者卡片模板里子表区域的数据源没有绑定到子实体数据集。解决方法是先检查保存动作里的级联保存参数再检查卡片模板里子表数据源的绑定情况数据源绑定错误时前端新增明细行根本不会进入主表 VO 的 subList自然也就无从保存。问题二保存时直接报“主键为空”异常。现象是点击保存按钮后服务端日志报主键为空数据库里也看不到记录。原因有两个方向主表主键没有配置生成规则保存时框架拿不到主键或者前端新增时主键字段被模板设置成了不可见导致主键一直没参与提交。解决方法是先给主键配置单号规则或用 UUID 兜底然后去卡片模板确认主键字段是否被隐藏。注意模板隐藏字段导致的问题光看数据库是看不出来的。5.3 列表与按钮链接相关的问题问题一列表能查到数据但按钮显示为灰色不可点。现象是数据都在但修改、删除这类按钮一直是灰色。原因大多是卡片模板里按钮的状态控制脚本返回了 false或者当前单据状态不满足按钮的可用条件。解决方法是先看按钮属性里有没有“可用状态脚本”把脚本条件逐个核对再确认测试账号对应的角色是否勾选了这个按钮的权限码。这两步都做完了按钮基本就能正常显示。问题二列表查不到刚保存的单据。现象是保存成功不刷新时能看到刷新后列表变空。原因通常是查询模板里配置了默认过滤条件比如默认只显示本人制单或未提交的单据而当前测试账号制单人字段为空。解决方法是把查询模板里的过滤条件临时去掉再判断到底是查询条件问题还是数据权限问题。我习惯先看数据库里记录是否存在记录存在但列表看不到就是查询链路的问题记录根本不存在才需要回头查保存链路。这个先后顺序能少走很多弯路。6. 进阶验证单据号规则与主子表联动验收前面五章把主子表单据从向导到发布完整走了一遍。最后一章讲一个我每次都会多做一步的收尾动作配好单据号规则再针对主子联动做一次完整的联调验收。别小看这一步它能暴露很多开发时发现不了的数据一致性问题。单号规则在单据号管理中配置把主表主键的生成方式从 UUID 换成“前缀日期流水号”。例如采购类单据可以配成 CG 202506 四位流水号。这样做的好处是单据号可读业务人员在列表里一眼能判断单据类型打印模板也更友好。配置时注意流水号重置周期一般按日重置或按月重置跨年不清零会导致单据号越来越长打印空间会被撑爆。联调验收我固定跑下面这个流程新建一张主表加两行明细保存后打开列表确认能查到再次打开卡片修改第二行明细的数量保存后再重新打开确认修改没有丢失接着把子表明细删掉一行再保存确认主表数据还在、子表只少一行最后执行提交动作确认状态字段从已保存变成已提交。验收的最后一个动作是跑一段查询 SQL检查主表与子表的外键关联是否完整。这段 SQL 是我每张主子表开发完都会执行的收尾检查。-- 联调验收检查主表与子表的外键完整性正常应该返回0行 SELECT b.pk_purchase, b.bill_no FROM purchase_main b LEFT JOIN purchase_sub s ON s.pk_purchase b.pk_purchase WHERE s.pk_purchase IS NULL;这段 SQL 的逻辑说明以主表为左表按外键去关联子表如果主表记录找不到任何一行子表明细说明这张单据的主子关联已经断裂。参数说明表名替换为你实际的主表和子表表名外键字段名换成你的实际引用字段。正常情况下返回为空一旦有记录保存动作里的外键回填肯定出了问题需要回到第 4 章去排查。从那以后我每次接到主子表单据的需求都强制自己按“环境确认、向导建模、元数据引用、模板绑定、动作配置、发布授权、联调验收”这套顺序完整走一遍而且一定先配好单据号再写业务脚本。许多看起来像框架 bug 的问题最后都出在某个字段没回填、某个模板没绑定、某个权限没分配这种细节上。这套流程不会让开发变得更快但能让你少加一半的夜班希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑