# baoyu-sales-copilot — data pipeline

### 金额行防falsy
```javascript
if (o.amount != null && o.amount !== '' && o.amount !== 0) // ✅ 显式检查
// ❌ if (o.amount) → 0===falsy, 行不显示
```

## 拜访卡工作台集成（2026-07-27）

访前准备Tab已集成拜访卡生成请求流程。


### 同步到访前准备中心

```bash
# 刷新 file_manifest.json
python3 -c "
import json, os
base = os.path.expanduser('~/Desktop/Hermes输出-工作类')
manifest = {}
for root, dirs, files in os.walk(os.path.join(base, '客户管理')):
    for f in files:
        if f.endswith(('.html','.pdf','.docx')) and not f.startswith('.'):
            fp = os.path.join(root, f)
            ftype = 'other'
            if '拜访卡' in f: ftype = 'visit'
            elif '调研卡' in f or '市场调研' in f: ftype = 'market'
            elif '访前背调' in f or '来访接待' in f: ftype = 'visitor'
            elif '客户调研' in f: ftype = 'report'
            manifest[fp] = {'path': fp, 'name': f, 'type': ftype}
with open(os.path.join(base, '数据/file_manifest.json'), 'w') as f:
    json.dump(manifest, f, ensure_ascii=False, indent=2)
print(f'{len(manifest)} files')
"
# 用户打开页面 → 点击 🔄 刷新检测 → 文件状态变为绿色
```

## 访前准备 Tab 架构（2026-07-27重构）

**必须完整继承访前准备中心的所有功能**，不可重建不相关的简化版：
- 5种材料checkbox（拜访卡/市场调研卡/来访接待/调研报告/微信摘要）
- 销售员选择器 + 拜访日期 + 备注
- "生成请求指令"按钮 → 生成Hermes prompt → 复制/已发送
- 归档区域（刷新检测 + CSV导出 + 文件状态自动匹配）
- 在此基础上叠加：AI诊断引擎 + TOP5商机排序 + 统计卡片

## IMA 知识库集成


### ⚠️ 管道脚本"空数据覆盖"保护（2026-08-21 血案）

`pull_complaints.py` 曾在飞书 MCP 拉取失败（返回空）时，用 `total=0, items=[]` 覆盖 `mcp_complaints.json`，导致客诉板块 327 条全丢（数据在备份里，但线上被清空）。

**铁律：所有 MCP 拉取管道脚本，写入输出文件前必须加空数据保护**：
```python
raw = query_all()
if not raw:
    print("❌ 拉取失败/空数据，保留旧文件，不覆盖")
    sys.exit(1)  # 非零退出让 cron 标记 error，但不覆盖旧数据
```
- 触发场景：飞书 MCP 临时网络抖动 / token 过期时，`mcp()` 返回 None，`query_all()` 返回空列表
- 两个 cron 会调 pull_complaints.py：`cb_sync.sh`（每2h → mcp_refresh.py → pull_complaints.py）+「工作台数据刷新」cron（Step1 直接调）
- 排查线索：`mcp_complaints.json` 的 `updated` 时间戳 + cron 的 `last_run_at`/`last_status` 对得上




### 5. 顺丰 API 常见踩坑（高频）

- ❌ **不传 checkPhoneNo** → "月结卡号不匹配"（路由空）
- ❌ **checkPhoneNo=宝锐注册号(6804)** → "手机号校验失败"
- ✅ 必须传 **收件人手机号后4位** = `phone4(o)` 
- ❌ **生产环境用 msgDigest 数字签名** → 返回 A1006，必须用 accessToken
- ✅ 沙箱/生产 Token 不互通；沙箱用网页端测试
- ❌ **trackingNumber 传字符串** → 必须传数组 `["SFxxx"]`
- ❌ **trackingType 传字符串 "1"** → 必须传数字 `1`
- ⚠️ **50%成功率保护**：成功>50%才写文件，积压数据刷新时会误杀 → 改为仅告警不中断
- ⚠️ **批量查询**: 同phone4的订单一批查询，不同phone4混批会导致其他订单"手机号校验失败"
- 调试: 单条测试 → `python3 -c` 调用API验证 → 确认后再跑watchdog


### 7. MCP 数据拉取分页
- ❌ `group_pagination_list` + `session_id` 翻页 → 返回重复数据，不可靠
- ✅ 使用 `LIMIT offset, count`（如 `LIMIT 50, 50` 跳第2页）
- 每页限制 50 条，3067 条 ≈ 62 页
- 全量拉取建议切 DeepSeek V4 Pro（按模型协作规则）


### 8. delegate_task 用于 MCP 批量操作
- ❌ 子 agent 需要重新探索 MCP 环境（查空间 key、类型 key、字段名），效率低
- ✅ 手动分页直调 MCP 更快（每次 1.3s + 无需重探索）


### 9. 顺丰签收状态判断（高频踩坑 ⚠️）
- ❌ `"签收" in remark` → 无法匹配顺丰实际 remark（顺丰用"派送至本人""派送成功"）
- ❌ `"派送" in remark` → 会把"派送至本人"(已签收)误判为派送中
- ✅ 详见 `references/shipment-status-logic.md`，19个关键词覆盖多种签收场景
- 修正前：2条已签收/435条派送中 → 修正后：414条已签收（78.6%）


### 10. JS 金额字段显示 falsy 陷阱（高频 ⚠️）
- ❌ `if (o.amount)` → `amount=0` 时条件为 false，金额行不显示
- ❌ `if (o.quantity)` → `quantity=""` 时也为 false
- ✅ 显式检查：`o.amount != null && o.amount !== '' && o.amount !== 0`
- 金额行应逐字段判断是否显示（`moneyParts` 数组组装），非整行一刀切


### 13. 下单人字段：`创建者` ≠ 真实下单人（⚠️ 2026-07-24 已踩坑）
- ❌ MCP `创建者`(owner) 字段 → 全部返回"殷士海"（系统记录创建人，非业务含义）
- ✅ 真正的下单人是角色字段 `__下单人`（role_id: `role_00a9b7`），返回 22 位不同销售员
- MQL 查询示例：`` SELECT `物流单号`, `__下单人` FROM `销售管理`.`销售订单` ``
- 角色字段返回格式：`value_type=user_value_list` → 取 `user_value_list[].name_cn`
- 注意 `name_cn` 不是 `name`（提取时容易取错导致空值）


### 14. MCP 直连会话过期（⚠️ 2026-07-24 已踩坑）
- ❌ 单个 MCP session 拉 63 页 → 约第 34 页开始 regex 匹配全部失败，返回空数据覆盖目标文件
- ✅ 每 20 页重建 session（`init_session()` + `pull_pages(seg_start, seg_end, sid)`）
- ✅ 写入前强制备份：`shutil.copy2(OUTPUT, OUTPUT + ".bak")`
- ✅ JSON 解析用 `JSONDecoder.raw_decode(content.strip())` 代替 `re.search(r"\{.*\}", content)`


### 16. 订单金额累加：必须按单据编号合并（⚠️ 2026-07-24 已踩坑）
- ❌ 按物流单号去重只取首行金额 → 多行物料订单金额严重偏低（327万 vs 实际2200万+）
- ❌ MCP 中一个单据编号可能对应多行物料，每行有独立的金额/数量
- ✅ 正确做法：
  1. 去掉 `物流单号 is not null` 过滤（无物流订单也要计入金额统计）
  2. 按**单据编号**(orderId)合并：同一单据编号的多行 → 金额/价税合计/数量累加
  3. 取首行的物流单号（首行为空则向后找），无物流单号的标记为"无物流"
  4. 只对有物流单号的订单调用顺丰API
- 结果：1233条有物流单号 → N条总订单（含无物流），金额从327万修正为准确的总额
- 对于订单管理页面：上半部分分析含全部订单金额，下半部分明细显示全部（无物流标注）


### 18. 暂无路由诊断（62条，顺丰返回空轨迹）

- remark 全为空 → 顺丰API返回空 `routes` 数组
- 全部有手机号 → 非字段缺失，而是手机号后4位在顺丰系统中不匹配
- 日期跨1/26~7/24 → 非"3个月过期"问题（新单也出现）
- 结论：收货人在顺丰系统留的不是MCP中的手机号 → 非物流异常，是数据口径差异
- 页面已正确归类为"暂无路由"（独立卡片），不计入"物流异常"


### 22. h1Summary 状态字段使用中文键名（⚠️ 2026-07-25 已踩坑）
- ❌ `data.h1Summary.delivered` → undefined，H1已签收数量未合并
- ❌ h1_summary 的 status 对象键名是中文：`{"已签收": 934, "无物流": 2718, ...}`
- ✅ `(data.h1Summary.status || {})['已签收']` 正确读取
- 影响范围：订单管理Tab"本年"的已签收卡片（修正前142→修正后1076）


### 26. 客诉Tab MCP数据源（2026-07-25 已确认）
- 客诉数据不在销售管理空间，在 `售后管理` 空间（project_key: `658288abfb8bd616b17025f1`, simple_name: `br-shgl`）
- 工作项类型：`客户反馈单`（khts, type_key: `6669433056a98249604376de`）
- 原 mcp_complaints.json 来自同一数据源，但手动导出过时（7/17的21条）
- 直连MCP拉取脚本：`references/mcp-complaints-pipeline.md`
- MCP token 可跨空间使用（销售管理、客户项目管理、售后管理三个空间共用同一token）
- 字段：流程名称/创建时间/状态/当前负责人/反馈区域/客诉类型/客户关联/销售订单关联/客户诉求/产品线/优先级


### 28. 本年按部门需合并H1归档数据（⚠️ 2026-07-25 已踩坑）
- ❌ `deptPeriod` 仅从 `periodOrders`（H2）计算 → 本年视图只有H2的4个部门
- ✅ H1部门数据需写入 `h1_summary.depts` → 前端 `if (year) { merge h1Depts }`
- 数据源：`h1_archive.json` → `departments`（按部门订单数+金额）→ 写入 `shipment_status.json` 的 `h1_summary.depts`
- 修正后：4部门 → 6部门（拓展部/大客户/浙江/广东 + 生物医药销售部/南区）


### 29. 订单管理页面标题固定"订单管理"
- ❌ `<h2>` 用 `range.label + '订单分析'` → 标题随切换变化（"本周订单分析"/"本月订单分析"）
- ✅ 固定标题：`<h2>📊 订单管理</h2>` + 右侧显示日期范围
- 按部门卡片同样固定：`<h3>🏢 按部门</h3>`


### 35. MCP 分页硬上限50条 + page_num重叠 (2026-07-28)
- page_size=100 仍只返回50条；page_num=2 与 page_num=1 数据重叠
- MCP search_by_mql 硬上限50条/请求
- 变通方案：用时间分片 start_time >= T1 AND start_time < T2


### 19. 联系人数必须从MCP客户表获取（⚠️ 2026-07-24 已踩坑）
- ❌ 用订单收货人(recipient)去重统计联系人数 → 仅261人，严重偏低
- ❌ 收货人是具体订单的收件人，不能代表客户联系人总数
- ✅ MCP `客户` 工作项(api_name=kh)有 `联系人数量`(field_8c870a) 字段，直接汇总1678个客户得7723
- ✅ 看板客户数也用MCP客户表总数1678（非订单去重163）
- MQL: `SELECT \`联系人数量\` FROM \`销售管理\`.\`客户\``
- 联系人数写入 `shipment_status.json` → 前端 `shipments.mcp_contact_count` 读取


### 20. H1客户金额需单独MCP回填管道（⚠️ 2026-07-24）
- H1归档到 `h1_archive.json` 后只有月度汇总，按客户金额需单独拉取
- MQL: `SELECT 购货单位#, 价税合计 FROM 销售管理.销售订单 WHERE 创建时间 >= '2026-01-01' AND 创建时间 <= '2026-06-30'`
- 8840行/177页 → 按客户汇总得606个客户，总额2296.9万
- 合并到 `mcp_customers.json`：H2金额(已有) + H1金额(回填) = 2026累计采购
