# Fbo Service Up

> 管理 Ozon FBO 发货代理服务 (4182, docker). 当用户说"起 FBO 服务"、"启动 4182"、"发货服务挂了"、"docker 重启 fbo"、"看 FBO 日志"、"FBO 服务健康检查"、"fbo shipment 服务"、"停 FBO 服务" 时触发。

- Skill: `yulianggan/fbo-service-up` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add yulianggan/fbo-service-up`
- Raw SKILL.md: https://api.skillmd.com/api/skills/yulianggan/fbo-service-up/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: yulianggan (https://skillmd.com/u/yulianggan)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/yulianggan/fbo-service-up

---


# FBO Shipment Service 运维 (4182, Docker)

`ozon_fbo_shipment_service` 是 Ozon 卖家 API 的 FBO 发货链路代理 (Draft/Supply Order/Cargoes/Label, 1:1 原子转发 + 2 个 flow 编排). 已容器化, 运行在 4182.

## 触发场景

- "起/停/重启 FBO 服务"
- "4182 服务健康检查"
- "看 FBO 容器日志"
- "FBO 服务挂了 / 没响应"
- "重建 FBO docker 镜像"

## 关键路径

- 源码 + Dockerfile + compose: `/Users/mac/Documents/ozns/github/ozon_fbo_shipment_service/`
- docker CLI **不在 PATH**, 路径是 `/Applications/Docker.app/Contents/Resources/bin/docker`
- 容器名: `ozon-fbo-shipment` (单容器, 没有网络依赖)
- 端口: 宿主 4182 → 容器 4182
- 卷: `./labels:/app/labels` (bind mount, 箱唛 PDF 落这里)

## 常用命令

**总是先 export PATH, 再 docker compose**:

```bash
export PATH=/Applications/Docker.app/Contents/Resources/bin:$PATH
cd /Users/mac/Documents/ozns/github/ozon_fbo_shipment_service
```

### 健康检查 (首选 — 先做这个再决定要不要重启)
```bash
docker ps --filter name=ozon-fbo-shipment --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
curl -s http://localhost:4182/health   # 4182 有 /health
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:4181/docs   # 4181 没 /health 用 /docs (200=活)
```
返回 `{"ok": true, "accounts": [...]}` = 正常.

注: rtk 代理会过滤 JSON, 用 `rtk proxy curl ...` 拿真实响应而不是 schema view.

### 启动 (首次 / 停机后)
```bash
docker compose up -d
```

### 重启 (代码没改, 只是卡了)
```bash
docker compose restart
```

### 代码改了 — 重建 + 重启
```bash
docker compose up -d --build
```

### 停服务
```bash
docker compose down
```

### 看日志
```bash
docker compose logs -f --tail 200             # 跟 (Ctrl-C 退出)
docker compose logs --since 10m               # 最近 10 分钟
docker compose logs | grep -i error | tail -20
```

### 进容器调试
```bash
docker exec -it ozon-fbo-shipment bash
# 容器内: python3 -c "from ozon_fbo_shipment_service.app import app; print(len(app.routes))"
```

## 冲突排查

### 端口 4182 被宿主机 uvicorn 占用
常见: 之前手启的 `python3 -m uvicorn ozon_fbo_shipment_service.app:app --port 4182` 还活着, compose 会起不来.
```bash
lsof -iTCP:4182 -sTCP:LISTEN
# 有 Python <PID> → 先 kill
kill <PID>
docker compose up -d
```

### 镜像构建慢 / 失败
```bash
docker compose build --no-cache 2>&1 | tail -40   # 看完整报错
docker system df                                    # 看磁盘占用
docker system prune -f                              # 清悬空镜像 (慎用)
```

### `.env` 凭据问题
- 路径: `/Users/mac/Documents/ozns/github/ozon_fbo_shipment_service/.env`
- `OZON_ACCOUNTS_JSON={"丝绸生活":{"client_id":"...","api_key":"..."},...}` 一行 JSON
- 改后: `docker compose restart` (env_file 只在启动时读)

### 服务返回 Ozon 403 `Api-key is deactivated`
`.env` 里该账号 api_key 失效. 去 seller.ozon.ru 重生 + 改 `.env` + `docker compose restart`.

### 限流碰太紧 (timeslot/info 持续 429)
`clients/rate_limits.py` `/v2/draft/timeslot/info` 已收到 `(1, 15)` (2026-04-25 收紧). 多 cluster fallback 阶段内部循环时仍会 429, 再收紧到 (1, 30) 但会拉慢 retry. 看 `feedback_429_masks_404_matrix.md` 决策点.

### docker cp 热修 (Docker Hub 挂时)
镜像 build 跑不了, 用 docker cp 推文件进运行容器临时跑:
```bash
docker cp clients/rate_limits.py ozon-fbo-shipment:/app/ozon_fbo_shipment_service/clients/
docker compose restart   # 重启 reload module
```
临时, 下次 `docker compose up -d --build` 会丢. 网络恢复后必须 build 把改动固化进镜像 (`feedback_docker_cp_hot_patch.md`).

## 已剥离的能力 (2026-04-24 定稿)

**`/internal/seller/*` (seller.ozon.ru BFF) 已去掉**: 原本依赖 `ozon-cookie-proxy:5100` + `curl_cffi` + 住宅代理. 用户定稿 *"接口可以去掉ozon-cookie-proxy,这个没什么用"*. 公共 API (multi-cluster 链路) 已打通, BFF 旁路不再需要.

如果以后要恢复, 3 个改动:
1. `app.py` 加 `from .routers import ... internal ...` + `app.include_router(internal.router)`
2. `clients/__init__.py` 加 `from .seller_ui_client import SellerUIClient, SellerUIError`
3. `routers/_deps.py` 加 `seller_ui_client()` 依赖
4. `requirements.txt` 加 `curl_cffi>=0.7.0`
5. `docker-compose.yml` 加 `extra_hosts: - "host.docker.internal:host-gateway"` + `OZON_COOKIE_PROXY_URL=http://host.docker.internal:5100`

## 端点速查 (27 条)

```bash
# 拿完整列表
python3 -c "import urllib.request,json; d=json.load(urllib.request.urlopen('http://localhost:4182/openapi.json')); print('\n'.join(sorted(d['paths'])))"
```

核心:
- `POST /cluster/list` — 集群列表
- `POST /warehouse/fbo/list` — 仓库搜索 (search 至少 4 字符)
- `POST /draft/crossdock/create` / `/draft/direct/create` / `/draft/multi-cluster/create`
- `POST /draft/create/info` (v2, body `{draft_id}`)
- `POST /draft/timeslot/info` (v2, supply_type=字符串)
- `POST /draft/supply/create` (v2) + `/draft/supply/create/status`
- `POST /supply-order/get` (v3) / `/supply-order/bundle`
- `POST /cargoes/create` + `/cargoes/create/info` (v2)
- `POST /cargoes-label/create` + `/cargoes-label/get`
- `GET  /cargoes-label/file/{guid}/save?supply_id=X` — 下载 PDF
- `POST /flow/create-supply` / `POST /flow/upload-cargoes` — 一把梭

交互文档: http://localhost:4182/docs

## 多账号调用

中文账号 header 必须 URL-encode:
```bash
ACCOUNT_URL=$(python3 -c "import urllib.parse; print(urllib.parse.quote('丝绸生活'))")
curl -X POST http://localhost:4182/cluster/list \
  -H "Content-Type: application/json" \
  -H "X-Ozon-Account: $ACCOUNT_URL" \
  -d '{"cluster_type":"CLUSTER_TYPE_OZON"}'
```

账号列表: `丝绸生活` (默认), `个人之路`, `个人之路_old` (见 `/health`).

## 相关 skill / memory

- `/fbo-plan` — 跑多集群发货 SOP 脚本
- `/fbo-retry` — 查/重试未发
- `/fbo-fill-boxes` — 填装箱 + 箱唛 PDF
- `/fbo-status` — 台账查询
- memory `reference_fbo_shipment_service.md` — 27 端点契约
- memory `feedback_fbo_service_docker_prereqs.md` — 容器化决策与 Dockerfile 细节
- memory `reference_services_ports.md` — 4180/4181/4182 全栈

