ARTICLE DETAIL

资讯详情

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

用友U9二次开发实战指南:元数据与插件驱动的拓展之路

用友U9二次开发实战指南:元数据与插件驱动的拓展之路 简介《U9二次开发技术资料文档说明》是一份面向U9/U9C实施与开发人员的系统化技术合集覆盖档案、单据、BE插件、UI插件、接口调用、报表打印等二次开发核心模块助读者从数据建模到系统集成建立完整思路。压缩包共74个文件约21.8MB以docx开发文档、cs源码、pdf说明手册为主辅以sln工程、xsd结构定义、sql脚本、xml配置等基本包含开发、调试、部署各环节常用材料。已有1393人学习资料内既有工作手册与培训课程表也有第三方调用U9服务等集成案例参照性强。对想快速上手U9C二次开发的技术人员而言这份资料能有效缩短项目探索周期补齐从档案设计到打印报表全流程的实战缺口。 “U9二次开发”这个关键词在ERP实施圈子里几乎天天被刷到。用友U9以及后来云化形态的U9C定位在多组织、多工厂的离散制造和项目制造场景标准功能再完善落到具体企业里总会出现配不出来的个性化需求报表格式要改、审批规则要调、上游系统要对接、甚至业务流程要整体重构。这些光靠实施顾问做配置是不够的必须通过二次开发来补位。这篇东西我从实际项目里的经验出发把U9二次开发的整体框架、核心操作、一个可复制的案例、以及那些文档里不会写的坑一次性捋清楚适合准备接U9项目的开发同事、刚入行的实施顾问、以及甲方内部负责ERP运维的信息化人员参考。1. 先把U9二次开发的整体脉络捋清楚1.1 U9二次开发到底在“开”什么很多人一听“二次开发”第一反应是“写代码”这个理解没错但在U9体系里写代码只是其中一个环节。U9的二次开发对象大致可以分成六类单据、列表、报表、插件、接口、工作流。单据层面解决的是“标准界面不够用”列表层面解决的是“查询和列表列不够直观”报表层面解决的是“企业要的打印和统计格式标准产品给不了”插件层面解决的是“某个业务动作发生的前后要挂自定义逻辑”接口层面解决的是“U9要和MES、WMS、PLM、OA等外部系统做数据交换”工作流则解决的是“审批路径和标准配置对不上”的情况。这套分类不是平行的而是围绕一套核心机制运转的元数据驱动。U9把业务对象的结构、界面、行为全部描述成元数据数据库中存的是元数据界面上渲染的也是元数据你做的二次开发大部分动作都是在“改元数据”或者“扩展元数据”。这点和很多CAD类软件的二次开发思路很不一样——像NX二次开发、SolidWorks二次开发本质上是在一个三维几何引擎之上做领域适配重点在图形算法和参数化建模而U9二次开发面对的是整条业务链上的数据流和状态流重点在业务语义、事务一致性、权限、审批这些企业级约束。所以你会发现U9二次开发更像是在一个“业务操作系统”上做应用扩展而不是在画图工具里加功能。1.2 开发平台与工具链UBF是绕不开的入口U9二次开发的核心工具是UBFUAP Business Framework它不是一个独立安装的IDE而是集成在Visual Studio里的一套插件式开发平台。打开VS后能看到UBF的菜单和工程模板新建工程时可以选择实体、单据模板、列表模板、报表、服务等不同类型。技术栈方面后端是C#和.NET数据库是SQL ServerU9的系统库和业务库在同一套实例里统一管理。我遇到过不少半路出家的开发第一反应是“我能不能直接写SQL改表、加存储过程”我的建议是先忍一忍。U9的数据库表结构和元数据之间是强绑定关系你手动在SQL Server里加一张表或者改一个字段UBF这边完全感知不到后续做元数据发布时极容易报错。正确的姿势是一切的起点都从UBF里的元数据设计开始让UBF来维护数据库表结构而不是反过来。这套约束是U9二次开发区别于“写脚本”类开发的最核心差异理解了它后面很多问题都能归位。提示U9二次开发的核心链路是“元数据 → 实体 → 表单 → 插件 → 发布”。把这条链路刻在脑子里后面遇到的绝大多数问题都能顺着它排查。2. U9二次开发的核心细节与实操要点2.1 单据开发从元数据到界面的一条线单据开发是U9二次开发里最基础也最高频的工作。一张完整单据涉及三个层次元数据定义、实体模型、表单模板。元数据定义描述这个单据有哪些字段、哪些子表、字段类型和长度实体模型是对数据库表的对象化映射主表一个实体子表一个实体两者通过外键关联表单模板则是用户实际在界面上看到的布局包括页签、字段位置、按钮事件。实际开发中如果你要给标准单据加一个字段流程是这样的在UBF的元数据管理里找到对应的标准单据实体右键扩展新增字段设置字段名、显示名、类型然后发布元数据UBF会自动在数据库表里增加物理字段。这里有一个特别容易犯的错字段命名。U9有自己的命名约定建议扩展字段统一加一个前缀比如公司简称“HX_”这样既能在数据库里一眼认出是扩展字段也能避免将来和标准字段或者别的二开字段撞名。我见过两个实施团队给同一张表加自定义字段都没有前缀结果一个叫“Remark”一个叫“Remark1”后续维护起来非常痛苦。更重要的是理解主表和子表的关系。以销售订单为例主表存单据头信息单据号、客户、订单日期、状态子表存行信息物料、数量、价格、交期。做二次开发时只要涉及金额、数量的汇总逻辑几乎都要同时操作主表和子表。很多新手只改了主表字段就做保存结果子表没同步数据对不上。记住一条经验在主表保存逻辑里如果要校验子表明细不要自己去查数据库而是通过实体的导航属性直接拿子表集合这样既安全又能跟上实物体的内存状态。2.2 列表模板与报表改造的两种路线列表模板是用户的“看板”标准列表往往显示不了所有必要字段或者查询条件不满足实际需要。改造列表模板在UBF里是可视化操作拖拽字段、调整列宽、增加查询区。但有一个隐藏点——列表的查询性能。U9的列表通常是通过SQL查询出来的如果你在列表模板里加了一列关联基础资料的字段标准查询会做表关联数据量大时明显变慢。我实测下来的经验是列表改造尽量只展示当前实体主表字段或者直接冗余存储的字段少去做跨实体动态关联确实需要关联的优先考虑在保存时把关联结果冗余到一张扩展表里。报表开发有两条路线。一条是用UBF报表向导开发适合格式要求不太复杂、数据源明确挂在某个单据或查询实体上的场景优点是和U9的权限体系、打印输出无缝衔接另一条是直接用SQL做数据源、在第三方报表工具里出报表适合复杂的统计报表比如跨单据汇总、多维透视这种。两条路线不冲突我实际项目里的经验是对外正式单据对账单、送货单优先用UBF报表保证格式和权限统一对内管理分析报表尤其是口径经常变的直接用SQL加报表工具迭代效率更高。2.3 插件把业务逻辑挂到事件点上插件是U9二次开发里最核心的扩展机制它让你能在不修改标准代码的前提下在业务动作发生的前后挂上自己的逻辑。U9的业务对象在生命周期里有大量事件点比如单据创建后、保存前、保存后、删除前、审批通过后等。插件类需要实现IEventSubscriber接口在Notify方法里做事件分发然后通过UBF的元数据配置把插件注册到对应实体的事件点上。写插件最重要的一件事搞清楚事件触发的顺序和重复触发的可能性。我遇到过的情况是一个单据做了两次“保存并提交”AfterSave事件被触发两次结果业务数据被重复生成。正确的写法是先在事件逻辑里做幂等判断比如按来源单号、来源行ID检查是否已经生成过下游单据没有才往下走。另外插件的异常处理一定要严谨插件里的异常会直接影响主流程的保存或者审批结果所以对外部系统的调用、对可能出现空值的字段访问都要做好保护和重试设计。调试时U9的插件能通过VS附加到客户端进程的方式打断点但服务器端部署后更稳的做法是输出日志文件把关键入参、判断结果写到特定目录方便现场排查。3. 实操复盘销售订单保存后自动生成一张扩展单据3.1 需求场景与设计思路拿一个我在项目里做过的真实需求来完整走一遍。客户的销售订单在审核通过后业务员必须手工录入一张“订单附加信息单”记录一些标准销售订单里没有的信息比如客户指定的运输注意事项、验收方式、实际对接的联系人。这个动作靠手工做天天有人漏。排查方案定了两条路一是给标准销售订单界面加字段直接扩展二是做一个独立的自定义单据然后用插件在销售订单保存后自动生成。最终选了第二种理由是客户要求这些附加信息在录入后只能由特定部门维护业务流程上属于独立的审核对象独立单据更容易控权也不影响标准销售订单的升级兼容性。设计上分三个部分自定义单据实体主表一个子表、事件订阅插件、相关权限和编码规则配置。自定义单据的主表字段包括单据编号、来源单据号、来源单据类型、附加说明子表存维护记录记录修改人、修改时间、备注内容。这里用主表加子表的结构不是为了炫技而是因为客户要求保留每次变更的痕迹这是一对多关系标准单据设计里也是这个套路。3.2 插件代码怎么落地插件类核心逻辑不复杂在销售订单的AfterSave事件里判断单据状态调用实体创建逻辑。下面是我实际写的代码骨架去掉了具体公司相关的命名空间保留了完整的逻辑结构。using UFSoft.UBF.Business; using UFSoft.UBF.Eventing; namespace U9DevDemo.Plugins { public class SaleOrderAfterSaveExt : IEventSubscriber { public void Notify(object sender, EventArgs e) { if (!(e is AfterSaveEventArgs afterSave)) { return; } // 注意实际项目中要根据发布版本确认BusinessEntity的实际类型 // 以及单据状态的枚举值这里以常见的销售订单实体做演示。 var order afterSave.BusinessEntity as SaleOrder; if (order null || order.Status ! 2) // 2表示已审核以实际配置为准 { return; } CreateExtOrder(order); } private void CreateExtOrder(SaleOrder order) { // 幂等检查来源单号已生成则直接返回防止重复触发 if (CheckExistsBySource(order.ID)) { return; } using (ISession session Session.Open()) { ExtOrderDoc doc ExtOrderDoc.Create(); doc.Code EXT DateTime.Now.ToString(yyyyMMddHHmmss); doc.Description 由销售订单自动生成; doc.SourceDocID order.ID; doc.SourceDocNo order.Code; // 做字段映射时优先用实体属性而不是直接拼SQL // doc.CustomerName order.Customer.Name; ExtOrderDocLine line ExtOrderDocLine.Create(); line.Remark 初始记录订单审核通过后自动生成; line.CreatedTime DateTime.Now; doc.Lines.Add(line); doc.Save(); session.Commit(); } } } }代码里有几个地方值得单独说明。首先是幂等检查我在生成扩展单据前会先按来源单据ID查一下是否已存在记录如果存在就直接跳过。这是防止重复生成的关键一步没有这个判断一次事件被触发多次就会产生垃圾数据。其次是字段映射能走实体属性的就尽量走实体属性不要直接写SQL去查一是慢二是脱离事务上下文容易读到脏数据。第三是使用Session管理事务整个操作在一个事务里提交避免出现主单据保存成功但附加单据生成失败的半截状态。3.3 发布、部署和验证的完整流程代码写完后发布流程比普通.NET项目要复杂一点。第一步是在UBF里发布元数据把新建立的扩展单据实体和插件注册信息同步到服务器元数据库。第二步是编译插件工程把生成的DLL文件拷贝到U9应用服务器对应目录并确认插件注册信息指向正确。第三步是处理客户端缓存U9登录时会加载元数据缓存如果界面还是看不到新单据通常需要清一下客户端缓存目录这个动作在新功能发布后几乎必做。第四步是配置编码规则和权限新单据没有编码规则时保存会直接报错没有授权时用户根本看不到菜单。验证阶段我的习惯是先建一张测试销售订单确认生成逻辑正常然后重点测试两个异常场景一是保存一张未审核的销售订单确认不会误触发二是手动把生成的扩展单据删除后再次审核销售订单确认插件能基于幂等判断正确重建或跳过。每次验证后看一眼数据库里的生成记录和时间确认没有重复数据。实测下来这套流程如果顺利一次发布从开发到验证大约半天能完成但第一次做的人往往会在权限和缓存环节多耗掉大半天。4. 常见问题与排查技巧实录4.1 元数据和数据库对不上的连锁反应U9二次开发里最隐蔽、破坏力最大的问题是元数据和数据库结构不一致。典型症状是某次发布元数据过程中报错数据库里字段已经建了但元数据发布没完成后续再发布时系统认为字段已存在跳过建表但元数据却仍然缺失结果运行时报表或列表找不到字段。这个情况的排查思路是先定位是“库表有、元数据无”还是“元数据有、库表无”。前者在UBF的元数据管理里重新做一次完整发布即可后者往往需要删掉对应字段/实体后重新发布操作前一定先备份。另一个常见的场景是多人同时开发导致的“撞车”。同一个实体A开发在本地加了一个字段并发布B开发拿到旧版本元数据发布时把A的字段覆盖掉了。我建议团队协作时所有涉及共享元数据的发布尽量通过一个统一的开发服务器进行本地只做代码开发不要反复做全量元数据发布能规避大量此类问题。4.2 改了界面却不生效缓存和发布顺序“我改完表单模板发布后客户端打开还是老样子”这是我反复被问到的问题。九成以上是客户端缓存没有更新。U9客户端登录时会把服务器端元数据缓存到本地发布新版本后旧缓存不清理看到的自然是旧界面。处理方式通常是删除客户端缓存目录后重新登录或者通过U9自带的缓存更新工具手动刷新。注意在这个动作做完之前先确认服务器端的元数据确实已经发布成功否则清多少次缓存都没用。发布顺序也要讲究。标准做法是先发布元数据再发布代码程序集最后验证客户端。如果先往服务器拷贝了新的DLL但元数据还没发布运行时容易出现“类型不匹配”的奇怪错误反过来先发布元数据再拷贝DLL一般不会出这种问题。我自己的习惯是严格按“元数据 → 程序集 → 缓存刷新”三步走每一步做完都做一次最小验证。4.3 权限、审批流、编码规则三个隐形门槛很多二次开发功能在开发环境测试一切正常一上生产就用不了大概率卡在这三个点。权限问题最直观新单据、新菜单发布后没有授权到任何角色用户看不到入口。排查方法是找管理员角色先授权确认功能正常后再给对应业务角色分配。审批流问题隐蔽一点自定义单据绑定了审批流但审批流没有在指定组织生效或者没有绑定到对应单据类型提交审批时提示找不到审批路径。这个需要在审批流配置里仔细检查组织范围和单据类型范围。编码规则是最容易被忽略的。新建单据在保存时如果系统里没有配置对应编码规则会直接报“无法获取流水号”。解决方案倒不复杂在基础设置里给新单据配置好编码规则再把适用组织范围覆盖完整。我见过项目里新单据开发完调试了很久都过不去保存这一步最后发现就是编码规则没配属于“问题本身不难但是流程上容易忘记”。4.4 常见问题速查表现象可能原因排查与解决发布元数据报错库表和元数据不一致先确认字段是否存在完整重发一次元数据新单据在客户端看不见客户端缓存未刷新清缓存目录或用缓存更新工具刷新保存单据提示取不到流水号编码规则未配置在基础设置里配置编码规则覆盖使用组织插件逻辑执行了两次事件重复触发增加幂等判断按来源单号查重插件报错但主单据保存成功插件异常时机检查AfterSave中的异常处理必要时加日志列表查询越来越慢跨实体关联过多减少列表关联字段考虑冗余存储新功能用户看不到菜单权限未授权用高级管理员角色授权后再给业务角色分配把这些坑提前记下来能省下大量在群里到处问人的时间。我个人在U9二次开发上最深的体会是这个领域最难的从来不是写代码而是理解业务边界和标准产品的设计意图。插件、元数据、报表本身都有明确的技术路径照着做就能通但“什么时候改标准流程”、“什么时候该用自定义单据”、“什么时候必须让实施顾问介入调整业务流程”这些判断才真正决定了二开的成败。如果你准备长期做这块建议给自己定一个规矩每接一个新需求先在UBF里熟悉标准单据的字段和事件链再动手写第一行代码。这样踩坑的概率会小很多。本文还有配套的精品资源点击获取
返回列表