# 背景 aPaaS 平台有完整的 Open API 能力,但是目前这些能力全都以单独接口的形式提供给开发者,不方便开发者调试和调用。 在此背景下,我们在一店一群项目的基础上,封装 aPaaS 平台 RESTful API 的 Node.js SDK,简化接口调用,内置限流与 token 缓存功能。 ## ✨ **功能特性** - ✅ 获取 accessToken,自动管理 token 有效期 - ✅ record 单条查询、批量查询(支持分页迭代) - ✅ record 单条创建、批量创建(支持分页迭代) - ✅ record 单条更新、批量更新 - ✅ record 单条删除、批量删除 - ✅ OQL、跨对象搜索、常量对象、数据集列表 - ✅ 工作流人工任务、异步流程状态、飞书集成 token - ✅ 内置 Bottleneck 限流器,基于 API 接口配置限流规则 - ✅ 自定义日志等级 - …… **📦 安装** ```Bash npm install apaas-oapi-client # or yarn add apaas-oapi-client ``` *** # **🚀 快速开始** ```JavaScript const { apaas } = require('apaas-oapi-client'); async function main() { const client = new apaas.Client({ clientId: 'your_client_id', clientSecret: 'your_client_secret', namespace: 'app_xxx' }); await client.init(); client.setLoggerLevel(3); // 设置日志等级 (0-5) console.log('Access Token:', client.token); console.log('Namespace:', client.currentNamespace); } main(); ``` *** ## **🤖 AI Agent / Skills 使用说明** 本 SDK 随包提供面向 AI Agent 的模块化 Skills,目录位于 `skills/`。推荐让 Agent 先使用 `apaas-shared` 读取通用规则,再按任务选择具体 Skill。 | **Skill** | **适用场景** | | :-- | :-- | | `apaas-shared` | Client 初始化、凭证安全、namespace、token、日志、OpenAPI 覆盖、分页与错误码处理 | | `apaas-object` | 对象列表、字段元数据、记录查询/创建/更新/删除、OQL、跨对象搜索、常量对象、数据集 | | `apaas-object-schema` | 对象结构管理、字段类型映射、lookup/reference 依赖规则 | | `apaas-function-flow` | 云函数调用、自动化流程 v1/v2 执行、工作流人工任务、飞书集成 token | | `apaas-builder` | 页面列表、页面详情、页面访问链接 | | `apaas-global` | 全局选项、环境变量读取与审计 | | `apaas-lark-id-exchange` | 用户/部门飞书/Lark ID 互换,包含单个和批量映射 | | `apaas-attachment` | 附件文件与头像图片上传、下载、删除 | ### **安装 Skills** 从 GitHub 安装单个 Skill: ```Bash npx skills add https://github.com/ennann/apaas-oapi-node-client --skill apaas-object ``` 安装全部 aPaaS Skills: ```Bash npx skills add https://github.com/ennann/apaas-oapi-node-client --skill '*' ``` 查看仓库内可安装的 Skills: ```Bash npx skills add https://github.com/ennann/apaas-oapi-node-client --list ``` 从 npm 包安装到本机 Codex Skill 目录: ```Bash npx apaas-oapi-client install-skills ``` 或从已安装依赖的项目中复制: ```Bash mkdir -p "${CODEX_HOME:-$HOME/.codex}/skills" cp -R node_modules/apaas-oapi-client/skills/apaas-* "${CODEX_HOME:-$HOME/.codex}/skills/" ``` ### **选择规则** - 写入记录、查询记录、OQL、常量对象、数据集:使用 `apaas-object`。 - 新建对象、改字段、删字段、删对象:使用 `apaas-object-schema`。 - 对象结构变更前,Agent 应先读取 `apaas-object-schema/references/field-schema-rules.md`。 - 大量读取优先使用 iterator 或 `apaas-object/references/id-cursor-pagination.md`。 - 删除、批量写入、流程执行、结构变更都按高风险写操作处理,执行前确认目标和影响。 `client.object.schema.*` 是对象结构编辑的推荐入口;`client.schema.*` 仍作为旧代码兼容入口保留。 *** ## **🔐 认证** ### **初始化 Client** | **参数** | **类型** | **说明** | | :-- | :-- | :-- | | clientId | string | 应用 clientId | | clientSecret | string | 应用 clientSecret | | namespace | string | 命名空间 | | disableTokenCache | boolean | 是否禁用 token 缓存,默认 false | *** ## **📝 日志等级** 可调用 setLoggerLevel(level) 设置日志等级。 | **Level** | **名称** | **说明** | | :-- | :-- | :-- | | 0 | fatal | 严重错误 | | 1 | error | 错误 | | 2 | warn | 警告 | | 3 | info | 信息(默认) | | 4 | debug | 调试信息 | | 5 | trace | 追踪 | *** # 💾 **数据模块** ## **📋 对象列表接口** ### **获取所有对象(数据表)** ```JavaScript const res = await client.object.list({ offset: 0, limit: 100, filter: { type: 'custom', quickQuery: 'store' } }); console.log(res); ``` *** ## **🔍 查询接口** 查询条件请根据实际需求自行拼装。详情参考 API 接口文档示例。 ### **单条查询** ```JavaScript const res = await client.object.search.record({ object_name: 'object_store', record_id: 'your_record_id', select: ['field1', 'field2'] }); console.log(res); ``` *** ### **批量查询** 每次查询最多返回 100 条记录。 ```JavaScript const res = await client.object.search.records({ object_name: 'object_store', data: { need_total_count: true, page_size: 100, offset: 0 } }); console.log(res); ``` *** ### **分页查询所有记录** 在上一个请求的基础上,封装每次查询最多返回 100 条记录。 ```JavaScript const { total, items } = await client.object.search.recordsWithIterator({ object_name: 'object_store', data: { need_total_count: true, page_size: 100, offset: 0 } }); console.log('Total:', total); console.log('Items:', items); ``` *** ### **执行 OQL** ```JavaScript const res = await client.object.oql({ query: 'SELECT _id, _name FROM _user WHERE _type = $1 LIMIT 10', args: ['_employee'] }); console.log(res); ``` ### **跨对象搜索** ```JavaScript const res = await client.object.search.recordsAcrossObjects({ q: 'Ethan', search_objects: [{ api_name: '_user', select: ['_id', '_name'], search_fields: ['_name'] }], page_size: 20, metadata: 'Label' }); console.log(res); ``` *** ## **➕ 创建接口** ### **单条创建** ```JavaScript const res = await client.object.create.record({ object_name: 'object_event_log', record: { name: 'Sample text', content: 'Sample text' } }); console.log(res); ``` ### **批量创建(最多 100 条)** ```JavaScript const res = await client.object.create.records({ object_name: 'object_event_log', records: [ { name: 'Sample text 1', content: 'Sample text 1' }, { name: 'Sample text 2', content: 'Sample text 2' } ] }); console.log(res); ``` ### **批量创建(支持超过 100 条,自动拆分)** > ⚠️ 超过 100 条会自动拆分为多次请求,SDK 已自动分组限流 ```JavaScript const { total, items } = await client.object.create.recordsWithIterator({ object_name: 'object_event_log', records: [ { name: 'Sample text 1', content: 'Sample text 1' }, { name: 'Sample text 2', content: 'Sample text 2' }, // ... 可以超过 100 条 ] }); console.log('Total:', total); console.log('Items:', items); ``` ## **✏️ 更新接口** ### **单条更新** ```JavaScript const res = await client.object.update.record({ object_name: 'object_store', record_id: 'your_record_id', record: { field1: 'newValue' } }); console.log(res); ``` *** ### **批量更新(最多 100 条)** ```JavaScript const res = await client.object.update.records({ object_name: 'object_store', records: [ { _id: 'id1', field1: 'value1' }, { _id: 'id2', field1: 'value2' } ] }); console.log(res); ``` ### **批量更新(支持超过 100 条,自动拆分)** > ⚠️ 超过 100 条会自动拆分为多次请求,SDK 已自动分组限流 ```JavaScript const results = await client.object.update.recordsWithIterator({ object_name: 'object_store', records: [ { _id: 'id1', field1: 'value1' }, { _id: 'id2', field1: 'value2' }, // ... 可以超过 100 条 ] }); console.log(results); // 返回所有子请求的结果数组 ``` *** ## **🗑️ 删除接口** ### **单条删除** ```JavaScript const res = await client.object.delete.record({ object_name: 'object_store', record_id: 'your_record_id' }); console.log(res); ``` *** ### **批量删除(最多 100 条)** ```JavaScript const res = await client.object.delete.records({ object_name: 'object_store', ids: ['id1', 'id2', 'id3'] }); console.log(res); ``` ### **批量删除(支持超过 100 条,自动拆分)** > ⚠️ 超过 100 条会自动拆分为多次请求,SDK 已自动分组限流 ```JavaScript const results = await client.object.delete.recordsWithIterator({ object_name: 'object_store', ids: ['id1', 'id2', 'id3', /* ... 可以超过 100 条 */] }); console.log(results); // 返回所有子请求的结果数组 ``` *** ## **📊 对象元数据接口** ### **获取指定对象字段元数据** ```JavaScript const res = await client.object.metadata.field({ object_name: '_user', field_name: '_id' }); console.log(res); ``` ### **获取指定对象所有字段信息** ```JavaScript const res = await client.object.metadata.fields({ object_name: 'object_store' }); console.log(res); ``` ### **导出数据对象文档为 Markdown** 将数据对象的元数据导出为详细的 Markdown 文档,包含完整的字段信息、类型、配置等。 ```JavaScript const fs = require('fs'); // 方式一:导出所有对象(推荐,无需参数) const markdown = await client.object.metadata.export2markdown(); fs.writeFileSync('all_objects.md', markdown, 'utf-8'); // 方式二:只导出指定的对象 const markdown2 = await client.object.metadata.export2markdown({ object_names: ['object_store', 'object_order', '_user'] }); fs.writeFileSync('specific_objects.md', markdown2, 'utf-8'); // 方式三:结合 listWithIterator 灵活筛选 const allObjects = await client.object.listWithIterator(); const customObjects = allObjects.items .filter(obj => !obj.apiName.startsWith('_')) // 只要自定义对象 .map(obj => obj.apiName); const markdown3 = await client.object.metadata.export2markdown({ object_names: customObjects }); fs.writeFileSync('custom_objects.md', markdown3, 'utf-8'); console.log('✅ 文档导出成功!'); ``` **生成的 Markdown 文档包含:** - 📋 自动生成的目录(带锚点链接) - 📊 每个对象的详细信息(中英文名称、创建时间、字段数量) - 📝 字段列表(中文名称、API名称、类型、必填、唯一性) - ⚙️ 字段配置详情: - **选项字段**:展示所有选项值 - **公式字段**:显示公式表达式和返回类型 - **引用字段**:显示引用来源和字段 - **汇总字段(rollup)**:汇总类型(计数/求和/平均值等)、汇总对象、关联字段、过滤条件 - **lookup 字段**:显示关联对象 - 其他配置:最大长度、小数位、显示样式等 - 🎯 字段智能排序(系统字段、业务字段、特殊字段分类展示) *** # **🧩 常量对象与数据集** ## **常量对象** 支持 `_currency`、`_country`、`_timeZone`。 ```JavaScript const currencies = await client.constant.records({ object_name: '_currency', data: { limit: 100, offset: 0, count: false, fields: ['_id', '_name'] } }); const country = await client.constant.record({ object_name: '_country', record_id: 'CN' }); ``` ## **数据集** ```JavaScript const datasets = await client.dataset.listWithIterator({ page_size: 100 }); console.log(datasets); ``` *** # **📎 附件模块** ## **文件操作** ### **上传文件** ```JavaScript const fs = require('fs'); const res = await client.attachment.file.upload({ file: fs.createReadStream('/path/to/file.zip') }); console.log(res); // 返回: { code: "0", msg: "success", data: { fileId, type, name, size } } ``` ### **下载文件** ```JavaScript const fileData = await client.attachment.file.download({ file_id: '625d2f602af94d46972073db32a99ed2' }); // 返回文件二进制流 ``` ### **删除文件** ```JavaScript const res = await client.attachment.file.delete({ file_id: '625d2f602af94d46972073db32a99ed2' }); console.log(res); ``` ## **头像图片操作** ### **上传头像图片** ```JavaScript const fs = require('fs'); const res = await client.attachment.avatar.upload({ image: fs.createReadStream('/path/to/avatar.jpg') }); console.log(res); ``` ### **下载头像图片** ```JavaScript const imageData = await client.attachment.avatar.download({ image_id: 'c70d03b21d3c40468ee710d984cfb7a8_o' }); // 返回图片二进制流 ``` *** # **💽 全局数据模块** ## **全局选项** ### **查询全局选项详情** ```JavaScript const res = await client.global.options.detail({ api_name: 'global_option_abc' }); console.log(res); ``` ### **查询全局选项列表** ```JavaScript const res = await client.global.options.list({ limit: 10, offset: 0, filter: { quickQuery: 'Sample Text' } }); console.log(res); ``` ### **分页查询所有全局选项** ```JavaScript const { total, items } = await client.global.options.listWithIterator({ limit: 100, filter: { quickQuery: 'Sample Text' } }); console.log('Total:', total); console.log('Items:', items); ``` ## **环境变量** ### **查询环境变量详情** ```JavaScript const res = await client.global.variables.detail({ api_name: 'global_variable_abc' }); console.log(res); ``` ### **查询环境变量列表** ```JavaScript const res = await client.global.variables.list({ limit: 10, offset: 0, filter: { quickQuery: 'Sample Text' } }); console.log(res); ``` ### **分页查询所有环境变量** ```JavaScript const { total, items } = await client.global.variables.listWithIterator({ limit: 100, filter: { quickQuery: 'Sample Text' } }); console.log('Total:', total); console.log('Items:', items); ``` *** # **📄 页面模块** ### **获取所有页面** ```JavaScript const res = await client.page.list({ limit: 10, offset: 0 }); console.log(res); ``` ### **分页查询所有页面** ```JavaScript const { total, items } = await client.page.listWithIterator({ limit: 100 }); console.log('Total:', total); console.log('Items:', items); ``` ### **获取页面详情** ```JavaScript const res = await client.page.detail({ page_id: 'appPage_page' }); console.log(res); ``` ### **获取页面访问地址** ```JavaScript const res = await client.page.url({ page_id: 'appPage_page', pageParams: { var_page: '1234567890' }, parentPageParams: { navId: 'page_nav_id', pageApiName: 'page_name' }, navId: 'page_nav_id', tabId: 'tab_id' }); console.log(res); // 返回: { code: "0", msg: "success", data: { link: "https://..." } } ``` *** # **🏢 部门模块** ## **部门 ID 交换** `department_id_type` 表示传入 ID 的类型,接口会返回同一个部门的其他 ID 映射,不是选择输出字段。 可选值: - `department_id`:aPaaS/Lark 部门 ID,例如 `1758534140403815` - `external_department_id`:外部平台部门 ID,无固定格式 - `external_open_department_id`:外部 open department ID,通常以 `oc_` 开头 ### **单个部门 ID 交换** ```JavaScript const res = await client.department.exchange({ department_id_type: 'external_department_id', department_id: 'Y806608904' }); console.log(res); ``` ### **批量部门 ID 交换** 每次最多 200 个,SDK 已自动拆分限流。 ```JavaScript const res = await client.department.batchExchange({ department_id_type: 'external_department_id', department_ids: ['id1', 'id2', 'id3'] }); console.log(res); ``` ***
# **👤 用户模块** ## **用户 ID 交换** `user_id_type` 表示传入 ID 的类型,接口会返回同一个用户的其他 ID 映射,不是选择输出字段。用户 ID 交换必须传真实 `feishu_app_id`。 可选值: - `user_id`:aPaaS/Lark 用户 ID,例如 `1758534140403815` - `external_user_id`:外部平台用户 ID,无固定格式 - `external_open_id`:外部 open ID,通常以 `ou_` 开头 ### **单个用户 ID 交换** ```JavaScript const res = await client.user.exchange({ user_id_type: 'external_open_id', user_id: 'ou_xxx', feishu_app_id: 'cli_xxx' }); console.log(res); ``` ### **批量用户 ID 交换** 每次最多 200 个,SDK 已自动拆分限流。返回后需要检查 `failedCount` 和 `failed`。 ```JavaScript const res = await client.user.batchExchange({ user_id_type: 'external_user_id', user_ids: ['u1', 'u2', 'u3'], feishu_app_id: 'cli_xxx' }); console.log(res); ``` ***
# **🔄 自动化流程模块** ## **V1 版本 - 执行流程** ```JavaScript const res = await client.automation.v1.execute({ flow_api_name: 'automation_cd05fdab67d', operator: { _id: 100, email: 'sample@feishu.cn' }, params: { varRecord_ab67d031d44: { _id: 100 } } }); console.log(res); // 返回: { code: "0", msg: "success", data: { errMsg, executionId, status, data, errCode } } ``` ## **V2 版本 - 执行流程** V2 版本支持流程重新提交功能。 ```JavaScript const res = await client.automation.v2.execute({ flow_api_name: 'automation_a9ec6ee5fb1', operator: { _id: 100, email: 'sample@feishu.cn' }, params: { storeId: 100 }, is_resubmit: true, pre_instance_id: '1835957428957195' }); console.log(res); // 返回: { code: "0", msg: "success", data: { errMsg, executionId, status, data, errCode } } ``` ## **查询异步流程状态** ```JavaScript const status = await client.workflow.execution.status({ execution_id: '1848390852196499' }); console.log(status); ``` ## **获取流程定义详情** ```JavaScript const flow = await client.workflow.definition.detail({ flow_api_name: 'package_xxx__c__action_xxx' }); console.log(flow); ``` ## **人工任务** ```JavaScript const tasks = await client.workflow.userTask.tasks({ type: 'pending', source: 'assignMe', kunlun_user_id: '1783981209205788', limit: 20, offset: 0 }); await client.workflow.userTask.agree({ approval_task_id: '1785996265147395', user_id: '1783981209205788', opinion: '同意' }); ``` *** # **🔗 集成模块** ## **飞书集成 Token** ```JavaScript const tenantToken = await client.integration.lark.defaultTenantAccessToken(); const appToken = await client.integration.lark.appAccessToken({ lark_integration_api_name: 'larkIntegration_xxx' }); ``` ***
# **☁️ 云函数模块** ```JavaScript const res = await client.function.invoke({ name: 'StoreMemberUpdate', params: { key: 'value' } }); console.log(res); ``` ***
## **🛠️ 高级** ### **获取当前 token** ```JavaScript console.log(client.token); ``` ### **获取 token 过期时间** ```JavaScript console.log(client.tokenExpireTime); // 返回剩余秒数 ``` ### **获取当前 namespace** ```JavaScript console.log(client.currentNamespace); ``` *** ## **💡 备注** - 本 SDK 默认使用 [axios](https://www.npmjs.com/package/axios) 请求。 - 内置 [bottleneck](https://www.npmjs.com/package/bottleneck) 进行请求限流。 - 日志打印默认使用 console.log 并带时间戳,可通过 setLoggerLevel 动态控制输出等级。 *** > 由 [aPaaS OAPI Client SDK](https://www.npmjs.com/package/apaas-oapi-client) 提供支持,如有问题请提交 Issue 反馈。 ---