1
0
forked from erp-dev/erp
Files
erpnew/docs/api_v2_printing_order_sync_external_snapshot.md

7.9 KiB
Raw Blame History

按 external_order_id 同步外部订单快照 API

概述

用于前端按 external_order_id 手动触发一次“外部订单快照重拉并覆盖更新本地数据”的操作。

  • 端点: POST /api/v2/printing-orders/sync-external-snapshot/
  • 认证: 需要登录
  • 权限: 印染工厂用户

此接口适用于以下场景:

  • 外部订单已同步过一次,但后续外部系统又修改了订单数据
  • 人工发现本地 PrintingOrder / PrintingJob 与外部系统不一致
  • 需要按单号重新拉取该订单当前完整快照并覆盖到本地

同步行为说明

接口会按以下顺序执行:

  1. 根据 external_order_id 查找本地目标 PrintingOrder
  2. 先写入一条同步审计记录,保存同步前本地快照
  3. 做覆盖前检查
  4. 调用外部快照接口拉取该订单当前完整 records
  5. 复用现有外部同步映射逻辑,更新或创建本地 PrintingOrder / PrintingJob
  6. 删除“本地存在但外部快照已不存在”的旧 PrintingJob(仅在安全条件满足时)

覆盖前检查规则

1. 关联销售品时立即失败

如果目标 PrintingOrder 下任意 PrintingJob 已关联销售品,则接口会立即失败,不会请求外部快照接口。

失败时会:

  • 返回 409 Conflict
  • 写入失败审计记录
  • 在失败原因中明确给出涉及的 printing_job_idssales_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 是否创建了新订单,01
orders_updated int 是否更新了已有订单,01
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);
}

相关文件