📋 文档信息
| 项目 | 说明 |
|---|---|
| 文档名称 | 泛微OA-金蝶API接口通用中间件说明文档 |
| 版本 | v2.0 |
| 创建日期 | 2025年11月24日 |
| 更新日期 | 2025年12月20日 |
| 作者 | 徐东瑞 |
| 当前功能 | API代理 + Token自动管理 + 前端框架 + 流程集成 |
🎯 变更记录
| 版本 | 日期 | 变更内容 |
|---|---|---|
| v1.0 | 2025-10-22 | 初始版本,包含基础API代理功能 |
| v2.0 | 2025-11-24 | 新增前端同步框架、流程集成、附件上传、权限控制等完整功能 |
🎯 项目概述
1.1 项目背景
为解决泛微E9 OA系统与金蝶云星空旗舰版ERP系统之间的数据交互需求,开发一套完整的API代理中间件。该中间件不仅提供API代理功能,还包含前端同步框架、自动Token管理、附件上传、权限控制等完整解决方案,实现安全、稳定、高效的数据同步。
1.2 已实现功能
✅ **统一接入**:提供统一的API入口,简化前端调用
✅ **Token管理**:自动管理金蝶API访问令牌,每小时自动刷新
✅ **API代理**:完整的请求转发和响应处理机制
✅ **附件上传**:支持文件流式上传到金蝶系统,避免内存溢出
✅ **权限校验**:基于角色的细粒度访问控制
✅ **前端框架**:可复用的JavaScript同步框架,支持步骤化执行,支持按条件执行步骤,支持延迟执行步骤
✅ **流程集成**:与泛微E9流程表单无缝集成
✅ **错误处理**:统一的错误解析和用户提示
✅ **进度显示**:可视化同步进度提示
🏗️ 系统架构
2.1 整体架构图
┌─────────────────────────────────────────────────────────────┐
│ 泛微E9 OA系统 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 流程表单 │ │ Ecode后台 │ │ 定时任务 │ │
│ │ (前端代码块) │ │ (框架代码) │ │ (Token管理) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ └────────────────┼────────────────┘ │
│ │ │
│ ┌───────────▼───────────┐ │
│ │ API代理中间件 │ │
│ │ KingdeeProxyServlet │ │
│ └───────────┬───────────┘ │
└──────────────────────────┼─────────────────────────────────┘
│
┌──────────────────────────▼─────────────────────────────────┐
│ 金蝶云星空ERP │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 业务API │ │ Token服务 │ │ 附件服务 │ │
│ │ /v2/... │ │ /oauth2/... │ │ /uploadFile │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘2.2 数据流向
- 流程发起:用户在OA流程表单提交数据
- 框架处理:前端KingdeeSync框架拦截提交事件
- 步骤执行:按配置步骤依次调用金蝶API
- 代理转发:中间件验证权限并转发请求
- 金蝶处理:金蝶系统处理业务逻辑
- 结果返回:响应逐层返回至前端界面
- 进度提示:用户实时查看同步进度
2.3 技术栈
| 层级 | 技术选型 |
|---|---|
| 前端框架 | JavaScript ES5+、jQuery、Promise异步 |
| 中间件 | Java Servlet、HttpURLConnection |
| 数据库 | SQL Server(泛微内置) |
| JSON处理 | FastJSON、原生JSON |
| 文件处理 | 流式传输、Zip解压、AES加密 |
| 权限控制 | 泛微角色体系、基于角色的访问控制 |
📊 数据库设计
3.1 数据表结构
3.1.1 Token存储表 (uf\_kingdeeAPI)
CREATE TABLE [uf_kingdeeAPI] (
[id] INT IDENTITY(1,1) PRIMARY KEY,
[clientid] VARCHAR(50) NULL, -- 客户端ID(固定为'weaverE9')
[clientsecret] VARCHAR(100) NULL, -- 客户端密钥
[dlyhgh] VARCHAR(20) NULL, -- 代理用户工号
[accountid] VARCHAR(50) NULL, -- 账户ID
[language] VARCHAR(10) DEFAULT 'zh_CN',-- 语言
[accesstoken] TEXT NULL, -- 访问令牌(Base64编码)
[tokengxsj] DATETIME NULL, -- 令牌更新时间
-- 泛微建模引擎标准字段
[modedatamodifier] INT NULL,
[modedatamodifydatetime] DATETIME NULL,
[formmodeid] INT NULL,
[modedatacreater] INT NULL,
[modedatacreatertype] INT NULL,
[modedatacreatedate] DATE NULL,
[modedatacreatetime] TIME NULL,
[form_biz_id] VARCHAR(50) NULL,
[MODEUUID] VARCHAR(36) NULL
);
-- 索引优化
CREATE INDEX idx_clientid ON uf_kingdeeAPI(clientid);
CREATE INDEX idx_tokengxsj ON uf_kingdeeAPI(tokengxsj);3.2 数据示例
INSERT INTO [uf_kingdeeAPI] (
[id], [clientid], [clientsecret], [dlyhgh], [accountid],
[language], [accesstoken], [tokengxsj], [form_biz_id], [MODEUUID]
) VALUES (
1, 'weaverE9', '2345678910JQKAwang.', '02250',
'2077625015653633024', 'zh_CN',
'OPENAPIAUTH_MjA3NzYyNTAxNTY1MzYzMzAyNF9FSExTeVN0c0xPTnBIMEVpZDJIVnpRTHd0V0o4clFtbDFldFNLRkFubjRrb2tDODNWaWs3MzR6RlRhSGQyRmFublNCaDF2d2R3SFRKN0NWdjhjRnZlWThmVm92eGR5SnNVbUZUMDY=',
'2025-11-24 14:19:33', '1000799', '97ffee80-c9f1-427d-9585-fe8d7e3abdc3'
);🔧 核心功能模块
4.1 Token自动管理模块
4.1.1 Token获取定时任务
| 配置项 | 说明 |
|---|---|
| 类名 | weaver.houdedev.cron.GetKingdeeTokenCmd |
| 执行频率 | 0 0 0/1 * * ?(每小时执行一次) |
| 认证地址 | https://ierp.houde-food.com/ierp/kapi/oauth2/getToken |
| 请求参数 | client\_id、client\_secret、username、accountId、nonce、timestamp、language |
| 响应处理 | 解析access\_token并更新到数据库 |
4.1.2 Token存储管理
- 自动获取:定时任务每小时自动刷新Token
- 缓存机制:中间件缓存最新Token,减少数据库查询
- 失效处理:Token过期时自动触发重新获取
- 多应用支持:预留多金蝶应用配置扩展
4.2 API代理服务模块
4.2.1 核心Servlet
| 属性 | 值 |
|---|---|
| 类名 | KingdeeProxyServlet |
| 包路径 | weaver.houdedev.cron |
| 访问路径 | /kingdee/proxy |
| 支持方法 | GET(健康检查)、POST(API代理) |
| 编码设置 | 全面UTF-8支持,解决中文乱码问题 |
4.2.2 请求处理流程
4.3 前端同步框架模块
4.3.1 KingdeeSync框架
| 特性 | 说明 |
|---|---|
| 类结构 | 面向对象设计,支持链式调用 |
| 步骤管理 | 支持顺序、条件、延迟步骤 |
| 进度提示 | 可视化弹窗显示执行进度 |
| 错误处理 | 自动解析金蝶错误信息 |
| 响应处理 | 统一的数据提取机制 |
4.3.2 核心方法
// 1. 创建同步实例
var sync = new KingdeeSync({
title: "同步进度",
showProgress: true,
timeout: 30000
});
// 2. 添加执行步骤
sync.addStep("步骤名称", requestBuilder, dataExtractor);
// 3. 添加条件步骤
sync.addStepIf(condition, "步骤名称", requestBuilder);
// 4. 添加延迟步骤
sync.addDelayStep("等待", 5000);
// 5. 执行流程
sync.execute(callback);4.4 附件上传模块
4.4.1 文件处理流程
- 文件获取:根据OA附件ID从ImageFile表获取文件信息
- 格式处理:支持ZIP压缩文件自动解压
- 加密处理:支持AES加密文件解密
- 流式上传:分块传输,避免大文件内存溢出
- 关联绑定:将附件与金蝶单据ID关联
4.4.2 关键技术
- 多部分表单:使用multipart/form-data格式
- 分块传输:
setChunkedStreamingMode(8192) - 内存优化:8KB缓冲区流式读写
- 超时设置:大文件上传120秒超时
4.5 权限控制模块
4.5.1 角色权限配置
// 允许访问的角色ID列表
private static final String[] ALLOWED_ROLE_IDS = {"2", "26"};
// 允许访问的角色名称列表
private static final String[] ALLOWED_ROLE_NAMES = {"系统管理员", "ERP接口访问"};4.5.2 权限验证流程
- 用户认证:通过
HrmUserVarify.getUser()获取当前用户 - 角色查询:查询用户关联的所有角色
- 权限检查:匹配角色ID或角色名称
- 访问控制:无权限用户返回403错误
🚀 接口详细设计
5.1 API代理接口
| 请求方式 | 请求地址 |
|---|---|
| post | OA地址/kingdee/proxy |
功能:代理调用任意金蝶API接口
请求头:
Content-Type: application/json;charset=UTF-8
Cookie: JSESSIONID=... (泛微会话Cookie)请求体:
{
"apiUrl": "/v2/basedata/bd_supplier/addSup",
"apiResultData": {
"data": [
{
"name": "供应商名称",
"number": "GYS2025001",
...
}
]
},
"fileId": [123, 456],
"attachmentUploadFileArgs": {
"entityNumber": "bd_supplier",
"controlKey": "d8b9_attachmentpanelap",
"billPkId": "供应商单据ID"
}
}参数说明:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| apiUrl | String | 是 | 金蝶API路径(不包含基础URL) |
| apiResultData | Object | 是 | 金蝶API要求的完整参数格式 |
| fileId | Array | 否 | OA附件文件ID列表 |
| attachmentUploadFileArgs | Object | 否 | 附件上传参数,包含单据关联信息 |
响应:
{
"status": true,
"data": {
"金蝶API返回的原始数据"
},
"message": "请求成功",
"attachmentResults": {
"file_123": {
"status": true,
"message": "上传完成",
"billId": "单据ID"
}
}
}5.2 健康检查接口
| 请求方式 | 请求地址 |
|---|---|
| get | OA地址/kingdee/proxy |
功能:服务健康状态检查
响应:
{
"status": true,
"message": "服务正常,当前有 1 个有效token",
"currentUser": "admin",
"userRoles": "系统管理员(2), ERP接口访问(26)"
}5.3 错误码定义
| 错误码 | 说明 | 处理建议 |
|---|---|---|
| 400 | 请求参数错误 | 检查apiUrl和apiResultData参数格式 |
| 401 | 用户未登录或Token无效 | 重新登录或检查Token获取任务 |
| 403 | 权限不足 | 为用户分配系统管理员或ERP接口访问角色 |
| 404 | 接口路径不存在 | 检查金蝶API路径是否正确 |
| 500 | 服务器内部错误 | 查看服务器日志,联系管理员 |
| 502 | 网络连接失败 | 检查到金蝶系统的网络连通性 |
| 504 | 请求超时 | 适当增加timeout参数值 |
🔧 核心代码实现
6.1 KingdeeProxyServlet 核心方法
6.1.1 主请求处理
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
// 1. 设置UTF-8编码(必须最先执行)
request.setCharacterEncoding("UTF-8");
response.setContentEncoding("UTF-8");
// 2. 用户身份验证和权限校验
User user = HrmUserVarify.getUser(request, response);
if (!checkUserRolePermission(user)) {
returnError(response, 403, "权限不足");
return;
}
// 3. 读取并解析请求体
String requestBody = readRequestBody(request);
JSONObject params = JSON.parseObject(requestBody);
// 4. 获取最新Token
String accessToken = getLatestTokenFromDB();
// 5. 调用金蝶API
String apiResponse = sendPostRequest(fullUrl, jsonBody, accessToken);
// 6. 处理附件上传(如果存在)
if (hasAttachments(params) && isSuccess(apiResponse)) {
uploadAttachments(fileIds, attachmentArgs, accessToken, billId);
}
// 7. 返回响应
returnSuccess(response, apiResponse);
}6.1.2 流式附件上传
private String sendStreamingMultipartPostRequest(String url, JSONObject args,
InputStream fileStream,
String fileName,
String accessToken) {
// 1. 设置分块传输模式
connection.setChunkedStreamingMode(8192);
// 2. 构建多部分表单边界
String boundary = "----WebKitFormBoundary" + UUID.randomUUID();
// 3. 流式写入表单数据
try (OutputStream os = connection.getOutputStream()) {
// 写入JSON参数部分
writeFormField(os, boundary, "attachmentUploadFileArgs",
args.toJSONString(), "application/json");
// 写入文件部分
writeFileField(os, boundary, "file", fileName, fileStream);
// 写入结束边界
writeBoundaryEnd(os, boundary);
}
// 4. 读取响应
return readResponse(connection);
}6.2 前端同步框架核心(ecode)
6.2.1 KingdeeSync类结构
function KingdeeSync(options) {
this.options = options || {};
this.steps = []; // 执行步骤数组
this.stepResults = []; // 步骤结果存储
this.currentStep = 0; // 当前步骤索引
this.progressModal = null; // 进度弹窗引用
}
// 添加执行步骤
KingdeeSync.prototype.addStep = function(name, requestBuilder, dataExtractor) {
this.steps.push({
name: name,
requestBuilder: requestBuilder,
dataExtractor: dataExtractor || function(response) {
return { rawData: response.data };
}
});
return this; // 支持链式调用
};
// 执行步骤
KingdeeSync.prototype.executeStep = function(stepIndex, callback) {
var step = this.steps[stepIndex];
var self = this;
// 更新进度显示
this.updateProgress(step.name);
// 构建请求数据
var requestData = step.requestBuilder(this.stepResults);
// 调用API
this.callKingdeeAPI(requestData).then(function(response) {
if (response.status && response.data.status) {
// 成功:提取数据并继续下一步
var extractedData = step.dataExtractor(response, self.stepResults);
self.stepResults.push(extractedData);
self.currentStep++;
self.executeStep(stepIndex + 1, callback);
} else {
// 失败:解析错误信息
var errorDetails = self.parseKingdeeError(response);
self.handleStepError(step.name, new Error(errorDetails));
}
}).catch(function(error) {
self.handleStepError(step.name, error);
});
};🛠️ 部署指南
7.1 环境要求
| 组件 | 要求 |
|---|---|
| 操作系统 | Windows Server 2012+ / Linux CentOS 7+ |
| Java环境 | JDK 1.8+ |
| 应用服务器 | 泛微E9自带Resin服务器 |
| 数据库 | SQL Server 2012+ / Oracle 11g+ / MySQL 5.7+ |
| 网络 | OA服务器可访问金蝶云星空API |
| 泛微版本 | E9 9.0及以上 |
7.2 部署步骤
7.2.1 数据库准备
- 创建数据表:通过建模引擎创建uf\_kingdeeAPI表并添加字段
- 初始化配置:插入金蝶API认证信息
- 权限配置:确保OA数据库用户有读写权限
7.2.2 文件部署
部署class文件:
/ecology/WEB-INF/classes/weaver/houdedev/cron/ ├── GetKingdeeTokenCmd.class ├── KingdeeProxyServlet.class └── ...- 配置web.xml:
<servlet>
<servlet-name>KingdeeProxyServlet</servlet-name>
<servlet-class>weaver.houdedev.cron.KingdeeProxyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>KingdeeProxyServlet</servlet-name>
<url-pattern>/kingdee/proxy</url-pattern>
</servlet-mapping>部署前端代码:
- 将ecode代码上传到泛微ecode后台
- 配置为全局可用的JavaScript模块
7.2.3 定时任务配置
- 登录泛微E9管理后台
- 进入“集成中心” → “功能集成”→ “计划任务”
- 点击“新建”按钮
- 配置任务参数:
| 参数项 | 值 |
|---|---|
| 任务名称 | 金蝶Token自动获取 |
| 类名 | weaver.houdedev.cron.GetKingdeeTokenCmd |
| 执行频率 | 0 0 0/1 * * ? |
| 状态 | 启用 |
| 描述 | 每小时获取金蝶API访问令牌 |
7.3 权限配置
角色分配:
- OA后台进入 组织权限中心 → 权限管理 → 角色设置
- 为需要调用金蝶接口的用户分配“系统管理员”或“ERP接口访问”角色
🔍 使用示例
8.1 前端调用示例
8.1.1 基本API调用
// 供应商查询示例
jQuery.ajax({
url: '/kingdee/proxy',
type: 'POST',
contentType: 'application/json;charset=UTF-8',
data: JSON.stringify({
"apiUrl": "/v2/basedata/bd_supplier/query",
"apiResultData": {
"data": {
"start_createtime": "2025-01-24 15:51:43",
"end_createtime": "2025-03-25 15:55:45",
"status": ["B", "C"]
},
"pageSize": 10,
"pageNo": 1
}
}),
success: function(response) {
if (response.status) {
console.log('查询成功:', response.data);
} else {
console.error('查询失败:', response.message);
}
}
});8.1.2 流程集成示例
// 流程表单前端代码块
jQuery(document).ready(function() {
// 1. 注册当前流程
KingdeeSyncUtils.registerWorkflow(1595);
// 2. 创建同步实例
var kingdeeSync = new KingdeeSync({
title: "供应商数据同步",
showProgress: true,
timeout: 30000
});
// 3. 定义执行步骤
kingdeeSync
.addStep("新增供应商", buildSupplierCreateRequest, extractSupplierId)
.addStep("提交审核", buildSubmitRequest)
.addDelayStepIf(needAssign, "等待分配", 3000)
.addStepIf(needAssign, "组织分配", buildAssignRequest);
// 4. 注册提交事件
WfForm.registerCheckEvent(WfForm.OPER_SUBMIT, function(callback) {
if (KingdeeSyncUtils.isEnabled()) {
kingdeeSync.execute(callback);
} else {
callback();
}
});
});8.2 健康检查示例
// 定期检查服务状态
function checkServiceHealth() {
fetch('/kingdee/proxy')
.then(response => response.json())
.then(data => {
if (data.status) {
console.log('✅ 金蝶接口服务正常:', data.message);
} else {
console.error('❌ 金蝶接口服务异常:', data.message);
}
})
.catch(error => {
console.error('❌ 服务连接失败:', error);
});
}
// 每5分钟检查一次
setInterval(checkServiceHealth, 5 * 60 * 1000);8.3 错误处理示例
// 自定义错误处理
kingdeeSync.handleStepError = function(stepName, error) {
// 记录错误日志
console.error('步骤"' + stepName + '"执行失败:', error);
// 显示用户友好的错误信息
var userMessage = "同步失败,请联系管理员";
if (error.message.includes("权限不足")) {
userMessage = "您没有操作权限,请联系系统管理员";
} else if (error.message.includes("网络连接")) {
userMessage = "网络连接失败,请检查网络设置";
} else if (error.message.includes("超时")) {
userMessage = "请求超时,请稍后重试";
}
// 显示错误提示
WfForm.showMessage(userMessage, 2, 5);
// 停止后续步骤
this.stopExecution();
};📈 监控与维护
9.1 系统监控
| 监控项 | 监控方法 | 告警阈值 |
|---|---|---|
| Token状态 | 检查uf\_kingdeeAPI.tokengxsj | 更新时间超过55分钟 |
| 服务可用性 | GET /kingdee/proxy 健康检查 | 响应时间>3秒或状态非200 |
9.2 故障排查
常见问题及解决方案:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| Token无效错误 | 1. 定时任务未执行2. 金蝶认证信息变更3. Token过期未刷新 | 1. 检查计划任务状态2. 检查数据库缓存表配置3. 手动执行Token获取定时任务 |
| 附件上传失败 | 1. 文件路径不存在2. ZIP文件损坏3. 网络超时 | 1. 检查ImageFile表记录2. 验证文件完整性3. 增加超时时间 |
| 中文乱码 | 1. 编码设置不一致2. 数据库编码问题3. 金蝶API编码限制 | 1. 统一使用UTF-82. 检查数据库连接字符串3. 排查金蝶API配置 |
| 权限拒绝 | 1. 用户角色未配置2. 角色配置错误3. 会话过期 | 1. 分配正确角色2. 检查角色ID/名称3. 重新登录OA系统 |
| 性能缓慢 | 1. 网络延迟2. 大文件处理3. 数据库查询慢 | 1. 网络优化2. 分块传输大文件3. 添加数据库索引 |
9.4 备份与恢复
备份策略:
配置文件备份:
# 备份web.xml配置 cp /ecology/WEB-INF/web.xml /backup/web.xml.$(date +%Y%m%d) # 备份数据库配置 sqlcmd -S localhost -d ecology -Q "SELECT * FROM uf_kingdeeAPI" -o /backup/kingdee_config.sql代码备份:
# 备份Java类文件 tar -czf /backup/kingdee_classes_$(date +%Y%m%d).tar.gz \ /ecology/WEB-INF/classes/weaver/houdedev/cron/恢复步骤:
- 停止Resin服务
- 恢复配置文件
- 恢复class文件
- 重启服务并验证
📋 参考资料
1. 金蝶API接口清单
请登录金蝶旗舰版,进入菜单 开放服务云-OpenAPI-API开发,到API列表中查看地址及请求方式
2. 泛微前端开发文档
3. 已应用流程
| 流程名称 | 实现功能 |
|---|---|
| HD-供应商新增流程 | 1. 供应商新增1. 供应商自动分配至其他组织1. 流程附件同步至金蝶供应商附件中 |
文档签署
| 角色 | 姓名 | 签字 | 日期 |
|---|---|---|---|
| 编写人 | 徐东瑞 | 2025-12-20 | |
| 审核人 | |||
| 批准人 |
文档状态:✅ 正式发布
保密级别:内部机密
分发范围:系统管理员、开发人员、实施顾问
