批量操作
本文说明当前代码实现的接入规则,不代表各环境已发布或可用。批量导入、批量删除接口是否已在目标环境启用,以 MCP 元工具 search_apis / 管理员控制台为准。
一次要新建或删除几十上百条数据时,不要循环调用单条的 create / delete:
每次调用都要走一遍鉴权、权限与转发,单条失败也不好汇总;循环写入时一旦某次超时,
还会陷入「这一条到底写没写进去」的不确定状态。
开放平台为批量场景提供两类专用接口:
| 场景 | 接口 | 执行方式 |
|---|---|---|
| 批量新建 | 各服务的 data-task-center.import-rows + import-result | 异步:提交后生成导入任务,轮询取结果 |
| 批量删除 | 各实体的 .batch-delete | 同步:一次返回成功 / 失败条数与原因 |
本文面向 MCP agent 与 API 集成方,示例均按统一直通入口 POST /v1/api/invoke 书写;
MCP 下把同样的 apiCode / query / body 交给 call_api 即可。
批量新建:JSON 行导入
接口
每个带导入能力的业务服务都有一组同形接口,与主站「云任务中心」的 Excel 导入共用同一条任务链路—— 业务校验、按行报错与页面上传完全一致,只是把 Excel 换成了 JSON 行:
| 接口编码 | 后端接口 | 所需范围 |
|---|---|---|
<服务>.data-task-center.import-rows | POST /v1/data-task-center/import-rows | <服务>:write |
<服务>.data-task-center.import-result | GET /v1/data-task-center/import-result | <服务>:read |
<服务> 取 srm / sales / finance / project。导入类型属于哪个服务,就必须调哪个服务的接口,
调错服务会返回「不支持的导入任务编码」。
该入口绕过了各业务页面上的按钮权限,因此收紧为仅本租户系统管理员可调用,
其他身份返回 403「仅系统管理员可通过接口批量导入数据」。
常用导入类型(taskCode)
| 服务 | taskCode | 导入内容 | 额外参数 |
|---|---|---|---|
| srm | SRM_PRODUCT | 物料 | — |
| srm | SRM_SUPPLIER | 供应商 | — |
| srm | SRM_PRICE_BOOK | 供应商价格本明细 | params.priceBookId 必填 |
| srm | SUBCONTRACT_ORDER | 委外订单 | — |
| srm | SRM_INSPECTION_ITEM | 质检项目 | — |
| srm | SRM_BRAND | 品牌 | — |
| sales | CUSTOMER_DATA_TASK | 客户 / 联系人 | params.importCategory:CUSTOMER 或 CONTACT |
| sales | SALES_LEAD_DATA_TASK | 线索 | — |
| sales | SALES_OPPORTUNITY_DATA_TASK | 商机 | — |
| finance | FIN_OPENING_AR_IMPORT | 期初应收 | — |
| finance | FIN_OPENING_AP_IMPORT | 期初应付 | — |
| project | PROJECT_DATA_TASK | 项目 | — |
| project | PROJECT_DRAWING_ARCHIVE_DATA_TASK | 图纸归档 | — |
以下导入类型依赖多个工作表或专用模板格式,不支持 import-rows,请在主站页面上传 Excel:
SRM_BOM(BOM)、SRM_PRD_GROUP(物料分组)、FIN_BANK_TXN_IMPORT(银行流水)。
请求体
| 字段 | 必填 | 说明 |
|---|---|---|
taskCode | 是 | 导入类型,见上表 |
columns | 是 | 有序列定义 [{field, header}]。field 是系统字段标识;header 是表头文字,可省略(省略时用 field)。最多 200 列 |
rows | 是 | 数据行,每行是 {<field>: 值},值只能是文本、数字或布尔值。可带保留键 rowKey(调用方自己的行标识,≤128 字符,同一任务内不可重复),用于回查结果 |
params | 否 | 该导入类型的业务参数(如 importCategory、priceBookId)。未提供 columnMappings 时服务端按 columns 顺序自动生成 |
requestId | 强烈建议 | 幂等键:同一用户 + 导入类型 + requestId 在 24 小时内重复提交,直接返回首次结果(deduplicated: true),不会重复建任务 |
sessionId | 分批时必填 | 分批上传会话标识。不传表示本次请求即全部数据 |
chunkIndex | 分批时必填 | 批次序号,从 0 开始连续递增 |
finalChunk | 分批时必填 | 最后一批传 true,服务端此时合并已暂存的全部行并创建任务 |
requestId 与 sessionId 只能包含字母、数字、下划线、中划线、点或冒号,且不超过 64 个字符。
字段标识从哪里来:columns[].field 与主站该业务导入模板的列一一对应。先用 describe_api 查看
import-rows 的参数说明;自定义字段标识可用 system.field.list 查询,见字段清单与自定义字段。
容量上限
| 维度 | 上限 |
|---|---|
| 单次请求 | 500 行 |
| 单个导入任务(含全部分批) | 5000 行 |
| 单个导入任务的单元格数(行数 × 列数) | 200,000 |
| 单个导入任务的数据总量 | 约 10MB 文本 |
超过 5000 行请拆成多个导入任务(多个 sessionId),而不是加大单批。
超过 500 行:分批上传
同一 sessionId 的各批按顺序串行提交——等上一批返回后再发下一批,不要并发:
- 第一批
chunkIndex: 0,带上完整的params; - 后续批次
chunkIndex依次加 1,省略params(若携带,必须与第一批完全相同); - 每批的
columns必须与第一批完全一致(字段与顺序); - 最后一批
finalChunk: true,此时才返回taskId;中间批次只返回sessionId与已暂存行数stagedRows。
每批使用各自的 requestId。某一批超时或结果不明时,用同一个 requestId 原样重发这一批:
已成功的会直接返回首次结果,不会重复暂存;换一个 requestId 重发则可能写入两次。
会话 30 分钟未完成即失效,返回「导入会话已失效」时,请换一个新的 sessionId 从 chunkIndex: 0 重新提交。
示例:分两批导入客户
第一批(chunkIndex: 0,带 params):
{
"apiCode": "sales.data-task-center.import-rows",
"body": {
"taskCode": "CUSTOMER_DATA_TASK",
"requestId": "crm-sync-20260929-c0",
"sessionId": "crm-sync-20260929",
"chunkIndex": 0,
"finalChunk": false,
"params": { "importCategory": "CUSTOMER" },
"columns": [
{ "field": "name", "header": "客户名称" },
{ "field": "categoryId", "header": "客户分类" },
{ "field": "ownerId", "header": "负责人" },
{ "field": "primaryContact", "header": "首要联系人" },
{ "field": "primaryPhone", "header": "联系电话" }
],
"rows": [
{ "rowKey": "ext-1001", "name": "示例科技有限公司", "categoryId": "重点客户", "ownerId": "张三", "primaryContact": "李四", "primaryPhone": "13800000001" },
{ "rowKey": "ext-1002", "name": "示例贸易有限公司", "categoryId": "普通客户", "ownerId": "张三", "primaryContact": "王五", "primaryPhone": "13800000002" }
]
}
}
返回(中间批次,只暂存不建任务):
{ "code": 200, "message": "success", "data": { "taskCode": "CUSTOMER_DATA_TASK", "sessionId": "crm-sync-20260929", "stagedRows": 2 } }
第二批(最后一批,省略 params,columns 与第一批一致):
{
"apiCode": "sales.data-task-center.import-rows",
"body": {
"taskCode": "CUSTOMER_DATA_TASK",
"requestId": "crm-sync-20260929-c1",
"sessionId": "crm-sync-20260929",
"chunkIndex": 1,
"finalChunk": true,
"columns": [
{ "field": "name", "header": "客户名称" },
{ "field": "categoryId", "header": "客户分类" },
{ "field": "ownerId", "header": "负责人" },
{ "field": "primaryContact", "header": "首要联系人" },
{ "field": "primaryPhone", "header": "联系电话" }
],
"rows": [
{ "rowKey": "ext-1003", "name": "示例制造有限公司", "categoryId": "重点客户", "ownerId": "赵六", "primaryContact": "孙七", "primaryPhone": "13800000003" }
]
}
}
返回(已建任务):
{ "code": 200, "message": "success", "data": { "taskId": "2104000000000000001", "taskCode": "CUSTOMER_DATA_TASK", "rowCount": 3, "sessionId": "crm-sync-20260929" } }
示例中的字段标识与取值仅演示结构;客户分类、负责人等引用型字段按名称填写,见下文注意事项。
数据量不超过 500 行时,省略 sessionId / chunkIndex / finalChunk,一次提交即返回 taskId。
轮询结果
{
"apiCode": "sales.data-task-center.import-result",
"query": { "taskId": "2104000000000000001", "pageNum": 1, "pageSize": 50 }
}
pageNum / pageSize 用于翻阅行级错误,pageSize 默认 20、最大 200。返回示例:
{
"code": 200,
"message": "success",
"data": {
"taskId": "2104000000000000001",
"taskCode": "CUSTOMER_DATA_TASK",
"status": 3,
"statusText": "部分成功",
"finished": true,
"progress": 100,
"totalCount": 3,
"successCount": 2,
"failCount": 1,
"errors": {
"total": 1,
"pageNum": 1,
"pageSize": 50,
"records": [
{ "rowNum": 3, "rowIndex": 1, "rowKey": "ext-1002", "field": "categoryId", "value": "普通客户", "message": "客户分类不存在" }
]
}
}
}
| 字段 | 说明 |
|---|---|
status / statusText | 0 排队中、1 执行中、2 成功、3 部分成功、4 失败、5 已取消 |
finished | 任务是否已结束;为 false 时间隔几秒再查,不要紧密轮询 |
totalCount / successCount / failCount | 行数统计 |
errorMessage | 任务级失败原因(如数据无法解析),此时可能没有行级错误 |
errors.records[] | 行级错误:rowNum 为 Excel 行号(表头为第 1 行)、rowIndex 为提交数据的下标(从 0 起)、rowKey 为你提交的行标识,field / value 仅在错误可定位到单个字段时返回 |
只重新提交失败的行:按 rowKey(或 rowIndex)找出失败行,修正后作为一个新的导入任务提交,
使用新的 requestId。不要把整批重发——成功的行已经写入,重发会产生重复数据。
只能查询自己创建的任务;系统管理员可查询本租户全部任务。任务同样出现在主站「云任务中心」。
注意事项
- 引用型字段按名称解析:客户负责人、所属客户、供应商等字段填写的是名称,服务端按名称匹配。 租户内存在同名数据时可能匹配到非预期的记录,导入前先确认名称唯一。
- 很多导入只新增、不去重:同一份数据提交两次就会生成两份记录。每次提交都带
requestId, 重试同一批时复用它。 - 单据类导入只生成草稿:导入的单据不会自动提交审批或过账,后续流程仍需在主站或经对应接口处理。
- 行级校验复用主站页面导入的同一套逻辑,报错提示与页面导入一致。
批量删除
接口与请求体
批量删除接口的编码形如 <域>.<实体>.batch-delete(如 sales.customer.batch-delete),
可用 search_apis 按「批量删除」检索。它们都是高危接口:MCP 调用须先向用户逐项复述将要删除的对象,
获得明确同意后携 confirm: true 调用。删除不可逆,不能由 agent 自行决定。
请求体是裸 JSON 数组,元素为待删除记录的 id(不是 { "ids": [...] } 对象):
{
"apiCode": "sales.customer.batch-delete",
"body": ["2104000000000000011", "2104000000000000012", "2104000000000000013"]
}
上限与返回
| 范围 | 单次上限 |
|---|---|
| 默认 | 200 条 |
| 销售订单、发货单 | 50 条 |
超过上限直接拒绝并提示分批提交。本次新增的批量删除逐条独立处理, 不满足删除条件(状态不允许、无权限、已被下游单据引用等)的记录跳过,其余照常删除,返回:
{
"code": 200,
"message": "success",
"data": {
"totalCount": 3,
"successCount": 2,
"failCount": 1,
"failReasons": ["示例贸易有限公司:存在关联单据,不允许删除"]
}
}
totalCount 为去重后的条数。failCount > 0 时把 failReasons 原样转告用户,不要自动重试失败项——
它们多半需要先处理业务状态。
物料、供应商、BOM、供应商价格本等此前已开放的 srm 批量删除保持原有的「全部成功或全部失败」语义:
任一条不满足条件即整批不删。其返回结构以 describe_api 为准。
超时与重试
直通转发对下游的读超时为 30 秒。批量写入更容易触及这个上限,超时后的处理方式取决于接口类型:
| 情况 | 返回 | 含义 | 正确做法 |
|---|---|---|---|
| 写接口等待下游响应超时 | 92205 写操作处理超时,结果未知 | 请求已送达,下游可能仍在执行 | 先查询业务数据(或 import-result)确认结果,再决定是否补做 |
| 超时后立即重发同一写请求 | 92302 该请求正在处理中 | 结果未知时防重锁续持 120 秒 | 不要重发;先查询 |
| 读接口超时 / 连接失败 | 92203 上游服务调用失败 | 请求未完成 | 读操作可重试 |
禁止盲目重试写操作。对 import-rows 而言,结果不明时用同一个 requestId 重发是安全的;
对其他写接口,先查询再决定。完整错误码见错误码。
给 agent 的建议流程
search_apis找到对应服务的import-rows/batch-delete,describe_api看参数说明;- 向用户复述:导入类型、行数、关键字段取值(删除则逐项列出对象),获得明确同意;
- 按 500 行一批串行提交,每批一个
requestId; - 轮询
import-result直到finished: true,向用户汇报成功 / 失败条数; - 只把失败行修正后作为新任务提交;遇到
92205/92302先查询,不自动重发。