# 批量换货脚本 该脚本读取两列 CSV,逐行调用 `POST /api/admin/exchanges` 执行换货。脚本固定使用: - `flow_type=direct`:只支持直接换货,接口创建换货单后会立即完成换货。 - `migrate_data=true`:必须执行全量数据迁移,不能通过参数关闭。 全量迁移由现有换货接口在同一事务中执行,包括钱包余额、套餐使用记录、累计充值字段和资产标签。脚本仅使用 Python 标准库,不需要安装依赖。 默认只预演,必须增加 `--execute` 才会真实换货。 ## CSV 格式 首行表头可选,第一列填写旧资产标识,第二列填写新资产标识: ```csv old_identifier,new_identifier 89860000000000000001,89860000000000000101 89860000000000000002,89860000000000000102 ``` 支持表头: - 英文:`old_identifier,new_identifier` 或 `old_asset_identifier,new_asset_identifier` - 中文:`旧资产标识,新资产标识` 或 `旧资产,新资产` 旧、新资产标识均使用换货接口已有的识别规则:物联网卡支持 ICCID、接入号、虚拟号;设备支持虚拟号、IMEI、SN。 脚本会在请求前拦截: - 列数不是两列,或任一列为空。 - 同一行新旧资产相同。 - 旧资产重复,或新资产重复。 - 同一资产在本批次中既作为旧资产又作为新资产。该情况会受到执行顺序影响,因此整批终止。 ## 预演 物联网卡示例: ```bash python3 scripts/batch_exchange/batch_exchange.py \ --base-url https://cmp-api.example.com \ --csv scripts/batch_exchange/exchanges.example.csv \ --asset-type iot_card ``` 设备批次将 `--asset-type` 改为 `device`。同一个 CSV 批次只能使用一种资产类型;新资产必须与旧资产类型一致,否则接口会拒绝该行。 预演只校验 CSV 并展示最多五条请求示例,不需要 Token,也不会调用接口。输出中会明确显示 `direct` 和 `migrate_data=true`。 ## 使用已有 Token 执行 推荐通过环境变量传递 Token,避免进入命令历史: ```bash JUNHONG_ADMIN_TOKEN='<后台Access Token>' \ python3 scripts/batch_exchange/batch_exchange.py \ --base-url https://cmp-api.example.com \ --csv /path/to/exchanges.csv \ --asset-type iot_card \ --execute ``` ## 使用账号自动登录后执行 未提供 Token 时,可以使用后台账号调用 `/api/admin/login` 自动获取 Token: ```bash JUNHONG_ADMIN_USERNAME='<后台账号>' \ JUNHONG_ADMIN_PASSWORD='<后台密码>' \ python3 scripts/batch_exchange/batch_exchange.py \ --base-url https://cmp-api.example.com \ --csv /path/to/exchanges.csv \ --asset-type iot_card \ --execute ``` 也可以使用 `--token`、`--username`、`--password` 参数。密码优先通过环境变量或交互输入,避免保存在 Shell 历史中。 ## 换货原因和备注 脚本默认使用 `批量直接换货` 作为换货原因。可按整批覆盖原因和备注: ```bash python3 scripts/batch_exchange/batch_exchange.py \ --base-url https://cmp-api.example.com \ --csv /path/to/exchanges.csv \ --asset-type iot_card \ --exchange-reason '故障卡批量换货' \ --remark '2026年7月批次' \ --execute ``` ## 结果文件 默认在输入 CSV 同目录生成: ```text 原文件名_换货结果_YYYYMMDD_HHMMSS.csv ``` 结果包含新旧资产标识、成功或失败状态、HTTP 状态码、业务错误码、错误消息、换货单 ID、换货单号、迁移完成状态和迁移余额。每处理一条都会立即刷新文件,中途中断时已完成的结果不会丢失。 为避免覆盖历史结果,`--output` 指定的文件已经存在时脚本会直接报错。 ## 其他参数和执行约束 - `--timeout`:单次请求超时秒数,默认 `30`。 - `--interval`:每次换货后的等待秒数,默认 `0.2`。 - `--execute`:显式开启真实换货。 每组资产单独调用一次接口,脚本不会自动重试 POST 请求。接口可能已经成功提交事务但客户端没有收到响应,自动重试可能造成误判;失败项应先查询换货单或资产状态,再决定是否单独重跑。 脚本不会回滚前面已经成功的行。执行前应先预演并确认完整映射;执行后根据结果 CSV 逐条核对失败项。