This commit is contained in:
2026-04-29 14:06:41 +08:00
parent 65216cd4cf
commit 8e4c2eef98
37 changed files with 2686 additions and 1985 deletions
+350 -154
View File
@@ -1,45 +1,137 @@
# Inventory Analytics 工程说明
# Inventory Analytics 项目说明
## 项目概览
## 1. 项目概览
当前工程包含两条独立业务链路:
当前项目围绕 3 条业务链路运行:
- 委外批次库存上传:通过本地网页上传 Excel/CSV 文件,保存原始文件,并逐行写入 Raw PostgreSQL 表。
- 飞书库存同步:从 Analytics PostgreSQL 查询库存、挂单量、销售和商品信息,并同步到飞书多维表格。
- 委外批次库存上传:通过本地网页上传 Excel/CSV 文件,归档原始文件,并逐行写入 Raw PostgreSQL。
- 渠道库存统计同步飞书:从 Analytics PostgreSQL 查询渠道库存、挂单量、销售与商品信息,并同步到飞书多维表格。
- 批次库存统计报表:读取最新批次库存、关联 Analytics 店铺库存与近 30 天销售,生成本地报表并可同步到飞书。
## 目录结构
命名说明:
- “渠道库存统计”是框架层命名,当前默认业务目标仍是羽心堂渠道库存。
- “批次库存统计”是框架层命名,当前默认业务目标仍是羽心堂批次库存。
- 后续扩展到其他品牌时,优先复用这两条通用链路,而不是继续新增带品牌名的脚本和包名。
项目已经完成一轮框架调整:
- 新实现逐步迁入 `inventory_analytics/` 包。
- 根目录脚本保留兼容入口。
- `scripts/` 目录提供新的标准执行入口。
- 旧的 `inventory_sync/` 仍保留,当前被渠道库存统计链路复用,属于过渡层。
## 2. 当前目录结构
```text
inventory_analytics/
├── .env
├── pyproject.toml
├── PROJECT_GUIDE.md
├── REFACTOR_PLAN.md
├── upload_inventory_page.py
├── Dockerfile
├── docker-compose.yaml
├── docker-entrypoint.sh
├── supervisord.conf
├── prefect.yaml
├── prefect_inventory_scheduler.py
├── generate_batch_inventory_report.py
├── generate_yxt_batch_inventory_report.py
├── list_feishu_base_tables.py
└── inventory_sync/
├── __init__.py
├── cli.py
├── feishu.py
├── postgres.py
├── service.py
├── settings.py
├── specs.py
└── sql.py
├── sync_channel_inventory.py
├── upload_inventory_page.py
├── scripts/
│ ├── generate_batch_inventory_report.py
│ ├── sync_channel_inventory.py
│ └── upload_batch_inventory.py
├── inventory_analytics/
│ ├── __init__.py
│ ├── config.py
│ ├── logging_utils.py
│ ├── paths.py
│ ├── db/
│ │ └── __init__.py
│ ├── feishu/
│ │ ├── __init__.py
│ │ └── client.py
│ ├── batch_upload/
│ │ ├── __init__.py
│ │ └── web.py
│ ├── channel_inventory_sync/
│ │ ├── __init__.py
│ │ ├── cli.py
│ │ ├── entrypoint.py
│ │ ├── queries.py
│ │ ├── service.py
│ │ └── specs.py
│ └── batch_inventory_report/
│ ├── __init__.py
│ ├── cli.py
│ └── service.py
├── inventory_sync/
│ ├── __init__.py
│ ├── cli.py
│ ├── feishu.py
│ ├── postgres.py
│ ├── service.py
│ ├── settings.py
│ ├── specs.py
│ └── sql.py
├── batch_inventory_data/
├── logs/
└── reports/
```
运行后会自动创建或使用:
说明:
```text
batch_inventory_data/
```
- `inventory_analytics/` 是当前主包,后续新增实现优先放这里。
- `scripts/` 是推荐执行入口。
- 根目录兼容入口仍保留,避免旧命令立即失效。
- `inventory_sync/` 当前仍在使用,但属于待进一步收敛的历史模块。
该目录保存上传的委外批次库存原始文件,不纳入业务代码结构。
## 3. 模块职责
## 环境变量
### 3.1 通用基础层
### 委外批次库存上传
| 文件 | 说明 |
| --- | --- |
| `inventory_analytics/config.py` | 统一加载 `.env`、读取数据库和飞书配置 |
| `inventory_analytics/logging_utils.py` | 统一日志初始化与敏感信息脱敏 |
| `inventory_analytics/paths.py` | 统一维护项目根目录、日志目录、报表目录、上传目录 |
上传页使用 `.env` 中的 Raw 数据库配置:
### 3.2 上传链路
| 文件 | 说明 |
| --- | --- |
| `scripts/upload_batch_inventory.py` | 推荐入口 |
| `upload_inventory_page.py` | 兼容入口 |
| `inventory_analytics/batch_upload/web.py` | 上传页、文件解析、Raw 写库实现 |
### 3.3 渠道库存统计链路
| 文件 | 说明 |
| --- | --- |
| `scripts/sync_channel_inventory.py` | 推荐入口,默认等价于 `--action all` |
| `sync_channel_inventory.py` | 通用兼容入口 |
| `list_feishu_base_tables.py` | 历史兼容入口 |
| `inventory_analytics/channel_inventory_sync/entrypoint.py` | 新入口日志封装 |
| `inventory_analytics/channel_inventory_sync/cli.py` | 当前复用旧 `inventory_sync.cli` |
| `inventory_sync/` | 当前渠道库存统计实际实现所在模块 |
### 3.4 批次库存统计链路
| 文件 | 说明 |
| --- | --- |
| `scripts/generate_batch_inventory_report.py` | 推荐入口 |
| `generate_batch_inventory_report.py` | 通用兼容入口 |
| `generate_yxt_batch_inventory_report.py` | 历史兼容入口 |
| `inventory_analytics/batch_inventory_report/service.py` | 报表查询、汇总、导出、飞书同步实现 |
## 4. 环境变量
项目主要使用以下配置:
### 4.1 Raw 数据库
```text
RAW_DB_HOST
@@ -49,32 +141,9 @@ RAW_DB_PASSWORD
RAW_DB_NAME
```
写入目标:
### 4.2 Analytics 数据库
```text
raw_data.wangdian_batch_inventory
```
表结构由 `upload_inventory_page.py` 内的 `get_create_table_sql()` 创建:
```sql
payload JSONB NOT NULL,
start_time TIMESTAMP WITH TIME ZONE,
end_time TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
reserved_1 TEXT,
reserved_2 TEXT
```
### 飞书库存同步
飞书同步使用 `.env` 中的飞书和 Analytics 数据库配置:
```text
FEISHU_APP_ID
FEISHU_APP_SECRET
FEISHU_BASE_URL
FEISHU_APP_TOKEN
ANALYTICS_DB_HOST
ANALYTICS_DB_PORT
ANALYTICS_DB_USER
@@ -82,22 +151,92 @@ ANALYTICS_DB_PASSWORD
ANALYTICS_DB_NAME
```
`FEISHU_APP_TOKEN` 可选;当 `FEISHU_BASE_URL` 已包含 `/base/` 或 `/wiki/` 时,可以不单独配置。
### 4.3 飞书配置
## 委外批次库存上传
```text
FEISHU_APP_ID
FEISHU_APP_SECRET
FEISHU_BASE_URL
FEISHU_APP_TOKEN
```
说明:
- `FEISHU_APP_TOKEN` 可选。
- 当 `FEISHU_BASE_URL` 已包含 `/base/` 或 `/wiki/` 时,可以不额外配置 `FEISHU_APP_TOKEN`。
- 配置优先从项目根目录 `.env` 读取,若缺失则回退到当前 shell 环境变量。
## 5. 执行方式
### 5.1 推荐入口
启动上传页:
```bash
python3 upload_inventory_page.py
python3 scripts/upload_batch_inventory.py
```
打开页面:
启动后访问:
```text
http://127.0.0.1:8080
```
执行渠道库存统计完整流程:
```bash
python3 scripts/sync_channel_inventory.py
```
这条命令默认等价于:
```bash
python3 scripts/sync_channel_inventory.py --action all
```
按动作拆分执行:
```bash
python3 scripts/sync_channel_inventory.py --action feishu
python3 scripts/sync_channel_inventory.py --action postgres
python3 scripts/sync_channel_inventory.py --action sync-brand
python3 scripts/sync_channel_inventory.py --action all
```
生成批次库存统计报表并同步飞书:
```bash
python3 scripts/generate_batch_inventory_report.py
```
只生成本地报表,不同步飞书:
```bash
python3 scripts/generate_batch_inventory_report.py --skip-feishu-sync
```
自定义输出路径:
```bash
python3 scripts/generate_batch_inventory_report.py --output reports/custom.xlsx
```
### 5.2 兼容入口
以下旧命令或兼容命令仍可继续使用:
```bash
python3 upload_inventory_page.py
python3 sync_channel_inventory.py --action all
python3 list_feishu_base_tables.py --action all
python3 generate_batch_inventory_report.py
python3 generate_yxt_batch_inventory_report.py
```
## 6. 各链路行为说明
### 6.1 委外批次库存上传
支持文件:
- `.xlsx`
@@ -113,144 +252,201 @@ http://127.0.0.1:8080
上传成功后会执行:
1. 保存文件到 `batch_inventory_data/`。
2. 解析 Excel/CSV 为行数据。
3. 创建 Raw schema/table 和 `created_at` 索引。
4. 每一行插入一条 JSONB payload。
1. 保存原始文件到 `batch_inventory_data/`
2. 解析 Excel 或 CSV
3. 自动确认 `raw_data.wangdian_batch_inventory` 表存在
4. 每一行写入一条 JSONB payload
payload 示例:
```json
{
"source_file": "委外批次库存.xlsx",
"stored_file": "20260423_172410_委外批次库存.xlsx",
"uploaded_at": "2026-04-23T18:20:00",
"total_count": 120,
"inventory_data": {
"货品编码": "SKU001",
"批次": "BATCH001",
"数量": 10
}
}
```
## 飞书库存同步
查看飞书 Base 表:
```bash
python3 list_feishu_base_tables.py --action feishu
```
只查询 PostgreSQL:
```bash
python3 list_feishu_base_tables.py --action postgres
```
同步品牌库存到飞书:
```bash
python3 list_feishu_base_tables.py --action sync-brand
```
执行完整流程:
```bash
python3 list_feishu_base_tables.py --action all
```
同步目标配置在 `inventory_sync/specs.py`:
```python
BRAND_SYNC_TARGETS = [
BrandSyncTarget(brand_name="羽心堂", table_name="羽心堂", warehouse_keys=(2, 3, 7, 13)),
]
```
字段映射也集中维护在 `inventory_sync/specs.py` 的 `FIELD_SPECS`。
## 日志说明
项目使用结构化事件日志,格式:
写入表:
```text
YYYY-MM-DD HH:MM:SS LEVEL logger_name 事件=中文事件描述 中文字段=字段值 ...
raw_data.wangdian_batch_inventory
```
上传页日志输出到 stdout,典型事件包括:
### 6.2 渠道库存统计同步飞书
当前支持动作:
```text
事件=上传服务启动中
事件=收到上传请求
事件=已解析上传文件
事件=上传文件已保存
事件=开始解析库存文件
事件=已解析Excel工作表
事件=已加载Raw数据库配置
事件=Raw数据表确认完成
事件=Raw数据写入完成
事件=上传请求处理完成
feishu
postgres
sync-brand
all
```
飞书同步日志输出到 stderr,JSON 结果仍输出到 stdout,便于命令行管道消费。典型事件包括:
说明:
- 当前脚本名已经泛化为“渠道库存统计”,但内部动作参数 `sync-brand` 仍来自历史实现,后续可再统一。
- 当前默认同步目标仍是羽心堂渠道库存。
渠道库存统计目标和字段映射当前仍维护在:
- `inventory_sync/specs.py`
- `inventory_sync/sql.py`
### 6.3 批次库存统计报表
当前报表链路会:
1. 读取 `raw_data.wangdian_batch_inventory` 中最新批次数据
2. 提取商家编码
3. 查询 Analytics 中对应店铺可发库存、商品信息、近 30 天销售数量
4. 生成合并报表
5. 输出到 `reports/`
6. 可选同步到飞书 `批次库存` 表
说明:
- 当前命名已泛化为“批次库存统计”
- 当前默认业务规则仍针对羽心堂
默认输出文件名格式:
```text
事件=飞书同步入口启动
事件=飞书同步动作开始
事件=已加载环境变量文件
事件=开始读取飞书Base信息
事件=开始查询PostgreSQL
事件=开始同步品牌数据
事件=开始同步目标飞书表
事件=目标飞书表同步完成
事件=飞书同步入口完成
reports/批次库存统计_YYYYMMDD_HHMMSS.xlsx
```
## 查看和关闭上传服务
## 7. 字段同步策略
查看 8080 端口:
渠道库存统计和批次库存统计在同步飞书时,都采用相同策略:
1. 先读取当前飞书表字段
2. 判断当前字段结构是否与期望配置一致
3. 如果一致:只删除旧记录,再写入新记录
4. 如果不一致:删除受管字段并按配置重新创建,再写入新记录
## 8. 日志说明
日志统一采用结构化事件格式:
```text
YYYY-MM-DD HH:MM:SS LEVEL logger_name 事件=中文事件描述 中文字段=字段值
```
主要日志文件:
```text
logs/upload_inventory_page.log
logs/sync_channel_inventory.log
logs/generate_batch_inventory_report.log
```
说明:
- 上传页日志主要输出到 stdout,并写入 `upload_inventory_page.log`
- 渠道库存统计日志主要输出到 stderr,并写入 `sync_channel_inventory.log`
- 批次库存统计日志输出到 stdout,并写入 `generate_batch_inventory_report.log`
## 9. 推荐验证方式
验证渠道库存统计入口:
```bash
lsof -nP -iTCP:8080 -sTCP:LISTEN
python3 scripts/sync_channel_inventory.py --action feishu
```
关闭服务:
验证上传页:
```bash
kill <PID>
python3 scripts/upload_batch_inventory.py
```
如果普通关闭失败:
验证批次库存统计本地导出:
```bash
kill -9 <PID>
python3 scripts/generate_batch_inventory_report.py --skip-feishu-sync
```
## 验证命令
语法检查:
编译校验:
```bash
python3 -m py_compile upload_inventory_page.py list_feishu_base_tables.py inventory_sync/*.py
PYTHONPYCACHEPREFIX=/tmp/inventory_analytics_pycache python3 -m compileall inventory_analytics scripts generate_batch_inventory_report.py generate_yxt_batch_inventory_report.py sync_channel_inventory.py list_feishu_base_tables.py upload_inventory_page.py prefect_inventory_scheduler.py
```
查看飞书同步帮助:
## 10. Docker 与 Prefect 部署
项目已补齐以下部署文件:
```text
Dockerfile
docker-compose.yaml
docker-entrypoint.sh
supervisord.conf
prefect.yaml
prefect_inventory_scheduler.py
```
### 10.1 调度目标
当前通过 Prefect 定义了 2 个 deployment,都会在每天 `09:15`(`Asia/Shanghai`)执行:
- 渠道库存统计并写入飞书
- 批次库存统计报表并写入飞书
### 10.2 依赖前提
需要先准备:
- 可访问的 Prefect API
- 已创建的 Prefect work pool:`inventory-analytics-pool`
- 项目根目录 `.env`
如果 Prefect API 跑在宿主机本地,容器默认使用:
```text
http://host.docker.internal:4200/api
```
### 10.3 本地注册 deployment
```bash
python3 list_feishu_base_tables.py --help
prefect deploy -p prefect.yaml --all
```
本地启动上传页:
### 10.4 启动 Docker worker
```bash
python3 upload_inventory_page.py
docker compose up -d --build
```
## 维护原则
这会启动一个容器,容器内通过 `supervisord` 常驻运行:
- 上传 Raw 数据保持原始粒度,每行一条 payload,不在上传阶段做复杂业务清洗。
- 飞书同步使用 `inventory_sync/specs.py` 管理品牌目标和字段映射。
- SQL 调整优先修改 `inventory_sync/sql.py`。
- 入口脚本保持稳定,便于人工执行和后续定时任务接入。
- 日志新增时优先使用 `事件=<中文描述>`,并输出足够定位问题的关键字段。
```text
prefect worker start --pool "inventory-analytics-pool" --type process
```
### 10.5 手工触发 Prefect flow
立即执行渠道库存统计:
```bash
python3 prefect_inventory_scheduler.py --run-channel-inventory-sync
```
立即执行批次库存统计:
```bash
python3 prefect_inventory_scheduler.py --run-batch-inventory-report
```
### 10.6 本地 serve 模式
```bash
python3 prefect_inventory_scheduler.py --serve-channel-inventory-sync
python3 prefect_inventory_scheduler.py --serve-batch-inventory-report
```
### 10.7 调度入口说明
Prefect 调度脚本不会走 shell 子进程,而是直接调用当前项目入口逻辑:
- 渠道库存统计等价于 `python3 scripts/sync_channel_inventory.py --action all`
- 批次库存统计等价于 `python3 scripts/generate_batch_inventory_report.py`
## 11. 维护建议
- 新增功能优先落在 `inventory_analytics/` 包内,不再继续向根目录增加重脚本。
- 新命令优先使用 `scripts/` 目录中的入口。
- 渠道库存统计后续应继续把 `inventory_sync/` 中的实现迁入 `inventory_analytics/channel_inventory_sync/` 与 `inventory_analytics/feishu/`。
- 公共配置、日志、路径处理统一走 `config.py`、`logging_utils.py`、`paths.py`。
- 涉及渠道库存统计 SQL 和字段映射时,当前仍优先修改 `inventory_sync/sql.py` 与 `inventory_sync/specs.py`。