# 2026-07-25 Session Learnings

## 1. 三表一书「关键人物」≠ MCP商机「当前负责人」

**症状**：用 MCP 商机的 `当前负责人`（刘新元/赵云浩）填充三表一书「关键人物」。
**用户纠正**："关键人物是客户那边的，不是宝锐内部的。"

**根因**：
- `商机.当前负责人` = 宝锐内部销售/技术（刘新元/赵云浩/时彦祎等）
- 三表一书「关键人物」= 客户侧决策链（研发负责人/采购负责人/老板）

**正确做法**：
1. 从客户 `field_555761`（客户关联联系人）获取联系人ID列表
2. 从联系人类型字段获取：`name`（姓名）、`field_cd82a5`（职务）、`field_c7c8c9`（电话）、`field_7aa285`（联系人类别）
3. 填充到三表一书关键人物下拉框
4. 保留手动输入框作为备用

## 2. HTML 跨页面链接必须用绝对路径

**症状**：从 `工具/销售工作台.html` 点击 📋 跳转到 `工具/工具/宝锐三表一书.html`（路径重复404）

**根因**：`<a href="工具/...">` 相对路径从当前页面所在目录解析

**修复**：`<a href="/工具/...">` 绝对路径

```bash
# 排查
grep -n 'href="[^/#]' xxx.html
```

## 3. 客诉详情页 URL 格式修正

**旧格式**（已废弃）：`https://project.feishu.cn/br-shgl/workObjectView/khts/{id}?scope=workspaces`
**正确格式**：`https://project.feishu.cn/br-shgl/khts/detail/{id}`

## 4. 飞书项目空间速查

| 空间 | project_key | simple_name |
|------|-------------|-------------|
| 销售管理 | 6593cd71471290e3cc6be6e6 | xsguanli |
| 客户项目管理 | 658bb60520ea78a2125f1b99 | csrw |
| 售后管理 | 658288abfb8bd616b17025f1 | br-shgl |

销售管理中：客户(kh/65ae1e403c87b152f3365ca6)、商机(story)、联系人(lxr/65b8a6cacde27fd415d769ca)、活动(sj/65ae1e5d44338dbe7c39a29a)

## 5. 三表一书 × 飞书集成架构

```
飞书MCP → Agent MQL查询 → customer_feishu_data.json → HTTP API(/api/feishu/customer_data) → HTML fetch
```

新增端点代码（`销售工作台服务.py`）：
```python
FEISHU_DATA_FILE = os.path.join(DATA_DIR, 'customer_feishu_data.json')

# do_GET 中：
if path == '/api/feishu/customer_data':
    qs = parse_qs(self.path.split('?')[1]) if '?' in self.path else {}
    cid = qs.get('customer_id', '')
    with open(FEISHU_DATA_FILE) as f:
        all_data = json.load(f)
    customer_data = all_data.get(str(cid))
    if customer_data: self._send_json({'ok': True, 'data': customer_data})
    else: self._send_json({'ok': False, 'error': 'no data'}, 404)
```

## 6. render() innerHTML 全量重建导致搜索框失焦/跳动

**症状**：客户页搜索框输入文字后跳动、闪烁，焦点被移走。

**根因**：`render()` → `renderCustomers(main)` → `main.innerHTML = '...'` 全量重建 DOM。搜索框 `<input id="custSearch">` 随着整个 main 区域被销毁后重建为全新元素。加上 `cs.focus()` 恢复焦点，产生"销毁→重建→聚焦"的跳动循环。

### ❌ 旧方案（hack，有跳动副作用）

在 `render()` 末尾恢复焦点：
```javascript
if (state.tab === 'customers') {
    var cs = document.getElementById('custSearch');
    if (cs) { cs.focus(); cs.setSelectionRange(cs.value.length, cs.value.length); }
}
```
**缺点**：搜索框仍被销毁重建，视觉上会跳动闪烁。

### ✅ 新方案（2026-07-25）：持久化工具栏

将搜索框和筛选器从可被重建的 `main` 区域移到持久化的 `#custToolbar` div：

**步骤**：
1. 在 HTML 结构中 `<main>` 上方添加 `<div id="custToolbar" style="display:none;"></div>`
2. 创建 `renderCustToolbar()` 函数，仅在首次进入 Tab 时渲染搜索框+筛选器
3. 用 `_rendered` 标记防止后续 render() 重建工具栏
4. `renderCustomers()` 从主内容中移除搜索框和筛选器代码
5. 工具栏计数用 `custCount` 元素实时更新，不重建整个工具栏

**关键代码**：
```javascript
// render() 中
var tb = document.getElementById('custToolbar');
if (state.tab === 'customers' && state.data.mcpCustomers) {
    if (!tb._rendered) renderCustToolbar();  // 仅首次渲染
    tb.style.display = 'flex';
} else {
    tb.style.display = 'none';
    tb._rendered = false;  // 切出Tab时重置，下次进入会重建
}

// renderCustToolbar() 渲染搜索框+部门筛选+计数标签
function renderCustToolbar() {
    document.getElementById('custToolbar').innerHTML =
        '<h3>📋 客户清单</h3>' +
        '<select onchange="customerDeptFilter=this.value;render()">...</select>' +
        '<div class="search-box"><input id="custSearch" ...></div>' +
        '<span id="custCount"></span>';
    document.getElementById('custToolbar')._rendered = true;
}

// renderCustomers() 末尾更新计数
var cc = document.getElementById('custCount');
if (cc) cc.textContent = filtered.length + ' 条';
```

**CSS**：
```css
#custToolbar { display: flex; align-items: center; gap: 10px; padding: 10px 24px; 
               background: #fff; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
#custToolbar h3 { font-size: 14px; font-weight: 800; white-space: nowrap; }
```

**效果**：用户输入时只有下面数据更新，搜索框完全不被销毁，不再跳动。

## 7. MCP 写操作需要用户在飞书中开启权限（2026-07-25 晚间）

**症状**：`create_workitem`、`add_comment`、`update_field`、`transition_node` 等所有写操作均返回：
```
user has not enabled this MCP feature. Please configure it in your personal preferences
```
读操作（`search_by_mql`、`list_workitem_types`、`get_view_detail`）完全正常。

**根因**：飞书项目 MCP 的读写权限是分开控制的。当前 Token `m-abfb29e8-...` 仅开通了读权限。

**修复**：用户在飞书项目中手动开启 → 右上角头像 → 个人偏好设置 → MCP/API 设置 → 开启「创建/编辑工作项」等写权限。

**降级方案**（写权限开启前临时方案）：使用飞书 URL 跳转代替 MCP 写操作：
- 新建活动：`https://project.feishu.cn/baoyu/workitem/create?project_key=6593cd71471290e3cc6be6e6&work_item_type_key=65ae1e5d44338dbe7c39a29a`
- 查看工作项：`https://project.feishu.cn/baoyu/workitem/{id}`

## 8. 飞书「活动」工作项 = 拜访纪要（2026-07-25）

飞书项目销售管理空间中的「活动」类型（type_key=`65ae1e5d44338dbe7c39a29a`）天然包含拜访纪要所需全部字段：

| 活动字段 | field_key | 用途 |
|----------|-----------|------|
| 名称 | `name` | 拜访标题 |
| 活动类型 | `field_76654e` | 线下拜访(v4zlj721e)/线上跟进(5vezmmm1y)/陌拜(wg15dd75b)/协同拜访(wsxnxsd60) |
| 活动关联客户 | `field_5f20fc` | 关联飞书客户 |
| 活动描述 | `field_b99055` | 富文本，拜访纪要正文 |
| 开始时间 | `field_a2b3f6` | date（毫秒时间戳） |
| 结束时间 | `field_8304fb` | date |
| 后续计划 | `field_847fa8` | 文本 |
| 跟进部门 | `field_5ed7ab` | select |
| 待办事项 | `field_68e9d5` | 关联资源协作任务 |
| 附件 | `field_a4c7e0` | multi-file |

**模板 ID**：`700373`（默认商机跟进管理类型）

**在工作台中集成**：顶栏加 📝 按钮，点击 → `window.open(FEISHU_NEW_ACTIVITY, '_blank')`。

## 9. CloudBase 数据定时同步（2026-07-25）

将本地 MCP 管道产出的 JSON 自动推送到 CloudBase 静态托管，实现页面数据自动刷新。

**同步脚本**：`~/Desktop/Hermes输出-工作类/脚本/cb_sync.sh`
```bash
cd ~/Desktop/Hermes输出-工作类/数据
python3 mcp_refresh.py          # MCP客诉+活动
python3 mcp_kpi_pipeline.py     # KPI  
cd ../脚本
python3 refresh_erp.py          # ERP数据
python3 update_kpi_progress.py  # KPI进度
# 上传到CloudBase
for f in 数据/mcp_*.json 数据/kpi_*.json ...; do
  tcb hosting deploy "$f" "$f" -e bier-sales-d0gatbvlx288724e9
done
```

**Cron 配置**：`cronjob create --schedule "0 */2 * * *" --script 脚本/cb_sync.sh --no_agent`

**⚠️** Mac 必须开机 + Hermes 运行中才执行。CDN 缓存 2-5 分钟。
