---
name: kd-enterprise-openapi
description: 金蝶AI星空企业版/标准版 OpenAPI（K3Cloud WebAPI）对接技能。语言无关的 HTTP API 协议：认证、会话、查询、保存、提交、审核、下推、操作执行、基础资料分配、工作流审批、报表查询。适用于外部系统集成、数据同步、RPA 自动化。
---

# 金蝶企业版 OpenAPI 对接

当需要**从外部系统调用**金蝶AI星空企业版/标准版的 OpenAPI（K3Cloud WebAPI）时使用本技能。包括：第三方系统集成、数据同步、RPA 自动化、批量数据导入导出。

不要把本技能用于金蝶插件开发（那是 `kd-enterprise-csharp` / `kd-enterprise-python-plugin` 的职责），也不要用于开发自定义 API 接口。

> **本技能描述语言无关的 HTTP API 协议。Python / C# / Java 等语言的 SDK 只是该协议的便捷封装；无 SDK 时直接 HTTP POST JSON 即可。**
## 触发边界

只在任务主体是外部系统/第三方系统/RPA/HTTP/OpenAPI/WebAPI 调用金蝶，或金蝶主动调用外部 HTTP/API 时使用本技能。此时才需要接口文档来源/版本、对接方向和触发时机、认证、超时、重试、限流、幂等、失败补偿、日志审计脱敏、请求/响应样例等事实。

不要把本技能用于以下场景：

- BOS 表单、列表、操作、转换、校验、报表插件开发或修改。
- 报表/账表增加字段、调整列头、扩展取数 SQL、继承现有报表插件构造数据源。
- 项目内部插件、已有服务、当前账套元数据字段验证。

如果用户说「星空企业版缺陷原因分析帐表增加字段」这类需求，应路由 `kd-enterprise-csharp`（或明确 IronPython 时 `kd-enterprise-python-plugin`），不是本技能。

## API 协议概览

K3Cloud WebAPI 是一套基于 HTTP 的 JSON API：

- **传输**：HTTP POST，`Content-Type: application/json`
- **URL 格式**：`{server_url}{ServiceName}.common.kdsvc`
  - `server_url`：金蝶服务器地址，以 `/k3cloud/` 结尾（如 `https://erp.example.com/k3cloud/`）
  - `ServiceName`：全限定服务名（如 `Kingdee.BOS.WebApi.ServicesStub.AuthService.LoginByAppSecret`）
- **请求体**：JSON 数组，元素按服务方法签名顺序排列
- **响应体**：JSON 字符串（查询返回二维数组，操作返回 `Result.ResponseStatus` 结构）
- **会话机制**：为了保持服务端的状态会话，登录后服务端下发 Cookie（`kdservice-sessionid`），后续请求建议复用同一 HTTP 客户端实例，使该 Cookie 能够自动在后续请求中携带。

### 服务名命名规则

| 类别 | 服务名前缀 | 示例 |
|------|-----------|------|
| 认证 | `Kingdee.BOS.WebApi.ServicesStub.AuthService.*` | `...AuthService.LoginByAppSecret` |
| 单据操作 | `Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.*` | `...DynamicFormService.Save` |
| 业务对象操作 | `Kingdee.BOS.WebApi.ServicesStub.BusinessDataService.*` | `...BusinessDataService.ExecuteBillQuery` |
| 自定义服务 | `{Namespace}.{Class}.{Method}` | `{custom_service_name}` |

## 认证与会话

### 认证方式一：应用授权（推荐，第三方对接）

用 `app_id` + `app_secret` 登录，无需用户密码。适合外部系统集成。

```
POST {server_url}Kingdee.BOS.WebApi.ServicesStub.AuthService.LoginByAppSecret.common.kdsvc
Content-Type: application/json

[
  "{acct_id}",      // 账套ID
  "{app_id}",       // 应用ID
  "{app_secret}",   // 应用密钥
  2052              // 语言ID（2052=简体中文，1033=English）
]
```

### 认证方式二：账号密码（内部脚本/管理工具）

用用户名 + 密码登录。适合内部运维脚本。

```
POST {server_url}Kingdee.BOS.WebApi.ServicesStub.AuthService.ValidateUser.common.kdsvc
Content-Type: application/json

[
  "{acct_id}",        // 账套ID
  "{user_name}",      // 用户名
  "{password}",       // 密码
  2052                // 语言ID
]
```

### 登录响应与会话维持

登录成功后：

1. 响应 JSON 包含 `Context`（用户/组织/账套信息）和 `LoginResultType`（1=成功）
2. 响应 Header 中下发 Cookie：`kdservice-sessionid=xxxxx`
3. **Cookie 携带**：为避免发生会话过期或无权限异常，后续的业务请求建议自动或手动带上登录响应下发的 Cookie 标识。

> **最佳实践**：为了保证会话状态在多次请求间持久有效，推荐在各种语言实现中维护一个专用的 Cookie 容器（如 `CookieContainer`、`requests.Session` 或 `http.Client.CookieJar`），以便将登录后的 Cookie 复用于所有后续业务请求。

### 必需配置项

| 字段 | 说明 | 示例 | 必需 |
|------|------|------|------|
| `server_url` | WebAPI 地址，以 `/k3cloud/` 结尾 | `https://erp.example.com/k3cloud/` | 是 |
| `acct_id` | 账套 ID | `{acct_id}` | 是 |
| `app_id` | 应用 ID（应用授权方式） | `{app_id}` | 二选一 |
| `app_secret` | 应用密钥（应用授权方式，**加密存储**） | `{app_secret}` | 二选一 |
| `user_name` | 用户名（密码方式） | `{user_name}` | 二选一 |
| `password` | 密码（密码方式，**加密存储**） | `{password}` | 二选一 |
| `lcid` | 语言 ID | `2052`（简体中文） | 否 |
| `org_num` | 组织编号 | `0` | 否 |

## 核心 API 操作

> 以下所有操作的请求体均为 JSON 数组。`formId` 是表单标识（不是表名），`content` 是业务参数 JSON 字符串。

### 1. 单据查询（ExecuteBillQuery）

查询返回**二维数组**（每行一个单据，每列对应 FieldKeys 顺序）。

```
POST {server_url}Kingdee.BOS.WebApi.ServicesStub.BusinessDataService.ExecuteBillQuery.common.kdsvc

[
  {
    "FormId": "BD_Material",
    "FieldKeys": "FNumber,FName",
    "FilterString": "FNumber = '{material_number}'",
    "OrderString": "FNumber ASC",
    "TopRowCount": 100,
    "StartRow": 0,
    "Limit": 2000
  }
]
```

**响应：**

```json
[
  ["{material_number_1}", "物料A"],
  ["{material_number_2}", "物料B"]
]
```

**关键参数：**

- `FormId`：表单标识。常用值：
  - `BD_Material` 物料、`BD_Customer` 客户、`BD_Supplier` 供应商、`BD_Empinfo` 员工、`BD_Org` 组织
  - `PUR_PurchaseOrder` 采购订单、`SAL_SaleOrder` 销售订单、`STK_InStock` 入库单
  - `AR_receivable` 应收单、`AP_Payable` 应付单
- `FieldKeys`：字段内码（`F` 前缀），逗号分隔，如 `FNumber,FName,FID`
- `FilterString`：KSQL 过滤语法，如 `FDocumentStatus = 'C' AND FDate >= '2026-01-01'`
  - `FDocumentStatus` 状态值：`A`=暂存，`B`=提交，`C`=审核，`D`=重新审核
- `TopRowCount` / `StartRow` / `Limit`：分页控制

### 2. 查看/明细（View）

按编号或内码查询单据完整数据包。

```
POST {server_url}Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.View.common.kdsvc

[
  "{formId}",
  {
    "CreateOrgId": 0,
    "Number": "SO-2026-001",
    "Id": "",
    "IsSortBySeq": "false"
  }
]
```

### 3. 保存（Save）

新建或修改单据。**Save 只保存，不会自动提交/审核**。

```
POST {server_url}Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Save.common.kdsvc

[
  "{formId}",
  {
    "NeedUpDateFields": [],
    "NeedReturnFields": [],
    "IsDeleteEntry": "True",
    "IsVerifyBaseDataField": "False",
    "IsEntryBatchFill": "True",
    "ValidateFlag": "True",
    "NumberSearch": "True",
    "IsAutoAdjustField": "False",
    "Model": {
      "FID": 0,
      "FBillNo": "SO-2026-001",
      "FDate": "2026-06-21",
      "FSaleOrgId": {"FNumber": "{sales_org_number}"},
      "FEntity": [
        {
          "FMaterialId": {"FNumber": "{material_number}"},
          "FQty": 10,
          "FPrice": 100.00
        }
      ]
    }
  }
]
```

**Model 结构要点：**

- `FID`：`0`=新建，非 0=修改已有单据
- 组织/基础资料引用：用 `{"FNumber": "xxx"}` 嵌套对象，不是直接填 ID
- 分录：`FEntity` 数组，每个元素是一行分录
- 修改时 `IsDeleteEntry: "True"` 会先删除原有分录再写入新的

### 4. 删除（Delete）

```
POST {server_url}Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Delete.common.kdsvc

[
  "{formId}",
  {
    "CreateOrgId": 0,
    "Numbers": ["SO-2026-001"],
    "Ids": ""
  }
]
```

`Numbers`（编号数组）和 `Ids`（内码逗号分隔字符串）二选一。

### 5. 提交（Submit）

```
POST {server_url}Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Submit.common.kdsvc

[
  "{formId}",
  {
    "CreateOrgId": 0,
    "Numbers": ["SO-2026-001"],
    "Ids": ""
  }
]
```

### 6. 审核（Audit）/ 反审核（UnAudit）

```
POST {server_url}Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Audit.common.kdsvc
POST {server_url}Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.UnAudit.common.kdsvc

[
  "{formId}",
  {
    "CreateOrgId": 0,
    "Numbers": ["SO-2026-001"],
    "Ids": ""
  }
]
```

### 7. 下推（Push）

源单下推生成目标单（如采购订单 → 入库单）。

```
POST {server_url}Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Push.common.kdsvc

[
  "{source_formId}",
  {
    "Ids": "100001",
    "RuleId": "{configured_rule_id}",
    "TargetFormId": "{target_form_id}",
    "TargetOrgId": 0,
    "CustomParams": {}
  }
]
```

### 8. 执行自定义操作（ExcuteOperation）

执行非标准操作（如"关闭"、"反关闭"、自定义操作）。

```
POST {server_url}Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.ExcuteOperation.common.kdsvc

[
  "{formId}",
  "{op_number}",       // 操作编码，如 "Close"、"CancelClose"
  {
    "CreateOrgId": 0,
    "Numbers": ["SO-2026-001"],
    "Ids": ""
  }
]
```

### 高级操作

以下操作不常用于基础对接，需要时读 [references/advanced-operations.md](references/advanced-operations.md)：

- **基础资料分配/取消分配**（Allocate / CancelAllocate）
- **基础资料分组操作**（GroupSave / GroupDelete / QueryGroupInfo）
- **工作流审批**（WorkflowAudit：通过/驳回/终止）
- **切换组织**（SwitchOrg）
- **查询业务信息**（QueryBusinessInfo：查表单元数据结构）
- **报表查询**（GetSysReportData：标准报表/分页账表）
- **自定义报表查询**（Execute + 当前环境已注册的报表服务）

## 响应格式

### 查询响应

返回 JSON 字符串，解析后为**二维数组**：

```json
[
  ["M.01", "物料A", "个"],
  ["M.02", "物料B", "箱"]
]
```

### 操作响应（Save/Delete/Submit/Audit/Push 等）

返回包含 `Result.ResponseStatus` 的对象：

```json
{
  "Result": {
    "ResponseStatus": {
      "IsSuccess": true,
      "Errors": [],
      "SuccessEntitys": [{"Id": 100001, "Number": "SO-2026-001"}],
      "SuccessMessages": [],
      "MsgCode": 0
    },
    "Id": 100001,
    "Number": "SO-2026-001",
    "NeedReturnData": []
  }
}
```

### 登录响应

```json
{
  "LoginResultType": 1,
  "Context": {
    "UserName": "{user_name}",
    "UserId": 0,
    "OrgId": 0,
    "CurrentOrganizationNumber": "{organization_number}",
    "CultureName": "zh-CN"
  },
  "Message": "登录成功"
}
```

`LoginResultType`：`1`=成功，其他=失败。

### 错误处理

```json
{
  "Result": {
    "ResponseStatus": {
      "IsSuccess": false,
      "Errors": [
        {
          "Code": "BD_001",
          "Message": "物料编码不存在",
          "FieldName": "FMaterialId"
        }
      ]
    }
  }
}
```

## 典型业务流程

### 新建单据完整流程

```
1. 登录（LoginByAppSecret）→ 获取会话 Cookie
2. Save（保存）→ 拿到单据编号/内码（状态 A=暂存）
3. Submit（提交）→ 状态 A→B
4. Audit（审核）→ 状态 B→C
```

### 修改单据完整流程

```
1. 登录
2. UnAudit（反审核）→ 状态 C→B（如已审核）
3. Save（修改保存）→ Model.FID 填已有内码，IsDeleteEntry=True
4. Submit（提交）→ 状态 B
5. Audit（审核）→ 状态 C
```

### 下推流程

```
1. 登录
2. ExecuteBillQuery（查源单）→ 确认状态 = C（已审核）+ 拿内码
3. Push（下推）→ 拿到目标单内码
4. View（查看目标单）→ 确认下推数据
5. Save/Submit/Audit 目标单
```

### 批量数据同步流程

```
1. 登录
2. ExecuteBillQuery（分页查询）→ StartRow + Limit 循环翻页
3. 逐批 Save + Submit + Audit
4. 每批检查 ResponseStatus.IsSuccess
5. 失败批次记录错误码+消息，不阻塞后续批次
```

## 语言实现指引

各语言（Python / C# / Java / Go / Rust）的 SDK 用法和 raw HTTP 调用示例见 [references/language-implementations.md](references/language-implementations.md)。

核心要点（所有语言通用）：
1. 创建带 Cookie 管理的 HTTP 客户端
2. POST 登录请求 → 会话 Cookie 自动保存
3. POST 业务请求（同一客户端实例，自动携带 Cookie）
4. 解析 JSON 响应

## 编码规范

### 安全

- **密钥安全原则**：敏感信息（如 `app_secret` 或 `password`）建议加密存储。推荐通过环境变量或配置文件动态加载，避免在代码中硬编码或在日志中输出。
- **传输加密要求**：生产环境推荐全部使用 HTTPS 安全通道，避免在明文 HTTP 协议下传输密钥或密码。

### 可靠性

- **重试机制**：网络超时/5xx 错误重试（指数退避，最多 3 次）；业务错误（4xx / `IsSuccess=false`）不重试
- **超时设置**：HTTP 请求超时 30s-120s，大查询用分页
- **幂等性**：Save 操作用 `FBillNo` 做幂等键，避免重复创建
- **失败隔离**：批量操作失败时记录失败项，不阻塞后续批次

### 性能

- **分页查询**：查询大结果集时推荐使用 `StartRow` + `Limit` 进行分页加载，避免一次性全量加载导致内存占用过高或网关超时。
- **缓存**：查询结果可缓存（TTL 300s），写入操作后清除关联 `FormId` 的缓存
- **并发控制**：单会话非线程安全（共享 Cookie/SID），并发场景用多会话或序列化
- **批量**：Save 支持一次提交多单（`Model` 为数组），减少请求次数

### 日志与审计

- **请求追踪**：每个请求分配唯一 `request_id`，贯穿日志
- **操作审计**：写入操作（Save/Delete/Submit/Audit/Push）记录 formId + 单据号 + 操作人 + 结果
- **耗时监控**：记录每个 API 调用耗时，慢查询告警

## 常见问题排查

| 问题 | 原因 | 解决 |
|------|------|------|
| 登录失败 | `acct_id` / `app_id` / `app_secret` 不匹配 | 检查账套 ID 和应用配置 |
| 会话过期 | Cookie 未携带或已超时 | 重新登录，确认 Cookie 容器复用 |
| 权限不足 | 用户无该表单操作权限 | 联系管理员授权 |
| FormId 不存在 | FormId 拼写错误或该账套未安装对应模块 | 用 `QueryBusinessInfo` 查可用 FormId |
| 字段不存在 | FieldKey 拼写错误 | 用 `View` 查看单据结构获取正确 FieldKey |
| 并发会话串号 | 单会话被多线程共享 | 每线程独立会话，或序列化调用 |
| 保存失败：基础资料不存在 | `IsVerifyBaseDataField=True` 但引用的基础资料不存在 | 确认基础资料 `FNumber` 正确 |
| 审核失败：状态不对 | 单据未提交就审核 | 先 `Submit` 再 `Audit` |
| 下推失败 | 源单未审核 | 先审核源单（状态 C）再下推 |

## 代码模板

完整索引见 [assets/snippets/snippets-guide.md](assets/snippets/snippets-guide.md)（**推荐按需只读 1 个文件**，避免全量加载造成上下文膨胀）。

| 类型 | 入口 |
|------|------|
| HTTP 客户端骨架 | `assets/OpenApiClient{Java,Python,Csharp}.*` |
| 官方 Java SDK | `assets/OpenApiClientK3CloudSdkJava.java`、`K3CloudApiServiceImpl.java` |
| 集成 DTO / 门面 | `KingdeeConfigDTO`、`KingdeeQueryRequest`、`KingdeeSaveRequest`、`KingdeeErpServiceJava` |
| 通用流程片段 | `assets/snippets/*Flow.md` |
| 销售链 Model 片段 | `assets/snippets/Sal*.md`、`Ar*.md`、`Iv*.md`、`Stk*.md` |
| 集成模式 | [references/integration-patterns.md](references/integration-patterns.md) |
| FormId / RuleId | [references/form-id-catalog.md](references/form-id-catalog.md)、[references/push-rules-catalog.md](references/push-rules-catalog.md) |
| 高级操作 / 多语言 | [references/advanced-operations.md](references/advanced-operations.md)、[references/language-implementations.md](references/language-implementations.md) |

使用说明：
- 先选客户端或 SDK 模板，替换连接参数；`app_secret` 不得硬编码
- 业务流程与 Save Model 按 `snippets-guide.md` 命中 1 条再读
- 标识真实性：为防止使用错误的标识导致下推失败，推荐将 FormId、RuleId、扩展字段、单据类型编码与当前账套的元数据进行核实并以其为准。
- 协议细节以本 SKILL 正文为准；插件开发见 `kd-enterprise-csharp`

## 硬性规则

- 不臆造 FormId、FieldKey、RuleId、服务名；账套未安装的表单不得写入对接代码
- 不把本技能用于 BOS 内插件或自定义 WebAPI 实现（见 `kd-enterprise-csharp`）
- 推荐在会话中复用 Cookie 容器；日志中推荐避免输出明文的 `app_secret`、密码或完整的 Cookie 信息。
- 业务失败（`IsSuccess=false`）不做盲目重试；仅网络超时/5xx 可有限重试
- 扩展字段、单据类型、RuleId 与基础资料编码只使用 `{...}` 占位符；生成代码前须用当前账套元数据替换

## 工具路由（KCode 二开版）

| 场景 | 工具 | 用法 |
|------|------|------|
| 社区/API 知识 | `kd_cosmic_qa` | 金蝶云社区智能问答（FormId/字段/操作编码） |
| 表结构查询 | KCode `read` `tables.json` | 确认字段名和类型 |
| SDK 文档查询 | `web_search` | 金蝶官方 WebAPI 文档 |
| 调用 API | KCode `bash`（运行脚本） | Python/C#/Java 脚本调用 HTTP API |
| 代码生成后校验 | KCode `review` | 审查对接代码（密钥安全、错误处理、并发控制） |
