
简介面向用友UAP NC65开发者的主子表单据开发指南聚焦向导式创建主子表单据的完整流程适合刚接触UAP平台、需要快速上手企业级表单开发的初学者。内容从主表创建、子表配置到主子关联与交互逻辑均有详细说明并涉及数据库设计、数据绑定、事件处理等关键知识点。资源包共126个文件包含37个Java源码及其编译后的class文件、16个SQL脚本、6个properties配置、5个XML配置以及bmf/bpf等UAP建模文件压缩包约4.56MB代码与配置可直接对照学习。已有1664人浏览学习可作为实战参考。通过阅读源码与建模文件可理解主子表单据的字段映射、外键关联、刷新规则与增删改查实现同时获得分页、排序等优化思路为后续复杂业务开发打下基础。 用友NC65的二开里主子表单据是出现频率最高、也最容易让人半路卡壳的一种需求。我第一次用UAP的向导做这种单据时以为点完之后会像其他低代码平台一样直接出一个能增删改查的页面结果向导只生成了一个模板框架后面功能注册、元数据建模、模板分配、节点挂接、权限配置这些环节一个都不能少任何一个断了页面要么出不来要么保存就报错。这篇文章就按我实际操作过的一条完整链路来写从UAP里做功能注册和主子表元数据建模开始到向导初始化主子单据模板、配置模板和分配模板再到NC65里挂接功能节点、配置权限和单据类型最后把平时开发中踩过的高频问题整理成排查思路。想快速上手用友UAP开发NC65主子表单据的同行按这个顺序走基本能避开大多数新手坑。1. 向导的真正作用先弄懂UAP的“元数据-模板-节点”三条线1.1 向导生成的是什么UAP的向导不会像Visual Studio那样一键生成一套完整可用的业务系统。它生成的是一套以元数据为骨架、以模板为皮肤的中间产物。元数据负责定义主表和子表的字段、类型、长度、主子关系数据库表结构就是由元数据同步生成的模板则决定这些字段在操作界面上如何排列、哪些字段可编辑、哪些字段隐藏、哪些字段必填。最后还要有一个功能节点把元数据和模板串起来再通过权限挂到菜单上整个单据才算真正可用。很多初学者把“向导初始化模板”当成终点实际上它只是整套开发流程里的“打底”环节。拿装修来类比元数据是房子的墙体结构决定房子有几间房、门开在哪模板是水电、油漆和家具摆放决定住得舒不舒服功能节点和权限则是门牌号和钥匙决定谁能进这栋房子。这三条线少任何一条你的“房子”都交付不了。1.2 什么情况下用向导什么情况下别硬用向导适合主表子表结构清晰、字段数量在二三十个以内的标准业务单据比如采购申请单、销售订单、报销单、简单入库单这类场景。因为向导生成的代码和模板结构是标准化的项目后期接手的人很容易看懂维护成本低。但如果你的单子有很多特殊交互比如表体行要按批次动态增删分组、表体字段之间要做复杂的联查回写、单据流程要和审批流强绑定、或者需要对接外部接口做大量自定义逻辑那我的建议是把向导结果当起点在生成的Action上做扩展不要试图全靠向导模板来解决一切。向导解决的是“基础框架”不是“全部业务”。1.3 开工前的准备清单照着下面这张表逐项确认能省掉一半的“环境问题”排查时间检查项具体要求说明开发环境中间件已启动UAP Studio能连上开发库连不上库时后面所有操作都无法保存数据库账号具备建表权限元数据生成脚本需要执行到业务库模块编码明确单据归属模块如po、so、hr等功能注册时用随意编编码后患无穷权限账号当前账号有UAP开发权限和功能节点管理权限没有开发权限进不了UAP的建模界面环境隔离优先在独立开发库操作不要动正式库元数据变更会影响表结构正式库出问题很难回滚这些条件里最容易被忽略的是第三项。很多人随手写个模块编码做到后面发现单据挂到了错误的菜单树下又得重新注册一遍功能节点白白浪费半天。2. 主表和子表建模这一步定调后面全是执行2.1 主表实体建模打开UAP工程在导航树上找到目标模块右键选择新建元数据实体类型选择单据类不同NC版本可能叫“自定义单据”或“虚单据”取同一个意思即可。主表实体承载的是“单头”数据一般要包含单据号、单据日期、组织、集团、备注这些字段再加上一套NC标准系统字段。我以一个采购申请类的例子来说明主表字段可以这样定义字段名字段类型长度说明billno字符串40单据号billdate字符串10单据日期pk_org字符串20业务组织pk_group字符串20集团remark字符串255备注pk_head字符串20主键由系统自动生成dr整型-删除标记默认0ts字符串19时间戳关键点是主表主键必须用pk_head这是NC65主子表机制的统一约定。后面子表外键、模板关联、保存Action全部依赖这个字段换掉它后面会出很多莫名其妙的问题。2.2 子表实体和主子关系子表实体承载的是“表体行”数据主键用pk_detail外键用pk_head再定义行号、物料、数量、金额、备注等业务字段。比如字段名字段类型长度说明pk_detail字符串20子表主键pk_head字符串20外键关联主表rowno整型-行号material字符串40物料编码num浮点16,4数量price浮点16,4单价money浮点16,2金额memo字符串255行备注定义完这两个实体后必须在元数据的关系视图里建立主子关联主表与子表通过pk_head一对一关联类型选“主表-子表”或“一对多”。这一步不做UAP向导在初始化模板时就识别不出这是张主子表单据后面所有流程都走不下去。我见过不止一个同事跳过这一步结果模板初始化界面里只能看到主表看不到子表卡在那里不知道怎么回事。2.3 生成数据库脚本并执行主表和子表实体都保存后执行UAP里的“生成数据库脚本”功能会生成对应的建表脚本脚本里会自动带上dr默认值、ts字段这些约定。手动建的库表可以参考下面这种结构create table t_po_demo_head ( pk_head char(20) primary key, billno varchar(40), billdate char(10), pk_org char(20), pk_group char(20), remark varchar(255), creator varchar(20), creationtime char(19), modifier varchar(20), modifiedtime char(19), ts char(19), dr int default 0 ); create table t_po_demo_item ( pk_detail char(20) primary key, pk_head char(20), rowno int, material varchar(40), num decimal(16,4), price decimal(16,4), money decimal(16,2), memo varchar(255), ts char(19), dr int default 0 ); create index idx_demo_item_head on t_po_demo_item(pk_head);执行完脚本后去数据库里确认两张表都创建成功并且子表能通过pk_head关联到主表再继续下一步。建模阶段出的问题越早暴露越省事因为元数据一改生成脚本要重刷后面初始化的模板可能全部作废。3. 走通UAP向导功能注册、模板初始化、模板分配3.1 功能注册元数据到位后在UAP的功能注册界面新增一个功能节点。节点编码要全局唯一命名建议带上模块前缀比如PO_DEMO_APPLY节点名称要写业务含义比如“采购申请Demo”所属模块选你第一步确认好的模块编码功能类型选“单据”。有一个细节容易被忽略功能注册时的节点编码和模板编码不一定相同但为了排查问题方便我习惯让它们同前缀。例如功能节点编码为PO_DEMO_APPLY模板编码就叫PO_DEMO_APPLY_T后缀T代表template一眼能看出对应关系。这算是个小经验不强制但能让你少查好几次数据库。3.2 初始化主子模板在UAP里找到“单据模板初始化”功能选择刚才注册的功能节点点击初始化模板后向导会让你选主表和子表。这时前面建的实体就会出现在候选列表里选中后向导会自动读取主表主键pk_head和子表外键pk_head的关联关系。在字段配置页面里左侧是可用字段树中间控制字段属性右侧调整显示顺序。这一步要做的不是把字段全拖进去而是按业务需要勾选主表单据号、单据日期、组织、集团等常规头字段全部显示pk_head和ts这些系统字段隐藏。子表物料、数量、价格、金额显示pk_detail、pk_head隐藏。必填项设置单据号、日期、组织必填子表物料、数量必填。字段排列的时候把主表字段放上面子表字段放下面前端展示时就是主附表布局。保存模板后会生成一个模板编码这个编码记下来模板分配时要用。3.3 模板分配不能跳过初始化完模板并不代表节点打开就有界面还差一步“模板分配”。UAP的模板机制里同一张模板可以在不同模块、不同角色下复用所以必须显式地把模板分配给目标功能和角色发布后在界面上才能渲染出来。这一步不做最典型的症状就是节点挂上去了点开却是空白页或者提示找不到模板。模板分配入口在UAP的“模板分配”功能里选择模块和功能节点再选择刚保存的模板编码进行绑定。分配完成后建议提前预览一下模板效果确认主表和子表字段都正常显示再进入NC65的发布环节。4. NC65里做挂接、授权、绑单据类型发布后的“最后一公里”4.1 功能节点挂到菜单并授权用管理员账号登录NC65客户端在“系统管理-功能节点权限配置”里找到刚才注册的功能节点把它分配给你的测试角色然后在“客户化-权限管理-角色管理”里给该角色授权这个节点。菜单树上不显示节点时多半是这里没分配。权限这块要分两个层面看一个层面是功能权限决定角色能不能看到这个节点和按钮另一个层面是数据权限决定用户能操作哪些组织、哪些部门的数据。主子表单据尤其是涉及金额的单据数据权限不配好经常出现“单据在A组织保存了B组织的人看不到”的诡异情况。我建议在小范围测试时先用超级管理员把功能权限和数据权限都放开跑通全流程后再按实际业务收敛。否则开发和权限配置交叉排查很难定位问题。4.2 在单据类型管理里绑定模板很多人在这一步翻车。如果只在UAP里做了功能注册和模板初始化直接打开单据节点你会发现单据号不生成、保存后退不回列表、部分业务动作没反应。原因是NC65里一张可使用的主子表单据必须先在“客户化-基础档案-单据类型管理”中建立单据类型并把模板编码绑定上去。操作流程是新建单据类型填写类型编码和类型名称选择对应的功能节点在模板设置里关联第3步生成的模板编码。然后配置单据号规则比如按日期流水号生成或者按组织流水号生成。单据类型配置完成后增删改查、编号生成、模板加载这些基础能力才真正生效。4.3 按钮和标准Action不需要重复开发向导生成的模板默认已经把新增、保存、修改、删除、提交、审核这些按钮和后台标准Action绑定了。标准Action内部会处理主表插入、子表批量插入、主子表级联删除这些逻辑日常开发根本不需要手写SQL。你只需要在模板展示层控制哪些按钮显示、哪些按钮隐藏即可。只有在需要额外业务逻辑时比如保存前检查库存、保存后回调第三方接口才需要继承对应的Action类并覆盖相关方法。UAP生成的Java工程会保留Action扩展点在代码管理平台里找到对应模块的源码找到YearEndAction之类的类按业务改即可。注意模板一旦发布UAP里默认会把它锁定为发布状态继续用开发工具直接修改会提示模板被锁定。要调整模板布局得在模板管理里先“回收”或走模板升级流程不要直接去数据库改模板表那样做容易把主子表字段关联改坏导致保存时报错。5. 高频问题排查从“保存报null”到子表空白5.1 保存报“null”、保存失败:null这类报错是NC65开发里被问得最多的问题后台日志里通常能看到一行NullPointerException。出现这种情况时我要么查主子关联关系要么查模板列配置步骤基本是固定的第一步去中间件日志目录下找最新的异常日志定位到具体是哪个类哪一行报空。第二步如果日志指向VO或元数据相关的对象回到UAP元数据里检查主表和子表是否建立了pk_head关联。第三步如果关联有的是好的检查向导生成的模板里pk_head字段在子表列中是不是被误删了或者被设置了隐藏但未赋值。模板上主表主键、子表外键这两个字段可以隐藏但不能不展示给保存逻辑。我在处理过的一个项目里遇到过这种情况子表的pk_head字段在模板中被误设置成“只读且无值”保存时子表数据带过来的外键为空主表插进去了子表全部失败最后报的就是类似“保存失败:null”的错误。把pk_head的值来源切换成“由主表主键带入”后问题立即解决。5.2 子表数据不显示节点能打开、主表能录入但填完主表数据子表区域一片空白。我排查这个问题的习惯是先看模板分配里的字段列表确认子表字段已经分配出来并且没有被标记为隐藏。其次看元数据中的子表实体是否被正确标记为“明细表”或“子表”类型而不是普通实体。再有一种情况是前端把子表区域渲染出来了但子表数据源没绑定。这时去检查模板中“主附表关系”配置UAP模板编辑里有一个主子表关联区域需要明确指向子表实体并对应pk_head字段。5.3 单据编号不自动生成这种情况绝大多数出在单据类型管理。要么是没有新建单据类型要么是新建了类型但没有配置单据号规则。NC65的单据编号由号规则引擎在保存时生成如果号规则为空系统不知道按什么规则给单号所以表现为单号空白或者保存报编码规则相关错误。正确的配置是在单据类型里设置号规则并确认规则的流水号前缀、日期格式和步长符合要求。不要想着在页面事件里手动写代码生成单号标准机制已经做得很好自己造轮子反而容易和保存流程冲突。5.4 模板被发布锁定后改不动UAP向导生成的模板在发布前可以随意编辑发布后模板状态变成“发布”再直接编辑会被拦截或提示需要回收。大多数情况下我建议在开发阶段不要急着点发布先用未发布状态跑测确认稳定后再统一发布。真被锁了通过系统管理的“模板升级调整”或“模板回收”功能把它恢复到开发状态调整完再重新发布一次即可。5.5 元数据加字段后模板没有出现新列改元数据加了字段并重刷脚本回到模板里发现字段列表还是旧的。模板不会自动同步元数据的最新字段这是UAP的机制设计。遇到这种情况要么手工在模板字段配置里把新增字段加进去要么重新初始化模板。重新初始化之前要考虑清楚因为新建模板会覆盖掉手动调整过的布局所以“先定元数据、再审模板”是更安全的节奏。下面用一张表汇总这几个高频问题的排查方向现象根因方向处理办法保存报null主子关联未建立或pk_head未赋值建关系、修正模板字段来源子表空白字段未分配或实体类型错误检查模板列、检查元数据实体类型单号不生成单据类型或号规则缺失在单据类型管理里绑定并配置模板改不动模板已发布锁定回收或升级调整后再改新字段不出现模板未同步手工加列或重新初始化模板6. 几个让我少加班的操作习惯6.1 先列字段清单再进UAP建模我后来养成的习惯是任何主子表单据开发先找业务把字段清单整理成一张表包括字段名、显示名、类型、长度、是否必填、是否显示、是否参与计算。这张表确认完再进UAP做元数据建模。字段问题在纸上改成本最低在元数据里反复改不仅脚本要重刷模板也要重配时间全耗在重复劳动上。6.2 每走完一个环节立刻验证我的验证节奏是功能注册完登录管理端看节点是否存在元数据建模完登录数据库查表结构模板初始化完预览模板看主子表布局是否正常模板分配完用测试账号实际打开单据新增一条数据并保存再修改、再删除。每步验证的耗时不会超过十分钟但能保证问题在最早期暴露而不是攒到最后集中爆发。6.3 报错先看日志别急着重建模板遇到问题先打开中间件日志目录下的日志文件找到异常堆栈再去分析是元数据问题、模板问题还是权限问题。我见过太多人一报错就重刷模板、重跑脚本结果问题没解决反倒把原有正确配置覆盖了。日志信息虽然看起来乱但真正定位后大多是大白话式的错误比如“XX字段为空”“找不到模板编码XX”按图索骥就行。这条主子表单据的开发链路走通之后你会发现后续再做类似的单子基本就是重复“建模-初始化-分配-授权”这几步真正需要思考的反而是业务规则放到哪个Action里实现。先把基础链路稳定下来剩下的就是往里面填业务逻辑了。本文还有配套的精品资源点击获取