简介:面向 NC Cloud 开发环境中的实施与开发顾问,这份 PDF 系统梳理了 NCC 参照开发的核心脉络,帮助读者掌握如何为档案字段创建列表参照、树表参照和树型参照。内容先从参照概念与主要类型展开,再按“创建参照工具类—前端代码实现—前后端绑定”三步骤拆解,并结合币种、供应商基本分类、客户信息等实际例子说明配置路径与请求访问关系。资源包为 1 个 PDF 文件,压缩后大小约 1.68MB,便于离线查阅。文档还附有前端 JS 代码示例、配置文件及后端工具类继承关系,适合有一定 NC Cloud 开发基础、需要快速上手参照开发的中级顾问参考学习。已有 370 人浏览学习该资源,整体而言,这份资料从原理到实操都做了针对性总结,可作为日常开发与团队内训的速查手册。
1. NCC 参照开发:先把它当成一套远程数据接口来理解
看到“NCC 参照开发”这个主题,大多数人第一反应是“做一个下拉框”。但在 NCC 平台上,参照(Refer)真正的产物不是一个 UI 控件,而是一套前后端协作的数据接口:前端负责弹窗、渲染、多选勾选,后端负责按关键词过滤、分页、返回行集,中间还有一层注册信息把两者绑定起来。很多开发把精力花在样式上,结果注册表里漏掉了缓存和权限参数,上线后被业务方反复投诉数据不准。这篇文章面向 NCC 二次开发工程师和实施顾问,讲清楚从概念、最小实现、参数调整到交付验证的完整路径,读完可以直接照着配置。
2. 做 NCC 参照开发,先分清三种可扩展的参照类型
在动手写代码之前,要先把“参照”这个词拆开。NCC 平台里至少有三种对象都叫参照:基础档案参照、单据参照、自定义参照。它们在前端都是同一个 Refer 控件,但后端数据来源和注册方式完全不同。开发时如果混着来,最常见的错误就是把自定义档案当成基础档案,去查平台内置的档案表,结果字段对不上、权限也拦不住。
2.1 基础档案参照、单据参照与自定义参照的区别
基础档案参照是平台已经实现的,比如客户、供应商、人员、部门。这类参照在标准产品里已经注册好,前端引用时只需要知道它的 refcode。单据参照则是把一张业务单据的头表或子表作为数据源,典型场景是“选择一张未结算的销售订单”,这种参照常要拼接业务状态过滤条件。自定义参照是三者里唯一需要从零搭建的,它面向你自己建的档案表或业务表,注册时指定数据来源查询入口,平台再把它包装成标准参照。
这三者前端都能显示成一样的弹窗,但底层差别很大。基础档案参照改的是数据权限,单据参照改的是查询 SQL,自定义参照则要同时考虑建表、注册、查询、权限四条链路。所以,接需求时先问一句“这个参照的数据源在哪张表”,比问“用什么控件”更接近问题本质。
2.2 开发自定义参照时,元数据模型里实际登记了什么
NCC 自研功能的元数据模型里,参照字段并不保存一整套下拉选项,它保存的是一个“引用入口”。这个入口在注册表里表现为一行配置:参照编码、显示名称、查询入口、缓存开关、多选开关、最大返回行数。理解这一点很重要,因为常规开发思维是先建字段,再写查询;NCC 参照开发是先注册入口,再在界面字段上绑定 refcode。
我一般会按下面这张表核对注册信息,字段名在不同版本里略有出入,但含义是稳定的:
| 配置项 | 典型值 | 作用 | 常见误区 |
|---|---|---|---|
| refcode | custfile | 前端绑定的唯一编码,也是 URL 请求里携带的编码 | 多个环境编码不一致,前端写死 |
| refname | 自定义档案 | 弹窗标题与无障碍标识 | 不填时弹窗显示空串 |
| refurl | /ref/custfile | 后端查询 action 的映射路径 | 路径少一个斜杠,直接 404 |
| iscache | N / Y | 是否缓存首次查询结果 | 开启后数据权限可能失效 |
| ismultiselect | N / Y | 是否允许多选 | 与前端控件的多选属性必须一致 |
| maxrowcount | 1000 | 单次最大返回行数,超了截断 | 设成 0 导致结果集被整体截断 |
这张表对应到开发里,就是把“参照”抽象成了“注册表一行配置加一个查询接口”。后续所有排障,都围绕这六个字段展开。
2.3 一个引用关系幕后有哪些对象在协作
当用户在单据上点击参照字段时,前端 Refer 控件拼装一个查询请求,带上关键词、页码、每页行数、组织主键和前端传入的过滤条件。后端收到后先做 count 查询,再取当前页行集,返回一个包含 totalCount 和 rows 的标准结构。前端拿到 rows 后,按注册配置决定显示哪些列、是否多选。这个模型和普通列表查询几乎一样,差别只在请求参数有约定。
一次典型请求的参数类似下面这样,开发时可以直接在浏览器开发者工具的 Network 面板里核对:
{ "refcode": "custfile", "keyword": "北京", "pageIndex": 1, "pageSize": 50, "orgPk": "1001A1100000000000Z", "filterSql": "status = 1" }参数里 orgPk 是组织权限过滤的关键,filterSql 是业务侧临时追加的条件。后端实现时要优先处理这两个参数,而不是只处理 keyword,否则参照能弹出来,但业务方继续筛数据时结果就对不上了。
2.4 选型:什么时候用配置,什么时候写代码
不是所有参照需求都要开发。数据量在两千行以内、只按编码或名称模糊检索、权限要求不高的场景,直接用平台自带的下拉参照配置就行,把档案表配置成下拉数据的来源即可,速度快,也不占开发工时。但如果数据量上万、需要按组织过滤、还要带业务状态控制,就必须写后端查询 action,注册成自定义参照。
我见过不少项目为了省事,把几百行的档案做成普通下拉框,结果一旦超过两千行,页面渲染和模糊搜索都开始卡。反过来,也有项目动不动就自研参照,把平台标准功能绕过去,维护成本明显偏高。选型的判断标准只有一个:数据规模和过滤规则的复杂程度是否突破了平台默认参照的边界。如果下拉框里需要展示“编码加名称加辅助属性”三列以上,就别用普通下拉了,直接按参照做,后面业务迟早会提这个需求。
3. 在 NCC 开发工程里跑通参照开发的最小链路
理论理顺后,我一般会直接搭一条最小链路:建一张新档案表,写一个查询 action,注册成自定义参照,然后在一张测试单据上把它挂出来。这个链路跑通,就说明工程环境、注册机制、前后端绑定三个环节都是通的,之后往里面加权限、加缓存都不难。
3.1 准备开发工程与热部署入口
NCC 二次开发通常是在标准产品基础上扩展一个插件工程。前端代码放在对应模块的 uap 资源目录,后端代码按模块拆成 jar。开发期可以让前端走热加载,后端代码改动后重启本机的 cloud 服务。很多团队没有把热部署配好,导致改一行 Java 代码要等两分钟,效率极低。
常见做法是先用 Maven 编译当前模块,再把生成的 jar 同步到开发环境的 lib 目录并重启服务:
mvn -pl custfile-module -am clean package -DskipTests cp custfile-module/target/custfile-module.jar $NCC_HOME/modules/custfile/META-INF/lib/ # 之后重启本机 cloud 服务这段命令的作用是把 custfile-module 模块连同依赖一起打包,然后替换到 NCC 的模块目录。实际运行时,前端页面资源也会随 jar 一同加载,所以前端热加载没配好的情况下,改页面同样需要重启。建议先把这两条命令固化成脚本,后面每天要跑很多次。
3.2 在后端写一个返回分页数据的查询动作
参照的后端查询本质上是一个分页接口。下面用一段示意代码展示核心逻辑,类名和父类在不同 NCC 版本里略有差异,但查询参数的名称基本一致。
@RequestMapping("/ref/custfile") @ResponseBody public PageResult query(RefQueryParam param) { PageResult result = new PageResult(); StringBuilder sql = new StringBuilder( "select pk_custfile, code, name, org_pk from bd_custfile where 1=1"); List<Object> args = new ArrayList<>(); // 关键词同时匹配编码、名称,需要时再拼 py 字段做首拼 if (StringUtils.hasText(param.getKeyword())) { sql.append(" and (code like ? or name like ?)"); args.add("%" + param.getKeyword() + "%"); args.add("%" + param.getKeyword() + "%"); } // 组织权限过滤:orgPk 为空时通常走全部数据配置 if (StringUtils.hasText(param.getOrgPk())) { sql.append(" and org_pk = ?"); args.add(param.getOrgPk()); } // 先查总量,再取当前页,避免前端分页出现空白页 int totalCount = queryCount(sql.toString(), args); sql.append(" order by code limit ?, ?"); args.add((param.getPageIndex() - 1) * param.getPageSize()); args.add(param.getPageSize()); result.setTotalCount(totalCount); result.setRows(queryList(sql.toString(), args)); return result; }这段代码有三个点要重点说明。第一,keyword 的处理建议同时匹配 code 和 name,如果档案有拼音码字段,还可以追加首拼匹配,这部分是业务方感知最明显的检索体验。第二,orgPk 过滤要在查询层做,不能依赖前端传回过滤后的结果集,否则数据权限形同虚设。第三,pageIndex 以 1 还是 0 开始,不同版本框架约定不同,联调时用 Network 面板确认,再把统一约定写进团队的开发规范。
3.3 在注册表里登记参照并绑定 Reference 控件
后端接口写好并验证能返回数据后,接下来把它注册成参照。注册动作通常是一行 insert,核心字段就是第 2 章表格里的那六个。
INSERT INTO sm_refinfo (refcode, refname, refurl, iscache, ismultiselect, maxrowcount) VALUES ('custfile', '自定义档案', '/ref/custfile', 'N', 'N', 1000);插入后,前端控件才能通过 refcode 找到这个查询入口。实际项目里,这张表的物理表名和字段名可能带有模块前缀,执行前先用 desc 命令确认表结构。开发阶段 iscache 一律设成 N,等全部功能验证完再评估是否开缓存,否则改代码后经常出现“明明改了却不生效”的假象。
前端控件绑定在页面模板的 items 配置里,把 refcode 挂在对应字段上:
{ "items": [ { "key": "custfile", "label": "自定义档案", "controlType": "refer", "refcode": "custfile", "props": { "isMultiSelect": false, "remoteSearch": true, "pageSize": 50 } } ] }这段配置的作用是告诉前端:字段 custfile 使用参照控件,数据入口的 refcode 是 custfile,远程搜索开启,每页显示 50 行。这里最容易出的问题是 refcode 与注册表里的值不一致,比如环境变量把编码替换成了另一个值,结果前端弹窗报“参照不存在”。排查时先对比这两处编码,能省下不少时间。
3.4 联调时最容易发现的三个问题
第一个是返回字段和前端显示字段对不上。前端配置里指定显示 code、name 两列,但后端 rows 里返回的是 pk、code、name、org_pk,此时前端通常只取前几个字段,表现为列显示错位。遇到这种情况,先看 Network 面板里 response 的 rows 结构,再调整后端返回的字段顺序。
第二个问题是翻页后过滤条件丢失。部分前端组件在翻页时只会把 pageIndex 和 pageSize 传回去,keyword 和 filterSql 是否继续传取决于配置。如果每次翻页后数据变成全量,就到网络请求里对比两次请求的参数,多半是 keyword 没带。
第三个是缓存干扰。开发阶段如果不小心把 iscache 设成了 Y,第一次查询成功后,后续请求都从缓存里取,后端打印的 SQL 根本不会出现。所以开发期统一设 N,测试阶段再单独验证缓存场景。
4. 参照开发必调的 4 个参数与 3 个隐藏坑
最小链路跑通后,进入真正的项目阶段:调整参数、处理权限、排查线上反馈。这一章讲的是从“能弹出来”到“能上线”之间必须过的几个关卡。
4.1 参数表:缓存、多选、返回行数、远程过滤
参照相关参数不少,但项目里真正需要人工调的通常是四个:iscache、ismultiselect、maxrowcount 和远程过滤开关。它们互相影响,下面这张表是项目里默认可抄的配置建议。
| 参数 | 推荐值 | 影响范围 | 备注 |
|---|---|---|---|
| iscache | N | 查询性能与权限一致性 | 开启后权限逻辑要在首次查询时全部生效 |
| ismultiselect | 按业务定 | 前端勾选方式与返回行集 | 必须和前端控件的多选属性一致 |
| maxrowcount | 1000 | 超过后截断返回 | 设 0 等于不限制,但大数据量会卡 |
| remoteSearch | true | 是否走远程过滤 | 关闭后前端在本地过滤,只适合几千行 |
项目里常见的一种误用是依赖前端本地过滤。remoteSearch 设成 false 后,前端首次拉取 maxrowcount 行,用户输入关键词时只在本地筛选。数据量一旦超过一万行,首次加载就慢,而且用户永远搜不到第 1000 行以后的内容。所以只要参照的服务端有条件过滤能力,就保持 remoteSearch 为 true。
4.2 缓存开启后,数据权限为什么失效
这是上线后才容易被发现的坑。开启 iscache 后,后端第一次查询会把结果集缓存在内存里,后续同一个参照的请求都直接命中缓存,不再执行权限过滤 SQL。也就是说,用户 A 第一次查询时带着 orgPk 过滤了数据,但用户 B 用同一个缓存的参照,如果没走 SQL,就会看到 A 的数据范围之外的行。
正确的做法是安全优先:凡是参照涉及数据权限、组织隔离,就别开缓存;如果确实要开,确保缓存的 key 包含组织主键,并在第一次查询时把权限条件固化进 SQL。有些版本里还要把缓存失效机制接进权限变动事件,权限调整后主动清理缓存。这个参数要单独写进运维手册,不要靠口头传。
4.3 前端注册编码不一致,为什么菜单上显示空白
现象是参照弹窗能打开,但列表空白,或者前端控制台报 refer not found。十有八九是 refcode 不一致。常见原因有三个:开发环境手工注册用的是 custfile,但前端配置从测试环境同步成了 custfile_test;或者注册后没有刷新元数据缓存;再或者大小写不一致,平台里编码是区分大小写的。
排查时先看注册表里实际存在的编码,再跟前端请求里的 refcode 做比对:
select refcode, refname, refurl, iscache from sm_refinfo where refcode like '%custfile%';前端请求时用的编码,可以从 Network 面板请求参数里抄出来。比对后修改其中一个,保持统一。研发规范里应该写死一条:注册编码只允许小写字母和数字,杜绝大小写问题。
4.4 字段受控导致参照字段只读
有时候参照本身没问题,但界面上的格子是灰色的,点不进去。这通常是页面模板里字段的状态控制导致的。NCC 页面模板的编辑、浏览、新增三种状态下,字段的可编辑、必填、只读属性是独立配置的,而且可以叠加。开发时只在 edit 状态下把字段设为可编辑,浏览态下就会显示为只读。
检查方法:在页面模板设计器里打开字段属性,把新增状态和编辑状态下的可编辑开关都打开,再确认参照控件的 editable 属性没有设置成 false。另外还要看动态状态控制脚本里有没有对该字段做赋值,这属于更隐蔽的覆盖场景,用浏览器检查 DOM 上字段的 disabled 状态能快速定位。
4.5 排查用的三条 SQL 和日志命令
下面三条命令覆盖最常遇到的注册、请求、缓存三类问题,可以写进团队的排障手册。
# 1. 查注册:在数据库客户端里执行,确认 refcode 和 refurl 是否和代码一致 select refcode, refname, refurl, iscache from sm_refinfo where refname like '%自定义%'; # 2. 看请求:实时跟踪后端日志里的 SQL 和报错 tail -f $NCC_HOME/logs/cloud/run.log | grep -E 'custfile|ref/custfile' # 3. 清缓存:调用平台缓存清理接口,cacheKey 换成目标 refcode curl -X POST http://localhost:8080/nccloud/cache/clearCache -d 'cacheKey=custfile'第三条的接口路径在版本之间差异较大,实际使用时先看平台缓存管理文档,并按实际端口修改。排查时按顺序执行这三条,能覆盖九成以上的线上问题。
5. 参照开发交付前,我会保留的一套最小验证清单
功能做完只算完成一半。参照类功能最怕的是“开发环境好好的,测试环境数据一多就崩”。所以每次交付前,我都按下面这张清单过一遍,半小时能跑完,能挡掉绝大部分线上问题。
| 验证项 | 操作 | 预期 | 失败时看哪里 |
|---|---|---|---|
| 关键词检索 | 输入编码片段、名称片段、首拼 | 三种方式都能命中 | 后端 SQL 里 keyword 拼接 |
| 分页与回填 | 翻到第 3 页再选一条 | 回填值正确,过滤条件不丢 | 请求参数 pageIndex、keyword |
| 多选边界 | 开启 ismultiselect 后选 200 条 | 能回填全部选中项 | maxrowcount、前端多选属性 |
| 数据权限 | 用两个不同组织账号查询 | 结果集按组织隔离 | orgPk 是否进入后端 SQL |
| 缓存切换 | iscache 从 N 切 Y | 首次查询后不再打印 SQL | 缓存 key 是否含组织 |
| 状态控制 | 新增、编辑、浏览三个态 | 只在指定状态可编辑 | 页面模板字段状态配置 |
清单里最容易被忽略的是最后两行。缓存切换这一项,如果测试时没验证,上线后出现权限越权,责任就大了。状态控制这一项,则是提测时最容易被 UI 测试漏掉的,因为他们通常只在编辑界面点一下,不会三种状态来回切。
验证出现问题时,先回到第 4 章的三条命令,用 SQL 查注册、用日志看请求、用缓存清理接口复位,再重新跑一条用例。反复出现同类问题时,把证据截图连同 refcode 一起贴到工单里,沟通成本会明显降低。
最后,把这一套验证清单连同三条排查命令,直接附到 NCC 参照开发的交付文档里,后续维护的人遇到问题不会再来打扰你。
本文还有配套的精品资源,点击获取