# baoyu-workbench-mcp-pipeline — archive changelog

### 2. 表名用完整 type_key（非 api_name 别名）

先用 `list_workitem_types` 获取 type_key：

| 别名（❌） | type_key（✅） |
|-----------|---------------|
| `kh` | `65ae1e403c87b152f3365ca6` |
| `xsdd` | `662612aea6bb7089fea044ef` |
| `sj` | `65ae1e5d44338dbe7c39a29a` |


### 4. 响应解析（必须处理 null value）

MCP 返回 `moql_field_list`，`value` 可能为 `null`（字段无值）。解析前必须先判空，否则 `TypeError`：

```python
for f in item.get('moql_field_list', []):
    k = f['key']
    v = f.get('value')
    # ⚠️ MCP 有时把值包在数组里：数字字段常见 [{double_value: 162000}]
    if isinstance(v, list) and len(v) > 0: v = v[0]
    if v is None:
        flat[k] = ''
    elif 'string_value' in v:
        flat[k] = v['string_value']
    elif 'long_value' in v:
        flat[k] = v['long_value']
    elif 'user_value' in v:
        flat[k] = v['user_value']
    elif 'user_value_list' in v:
        flat[k] = v['user_value_list']
    elif 'key_label_value' in v:
        flat[k] = v['key_label_value']
    elif 'key_label_value_list' in v:
        flat[k] = v['key_label_value_list']
    else:
        flat[k] = str(v)
```


### 5. 翻页 — ⛔ session_id 自动化禁用，OFFSET 唯一可靠

> **🔴 2026-09-09：MCP `search_by_mql` 的 LIMIT OFFSET 硬上限为 1000！**
> `LIMIT 50 OFFSET 1050` 返回 `error=ErrMoqlInvalidArgument, LIMIT offset 1050 exceeds the maximum of 1000`。
> 即 OFFSET 翻页最多拉 21 页 = 1050 行（offset 0..1000）。**表 >1000 行时 OFFSET 翻页会静默截断**。
> 实测破坏：`_full_refresh_offset.py` 拉客户(1727)/活动(4493)在第22页(OFFSET=1050)失败，只拉到1050条还覆盖了旧完整文件。
> **修复**：按 `start_time` 日期范围分段（每段 <1000 行），逐段 OFFSET 拉取后按 work_item_id 全局去重。
> 恢复脚本已落地：`数据/_recover_pull_offset.py`（客户分5段、活动分7段，每段 <1000，写临时文件校验条数后才替换）。

> **🔴 2026-08-08：session_id + group_pagination_list 在 cron/自动化脚本中会无限循环！**
> 每页始终返回 50 条，`len(items) < 50` 永不触发。改用 **OFFSET 分页**。

```python
# ✅ OFFSET 翻页 — cron/自动化唯一推荐
def pull_with_offset(mql, project_key, max_pages=300):
    all_items = []
    seen_ids = set()
    offset = 0
    empty_streak = 0
    for page in range(1, max_pages + 1):
        paginated_mql = f"{mql} LIMIT 50 OFFSET {offset}"
        result = mcp_call_simple(paginated_mql, project_key)
        items = parse_moql(result)
        if len(items) == 0:
            empty_streak += 1
            if empty_streak >= 2: break  # 连续2页空→真正结束
        else:
            empty_streak = 0
        for item in items:
            wid = item.get('work_item_id', '')
            if wid and wid in seen_ids: continue
            if wid: seen_ids.add(wid)
            all_items.append(item)
        offset += 50
    return all_items
```

**关键参数**：
- `max_pages`：硬上限（活动/客户 300，订单 100）
- `empty_streak >= 2`：连续2页空才停止
- `seen_ids` 去重：按 `work_item_id`（OFFSET 偶有边界重叠）
- `list[i].count` 为首页总数参考

## 缓存策略

| 类型 | TTL | 原因 |
|------|-----|------|
| 客户 | 30min | 变化少，数据量大（1700条，拉取~100s） |
| 订单 | 5min | 变化频繁 |
| 活动 | 5min | 变化频繁 |
| 客诉 | 15min | 中等 |

缓存存内存 + 写 JSON 文件做降级备份。

## 前端数据适配
