Metadata-Version: 2.4
Name: ozon-seller-cli
Version: 0.1.0
Summary: Pure Python Ozon seller CLI for OpenClaw
Requires-Python: >=3.12
Description-Content-Type: text/markdown
Requires-Dist: redis<6,>=5

# Ozon Seller Skill for OpenClaw

一个纯 Python 的 Ozon 卖家 OpenClaw skill，覆盖商品、价格、库存、订单、发货、财务、分析、仓库、促销、退货、分类、聊天、品牌和队列自动化。

## 当前状态

截至 `2026-04-22`，当前实现与验证结果如下：

- `python3 -m unittest discover -s tests -p 'test_*.py'` -> `135 passed, 0 failed`
- `python3 -m compileall src/ozon_seller_cli ozon_cli.py scripts/live_smoke.py scripts/orchestration_runner.py sdk_server` -> `passed`
- `python3 ozon_cli.py --help` -> `passed`
- `python3 scripts/live_smoke.py --list-write-tiers` -> `passed`
- `python3 scripts/live_smoke.py` -> `待本次新增只读 smoke 同步后更新`

当前 live smoke 默认跳过的 3 项：

- 当前账号只有 FBS，没有可用的 FBO posting detail fixture
- 低风险 dangerous live 的 `update_product_prices` 默认关闭
- 低风险 dangerous live 的 `create_report` 默认关闭

## OpenClaw 使用方式

OpenClaw 侧推荐直接说自然语言，不需要记函数名。

高频示例：

```text
用 Ozon skill 查询 主账户 当前全部商品状态
用 Ozon skill 查询 主账户 最近7天的交易流水
用 Ozon skill 查询 主账户 本月利润损失报表
用 Ozon skill 查询 主账户 错误率和物流罚金预警
用 Ozon skill 查询 主账户 退货取消归因报表
用 Ozon skill 获取 主账户 所有未履约 FBS 订单
用 Ozon skill 搜索 主账户 卖家类目 家具
用 Ozon skill 巡检 主账户 被系统自动加入的促销活动
用 Ozon skill 预览 主账户 自动退出被系统自动加入的促销活动
直接执行：用 Ozon skill 把 主账户 SKU-123 的价格更新为 299
直接执行：用 Ozon skill 让 主账户 退出所有被系统自动加入的促销活动
```

更完整的自然语言示例见 [OPENCLAW-PROMPTS.md](https://github.com/laochendeai/ozon-seller/blob/master/OPENCLAW-PROMPTS.md)。

## SDK 入口（V1 起点）

当前仓库在保留现有 OpenClaw / CLI 调用链的同时，新增了一个最小 SDK 入口 `OzonSdk`，用于承接后续受授权控制的公共能力面。

此外，仓库现在还提供一个最小授权服务骨架 `sdk_server/`，用于本地联调 activate / validate / entitlement / Afdian webhook / resync 这条闭环；它与现有 `ozon_cli.py -> cli.py -> services/*.py` 主链隔离，不会反向破坏 OpenClaw skill 工作流。

当爱发电没有 webhook / API 等开发者能力时，当前 V1 路线采用“人工 / 半自动映射闭环”：付款事实仍发生在爱发电，但真正决定 SDK 是否可用的是你自己的授权服务。当前最小管理入口包括：

- `POST /admin/manual-bind`
- `POST /admin/manual-set-status`

这意味着你可以先用人工核验截图 / 用户名 / 备注绑定码的方式，把付款事实写入 License，再由 SDK 通过 `validate` / `entitlements` 读取结果。

授权服务默认拒绝未知 `license_key`，远程 feature flag 也默认拒绝；只有通过后台或 webhook provision 的授权才会生效。启动 `sdk_server` 前必须配置非默认的 `OZON_SDK_ADMIN_TOKEN` 和 `OZON_AFDIAN_WEBHOOK_TOKEN`，所有 `/admin*` 路径都需要 `X-Admin-Token`。

当前最先暴露的 3 个低风险只读能力是：

- `get_products_v3`
- `get_cash_flow`
- `get_product_stocks`

示例：

```python
from ozon_seller_cli import OzonSdk

sdk = OzonSdk()
products = sdk.get_products_v3("主账户", '{"visibility":"ALL"}', "", 10)
cash_flow = sdk.get_cash_flow("主账户", "2026-03-01", "2026-03-30", 1, 10)
stocks = sdk.get_product_stocks("主账户", "SKU-123", 10)
```

SDK 当前默认使用 permissive entitlement provider，因此不会破坏现有内部开发与 OpenClaw skill 工作流；后续授权服务器接入时会沿着这条 seam 增强。

## 直接调用 Python CLI

如果你要在终端里直接调试，进入仓库根目录后使用：

```bash
python3 ozon_cli.py get_products_v3 --account "主账户" --filter '{"visibility":"ALL"}' --limit 10
python3 ozon_cli.py get_transactions --account "主账户" --from-value 2026-03-24 --to-value 2026-03-30
python3 ozon_cli.py update_product_prices --account "主账户" --prices '[{"offer_id":"SKU-123","price":"299.00"}]'
python3 ozon_cli.py get_product_stocks --account "主账户" --offer-id "SKU-123" --limit 10
python3 ozon_cli.py search_categories --account "主账户" --search-term "家具"
python3 ozon_cli.py get_profit_leakage_report --account "主账户" --from-value 2026-03-01 --to-value 2026-03-31
python3 ozon_cli.py get_penalty_alerts --account "主账户" --from-value 2026-03-01 --to-value 2026-03-31
python3 ozon_cli.py get_returns_attribution_report --account "主账户" --from-value 2026-03-01 --to-value 2026-03-31
python3 ozon_cli.py get_forced_promotions_audit --account "主账户"
python3 ozon_cli.py leave_forced_promotions --account "主账户"
python3 ozon_cli.py leave_forced_promotions --account "主账户" --title-keywords '["бустинг"]' --execute
python3 ozon_cli.py queue_stats
```

## 日期处理

财务、订单、分析类查询推荐直接说自然语言日期或完整日期范围。

OpenClaw 中可直接这样说：

```text
用 Ozon skill 查询 主账户 最近7天的交易流水
用 Ozon skill 查询 主账户 本月现金流
用 Ozon skill 查询 主账户 2026-03-24 到 2026-03-30 的现金流
```

skill 内部会把这些日期转成 Ozon 要求的 RFC3339 UTC 时间戳再发请求。例如：

- `2026-03-24` -> `2026-03-24T00:00:00Z`
- `2026-03-30` -> `2026-03-30T23:59:59Z`

直接调用 Python CLI 时，财务接口继续接受 `YYYY-MM-DD`，CLI 也会在请求前完成同样的转换。

## 写接口 live 分级

当前写接口按 3 层管理：

- 默认 `safe live`：真实读接口 + 隔离 Redis 命名空间下的 `queue_init` / `queue_add` / `queue_clear`
- 低风险 `dangerous live`：`update_product_prices` 同值写回、`create_report` 创建报表任务
- 高风险人工确认后再跑：发货、改单号、取消订单、退货推进、促销写入、商品写入、仓库调拨、聊天发送、`start_chat`

查看完整分级清单：

```bash
python3 scripts/live_smoke.py --list-write-tiers
```

## 主要能力

- 商品查询、商品详情、评分、图片、证书信息
- 单商品改价、批量改价、库存查询、仓库库存查询
- FBO/FBS 订单读取、未履约订单读取、发货和物流信息
- 财务流水、现金流、利润损失报表、罚金预警、退货取消归因报表、报表创建与报表查询
- 分析报表、仓库库存、库存覆盖和低库存商品
- 促销、候选商品、自动活动、促销自动巡检、自动退出系统自动加入的活动商品
- 退货列表、退货状态变更、退货创建
- 类目树、类目属性、属性字典值、商品创建辅助流程
- 聊天、品牌资质
- Redis 驱动的 Python 队列与自动化 worker

## Python 服务布局

| 领域 | 代表文件 | 典型能力 |
|------|----------|----------|
| 价格 | `src/ozon_seller_cli/services/prices.py` | 查询价格、改价、批量改价 |
| 库存 | `src/ozon_seller_cli/services/inventory.py` | 查询库存、批量查库存、FBS 仓库库存 |
| 商品 | `src/ozon_seller_cli/services/products.py` | 商品列表、商品详情、图片、评分、证书、商品写接口 |
| 商品创建 | `src/ozon_seller_cli/services/product_workflow.py` | 卖家类目、必填属性、属性字典、模板、校验、创建 |
| 订单与履约 | `src/ozon_seller_cli/services/orders.py`, `src/ozon_seller_cli/services/posting.py` | 订单读取、未履约 fallback、发货和状态流转 |
| 标签与物流 | `src/ozon_seller_cli/services/shipping.py` | 标签、物流单号、物流轨迹 |
| 财务 | `src/ozon_seller_cli/services/finance.py` | 交易流水、现金流、报表 |
| 分析 | `src/ozon_seller_cli/services/analytics.py` | 销售趋势、订单统计、区域数据 |
| 仓库 | `src/ozon_seller_cli/services/warehouse.py` | 仓库、库存覆盖、调拨 |
| 促销 | `src/ozon_seller_cli/services/promotions.py` | 活动、候选商品、自动活动、自动退出 AUTO 加入的活动商品 |
| 退货 | `src/ozon_seller_cli/services/returns.py` | 退货读取与处理 |
| 分类 | `src/ozon_seller_cli/services/categories.py` | 类目树、类目属性、属性值、路径搜索 |
| 聊天 | `src/ozon_seller_cli/services/chat.py` | 聊天列表、消息、统计 |
| 品牌 | `src/ozon_seller_cli/services/brands.py` | 品牌资质 |
| 队列 | `src/ozon_seller_cli/services/queue_runtime.py` | 队列初始化、投递、清空、worker |

## 真实环境兼容说明

当前实现已显式保留这些兼容行为：

- `get_fbs_orders` 优先使用 `/v3/posting/fbs/list`
- `get_unfulfilled_fbo` / `get_unfulfilled_fbs` 在端点不可用或返回业务错误时自动回退
- `get_category_tree` 保留 `/v1/category/tree -> /v1/description-category/tree` fallback
- `get_category_tree_v2` 保留 `/v2/category/tree -> /v1/description-category/tree` fallback
- `get_category_attributes` / `get_category_attributes_v2` 会通过派生出的 `type_id` 回退到 `/v1/description-category/attribute`
- `get_attribute_values` 保留第 4 个参数非数字时把它当 `language` 的位置兼容行为
- `get_all_attribute_values` 保留“第一页失败直接失败，后续页失败返回已累积结果”的语义
- `search_categories` 在后代命中时会把祖先节点一并放进结果
- `get_product_pictures_info` 在账号类型不支持时返回结构化 `note`
- `get_certificate_types` / `get_certificate_type_list` 使用 GET
- 大分类树仅对 whole-tree 请求使用本地缓存
- `batch_query_stocks` 兼容单账号和账号数组两种调用形态
- queue task 结构保持 `{action, account, params, timestamp}`
- queue worker 继续处理 `get_prices`、`update_prices`、`get_stocks`、`get_orders`

## 安装

### PyPI / pip

发布后可直接安装：

```bash
python3 -m pip install ozon-seller-cli
```

源码安装和本地开发继续使用：

```bash
python3 -m pip install -e .
```

如果你只想让队列和分布式限流可用，至少安装：

```bash
python3 -m pip install redis
```

Redis 服务只对 `queue_*` 命令和 Redis 限流生效。普通 API 读取和写入命令不依赖额外的 shell 二进制。

### Linux / macOS

```bash
mkdir -p ~/.openclaw/skills
cp -r ozon-seller-skill ~/.openclaw/skills/
```

安装依赖：

```bash
# Ubuntu / Debian
sudo apt-get install python3 python3-pip redis-server

# macOS
brew install python redis
```

### Windows

推荐使用 WSL2。把 skill 放到 `~/.openclaw/skills/ozon-seller-skill` 后，安装：

```bash
sudo apt-get update
sudo apt-get install python3 python3-pip redis-server
sudo service redis-server start
python3 -m pip install -e .
```

## 配置

```bash
cp ~/.openclaw/skills/ozon-seller-skill/config.example.json ~/.openclaw/skills/ozon-seller-skill/config.json
chmod 600 ~/.openclaw/skills/ozon-seller-skill/config.json
```

配置示例：

```json
{
  "accounts": [
    {
      "name": "主账户",
      "client_id": "your-client-id",
      "api_key": "your-api-key",
      "default": true
    }
  ]
}
```

可配置多个账号：

```json
{
  "accounts": [
    {
      "name": "主账户",
      "client_id": "xxx",
      "api_key": "xxx",
      "default": true
    },
    {
      "name": "店铺B",
      "client_id": "yyy",
      "api_key": "yyy"
    }
  ]
}
```

## 运行时环境变量

- `OZON_CONFIG_FILE`：配置文件路径
- `OZON_RATE_LIMIT`：每分钟请求数，默认 `100`
- `OZON_TIMEOUT`：默认请求超时，默认 `30`
- `OZON_REQUEST_TIMEOUT`：单次请求超时覆盖
- `OZON_CATEGORY_TIMEOUT`：分类树专用超时，默认 `120`
- `OZON_CATEGORY_CACHE_DIR`：分类树本地缓存目录
- `OZON_CATEGORY_CACHE_TTL`：分类树缓存 TTL，默认 `21600`
- `OZON_LOG_LEVEL`：`info` 或 `error`
- `OZON_LIVE_DANGEROUS=true`：开启危险 live 测试
- `OZON_LIVE_DANGEROUS_REPORT_TYPE`：dangerous live 下 `create_report` 使用的报表类型
- `OZON_SDK_ADMIN_TOKEN`：授权后台 `/admin*` 路径的管理 token，启动授权服务时必须配置非默认值
- `OZON_AFDIAN_WEBHOOK_TOKEN`：爱发电 webhook 签名 token，启动授权服务时必须配置非默认值

## 测试

```bash
python3 -m unittest discover -s tests -p 'test_*.py'
python3 -m compileall src/ozon_seller_cli ozon_cli.py scripts/live_smoke.py scripts/orchestration_runner.py sdk_server
python3 ozon_cli.py --help
python3 scripts/live_smoke.py --list-write-tiers
python3 scripts/live_smoke.py
```

低风险 dangerous live 校验：

```bash
OZON_LIVE_DANGEROUS=true python3 scripts/live_smoke.py
OZON_LIVE_DANGEROUS=true OZON_LIVE_DANGEROUS_REPORT_TYPE=<report_type> python3 scripts/live_smoke.py
```

## 参考文档

优先看本仓库 `references/`：

- `references/products-api.md`
- `references/posting-api.md`
- `references/finance-api.md`
- `references/promotions-api.md`
- `references/categories-api.md`

再看官方文档：

- `https://docs.ozon.ru/api/seller/`

## 故障排查

### 队列不可用

- 先安装 Python Redis client：`python3 -m pip install redis`
- 确认 Redis 服务已启动
- `queue_*` 命令失败不会影响普通 API 命令；普通 API 仍可直接使用
