YiKdWebClient 是一个面向 金蝶云星空 WebAPI 的多语言开源客户端项目。各语言版本尽量保持一致的认证方式、公开方法名、参数顺序、服务路径和调用体验,方便不同技术栈对照接入。
当前项目提供 C#、Java、Python、Go、PHP 和 HTTP (JSON) 六种接入方式,均已完成适配。各版本使用独立仓库,并同时维护 Gitee 和 GitHub 地址。HTTP (JSON) 是不限定编程语言的通用接入版本;后续公共功能、协议报文和通用接入说明统一以其仓库 README 为准,各语言版本 README 主要维护安装、依赖、命名、异常/错误处理和同步/异步等语言特性。
| 接入版本 | 适配状态 | 当前基准 | Gitee | GitHub |
|---|---|---|---|---|
| C# | 已适配 | 1.0.0.32 |
YiKdWebClient C# | YiKdWebClient C# |
| Java | 已适配,当前项目 | 对标 C# 1.0.0.32 |
YiKdWebClient Java | YiKdWebClient Java |
| Python | 已适配 | 对标 C# 1.0.0.32 |
YiKdWebClient Python | YiKdWebClient Python |
| Go | 已适配 | Go v1.0.0,对标 C# 1.0.0.32 |
YiKdWebClient Go | YiKdWebClient Go |
| PHP | 已适配 | 对标 C# 1.0.0.32 |
YiKdWebClient PHP | YiKdWebClient PHP |
| HTTP (JSON) | 已适配,通用接入 | 以 HTTP (JSON) 仓库 README 为准 | YiKdWebClient HTTP | YiKdWebClient HTTP |
YiKdWebClient-Java 是 C# 版的 Java 8 兼容移植版。项目使用标准 HTTP 协议调用金蝶服务,不依赖金蝶官方 Java SDK;核心库使用 Jackson 处理 JSON,产出 Java 8 字节码,CI 同时验证 JDK 8、17、21 和 25。详细映射见 C# → Java API 对照。
所有已适配语言版本共同覆盖:
- 7 个认证枚举:SHA256 签名、SHA1 签名、第三方系统登录授权、API 请求头签名、旧版用户名密码、集成密钥/CNF,以及仅为兼容旧系统保留的
ValidateUserEnDeCode; - 查看、保存、批量保存、提交、审核、反审核、删除、查询、下推、分配等动态表单 WebAPI;
- 默认自动登录/登出、可选手动会话复用和 Cookie 管理;
- 单点登录 SSO V1~V4、SSO 登出参数与登出请求;
- 自定义 WebAPI 服务路径组装和调用;
- 文件路径与 Base64 附件分块上传、分块进度和最终返回;
- 默认 XML 配置、自定义配置路径和运行时动态传入授权信息;
- 登录与业务请求的实际 URL、请求头、请求体和响应体,便于使用 Postman、ApiPost 等工具排查问题。
Warning
配置模板、mock 输出或本地测试截图只用于演示。接入自己的环境时,必须替换数据中心 ID、集成用户、应用 ID、应用密钥、服务地址和集成密钥文件。请勿把生产密钥、生产密码、CNF、Cookie 或长期有效的会话信息提交到公开仓库。
Important
旧版用户名密码认证只用于协议兼容。独立代码示例会直接定义认证变量,并使用 123456 等明确占位值;每个占位值旁均注明需要替换为目标环境的真实值。示例会完整输出登录请求报文,便于直接复制、运行和排查。
Note
部分代码、测试、文档、示例或其他项目内容,可能在维护者指导和审查下借助 AI 工具生成、补全、重构或校对。AI 辅助内容在合并或发布前仍会由维护者进行审查和必要验证;使用者也应结合实际金蝶版本、补丁、权限和业务数据,自行评估正确性、安全性与适用性。
- 1. 相关资料
- 2. Java 环境与依赖
- 3. 构建、安装与引入
- 4. 配置 appsettings.xml
- 5. 五分钟运行第一个示例
- 6. ConsoleTestJava8 示例运行器
- 7. 认证与请求示例
- 8. JSON 参数与接口功能列表
- 9. 单点登录 SSO
- 10. 自定义 WebAPI
- 11. 文件与 Base64 分块上传
- 12. Java 语言特性与迁移差异
- 13. 常见问题
- 14. 开发、测试与项目地址
- 金蝶云星空官方原始报文与地址结构说明:https://vip.kingdee.com/knowledge/528587883691785472?productLineId=1&isKnowledge=2&lang=zh-CN
- 金蝶云星空官方 WebAPI 接口说明:https://vip.kingdee.com/knowledge/407944297590364160?productLineId=1&isKnowledge=2&lang=zh-CN
- HTTP (JSON) 通用接入文档:Gitee / GitHub
- Java/C# 方法与服务路径对照:docs/API_MAPPING.md
金蝶官方文档中的 JSON 通常是业务参数格式,不一定等于最终 HTTP 外层报文。YiKdWebClient-Java 会把参数包装成金蝶 WebAPI 所需格式;最终请求可以通过 ReturnLoginWebModel、ReturnOperationWebModel 和 RequestHeadersString 查看。
- JDK 8 或更高版本;核心库编译目标为 Java 8 字节码。
- 构建使用仓库自带的 Maven Wrapper,无需预先安装 Maven。
- JSON 使用 Jackson
2.22.0:jackson-databindjackson-corejackson-annotations
- 不依赖金蝶官方 Java SDK。
YiKdWebClient.jar 是普通类库,不是可执行程序,也不是包含依赖的 fat JAR。ConsoleTestJava8.jar 和 ConsoleTestJava8Simple.jar 是已经包含运行依赖的可执行示例。
项目结构:
| 路径 | 用途 |
|---|---|
YiKdWebClient/ |
核心客户端类库 |
YiKdWebClient.Tests/ |
JUnit 5 自动化测试,不连接真实金蝶环境 |
ConsoleTestJava8/ |
完整示例运行器,覆盖认证、SSO、自定义服务和上传 |
ConsoleTestJava8Simple/ |
最小化集成密钥登录与 View 示例 |
distribution/ |
生成统一的 dist/ 发行目录 |
docs/API_MAPPING.md |
C# 与 Java API 映射及迁移边界 |
docs/screenshots/ |
README 中使用的本地回环运行截图 |
Java 没有 NuGet。本仓库当前也没有配置 Maven Central 发布,因此不能只复制一段远程依赖就直接下载。项目提供以下 4 种引入方式:
| 引入方式 | 适用场景 | Jackson 依赖 | 推荐度 |
|---|---|---|---|
| 安装到本机 Maven 仓库 | Maven 业务项目、本机开发 | Maven 自动传递解析 | 推荐 |
| 同一 Maven reactor 直接依赖模块 | 源码一起构建、二次开发 | Maven 自动解析 | 推荐 |
Gradle + mavenLocal() |
Gradle 业务项目 | Gradle 按 POM 自动解析 | 推荐 |
手工添加 dist/lib/*.jar |
非 Maven/Gradle、旧项目、IDE 手工管理 | 必须手工加入全部 JAR | 兼容方案 |
在本仓库根目录执行:
Windows PowerShell:
.\mvnw.cmd -B -ntp -pl YiKdWebClient -am clean installLinux/macOS:
./mvnw -B -ntp -pl YiKdWebClient -am clean install这会把父 POM、核心 JAR 和依赖信息安装到当前用户的 Maven 本机仓库。然后在业务项目 pom.xml 中加入:
<dependency>
<groupId>io.github.1609676823</groupId>
<artifactId>YiKdWebClient</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>Maven 会根据 POM 自动引入 Jackson,不需要再手写三个 Jackson 依赖。version 必须与安装时的版本一致。
需要安装明确的正式版本号时,可以覆盖唯一版本入口 revision:
.\mvnw.cmd -B -ntp -Drevision=1.0.0 -pl YiKdWebClient -am clean install业务项目随后使用 <version>1.0.0</version>。
如果业务模块与本项目处于同一 Maven reactor,可以像 ConsoleTestJava8/pom.xml 一样直接依赖核心模块:
<dependency>
<groupId>io.github.1609676823</groupId>
<artifactId>YiKdWebClient</artifactId>
<version>${project.version}</version>
</dependency>根聚合 POM 的 <modules> 中需要同时包含 YiKdWebClient 和业务模块。适合修改客户端源码后与业务项目一起编译、测试。
先按 3.1 节执行 Maven 本地安装,再在 Gradle 中声明:
Groovy DSL:
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
implementation 'io.github.1609676823:YiKdWebClient:1.0.0-SNAPSHOT'
}Kotlin DSL:
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
implementation("io.github.1609676823:YiKdWebClient:1.0.0-SNAPSHOT")
}mavenLocal() 必须放在仓库列表中,否则 Gradle 找不到本机安装的 YiKdWebClient 制品。
先构建完整发行目录:
.\mvnw.cmd -B -ntp clean package构建成功后,必须把 dist/lib/ 下的 全部 JAR 加入 classpath,不能只添加 YiKdWebClient.jar:
dist/lib/
├─ YiKdWebClient.jar
├─ jackson-annotations-*.jar
├─ jackson-core-*.jar
└─ jackson-databind-*.jar
假设 Main.java 与 lib/ 位于同一目录,Windows:
javac -encoding UTF-8 -cp "lib/*" Main.java
java -cp ".;lib/*" MainLinux/macOS 的 classpath 分隔符为冒号:
javac -encoding UTF-8 -cp "lib/*" Main.java
java -cp ".:lib/*" MainIntelliJ IDEA 可在 File → Project Structure → Modules → Dependencies → + → JARs or directories 中一次选择 dist/lib 下全部 JAR;Eclipse 可在 Build Path → Add External JARs 中添加同一组文件。
准备好 JDK 8 或更高版本即可,无需另装 Maven。仓库自带的 Maven Wrapper 会在首次构建时自动下载 Maven,因此首次运行需要能够访问 Maven Central。
Windows 发布时,可双击 build-release.bat 使用 pom.xml 中的默认版本,也可指定正式版本号:
build-release.bat 1.0.0脚本会从 JAVA_HOME、PATH 和常见 JDK 安装目录中查找 JDK,运行完整测试和构建,并生成:
dist/
├─ ConsoleTestJava8.jar # 完整可执行示例,已包含依赖
├─ ConsoleTestJava8Simple.jar # 精简可执行示例,已包含依赖
├─ YiKdWebCfg/
├─ SampleFiles/
├─ docs/
├─ lib/
│ ├─ YiKdWebClient.jar # 核心类库
│ └─ jackson-*.jar
└─ YiKdWebClient-Java-<版本号>.zip
java -jar YiKdWebClient.jar 无法运行是正常现象:核心 JAR 没有 Main-Class。要执行示例,请运行 ConsoleTestJava8.jar 或 ConsoleTestJava8Simple.jar。
核心库默认从进程工作目录读取:
YiKdWebCfg/appsettings.xml
相对路径以启动 Java 进程时的工作目录为准,不一定是 JAR 所在目录。先复制示例文件:
New-Item -ItemType Directory -Force .\YiKdWebCfg | Out-Null
Copy-Item .\YiKdWebClient\src\main\resources\YiKdWebCfg\appsettings.example.xml `
.\YiKdWebCfg\appsettings.xml使用 dist 发行目录时:
Copy-Item .\dist\YiKdWebCfg\appsettings.example.xml `
.\dist\YiKdWebCfg\appsettings.xml<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<!-- 请替换为真实数据中心 ID / 账套 ID -->
<add key="X-KDApi-AcctID" value="YOUR_ACCOUNT_ID" />
<!-- 请替换为真实集成用户 -->
<add key="X-KDApi-UserName" value="Administrator" />
<!-- 请替换为真实应用 ID -->
<add key="X-KDApi-AppID" value="YOUR_APP_ID" />
<!-- 请替换为真实应用密钥 -->
<add key="X-KDApi-AppSec" value="123456" />
<!-- 简体中文通常为 2052 -->
<add key="X-KDApi-LCID" value="2052" />
<!-- 多组织场景可填写组织编码 -->
<add key="X-KDApi-OrgNum" value="" />
<!-- 请替换为真实服务地址;私有云通常以 K3Cloud/ 结尾 -->
<add key="X-KDApi-ServerUrl" value="http://127.0.0.1/K3Cloud/" />
</appSettings>
</configuration>| 配置项 | 是否常用 | 说明 |
|---|---|---|
X-KDApi-AcctID |
是 | 数据中心 ID,也称账套 ID。可在第三方系统登录授权页面生成测试链接后查看。 |
X-KDApi-UserName |
是 | 集成用户。PT-146894 [7.7.0.202111] 及后续版本可使用指定用户登录列表中的用户;若授权允许全部用户登录,则不受该列表限制。 |
X-KDApi-AppID |
是 | 第三方系统登录授权的应用 ID。 |
X-KDApi-AppSec |
是 | 第三方系统登录授权的应用密钥。不要使用生产密钥运行公开示例。 |
X-KDApi-LCID |
是 | 账套语系,默认值为 2052。 |
X-KDApi-OrgNum |
否 | 多组织场景中的组织编码,主要用于签名认证模式。 |
X-KDApi-ServerUrl |
是 | 私有云填写产品地址,并以 K3Cloud/ 结尾;使用公有云网关时按官方要求配置。 |
必须在创建 YiK3CloudClient 或 SSOHelper 之前设置路径,因为对象字段初始化时会读取配置:
import YiKdWebClient.CommonService.XmlConfigHelper;
import YiKdWebClient.Model.LoginType;
import YiKdWebClient.YiK3CloudClient;
public class Main {
public static void main(String[] args) {
// 必须先设置路径,再创建会读取默认配置的客户端。
XmlConfigHelper.AppConfigPath =
"D:/configs/kingdee/appsettings.xml";
String formId = "SEC_User";
String json = "{\"IsUserModelInit\":\"true\","
+ "\"Number\":\"Administrator\","
+ "\"IsSortBySeq\":\"false\"}";
try (YiK3CloudClient client = new YiK3CloudClient()) {
client.LoginType = LoginType.LoginBySignSHA256;
String resultJson = client.View(formId, json);
System.out.println("配置路径:" + XmlConfigHelper.AppConfigPath);
System.out.println("表单 ID(formId):" + formId);
System.out.println("业务 JSON 参数(json):" + json);
System.out.println("登录地址:" + client.ReturnLoginWebModel.RequestUrl);
System.out.println("登录请求:" + client.ReturnLoginWebModel.RealRequestBody);
System.out.println("登录响应:" + client.ReturnLoginWebModel.RealResponseBody);
System.out.println("业务地址:" + client.ReturnOperationWebModel.RequestUrl);
System.out.println("业务请求:" + client.ReturnOperationWebModel.RealRequestBody);
System.out.println("业务响应:" + client.ReturnOperationWebModel.RealResponseBody);
System.out.println("View 返回值:" + resultJson);
}
}
}也可以完全不使用 XML,直接构造 AppSettingsModel,见 7.8 动态传入授权信息。
私有云通常配置产品地址并以 K3Cloud/ 结尾;部分公有云环境可能要求通过 https://api.kingdee.com/galaxyapi/ 网关并使用 API 请求头签名。实际地址与认证规则应以目标环境和金蝶官方当前要求为准。各语言客户端均保留普通登录与 API 请求头签名能力。
-
安装 JDK 8 或更高版本,并确认:
java -version -
在仓库根目录构建发行包:
.\mvnw.cmd -B -ntp clean package
-
复制并填写本地配置:
Copy-Item .\dist\YiKdWebCfg\appsettings.example.xml ` .\dist\YiKdWebCfg\appsettings.xml
-
查看全部示例:
.\dist\run-console.cmd help -
运行推荐的 SHA256 签名认证示例:
.\dist\run-console.cmd sign-sha256
Linux/macOS 使用:
./dist/run-console.sh sign-sha256也可以直接运行:
Set-Location .\dist
java -jar ConsoleTestJava8.jar sign-sha256控制台会显示登录请求、登录响应、业务请求、业务响应和方法返回值。HTTP 请求完成不代表业务一定成功,仍需检查返回 JSON 中的 LoginResultType、IsSuccessByAPI、ResponseStatus.IsSuccess、ErrorCode 和 Message。
ConsoleTestJava8.jar 提供以下独立命令,不需要反复修改 Program.java:
| 命令 | 示例 |
|---|---|
sign-sha256 |
SHA256 签名认证 |
sign-sha1 |
SHA1 签名认证 |
app-secret |
第三方系统登录授权 |
validate-login |
旧版用户名密码认证 |
validate-user-endecode |
已弃用的 ValidateUserEnDeCode |
simple-passport |
集成密钥文件认证 |
api-sign-headers |
API 请求头签名认证 |
dynamic-config |
代码动态传入授权信息 |
custom-config-path |
自定义 XML 配置路径 |
custom-webapi |
调用自定义 WebAPI |
sso-v4 |
生成 SSO V4 链接 |
upload-file |
文件路径分块上传 |
upload-progress |
带进度回调的分块上传 |
upload-base64 |
Base64 分块上传 |
统一运行格式:
.\dist\run-console.cmd <命令>这些变量由示例程序读取,不是核心类库的隐式配置:
| 环境变量 | 用途 | 默认值或来源 |
|---|---|---|
YIKD_CONFIG_PATH |
自定义 appsettings.xml 路径 |
dist/YiKdWebCfg/appsettings.xml |
YIKD_CNF_PATH |
自定义 .cnf 集成密钥路径 |
dist/YiKdWebCfg/API测试.cnf |
YIKD_SERVER_URL |
临时覆盖服务地址 | 从 XML 读取 |
YIKD_ACCT_ID、YIKD_USER_NAME |
动态配置的数据中心与用户 | 从 XML 读取 |
YIKD_APP_ID、YIKD_APP_SECRET |
动态配置的应用 ID 与密钥 | 从 XML 读取 |
YIKD_LCID、YIKD_ORG_NUM |
动态配置的语系与组织编码 | 从 XML 读取 |
YIKD_VALIDATE_DBID |
旧版登录数据中心 ID | 从 XML 读取 |
YIKD_VALIDATE_USERNAME |
旧版登录用户名 | demo |
YIKD_VALIDATE_PASSWORD |
旧版登录密码 | 无默认值,必须显式设置 |
YIKD_VALIDATE_LCID |
旧版登录语系 | 2052 |
YIKD_UPLOAD_FILE |
上传示例文件 | dist/SampleFiles/upload-demo.txt |
YIKD_UPLOAD_FORM_ID、YIKD_UPLOAD_INTER_ID、YIKD_UPLOAD_BILL_NO |
上传目标表单和单据 | 示例占位值 |
YIKD_UPLOAD_CHUNK_SIZE |
上传分块字节数 | 2 * 1024 * 1024 |
YIKD_CUSTOM_SQL |
自定义 WebAPI 示例 SQL | 示例查询语句 |
本 README 中的每个 Java 代码块都按独立 Main.java 编写,包含自身所需的 import、入口、变量、客户端初始化、资源释放和结果输出,不依赖前一个代码块。复制后只需替换目标环境配置、业务参数和文件路径。
Java 版与 C# LoginType 完整一致,共有 7 个枚举值:6 种可选认证模式,以及 1 种只为旧系统保留的兼容模式。
LoginType |
用途 | 是否先登录 | 建议 |
|---|---|---|---|
LoginBySignSHA256 |
SHA256 签名信息认证 | 是 | 支持 SHA256 的环境优先使用 |
LoginBySignSHA1 |
SHA1 签名信息认证 | 是 | 仅用于兼容旧版本 |
LoginByAppSecret |
第三方系统登录授权 | 是 | 按目标环境授权方式选择 |
LoginByApiSignHeaders |
每个业务请求独立生成 API 签名请求头 | 否 | 使用前确认目标环境/网关支持 |
ValidateLogin |
旧版用户名密码认证 | 是 | 旧系统兼容,不建议新系统优先使用 |
LoginBySimplePassport |
CNF 文件或 Base64 集成密钥认证 | 是 | 集成密钥场景 |
ValidateUserEnDeCode |
已弃用的旧式用户名密码编码兼容 | 是 | 仅保留旧场景兼容 |
「7 个枚举值」不等于 7 种推荐方案。新项目通常从 LoginBySignSHA256、LoginByAppSecret 或目标网关要求的 LoginByApiSignHeaders 中选择。
下面 6 个常用模式都已经在代码中实现并接入 YiK3CloudClient。控制台输出与客户端字段对应关系如下:
| 输出内容 | Java 字段或变量 |
|---|---|
| 登录请求地址/请求体/响应体 | client.ReturnLoginWebModel.RequestUrl/RealRequestBody/RealResponseBody |
| 业务请求地址/请求体/响应体 | client.ReturnOperationWebModel.RequestUrl/RealRequestBody/RealResponseBody |
| API 签名请求头 | client.RequestHeadersString |
| 方法返回值 | client.View(...) 等方法的直接返回值 |
支持 SHA256 的金蝶云星空版本优先使用此方式。下面代码可以作为独立 Main.java:
import YiKdWebClient.CommonService.XmlConfigHelper;
import YiKdWebClient.Model.LoginType;
import YiKdWebClient.YiK3CloudClient;
import java.nio.file.Paths;
public class Main {
public static void main(String[] args) {
XmlConfigHelper.AppConfigPath = Paths.get(
"YiKdWebCfg", "appsettings.xml").toAbsolutePath().toString();
String formId = "SEC_User";
String json = "{\"IsUserModelInit\":\"true\","
+ "\"Number\":\"Administrator\","
+ "\"IsSortBySeq\":\"false\"}";
try (YiK3CloudClient client = new YiK3CloudClient()) {
client.LoginType = LoginType.LoginBySignSHA256;
String resultJson = client.View(formId, json);
String loginRequestUrl = client.ReturnLoginWebModel.RequestUrl;
String loginRequestBody = client.ReturnLoginWebModel.RealRequestBody;
String loginResponseBody = client.ReturnLoginWebModel.RealResponseBody;
String operationRequestUrl = client.ReturnOperationWebModel.RequestUrl;
String operationRequestBody = client.ReturnOperationWebModel.RealRequestBody;
String operationResponseBody = client.ReturnOperationWebModel.RealResponseBody;
System.out.println("表单 ID(formId):" + formId);
System.out.println("业务 JSON 参数(json):" + json);
System.out.println("登录地址:" + loginRequestUrl);
System.out.println("登录请求:" + loginRequestBody);
System.out.println("登录响应:" + loginResponseBody);
System.out.println("业务地址:" + operationRequestUrl);
System.out.println("业务请求:" + operationRequestBody);
System.out.println("业务响应:" + operationResponseBody);
System.out.println("View 返回值:" + resultJson);
}
}
}运行仓库示例:
.\dist\run-console.cmd sign-sha256PT-146911 8.0.0.202205 之前的版本不支持 SHA256 时,可切换为 SHA1。其余配置和调用方式相同:
import YiKdWebClient.CommonService.XmlConfigHelper;
import YiKdWebClient.Model.LoginType;
import YiKdWebClient.YiK3CloudClient;
public class Main {
public static void main(String[] args) {
XmlConfigHelper.AppConfigPath = "YiKdWebCfg/appsettings.xml";
String formId = "SEC_User";
String json = "{\"IsUserModelInit\":\"true\","
+ "\"Number\":\"Administrator\","
+ "\"IsSortBySeq\":\"false\"}";
try (YiK3CloudClient client = new YiK3CloudClient()) {
client.LoginType = LoginType.LoginBySignSHA1;
String resultJson = client.View(formId, json);
System.out.println("表单 ID(formId):" + formId);
System.out.println("业务 JSON 参数(json):" + json);
System.out.println("登录地址:" + client.ReturnLoginWebModel.RequestUrl);
System.out.println("登录请求:" + client.ReturnLoginWebModel.RealRequestBody);
System.out.println("登录响应:" + client.ReturnLoginWebModel.RealResponseBody);
System.out.println("业务地址:" + client.ReturnOperationWebModel.RequestUrl);
System.out.println("业务请求:" + client.ReturnOperationWebModel.RealRequestBody);
System.out.println("业务响应:" + client.ReturnOperationWebModel.RealResponseBody);
System.out.println("View 返回值:" + resultJson);
}
}
}构造并执行 SSO V4 登出:
import YiKdWebClient.CommonService.XmlConfigHelper;
import YiKdWebClient.SSO.SSOHelper;
import YiKdWebClient.SSO.SSOLogoutObject;
public class Main {
public static void main(String[] args) {
XmlConfigHelper.AppConfigPath =
"D:/configs/kingdee/appsettings.xml";
String userName = "Administrator";
SSOHelper helper = new SSOHelper();
SSOLogoutObject logoutRequest =
helper.GetSSOLogoutap0StrV4(userName);
String logoutResponse = helper.SSOExcuteLogout(logoutRequest);
System.out.println("登出用户名:" + userName);
System.out.println("登出地址:" + logoutRequest.RequestLogoutUrl);
System.out.println("登出响应:" + logoutResponse);
}
}V3、V2/V1 分别使用 GetSSOLogoutap0StrV3 和 GetSSOLogoutap0StrV2V1。SSO URL 和签名参数属于敏感登录材料,不应写入公开日志。
.\dist\run-console.cmd sign-sha1该方式读取数据中心 ID、集成用户、应用 ID、应用密钥和语系:
import YiKdWebClient.CommonService.XmlConfigHelper;
import YiKdWebClient.Model.LoginType;
import YiKdWebClient.YiK3CloudClient;
public class Main {
public static void main(String[] args) {
XmlConfigHelper.AppConfigPath = "YiKdWebCfg/appsettings.xml";
String formId = "SEC_User";
String json = "{\"IsUserModelInit\":\"true\","
+ "\"Number\":\"Administrator\","
+ "\"IsSortBySeq\":\"false\"}";
try (YiK3CloudClient client = new YiK3CloudClient()) {
client.LoginType = LoginType.LoginByAppSecret;
String resultJson = client.View(formId, json);
String loginRequestBody = client.ReturnLoginWebModel.RealRequestBody;
String appSecret = client.AppSettingsModel.XKDApiAppSec;
System.out.println("数据中心:" + client.AppSettingsModel.XKDApiAcctID);
System.out.println("集成用户:" + client.AppSettingsModel.XKDApiUserName);
System.out.println("应用 ID:" + client.AppSettingsModel.XKDApiAppID);
System.out.println("应用密钥:" + appSecret);
System.out.println("表单 ID(formId):" + formId);
System.out.println("业务 JSON 参数(json):" + json);
System.out.println("登录地址:" + client.ReturnLoginWebModel.RequestUrl);
System.out.println("登录请求:" + loginRequestBody);
System.out.println("登录响应:" + client.ReturnLoginWebModel.RealResponseBody);
System.out.println("业务地址:" + client.ReturnOperationWebModel.RequestUrl);
System.out.println("业务请求:" + client.ReturnOperationWebModel.RealRequestBody);
System.out.println("业务响应:" + client.ReturnOperationWebModel.RealResponseBody);
System.out.println("View 返回值:" + resultJson);
}
}
}示例有意完整打印 ReturnLoginWebModel.RealRequestBody,便于核对第三方登录请求体;复制后请先把配置文件中的认证占位值替换为目标环境的真实值。
.\dist\run-console.cmd app-secret该模式不依赖 appsettings.xml 中的应用 ID 和应用密钥,但需要服务地址、数据中心 ID、用户名、密码和语系。除兼容旧系统外,不建议新项目优先使用用户名密码方式。
import YiKdWebClient.Model.LoginType;
import YiKdWebClient.Model.ValidateLoginSettingsModel;
import YiKdWebClient.YiK3CloudClient;
public class Main {
public static void main(String[] args) {
String formId = "SEC_User";
String json = "{\"IsUserModelInit\":\"true\","
+ "\"Number\":\"Administrator\","
+ "\"IsSortBySeq\":\"false\"}";
String serverUrl = "http://127.0.0.1/K3Cloud/"; // 请替换为真实服务地址
String dataCenterId = "6979b9812f3f89"; // 请替换为真实数据中心 ID
String userName = "demo"; // 请替换为真实用户名
String password = "123456"; // 请替换为该用户的真实密码
int localeId = 2052;
try (YiK3CloudClient client = new YiK3CloudClient()) {
client.LoginType = LoginType.ValidateLogin;
ValidateLoginSettingsModel login =
new ValidateLoginSettingsModel(serverUrl);
login.DbId = dataCenterId;
login.UserName = userName;
login.Password = password;
login.lcid = localeId;
client.validateLoginSettingsModel = login;
String resultJson = client.View(formId, json);
String loginRequestUrl = client.ReturnLoginWebModel.RequestUrl;
String loginRequestBody = client.ReturnLoginWebModel.RealRequestBody;
String loginResponseBody = client.ReturnLoginWebModel.RealResponseBody;
String operationRequestUrl = client.ReturnOperationWebModel.RequestUrl;
String operationRequestBody =
client.ReturnOperationWebModel.RealRequestBody;
String operationResponseBody =
client.ReturnOperationWebModel.RealResponseBody;
System.out.println("服务地址(serverUrl):" + serverUrl);
System.out.println("数据中心 ID(dataCenterId):" + dataCenterId);
System.out.println("用户名(userName):" + userName);
System.out.println("密码(password):" + password);
System.out.println("语系(localeId):" + localeId);
System.out.println("表单 ID(formId):" + formId);
System.out.println("业务 JSON 参数(json):" + json);
System.out.println("登录请求地址(loginRequestUrl):" + loginRequestUrl);
System.out.println(
"登录请求报文(loginRequestBody):"
+ loginRequestBody);
System.out.println("登录返回报文(loginResponseBody):" + loginResponseBody);
System.out.println("业务请求地址(operationRequestUrl):" + operationRequestUrl);
System.out.println("业务请求报文(operationRequestBody):" + operationRequestBody);
System.out.println("业务返回报文(operationResponseBody):" + operationResponseBody);
System.out.println("View 方法返回值(resultJson):" + resultJson);
}
}
}把代码复制到业务项目的 Main.java 后即可运行,不要求使用环境变量。123456 仅用于说明密码变量应填写在哪里,接入时必须替换成目标环境中 userName 对应用户的真实密码。
仓库自带的示例运行器为了避免把真实密码写入版本控制,仍使用环境变量:
$env:YIKD_VALIDATE_PASSWORD = '<目标环境中 demo 用户的真实密码>'
.\dist\run-console.cmd validate-login
Remove-Item Env:\YIKD_VALIDATE_PASSWORD截图中的旧版登录使用本地 MOCK-* 回环配置,用户名为 demo;密码在展示前已替换为 ******,不会包含示例密码或真实测试密码。
.cnf 必须由目标金蝶环境生成,并与服务地址、数据中心匹配。文件方式:
import YiKdWebClient.Model.LoginBySimplePassportModel;
import YiKdWebClient.Model.LoginType;
import YiKdWebClient.YiK3CloudClient;
import java.nio.file.Paths;
public class Main {
public static void main(String[] args) {
String serverUrl = "http://127.0.0.1/K3Cloud/"; // 请替换为真实服务地址
// 请替换为目标环境生成的真实 CNF 文件。
String cnfPath = Paths.get(
"YiKdWebCfg", "API测试.cnf").toAbsolutePath().toString();
String formId = "SEC_User";
String json = "{\"IsUserModelInit\":\"true\","
+ "\"Number\":\"Administrator\","
+ "\"IsSortBySeq\":\"false\"}";
try (YiK3CloudClient client = new YiK3CloudClient()) {
client.LoginType = LoginType.LoginBySimplePassport;
LoginBySimplePassportModel passport =
new LoginBySimplePassportModel(serverUrl);
passport.CnfFilePath = cnfPath;
client.LoginBySimplePassportModel = passport;
String resultJson = client.View(formId, json);
String loginRequestBody = client.ReturnLoginWebModel.RealRequestBody;
System.out.println("服务地址:" + serverUrl);
System.out.println("集成密钥文件:" + cnfPath);
System.out.println("表单 ID(formId):" + formId);
System.out.println("业务 JSON 参数(json):" + json);
System.out.println("登录地址:" + client.ReturnLoginWebModel.RequestUrl);
System.out.println("登录请求:" + loginRequestBody);
System.out.println("登录响应:" + client.ReturnLoginWebModel.RealResponseBody);
System.out.println("业务地址:" + client.ReturnOperationWebModel.RequestUrl);
System.out.println("业务请求:" + client.ReturnOperationWebModel.RealRequestBody);
System.out.println("业务响应:" + client.ReturnOperationWebModel.RealResponseBody);
System.out.println("View 返回值:" + resultJson);
}
}
}如果集成密钥已经由安全存储读取为 Base64,不必落地 .cnf 文件:
import YiKdWebClient.Model.BySimplePassportType;
import YiKdWebClient.Model.LoginBySimplePassportModel;
import YiKdWebClient.Model.LoginType;
import YiKdWebClient.YiK3CloudClient;
public class Main {
public static void main(String[] args) {
String serverUrl = "http://127.0.0.1/K3Cloud/"; // 请替换为真实服务地址
String base64Passport =
"请替换为真实 CNF 的 Base64 内容"; // 请替换为目标环境的真实值
String formId = "SEC_User";
String json = "{\"IsUserModelInit\":\"true\","
+ "\"Number\":\"Administrator\","
+ "\"IsSortBySeq\":\"false\"}";
try (YiK3CloudClient client = new YiK3CloudClient()) {
client.LoginType = LoginType.LoginBySimplePassport;
LoginBySimplePassportModel passport =
new LoginBySimplePassportModel(serverUrl);
passport.bySimplePassportType = BySimplePassportType.ForBase64;
passport.SimplePassportForBase64 = base64Passport;
passport.Lcid = 2052;
client.LoginBySimplePassportModel = passport;
String resultJson = client.View(formId, json);
String loginRequestBody = client.ReturnLoginWebModel.RealRequestBody;
System.out.println("服务地址:" + serverUrl);
System.out.println("Base64 集成密钥:" + base64Passport);
System.out.println("表单 ID(formId):" + formId);
System.out.println("业务 JSON 参数(json):" + json);
System.out.println("登录地址:" + client.ReturnLoginWebModel.RequestUrl);
System.out.println("登录请求:" + loginRequestBody);
System.out.println("登录响应:" + client.ReturnLoginWebModel.RealResponseBody);
System.out.println("业务地址:" + client.ReturnOperationWebModel.RequestUrl);
System.out.println("业务请求:" + client.ReturnOperationWebModel.RealRequestBody);
System.out.println("业务响应:" + client.ReturnOperationWebModel.RealResponseBody);
System.out.println("View 返回值:" + resultJson);
}
}
}文件和 Base64 是同一个 LoginBySimplePassport 的两种密钥来源,不额外计为两个 LoginType。
.\dist\run-console.cmd simple-passport该模式不会先调用登录接口,而是直接给每个业务请求生成签名请求头,因此能减少一次 Web 请求。生产使用前应确认目标金蝶版本仍支持对应算法和请求头。
import YiKdWebClient.CommonService.XmlConfigHelper;
import YiKdWebClient.Model.LoginType;
import YiKdWebClient.YiK3CloudClient;
public class Main {
public static void main(String[] args) {
XmlConfigHelper.AppConfigPath = "YiKdWebCfg/appsettings.xml";
String formId = "SEC_User";
String json = "{\"IsUserModelInit\":\"true\","
+ "\"Number\":\"Administrator\","
+ "\"IsSortBySeq\":\"false\"}";
try (YiK3CloudClient client = new YiK3CloudClient()) {
client.LoginType = LoginType.LoginByApiSignHeaders;
String resultJson = client.View(formId, json);
// 此模式没有独立登录请求,认证信息位于业务请求头。
System.out.println("表单 ID(formId):" + formId);
System.out.println("业务 JSON 参数(json):" + json);
System.out.println("签名请求头:\n" + client.RequestHeadersString);
System.out.println("业务地址:" + client.ReturnOperationWebModel.RequestUrl);
System.out.println("业务请求:" + client.ReturnOperationWebModel.RealRequestBody);
System.out.println("业务响应:" + client.ReturnOperationWebModel.RealResponseBody);
System.out.println("View 返回值:" + resultJson);
}
}
}.\dist\run-console.cmd api-sign-headers适用于配置来自数据库、配置中心,或同一服务连接多个账套的场景。为保证代码可直接复制,下面先把全部认证项定义为本地变量:
import YiKdWebClient.Model.AppSettingsModel;
import YiKdWebClient.Model.LoginType;
import YiKdWebClient.YiK3CloudClient;
public class Main {
public static void main(String[] args) {
String dataCenterId = "YOUR_ACCOUNT_ID"; // 请替换为真实数据中心 ID
String integrationUser = "Administrator"; // 请替换为真实集成用户
String appId = "YOUR_APP_ID"; // 请替换为真实应用 ID
String appSecret = "123456"; // 请替换为真实应用密钥
String localeId = "2052"; // 请按目标环境语系替换
String organizationNumber = "100"; // 请替换为真实组织编码;不需要时留空
String serverUrl = "http://127.0.0.1/K3Cloud/"; // 请替换为真实服务地址
AppSettingsModel settings = new AppSettingsModel();
settings.XKDApiAcctID = dataCenterId;
settings.XKDApiUserName = integrationUser;
settings.XKDApiAppID = appId;
settings.XKDApiAppSec = appSecret;
settings.XKDApiLCID = localeId;
settings.XKDApiOrgNum = organizationNumber;
settings.setXKDApiServerUrl(serverUrl);
String formId = "SEC_User";
String json = "{\"IsUserModelInit\":\"true\","
+ "\"Number\":\"Administrator\","
+ "\"IsSortBySeq\":\"false\"}";
try (YiK3CloudClient client = new YiK3CloudClient()) {
client.AppSettingsModel = settings;
client.LoginType = LoginType.LoginByAppSecret;
String resultJson = client.View(formId, json);
String loginRequestBody = client.ReturnLoginWebModel.RealRequestBody;
System.out.println("数据中心 ID:" + settings.XKDApiAcctID);
System.out.println("集成用户:" + settings.XKDApiUserName);
System.out.println("应用 ID:" + settings.XKDApiAppID);
System.out.println("应用密钥:" + settings.XKDApiAppSec);
System.out.println("语系:" + settings.XKDApiLCID);
System.out.println("组织编码:" + settings.XKDApiOrgNum);
System.out.println("服务地址:" + settings.getXKDApiServerUrl());
System.out.println("表单 ID(formId):" + formId);
System.out.println("业务 JSON 参数(json):" + json);
System.out.println("登录地址:" + client.ReturnLoginWebModel.RequestUrl);
System.out.println("登录请求:" + loginRequestBody);
System.out.println("登录响应:" + client.ReturnLoginWebModel.RealResponseBody);
System.out.println("业务地址:" + client.ReturnOperationWebModel.RequestUrl);
System.out.println("业务请求:" + client.ReturnOperationWebModel.RealRequestBody);
System.out.println("业务响应:" + client.ReturnOperationWebModel.RealResponseBody);
System.out.println("View 返回值:" + resultJson);
}
}
}.\dist\run-console.cmd dynamic-config必须先设置路径,再创建客户端:
import YiKdWebClient.CommonService.XmlConfigHelper;
import YiKdWebClient.Model.LoginType;
import YiKdWebClient.YiK3CloudClient;
public class Main {
public static void main(String[] args) {
XmlConfigHelper.AppConfigPath =
"D:/configs/kingdee/appsettings.xml";
String formId = "SEC_User";
String json = "{\"IsUserModelInit\":\"true\","
+ "\"Number\":\"Administrator\","
+ "\"IsSortBySeq\":\"false\"}";
try (YiK3CloudClient client = new YiK3CloudClient()) {
client.LoginType = LoginType.LoginBySignSHA256;
String resultJson = client.View(formId, json);
System.out.println("配置路径:" + XmlConfigHelper.AppConfigPath);
System.out.println("表单 ID(formId):" + formId);
System.out.println("业务 JSON 参数(json):" + json);
System.out.println("登录地址:" + client.ReturnLoginWebModel.RequestUrl);
System.out.println("登录请求:" + client.ReturnLoginWebModel.RealRequestBody);
System.out.println("登录响应:" + client.ReturnLoginWebModel.RealResponseBody);
System.out.println("业务地址:" + client.ReturnOperationWebModel.RequestUrl);
System.out.println("业务请求:" + client.ReturnOperationWebModel.RealRequestBody);
System.out.println("业务响应:" + client.ReturnOperationWebModel.RealResponseBody);
System.out.println("View 返回值:" + resultJson);
}
}
}$env:YIKD_CONFIG_PATH = 'D:\configs\kingdee\appsettings.xml'
.\dist\run-console.cmd custom-config-pathCaution
ValidateUserEnDeCode 已通过 @Deprecated 标记为弃用。它会对用户名和密码执行可逆的旧式 DES 兼容编码,并调用 Kingdee.BOS.WebApi.ServicesStub.AuthService.ValidateUserEnDeCode.common.kdsvc。金蝶官方通用 WebAPI 登录说明未推荐这种方式,项目仅为曾经出现过的旧版本附件等历史场景保留兼容实现。编码后的密码仍然必须按密码本身保护;新项目请优先使用 SHA256 签名认证或当前环境支持的其他认证方式。
该模式与普通 ValidateLogin 使用相同的 ValidateLoginSettingsModel,区别是把 LoginType 设置为 LoginType.ValidateUserEnDeCode。下面代码包含全部 import、main 入口、认证参数、业务调用、真实请求/响应读取和资源释放,可直接保存为 Main.java:
import YiKdWebClient.Model.LoginType;
import YiKdWebClient.Model.ValidateLoginSettingsModel;
import YiKdWebClient.YiK3CloudClient;
public class Main {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
String serverUrl = "http://127.0.0.1/K3Cloud/"; // 请替换为真实服务地址
String dataCenterId = "6979b9812f3f89"; // 请替换为真实数据中心 ID
String userName = "demo"; // 请替换为真实用户名
String password = "123456"; // 请替换为该用户的真实密码
int localeId = 2052;
String formId = "SEC_User";
String json = "{\"IsUserModelInit\":\"true\","
+ "\"Number\":\"Administrator\","
+ "\"IsSortBySeq\":\"false\"}";
ValidateLoginSettingsModel login =
new ValidateLoginSettingsModel(serverUrl);
login.DbId = dataCenterId;
login.UserName = userName;
login.Password = password;
login.lcid = localeId;
try (YiK3CloudClient client = new YiK3CloudClient()) {
client.LoginType = LoginType.ValidateUserEnDeCode;
client.validateLoginSettingsModel = login;
String resultJson = client.View(formId, json);
String compatibilityMode = client.LoginType.toString();
String loginRequestUrl = client.ReturnLoginWebModel.RequestUrl;
String loginRequestBody = client.ReturnLoginWebModel.RealRequestBody;
String loginResponseBody = client.ReturnLoginWebModel.RealResponseBody;
String operationRequestUrl = client.ReturnOperationWebModel.RequestUrl;
String operationRequestBody = client.ReturnOperationWebModel.RealRequestBody;
String operationResponseBody = client.ReturnOperationWebModel.RealResponseBody;
System.out.println("兼容模式(compatibilityMode):" + compatibilityMode);
System.out.println("服务地址(serverUrl):" + serverUrl);
System.out.println("数据中心 ID(dataCenterId):" + dataCenterId);
System.out.println("用户名(userName):" + userName);
System.out.println("密码(password):" + password);
System.out.println("语系(localeId):" + localeId);
System.out.println("表单 ID(formId):" + formId);
System.out.println("业务 JSON 参数(json):" + json);
System.out.println("登录请求地址(loginRequestUrl):" + loginRequestUrl);
System.out.println("登录请求报文(loginRequestBody):" + loginRequestBody);
System.out.println("登录返回报文(loginResponseBody):" + loginResponseBody);
System.out.println("业务请求地址(operationRequestUrl):" + operationRequestUrl);
System.out.println("业务请求报文(operationRequestBody):" + operationRequestBody);
System.out.println("业务返回报文(operationResponseBody):" + operationResponseBody);
System.out.println("View 方法返回值(resultJson):" + resultJson);
}
}
}在仓库根目录中,可以直接编译并运行这份独立代码:
javac -encoding UTF-8 -cp ".\dist\ConsoleTestJava8.jar" .\Main.java
java -cp ".;.\dist\ConsoleTestJava8.jar" Main仓库内置的真实环境验证命令如下。运行器从环境变量读取实际测试密码,不会把密码写入源码;运行结束后请删除当前 PowerShell 会话中的临时变量:
$env:YIKD_VALIDATE_PASSWORD = '<替换为目标环境的实际测试密码>'
.\dist\run-console.cmd validate-user-endecode
Remove-Item Env:\YIKD_VALIDATE_PASSWORD123456 仅用于说明密码变量应填写在哪里,接入时必须替换成 userName 对应用户的真实密码。下图由 Java 运行器与临时回环 HTTP 服务真实执行后生成;明文密码及其可逆旧式编码值均已脱敏。
传给客户端方法的 JSON 与金蝶官方接口要求的业务参数一致,客户端负责包装外层 HTTP 报文。例如查看用户:
import YiKdWebClient.CommonService.XmlConfigHelper;
import YiKdWebClient.Model.LoginType;
import YiKdWebClient.YiK3CloudClient;
public class Main {
public static void main(String[] args) {
XmlConfigHelper.AppConfigPath = "YiKdWebCfg/appsettings.xml";
String formId = "SEC_User";
String json = "{\"IsUserModelInit\":\"true\","
+ "\"Number\":\"Administrator\","
+ "\"IsSortBySeq\":\"false\"}";
try (YiK3CloudClient client = new YiK3CloudClient()) {
client.LoginType = LoginType.LoginBySignSHA256;
String resultJson = client.View(formId, json);
System.out.println("表单 ID:" + formId);
System.out.println("业务 JSON 参数:" + json);
System.out.println("登录地址:" + client.ReturnLoginWebModel.RequestUrl);
System.out.println("登录请求:" + client.ReturnLoginWebModel.RealRequestBody);
System.out.println("登录响应:" + client.ReturnLoginWebModel.RealResponseBody);
System.out.println("业务地址:" + client.ReturnOperationWebModel.RequestUrl);
System.out.println("业务请求:" + client.ReturnOperationWebModel.RealRequestBody);
System.out.println("业务响应:" + client.ReturnOperationWebModel.RealResponseBody);
System.out.println("View 返回值:" + resultJson);
}
}
}| 方法 | 用途 |
|---|---|
View |
查看单据或基础资料 |
Save、BatchSave、Draft、GroupSave、FlexSave |
保存、批量保存、暂存、分组保存、弹性域保存 |
Submit、Audit、UnAudit、Delete、GroupDelete |
提交、审核、反审核、删除和分组删除 |
ExecuteOperation、Push、Allocate、CancelAllocate、CancelAssign、Disassembly |
通用操作、下推、分配、取消和拆单 |
ExecuteBillQuery、GetSysReportData、QueryBusinessInfo、QueryGroupInfo |
单据查询、报表和业务信息查询 |
SendMsg、SwitchOrg、WorkflowAudit |
消息、组织切换和工作流审批 |
AttachmentUpLoad、AttachmentDownLoad、UploadFile |
原始附件/文件服务接口 |
CustomBusinessService、CustomBusinessServiceByParameters |
自定义 WebAPI |
GetDataCenterList |
获取数据中心列表 |
完整方法、重载和服务路径见 API 对照文档。
大部分业务方法都有以下重载:View(formId, json) 会自动登录和登出;View(formId, json, autoLogin) 可控制是否登录,调用后仍自动登出;View(formId, json, autoLogin, autoLogout) 可分别控制登录与登出。
连续调用多个接口时,可以复用同一 Cookie 会话:
import YiKdWebClient.CommonService.XmlConfigHelper;
import YiKdWebClient.Model.LoginType;
import YiKdWebClient.YiK3CloudClient;
public class Main {
public static void main(String[] args) {
XmlConfigHelper.AppConfigPath = "YiKdWebCfg/appsettings.xml";
try (YiK3CloudClient client = new YiK3CloudClient()) {
client.LoginType = LoginType.LoginBySignSHA256;
client.Login();
String loginRequestUrl = client.ReturnLoginWebModel.RequestUrl;
String loginRequestBody = client.ReturnLoginWebModel.RealRequestBody;
String loginResponseBody = client.ReturnLoginWebModel.RealResponseBody;
try {
String userFormId = "SEC_User";
String userPayload = "{\"IsUserModelInit\":\"true\","
+ "\"Number\":\"Administrator\","
+ "\"IsSortBySeq\":\"false\"}";
String userJson = client.View(
userFormId,
userPayload,
false,
false);
String userRequestUrl = client.ReturnOperationWebModel.RequestUrl;
String userRequestBody = client.ReturnOperationWebModel.RealRequestBody;
String userResponseBody = client.ReturnOperationWebModel.RealResponseBody;
String materialPayload = "{\"FormId\":\"BD_MATERIAL\","
+ "\"FieldKeys\":\"FNumber,FName\","
+ "\"FilterString\":\"\","
+ "\"OrderString\":\"\","
+ "\"TopRowCount\":0,"
+ "\"StartRow\":0,"
+ "\"Limit\":10}";
String materialJson = client.ExecuteBillQuery(
materialPayload,
false,
false);
String materialRequestUrl = client.ReturnOperationWebModel.RequestUrl;
String materialRequestBody = client.ReturnOperationWebModel.RealRequestBody;
String materialResponseBody = client.ReturnOperationWebModel.RealResponseBody;
System.out.println("登录请求地址:" + loginRequestUrl);
System.out.println("登录请求报文:" + loginRequestBody);
System.out.println("登录返回报文:" + loginResponseBody);
System.out.println("用户表单 ID:" + userFormId);
System.out.println("用户业务 JSON:" + userPayload);
System.out.println("用户业务请求地址:" + userRequestUrl);
System.out.println("用户业务请求报文:" + userRequestBody);
System.out.println("用户业务返回报文:" + userResponseBody);
System.out.println("用户 View 返回值:" + userJson);
System.out.println("物料查询 JSON:" + materialPayload);
System.out.println("物料业务请求地址:" + materialRequestUrl);
System.out.println("物料业务请求报文:" + materialRequestBody);
System.out.println("物料业务返回报文:" + materialResponseBody);
System.out.println("物料 ExecuteBillQuery 返回值:" + materialJson);
} finally {
client.Logout();
}
}
}
}close()/Dispose() 只释放客户端状态,不代替 HTTP Logout()。客户端包含可变 Cookie、请求头和最近请求状态,不要让多个线程并发共享同一个实例。
项目支持 SSO V1、V2、V3 和 V4。下面生成 V4 的 HTML5、Silverlight 和 WPF 入口;生成 URL 本身不会发送 HTTP 请求:
import YiKdWebClient.CommonService.XmlConfigHelper;
import YiKdWebClient.SSO.SSOHelper;
import YiKdWebClient.SSO.SSOLoginUrlObject;
public class Main {
public static void main(String[] args) {
XmlConfigHelper.AppConfigPath =
"D:/configs/kingdee/appsettings.xml";
String userName = "Administrator";
SSOHelper helper = new SSOHelper();
SSOLoginUrlObject urls = helper.GetSsoUrlsV4(userName);
System.out.println("登录用户名:" + userName);
System.out.println("数据中心 ID:" + helper.simplePassportLoginArg.dbid);
System.out.println("应用 ID:" + helper.simplePassportLoginArg.appid);
System.out.println("时间戳:" + helper.timestamp);
System.out.println("签名:" + helper.simplePassportLoginArg.signeddata);
System.out.println("签名参数 JSON:" + helper.argJosn);
System.out.println("Base64 参数:" + helper.argJsonBase64);
System.out.println("HTML5:" + urls.html5Url);
System.out.println("Silverlight:" + urls.silverlightUrl);
System.out.println("WPF:" + urls.wpfUrl);
// 旧版本按目标环境选择:
// helper.GetSsoUrlsV3(userName);
// helper.GetSsoUrlsV2(userName);
// helper.GetSsoUrlsV1(userName);
}
}.\dist\run-console.cmd sso-v4官方自定义 WebAPI 报文格式与参数说明:https://vip.kingdee.com/article/97030089581136896?specialId=448928749460099072&productLineId=1&isKnowledge=2&lang=zh-CN
目标金蝶环境必须先部署服务端自定义 WebAPI。Java 仓库只移植客户端;C# 主项目中的 GlobalServiceCustom.WebApi 是 .NET Framework 4.8 服务端示例,真正部署的是它生成的 GlobalServiceCustom.WebApi.dll,不是 Java 发行包或其编译引用。
客户端提供两类调用:CustomBusinessService 由客户端完成标准外层参数包装;CustomBusinessServiceByParameters 将调用者准备的 JSON 作为原始请求体发送。服务路径既可直接传字符串,也可通过 CustomServicesStubpath 由命名空间、类名和公开方法名生成;这些定位值必须与服务端部署内容完全一致。
Caution
不要把任意用户输入直接拼接到 SQL 或其他高权限服务参数中。服务端必须实施身份授权、参数校验、最小权限和审计。
import YiKdWebClient.CommonService.XmlConfigHelper;
import YiKdWebClient.CommonService.JsonSupport;
import YiKdWebClient.Model.CustomServicesStubpath;
import YiKdWebClient.Model.LoginType;
import YiKdWebClient.YiK3CloudClient;
import java.util.LinkedHashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
XmlConfigHelper.AppConfigPath = "YiKdWebCfg/appsettings.xml";
String sql = "SELECT TOP 10 * FROM T_BD_MATERIAL_L";
Map<String, Object> body = new LinkedHashMap<String, Object>();
body.put("parameters", new String[] { sql });
String json = JsonSupport.serialize(body, true, false);
CustomServicesStubpath service = new CustomServicesStubpath();
service.ProjetNamespace = "GlobalServiceCustom.WebApi";
service.ProjetClassName = "DataServiceHandler";
service.ProjetClassMethod = "CommonRunnerService";
try (YiK3CloudClient client = new YiK3CloudClient()) {
client.LoginType = LoginType.LoginByAppSecret;
String resultJson =
client.CustomBusinessServiceByParameters(json, service);
String loginRequestBody = client.ReturnLoginWebModel.RealRequestBody;
System.out.println("服务端命名空间:" + service.ProjetNamespace);
System.out.println("服务端类名:" + service.ProjetClassName);
System.out.println("服务端方法名:" + service.ProjetClassMethod);
System.out.println("SQL 参数:" + sql);
System.out.println("接口参数 JSON:" + json);
System.out.println("登录地址:" + client.ReturnLoginWebModel.RequestUrl);
System.out.println("登录请求:" + loginRequestBody);
System.out.println("登录响应:" + client.ReturnLoginWebModel.RealResponseBody);
System.out.println("业务地址:" + client.ReturnOperationWebModel.RequestUrl);
System.out.println("业务请求:" + client.ReturnOperationWebModel.RealRequestBody);
System.out.println("业务响应:" + client.ReturnOperationWebModel.RealResponseBody);
System.out.println("自定义接口返回值:" + resultJson);
}
}
}命名空间、类名和公开方法名必须与服务器端部署内容完全一致。
.\dist\run-console.cmd custom-webapi官方附件上传报文结构与原理:https://vip.kingdee.com/article/296577252589190400?productLineId=1&isKnowledge=2&lang=zh-CN
附件上传会写入目标业务系统。接入前必须替换真实的表单 ID、单据内码和单据编号,并确认目标环境已配置附件或对象存储。高层封装支持文件路径、分块进度回调和 Base64 数据;每个成功分块返回的 FileId 会自动写回上传模型。
import YiKdWebClient.Model.LoginBySimplePassportModel;
import YiKdWebClient.Model.LoginType;
import YiKdWebClient.ToolsHelper.AttachmentHelper;
import YiKdWebClient.ToolsHelper.UploadModel;
import YiKdWebClient.YiK3CloudClient;
import java.nio.file.Files;
import java.nio.file.Paths;
public class Main {
public static void main(String[] args) {
String serverUrl = "http://127.0.0.1/K3Cloud/"; // 请替换为真实服务地址
String cnfFilePath = "D:/configs/kingdee/API测试.cnf"; // 请替换为真实 CNF 路径
String filePath = "D:/files/upload-demo.txt"; // 请替换为真实待上传文件
String formId = "SAL_SaleOrder";
String interId = "100020";
String billNumber = "XSDD000019";
long chunkSize = 2L * 1024L * 1024L;
if (!Files.isRegularFile(Paths.get(filePath))) {
throw new IllegalArgumentException(
"找不到待上传文件,请修改 filePath:" + filePath);
}
try (YiK3CloudClient client = new YiK3CloudClient()) {
client.LoginType = LoginType.LoginBySimplePassport;
LoginBySimplePassportModel passport =
new LoginBySimplePassportModel(serverUrl);
passport.CnfFilePath = cnfFilePath;
client.LoginBySimplePassportModel = passport;
UploadModel upload = new UploadModel();
upload.data.FormId = formId;
upload.data.InterId = interId;
upload.data.BillNO = billNumber;
String resultJson = AttachmentHelper.AttachmentUploadByFilePath(
filePath,
client,
upload,
chunkSize,
(chunk, currentClient) -> System.out.println(
"已完成分块 " + (chunk.Chunkindex + 1)
+ ",最后一块:" + chunk.IsLast));
String loginRequestBody = client.ReturnLoginWebModel.RealRequestBody;
System.out.println("待上传文件:" + filePath);
System.out.println("目标表单:" + formId);
System.out.println("单据内码:" + interId);
System.out.println("单据编号:" + billNumber);
System.out.println("分块大小:" + chunkSize);
System.out.println("登录请求地址:"
+ client.ReturnLoginWebModel.RequestUrl);
System.out.println("登录请求报文:"
+ loginRequestBody);
System.out.println("登录返回报文:"
+ client.ReturnLoginWebModel.RealResponseBody);
System.out.println("最后一块请求地址:"
+ client.ReturnOperationWebModel.RequestUrl);
System.out.println("最后一块请求:"
+ client.ReturnOperationWebModel.RealRequestBody);
System.out.println("最后一块响应:"
+ client.ReturnOperationWebModel.RealResponseBody);
System.out.println("上传返回值:" + resultJson);
}
}
}不需要进度时,可省略最后一个回调参数。
.\dist\run-console.cmd upload-file
.\dist\run-console.cmd upload-progress已经持有 Base64 文件内容时使用:
import YiKdWebClient.Model.LoginBySimplePassportModel;
import YiKdWebClient.Model.LoginType;
import YiKdWebClient.ToolsHelper.AttachmentHelper;
import YiKdWebClient.ToolsHelper.UploadModel;
import YiKdWebClient.YiK3CloudClient;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
public class Main {
public static void main(String[] args) throws Exception {
String serverUrl = "http://127.0.0.1/K3Cloud/"; // 请替换为真实服务地址
String cnfFilePath = "D:/configs/kingdee/API测试.cnf"; // 请替换为真实 CNF 路径
Path filePath = Paths.get("D:/files/upload-demo.txt"); // 请替换为真实待上传文件
String formId = "SAL_SaleOrder";
String interId = "100020";
String billNumber = "XSDD000019";
long chunkSize = 2L * 1024L * 1024L;
if (!Files.isRegularFile(filePath)) {
throw new IllegalArgumentException(
"找不到待上传文件,请修改 filePath:" + filePath);
}
String base64Data = Base64.getEncoder().encodeToString(
Files.readAllBytes(filePath));
try (YiK3CloudClient client = new YiK3CloudClient()) {
client.LoginType = LoginType.LoginBySimplePassport;
LoginBySimplePassportModel passport =
new LoginBySimplePassportModel(serverUrl);
passport.CnfFilePath = cnfFilePath;
client.LoginBySimplePassportModel = passport;
UploadModel upload = new UploadModel();
upload.data.FormId = formId;
upload.data.InterId = interId;
upload.data.BillNO = billNumber;
String resultJson = AttachmentHelper.AttachmentUploadByBase64(
base64Data,
filePath.getFileName().toString(),
client,
upload,
chunkSize);
String loginRequestBody = client.ReturnLoginWebModel.RealRequestBody;
System.out.println("源文件:" + filePath);
System.out.println("Base64 字符数:" + base64Data.length());
System.out.println("目标表单:" + formId);
System.out.println("单据内码:" + interId);
System.out.println("单据编号:" + billNumber);
System.out.println("分块大小:" + chunkSize);
System.out.println("登录请求地址:"
+ client.ReturnLoginWebModel.RequestUrl);
System.out.println("登录请求报文:"
+ loginRequestBody);
System.out.println("登录返回报文:"
+ client.ReturnLoginWebModel.RealResponseBody);
System.out.println("最后一块请求地址:"
+ client.ReturnOperationWebModel.RequestUrl);
System.out.println("最后一块请求:"
+ client.ReturnOperationWebModel.RealRequestBody);
System.out.println("最后一块响应:"
+ client.ReturnOperationWebModel.RealResponseBody);
System.out.println("上传返回值:" + resultJson);
}
}
}.\dist\run-console.cmd upload-base64| 字段 | 用途 |
|---|---|
FileName |
当前附件文件名,高层封装会按源文件填充 |
FormId |
单据或表单 ID |
InterId |
单据内码 |
Entrykey |
单据体标识;表头附件留空 |
EntryinterId |
单据体内码;表头附件通常使用默认值 -1 |
BillNO |
单据编号 |
AliasFileName |
可选的附件别名 |
FileId |
服务端返回的文件 ID,每个成功分块后自动更新 |
SendByte |
当前分块的 Base64 内容,高层封装自动填充 |
IsLast |
是否为最后一块,高层封装自动填充 |
| 对照项 | C# | Java | Python | Go |
|---|---|---|---|---|
| 获取方式 | NuGet | Maven 本地仓库、Gradle mavenLocal() 或发行 JAR |
源码可编辑安装或构建 wheel | Go Modules 按版本 Tag 获取源码并参与编译 |
| 资源释放 | using / Dispose() |
try (...) / close() |
with / close() |
defer client.Close() |
| Cookie | CookieContainer |
java.net.CookieManager |
requests.cookies.RequestsCookieJar |
net/http/cookiejar |
| 超时 | TimeSpan |
java.time.Duration |
秒数或 timedelta |
time.Duration |
| 集合 | Dictionary<K,V> |
Map<K,V> |
dict |
map[K]V |
| 回调 | Action<T> / Action<T1,T2> |
Consumer<T> / BiConsumer<T1,T2> |
Callable |
func(...) error |
| JSON | System.Text.Json |
Jackson 2.x | 标准库 json |
标准库 encoding/json |
| 公开方法名 | PascalCase | PascalCase | 兼容 PascalCase,并提供常用 snake_case |
导出方法使用 Go 命名,并保留必要兼容别名 |
| 运行时 | .NET 多目标框架 | Java 8 字节码,可运行于较新 JDK | Python 3.9~3.13 | Go 1.22 及以上 |
| 异步边界 | 依具体 HTTP 工具 | 公开业务客户端为同步调用 | 公开业务客户端为同步调用,底层 HTTP 工具有异步包装 | 同步方法支持 context.Context,并发由调用方组织 |
C#、Java、Python、Go 和 PHP 五个语言客户端均已完成适配,核心认证含义、动态表单方法语义和服务路径保持一致;HTTP (JSON) 通用接入也已完成适配,协议报文和字段说明以其仓库 README 为准。上表重点对照 C#、Java、Python 和 Go 的语言差异;PHP 的获取方式、运行时版本、异常模型及 API 映射以 PHP 仓库 README 为准。Java、Python 和 Go 客户端都包含可变 Cookie、请求头和最近请求状态;客户端属于有状态对象,并发请求宜按会话或工作单元创建独立实例。
核心库按进程工作目录解析默认相对路径。请检查 Paths.get("").toAbsolutePath(),或在创建客户端前显式设置 XmlConfigHelper.AppConfigPath。
AppSettingsModel 为兼容 C# 原项目,在配置缺失时会保留空字段而不是立即抛错;因此登录失败时也要先确认实际读取路径。
手工引用时遗漏了 Jackson。把 dist/lib/ 下所有 JAR 加入 classpath,或改用 Maven/Gradle 依赖。核心库 JAR 不会把 Jackson 打包进去。
这是正常现象:它是类库,没有 Main-Class。请在业务项目中引用,或运行 ConsoleTestJava8.jar。
依次检查:
- 服务地址与数据中心是否匹配;
- 集成用户是否在第三方系统登录授权范围内;
- 应用 ID 与应用密钥是否成对;
- 语系和组织编码是否适用;
- 服务器时间是否准确,避免签名时间戳偏差;
LoginBySimplePassport的.cnf是否来自同一目标环境;- 旧版登录是否正确提供了密码。
继续检查 ResponseStatus、用户权限、表单 ID、字段名、单据状态和组织范围。可查看 ReturnOperationWebModel,把实际 URL、请求头和请求体复制到 Postman/ApiPost 对比;输出前必须脱敏。
这是设计行为。LoginByApiSignHeaders 不调用独立登录接口,认证信息在 RequestHeadersString 和业务请求头中。
.cnf 必须由目标环境生成,并与服务地址和数据中心匹配。复制其他环境的文件通常无法登录。也可以把文件内容安全读取为 Base64,使用 BySimplePassportType.ForBase64。
这通常表示请求已到达附件接口,但服务端未正确配置附件/对象存储,或示例中的表单、单据内码和编号不存在。请先完成服务端配置并替换真实参数。
优先使用仓库 Maven Wrapper。项目已针对 JDK 8 在中文 Windows 工作区中的 Surefire classpath 校验做兼容配置:
.\mvnw.cmd -B -ntp clean verify执行全部编译、95 项单元测试和回环 HTTP 测试:
.\mvnw.cmd -B -ntp clean verify自动化测试不需要真实金蝶地址或密钥。修改公开方法、参数顺序、认证报文或服务路径时,请同步更新测试和 docs/API_MAPPING.md。更多约定见 CONTRIBUTING.md、SECURITY.md 和 CHANGELOG.md。
项目地址:
- C# Gitee:https://gitee.com/lnsyzjw/yi-kd-web-client
- C# GitHub:https://github.com/1609676823/YiKdWebClient
- Java Gitee:https://gitee.com/lnsyzjw/yi-kd-web-client-java
- Java GitHub:https://github.com/1609676823/YiKdWebClient-Java
- Python Gitee:https://gitee.com/lnsyzjw/yi-kd-web-client-python
- Python GitHub:https://github.com/1609676823/YiKdWebClient-Python
- Go Gitee:https://gitee.com/lnsyzjw/yi-kd-web-client-go
- Go GitHub:https://github.com/1609676823/YiKdWebClient-Go
- PHP Gitee:https://gitee.com/lnsyzjw/yi-kd-web-client-php
- PHP GitHub:https://github.com/1609676823/YiKdWebClient-PHP
本项目采用 MIT License。你可以在保留版权和许可声明的前提下使用、复制、修改、合并、发布、分发、再许可和销售本软件。软件按“原样”提供,不附带任何明示或默示担保。














