--- name: hap-v3-api description: 使用明道云 HAP V3 接口搭建页面和操作数据的专业技能。当用户提到"HAP V3 接口"、"HAP API"、"接口调用"、"数据接口"、"Appkey"、"Sign"、"接口鉴权"等需求时,必须使用此技能。此技能提供完整的 HAP V3 接口使用指南:鉴权配置、接口调用、筛选器使用、数据操作等。如果用户已配置 HAP MCP,AI 应该自动从 MCP 配置中提取鉴权密钥。 license: MIT --- # HAP V3 API 使用技能 此技能提供使用明道云 HAP V3 接口搭建页面、实时获取数据和操作数据的完整指南。 ## Overview 明道云 HAP (High-performance Application Platform) 是一个超级应用平台,通过这个基座可以搭建很多应用。通过 HAP V3 接口,您可以: 1. **在自定义视图插件中**调用 V3 接口操作数据 2. **在独立前端页面中**使用 V3 接口编排业务逻辑 3. **实时获取和操作**明道云应用中的数据 **核心能力:** - ✅ 完整的 API 使用工作流(从零搭建应用到数据操作) - ✅ 详细的字段类型处理规范 - ✅ Filter 筛选器完整语法 - ✅ 关联字段深度查询指南 - ✅ 常见陷阱和解决方案 - ✅ 性能优化最佳实践 **详细文档:** 参考 `references/hap-api-usage-guide.md` 获取完整的 API 使用规范 --- ## ⚠️ 重要:何时使用此技能 **使用优先级规则**: ### 优先级 1: 已配置 HAP MCP → 直接使用 MCP 工具 ✅ 如果用户已经配置了 HAP 应用执行 MCP,AI 应该**直接使用 MCP 工具**,而不是编写 API 请求代码。 **MCP 工具优势**: - ✅ 无需手动处理鉴权 - ✅ 无需编写请求代码 - ✅ MCP 工具自动处理所有细节 - ✅ 更简洁、更可靠 **可用的 MCP 工具**: - `get_app_info` - 获取应用信息 - `get_app_worksheets_list` - 获取工作表列表 - `get_worksheet_structure` - 获取工作表结构 - `get_record_list` - 查询记录列表 - `get_record_pivot_data` - 获取透视表数据 - `create_record` - 创建记录 - `update_record` - 更新记录 - `delete_record` - 删除记录 - `batch_create_records` - 批量创建记录 - `batch_update_records` - 批量更新记录 - `batch_delete_records` - 批量删除记录 - 等等...(完整列表请参考 MCP 工具文档) **示例 - 使用 MCP 工具查询数据**: ```javascript // ✅ 正确 - 直接使用 MCP 工具 const records = await mcpClient.call('get_record_list', { worksheet_id: 'xxx', pageSize: 100, pageIndex: 1, filter: { type: 'group', logic: 'AND', children: [ { type: 'condition', field: 'status', operator: 'eq', value: ['已成交'] } ] } }); // ❌ 错误 - 不要编写 API 请求代码 // fetch('https://api.mingdao.com/v3/open/worksheet/getFilterRows', ...) ``` ### 优先级 2: 仅在以下情况使用 HAP V3 API 编写代码 **何时使用 V3 API 代码**: 1. ✅ 用户**明确需要编写代码实现**(如在视图插件、独立前端项目中集成) 2. ✅ 用户需要在代码中**编排复杂的业务逻辑** 3. ✅ **未配置 HAP MCP** 或 MCP 不可用 **示例场景**: - "帮我在 React 组件中集成 HAP API 查询数据" → ✅ 使用 V3 API 代码 - "帮我写一个函数,批量导入数据到 HAP" → ✅ 使用 V3 API 代码 - "帮我查询客户管理表中的数据"(已配置 MCP)→ ❌ 不要写代码,使用 MCP 工具 --- ## 快速开始 ### 0. HAP 产品线说明 ⚠️ HAP 支持多个产品线和私有部署,**API Host 配置不同**: | 产品线 | API Host | 说明 | |--------|----------|------| | **明道云 HAP** | `https://api.mingdao.com` | 官方 SaaS 服务 | | **Nocoly HAP** | `https://www.nocoly.com` | Nocoly SaaS 服务 | | **私有部署 HAP** | `https://your-domain.com/api` | ⚠️ **注意:私有部署需要在域名后加 `/api`** | **示例**: - 明道云:`https://api.mingdao.com/v3/open/worksheet/getFilterRows` - Nocoly:`https://www.nocoly.com/v3/open/worksheet/getFilterRows` - 私有部署:`https://p-demo.mingdaoyun.cn/api/v3/open/worksheet/getFilterRows` ← 注意 `/api` **AI 必须根据用户的 MCP 配置自动判断使用哪个 host**。如果用户未提供 MCP 配置,需询问使用哪个产品线。 --- ### 1. 获取 API 凭证 **方法一: 从 MCP 配置中提取(推荐)** 如果用户已经配置了 HAP 应用执行 MCP,AI 助手应该**自动从 MCP 配置中提取鉴权密钥**,而不是让用户手动输入。 **⚠️ 重要**: AI 必须**自动检测用户当前使用的 IDE**,然后读取对应的配置文件。不要假设用户使用 Cursor。 #### 自动化提取步骤 **Step 1: 自动检测当前使用的 IDE 平台** AI 必须**自动检测**用户当前使用的 IDE,不要假设或询问。检测方法(按优先级): 1. **检测当前运行的 IDE**(最高优先级) - 检查环境变量:`$TERM_PROGRAM`(可能值:`cursor`, `claude`, `trae` 等) - 检查会话变量:`$CLAUDE_SESSION`, `$TRAE_SESSION`, `$ANTIGRAVITY_SESSION` 等 - 检查进程:`pgrep -x "Cursor"`, `pgrep -x "Claude"` 等 2. **检查已安装的 IDE**(如果第1步失败) - 检查配置目录:`~/.cursor`, `~/.claude`, `~/.trae` 等是否存在 - 如果检测到多个,按流行度选择:Cursor → Claude Code → TRAE → 其他 3. **检查项目级配置**(如果前2步失败) - 检查当前项目目录:`.cursor/`, `.trae/`, `.claude/` 等 **支持的 IDE 平台和配置路径**: | IDE 平台 | 全局配置文件路径 | 项目级配置路径 | |---------|----------------|--------------| | **Claude Code** | `~/.claude/config.json` 或通过 `claude mcp list` 命令 | `.claude/config.json` | | **Cursor** | `~/.cursor/mcp.json` 或 `~/Library/Application Support/Cursor/User/settings.json` (macOS) | `.cursor/mcp.json` | | **TRAE** | `~/.trae/mcp.json` | `.trae/mcp.json` | | **GitHub Copilot** | `~/.copilot/mcp.json` | `.github/mcp.json` | | **Antigravity** | `~/.gemini/antigravity/config.json` | `.agent/config.json` | | **OpenCode** | `~/.config/opencode/mcp.json` | `.opencode/mcp.json` | | **Windsurf** | `~/.codeium/windsurf/mcp.json` | `.windsurf/mcp.json` | | **Gemini CLI** | `~/.gemini/config.json` | `.gemini/config.json` | | **Codex** | `~/.codex/config.toml` | `.codex/config.toml` | | **Manus** | `~/.manus/mcp.json` | - | | **Coze** | `~/.coze/mcp_config.json` | - | **Step 2: 读取对应平台的 MCP 配置文件** 根据检测到的平台,读取对应的配置文件。 **Step 3: 查找 HAP MCP 配置** - 在配置对象中查找以 `hap-mcp-` 开头的服务器配置 - 识别应用执行 MCP(包含 `url` 字段且 URL 指向 `api.mingdao.com/mcp`) **MCP 配置格式示例**: ```json { "mcpServers": { "hap-mcp-应用名": { "url": "https://api.mingdao.com/mcp?HAP-Appkey=6802bfa5da37d75f&HAP-Sign=MWZmZWU1YmMyMzE4ZTAxYjY3NTViYjM5NzhlNTdhOTIwZWFhYTc2Y2I2YzljNWMzNDFmMjk4NTM2N2M0YTg2OA==" } } } ``` **Step 4: 从 URL 中解析鉴权参数** - 从 URL 的查询参数中提取 `HAP-Appkey` 的值 - 从 URL 的查询参数中提取 `HAP-Sign` 的值 - 注意:URL 参数可能经过 URL 编码,需要正确解码 #### 代码示例(AI 助手操作) ```javascript // 1. 检测当前使用的 IDE 平台 function detectCurrentIDE() { // 检查环境变量 const termProgram = process.env.TERM_PROGRAM; if (termProgram === 'cursor') return 'cursor'; if (termProgram === 'claude') return 'claude'; if (termProgram === 'trae') return 'trae'; // 检查会话变量 if (process.env.CLAUDE_SESSION) return 'claude'; if (process.env.TRAE_SESSION) return 'trae'; if (process.env.ANTIGRAVITY_SESSION) return 'antigravity'; // 检查已安装的 IDE(降级检查) const homeDir = process.env.HOME || process.env.USERPROFILE; if (fs.existsSync(path.join(homeDir, '.cursor', 'mcp.json'))) return 'cursor'; if (fs.existsSync(path.join(homeDir, '.claude', 'config.json'))) return 'claude'; if (fs.existsSync(path.join(homeDir, '.trae', 'mcp.json'))) return 'trae'; // 默认使用 Cursor(最流行) return 'cursor'; } // 2. 根据 IDE 获取配置文件路径 function getConfigPath(ide) { const homeDir = process.env.HOME || process.env.USERPROFILE; const configPaths = { 'cursor': path.join(homeDir, '.cursor', 'mcp.json'), 'claude': path.join(homeDir, '.claude', 'config.json'), 'trae': path.join(homeDir, '.trae', 'mcp.json'), 'copilot': path.join(homeDir, '.copilot', 'mcp.json'), 'antigravity': path.join(homeDir, '.gemini', 'antigravity', 'config.json'), 'opencode': path.join(homeDir, '.config', 'opencode', 'mcp.json'), 'windsurf': path.join(homeDir, '.codeium', 'windsurf', 'mcp.json'), 'gemini': path.join(homeDir, '.gemini', 'config.json'), 'codex': path.join(homeDir, '.codex', 'config.toml'), 'manus': path.join(homeDir, '.manus', 'mcp.json'), 'coze': path.join(homeDir, '.coze', 'mcp_config.json') }; return configPaths[ide]; } // 3. 读取并解析配置 const currentIDE = detectCurrentIDE(); const configPath = getConfigPath(currentIDE); if (!fs.existsSync(configPath)) { console.log(`未找到 ${currentIDE} 的 MCP 配置文件`); return; } const configContent = fs.readFileSync(configPath, 'utf8'); const config = JSON.parse(configContent); // 4. 查找 HAP MCP 配置 const mcpServers = config.mcpServers || {}; const hapMcpConfig = Object.entries(mcpServers).find( ([name, config]) => name.startsWith('hap-mcp-') && config.url ); if (hapMcpConfig) { const [mcpName, mcpConfig] = hapMcpConfig; const url = new URL(mcpConfig.url); // 5. 提取 Appkey 和 Sign const appkey = url.searchParams.get('HAP-Appkey'); const sign = url.searchParams.get('HAP-Sign'); console.log(`✅ 从 ${currentIDE} 的 MCP 配置中提取到鉴权信息`); console.log('MCP 名称:', mcpName); console.log('Appkey:', appkey); console.log('Sign:', sign); } else { console.log(`未找到 HAP MCP 配置,请先配置 MCP 或手动输入鉴权信息`); } ``` #### 实际操作流程 当用户需要调用 HAP API 时,AI 应该: 1. **检查是否已配置 MCP** - 读取 Cursor 全局设置文件 - 查找 `hap-mcp-*` 配置 2. **提取鉴权信息** - 如果找到 MCP 配置,自动从 URL 中提取 Appkey 和 Sign - 如果找到多个 MCP 配置,询问用户使用哪个应用 3. **使用提取的密钥** - 在 API 请求头中使用提取的 `HAP-Appkey` 和 `HAP-Sign` - 如果提取失败,提示用户手动提供或检查 MCP 配置 #### 注意事项 - ✅ **优先使用 MCP 配置**: 如果用户已配置 MCP,优先从配置中提取 - ✅ **URL 解码**: 注意 URL 参数可能经过编码,需要正确解码 - ✅ **多个应用**: 如果配置了多个 HAP MCP,询问用户使用哪个应用 - ⚠️ **配置不存在**: 如果未找到 MCP 配置,提示用户先配置 MCP 或手动提供密钥 - ⚠️ **权限问题**: 如果无法读取配置文件,提示用户检查文件权限 **方法二: 手动获取** 如果用户未配置 MCP 或需要手动提供: 1. 登录明道云 → 应用 → 设置 → API 密钥 2. 复制 Appkey 和 Sign 3. 或提供 MCP 配置信息,让 AI 自动提取 ### 2. 配置请求头 所有 HAP V3 API 请求都需要以下请求头: ```javascript const headers = { 'Content-Type': 'application/json', 'HAP-Appkey': 'your-app-key', 'HAP-Sign': 'your-sign-key' }; ``` **⚠️ 注意:** 请求头使用 `HAP-Appkey` 和 `HAP-Sign`(不是 `AppKey` 和 `Sign`) ### 3. 获取 API 文档 **使用 Apifox MCP Server(推荐):** ```json { "应用 API - API 文档": { "command": "npx", "args": [ "-y", "apifox-mcp-server@latest", "--site-id=5442569" ] } } ``` **在线文档资源:** - [API 整体介绍](https://apifox.mingdao.com/7271706m0.md) - [字段类型对照表](https://apifox.mingdao.com/7271709m0.md) - [筛选器使用指南](https://apifox.mingdao.com/7271713m0.md) - [错误码说明](https://apifox.mingdao.com/7271715m0.md) --- ## 核心工作流程 ### 阶段一: 准备工作 **Step 1: 获取 API 凭证** - **优先方式**: 从 Cursor MCP 配置中自动提取 Appkey 和 Sign(如果用户已配置) - **备选方式**: 从 HAP 后台手动获取或让用户提供 **Step 2: 配置 API 请求头** - 使用提取或提供的 Appkey 和 Sign 设置请求头 - 设置 `HAP-Appkey` 和 `HAP-Sign` 请求头 ### 阶段二: 创建应用结构 **Step 3: 获取应用信息(可选)** ```javascript GET /v3/app/info ``` **Step 4: 创建工作表** ```javascript POST /v3/app/worksheets { "name": "客户信息表", "alias": "customers", "fields": [ { "name": "客户名称", "alias": "customer_name", "type": "Text", "isTitle": true, "required": true } ] } ``` **详细规范:** 参考 `references/hap-api-usage-guide.md` 第 1 节 ### 阶段三: 填充数据 **Step 5: 准备选项字段映射** - 对于单选/多选字段,需要先获取选项的 key(UUID) - 查询工作表结构获取 options 列表 **Step 6: 创建记录** ```javascript POST /v3/app/worksheets/{worksheet_id}/rows { "fields": [ { "id": "customer_name", "value": "明道云科技有限公司" }, { "id": "customer_type", "value": ["74c7b607-864d-4cc4-b401-28acba2636e9"] // ⚠️ 使用选项key } ], "triggerWorkflow": true } ``` **关键点:** - ⚠️ 选项字段必须用 key,不能用显示文本 - ⚠️ 选项字段即使单选也要用数组格式 - ✅ 数值字段写入时传数字,读取时返回字符串 **详细规范:** 参考 `references/hap-api-usage-guide.md` 第 3 节 ### 阶段四: 查询和分析数据 **Step 7: 查询记录列表** ```javascript POST /v3/app/worksheets/{worksheet_id}/rows/list { "filter": { "type": "group", "logic": "AND", "children": [ { "type": "condition", "field": "customer_type", "operator": "eq", "value": ["74c7b607-864d-4cc4-b401-28acba2636e9"] // 使用key } ] }, "sorts": [{ "field": "annual_budget", "isAsc": false }], "pageIndex": 1, "pageSize": 20 } ``` **详细规范:** 参考 `references/hap-api-usage-guide.md` 第 4 节 --- ## Filter 筛选器规范 ⭐重点 ### Filter 对象结构 **基础结构:** ```typescript Filter = { type: 'group' | 'condition'; // type='group' 时的字段 logic?: 'AND' | 'OR'; children?: Filter[]; // 子条件,最多两层嵌套 // type='condition' 时的字段 field?: string; // 字段ID或别名 operator?: string; // 操作符 value?: any[]; // 值数组 } ``` **规则:** 1. 顶层必须是 `group` 类型 2. 最多两层嵌套: group → group → condition 3. 同一 group 的 children 必须类型一致 4. group 必须指定 `logic` (AND/OR) 5. condition 必须指定 `field`, `operator` ### 操作符完整列表 | 操作符 | 说明 | 需要value | value格式 | 适用字段 | |-------|------|----------|----------|---------| | `eq` | 等于 | ✅ | `["值"]` | 所有类型 | | `ne` | 不等于 | ✅ | `["值"]` | 所有类型 | | `contains` | 包含 | ✅ | `["值"]` | Text, MultipleSelect | | `notcontains` | 不包含 | ✅ | `["值"]` | Text, MultipleSelect | | `startswith` | 开头是 | ✅ | `["值"]` | Text | | `endswith` | 结尾是 | ✅ | `["值"]` | Text | | `gt` | 大于 | ✅ | `["值"]` | Number, Date | | `gte` | 大于等于 | ✅ | `["值"]` | Number, Date | | `lt` | 小于 | ✅ | `["值"]` | Number, Date | | `lte` | 小于等于 | ✅ | `["值"]` | Number, Date | | `between` | 介于之间 | ✅ | `["最小值", "最大值"]` | Number, Date | | `isempty` | 为空 | ❌ | 不需要 | 所有类型 | | `isnotempty` | 不为空 | ❌ | 不需要 | 所有类型 | | `belongsto` | 属于 | ✅ | `["ID1", "ID2"]` | Relation, Department | | `in` | 在...中 | ✅ | `["值1", "值2"]` | 所有类型 | | `concurrent` | 同时包含 | ✅ | `["值1", "值2"]` | MultipleSelect | ### 筛选示例 **示例1: 单选字段筛选(⚠️ 必须使用 key)** ```javascript { "type": "group", "logic": "AND", "children": [{ "type": "condition", "field": "customer_type", "operator": "eq", "value": ["74c7b607-864d-4cc4-b401-28acba2636e9"] // ✅ 使用key }] } // ❌ 错误: value: ["成交客户"] // 不能用显示文本! ``` **示例2: 数值范围筛选(⚠️ value 必须是字符串数组)** ```javascript { "type": "condition", "field": "annual_budget", "operator": "between", "value": ["500000", "2000000"] // ✅ 字符串数组 } // ❌ 错误: value: [500000, 2000000] // 不能用数字! ``` **示例3: 关联字段筛选(⚠️ 必须用 belongsto)** ```javascript { "type": "condition", "field": "related_customer", "operator": "belongsto", // ✅ 关联字段用 belongsto "value": ["customer-row-id"] } // ❌ 错误: operator: "eq" // 关联字段不支持 eq! ``` **详细规范:** 参考 `references/hap-api-usage-guide.md` 第 4 节 --- ## 字段类型处理规范 ### 关键字段类型处理 #### 1. 选项字段(SingleSelect/MultipleSelect)⭐⭐⭐ **写入:** 必须传选项 key 的数组 ```javascript { "id": "customer_type", "value": ["74c7b607-864d-4cc4-b401-28acba2636e9"] // 选项key } ``` **读取:** 返回包含 key 和 value 的对象数组 ```javascript { "customer_type": [ { "key": "74c7b607-864d-4cc4-b401-28acba2636e9", "value": "成交客户" } ] } ``` **⚠️ 关键点:** - 即使是单选,也要用数组 `["key"]` - 不能传显示文本 `["成交客户"]`,必须用 key - 筛选时必须使用 key,不能使用显示文本 #### 2. 附件字段(Attachment)⭐ **写入:** 支持 URL 和 base64 ```javascript { "id": "attachments", "type": "0", // 0=覆盖, 1=追加 "value": [{ "name": "产品宣传册.pdf", "url": "https://example.com/brochure.pdf" }] } ``` **读取:** 返回附件对象数组 ```javascript { "attachments": [{ "file_id": "...", "file_name": "...", "downloadUrl": "https://...", // ⚠️ 使用 downloadUrl "file_size": 2048576 }] } ``` **⚠️ 重要提示:** - 附件上传是异步处理,通常需要 5-10 秒 - API 返回成功不代表附件已上传完成 - 使用 `downloadUrl` 而非 `url` #### 3. 关联字段(Relation)⭐⭐⭐ **写入:** 传记录 ID 数组 ```javascript { "id": "related_customer", "value": ["945e6503-3823-4e91-9d84-a53f8bdd6fc5"] // 记录rowid } ``` **读取:** 返回对象数组(只包含 sid 和 name) ```javascript { "related_customer": [{ "sid": "945e6503-3823-4e91-9d84-a53f8bdd6fc5", "name": "明道云科技有限公司" }] } ``` **获取完整关联数据:** ```javascript // 方法1: 使用专用 API GET /v3/app/worksheets/{worksheet_id}/rows/{row_id}/relations/{field_id} // 方法2: 使用 sid 查询目标表 POST /v3/app/worksheets/{target_worksheet_id}/rows/list { "filter": { "type": "group", "logic": "AND", "children": [{ "type": "condition", "field": "rowid", // ⚠️ 使用系统字段 rowid "operator": "in", "value": ["sid1", "sid2"] // 传入关联记录的 sid }] } } ``` **详细规范:** 参考 `references/hap-api-usage-guide.md` 第 6 节 #### 4. 成员字段(Collaborator) **写入:** 传用户 ID 数组 ```javascript { "id": "owner", "value": ["user-account-id-123"] // 用户ID,不是用户名 } ``` **获取用户ID:** ```javascript POST /v3/users/lookup { "name": "张三" // 精确匹配姓名 } ``` #### 5. 数值字段(Number) **写入:** 传数字类型 ```javascript { "id": "annual_budget", "value": 1000000.50 } ``` **读取:** 返回字符串 ```javascript { "annual_budget": "1000000.50" } ``` **⚠️ 注意:** 写入数字,读取字符串 **详细规范:** 参考 `references/hap-api-usage-guide.md` 第 2、3 节 --- ## triggerWorkflow 参数详解 ⭐重要 `triggerWorkflow` 参数控制是否在数据操作时触发工作表相关的工作流。 **适用范围:** - ✅ 创建记录 - ✅ 批量创建 - ✅ 更新记录 - ✅ 批量更新 - ✅ 删除记录 - ✅ 批量删除 **参数说明:** | 参数值 | 说明 | 默认值 | 使用场景 | |-------|------|--------|---------| | `true` | 触发工作流 | ✅ 是 | 正常业务操作,需要执行自动化流程 | | `false` | 不触发工作流 | ❌ 否 | 数据迁移、批量初始化、测试数据 | **✅ 应该设置为 `true` 的场景:** - 正常业务操作(用户提交表单、更新状态等) - 需要自动化处理的操作 **❌ 应该设置为 `false` 的场景:** - 数据迁移和导入 - 批量数据初始化 - 定时同步任务 - 测试和调试 **性能影响:** - `triggerWorkflow: false` - API 响应快,通常 < 500ms - `triggerWorkflow: true` - 需要等待工作流执行,可能需要 1-5 秒 **详细说明:** 参考 `references/hap-api-usage-guide.md` 第 3.2 节 --- ## 常见陷阱与解决方案 ⭐⭐⭐ ### 陷阱1: 选项字段筛选使用显示文本 **问题:** 筛选单选/多选字段时返回空结果 **错误示例:** ```javascript { "field": "customer_type", "operator": "eq", "value": ["成交客户"] // ❌ 使用了显示文本 } ``` **正确做法:** ```javascript { "field": "customer_type", "operator": "eq", "value": ["74c7b607-864d-4cc4-b401-28acba2636e9"] // ✅ 使用选项key } ``` **解决方案:** 1. 初始化时查询工作表结构,缓存选项映射 2. 或先查询一条记录,从返回数据获取 key 3. 建立 value → key 的映射表 ### 陷阱2: 数值字段筛选使用数字类型 **问题:** 数值筛选无结果或报错 **错误示例:** ```javascript { "field": "annual_budget", "operator": "gt", "value": [1000000] // ❌ 数字类型 } ``` **正确做法:** ```javascript { "field": "annual_budget", "operator": "gt", "value": ["1000000"] // ✅ 字符串数组 } ``` **记忆口诀:** 筛选条件的 value 永远是字符串数组 ### 陷阱3: 关联字段使用错误的操作符 **问题:** 使用错误的操作符筛选关联字段 **错误示例:** ```javascript { "field": "related_customer", "operator": "eq", // ❌ 关联字段不支持 eq "value": ["customer-id"] } ``` **正确做法:** ```javascript { "field": "related_customer", "operator": "belongsto", // ✅ 使用 belongsto "value": ["customer-id"] } ``` ### 陷阱4: 关联字段 N+1 查询问题 **问题:** 在列表页逐个查询关联数据 **错误示例:** ```javascript // ❌ 性能灾难:100个产品 = 1 + 100 = 101次请求 const products = await getProductList(); // 1次请求 for (const product of products) { const categoryId = product.category[0].sid; const category = await getCategoryById(categoryId); // 100次请求! } ``` **正确做法:** 批量查询 ```javascript // ✅ 性能优化:100个产品 = 1 + 1 = 2次请求 const products = await getProductList(); // 1次请求 // 收集所有分类ID const categoryIds = new Set(); products.forEach(p => { if (p.category && p.category.length > 0) { categoryIds.add(p.category[0].sid); } }); // 批量查询所有分类 const categories = await getRows('category-worksheet-id', { filter: { type: 'condition', field: 'rowid', operator: 'in', value: Array.from(categoryIds) } }); // 1次请求 // 建立映射 const categoryMap = {}; categories.rows.forEach(cat => { categoryMap[cat.rowid] = cat; }); ``` **详细说明:** 参考 `references/hap-api-usage-guide.md` 第 7 节 --- ## 性能优化建议 ### 1. 查询优化 - **合理使用分页**: pageSize 不要超过 1000 - **指定返回字段**: 使用 fields 参数,只返回需要的字段 - **使用字段ID**: 比别名查询性能更好 - **避免过度嵌套**: Filter 嵌套控制在 2 层以内 - **善用视图**: 复杂筛选可先创建视图,再查询视图 ### 2. 批量操作优化 - **批量创建**: 一次最多 100 条 - **批量更新**: 一次最多 100 条 - **包含关联字段**: 减少到每批 30-50 条 - **包含附件**: 建议单独处理,不要批量 - **批次间延迟**: 每批间隔 1-2 秒 ### 3. 关联字段优化 - **减少嵌套查询**: 使用 get_record_relations API 一次获取 - **批量查询**: 避免 N+1 查询问题 - **缓存关联数据**: 频繁访问的关联数据可缓存 - **控制 showFields**: 只显示必要字段,减少数据量 **详细说明:** 参考 `references/hap-api-usage-guide.md` 第 8 节 --- ## 🤖 AI 助手使用指南 当用户需要调用 HAP V3 API 时,AI 助手应该遵循以下原则: ### 1. 自动提取鉴权密钥 **优先级顺序:** 1. **优先从 MCP 配置提取**(推荐) - 读取 Cursor 全局设置文件 - 查找 `hap-mcp-*` 配置 - 从 URL 中提取 `HAP-Appkey` 和 `HAP-Sign` - 如果找到多个配置,询问用户使用哪个应用 2. **用户手动提供** - 如果未找到 MCP 配置,提示用户提供 Appkey 和 Sign - 或引导用户先配置 MCP 3. **引导配置 MCP** - 如果用户有 MCP 配置信息,帮助用户配置到 Cursor - 然后从配置中提取密钥 ### 2. 配置请求头 提取到密钥后,自动配置请求头: ```javascript const headers = { 'Content-Type': 'application/json', 'HAP-Appkey': extractedAppkey, // 从 MCP 配置提取 'HAP-Sign': extractedSign // 从 MCP 配置提取 }; ``` ### 3. 处理多个应用 如果用户配置了多个 HAP MCP: - **明确指定应用名**: 如果用户提到具体应用名,使用对应的配置 - **询问用户**: 如果未指定,列出所有配置的应用,让用户选择 - **默认使用**: 如果只有一个配置,直接使用 ### 4. 错误处理 - **配置不存在**: 提示用户先配置 MCP 或手动提供密钥 - **URL 解析失败**: 检查 URL 格式是否正确 - **参数缺失**: 检查 Appkey 和 Sign 是否都存在 - **权限问题**: 如果无法读取配置文件,提示用户检查文件权限 ### 5. 实际操作示例 **场景**: 用户说"帮我调用 HAP API 查询数据" **AI 操作流程**: 1. 读取 Cursor 全局设置文件 `~/Library/Application Support/Cursor/User/settings.json` 2. 查找 `mcpServers` 中的 `hap-mcp-*` 配置 3. 如果找到配置,从 URL 中提取 Appkey 和 Sign 4. 如果找到多个配置,询问用户使用哪个应用 5. 使用提取的密钥配置 API 请求头 6. 执行 API 调用 **场景**: 用户提供了 MCP 配置信息 **AI 操作流程**: 1. 先帮助用户将 MCP 配置添加到 Cursor 全局设置文件 2. 然后从配置中提取 Appkey 和 Sign 3. 使用提取的密钥进行后续 API 调用 --- ## 最佳实践 ### 1. 初始化阶段 **必做事项:** 1. 查询所有工作表结构 2. 缓存所有选项字段的 key-value 映射 3. 缓存工作表 ID 和字段 ID 4. 建立用户姓名→ID 映射 **示例代码:** ```javascript // 1. 获取工作表结构 const structure = await getWorksheetStructure(worksheetId); // 2. 提取选项字段映射 const optionMaps = {}; structure.fields.forEach(field => { if (field.type === 'SingleSelect' || field.type === 'MultipleSelect') { optionMaps[field.id] = {}; field.options.forEach(opt => { optionMaps[field.id][opt.value] = opt.key; // value → key }); } }); // 3. 使用时查找key const customerTypeKey = optionMaps['customer_type']['成交客户']; ``` ### 2. 查询阶段 **建议:** 1. 优先使用字段 ID 而不是别名 2. 选项字段必须用 key,提前转换 3. 数值字段 value 用字符串 4. 关联字段用 belongsto 操作符 5. 合理设置 pageSize(建议 100-500) ### 3. 写入阶段 **检查清单:** - [ ] 选项字段 value 是数组格式 - [ ] 选项字段传的是 key 不是 value - [ ] 数值字段传数字类型 - [ ] 关联字段传的是 rowid - [ ] 成员字段传的是 accountId - [ ] 附件字段设置了 type 参数 ### 4. 错误处理 **常见错误码:** - `error_code: 1` - 成功 - `error_code: -1` - 失败,查看 error_msg - `error_code: 4` - 权限不足 - `error_code: 10` - 参数错误 **建议:** 所有 API 调用都要检查 error_code 和 success **详细说明:** 参考 `references/hap-api-usage-guide.md` 第 9 节 --- ## 常用 API 端点速查 | 场景 | API 端点 | 关键参数 | |-----|---------|---------| | 创建工作表 | `POST /v3/app/worksheets` | fields | | 添加字段 | `POST /v3/app/worksheets/{id}` | addFields | | 创建记录 | `POST /v3/app/worksheets/{id}/rows` | fields | | 批量创建 | `POST /v3/app/worksheets/{id}/rows/batch` | rows | | 查询记录 | `POST /v3/app/worksheets/{id}/rows/list` | filter, sorts | | 更新记录 | `POST /v3/app/worksheets/{id}/rows/{row_id}` | fields | | 批量更新 | `PUT /v3/app/worksheets/{id}/rows/batch` | rowIds, fields | | 删除记录 | `DELETE /v3/app/worksheets/{id}/rows/{row_id}` | permanent | | 批量删除 | `DELETE /v3/app/worksheets/{id}/rows/batch` | rowIds, permanent | | 透视分析 | `POST /v3/app/worksheets/{id}/rows/pivot` | rows, values | | 查找用户 | `POST /v3/users/lookup` | name | | 查找部门 | `POST /v3/departments/lookup` | name | | 获取地区 | `POST /v3/regions` | search, id | --- ## 参考资源 ### 核心文档 - **`references/hap-api-usage-guide.md`** - HAP V3 API 使用规范完整指南 - 快速开始 - API 使用流程 - 创建工作表规范 - 字段类型参数详解 - 创建/更新记录规范(triggerWorkflow 详解) - 查询筛选规范(Filter 对象结构、操作符列表) - 数据透视分析规范 - 关联字段完整指南 - 常见陷阱与解决方案 - 性能优化建议 - 最佳实践总结 ### 在线文档 - [API 整体介绍](https://apifox.mingdao.com/7271706m0.md) - [字段类型对照表](https://apifox.mingdao.com/7271709m0.md) - [筛选器使用指南](https://apifox.mingdao.com/7271713m0.md) - [错误码说明](https://apifox.mingdao.com/7271715m0.md) ### 相关技能 - **HAP 前后端项目搭建指南** - 使用 HAP 作为数据库搭建独立网站 - **HAP MCP 使用指南** - 了解如何使用 HAP MCP 进行应用管理 - **HAP 视图插件开发指南** - 开发 HAP 自定义视图插件 --- ## 关键概念速查 **字段类型 (type):** - 基础: `Text`, `Number`, `Date`, `Time` - 选择: `SingleSelect`, `MultipleSelect` - 关系: `Relation`, `Collaborator`, `Department` - 其他: `Attachment`, `Rating` **筛选操作符 (operator):** - 比较: `eq`, `ne`, `gt`, `gte`, `lt`, `lte` - 文本: `contains`, `startswith`, `endswith` - 范围: `between`, `in` - 关联: `belongsto` - 空值: `isempty`, `isnotempty` **subType 参数:** - Collaborator: `0`=单选, `1`=多选 - Relation: `1`=单条, `2`=多条 - Time: `1`=时:分, `6`=时:分:秒 - Date: `3`=年月日, `6`=年月日时分秒 --- ## 错误排查清单 **筛选无结果:** - [ ] 选项字段是否用了 key 而不是 value? - [ ] 数值字段 value 是否用了字符串? - [ ] 关联字段是否用了 belongsto? - [ ] Filter 嵌套是否超过 2 层? - [ ] 字段 ID 是否正确? **创建/更新失败:** - [ ] 必填字段是否都提供了? - [ ] 关联字段的 dataSource 是否存在? - [ ] 选项字段的 key 是否有效? - [ ] 成员字段的 accountId 是否有效? - [ ] 数值字段是否超出范围? **数据异常:** - [ ] 附件是否等待了 5-10 秒? - [ ] 日期精度 subType 是否正确? - [ ] 关联记录是否已删除? --- **技能版本**: v2.0 **最后更新**: 2026-01-11 **基于**: HAP API V3 **详细规范**: 参考 `references/hap-api-usage-guide.md`