简介面向用友U8 ERP二次开发者的官方技术文档由用友软件商平锋撰写系统讲解以Visual Basic调用U8API完成采购、销售、库存等模块的客户化开发。内容涵盖U8API资源管理器、U8APIBroker运行时框架、环境上下文与API调用六步流程并配有VB代码示例与组件引用说明适合需要在U8基础上做单据增删改、审核/弃审等个性化扩展的开发人员。资源包为1个doc文档体积417KB打开即可按章节查阅接口分类、开发步骤与参数设置文档结构完整相当于一本可直接上手的VB版U8API速查手册。已有162人学习对刚接触U8UAP或准备调用U8API完成二开任务的读者可快速建立整体调用模型并避开常见引用与上下文配置问题。1. U8API 到底解决什么问题一类被忽视的 ERP 二次开发接口做 ERP 二次开发的人大多撞过这样一堵墙客户要个性化标准产品只能给标准动作。比如客户要求采购订单保存前做超预算拦截界面层改不起数据库层不敢碰唯一能下手的是业务接口层U8API 就是这一层。U8API 是以单据为操作对象的标准化业务编程接口覆盖采购、销售、库存三大供应链模块的增删改、审核/弃审、装载用 VB 或 C# 调用适合做 U8 项目交付实施、客户化开发的工程师也适合在 U8 上做功能扩展的计算机技术团队。需要先说清边界它不是用来做外部系统对接的那是 U8EAI 的活很多初学者一开始就把这两者搞混了。2. 先看懂框架再写代码U8API 资源管理器、Broker 与上下文环境2.1 资源管理器找 API、看定义、生成代码的三合一入口U8API 不像一般 SDK 那样给一份函数清单就完事它带一个独立的桌面工具叫 U8API 资源管理器。安装 U8 客户端后在“开始”→“程序”→U8 的 UAP 目录下能找到它。登录进去后左侧是一棵按业务模块分类的树采购管理、销售管理、库存管理每个模块下面挂着该模块涉及的单据比如库存管理下有盘点单、采购入库单。展开一张单据会看到两个子节点事件和服务接口。“服务接口”下才是真正可调的 API比如“添加新单据”“审核单据”“弃审单据”“装载单据”“删除单据”。我一般建议开发者拿到手册后第一件事不是读代码而是打开资源管理器把目标单据的“服务接口”节点整个展开看一遍。这一步能避免后面很多返工因为你会发现同一个“审核”动作在采购模块和销售模块里的方法名根本不一样模块审核/弃审方法保存方法装载方法采购管理ConfirmPO / CancelconfirmPOVoucherSaveLoad销售管理AuditbVerify 区分审核/弃审SaveLoad双击任意 API 节点右侧会显示接口的完整描述参数列表、参数类型、必输还是可选、API 上下文要求、返回值定义。这些信息就是调用时的“合同”字段名、参数名、上下文都以这里为准。在节点上右键有“代码示例”子菜单支持 VB 和 C# 两种语言。点“VB 代码示例”会弹出一个已经拼好 Login、EnvContext、Broker、InvokeApi 的骨架代码可以直接拷贝到 IDE 里改参数用。这个工具还有另一面所有业务 API 都必须在这里注册包括你自己开发的私有 API。也就是说它不只是消费端查接口的字典还是生产端发布接口的注册中心。你在这个资源管理器里能看到的就是当前 U8 环境里全部可用的 API 资源库。2.2 U8APIBroker为什么建议走代理而不是直连业务组件U8API 的调用入口不是业务对象本身而是一个统一代理叫 U8APIBroker。使用时先创建 Broker然后调用 Connect传入 API 地址标识URI和环境上下文。URI 是 API 在 U8API 系统中的唯一标识比如U8API/SaleOrder/Save代表销售订单“新增或修改”接口U8API/PurchaseOrder/VoucherSave代表采购订单保存接口。Connect 之后用 AssignNormalValue 和 SetBoParam 填参数再 InvokeApi 执行。为什么要套一层 Broker最直接的原因是解耦。API 提供者业务组件和 API 使用者你的 VB/C# 工程之间隔了一个服务代理中间件业务组件换了内部实现、改了适配器版本调用方代码不必跟着变。另一个原因是统一错误处理所有 API 调用失败后都通过 GetLastError 取错误信息通过 ErrorType 区分业务错误ExceptionType_Business比如单据状态不允许审核和系统错误ExceptionType_System比如连接中断、组件未安装。这一点在实际项目里非常值钱没有这层封装每个业务组件的报错风格都不一样你根本没法写统一的错误弹窗。Broker 底下还挂着一层适配器组件按模块划分采购管理要装 PUAPIConcrete.dll销售管理要装 SAAPIConcrete.dll。这些适配器由 U8 产品安装程序部署手册里要求环境必须满足.NET Framework 2.0、U8-872 版本的对应产品模块。如果缺适配器Connect 本身能成功但 InvokeApi 会直接抛系统异常而且错误信息往往很含糊只给一句话带过。所以环境检查要放在写代码之前做我习惯先到安装目录确认 UFMOM\U8APIFramework 下有 Broker 类库再确认模块适配器文件存在才往下走。这里需要注意 U8API 与 U8EAI 的定位差异选错会白做一整个项目。U8API 是传统编程模型贴近底层业务逻辑粒度细调用时依赖 login、事务等上下文环境U8EAI 则是松耦合的 XML 请求/响应消息模式业务粒度粗。按手册的原话U8API 适合 U8 内部功能扩展和客户个性化开发U8EAI 适合外部系统与 U8 之间以业务数据交换为特征的应用集成。做界面按钮、单据校验、流程增强选 U8API做电商平台、外部 OA 与 U8 的数据同步选 U8EAI。两者的技术栈完全不一样一开始站错队后面不是补代码能救回来的。3. 从引用到调用六步流程与三类参数的传值规则3.1 环境上下文登录、事务、特殊业务参数先说组件引用这是 U8API 调用的前置条件漏一个后面全是“自动化错误”。VB 工程要引用四个东西%U8SOFT%\UFMOM\U8APIFramework\ 下的 UFIDA.U8.U8APIFramework.tlb、UFIDA.U8.MomServiceCommon.tlb、UFIDA.U8.U8MOMAPIFramework.tlb外加 Microsoft XML v3.0处理 DOM 对象用。%U8SOFT% 是 U8-872 的安装目录环境变量名。C# 工程引用的是 Interop 目录下的 U8Login.dll、MSXML2.dll 和 UFMOM 下的三个 dll。建议把引用路径固定写成从 %U8SOFT% 展开的完整路径不要相对路径避免换机器后引用丢失。引用配好之后调用任何 U8API 都统一走六个步骤构造 U8Login 对象并登录构造环境上下文 U8EnvContext 传入 login创建 U8ApiComBroker 并 Connect传入 API 地址和上下文参数赋值调用 InvokeApi获取返回结果。以下是用 VB 封装前两步的骨架Dim ologin As Object Set ologin CreateObject(U8Login.clsLogin) If Not ologin.login(AS, (default)001, 2008, demo, , 2008-7-7, localhost) Then MsgBox ologin.ShareString End If Dim u8EnvCtx As New U8EnvContext Set u8EnvCtx.U8Login ologin u8EnvCtx.SetApiContext VoucherType, 23login 方法的七个参数分别是数据源、账套、语言 ID、操作员、密码、登录日期、服务器。登录失败时往往不是密码问题而是账套参数格式不对默认账套要写成(default)001这种带括号的格式。ShareString 返回登录失败原因看不到错误内容时先把它打出来。环境上下文里的 U8Login 必须是已经登录成功的对象框架后续所有业务校验都依赖这个登录态。除了 login上下文还可以带两样东西外部事务和特殊业务参数。如果需要由外部发起事务把 ADO 的 Connection 对象传给 BizDbConnection并把 IsIndependenceTransaction 置为 True表示事务边界由调用方控制。特殊业务参数则因 API 而异最典型的是销售模块的 VoucherType单据类型文档里明确写着销售 API 必须设置。下面是完整上下文参数清单上下文参数类型含义典型值VoucherTypeInteger单据类型采购订单、销售订单对应的类型编码bPositiveBoolean红蓝标识True 蓝字False 红字sBillTypeString单据子类型一般传空串sBusTypeString业务类型普通采购、直运采购、受托代销这些参数全部通过 u8EnvCtx.SetApiContext 设置参数名大小写不敏感。踩坑提醒不是每个 API 都要设全这四个多设不报错漏设必报错。以资源管理器接口描述里“API 上下文”一节列出的字段为准销售只看 VoucherType采购才看 bPositive、sBusType 那一组。3.2 三类参数普通参数、BO 参数与 OUT/INOUT 参数的传值规则U8API 的参数分普通参数和 BO 参数两类普通参数是单据表头/表体之外的值比如保存状态、单据 ID、审核标志BO 参数是单据表头和表体本身。普通参数用 AssignNormalValue 赋值示例u8apiBroker.AssignNormalValue VoucherState, 2“VoucherState”在采购的 VoucherSave 接口里是必输参数2 代表新增1 代表修改0 代表非编辑状态。参数名不限大小写但不允许拼错拼错的报错信息和“字段不允许为空”几乎一样很难分辨需要回查接口定义。BO 参数有两种传法。第一种是传 BO 对象BusinessObject推荐使用因为框架会对字段做参数校验必输字段漏掉会立刻报错。用法是先用 GetBoParam 取出参数对象再 SetValue 逐字段赋值Dim domHead As BusinessObject Set domHead u8apiBroker.GetBoParam(DomHead) domHead.RowCount 1 domHead(0).SetValue dpodate, 2008-07-07 domHead(0).SetValue cpoid, PO001RowCount 是 BO 对象行数设置成 1 表示先给第一行赋值的意图。表头只有一行表体才有行数概念。表体行数有一套固定规则设置 RowCount 为 10你可以对第 0 到第 9 行赋值当对第 10 行赋值时系统会顺序追加一个新行但此时不能跳到第 11 行追加出第 10 行之后才能对第 11 行赋值。本质是行必须按顺序生长不允许跳行。赋值时值可以是真实类型也可以是无类型字符串框架会自动转换。第二种传法是直接传 MSXML2.DOMDocument 对象先 load 一个 XML 文件再 AssignNormalValue 传进去。这种方式的坏处是框架不做参数校验XML 里字段名写错、必输项缺失都不会在赋值时报错而是等提交数据库时报一堆让人摸不着头脑的业务异常。能用 BO 对象就不要用 DOM 对象除非你本身就是从其他系统拼 XML 过来的。OUT 型参数也要单说。值类型String、Integer、Long、Double、Boolean、Date的 OUT 参数可以不传变量调用返回后用 GetResult(参数名) 取引用类型比如 MSXML2.IXMLDOMDocument2则必须提前 Dim 一个变量传进去调用返回后直接使用该变量。INOUT 型参数无论是否值类型都必须传变量。判断参数类型看接口定义里的“参数数据类型”拿不准时统一按“必须传变量”处理最保险。4. 复合操作实战先装载后保存的订单接口完整调用链4.1 单一操作与复合操作什么场景选哪种API 的典型应用分两种。单一操作是指一项任务只调一个 API比如点“审核”按钮调 ConfirmPO 或 Audit点“删除”调 Delete。这是最常见的用法构造参数、调用、拿结果三步结束。复合操作是指一项任务由多个 API 协作完成最典型的是自定义单据维护界面加载单据、界面上显示、用户修改字段、提交保存。这里表面看是两次调用实则是同一个 BO 对象的两次旅行——第一次调用“装载”API 把单据数据从数据库取出来变成 BO 对象用户在界面上修改的就是这个对象第二次调用“保存”API 时必须把这个被修改过的同一个 BO 对象传回去而不是重新构造一个新对象。为什么必须复用因为 BO 对象里承载的不只是你改的那几个字段还有表头表体所有原始字段值、行状态、单据内部的业务状态标记。如果你 new 一个新 BO 只填修改后的字段保存接口会认为其他字段不存在轻则非必输字段被清空重则破坏单据完整性导致保存失败。这条是复合操作里最后悔药式的坑看起来代码没错逻辑也没错但保存后的单据总是丢字段。4.2 一个订单流程装载、改字段、保存、错误处理以下是我在销售订单场景下常用的完整调用链前半段是装载后半段是保存中间复用 BO 对象 第1步登录 Dim ologin As Object Set ologin CreateObject(U8Login.clsLogin) If Not ologin.login(AS, (default)001, 2008, demo, , 2008-7-7, localhost) Then MsgBox ologin.ShareString End If 第2步构造环境上下文销售API必须设置VoucherType Dim u8EnvCtx As New U8EnvContext Set u8EnvCtx.U8Login ologin u8EnvCtx.SetApiContext VoucherType, 23 第3步连接装载API并调用 Dim u8apiBroker As New U8ApiComBroker u8apiBroker.Connect U8API/SaleOrder/Load, u8EnvCtx u8apiBroker.AssignNormalValue VouchID, 10001 If u8apiBroker.InvokeApi() False Then MsgBox u8apiBroker.GetLastError() End If 第4步获取返回的BO对象 Dim domHeadOrBodyRet As BusinessObject Set domHeadOrBodyRet u8apiBroker.GetBoParam(DomHead) 第5步用户修改字段把预发货日期改掉 domHeadOrBodyRet(0).SetValue dPreDate, 2008-08-08 第6步重新连接保存API把同一个BO对象传回去 u8apiBroker.Connect U8API/SaleOrder/Save, u8EnvCtx u8apiBroker.SetBoParam DomHead, domHeadOrBodyRet u8apiBroker.AssignNormalValue VoucherState, 1 If u8apiBroker.InvokeApi() False Then If u8apiBroker.ErrorType ExceptionType_Business Then MsgBox 业务错误 u8apiBroker.GetLastError() ElseIf u8apiBroker.ErrorType ExceptionType_System Then MsgBox 系统错误 u8apiBroker.GetLastError() End If Else Dim result As String result CStr(u8apiBroker.GetReturnValue()) End If 第7步结束调用释放资源 u8apiBroker.Disconnect Set u8apiBroker Nothing这段代码有几个关键点需要说明。装载时传入的 VouchID 是单据号可选参数不传则按过滤条件装载这里为了演示写死了一个值实际项目里应该把界面上选中的单据 ID 传进来。Connect 动作在装载和保存之间做了第二次同一个 Broker 对象可以切换不同 API 地址这是 U8API 允许的不是重复创建。保存时 VoucherState 传 1 表示修改与装载配对的场景里永远是 1不可能是 2如果是在数据维护界面里点“新增”才传 2。Save 接口里的 VNewID、DomConfig 等可选参数此时不需要填DomConfig 在 ATO、PTO 选配业务里才用得上普通流程可以不管。整个调用的失败分支分两层看GetLastError 给的是可读错误文本ErrorType 给的是错误性质。业务错误不需要改调用代码往往是单据状态不对、必输字段没填、权限不足系统错误则要先检查环境比如适配器组件缺失、连接中断。错误处理是最容易被新手省略的部分实际项目里我见过大量上线后“点了没反应”的案例最后排查下来都是错误分支只写了一个 Debug.Print用户根本看不到发生了什么。采购模块和销售模块的方法名差异在上表列过采购的“新增或修改”走 VoucherSave销售走 Save采购的审核是 ConfirmPO销售是带 bVerify 参数的 Audit。如果你在两个模块之间复制代码务必把方法名和参数名整体换掉只改 API 地址是不够的参数结构根本对不上。5. 常见踩坑与排查登录、上下文、BO 行数与外部事务5.1 登录与组件引用起手式最容易翻车现象代码执行到 CreateObject(U8Login.clsLogin) 直接抛自动化错误或者登录调用返回 False 且 ShareString 是空的。原因绝大多数是 U8Login 组件没有正确注册或版本不匹配。U8API 框架要求 U8-872 版本的客户端组件如果你机器上装的是其他版本的 U8 客户端Interop 目录下的 U8Login.dll 版本会对不上。还有一种情况是 VB 工程引用了 tlb 组件但运行时找不到对应 dll本质是安装目录被改动过。解决先到 %U8SOFT%\Integererop\ 下确认 Integererop.U8Login.dll 存在注册表中能查到 U8Login.clsLogin 对应的 ProgID。再确认 VB 工程里“引用”中列出的 U8API 框架类库没有黄色感叹号有感叹号就去掉重新引用一遍。登录参数里最容易错的是账套格式默认账套必须写成(default)001而不是001这个细节能卡住半天。5.2 上下文与事务玄学报错的大本营现象登录成功Connect 也成功InvokeApi 返回 FalseGetLastError 提示“上下文参数缺失”或“VoucherType 上下文值无效”。销售单据保存时尤其高频。原因销售管理模块的 API 强制要求 VoucherType 上下文。VoucherType 的值不是单据名称而是单据类型编码比如销售订单要传资源管理器接口描述里对应的 int 值。代码示例里默认给的是 23 这类占位值直接照抄不改成真实类型码就会报这个错。另一个隐蔽原因是上下文的设置时机必须在 Connect 之前 SetApiContextConnect 之后设置不生效。解决回资源管理器查看目标 API 的“API 上下文”描述逐个确认 VoucherType 取值。把 SetApiContext 的调用调整到 Connect 之前并建议在登录后第一时间统一设置好不要把上下文设置散落在业务代码里。现象复合操作里两个 API 各自成功但用户取消操作后数据库里留下一条半成品单据想回滚回滚不掉。原因默认情况下每个 API 调用是独立事务API 之间没有共享事务边界。有人以为同一个 Broker 对象连接两次就应该在同一个事务里其实不对事务边界由环境上下文决定。外部事务必须显式传递 ADO Connection 并置位 IsIndependenceTransaction否则框架每次调用自己提交。解决在上下文构造时把连接对象挂上去Set u8EnvCtx.BizDbConnection New ADODB.Connection u8EnvCtx.IsIndependenceTransaction True然后由调用方统一控制 Connection 的 BeginTrans、CommitTrans、RollbackTrans。注意这里用的是 ADODB.Connection不是 ADO.Connection引用要提前加 Microsoft ActiveX Data Objects Library。5.3 参数与 BO 对象丢字段、丢行的重灾区现象保存后单据能提交但之前加载出来的部分字段变成空值或者表体多行数据保存后只剩最后几行。原因复合操作里新建了 BO 对象而不是复用 Load 返回的对象。新 BO 只包含你显式 SetValue 的字段其他字段全为空保存接口就把这些空值写回数据库了。表体丢行则是行数生长规则被违反——跳行赋值会让未被赋值的行不进入保存范围。解决装载后把 GetBoParam 返回的 BusinessObject 对象直接保存到窗体级变量用户改动只是 SetValue 覆盖具体字段保存时把这个变量 SetBoParam 传回。表体赋值永远从第 0 行开始顺序写入不要尝试先给第 10 行赋值再回填第 0 行。如果你确实需要按行号处理外部数据先按顺序把行数撑到 RowCount 目标值再逐行填值。现象保存时业务报错说“字段 XX 不允许为空”但你明明在 XML 里传了这个字段。原因传的是 DOM 对象而不是 BO 对象。DOM 方式框架不校验参数字段名大小写、命名空间、XML 结构都靠自觉。一旦 XML 里字段名与接口定义不完全一致框架会在数据库层做字段映射时失败报错信息指向的字段往往不是你传错的字段排查成本极高。解决改用 BO 对象传参。字段名以资源管理器接口定义为准不要凭数据库字段名猜测。开发阶段先跑通 BO 方式再决定要不要为了性能改用 DOM 方式——多数项目里 BO 方式的性能已经够了没必要引入 DOM 的不确定性。6. 把资源管理器的代码示例改造成连通性验证脚本拿到一个新模块的 API 时我建议不要直接上手写业务代码先把资源管理器生成的 VB 示例改造成一个最小连通性脚本。做法是在“代码示例”菜单里点出 VB 骨架然后只改三处登录参数换成真实账套和日期按接口描述的 API 上下文补全 SetApiContext给必输字段填测试值。其他代码一律不动。跑通这个脚本只说明一件事当前环境的登录、Broker、适配器和单据接口链路是通的。链路不通后面所有业务代码都白写。这个验证脚本里我还习惯把错误输出封装成一个小函数专治“用户只看到了一个弹出框”的问题Private Sub DumpApiError(broker As U8ApiComBroker) Debug.Print ErrorType broker.ErrorType Debug.Print LastError broker.GetLastError() If broker.ErrorType ExceptionType_Business Then Debug.Print 业务错误检查单据状态与必输字段 ElseIf broker.ErrorType ExceptionType_System Then Debug.Print 系统错误检查组件与连接 End If End Sub这个函数不解决任何业务问题但它能把黑匣子打开一条缝。Debug.Print 输出到立即窗口实际项目里可以改成写日志文件或返回给界面的状态栏。验证脚本的另一层价值是当对照物业务代码出问题时跑一遍脚本就能区分是环境坏了还是业务代码坏了不用反复猜。我自己的习惯是每接一个 API 都强制先走一遍“装载-什么都不改-保存”的流程验证连通性再往里加业务逻辑。有次在销售模块里省了 VoucherType报错后从登录到 Connect 逐行排查到凌晨发现就是上下文少了一行。从那以后不管多熟都先跑资源管理器模板再小步验证最后才写业务希望帮到你。本文还有配套的精品资源点击获取