```sql ALTER TABLE order_items ADD COLUMN qc_started_at DATETIME NULL; ALTER TABLE order_items ADD COLUMN qc_completed_at DATETIME NULL; CREATE INDEX idx_order_items_qc_started ON order_items(qc_started_at); ``` --- ## 使用示例 ### 合伙人工作流程 ```javascript // 1. 开始采购 const response1 = await fetch('/partner/orders/{order_id}/start-purchasing', { method: 'POST', headers: { 'Authorization': 'Bearer {token}' } }); // 2. 标记待入库 const response2 = await fetch('/partner/orders/{order_id}/mark-awaiting-warehouse', { method: 'POST', headers: { 'Authorization': 'Bearer {token}', 'Content-Type': 'application/json' }, body: JSON.stringify({ tracking_no: 'SF1234567890' }) }); // 3. 扫码入库(现有API) const response3 = await fetch('/partner/warehouse/bind', { method: 'POST', headers: { 'Authorization': 'Bearer {token}' }, body: new URLSearchParams({ item_id: '{item_id}', barcode: 'BC123456' }) }); // 4. 上传QC照片(现有API) const response4 = await fetch('/partner/warehouse/qc/{item_id}/multiple', { method: 'POST', headers: { 'Authorization': 'Bearer {token}', 'Content-Type': 'application/json' }, body: JSON.stringify({ images: ['url1', 'url2', 'url3'] }) }); ``` ### 管理员监督 ```javascript // 1. 获取预警订单 const alerts = await fetch('/admin/partner-monitoring/alerts', { headers: { 'Authorization': 'Bearer {admin_token}' } }); // 2. 获取时效统计 const efficiency = await fetch('/admin/partner-monitoring/efficiency?days=30', { headers: { 'Authorization': 'Bearer {admin_token}' } }); // 3. 获取质量统计 const quality = await fetch('/admin/partner-monitoring/quality?partner_id={id}&days=30', { headers: { 'Authorization': 'Bearer {admin_token}' } }); ``` --- ## 包裹管理 API ### 合伙人端点 #### 1. 合伙人发货 **端点**: `POST /api/v1/partner/parcels/{parcel_id}/ship` **权限**: Partner **描述**: 合伙人标记包裹已发货,填写内部追踪单号(不显示给客户) **请求参数**: - `parcel_id` (path): 包裹ID **请求体**: ```json { "internal_tracking_no": "SF1234567890" } ``` **响应**: ```json { "success": true, "parcel_number": "PKG20260522001", "status": "shipped_waiting" } ``` **错误码**: - `400`: 包裹状态错误或参数无效 - `403`: 无权限操作此包裹 - `404`: 包裹不存在 **注意事项**: - `internal_tracking_no` 仅供内部使用,不会显示给客户 - 包裹状态必须为 `payment_pending`(已支付运费) - 发货后状态变为 `shipped_waiting`(等待管理员填写国际单号) --- ### 管理员端点 #### 1. 获取包裹详情 **端点**: `GET /api/v1/admin/parcels/{parcel_id}/details` **权限**: Admin **描述**: 获取包裹的完整详情,包括商品列表、地址快照、追踪信息等 **请求参数**: - `parcel_id` (path): 包裹ID **响应**: ```json { "parcel_id": "uuid", "parcel_number": "PKG20260522001", "user_id": "uuid", "user_email": "customer@example.com", "status": "awaiting_shipment", "parcel_status": "awaiting_shipment", "dimensions": { "weight_kg": 2.5, "length_cm": 30.0, "width_cm": 20.0, "height_cm": 15.0 }, "pricing": { "shipping_quote_usd": 45.00, "storage_fee_usd": 5.00, "total_usd": 50.00 }, "tracking": { "internal_tracking_no": "SF1234567890", "international_tracking_no": null }, "address_snapshot": { "recipient_name": "John Doe", "phone": "+1234567890", "address_line1": "123 Main St", "address_line2": "Apt 4B", "city": "New York", "state_province": "NY", "country": "USA", "postal_code": "10001" }, "items": [ { "item_id": "uuid", "product_name": "商品名称", "specification": "颜色: 红色, 尺寸: L", "quantity": 2, "qc_images": ["url1", "url2"] } ], "timestamps": { "created_at": "2026-05-20T10:00:00Z", "awaiting_shipment_at": "2026-05-21T14:30:00Z", "shipped_at": null }, "packing_note": "请在包裹上标注 'FRAGILE'" } ``` **错误码**: - `404`: 包裹不存在 --- #### 2. 管理员报价 **端点**: `POST /api/v1/admin/parcels/{parcel_id}/quote` **权限**: Admin **描述**: 管理员为包裹设置运费报价和仓储费,自动通知客户支付 **请求参数**: - `parcel_id` (path): 包裹ID **请求体**: ```json { "shipping_quote_usd": 45.00, "storage_fee_usd": 5.00 } ``` **响应**: ```json { "success": true, "parcel_number": "PKG20260522001", "status": "payment_pending", "shipping_quote_usd": 45.00, "storage_fee_usd": 5.00, "total_usd": 50.00 } ``` **错误码**: - `400`: 包裹状态错误(必须为 `awaiting_shipment`)或金额无效 - `404`: 包裹不存在 **业务逻辑**: - 包裹状态从 `awaiting_shipment` 变为 `payment_pending` - 系统自动发送邮件通知客户支付运费 - 仓储费按商品计算,报价时自动汇总 - 客户支付后状态变为 `paid`,等待发货 --- #### 3. 管理员发货 **端点**: `POST /api/v1/admin/parcels/{parcel_id}/ship` **权限**: Admin **描述**: 管理员标记包裹已发货,填写国际追踪单号(显示给客户) **请求参数**: - `parcel_id` (path): 包裹ID **请求体**: ```json { "international_tracking_no": "UPS1234567890", "carrier": "UPS" } ``` **响应**: ```json { "success": true, "parcel_number": "PKG20260522001", "status": "shipped", "international_tracking_no": "UPS1234567890", "shipped_at": "2026-05-22T16:00:00Z" } ``` **错误码**: - `400`: 包裹状态错误(必须为 `paid` 或 `shipped_waiting`)或参数无效 - `404`: 包裹不存在 **业务逻辑**: - 包裹状态变为 `shipped`(已发货) - 系统自动发送邮件通知客户,包含国际追踪单号 - `international_tracking_no` 会显示给客户用于追踪 - 记录发货时间 `shipped_at` --- ### 包裹状态流程 ``` packing_requested (打包中) ↓ 合伙人提交打包信息 awaiting_shipment (待发货-等报价) ↓ 管理员报价 payment_pending (待支付运费) ↓ 客户支付 paid (已支付-待发货) ↓ 合伙人发货(填写内部单号) shipped_waiting (已发货-等转单) ↓ 管理员填写国际单号 shipped (已发货) ↓ 客户确认 delivered (已签收) ``` --- ### 追踪单号说明 系统使用两种追踪单号: 1. **内部追踪单号** (`internal_tracking_no`) - 由合伙人填写 - 用于内部物流追踪 - **不显示给客户** - 例如:国内快递单号 2. **国际追踪单号** (`international_tracking_no`) - 由管理员填写 - 用于国际物流追踪 - **显示给客户** - 例如:UPS、DHL、FedEx 单号 --- ### 滞留费计算规则 **计算方式**: - 滞留费按**商品**计算,不是按包裹 - 每个商品入库后开始计时(`warehouse_date`) - 超过免费期后,每天收取固定费用 - 打包时自动汇总所有商品的滞留费 **费率**: - 免费期:入库后 30 天 - 超期费用:$0.50/商品/天 **示例**: ``` 包裹包含 3 个商品: - 商品A:入库 35 天,滞留费 = (35-30) × $0.50 = $2.50 - 商品B:入库 40 天,滞留费 = (40-30) × $0.50 = $5.00 - 商品C:入库 25 天,滞留费 = $0(未超期) 包裹总滞留费 = $2.50 + $5.00 + $0 = $7.50 ``` --- ### 使用示例 #### 合伙人发货流程 ```javascript // 1. 获取待发货包裹列表 const parcels = await fetch('/api/v1/partner/parcels/pending-shipment', { headers: { 'Authorization': 'Bearer {partner_token}' } }); // 2. 标记包裹已发货 const response = await fetch('/api/v1/partner/parcels/{parcel_id}/ship', { method: 'POST', headers: { 'Authorization': 'Bearer {partner_token}', 'Content-Type': 'application/json' }, body: JSON.stringify({ internal_tracking_no: 'SF1234567890' }) }); ``` #### 管理员报价和发货流程 ```javascript // 1. 获取包裹详情 const details = await fetch('/api/v1/admin/parcels/{parcel_id}/details', { headers: { 'Authorization': 'Bearer {admin_token}' } }); // 2. 设置运费报价 const quoteResponse = await fetch('/api/v1/admin/parcels/{parcel_id}/quote', { method: 'POST', headers: { 'Authorization': 'Bearer {admin_token}', 'Content-Type': 'application/json' }, body: JSON.stringify({ shipping_quote_usd: 45.00, storage_fee_usd: 5.00 }) }); // 3. 等待客户支付后,标记已发货 const shipResponse = await fetch('/api/v1/admin/parcels/{parcel_id}/ship', { method: 'POST', headers: { 'Authorization': 'Bearer {admin_token}', 'Content-Type': 'application/json' }, body: JSON.stringify({ international_tracking_no: 'UPS1234567890', carrier: 'UPS' }) }); ```