# 每日销售播报 — 双保险架构

> 最后更新：2026-05-30  
> 脚本位置：`~/.hermes/scripts/sietadata_cew_daily.py` / `sietadata_daily_pipeline.py`

---

## 架构概览

```
22:15 CEW（主）                    22:30 Pipeline（备用）
─────────────────                  ─────────────────
ERP curl 直抓 4 表                  himalaya 收邮件 4 附件
  ↓                                   ↓
入库 sales_ledger                   入库 4 表（DELETE+INSERT 幂等）
入库 inventory_snapshot + product     ↓
  ↓                                与 22:15 对照数据比对
发群播报 oc_9eed                    ├─ 一致 → 私聊「数据正确」
存对照数据 /tmp/                    └─ 不一致 → 发群播报兜底
```

**设计原则**：两条都用 DELETE+INSERT 幂等写入，谁先谁后都不重复。22:15 是主力、22:30 是备用。

---

## Cron 配置

| Job ID | 时间 | 模式 | 脚本 | 说明 |
|--------|------|------|------|------|
| `8145a0406c09` | 22:15 | agent（脚本失败时通知） | `sietadata_cew_daily.py` | CEW 主流程 |
| `b7a874fb42d0` | 22:30 | no_agent 纯脚本 | `sietadata_daily_pipeline.py` | 邮件备用 |

---

## 22:15 CEW 脚本 — 数据流

### ERP 抓取 4 表

| 表 | ERP 接口 | mode | 参数 |
|----|---------|------|------|
| 店铺销售 | `/retail/summary` | 10 | `start_date/end_date` |
| 营业员销售 | `/retail/summary` | 6 | 同上 |
| 基础库存 | `/inventory/cate_summary` | 6 | sxname1-4 全选（逗号分隔） |
| 带分类库存 | `/inventory/cate_summary` | 6 | sxname1-4,8,9 全选（逗号分隔） |

### 分类全选值

存储在脚本顶部常量中：`CAT1`(一类 8个), `BRANDS`(品牌 96个), `YEARS`(年份 18个), `SEASONS`(季节 6个), `CAT2`(二类 5个), `CAT3`(三类 30个)。

**关键坑**：分类值必须用**逗号拼接**单个参数 `sxname1=女鞋,男鞋,包包`，不能用重复参数。

### 入库（DELETE+INSERT 幂等）

1. **sales_ledger**：逐行写入（日期, 店铺编码, 店铺, 货号, 商品名称, 颜色编码, 颜色名称, 数量, 单价, 折扣, 金额, 结算金额, 进价, 成本）
2. **inventory_snapshot**：Table1 LEFT JOIN Table2（按 货号+店铺+颜色 补二类三类）→ 写入（snapshot_date, 货号, 店铺编码, 颜色编码, 颜色名称, 数量, 进价, 成本, snapshot_type='daily'）
3. **product**：UPSERT（货号, 商品名称, 品牌, 年份, 季节, 二类, 三类）

### 播报格式

```
📊 YYYY-MM-DD 每日销售播报（ERP直接抓取）
━━━━━━━━━━━━━━━━━━━━
📅 日期：YYYY-MM-DD

📈 销售汇总
• 总数量：XX 双
• 总金额：¥XX,XXX
• 总毛利：¥XX,XXX
• 毛利率：XX%

🏪 各店明细
• 二店：X双 | ¥XXX | 毛利¥XXX
• 四店：X双 | ¥XXX | 毛利¥XXX
• 五店：X双 | ¥XXX | 毛利¥XXX
• 六店：X双 | ¥XXX | 毛利¥XXX
• 八店：X双 | ¥XXX | 毛利¥XXX

📈 销售分析（手机版）：https://sieta.vip/sietadata/sales_analysis_mobile.html
📸 销售图片：https://sieta.vip/sietadata/sales_pics.html
```

### 对照数据

播报后保存 `/tmp/sietadata_cew_compare.json`，供 22:30 pipeline 比对：

```json
{
  "date": "2026-05-29",
  "total_qty": 28.0,
  "total_amount": 8343.0,
  "total_gp": 4452.0,
  "margin": 53.4,
  "stores": [
    {"code": "STA102", "name": "二店", "qty": 6.0, "amount": 2078.0, "gp": 1203.0},
    ...
  ],
  "fetched_at": "2026-05-29T22:15:00+08:00"
}
```

---

## 22:30 Pipeline 脚本 — 对照逻辑

邮件入库完成后：
1. 读 `/tmp/sietadata_cew_compare.json`
2. 查 DB 本日店铺维度数据
3. 比对总量（双数差 ≤1，金额差 ≤¥10）和各店明细
4. 一致 → `send_feishu` 私发「✅ 日期 数据正确」→ stdout 空（静默）
5. 不一致或无 CEW 数据 → stdout 输出播报 → cron 投递群 oc_9eed

---

## 异常处理

| 场景 | 处理 |
|------|------|
| CEW ERP 登录失败 | agent 分析错误 → 飞书私聊通知 |
| CEW 抓取 0 行 | 正常播报（0 双），可能当天闭店 |
| CEW 飞书发送失败 | try/except 兜底，不阻塞入库 |
| 邮件日期冲突 | pipeline 飞书通知 + 退出（已有保护） |
| CEW 对照文件不存在 | pipeline 输出播报兜底 |
| 两次入库 | DELETE+INSERT 幂等，无影响 |

---

## 历史教训

- **2026-05-28**：基础库存漏发 + 营业员销售延迟邮件混入 → pipeline 日期冲突拒入库 → 催生 CEW 方案
- **分类逗号拼接**：`sxname1=女鞋,男鞋` 正确，`sxname1=女鞋&sxname1=男鞋` 错误
- **Bearer token bug**：pipeline `send_feishu` 曾写死 `***` 而非 `{token}`，已于 5/29 修复
- **cate_summary 必须选 sx**：不选任何分类维度返回空表
