diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index f65da4a..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..4dce4d1 --- /dev/null +++ b/README.md @@ -0,0 +1,233 @@ +# GMV Analytics + +按项目、店铺和日期统计销售金额、退款金额及 GMV,并同步到飞书多维表格。支持本地手动执行、历史日期补数,以及通过 Docker + Prefect 定时运行。 + +## 项目介绍 + +数据来自 PostgreSQL,通过 `店铺映射表.xlsx` 将 BI 店铺名称转换为业务店铺名称,再按项目和月份匹配飞书目标表。 + +- 支持项目:大眼睛、羽心堂、MMMJ、EM、春回盏、BROKEN ZOO。 +- 计算口径:`GMV = 销售金额 - 退款金额`,具体取数 SQL 位于 `project_bitable_common.py`。 +- 本地流水线逐日查询销售额和退款额,计算 GMV,写入对应项目的月份表。 +- 当前流水线使用“日期”“指标”和各店铺数值列,指标包括“销售金额”“退款金额”“GMV”“汇总GMV”。汇总逻辑读取表内当月、不晚于处理日期的 GMV 记录进行累加。 +- 可选择同步完成后向飞书群发送通知。 + +## 环境准备 + +需要 Python 3.12 或以上版本、可连接的 PostgreSQL 数据库,以及具有目标多维表格访问和写入权限的飞书应用。 + +以下命令均在项目根目录执行。已有 Conda / Python 环境时,先激活该环境。 + +使用 uv 安装依赖并进入虚拟环境: + +```bash +uv sync +source .venv/bin/activate +``` + +也可以在已有 Python 环境中安装项目声明的依赖: + +```bash +python - <<'PY' +import subprocess +import sys +import tomllib +from pathlib import Path + +config = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8")) +subprocess.check_call([sys.executable, "-m", "pip", "install", *config["project"]["dependencies"]]) +PY +``` + +## 配置 .env + +在项目根目录创建或编辑 `.env`。下面均为占位值,请替换为实际配置: + +```dotenv +ANALYTICS_DB_HOST=your-db-host +ANALYTICS_DB_PORT=5432 +ANALYTICS_DB_NAME=your-db-name +ANALYTICS_DB_USER=your-db-user +ANALYTICS_DB_PASSWORD=your-db-password + +FEISHU_APP_ID=your-app-id +FEISHU_APP_SECRET=your-app-secret +FEISHU_BASE_URL=https://your-tenant.feishu.cn/base/your-base-token + +SYNC_START_DATE=2026-09-01 +``` + +脚本自动加载项目根目录的 `.env`,已存在的进程环境变量优先于 `.env`。当前加载器按 `KEY=value` 读取,值不要额外加引号,也不要在值后追加行内注释。 + +`.env` 已加入 `.gitignore`,用于保存本地配置。 + +可选配置: + +| 变量 | 用途与默认行为 | +| --- | --- | +| `PROJECT_MAPPING_FILE` | 店铺映射文件路径;不设置时使用脚本目录下的 `店铺映射表.xlsx` | +| `SYNC_START_DATE` | 本地流水线默认开始日期;未设置时为 `2026-04-01` | +| `FEISHU_CHAT_ID` | 应用机器人通知的目标群;配置后优先使用此方式 | +| `FEISHU_BOT_WEBHOOK` | 未设置群 ID 时使用的群机器人 Webhook | +| `FEISHU_REPORT_IMAGE_PATH` | 应用机器人通知使用的本地预览图路径 | +| `FEISHU_STORE_FIELD_NAME` | 独立脚本使用的店铺字段名,默认 `店铺名称` | +| `PREFECT_API_URL` | Prefect Server API 地址,本地流水线不需要 | +| `LOG_DIR` | 日志目录,默认项目下的 `logs` | +| `LOG_LEVEL` | 日志级别,默认 `INFO` | + +映射文件读取第一个活动工作表,跳过第一行表头。三列及以上时,使用第 2 列作为业务店铺名称、第 3 列作为 BI 店铺名称;只有两列时,依次为业务店铺名称、BI 店铺名称。 + +## 本地执行 + +本地入口为 `run_project_bitable_pipeline.py`,可直接使用 Python 运行,无需启动 Docker 或 Prefect Server。未激活 uv 虚拟环境时,也可以将以下命令中的 `python` 替换为 `uv run python`。 + +### 指定一天 + +将开始日期和结束日期设为同一天: + +```bash +python run_project_bitable_pipeline.py \ + --start-date 2026-09-07 \ + --end-date 2026-09-07 \ + --no-notify \ + --skip-missing-tables +``` + +### 指定日期范围 + +例如同步 2026 年 9 月 1 日至 9 月 7 日,包含首尾两天: + +```bash +python run_project_bitable_pipeline.py \ + --start-date 2026-09-01 \ + --end-date 2026-09-07 \ + --no-notify \ + --skip-missing-tables +``` + +`--no-notify` 表示只同步数据,不发群通知。需要通知时,配置通知渠道并去掉该参数。 + +`--skip-missing-tables` 表示找不到对应表时记录日志并继续处理其他项目和日期,不会自动创建月份表。当前实现也会跳过匹配到多个表、映射指定表不存在等表匹配错误;应查看跳过日志确认哪些项目尚未同步。数据库连接或飞书写入失败等其他错误仍会中断运行。 + +### 预览执行 + +```bash +python run_project_bitable_pipeline.py \ + --start-date 2026-09-01 \ + --end-date 2026-09-07 \ + --dry-run \ + --no-notify \ + --skip-missing-tables +``` + +`--dry-run` 会查询数据库、获取飞书访问令牌和表列表,并输出计划处理的表、日期和指标,不写入飞书、不发通知。因此仍需有效的数据库和飞书配置。 + +### 常用参数 + +| 参数 | 说明 | +| --- | --- | +| `--start-date YYYY-MM-DD` | 开始日期,默认读取 `SYNC_START_DATE` | +| `--end-date YYYY-MM-DD` | 结束日期,默认运行机器本地日期的昨天 | +| `--no-notify` | 跳过飞书群通知,默认未开启 | +| `--skip-missing-tables` | 跳过表匹配错误,默认未开启 | +| `--dry-run` | 预览,不执行飞书写入和通知 | +| `--mapping-file PATH` | 指定店铺映射 Excel 文件,覆盖环境变量配置 | +| `--table-map-file PATH` | 指定项目月份到飞书表名的 JSON 映射 | +| `--base-url URL` | 指定飞书多维表格地址;仍需在环境中配置 `FEISHU_BASE_URL`,当前参数解析会读取其默认值 | + +日期范围的结束日期不能早于开始日期。本地流水线未传日期时会从默认开始日期一直同步到昨天;补数时建议明确传入两个日期。 + +完整参数可在配置好 `.env` 后查看: + +```bash +python run_project_bitable_pipeline.py --help +``` + +## 常见问题 + +### 映射表路径不存在 + +如果出现 `FileNotFoundError`,且路径仍指向其他机器上的 `/Users/idss/...`,请修改 `.env` 中的 `PROJECT_MAPPING_FILE`,或删除该配置以使用项目自带文件。 + +也可以在命令中添加以下参数,直接覆盖旧路径: + +```bash +--mapping-file ./店铺映射表.xlsx +``` + +### 找不到对应月份的飞书表 + +例如“羽心堂”只有 3—8 月表,执行 9 月数据时会报 `未找到匹配表`。临时跳过可添加 `--skip-missing-tables`;补齐月份表后,再运行对应日期范围。 + +### 表名不规范或匹配到多个表 + +创建 `table_map.json`,键格式为 `项目:YYYY-MM`,值为飞书中实际存在的完整表名: + +```json +{ + "大眼睛:2026-09": "大眼睛买买买-9月", + "羽心堂:2026-09": "羽心堂-9月" +} +``` + +执行时添加 `--table-map-file ./table_map.json`。自动匹配支持项目别名和月份文本,跨年份存在同名月份表时应显式指定映射。 + +## Prefect 执行与定时调度 + +手动触发一次 Prefect 数据同步: + +```bash +python prefect_gmv_scheduler.py \ + --mode sync \ + --start-date 2026-09-01 \ + --end-date 2026-09-07 \ + --skip-missing-tables +``` + +| 模式 | 行为 | +| --- | --- | +| `--mode sync` | 只同步数据 | +| `--mode notify` | 只发送通知 | +| `--mode all` | 先同步再通知,默认模式 | + +Prefect 同步未显式指定日期时,默认处理截至昨天的最近 5 天,并以 `SYNC_START_DATE` 为最早边界。通知未指定日期时,优先读取上次成功同步保存的日期范围。 + +`prefect.yaml` 定义了北京时间每天 08:00 同步、09:00 通知两条调度。部署前需准备可访问的 Prefect Server,并配置 `PREFECT_API_URL`。 + +```bash +docker compose up -d --build +docker exec -it prefect-gmv-worker sh -lc 'cd /app && prefect deploy --all' +``` + +Compose 启动的是等待调度任务的 worker。定时任务需要跳过缺失表时,将 `prefect.yaml` 对应 deployment 的 `parameters.skip_missing_tables` 改为 `true`,再重新执行 `prefect deploy --all` 注册配置。 + +更多部署说明见项目中的 `DOCKER_PREFECT_USAGE.md`;其中示例的本机目录需要替换为实际项目目录。 + +## 文件说明与日志 + +| 文件 | 用途 | +| --- | --- | +| `run_project_bitable_pipeline.py` | 本地完整流水线入口,使用日期 / 指标行结构 | +| `project_bitable_common.py` | SQL、店铺映射、表匹配、飞书 API、同步与通知逻辑 | +| `prefect_gmv_scheduler.py` | Prefect flow、日期窗口和执行模式 | +| `generate_project_sales_csv.py` | 独立销售额同步脚本 | +| `generate_project_refund_csv.py` | 独立退款额同步脚本 | +| `generate_project_gmv_csv.py` | 从飞书销售额和退款额字段计算并回写 GMV | +| `店铺映射表.xlsx` | BI 店铺与业务店铺名称映射 | +| `logging_config.py` | 控制台与轮转文件日志 | +| `prefect.yaml` | 定时任务与 work pool 配置 | +| `docker-compose.yaml` / `Dockerfile` | 容器运行与构建配置 | + +三个 `generate_project_*_csv.py` 文件虽带有 `csv` 后缀,实际执行飞书同步;它们使用以店铺为记录、日期指标为字段的结构,与当前完整流水线的表结构不同。日常运行使用完整流水线入口。 + +默认日志写入 `logs/gmv_sync.log`,单个文件达到 10 MiB 后轮转,保留 7 个备份。 + +```bash +tail -f logs/gmv_sync.log +``` + +Docker worker 日志: + +```bash +docker logs -f prefect-gmv-worker +``` diff --git a/__pycache__/project_bitable_common.cpython-312.pyc b/__pycache__/project_bitable_common.cpython-312.pyc index 47c2077..78a78a6 100644 Binary files a/__pycache__/project_bitable_common.cpython-312.pyc and b/__pycache__/project_bitable_common.cpython-312.pyc differ