上个月,公司财务部的小张又加班到了晚上九点。不是因为核算量大,而是同一笔费用报销单,她已经在OA里审过了,还要把申请人、金额、费用项目、发票信息一个个手敲进用友YonSuite里。一天几十笔,眼睛都看花了,偶尔还会把金额输错,被业务部门投诉。
这不是个例。很多企业都在用致远OA走审批流程,用用友YonSuite做财务核算,两个系统之间隔着一道"数据鸿沟"。审批流结束了,财务的工作才真正开始。
这篇文章,我会把最近刚做完的一个真实项目完整复盘出来——如何把致远OA-V8里的费用报销单、付款申请单、差旅费申请,自动同步到用友YonSuite,让财务人员从重复的录入工作中解放出来。全文不讲虚的,只讲接口怎么调、字段怎么对、坑怎么躲。
一、项目背景:为什么要做这件事
先说说我们公司的现状:
- 致远OA-V8:用于日常审批,员工提单、领导审批、流程归档都在这里完成。
- 用友YonSuite:财务核算系统,费用报销、付款结算、总账凭证都在这套系统里跑。
两套系统各自独立,数据不互通。带来的问题很直接:
- 重复录入:OA审批完的单据,财务要在用友里重新录入一遍,日均几十笔,人力成本高。
- 数据不一致:手工录入难免出错,金额、部门、费用项目对不上,月底对账头疼。
- 时效滞后:审批当天完成了,财务往往第二天甚至第三天才录完,资金流信息滞后。
业务部门的诉求很简单:审批一结束,财务系统里就能自动生成单据,财务只需要审核和付款。这就是我们这次集成的目标。
二、总体架构设计:怎么连的
我们采用的是"中间数据库 + 定时同步"的方案。没走实时消息队列,原因很简单:费用单据不是毫秒级要求的业务,分钟级甚至十分钟级的延迟完全可以接受,而且定时轮询的实现和维护成本更低。

图1:致远OA-V8 ↔ 用友YonSuite 系统集成总体架构–基于轻易云数据集成平台
整个架构分三层:
- 展示层:左边是致远OA-V8,右边是用友YonSuite,业务人员各干各的,感知不到中间过程。
- 集成层:自研的集成服务中间件,负责定时轮询OA已归档单据、调接口取数据、做字段映射、向用友写入。核心逻辑就四块:数据抽取、格式转换、字段映射、异常重试。
- 数据层:中间数据库,存三样东西——基础资料对照表(OA名称与用友编码的映射)、同步日志(成功失败都记)、异常队列(失败的单据进这里,人工处理后重新触发)。
数据流向是单向的:OA → 中间件 → 用友。用友审核后的状态不回传OA,因为财务审核是另外一套流程,没必要耦合。
—
三、致远OA-V8接口详解:数据怎么拿出来
致远V8从SP1版本开始,对外提供了一套REST开放接口。要用这些接口,第一步是拿到访问令牌(Token)。
3.1 认证方式:三方互信机制
致远V8.2SP1之后的版本,引入了"三方互信"的安全认证。调用流程分两步:
第一步:获取Token
POST http://{OA服务器IP}:{端口}/seeyon/rest/token/{用户名}/{密码}
这个接口返回一个Token字符串,后续所有请求都要在HTTP
Header里带上它。Token有效期默认23分钟,过期前需要重新获取。我们的做法是把它封装成一个独立模块,在过期前5分钟自动刷新,避免同步过程中突然断掉。
第二步:三方互信验证(V8.2SP1+ 必做)
POST http://{OA服务器IP}:{端口}/seeyon/rest/token/channel
OA在返回前会调用你预先配置好的回调接口,把Token传给你。你的服务返回HTTP 200,就算验证通过。如果这一步没过,后面所有接口都会报401。
3.2 取数据的核心接口
认证通过后,就要拿审批完成的单据数据了。实际用得最多的就三个接口:
① 根据SummaryID获取表单数据(最常用)
GET http://{OA服务器IP}:{端口}/seeyon/rest/form/getformdata/{summaryId}
summaryId就是流程实例ID,可以理解为单据的唯一标识。这个接口返回完整的表单数据,主表字段直接是key-value,从表数据在sub节点下以数组形式返回。我们用的是JSON格式,处理起来比XML方便得多。
返回结构大概是这个样:
{
"申请人":
"张三",
"申请部门":
"销售一部",
"报销事由":
"客户招待",
"报销总金额":
"2580.00",
"sub": [
{"费用项目":
"餐饮费", "金额":
"1580.00"},
{"费用项目":
"交通费", "金额":
"1000.00"}
]
}
② 批量获取已结束流程ID
GET http://{OA服务器IP}:{端口}/seeyon/rest/flow/FromFinish/{templateCode}/{startTime}/{endTime}
集成服务不可能每次把OA里所有单据都扫一遍,这个接口按模板编号和时间范围,返回已正常结束的流程ID列表。我们每10分钟跑一次,只扫这10分钟内新归档的单据,效率很高。
③ 查询流程状态
GET http://{OA服务器IP}:{端口}/seeyon/rest/flow/state/{flowId}
返回一个整数状态码:0表示正常结束,1表示待发,¾表示审批中,5表示撤销,6/7表示退回/取回,15表示终止。我们只同步state=0的单据,其他状态一律跳过。

四、用友YonSuite接口详解:数据怎么写进去
用友YonSuite的开放接口风格跟金蝶完全不同,它是基于开放平台的RESTful模式,需要先建应用、拿授权,再用access_token调业务接口。
4.1 开放平台认证:AppKey + HmacSHA256签名
第一步,在YonSuite后台的"我的应用"里创建一个自建应用,拿到三样东西:AppKey、AppSecret、租户ID(tenantId)。
第二步,用AppKey和AppSecret去换access_token:
GET https://open.yonyoucloud.com/open-auth/selfAppAuth/getAccessToken
?appKey={appKey}×tamp={timestamp}&signature={signature}
这里的signature不是明文密码,而是用HmacSHA256算出来的签名。具体做法:把appKey、timestamp这些参数按名称排序后拼成字符串,用AppSecret做密钥做HmacSHA256签名,结果Base64编码后再URL编码。这个签名计算建议直接写在代码里,不要每次手拼。
接口返回的JSON里有两个关键字段:
{
"code": "00000",
"message": "成功",
"data": {
"access_token":
"2215d3c77b684876980ff15f9a0df6c7",
"expire": 7200
}
}
access_token有效期2小时(7200秒),调用业务接口时放在Header里:
Authorization: Bearer {access_token}
Content-Type: application/json
注意:用友大部分接口都有单独的限流策略,默认每秒2次请求。我们在开发时踩过这个坑——一开始没做速率控制,批量同步20条单据时直接触发限流,接口返回429错误。后来加了请求队列和sleep控制,问题解决。
4.2 单据保存接口:singleSave 和batchSave
用友的单据操作接口统一以yonbip为前缀,格式很规整:
POST https://{数据中心域名}/yonbip/{业务域}/{单据标识}/batchSave
请求Body是JSON结构,核心字段有三个:

图3:用友YonSuite 数据写入接口调用完整流程
五、三类单据对接实战:字段怎么对
下面这张是整个同步过程的业务全景图,从员工提单到财务核算,三个单据类型走的是同一条主干道,只是各自进不同的用友单据模块。

5.1 费用报销单 → 用友费用报销单(er_expensebill)
这是最常规的单据,员工报销差旅费、招待费、办公用品都走这个模板。
| 致远字段 | 用友字段 | 转换说明 |
| 申请人 | applicantId | 通过基础资料对照表取职员ID/编码 |
| 申请部门 | applyDeptId | 通过基础资料对照表取部门ID/编码 |
| 报销事由 | memo | 直接映射 |
| 报销总金额 | amount | Decimal精度处理,去除千分位 |
| 费用明细.费用项目 |
| expenseItems.expItemId | 子表映射,取费用项目ID |
| 费用明细.金额 | expenseItems.amount |
| 直接映射 |
接口调用四步走:
- 扫致远已归档的报销单,拿summaryId调getformdata取明细;
- 根据对照表把致远字段转成用友编码;
- 组装batchSave报文,单据标识写
er_expensebill,_status写Insert;
- 调用用友接口,errcode=0即成功,否则进异常队列。
5.2 付款申请单 → 用友资金管理结算单(fi_settlebill)
这是这次对接最有差异化的地方。OA里的"付款申请单",在用友侧不是简单的费用单据,而是资金管理模块的结算单。结算单管的是实际资金收付,比费用报销单更靠近资金执行层。
| 致远字段 | 用友字段 | 转换说明 |
| 申请人 | applicantId | 取职员ID/编码 |
| 申请日期 | billDate | YYYY-MM-DD格式 |
| 往来单位 | counterpartId | 根据往来类型判断:员工/供应商/客户 |
| 应付金额 | payAmount | 直接映射 |
| 申请付款金额 | applyAmount | 直接映射 |
| 付款用途 | payPurposeId | 固定值编码,以用友配置为准 |
付款申请单有几个特殊处理点:
- 结算类型(billType):供应商付款、员工报销付款、客户退款对应的类型编码不一样,需要在基础资料对照表里提前配好映射关系。
- 期望付款日期:通常是审批通过日期加上合同约定的付款周期,比如30天账期就是审批日期+30天。
- 收付组织 + 结算方式:这两个字段在用友基础数据里维护,同步时根据OA表单里的对应选项查找转换。
- 自动审核:结算单我们建议保存后直接调用audit接口自动提交,让单据进入待审核状态,财务只需要点审核即可付款,节省时间。
5.3 差旅费申请 → 用友差旅报销单
差旅业务分两块:事前申请和事后报销。用友YonSuite里没有单独的"出差申请单",而是通过费用报销单的业务类型字段来区分。
| 致远字段 | 用友字段 | 转换说明 |
| 申请人 | applicantId | 取职员ID/编码 |
| 出差事由 | memo | 直接映射 |
| 出发日期 | startDate | YYYY-MM-DD格式 |
| 返回日期 | endDate | YYYY-MM-DD格式 |
| 出发地 | departure | 直接映射 |
| 目的地 | destination | 直接映射 |
| 交通工具 | vehicleType | 枚举值映射,如飞机/火车/汽车 |
差旅报销单同步到用友时,同样走er_expensebill,但busiType字段要赋值为差旅相关的业务类型编码(具体编码咨询用友顾问或查开放平台字典表)。差旅独有的字段(出发地、目的地、交通工具)可以通过扩展字段或子表自定义字段承载。
—
六、踩坑与避坑指南
这个项目做了两个月,踩了不少坑。整理出来供大家参考:
| 坑点 | 现象 | 解决方案 |
| Token过期 | 同步到一半报401,后面单据全失败 | 封装Token管理模块,过期前5分钟自动刷新 |
| 接口限流 | 批量同步时返回429,请求被拒 | 加请求队列,控制每秒不超过2次,超限自动sleep |
| 基础资料不匹配 | 部门/职员编码在用友里找不到,保存报错 | 中间库维护对照表,未匹配的先写默认值并告警 |
| 金额精度丢失 | 大额单据小数点后几位对不上 | 全程用Decimal类型,最后转字符串写入JSON |
| 来源单号断链 | 用友里联查不到OA来源单据 | 结算单必须使用来源生单接口,回写来源标识 |
| 子表字段缺失 | 用友返回缺少某某字段key | 子表数据通过"子表明细聚合"字符串方式拼接传递 |
—
结语
系统集成这件事,技术难度其实不算高,致远和用友都有成熟的开放接口。真正的难点在于业务细节的梳理:字段能不能对上、基础资料是不是一致、单据状态怎么流转、异常了谁负责处理。
这个项目上线后,财务小张每天的手工录入量从几十笔降到了几笔(只需要处理异常单据),数据准确率也明显提升。最重要的是,业务部门再也不用等两三天才能看到财务状态了。
如果你也在做致远和用友的对接,希望这篇文章能帮你少走一些弯路。有具体问题欢迎在评论区交流。