# feishu-project-mcp — feishu api

### ⚠️ 活动 ≠ 商机（血泪教训）

**`story` 是商机，不是活动。** 活动是独立的 `销售管理.活动` 类型（type_key=`65ae1e5d44338dbe7c39a29a`）。

- 查询商机 → `SELECT ... FROM 销售管理.story`
- 查询活动 → `SELECT ... FROM 销售管理.活动`
- 错查导致：7月活动34条（活动） vs 2条（story），差12倍
- 活动URL格式：`xsguanli/65ae1e5d44338dbe7c39a29a/detail/{id}`

## 常用查询模式


### 审批阻塞检测

⚠️ **`actual_blockers` 列表具有误导性**（2026-07-10 血泪教训）：`get_node_detail` 返回所有 in_progress 节点，但包含刘新元名字不代表他在阻塞——需要检查完整节点链中每个节点的 status。

- `finished`/`done` → 已完成，不是阻塞
- `in_progress`/`doing` → **真正的当前阻塞**
- `not_started` → 尚未到达，被上游阻塞

详见 `references/blocker-analysis.md` — 三类阻塞场景、跨空间分布、正确分析流程。

```
list_todo → 过滤node_name含"审核/评审" → 按days_inactive排序
→ get_node_detail 查看完整节点链 → 区分 true_blocker vs downstream_blocked
```


### 客诉追踪（2026-08-04 新增 MQL 直查方式）

**方式A：MQL 直接查询（推荐，2026-08-04 验证）**

⚠️ **客诉不在 `销售管理` 空间！** `FROM 销售管理.客诉` 报 metadata error。客诉在 **售后管理** 空间（project_key=`658288abfb8bd616b17025f1`），type_key=`6669433056a98249604376de`。

```sql
-- 直接 MQL 查询客诉（2026-08-04 实测 318 条）
SELECT name, start_time, owner, work_item_id
FROM `售后管理`.`6669433056a98249604376de`
ORDER BY start_time DESC LIMIT 50
```

每条返回：`name`（流程名称，如 "20260727-湖南国测-非瘟原位冻干粉萎缩客诉"）、`start_time`（创建时间）、`owner`（创建者，含 name_cn/email）、`work_item_id`。

⚠️ **Chinese WHERE clause pitfall**：`WHERE 状态 != '已关闭'` 的中文字符在 MQL 中触发 `syntax error at position XX near 'ç'`。**无法用 MQL WHERE 过滤中文状态值**。替代方案：不带 WHERE 拉取全量（按 start_time DESC 排序），在客户端用 Python 过滤近 30 天数据。

**客诉详情页链接**：`https://project.feishu.cn/br-shgl/khts/detail/{work_item_id}`

**方式B：视图 API + 二级跳转（获取关联客户）**
```
list_todo → 过滤type_key=6669433056a98249604376de → get_workitem_brief/get_node_detail
```


### ⚠️ 工作项类型主页 URL（2026-07-25 发现）

飞书项目每种工作项类型有一个主页 URL，格式为：
```
https://project.feishu.cn/{simple_name}/{api_name}/homepage
```

| 用途 | URL |
|------|-----|
| 活动主页（列表+新建） | `https://project.feishu.cn/xsguanli/sj/homepage` |
| 商机主页 | `https://project.feishu.cn/xsguanli/story/homepage` |
| 客户主页 | `https://project.feishu.cn/xsguanli/kh/homepage` |

其中 `simple_name` 来自 `search_project_info` 返回的 `simple_name` 字段，`api_name` 来自 `list_workitem_types` 的 `api_name` 字段。

**应用场景**：让销售员手动创建活动时，直接跳转到活动主页（而非 create URL），销售员可以在列表中看到已有活动，再点"新建"手动填写。比 MCP `create_workitem` 更可靠（不依赖写权限）。


### 飞书页面读取回退方案（2026-07-11）

当 MCP API 无法获取表单自定义字段时，用 **AppleScript 操控本机已登录 Chrome**：

```bash
# 1. 一次性开启权限：Chrome → 查看 → 开发者 → 允许 Apple 事件中的 JavaScript
# 2. 导航到页面
osascript -e 'tell application "Google Chrome" to open location "https://project.feishu.cn/..."'
sleep 4
# 3. 提取 innerText
osascript -e '
tell application "Google Chrome"
  set js to "document.body.innerText"
  set result to execute front window\"s active tab javascript js
  return result
end tell'
```

**限制**：飞书虚拟表格不暴露标准DOM元素（无 `<table>`/`<tr>`），仅能提取 `innerText`。Wiki画板文档DOM无文本内容。`screencapture` 需要辅助功能权限。
| 字段 | MQL名 | 备注 |
|------|-------|------|
| 单据编号 | `单据编号` | |
| 金额 | `金额` | `double_value` 数值 |
| 货号 | `货号#` | 注意含#号 |
| 单价 | `单价` | `double_value` 数值 |
| 销售数量 | `销售数量` | `double_value` 数值 |
| 关联客户 | ❌ | 需relation遍历 |
| 销售员 | ❌ | 在role_members中 |

**⚠️ 销售订单是分录级数据**（2026-07-24 实测）：一张订单的每个物料行是独立工作项，`单据编号` 会重复。拉取 500 条时按（单据编号+物料编码+货号#）去重只剩 446 —— 这是正常的同单多行/空编码行，**不是分页重复**，不要因此误判 session_id 翻页有 bug。金额类字段（金额/价税合计/单价/含税单价/销售数量）以 `double_value` 返回。

**销售订单字段共 89 个**（`list_workitem_field_config` 需翻 2 页）。已验证可用的 29 字段 SELECT 集合（2026-07-24 批量拉取）：单据编号、购货单位#、销售订单关联客户、物料名称、物料编码、货号#、出货批号#、规格型号、销售单位、销售部门、单据类型、状态、创建时间、收货人姓名、收货人联系电话#、收货地址、物流单号、合同日期、合同编号#、备注（内部）#、收款条件、产品类型、销售数量、含税单价、单价、金额、价税合计、是否赠品、结算币别、销售组织。

**⚠️ 「出货批号#」字段在飞书侧大量为空（2026-09 实测）**：应按 `出货批号# = 'YYYYMMDD'` 或按客户查批次时，飞书销售订单往往返回空白 —— 佰奥莱博 32 条 FMD2011 行全空，而本地 ERP 导出（`~/Desktop/hermas输入-工作台数据库/2024和2025历史数据/2024全年订单.xlsx`）同订单同货号写着 `20241207`。**批次→订单编号的取证只能走 ERP Excel**，飞书的空白不等于 ERP 空白。

**ERP Excel 定向取证法（无 grep 可用时）**：`read_file` 可解出 xlsx 文本，按「合同日期 与行号近似单调」做二分定位 —— 先用 limit=3 探 1000/2000/3000/4000 行读日期（2024 文件：行1000≈12-19，2000≈12-06，3000≈11-26，4000≈11-13，5063 行含末尾员工表），估计目标日期行号后读 ±60 行窗口即可命中目标订单的整块分录（同一订单行相邻、含组分行）。整文件 ~5000 行、每次读 200 行即可，勿全量灌入上下文。
