# MCP 实时数据 API v2.0 架构（2026-07-26 上线）

## 概述

将销售工作台从「手动 Excel → JSON → 静态文件服务」升级为「MCP 实时拉取 → 缓存 → API → 前端 fetch」。

## 架构图

```
用户浏览器 → CloudBase CDN (index.html)
                 │ 优先 fetch
                 ▼
         Tunnel API (board-celebrate-nylon-address.trycloudflare.com)
                 │
                 ▼
         本地后端 macOS :8765 (销售工作台服务.py)
                 │ HTTP POST JSON-RPC
                 ▼
         飞书项目 MCP (project.feishu.cn/mcp_server/v1)
```

## 三级降级

```
1. Tunnel API（实时，缓存 5-30min）
   ↓ 失败
2. http://127.0.0.1:8765（本地直连，Tunnel 挂了仍可用）
   ↓ 失败
3. 本地 JSON 文件（离线备份，每次缓存刷新时自动写入）
```

## 后端 API 端点

| 端点 | MQL 表名 | 数据量 | 缓存 TTL |
|------|---------|--------|---------|
| GET /api/mcp/customers | `65ae1e403c87b152f3365ca6` | ~1,700 | 30min |
| GET /api/mcp/orders | `662612aea6bb7089fea044ef` | ~2,500 | 5min |
| GET /api/mcp/activities | `65ae1e5d44338dbe7c39a29a` | ~2,500 | 5min |
| GET /api/mcp/complaints | `issue` | 动态 | 15min |
| GET /api/mcp/opportunities | `story` | 动态 | 5min |
| POST /api/mcp/refresh | 强制刷新缓存 | — | — |

## 缓存实现

```python
MCP_CACHE = {}  # 内存缓存 {key: {time, data}}
MCP_CACHE_TTL = {'customers': 1800, 'orders': 300, ...}

# 每次 set_mcp_cache 同时写 JSON 文件作为降级备份
backup_path = f'数据/mcp_{cache_key}.json'
```

## 前端加载

```javascript
var API_BASE_URLS = [
  'https://board-celebrate-nylon-address.trycloudflare.com',
  'http://127.0.0.1:8765'
];

// 优先 API → 降级本地 JSON
function fetchWithFallback(key, apiPath, localPath) {
  if (apiPath) {
    return tryApiUrls(apiPath)
      .then(data => data.items || data)  // MCP API 返回 {items: [...]}
      .catch(() => fetch(localPath));     // 降级 JSON
  }
  return fetch(localPath);
}
```

## 自动刷新

```javascript
var AUTO_REFRESH_MS = 5 * 60 * 1000;
refreshTimer = setInterval(function() {
  if (document.visibilityState === 'visible') refreshData();
}, AUTO_REFRESH_MS);
```

## 关键发现

### MQL 字段级权限
某些字段返回 `no permission`（如 `field_ec1e8b` 行业），导致查询返回空。逐个字段二分法排查，移除无权限字段。

### moql_field_list 展开
MCP 返回的 item 是 `{moql_field_list: [{key, value: {key_label_value: {label}}}]}`，需要展平为 `{key: label}`。

### 翻页模式
- `data` 是 dict，每页一个 `group_id` key
- `list[0].count` 是总行数
- `session_id` 跨页传递
- 每页最多 50 条，末页 `len(items) < 50`

### CORS 处理
```python
def _send_json(self, obj, code=200):
    self.send_header('Access-Control-Allow-Origin', '*')
    ...

def do_OPTIONS(self):
    self.send_header('Access-Control-Allow-Origin', '*')
    ...
```
