# 送货单状态流转 API v1 说明 本文档说明送货单状态枚举、状态流转规则,以及如何通过 API 将送货单切换到“送货中/已送达/已取消”。 接口统一前缀为 `/api/v1/`。 ## 状态枚举 `ShipmentDeliveryStatus` | 值 | 枚举名 | 展示文案 | 说明 | | --- | --- | --- | --- | | `1` | `PENDING` | 待送货 | 送货单已创建,尚未开始送货 | | `2` | `IN_TRANSIT` | 送货中 | 已开始送货,系统会记录 `started_at` | | `3` | `DELIVERED` | 已送达 | 已完成送达,系统会记录 `delivered_at` | | `4` | `CANCELLED` | 已取消 | 送货单已取消,系统会记录 `cancelled_at` | ## 状态流转规则 当前允许的正常流转: ```text 待送货(1) -> 送货中(2) -> 已送达(3) ``` 取消流转: ```text 待送货(1) -> 已取消(4) 送货中(2) -> 已取消(4) 已送达(3) -> 已取消(4) ``` 注意:取消不通过通用 `/status/` 接口完成,而是通过独立 `/cancel/` 接口。 不允许的流转: | 当前状态 | 目标状态 | 结果 | | --- | --- | --- | | 待送货(1) | 已送达(3) | 不允许,必须先切到送货中 | | 送货中(2) | 待送货(1) | 不允许回退 | | 已送达(3) | 待送货(1)/送货中(2) | 不允许回退 | | 已取消(4) | 任意状态 | 不允许恢复 | | 任意状态 | 已取消(4) via `/status/` | 不允许,请使用 `/cancel/` | 如果传入的目标状态等于当前状态,后端会幂等返回当前送货单,不重复更新时间字段。 ## 修改为送货中 `POST /api/v1/shipment/deliveries/{id}/status/` 请求体: ```json { "status": 2 } ``` 成功响应:`200 ShipmentDelivery` 关键字段示例: ```json { "id": 1, "status": 2, "status_display": "送货中", "started_at": "2026-07-10T14:30:00+08:00", "delivered_at": null, "cancelled_at": null, "operator_id": 10, "operator_name": "操作人" } ``` 行为说明: - 仅允许当前状态为 `待送货(1)` 时切换到 `送货中(2)`。 - 成功后自动写入 `started_at`。 - 成功后自动写入当前操作人为 `operator`。 ## 修改为已送达 `POST /api/v1/shipment/deliveries/{id}/status/` 请求体: ```json { "status": 3 } ``` 成功响应:`200 ShipmentDelivery` 关键字段示例: ```json { "id": 1, "status": 3, "status_display": "已送达", "started_at": "2026-07-10T14:30:00+08:00", "delivered_at": "2026-07-10T16:30:00+08:00", "cancelled_at": null, "operator_id": 10, "operator_name": "操作人" } ``` 行为说明: - 仅允许当前状态为 `送货中(2)` 时切换到 `已送达(3)`。 - 成功后自动写入 `delivered_at`。 - 成功后自动写入当前操作人为 `operator`。 ## 取消送货单 `POST /api/v1/shipment/deliveries/{id}/cancel/` 请求体: ```json {} ``` 成功响应:`200 ShipmentDelivery` 关键字段示例: ```json { "id": 1, "status": 4, "status_display": "已取消", "cancelled_at": "2026-07-10T17:00:00+08:00", "cancelled_by_id": 1, "cancelled_by_name": "取消人", "operator_id": 10, "operator_name": "操作人" } ``` 权限要求:需要 `shipment.cancel_shipmentdelivery`。 行为说明: - 取消接口是独立接口,不使用 `/status/`。 - 成功后自动写入 `cancelled_at`。 - 成功后自动写入 `cancelled_by`。 - 成功后自动写入当前操作人为 `operator`。 - 对已经取消的送货单再次调用取消接口是幂等的。 ## 错误响应 目标状态非法:`400 Bad Request` ```json { "status": ["\"4\" 不是合法选项。"] } ``` 说明:`/status/` 接口只接受 `1/2/3`,取消请使用 `/cancel/`。 不允许的状态流转:`400 Bad Request` ```json { "detail": "不允许将送货单状态从 待送货 修改为 已送达" } ``` 送货单不存在或无权访问:`404 Not Found` ```json { "detail": "未找到送货单" } ``` 取消权限不足:`403 Forbidden` ```json { "detail": "没有权限取消送货单" } ``` ## 前端建议 - “开始送货”按钮:调用 `/status/`,传 `status=2`。 - “确认送达”按钮:调用 `/status/`,传 `status=3`。 - “取消送货单”按钮:调用 `/cancel/`。 - 不要通过 `/status/` 传 `status=4`。 - 不要尝试状态回退;当前业务不支持从 `送货中/已送达/已取消` 回退。