---
name: baoyu-workbench-ops
description: 宝锐销售工作台运维参考 — 日常简报格式、MCP活动查询模式、CloudBase部署pitfalls、Funnel配置。
triggers:
  - 简报/日报/每日推送格式
  - MCP活动数据查询
  - 工作台logo/资源丢失
  - Funnel不通/端口问题
---


# 宝锐销售工作台 · 运维参考

## 日常简报格式

见 `references/daily-brief-format.md`。

**生成脚本 v2**：`~/Desktop/Hermes输出-工作类/数据/gen_daily_brief_v2.py`（2026-09-11 起为日报主脚本）— 一条龙：MCP 拉昨日活动（相对时间，OFFSET 翻页）→ 拉客诉全量客户端过滤 → 读 workbench_data.json 近7天订单 + erp_amount.json ERP 金额 → 写 `数据/daily_briefs/latest.md`。含去 HTML 残留（`clean_text`，2026-09-14 起同时剔除飞书富文本内嵌图片 `![](https://...)`）

**🔴 clean_text 标签正则必须按「标签名开头」而非长度截断（2026-09-17 修复）**：飞书富文本行内样式标签可长达 50+ 字符（如 `<span style="background-color: rgb(255, 255, 255)">`），旧写法 `<[^>]{1,40}>` 只截 40 字符 → 残留直出到「风险/受阻」条目正文（实测 1 条）。改为 **`<[a-zA-Z/][^>]{0,300}>`**：要求 `<` 后紧跟字母或 `/`，既覆盖任意长标签，又不会误吃正常文本里的比较符（`CT<40` 无闭合 `>` 不受影响）。改完必须 `grep -c '<[a-zA-Z/]' latest.md` 期望 0。、绿色/红色信号按命中关键词数排序、单客金额仅 ≥5000 元展示。

**🔴 周一必须回退到周五（2026-09-14 修复）**：日报 cron 是 `30 8 * * 1-5`（**只跑工作日**），周一 8:30 的 `RELATIVE_DATETIME_EQ(start_time,'yesterday')` = 周日 = **0 条**（实测周一返回 0，硬编码 `2026-09-13` 也是 0，周五 `2026-09-11` = 94 条）。v2 已加两级兜底：① 相对函数返回 0 → 显式日期区间（昨日）；② 仍为 0 → 逐日回退最多 4 天，取最近有数据的一天并记 `DATA_DATE`。报告标题仍写生成当天日期，数据源行改标 `📅 活动数据来源：9月11日（周五）——9月13日（周日）无活动记录已回退`，底部口径行同步说明。**没有周五回退 = 周五全天活动永远无人覆盖**（周一报周日、周五报周四）。

**🔴 ERP 导出滞后 → 周金额会显示 ¥0.0万（2026-09-16 修复）**：日报的「近7天金额」取自 `erp_amount.json`，而 ERP 导出文件（如 `销售订单_20260101-20260909.xlsx`）常滞后数天。窗口 `TODAY-6~TODAY` 一旦整段落在外，金额就假报 0。**修复**：`gen_daily_brief_v2.py` 检测 `erp_last < TODAY-6` 时自动回退为「ERP 最新 7 天」（`erp_last-6 ~ erp_last`）并在正文标 `导出滞后`、脚注标 `⚠️ ERP 导出未覆盖最近 7 天，已回退口径`。**别把 0 当真实周金额发出**——看到 ¥0.0万 先查 `erp_last` 与最新 ERP 导出文件，多半是导出没更新。

**🔴 今日关注去重**：同一活动若同时命中绿/红关键词，会在「重点进展」和「风险信号」各出现一次（原文确实两头都有信号）。关注区已按 link 去重（绿优先），客诉信号按 客户+关键词 去重；正文两个列表仍各自保留，避免漏掉风险面。

**⚡ `BRIEF_USE_CACHE_COMPLAINTS=1`**：复用当日管线已拉的 `数据/mcp_complaints.json`（该文件每日 08:00 由 daily_full_refresh 刷新，字段 `id/name/created/creator/status`），省掉客诉 7 页 MCP 翻页（约 2 分钟）→ 整篇日报 ~40 秒出稿。08:30 生成、08:35 推送的链路下，这 2 分钟决定推送的是当天还是上一版。不设该变量则回退 MCP 全量拉取。

**生成脚本 v1**：`scripts/gen_daily_brief.py` — 直接处理原始MCP `moql_field_list` 格式（无需预处理），读入活动JSON + 客诉/发货数据 → 输出 `latest.md`。

**日报推送脚本**：`~/.hermes/scripts/push_daily_brief_card.py` — 读 `数据/daily_briefs/latest.md` → 建飞书交互式卡片 → 推送刘新元。格式与陷阱见 `references/feishu-card-push.md`（含「🚀 重点项目子标题截断陷阱」——风险/受阻条目别忘了）。

**⚠️ 两份副本，改一份要同步另一份**：手工/备用副本是 `脚本/send_daily_brief_card.py`（桌面工作区），逻辑同源。**脚注里的工作台 CDN 链接会随重新部署换代**——旧域 `bier-sales-6gilsvtp0e5cc973-...` 实测返回 418（失效），当前有效域是 `bier-sales-d0gatbvlx288724e9-1458438710.tcloudbaseapp.com`。推送前先验：
```bash
curl -s -o /dev/null -w "%{http_code}\n" -m 12 "https://bier-sales-d0gatbvlx288724e9-1458438710.tcloudbaseapp.com/workbench/index.html"  # 期望 200
```

**卡片会静默跳过的内容**：脚本按 `\n\n` 切段，**以 `---` 开头的整段被丢弃**——所以 latest.md 末尾那段 `---\n🤖 生成时间…（未推送，仅本地归档）` 永远不会出现在卡片里（改文案无效，别在这上面浪费轮次）。推送后回读验证：`GET /open-apis/im/v1/messages/{message_id}`，返回体是 `{title, elements:[[{tag:text/a},…]]}` 富文本嵌套结构，**不是**卡片 v1 原 JSON，用一个递归收集 `text`/`href` 的 flatten 函数比对，别按 `header/elements` 结构去解析（会报 KeyError）。

**数据拉取脚本**：`scripts/pull_daily_brief_data.py` — 一条龙拉取昨日活动 + 全量客诉 → 写 `/tmp/daily_brief_activities.json` + `mcp_complaints.json` → 自动调用 `gen_daily_brief.py`。直连 MCP HTTP（不经过 Hermes MCP 工具层），OFFSET 翻页，周末回退。2026-08-12 cron 实测验证成功（98条活动+322条客诉，2+7页）。

**日报日期逻辑（2026-09-11 复核）**：优先用 `RELATIVE_DATETIME_EQ(start_time,'yesterday')` 取昨日活动（**该函数 2026-09-11 实测已恢复可用**）；若返回 0 条但硬编码日期有数据，说明函数再次失效，回退 Python 计算日期 + 周末回退。无论走哪条路径，都要拿两个口径的 count 互相对齐后再出报。日报标题写生成当天日期，标注实际数据日期。

**周一"本周0单"陷阱（2026-08-17 修复）**：`gen_daily_brief.py` 原用 `monday = today - today.weekday()` 算"本周"，周一早上（weekday=0）日历周无订单 → 报"本周0单¥0.0万"。**修复**：订单窗口改为滚动7天 `week_start = today - timedelta(days=6)`，字幕/小节标题改为"近7天"。同时新增活动数据日期标注行：正常日为"📅 昨日活动：{date}（周X）"，周末回退日为"📅 活动数据来源：{date}（周X），昨日/周末无活动已回退"（通过对比 data_date 与 `today-1` 判断）。

Cron 执行流程（2026-08-12 验证）：
1. `python3 ~/.hermes/skills/baoyu-workbench-ops/scripts/pull_daily_brief_data.py` — 直连 MCP HTTP → 动态日期 → OFFSET 翻页 → 写活动+客诉JSON → 调 gen_daily_brief.py
2. 解析 `latest.md` → 构建交互式卡片 → 通过飞书 API 推送给刘新元

⚠️ **客诉关键词已收紧（2026-08-03）**：移除 `沉淀`/`污染`/`质量`（太宽泛导致大量误报，如"比他们质量好"被标记为客诉）。当前使用：`客诉`、`投诉`、`批间差`、`漏液`、`非特异性`、`换货`、`退货`。

## 销售日报 v3（2026-09-17 改版：HTML 全量 + 链接推送）

**用户诉求**：「因为消息篇幅限制显示不完整，每天推送一个 HTML 更好」。

### 🔴 日报链接点不动 = 可编辑图层吞掉 `<a>`（不是链接坏了）

`contenteditable` 区域里点 `<a>` 只落光标、不跳转，**不报错**。用户报「客诉/飞书原文点不动」先查这里，不要去改链接生成（链接本身是对的，实测 116/116 都是合法绝对 https）。修复在图层模块：`protectLinks()` 给所有 `<a href>` 标 `contenteditable=false` + 编辑态捕获阶段 `window.open` 兜底；日报生成器只负责传 `editable_default=False`。

- **实际生效的是 `脚本/html_editable_layer.py`**，技能模板 `editable-html-deliverables/templates/html_editable_layer.py` 只是副本 —— 改一份不同步另一份 = 下一天日报照旧点不动（曾经发生过：模板改了、模块没改）。改完 `diff -q` 两份无差异。
- **只改源码不算修好，必须验产物**：`grep -c 'protectLinks\|EDIT_DEFAULT=false' 数据/daily_briefs/宝锐销售日报_<日期>.html` 达到预期才交付；推完再验线上同一串 + md5。
- **旧产物就地修**：`python3 脚本/upgrade_generated_edit_layer.py --deploy`（幂等；只动「有 http 外链且缺 protectLinks」的文件，纯表单页不碰）。
- 验证点击行为要逮 `window.open` 的入参（`target=_blank` 不改主文档 URL），别拿 `location.href` 当判据；阅读模式下 JS 应**不接管**（0 次 window.open，交浏览器原生跳转）。

### 🔴 口径陷阱（本次踩到）
- **`erp_amount.json` 是嵌套结构**：`monthly` = 月→金额；`monthly_dept`/`monthly_person`/`customer_monthly` = 月→{名称:金额}。**最新月可能不含本月**（ERP 导出滞后）→ 必须 `max(monthly.keys())` 取最新月并**在标题标注月份**，否则又出现「本月 ¥0.0万」假报。
- **CloudBase 所有 HTML 都返回 `content-disposition: attachment`**（实测含 `/workbench/index.html`）→ 桌面 Chrome 会弹下载/风险提醒，但**飞书 WebView 正常**（与工作台入口行为完全一致，无需改）。日报链接必须用 **ASCII 路径 `/daily/...`**（中文路径飞书入口会拒）；**部署也必须推到 ASCII 路径** —— 拿本地中文文件名 `宝锐销售日报_<日期>.html` 当目标路径，`tcb` 照样返回「✔ 部署成功」而线上文件不变，且那个旧中文路径 curl 也能取到内容，极易误判成「修了不生效」。**核对一律以 `/daily/index.html` 里的实际 href 为准 + 无缓存请求比 md5（本地==线上）。**
- **飞书附件上传需应用权限 `im:resource:upload`**：`cli_aaee6a2f90b89bc1` 当前**未开通** → `POST /open-apis/im/v1/files` 返回 400/99991672。开通入口（管理员点一下）：`https://open.feishu.cn/app/cli_aaee6a2f90b89bc1/auth?q=im:resource:upload,im:resource&op_from=openapi&token_type=tenant`。脚本已容错：**卡片送达即 rc=0**，附件失败只打印指引、不影响日报送达。
- 旧卡片脚本仍保留为备用，但**全量 MD 会让它超 30KB 而 exit 1** —— 别再让 cron 调它。

## MCP 活动查询模式

```sql
-- 活动详情页链接
https://project.feishu.cn/xsguanli/sj/detail/{work_item_id}
```

### ⚠️ Cron 模式工具限制
Cron 任务运行时无用户在场，以下工具行为与交互模式不同：
- **`execute_code`** 在 cron 模式下被阻止（需 `approvals.cron_mode: approve`），改用 `terminal` 执行 Python 脚本
- **`write_file`** 会校验 JSON 语法，含特殊字符的数据文件可能被拒绝，大 JSON 写入改用 `terminal` 的 heredoc 或 `python3 -c`
- **MCP 翻页**：查询 count ≥ 返回条数时必须翻页拉全量。**用 `LIMIT 50 OFFSET n` 递增翻页**（实测 107 条 = OFFSET 0/50/100 三页）；`session_id` + `group_pagination_list` 在 cron 下会死循环，勿用。单页 LIMIT 上限 1000，OFFSET 上限 1000。

### 🔴 源文件映射（铁律）
本地源文件与 CloudBase 路径不是 1:1 对应。**工作台主力入口的源文件是 `工具/index.html`，`销售工作台.html` 已废弃**：

| 本地源文件 | CloudBase 路径 | 说明 |
|-----------|---------------|------|
| `工具/index.html` | `/workbench/index.html` | **主力文件**，飞书工作台入口 |
| `工具/app.html` | `/工具/app.html` | 测试页（无飞书限制），`?line=da/ls` 业务线参数 |
| `工具/销售工作台.html` | ~~已废弃~~ → `_DEPRECATED.html` | 2026-07-29起不再维护 |
| `数据/workbench_data.json` | `数据/workbench_data.json` | `cb_sync.sh` 自动部署 |

### 🔴 Tab 增删/隐藏必须双文件同步（双入口铁律）

工作台有**两个入口**：`工具/index.html`（飞书主力）和 `工具/app.html`（测试页），两者各自维护一份 `TABS` 数组和 `switch` 渲染分支。**任何 tab 的增删/隐藏/改名必须同时改这两个文件**，并各自 deploy 到不同路径：

```bash
tcb hosting deploy 工具/index.html /workbench/index.html -e bier-sales-d0gatbvlx288724e9
tcb hosting deploy 工具/app.html   /工具/app.html   -e bier-sales-d0gatbvlx288724e9
```

**隐藏 tab 的可逆做法**：注释掉 `TABS` 里的那一行（不要删除），保留 `switch` 的 `case` 和 `renderXxx` 函数不动——它们只在 tab id 匹配时被调用，tab 移除后自然休眠，恢复时只需取消注释。加日期标记便于追踪：

```javascript
// { id: 'training', label: '岗前培训', icon: '🎓', lines: ['da','ls'] }, // 已隐藏(2026-09-05)，需恢复时取消注释
```

验证（回读线上确认活跃 tab 已移除）：
```bash
curl -s -H "Cache-Control: no-cache" "https://bier-sales-d0gatbvlx288724e9-1458438710.tcloudbaseapp.com/workbench/index.html" | grep -E "^\s*\{ id: 'training'" | wc -l  # 期望 0
```

**⚠️ 致命陷阱**：改 `销售工作台.html` 后用户看不到更新——因为用户通过飞书打开的是 `/workbench/index.html`（源文件 `工具/index.html`）。所有修改必须在 `工具/index.html` 进行并 `tcb hosting deploy` 到 CloudBase。

验证：
```bash
curl -s -H "Cache-Control: no-cache" "https://bier-sales-...tcloudbaseapp.com/workbench/index.html" | grep -c "新代码特征"
```

### 中文路径
CloudBase 飞书入口拒中文URL，工作台入口必须用 `/workbench/index.html`。

## 业务线分拆（2026-08-01 草稿）

工作台已按业务线分入口：`/workbench/index.html`(分流首页) → `app.html?line=da`(诊断原料) / `?line=ls`(生命科学)。
普通销售员按部门自动锁定业务线，admin/gm可切换。详见 `baoyu-workbench-mcp-pipeline` 的 `references/business-line-split.md`。

## 飞书Gateway 稳定性

### 🔴 ts.net 国内不可达（2026-09-08 实测，铁律）

Tailscale Funnel 域名 `macbook-air-2.tail6864e8.ts.net` **从国内公网不可达**，三层证据：
- 腾讯 DNS(119.29.29.29) → NXDOMAIN（查无此域名）；阿里 DNS(223.5.5.5) → 能解析到 `103.84.155.x`，但该 IP 直连 SSL 握手被掐断
- 腾讯云云函数（同网络）解析 ts.net → ENOTFOUND
- 只有**装了 Tailscale 的设备**（走 MagicDNS 本地解析）能连 ts.net

**结论**：本机 curl 测 Funnel"正常"是假象——它掩盖了国内远程访问根本不通。远程销售员/另一台 Mac 走公共 DNS 访问 ts.net 必失败（DNS 封堵是动态的，可能"之前好今天坏"）。

**mihomo 代理会加重此问题**：系统代理(随云/Clash, 127.0.0.1:7897) 会掐断 ts.net 的 SSL 握手（`SSL_ERROR_SYSCALL`）。修法是把 `*.ts.net` 加进系统代理绕过列表：
```bash
networksetup -setproxybypassdomains Wi-Fi "*.local" "169.254/16" "*.ts.net"
```
（curl 默认不走系统代理，所以用 curl 排查时测不出这个坑，要用 `curl -x http://127.0.0.1:7897` 显式走代理才能复现。）

**CloudBase 云函数无法转发**：云函数跑在腾讯云网络，同样解析不了 ts.net（ENOTFOUND），所以"云函数代理 ts.net"方案死路。

### 新增 JSON 检查清单

每次新增 MCP 管线产出后：
1. 确认该 JSON 在前端 ENDPOINTS 中被引用
2. 如果是动态数据 → 加入 `cb_sync.sh` FILES 数组
3. 如果是静态数据 → 一次性 `tcb hosting deploy`，不加到 cb_sync

### 修复：端到端 SSE 流式（保留思考模式）

链路：前端 → `/api/chat` → `销售工作台服务.py` → `ai_chat.py` → 8642 网关 API → 模型。

- 8642 网关 API 支持 `stream:true` → `text/event-stream` SSE，chunk 含 `delta.content`，末 chunk 含 `usage`，结尾 `data: [DONE]`。
- `ai_chat.py` 加 `_call_hermes_stream()`（读 SSE yield delta，usage 写入传入 dict）+ `handle_stream()`（yield 事件 dict `type∈{status,delta,done,error}`，检索前先发 `status` 让前端秒收）。
- `销售工作台服务.py` 加 `_stream_sse()` 方法，`/api/chat` 判断 `payload.get('stream')` 走 SSE 透传，否则走原 `handle()`（非流式路径不动）。
- 前端 `index.html`+`app.html`（双入口都要改）：payload 加 `stream:true`；fetch 用 `r.body.getReader()` 读 SSE，`status` 显示进度、`delta` 逐字渲染；**空闲超时替代硬超时**（`IDLE_MS=240000`，收到数据就 `resetTimer()`，覆盖思考静默期）。
- 部署：改后端 `launchctl kickstart -k gui/$(id -u)/ai.hermes.workbench` 重启 8765；改前端 `tcb hosting deploy 工具/index.html /workbench/index.html -e bier-sales-d0gatbvlx288724e9`（app.html 走 8765 本地，不在 cb_sync 清单）。

## 详细资料（按需查阅）

> 以下文件不在本 SKILL.md 内，需要时再读；正文只保留每次都会用到的部分。

- **`references/data-pipeline.md`** — 含 4 节：⚠️ 大 JSON 文件解析、数据归档、⚠️ 新增销售员数据补充、多用户服务化（已上线：本机 Mac 服务 + 外部用户上传）
- **`references/deploy-cdn.md`** — 含 1 节：✅ 国内远程访问正解：反向隧道 frp（Mac 主动出站）
- **`references/details.md`** — 含 21 节：🔴 三层截断根因（改版前）、✅ 新链路（全部确定性脚本，零 LLM 消耗）、交付物、新增「管理视角」板块（HTML 独有）、⚠️ 周末回退、logo.png 缺失
- **`references/feishu-api.md`** — 含 1 节：🔴 folder 类型消息被网关静默忽略
- **`references/frontend-ui.md`** — 含 1 节：✅ RELATIVE_DATETIME_EQ 已恢复可用（2026-09-11 实测）

### 既有参考文件（原正文未引用，此处补齐索引）

- **`references/dashboard-data-consistency-cascade.md`** — 看板数据一致性级联故障（2026-08-07 实战）
- **`references/dashboard-data-consistency-pitfalls.md`** — 看板数据一致性陷阱（2026-08-07 实战复盘）
- **`references/data-mgmt-portal.md`** — 数据管理门户（📤 Tab · 2026-08-07）
- **`references/data-source-authority.md`** — 数据源权威等级
- **`references/erp-processing-pitfalls.md`** — ERP 数据处理 Pitfalls（2026-08-07）
- **`references/erp-salesperson-caliber.md`** — ERP 订单 → 销售员级月度销售额 口径（2026-09-08 校准）
- **`references/erp-upload-pipeline.md`** — ERP 上传处理管线（2026-08-07 新增）
- **`references/file-upload-tab.md`** — 📤 数据管理 Tab（文件上传入口）
- **`references/frp-tunnel-deployment.md`** — frp 反向隧道部署（工作台线上出口，替代 ts.net）
- **`references/kpi-pipeline.md`** — KPI 管线 + 工作台数据流（2026-07-30）
- **`references/mcp-pagination-pitfall.md`** — MCP MQL 翻页重复数据陷阱（2026-07-31 实测）
- **`references/refresh-cron-schedule.md`** — 工作台数据刷新定时策略
- **`references/security-audit-20260729.md`** — 安全审计报告 2026-07-29
- **`references/sf-logistics-pipeline.md`** — 顺丰物流管道 · 运维参考
- **`references/sf-pipeline-debug.md`** — 顺丰物流管线 · 完整调试记录（2026-07-30/08-01）
- **`references/smart-month-detection.md`** — ERP上传智能月份检测
- **`references/tab-data-freshness.md`** — 工作台 Tab 数据源与新鲜度检查
- **`references/todo-cloudbase-sync.md`** — Todo CloudBase 跨设备同步
- **`references/upload-pipeline.md`** — 宝锐工作台 · 文件上传 + 数据管道
- **`references/visit-card-css-template.md`** — 拜访卡 CSS 设计规范（白底卡片风格）
