# 飞书推送：按销售员分发（2026-07-27 终版）

## 两个 App

| App | ID | Secret | 当前用途 |
|-----|----|--------|---------|
| 消息推送 | `cli_aac5af01fd625bdd` | `FxAZsdQWVRuPOIK8byWDccwNTan7AadN` | 顺丰推送（仅可推给刘新元） |
| 宝锐销售工作台 | `cli_aaee6a2f90b89bc1` | `arht5fd8wyppZysFvOjUcb2bV0UMVVYF` | 工作台网页 + 顺丰推送（已就绪）✅ |

## 工作台 App 开通推送能力步骤

1. 飞书开放平台 → `cli_aaee6a2f90b89bc1`
2. 「添加应用能力」→ 勾选「机器人」
3. 「权限管理」→ 开通 `im:message`、`im:message:send`、`contact:user.id:readonly`
4. 「应用发布」→ 发布新版本（能力变更后必须发布）
5. 「应用发布」→ 可用范围添加赵云浩等销售员
6. 销售员需从飞书工作台入口打开应用一次（触发 OAuth → 创建 open_id）

## open_id 是按 App 独立的

**关键**：`cli_aaee6a2f90b89bc1` 和 `cli_aac5af01fd625bdd` 对同一用户产生**不同 open_id**。`batch_get_id` 只对已 OAuth 登录该 App 的用户返回 open_id。

## 完整推送架构

```
订单物流变化 → pull_orders → shipment_pipeline → 状态diff
    ↓
按creator分组 → email→open_id(缓存24h) → 飞书卡片推送每人
```

## 代码实现（sf_delivery_watchdog.py）

```python
def get_openid_map(emails, token):
    cache_file = 'sf_openid_cache.json'
    cached = {}
    try:
        if time.time() - os.path.getmtime(cache_file) < 86400:
            with open(cache_file) as f: cached = json.load(f)
    except: pass
    missing = [e for e in emails if e not in cached]
    if missing:
        body = json.dumps({'emails': missing}).encode()
        req = Request(f'{FEISHU_API}/contact/v3/users/batch_get_id?user_id_type=open_id',
            data=body, headers={'Authorization': f'Bearer {token}'})
        # 解析 user_list → cached[email] = user_id
    return cached

# 按创建者分组推送
for o in items:
    cr = o.get('creator', '') or '未知'
    email = creator + '@biori.com'
    oid_map = get_openid_map([email], _feishu_token)
    oid = oid_map.get(email, '')
    if not oid: continue  # 无open_id则跳过
    send_feishu_card(oid, title, body, tn)
```

## 已确认 open_id

| 姓名 | open_id |
|------|--------|
| 刘新元 | `ou_901b790c9afdda4a8f14554962b32664` |
| 赵云浩 | `ou_7fab49a17fecc17934456d76a45d0ca0` |
