# 宝锐销售工作台 · 重建规格说明书

> 本文档是「宝锐销售工作台」的完整重建规格，供专业编程工具（AI coding agent / 开发者）从零复刻整个系统使用。
> 目标：照着本文档，不依赖现有代码，即可重建一个功能等价、数据口径一致、可运维的销售工作台。
> 编制日期：2026-08-24。数据口径以 ERP 为准（铁律）。

---

## 0. 文档导航

| 章节 | 内容 | 用途 |
|---|---|---|
| §1 | 系统概述 | 理解业务与边界 |
| §2 | 设计原则（八条铁律） | 所有实现的约束 |
| §3 | 技术栈与目录结构 | 环境与文件布局 |
| §4 | 数据模型（14 个 JSON） | 每个数据文件的 schema 与字段 |
| §5 | 数据管道（ERP + MCP） | 数据如何从源头流到页面 |
| §6 | 前端页面 | Tab、数据源、渲染、权限过滤 |
| §7 | 认证与权限 | CloudBase 登录 + 飞书免登 + 四级角色 |
| §8 | 定时任务（16 个 cron） | 自动化调度 |
| §9 | 部署 | CloudBase 路径映射、同步、CDN |
| §10 | 顺丰物流管道 | 物流轨迹追踪 |
| §11 | 日报/周报推送 | 飞书消息推送 |
| §12 | 关键规则与陷阱汇总 | 口径、映射、pitfall |
| §13 | 重建落地顺序建议 | 给编程工具的执行路径 |

---

## 1. 系统概述

### 1.1 业务背景
宝锐生物（BIORI BIOTECH）营销中心，两条业务线：
- **诊断原料（da）**：核酸诊断原料，面向企业客户（IVD 厂商）。
- **生命科学（ls）**：科研试剂，面向高校/科研院所课题组。

三个部门、约 30 名销售 + 管理层：
- **诊断原料大客户销售部**（大客户部）：年度目标 2,650 万。
- **诊断原料销售拓展部**（销售拓展部）：年度目标 3,400 万。
- **生命科学销售部**（生命科学部）：年度目标 900 万。

### 1.2 五大核心模块
工作台把散落的 Excel/聊天记录收进一个「作战室」：
1. **KPI 看板**（当月/当季/当年三层递进）
2. **客户管理**（客户档案 + 集团归集）
3. **订单追踪**（订单 + 物流）
4. **数据管理**（ERP 上传 → 自动处理）
5. **测试反馈**（科研测试正负案例库）

### 1.3 双入口（关键）
| 入口 | 文件 | 通道 | 用途 |
|---|---|---|---|
| 飞书内嵌版 | `工具/app.html` | 飞书 WebView | 业务线分流（?line=da / ?line=ls） |
| 公网版 | `工具/index.html` | CloudBase `/workbench/` | 主力工作台 |

两条入口必须**功能同步**（改一处要同步另一处）。

---

## 2. 设计原则（八条铁律）

> 这些不是建议，是硬约束，任何实现不得违反。

- **P1 金额只认 ERP**：MCP 显示金额不可靠（测试单/预测单金额为 0），只用于活动/客诉/客户。禁止从 MCP 拉金额或按比例分摊。
- **P2 页面零依赖**：所有 HTML 自包含，Chart.js 本地化（`工具/chart.umd.min.js`），不引用外部 CDN。飞书 WebView 网络复杂。
- **P3 数据权威等级**：ERP > 月度报告 Excel > MCP > 推算。冲突时等级高者胜。
- **P4 分权**：admin 全看、gm 全看（只读）、manager 看本部门、sales 只看自己。
- **P5 修复按 CAPA**：观察现象 → 分析根因 → 纠正 + 预防。
- **P6 出问题先体检**：全量排查完再写修复代码。
- **P7 改完必须测**。
- **P8 渐进迭代**：先最小可用，再往上加。

---

## 3. 技术栈与目录结构

### 3.1 技术栈（刻意克制）
- **前端**：零依赖 HTML + 内联 CSS/JS，vanilla JS（无框架）。
- **图表**：Chart.js（本地化，不碰 CDN）。
- **数据**：JSON 驱动，前端 `fetch()` 渲染。
- **托管**：腾讯 CloudBase 静态托管。
- **本地服务**：Python HTTP server（:8765）+ Tailscale Funnel 出公网。
- **数据管道**：Python（pandas + openpyxl + xlrd）脚本。
- **认证**：CloudBase 匿名登录 + `user_roles` 集合 + 飞书 OAuth 免登。

### 3.2 目录结构
```
~/Desktop/Hermes输出-工作类/
├── 宝锐工作台.md          ← L1 原则 + 映射 + Knowledge 索引
├── PROJECT.md             ← L2 项目定义
├── TASK.md                ← L3 当前任务队列
├── RULES.md               ← 共同规范（R1-R5）
├── 同步清单.md             ← app.html ↔ index.html 差异对照
├── 工具/                   ← HTML 页面（index.html 主力，app.html 飞书）
├── 数据/                   ← JSON 数据文件（14 个 + pricing/ + h1_archive/ + _backup/）
├── 脚本/                   ← Python 管线脚本
├── 会话存档/               ← 历史会话记录
└── 客户管理/、知识库-copilot/ 等 ← 业务产出目录
```

### 3.3 关键常量
```
CloudBase 环境: bier-sales-d0gatbvlx288724e9
公网 base URL:  https://bier-sales-d0gatbvlx288724e9-1458438710.tcloudbaseapp.com
本地服务:       http://127.0.0.1:8765
Tailscale Funnel: https://macbook-air-2.tail6864e8.ts.net
飞书 App:        cli_aaee6a2f90b89bc1（工作台 App）
```

---

## 4. 数据模型（14 个 JSON 数据源）

前端通过 `merge_workbench_data.py` 把以下 14 个 JSON 合并成单一 `workbench_data.json`（前端只读这个合并体）。但管道各自维护源 JSON。

### 4.1 源 JSON → workbench_data 映射
| workbench_data key | 源文件 | 说明 |
|---|---|---|
| customers | `pricing/_key_customers.json` | 客户主数据（444 条） |
| personnel | `pricing/_personnel.json` | 人员名单（45 人） |
| kpiDashboard | `kpi_dashboard.json` | 部门+个人 KPI 家底 |
| kpiProgress | `kpi_progress.json` | 当月进度（MCP 每日更新） |
| h1Monthly | `h1_monthly.json` | H1 部门 actuals |
| myKpis | `my_kpis.json` | 个人 KPI（月度明细） |
| complaints | `mcp_complaints.json` | 客诉 |
| threeYearMonthly | `three_year_monthly.json` | 2024-2026 逐月销售额 |
| h2Targets | `h2_monthly_targets.json` | H2 目标 |
| oppSandbox | `opportunity_sandbox.json` | 商机沙盘（手动 Excel 导入） |
| shipments | `shipment_status.json` | 顺丰物流状态 |
| mcpCustomers | `mcp_customers.json` | MCP 客户 |
| customerContacts | `customer_contacts.json` | 客户联系人/决策链 |
| myActivities | `my_activities.json` | 销售活动 |

### 4.2 核心 JSON schema

**`kpi_dashboard.json`**（KPI 家底）：
```json
{
  "period": "2026年 (全年=ERP·累计2915.0万)",
  "source": "ERP销售订单导出(20260824)",
  "total_target": 69500000.0,          // 全年总目标
  "total_h1": 22688954.46,             // H1 实际
  "total_h1_rate": 32.6,               // H1 达成率
  "h1_labels": ["1月",...], "h2_labels": ["7月",...],
  "h2_weights": [0.12,...],            // H2 各月权重
  "depts": [ { "dept", "target", "h1_months"[], "h1_total", "h1_rate",
               "h2_months"[], ... } ],  // 3 个部门
  "people": [ { "name", "dept", "target", "h1_months"[], "h1_total",
                "h1_rate", "h2_months"[], ... } ],  // 22 人
  "total_h2_actual": 6461285.47,
  "total_h1_h2": 29150239.93,          // 全年累计（getKPI() 的 ytd 用这个）
  "updated_at": "2026-08-24T09:44:38"
}
```

**`kpi_progress.json`**（当月进度）：
```json
{
  "updated_at": "2026-08-24T09:44:39",
  "month": "2026-08",
  "total": 2925169.28,                 // 当月销售额
  "by_salesperson": { "赵云浩": 558354.84, ... },  // 18 人
  "source": "ERP销售订单导出",
  "file": "销售订单_20260801-0824订单ERP导出.xlsx"
}
```

**`three_year_monthly.json`**（三年逐月）：
```json
{ "2024": {"1":..., "12":...}, "2025": {...}, "2026": {...}, "meta": {...} }
```
- 口径：2024/2025 = `是否赠品=否 价税合计>0`；2026 = `标准+已审核+非赠品+非宝锐系`。

**`my_kpis.json`**（个人 KPI，月度明细）：
```json
{
  "period": "2026年1-6月",
  "total_target": 65100000, "total_actual": 22835518.83,
  "people": [ { "name", "dept", "annual_target", "actual_ytd",
                "completion_pct", "monthly": {"2026-01": ..., "2026-07": ...} } ]
}
```

**`workbench_data.json`**：合并体，顶部加 `_merged_at`、`_version`。`shipments.orders` 额外注入 H1 归档订单（`h1_archive/h1_all.json`），H1 在前、H2 在后，H2 同单号覆盖 H1。

### 4.3 静态 vs 动态 JSON
- **动态**（cb_sync.sh 每日同步 9 个）：`mcp_customers`、`mcp_complaints`、`opportunity_sandbox`、`shipment_status`、`kpi_dashboard`、`kpi_progress`、`my_activities`、`_key_customers`、`_personnel`。
- **静态**（一次性部署）：`h1_monthly`、`h2_targets`、`three_year_monthly`、`my_kpis`。

---

## 5. 数据管道（ERP + MCP）

### 5.1 ERP 管线（金额唯一可靠源）

**流程**：ERP Excel 上传 → `erp_sales_core.py` 解析聚合 → `mcp_kpi_pipeline.py` 更新 KPI JSON → `merge_workbench_data.py` 合并 → `cb_sync` 部署。

**ERP 文件两种格式**（`erp_sales_core.py` 兼容）：
| 格式 | 特征 | 金额口径 | 销售员 |
|---|---|---|---|
| 旧格式 | 含「销售员」列 +「价税合计」列 | 价税合计（含税） | 销售员列 |
| 新格式 | 无「销售员」列 +「金额」列 | 金额 × (1+税率%) | 单据编号精确匹配 → 客户映射兜底 |

**口径（4 道过滤，必须）**：
1. 单据类型 = 标准销售订单（排除测试/预测单，预测单编号前缀 FO）
2. 单据状态 = 已审核
3. 是否赠品 ≠ 是
4. 剔除宝锐系内部交易（`宝锐|宝泰|横琴`）

**人员映射（map_person）**：
- 黄明月（离职）→ 刘子研
- 张立娅（离职）→ 刘子研
- 刘新元 且 客户 ∈ 刘子研的 23 家客户 → 刘子研
- 其余按 `pricing/_personnel.json` 的 dept 归部门

**2025 年及更早的代号映射**（RULES R2.2）：
- 大客户1 → 黄明月，大客户2 → 赵云浩，王昕伟 → 赵云浩

**月份识别**：单据编号 `ZHBR{YYMM}{seq}`，月份 = 编号第 5-6 位。

**合计行过滤**：客户列或单据编号为空（或含「合计」）的行必须跳过。

### 5.2 MCP 管线（飞书项目数据）

拉取客诉/客户/活动/订单（`feishu-project-mcp` 技能，48 个 MCP 工具）。
- 项目空间：销售管理 `6593cd71471290e3cc6be6e6`（xsguanli）、客户项目 `658bb60520ea78a2125f1b99`（csrw）、售后 `658288abfb8bd616b17025f1`（br-shgl）。
- 工作项类型：商机 story、活动（sj）、销售订单（xsdd）、客户（kh）、联系人（lxr）等 13 种。
- MQL 查询用 `search_by_mql`，日期必须用 `date.today()` 动态计算，禁止写死。`RELATIVE_DATETIME_EQ` 已失效。
- 客诉字段是 `work_item_id`（不是 `id`）。

### 5.3 关键脚本清单
| 脚本 | 作用 |
|---|---|
| `脚本/erp_sales_core.py` | ERP 解析核心（口径 + 映射 + 聚合），供 refresh/mcp 复用 |
| `数据/mcp_kpi_pipeline.py` | 更新 kpi_dashboard/three_year_monthly/h1_monthly（有 has_h1 保护） |
| `脚本/merge_workbench_data.py` | 14 JSON → workbench_data.json |
| `脚本/refresh_erp.py` | ERP 刷新入口 |
| `脚本/refresh_kpi.py` | KPI 刷新（备用，从飞书导出 Excel） |
| `脚本/refresh_kpi_mcp.py` | KPI 刷新（MCP） |
| `脚本/update_kpi_progress.py` | 更新当月 kpi_progress |
| `脚本/reconcile_monthly.py` | 月度校对 |
| `脚本/check_sync.py` | 数据变更检测（hash 对比） |
| `脚本/销售工作台服务.py` | 本地 :8765 服务（API：/api/feishu/auth、/api/todos、/api/visit-card 等） |

---

## 6. 前端页面

### 6.1 Tab 结构（`工具/index.html` 的 TABS 数组）
| id | 标签 | 业务线 | 说明 |
|---|---|---|---|
| dashboard | 看板 | da+ls | KPI 三层递进（当月/当季/当年） |
| groups | 课题组 | ls | 生命科学课题组（ls 默认首页） |
| customers | 客户 | da | 客户档案（da 默认首页） |
| visitPrep | 访前准备 | da+ls | 拜访卡/访前背调 |
| oppSandbox | 商机沙盘 | da | 商机（手动 Excel 导入） |
| complaints | 客诉 | da | 客诉列表 |
| orders | 订单管理 | da+ls | 订单 + 物流 |
| myActivities | 我的活动 | da+ls | 销售活动 |
| todos | 待办 | da+ls | 待办（双写：后端 todos.json + CloudBase todos 集合） |
| aiAssistant | AI 助手 | da+ls | 对话参谋 |
| dataMgmt | 数据管理 | da+ls（adminOnly） | ERP 上传入口 |

### 6.2 数据源（ENDPOINTS）
前端 `fetch('../数据/<file>.json')` 拉 14 个源 JSON（实际由 CloudBase 部署提供）。业务线分流：
- `?line=da` → 默认 dashboard；`?line=ls` → 默认 groups。
- 普通销售按部门自动锁定业务线；admin/gm 可切换。

### 6.3 权限过滤
- `isFullView()` = `role==='admin' || role==='gm'` 应用到所有数据可见性。
- `USER_AREA_MAP` / `USER_DEPT_MAP` 必须**同时配简称和全称**（如 `大客户部` 和 `诊断原料大客户销售部`），否则 fallback 失败导致过滤静默失效。
- 个人榜单包含在职且有个人 KPI 的区域经理（吴云、韩远怀）。

### 6.4 KPI 渲染关键逻辑
- `getKPI()` 的 ytd 用 `total_h1_h2`（全年累计），fallback `total_h1`。
- 月实际从 `threeYearMonthly` 按月份索引取。
- 非管理员看个人 KPI，管理员看公司汇总（避免所有人看到同一个数字）。

---

## 7. 认证与权限

### 7.1 登录体系
- CloudBase 匿名登录（`cloudbase.init({env: CB_ENV})` + `auth.anonymousAuthProvider().signIn()`）。
- 用户角色存 CloudBase `user_roles` 集合（字段 `pwd`，**不是** `password`）。
- 登录失败限制：每邮箱每小时 5 次，成功清零。
- `ROLE_KEY = 'bior_sw_user_v2'`（本地缓存 key）。

### 7.2 飞书免登
- 检测 `?code=` → 调 `/api/feishu/auth`（走 Tailscale Funnel 代理的 Python 服务端）。
- App Secret 不暴露前端，OAuth 全在服务端。
- Funnel 断开时降级为手动登录。

### 7.3 四级角色
| 角色 | role 值 | 数据范围 | 编辑 | 改密 |
|---|---|---|---|---|
| 管理员 | admin | 全公司 | ✅ | ✅ |
| 总经理 | gm | 全公司（只读） | ❌ | ❌ |
| 经理 | manager | 部门范围 | ❌ | ❌ |
| 销售员 | sales | 仅自己 | ❌ | ✅ |

- `user_roles` 写权限：`auth.openid == doc._openid`（仅文档创建者可写）。
- 超管页面 `admin.html` 管理用户；Sales 可自行改密。

---

## 8. 定时任务（16 个 cron）

| 时间 | 任务 | job_id | 脚本 | 说明 |
|---|---|---|---|---|
| 每 1 分钟 | 子 Agent 任务扫描 | b88554901010 | scan_subagent_tasks.py | 无 LLM |
| 每 2 小时 | CloudBase 数据同步 | 4e22bc74300b | cb_sync.sh | 同步 9 个动态 JSON |
| 每 2 小时 | Gateway 健康监控 | f0d05a94bef0 | gateway_health_check.sh | 仅报警不重启 |
| 每 15 分钟 | 拜访卡自动生成 | 40018d5a5e42 | visit_request_monitor.py | monitor 触发 |
| 每日 00:00 | KPI 刷新 MCP | b5c6c96b1eee | — | （当前已暂停） |
| 每日 08:30 | 销售日常简报生成 | b60c79153e2c | — | 写 latest.md |
| 每日 08:35 | 简报推送 | 3a16409d3361 | — | 飞书推送 |
| 每日 09:00 | 全量数据刷新 | f846b80f102d | daily_full_refresh.py | MCP+顺丰 |
| 每日 09:00 | Hermes 版本检测 | 2b7ff9d1074f | check-hermes-update.sh | 无 LLM |
| 每日 12:00+21:00 | 工作台数据刷新 | 46742443f5a8 | — | MCP 客诉/客户/活动/订单 |
| 每日 15:00 | 顺丰路由刷新 | 3a404ba952ff | sf_delivery_watchdog.py | 仅顺丰 |
| 每日 22:00 | 顺丰路由刷新 | 6910c308a1db | sf_delivery_watchdog.py | 仅顺丰 |
| 每周一 09:00 | 市场调研周报 | d0396a78f936 | — | 竞品动态 |
| 每周日 03:30 | Hermes 自动更新 | a9bc41c50009 | hermes-auto-update.sh | 无 LLM |
| 每月 3 号 09:00 | 月度数据校对 | bc9a9958aff4 | reconcile_monthly.sh | 无 LLM |

**日报日期逻辑**：今天推最近一个工作日；周一推周五；订单窗口用「近 7 天滚动」（不是自然周）。

---

## 9. 部署

### 9.1 CloudBase 路径映射（铁律，非 1:1）
| 本地源文件 | CloudBase 路径 | 说明 |
|---|---|---|
| `工具/index.html` | `/workbench/index.html` | **主力文件**（飞书入口） |
| `工具/app.html` | — | 飞书内嵌，另有部署路径 |
| `工具/logo.png` | `/workbench/logo.png` | Logo |
| `数据/*.json` | `数据/*.json` | cb_sync 部署 |

⚠️ `销售工作台.html` 已废弃（→ `_DEPRECATED.html`），改动必须落在 `工具/index.html`。

### 9.2 部署命令
```bash
# 全量部署数据 JSON
bash ~/.hermes/scripts/cb_sync_data.sh
# 部署 HTML（改 HTML 必须手动这条）
tcb hosting deploy 工具/index.html /workbench/index.html -e bier-sales-d0gatbvlx288724e9
# 健康检查
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8765/
```

### 9.3 注意事项
- CDN 缓存 2-5 分钟，验证用 `curl -H "Cache-Control: no-cache"`。
- 飞书环境拒中文 URL，入口必须用 `/workbench/index.html`。
- 外部/公网页面用英文路径（中文路径飞书打开会重定向到工作台）。

---

## 10. 顺丰物流管道

### 10.1 流程
`pull_orders.py`（MCP 拉订单 → `mcp_orders_input.json`）→ `shipment_pipeline.py`（读订单 → 查顺丰轨迹 → `shipment_status.json`）→ `sf_delivery_watchdog.py`（拉订单+查轨迹+diff+飞书推送）。

### 10.2 顺丰 API 配置
```
partnerID = BRSWK5CQ08HV
secret = V7JDT0azGX93pnIXRyZe9zvlzY6fk1rC
月结卡 = 7561222900
注册手机 = 18666926804
checkPhoneNo = 收件人手机后 4 位（每运单独立）
```

### 10.3 状态判定（铁律）
- opCode 优先：80=已签收，204/30=派送中，54=已揽收；文本匹配仅兜底。
- 「已派送成功」含「派送」但 opCode=80，文本匹配会误判，必须 opCode 优先。
- 订单按「单据编号」去重（飞书一单多工作项，不能只按 work_item_id）。

---

## 11. 日报/周报推送

- **日报**：08:30 生成（`pull_daily_brief_data.py` 直连 MCP 拉活动+客诉 → `gen_daily_brief.py` 生成 latest.md）→ 08:35 推送飞书。
- **周报**（待实现）：每周第一个工作日推上周周报（自然周 周一~周日），9 板块（经营总览/分部门战报/销售员周榜/客诉/竞品/重点项目/回款/风险预警/下周关注）。
- 推送 App：`cli_aaee6a2f90b89bc1`（工作台 App，非旧 SF App）。
- open_id 映射：赵云浩 `ou_7fab49a17fecc17934456d76a45d0ca0`、刘新元 `ou_901b790c9afdda4a8f14554962b32664`。

---

## 12. 关键规则与陷阱汇总

### 12.1 ERP 口径（R1）
- 列索引随文件变化，每次先打印列头确认，不硬编码。
- 2025 文件日期列是字符串 `YYYY-MM`，不是 datetime。
- `.xls` 用 xlrd，`.xlsx` 用 openpyxl（`values_only=True`，不用 `read_only=True`，不用 `wb.active`）。

### 12.2 KPI 口径（R2）
- 金额唯一源 = ERP，严禁 MCP 金额/比例分摊/估算。
- 预测单（FO 前缀）不计销售。
- 年化达成率 = (累计实际 / 已过月数 × 12) / 年度目标 × 100。

### 12.3 前端陷阱
- 客诉链接用 `work_item_id || id` 兼容。
- AUTH 里 `state.user` 现在是对象，用 `getUserName()` / `isAdminUser()`。
- `USER_AREA_MAP` 必须同时配简称和全称。
- 月文件不能清空 H1/历史 H2 月（`mcp_kpi_pipeline.py` 用 `has_h1` 保护）。

### 12.4 数据源权威性
| 用途 | 权威源 | 勿用 |
|---|---|---|
| 销售金额 | kpi_dashboard.json | shipment_status.json（仅物流） |
| 订单数 | diag_kpis.json.orders | shipment_status.json |
| 客户清单 | _key_customers.json | — |
| 月度明细 | kpi_dashboard.json.h1_months | — |

---

## 13. 重建落地顺序建议（给编程工具）

1. **搭骨架**：目录结构 + 八条铁律写进 README/约束。
2. **建数据模型**：先定义 14 个 JSON 的 schema（§4），写示例数据。
3. **写数据管道**：`erp_sales_core.py`（口径+映射）→ `mcp_kpi_pipeline.py` → `merge_workbench_data.py`。用真实 ERP Excel 验证口径。
4. **写前端**：`index.html`（11 个 Tab + ENDPOINTS + 权限过滤），零依赖 + Chart.js 本地化。
5. **接入认证**：CloudBase 匿名登录 + user_roles + 飞书免登。
6. **接顺丰**：pull_orders → shipment_pipeline → watchdog。
7. **配部署**：cb_sync_data.sh + tcb hosting deploy。
8. **建定时任务**：16 个 cron（§8）。
9. **回归验证**：用真实数据对比新旧两套输出，金额口径必须一致。

---

> 本文档是「规格」，不是「实现」。重建时任何口径歧义，以 §2 八条铁律 和 §12 规则为准；数据冲突时以 ERP 为准。
