{
  "name": "apizt sheets 离线审核教学（n8n 2.42.3）",
  "active": false,
  "nodes": [
    {
      "parameters": {},
      "id": "sheets-trigger",
      "name": "手动开始",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "language": "javaScript",
        "jsCode": "const bundles = [{\"source\": {\"qr_id\": \"QR-SHEET-A\", \"input_revision\": 1, \"input_text\": \"订单已发货三天，想查询配送进度。\"}, \"current\": {\"qr_id\": \"QR-SHEET-A\", \"input_revision\": 1, \"input_text\": \"订单已发货三天，想查询配送进度。\", \"ai_status\": \"pending\"}, \"model_output\": {\"qr_id\": \"QR-SHEET-A\", \"category\": \"配送\", \"summary\": \"查询已发货订单的配送进度。\"}}, {\"source\": {\"qr_id\": \"QR-SHEET-B\", \"input_revision\": 1, \"input_text\": \"重复付款了一次，希望退回多付的金额。\"}, \"current\": {\"qr_id\": \"QR-SHEET-B\", \"input_revision\": 2, \"input_text\": \"重复付款问题已经解决。\", \"ai_status\": \"pending\"}, \"model_output\": {\"qr_id\": \"QR-SHEET-B\", \"category\": \"退款\", \"summary\": \"请求退回重复付款金额。\"}}, {\"source\": {\"qr_id\": \"QR-SHEET-C\", \"input_revision\": 1, \"input_text\": \"发票抬头填错了，需要重新开具。\"}, \"current\": {\"qr_id\": \"QR-SHEET-C\", \"input_revision\": 1, \"input_text\": \"发票抬头填错了，需要重新开具。\", \"ai_status\": \"done\", \"processed_revision\": 1}, \"model_output\": {\"qr_id\": \"QR-SHEET-C\", \"category\": \"其他\", \"summary\": \"申请更正发票抬头。\"}}];\nreturn bundles.map((bundle) => ({json: bundle}));"
      },
      "id": "sheets-fixture",
      "name": "离线教学资料",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        280,
        0
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "language": "javaScript",
        "jsCode": "function buildSheetReview(bundles) {\n  if (!Array.isArray(bundles) || bundles.length > 100) throw new Error(\"Expected at most 100 row bundles\");\n  const seen = new Set();\n  return bundles.map(({ source, current, model_output }) => {\n    if (!source || typeof source.qr_id !== \"string\" || !source.qr_id.trim() || seen.has(source.qr_id)) throw new Error(\"Missing or duplicate qr_id\");\n    seen.add(source.qr_id);\n    if (!Number.isSafeInteger(source.input_revision) || source.input_revision < 1 || typeof source.input_text !== \"string\" || source.input_text.length > 4000) throw new Error(\"Invalid source row\");\n    const identity = { qr_id: source.qr_id, input_revision: source.input_revision };\n    if (!current || current.qr_id !== source.qr_id || current.input_revision !== source.input_revision || current.input_text !== source.input_text) return { ...identity, decision: \"stale\", write_allowed: false };\n    if (current.ai_status === \"done\" && current.processed_revision === source.input_revision) return { ...identity, decision: \"already_done\", write_allowed: false };\n    const result = typeof model_output === \"string\" ? JSON.parse(model_output) : model_output;\n    if (!result || Object.keys(result).sort().join(\",\") !== \"category,qr_id,summary\" || result.qr_id !== source.qr_id || ![\"配送\", \"退款\", \"其他\"].includes(result.category) || typeof result.summary !== \"string\" || !result.summary.trim() || Array.from(result.summary).length > 80 || /[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f]/.test(result.summary)) throw new Error(\"Model output has invalid ID, fields, category or summary\");\n    return { ...identity, ai_category: result.category, ai_summary: result.summary, decision: \"awaiting_human\", write_allowed: false };\n  });\n}\n\nreturn buildSheetReview($input.all().map((item) => item.json)).map((json, index) => ({json, pairedItem: {item: index}}));"
      },
      "id": "sheets-guard",
      "name": "生成审核计划",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        560,
        0
      ]
    }
  ],
  "connections": {
    "手动开始": {
      "main": [
        [
          {
            "node": "离线教学资料",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "离线教学资料": {
      "main": [
        [
          {
            "node": "生成审核计划",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {}
}
