# AGENTS.md ETL 工作流项目,使用 [Prefect](https://docs.prefect.org.cn/v3/get-started) 编排。 任务:REST API → PostgreSQL(Extract / Transform / Load)。 ## 设计理念(简述) - **高内聚、低耦合、关注点分离**。 - `api/` 是纯客户端层(httpx + pydantic,**禁止 import prefect**),封装 RIIN/SDS 两个外部系统的 REST API,可独立复用/测试。 - `flows/` 是编排层(依赖 `api/`,使用 prefect),负责重试/缓存/并发/调度/密钥。 - 共享行为(token 注入、401 重登、分页、批量、缓存)放 `api/_common/`,用组合式 helper,不过度抽象。 - 新增系统是加法式:加一个 `api/<系统>/` + 一个 flow,不碰旧代码。 - 详细架构说明见 [`docs/dev/systematic-design.md`](docs/dev/systematic-design.md)。 ## 依赖管理:使用 uv(不要用 requirements.txt) 本项目用 [uv](https://docs.astral.sh/uv/) 管理依赖,锁定文件为 `uv.lock`。 **没有 requirements.txt,也不要新建。** ```bash uv sync # 按 uv.lock 安装依赖到 .venv(首次/拉取代码后执行) uv add # 添加依赖(自动写入 pyproject.toml + 更新 uv.lock) uv add --dev # 添加开发依赖 uv remove # 移除依赖 uv lock --upgrade # 升级所有依赖的锁定版本 uv run python # 在项目环境中运行(无需手动激活 venv) uv run prefect # 运行 prefect CLI ``` - Python 版本固定为 `>=3.12`(见 `pyproject.toml`)。 - 所有命令都用 `uv run ...` 前缀,**不要**直接用系统 `python` / `pip`。 - 提交代码时一并提交 `pyproject.toml` 和 `uv.lock`;**不要提交 `.venv/`**。 ## 运行环境 - **Prefect server**:自托管,通过 Docker Compose 运行,数据持久化。 ```bash docker compose up -d # 启动 server(UI: http://192.168.124.92:4200) docker compose ps # 查看状态 docker compose logs -f # 查看日志 docker compose down # 停止 ``` - **API 地址**:`http://localhost:4200/api`(本机)/ `http://192.168.124.92:4200`(局域网)。 - 客户端已配置:`uv run prefect config set PREFECT_API_URL="http://localhost:4200/api"`。 ## 密钥管理(标准做法) - **不要把密钥写进代码或 pyproject.toml**。 - 数据库凭据用 Prefect Block 加密存储,Block 名:`pg-inkreach-dmp`(`SqlAlchemyConnector` 类型)。 ```python from prefect_sqlalchemy import SqlAlchemyConnector connector = SqlAlchemyConnector.load("pg-inkreach-dmp") engine = connector.get_engine() ``` - RIIN / SDS 登录凭据用 `Secret` Block 加密存储,值为 JSON:`{"username":"...","password":"..."}`。 Block 名:`riin-credentials`、`sds-credentials`。flow 在 extract task 内部读取,不外泄。 - 本地开发备用:`.env`(已 gitignore)含 `DB_DSN`,加载用 `python-dotenv` 或 `os.environ`。 ## 编写/运行工作流 - 同步任务(对齐 inkreach-bi-auth-mng 的 sync tasks)放在 `flows/riin/` 和 `flows/sds/`,每系统各 6 个: - `order_sync`(按时间窗口)、`incremental_sync`(按 DB 最近下单时间增量,无参)、 `recent_sync`(近 N 天)、`refresh_pending`(刷新未完结)、`by_orders`(RIIN 指定单号)/ `refresh_100_prefix`(SDS 100 前缀)、`verify`(逐天核验修复)。 - 共享层在 `flows/shared/`:`mappers.py`(RIIN/SDS→统一 Order/Goods)、`db.py`(PG upsert `SyncDB`)、`clients.py`(客户端工厂+顺序分页+重试+渠道字典)。 - 运行单个 flow:`uv run python flows/riin/.py`(或 `flows/sds/.py`) - 部署调度:flow 末尾 `flow.serve(name=..., cron=...)`,保持 serve 进程运行。 - 写入目标:PG 的 `Orders` / `Goods` / `Merchant` 表(主键 `Orders=("orderNo","createdAt")`、`Goods=("orderNo","productionNo")`),`createdAt` 写入前以库中已有值覆盖以保证幂等。 ## 参考文档 - Prefect 入门:https://docs.prefect.org.cn/v3/get-started - API 驱动 ETL 示例:https://docs.prefect.org.cn/v3/examples/run-api-sourced-etl - 密钥存储:https://docs.prefect.org.cn/v3/how-to-guides/configuration/store-secrets