最后更新: 2026-05-24
> 最后更新: 2026-05-24
> 此文档是所有已确认规则的单一源头。每次有新的持久化约定时更新此文件。
---
| 项 | 值 |
|---|---|
| 路径 | `~/sietadata/` |
| 数据库 | `~/sietadata/db/sieta.db` (SQLite) |
| HTTP 根 | `https://sieta.vip/sietadata/` |
| 部署路径 | `/var/www/sieta/sietadata/` |
| 上传目录 | `/home/ubuntu/uploads/` |
---
```sql
CREATE TABLE product (
货号 TEXT PRIMARY KEY,
商品名称 TEXT,
品牌 TEXT,
年份 TEXT,
季节 TEXT,
二类 TEXT,
三类 TEXT,
...
);
```
- ⚠️ 仅 ~20% SKU 有分类值(625/3087)
- 品牌通过 `LEFT JOIN product ON sales_ledger.货号 = product.货号` 获取
```sql
CREATE TABLE sales_ledger (
日期 TEXT,
货号 TEXT,
店铺编码 TEXT,
店铺 TEXT,
数量 REAL,
单价 REAL,
金额 REAL,
进价 REAL,
销售成本 REAL,
毛利 REAL,
颜色编码 TEXT,
颜色 TEXT,
营业员编码 TEXT,
营业员 TEXT,
created_at TEXT,
UNIQUE(日期, 货号, 颜色编码, 营业员编码, 数量, 单价)
);
```
- **来源**: Mobile BI `自动报表_零售汇总_当日销售_店铺_YYYYMMDD_HHMMSS.xlsx`
- **毛利公式**: `金额 - (进价 × 数量)`
- **营业员字段可能为空**: 部分日期(如 5/20)只有店铺维度数据
```sql
CREATE TABLE staff_sales (
日期 TEXT NOT NULL,
营业员编码 TEXT,
姓名 TEXT NOT NULL,
货号 TEXT NOT NULL,
颜色编码 TEXT,
颜色 TEXT,
数量 REAL NOT NULL,
金额 REAL,
毛利 REAL,
created_at TEXT DEFAULT (datetime('now','localtime')),
UNIQUE(日期, 营业员编码, 姓名, 货号, 颜色编码)
);
```
- **来源**: Mobile BI `自动报表_零售汇总_当日销售_营业员_YYYYMMDD_HHMMSS.xlsx`
- **拆分规则**: 两人名按 `、` 或 `/` 分隔,数量/金额/毛利各分一半
```sql
CREATE TABLE inventory_snapshot (
snapshot_date TEXT,
货号 TEXT,
店铺编码 TEXT,
颜色编码 TEXT,
数量 REAL,
... ,
snapshot_type TEXT, -- 'full'(周日) or 'delta'(周一至六)
UNIQUE(snapshot_date, 货号, 店铺编码, 颜色编码)
);
```
- **全量快照**: 每周日,~7K+ 行
- **增量快照**: 周一至六,只记变动行
- **查询**: `get_inventory_on_date(date)` 找最近周日 full + 累加 delta
```sql
CREATE TABLE inventory_ledger (
change_date TEXT,
货号 TEXT,
店铺编码 TEXT,
颜色编码 TEXT,
change_type TEXT, -- 'sale' / 'adjustment_in' / 'adjustment_out'
数量 REAL,
...
);
```
---
**这是最严重的错误,已犯 3 次。**
ERP 无法同时导出店铺和营业员,必须分两次导出。但数据是同一批销售。
- ✅ 两个表各自独立,**绝不能加总**
- ✅ 报表只用其中一个表(周报用 sales_ledger,营业员业绩用 staff_sales)
- ❌ sales_ledger.金额 + staff_sales.金额 = 金额翻倍
**自检**: 每次做报表时问自己——两个表有没有无意中加在一起?
数据中台每天发两封邮件(店铺维度 + 营业员维度),按合并键导入:
- **合并键**: `(商品编码, 颜色编码, 数量, 单价, 金额)` 五字段精确匹配
- **期望匹配率**: ≥94%
- ❌ 不能各自独立导入 → 双倍数据
`INSERT OR IGNORE` 在 UNIQUE 索引列含 NULL 时**不防重**(SQLite 允许多个 NULL 视为不同值)。
- 批量导入前需先 `DELETE WHERE 日期=?` 清理该日期数据
- 去重时用 `SELECT ... GROUP BY HAVING COUNT(*) > 1` 先确认
---
**Cron ID**: `b7a874fb42d0` | **时间**: 30 22 * * *
**文件(Mobile BI 邮件,主题 `【Mobile BI】自动订阅报表 (1份)`)**:
| 报表 | 文件名模式 |
| 基础库存 | `自动报表_库存分类统计_当日库存_基础_YYYYMMDD_HHMMSS.xlsx` |
| 含二三类库存 | `自动报表_库存分类统计_当日库存_含二三类_YYYYMMDD_HHMMSS.xlsx` |
| 店铺销售 | `自动报表_零售汇总_当日销售_店铺_YYYYMMDD_HHMMSS.xlsx` |
| 营业员销售 | `自动报表_零售汇总_当日销售_营业员_YYYYMMDD_HHMMSS.xlsx` |
**硬性规则**:
- 日期从 xlsx 文件名提取,不用系统时钟
- 只查当天 22:00 之后的邮件
- 4 个附件日期必须一致
- 邮件正文"数据范围"必须与文件名日期一致
**投递**(飞书私聊 `oc_569a01e75d67f5f0422a994aa7132365`):
1. 文字总结(双数/金额/毛利/各店)
2. 📈 销售分析: https://sieta.vip/sietadata/sales_analysis_mobile.html
3. 📸 销售图片: https://sieta.vip/sietadata/sales_pics.html
**Cron ID**: `314b745063bd` | **时间**: 0 9 * * 1
- 脚本: `~/sietadata/scripts/generate_weekly_report.py`
- 生成上周(周一~周日)周报 HTML
- 投递飞书群: `oc_9eeddda2511e3e8c62ed671017134814`(同每晚 22:30 群)
**Cron ID**: `1e93398d4126` | **时间**: 59 22 * * 6(每周六 22:59)
- 快照到 `~/sietadata/db/snapshots/sieta_YYYY-MM-DD.db`
- 保留最新 5 份滚动
- 脚本: `~/sietadata/scripts/db_snapshot.sh`
---
| 页面 | URL |
| 销售分析(H5) | https://sieta.vip/sietadata/sales_analysis_mobile.html |
| 销售图片墙 | https://sieta.vip/sietadata/sales_pics.html |
| 周报 | https://sieta.vip/sietadata/weekly_review_YYYY-MM-DD.html |
- **背景色**: `#FFFDF5`
- **卡片**: 白色背景 + `3px solid #000` 边框 + `4px 4px 0 #000` 阴影
- **字体**: `-apple-system, BlinkMacSystemFont, "Noto Sans SC", sans-serif`
- **KPI 数值**: `1.6em`, `font-weight:900`
- **按钮**: `#FFD93D` 背景, `3px solid #000` 边框
固定顺序: STA102(二店)→ STA104(四店)→ STA105(五店)→ STA106(六店)→ STA108(八店)
| 品牌 | 颜色 | Hex |
| 依歌尔诗 | 淡紫 | `#C4B5FD` |
| 娇娅媚妮 | 粉色 | `#F472B6` |
| 是她 | 深红 | `#DC2626` |
| 迷人角色 | 绿色 | `#4ADE80` |
| 其他 | 灰色 | `#94A3B8` |
**柱状图(各店销售额)**:
- `indexAxis: 'y'` 横向
- 双坐标轴:上=销售额(¥)、下=双数
- `barPercentage: 1.0, categoryPercentage: 0.78`
- 销售额数据集: `#FF6B6B`, yAxisID='y', xAxisID='x1'
- 双数数据集: `#C4B5FD`, yAxisID='y', xAxisID='x'
**环形图(品牌排名)**:
- 3 层嵌套: 内层双数(weight=2, CC透明度) + 透明间隔(weight=1) + 外层销售额(weight=7)
- `cutout: '2%'`
- 仅标注前 3 名: `display: function(ctx){return ctx.dataIndex<3}`
- datalabels 插件本地托管: `/sietadata/chartjs-plugin-datalabels.min.js`
1. 标题: `Sieta 销售分析` + `YYYYWNN · YYYY/MM/DD — YYYY/MM/DD`(18px, bold)
2. KPI 五栏(销售额/双数/毛利/店铺均价/商场均价,含日均)
3. 各店销售额柱状图
4. 品牌排名环形图
5. 营业员业绩表
6. 每日明细表(黄色背景)
---
| 内容 | 目标 | 时间 |
| 每日销售日报 | 飞书群 `oc_9eeddda2511e3e8c62ed671017134814` | 22:30 |
| 周报 | 同上群 | 周一 09:00 |
| 库存变化 | 大头哥私聊 `oc_569a01e75d67f5f0422a994aa7132365` | 22:30 |
| 错误告警 | 大头哥私聊 | 即时 |
---
1. **绝不**把 `sales_ledger` 和 `staff_sales` 的金额/双数相加
2. **绝不**只给本地文件路径(必须给 HTTP 链接)
3. **绝不**跳过 gbrain/记忆召回直接动手
4. **绝不**在 `.xls` 文件上用默认 `header=0`(oupu 老格式表头在第 5 行)
5. **绝不**在数据库为空时跑 `INSERT OR IGNORE` 不先 `DELETE`(UNIQUE+NULL 不防重)
6. **绝不**修改 Gallery/Finance 页面时破坏现有 `Sieta@2006` token 登录
7. **绝不**顺手加功能——修 Bug 只修目标,不改造工作流
8. **决不**用 guesswork——不知道就说不知道
---
| Job ID | 名称 | 时间 | 脚本/模式 |
| `b7a874fb42d0` | 每日 Pipeline | 30 22 * * * | agent 模式,收邮件→入库→发飞书私聊 |
| `314b745063bd` | 每周周报 | 0 9 * * 1 | agent 模式,跑脚本→发飞书群 |
| `1e93398d4126` | DB 快照 | 59 22 * * 6 | no_agent,`db_snapshot.sh` |
---
所有 API 在 `~/admin-auth-server.py` (port 7891),nginx `/api/` 代理:
| 路由 | Auth | 说明 |
| `/api/sietadata/sales` | 公开 | 单日销售 |
| `/api/sietadata/sales-range` | 公开 | 日期范围聚合 |
| `/api/sietadata/sales-pics` | 公开 | 图片墙数据 |
| `/api/sietadata/available-dates` | 公开 | 可用日期列表 |
| `/api/sietadata/inventory` | 公开 | 库存数据 |
| `/api/auth/*`, `/api/upload` | admin_token JWT | 管理功能 |
⚠️ sietadata 接口**不加** `get_current_user` 检查。
---
| Python | 用途 |
| 系统 python3 | ✅ 所有 sietadata 脚本 |
| hermes venv | ❌ 不要用(缺 openpyxl 等包) |
依赖: `pip install openpyxl pandas xlrd -q`
himalaya: `~/.cargo/bin/himalaya`, 配置 `foxmail` 账户