forked from erp-dev/erp
7.9 KiB
7.9 KiB
按 external_order_id 同步外部订单快照 API
概述
用于前端按 external_order_id 手动触发一次“外部订单快照重拉并覆盖更新本地数据”的操作。
- 端点:
POST /api/v2/printing-orders/sync-external-snapshot/ - 认证: 需要登录
- 权限: 印染工厂用户
此接口适用于以下场景:
- 外部订单已同步过一次,但后续外部系统又修改了订单数据
- 人工发现本地
PrintingOrder/PrintingJob与外部系统不一致 - 需要按单号重新拉取该订单当前完整快照并覆盖到本地
同步行为说明
接口会按以下顺序执行:
- 根据
external_order_id查找本地目标PrintingOrder - 先写入一条同步审计记录,保存同步前本地快照
- 做覆盖前检查
- 调用外部快照接口拉取该订单当前完整
records - 复用现有外部同步映射逻辑,更新或创建本地
PrintingOrder/PrintingJob - 删除“本地存在但外部快照已不存在”的旧
PrintingJob(仅在安全条件满足时)
覆盖前检查规则
1. 关联销售品时立即失败
如果目标 PrintingOrder 下任意 PrintingJob 已关联销售品,则接口会立即失败,不会请求外部快照接口。
失败时会:
- 返回
409 Conflict - 写入失败审计记录
- 在失败原因中明确给出涉及的
printing_job_ids和sales_item_ids
2. 已有工序记录时默认失败
如果目标 PrintingOrder 下存在任意 PrintingJob 已有未撤销的工序记录,则默认失败。
失败时会:
- 返回
409 Conflict - 写入失败审计记录
- 提示前端可使用
allow_reset_stateflow=true重试
3. allow_reset_stateflow=true 的含义
当请求体传入:
{
"allow_reset_stateflow": true
}
接口会先撤销目标订单下所有 PrintingJob 的未撤销工序记录,然后再执行覆盖同步。
注意:
- 默认值是
false - 只有在“没有销售品关联”的前提下才会继续执行
- 该撤销操作只处理工序状态,不会保留当前流程执行进度
请求
请求体
{
"external_order_id": "KD20432358",
"allow_reset_stateflow": false
}
字段说明
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
external_order_id |
string | 是 | - | 外部订单号 |
allow_reset_stateflow |
boolean | 否 | false |
是否允许先撤销目标订单下已有工序再执行覆盖同步 |
请求示例
curl -X POST "http://localhost:8000/api/v2/printing-orders/sync-external-snapshot/" \
-H "Authorization: Token <your-token>" \
-H "Content-Type: application/json" \
-d '{
"external_order_id": "KD20432358",
"allow_reset_stateflow": false
}'
响应
成功响应 (200 OK)
{
"detail": "外部订单快照同步成功",
"audit_id": 12,
"external_order_id": "KD20432358",
"printing_order_id": 55,
"orders_created": 0,
"orders_updated": 1,
"jobs_created": 1,
"jobs_updated": 2,
"jobs_deleted": 1,
"reset_stateflow_job_count": 0
}
成功响应字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
detail |
string | 结果说明 |
audit_id |
int | 同步审计记录ID |
external_order_id |
string | 本次同步的外部订单号 |
printing_order_id |
int | 本地印染订单ID |
orders_created |
int | 是否创建了新订单,0 或 1 |
orders_updated |
int | 是否更新了已有订单,0 或 1 |
jobs_created |
int | 新创建的 PrintingJob 数量 |
jobs_updated |
int | 被更新的 PrintingJob 数量 |
jobs_deleted |
int | 被删除的旧 PrintingJob 数量 |
reset_stateflow_job_count |
int | 本次先撤销工序的 PrintingJob 数量 |
错误响应
1. 参数错误 (400 Bad Request)
external_order_id 为空
{
"external_order_id": ["external_order_id 不能为空"]
}
2. 未认证 (401 Unauthorized)
{
"detail": "Authentication credentials were not provided."
}
3. 无权限 (403 Forbidden)
当用户不是印染工厂用户时:
{
"detail": "您没有访问印染订单的权限"
}
4. 外部订单不存在 (404 Not Found)
{
"detail": "未找到该 external_order_id 对应的订单",
"audit_id": 13,
"external_order_id": "KD-NOT-FOUND"
}
说明:
- 即使外部未命中,也会创建失败审计记录
- 可通过
audit_id关联后端审计信息
5. 目标订单下已有销售品,禁止覆盖 (409 Conflict)
{
"detail": "目标订单存在已关联销售品的 printing_job,禁止覆盖同步。 printing_job_ids=[101], sales_item_ids=[201]",
"audit_id": 14,
"external_order_id": "KD20432358"
}
说明:
- 这是最优先的阻断条件
- 一旦命中,不会继续请求外部快照接口
6. 目标订单下已有工序记录,默认禁止覆盖 (409 Conflict)
{
"detail": "目标订单存在已执行工序的 printing_job,默认不允许覆盖同步。 如确认要覆盖,请传 allow_reset_stateflow=true。 printing_job_ids=[101]",
"audit_id": 15,
"external_order_id": "KD20432358"
}
说明:
- 当前端收到这类错误时,可以提示用户确认是否改用
allow_reset_stateflow=true再次发起请求
7. 旧明细存在业务引用,无法删除 (409 Conflict)
在外部快照同步阶段,如果发现本地“待删除旧明细”仍被其他业务引用,也会失败:
{
"detail": "以下待删除 printing_job 已关联销售单明细,禁止覆盖同步: [102]",
"audit_id": 16,
"external_order_id": "KD20432358"
}
审计说明
每次请求都会生成一条审计记录,无论成功还是失败。
当前审计记录包含:
external_order_id- 操作者
request.user - 操作员工
request.user.employee - 同步前本地订单与明细快照(JSON)
- 本次是否允许撤销工序
- 是否成功
- 失败原因
前端当前只会收到:
audit_id
如果后续需要展示详细审计记录,需要后端再补充查询接口。
前端使用建议
普通重同步
默认先使用:
{
"external_order_id": "KD20432358"
}
当后端提示存在工序记录时
可以提示用户:
- 覆盖同步会先撤销当前流程进度
- 是否继续
用户确认后,再次发送:
{
"external_order_id": "KD20432358",
"allow_reset_stateflow": true
}
不建议自动重试的情况
遇到以下错误时,前端不应自动重试:
- 已关联销售品
- 已关联销售单明细
- 外部订单不存在
这些都应提示人工处理。
前端调用示例
async function syncExternalSnapshot(externalOrderId, allowResetStateflow = false) {
const response = await fetch('/api/v2/printing-orders/sync-external-snapshot/', {
method: 'POST',
headers: {
Authorization: `Token ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
external_order_id: externalOrderId,
allow_reset_stateflow: allowResetStateflow,
}),
});
const data = await response.json();
if (!response.ok) {
const error = new Error(data.detail || '同步失败');
error.status = response.status;
error.auditId = data.audit_id;
error.externalOrderId = data.external_order_id;
throw error;
}
return data;
}
// 示例:先按默认模式重同步
try {
const result = await syncExternalSnapshot('KD20432358');
console.log('同步成功', result);
} catch (error) {
// 如果提示需要 allow_reset_stateflow=true,再由用户二次确认
console.error(error.message, error.auditId);
}
相关文件
- 路由: api_v2/urls.py
- 视图: api_v2/views/printing.py
- 核心同步逻辑: api_v1/tasks.py
- 审计模型: printing/models.py