# 批量换货脚本功能总结

## 功能说明

新增 Python 运维脚本 `scripts/batch_exchange/batch_exchange.py`，用于读取旧资产标识、新资产标识两列 CSV，并逐行调用后台 `POST /api/admin/exchanges` 接口。

脚本复用现有换货业务事务，不直接操作数据库，不新增换货接口，也不改变现有 Handler、Service 或数据模型。

## 固定业务约束

- 换货流程固定为 `flow_type=direct`，不支持物流换货。
- 数据迁移固定为 `migrate_data=true`，不提供关闭选项。
- 每组资产调用一次创建换货接口；现有接口会在同一事务内创建并立即完成直接换货。
- 资产类型通过 `--asset-type` 按批次指定为 `iot_card` 或 `device`，新旧资产类型仍由接口校验一致性。
- 换货原因默认使用 `批量直接换货`，支持按批次覆盖原因和备注。

## 数据迁移范围

数据迁移完全复用现有换货服务，包含：

- 资产钱包余额。
- 套餐使用记录。
- 累计充值字段。
- 资产标签。

此外，直接换货原有流程仍会处理客户绑定切换以及新旧资产状态更新。

## 安全控制

- 默认仅预演，显式增加 `--execute` 后才会真实换货。
- 请求前校验 CSV 必须正好两列且新旧标识非空。
- 拦截同一行新旧资产相同、重复旧资产、重复新资产。
- 拦截同一资产在批次内同时作为旧资产和新资产，避免顺序执行改变后续行的资产状态。
- 接口返回成功时仍要求响应中的 `migration_completed=true`，否则结果记为失败并要求人工核对。
- 每行结果立即写入结果 CSV，中断后保留已处理记录。
- POST 请求不自动重试，避免接口已成功但客户端未收到响应时产生误操作。
- Token 和密码支持环境变量传入，不写入结果文件。

## 输出

结果 CSV 包含：

- CSV 原始行号、新旧资产标识。
- 成功或失败状态。
- HTTP 状态码、业务错误码和接口消息。
- 换货单 ID、换货单号。
- 迁移完成状态和迁移余额。

全部成功时退出码为 `0`；存在失败或认证失效导致中途停止时退出码为 `2`；参数、CSV 或登录错误时退出码为 `1`。
