8
n8n 中文网amn8n.com

使用 BOAMP API 和 Google Sheets 监控并筛选法国采购招标

高级

这是一个Content Creation, Multimodal AI领域的自动化工作流,包含 55 个节点。主要使用 If, Code, Wait, Filter, HttpRequest 等节点。 使用 BOAMP API 和 Google Sheets 监控并筛选法国采购招标

前置要求
  • 可能需要目标 API 的认证凭证
  • Google Sheets API 凭证
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "meta": {
    "instanceId": "393ca9e36a1f81b0f643c72792946a5fe5e49eb4864181ba4032e5a408278263",
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "97a8d56f-29ec-4bd7-a9e9-52707fdaa646",
      "name": "HTTP 请求",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        304,
        -304
      ],
      "parameters": {
        "url": "https://boamp-datadila.opendatasoft.com/api/explore/v2.1/catalog/datasets/boamp/records",
        "options": {},
        "sendQuery": true,
        "sendHeaders": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "select",
              "value": "idweb,dateparution,objet,nomacheteur,datelimitereponse,type_marche,url_avis,code_departement"
            },
            {
              "name": "order_by",
              "value": "dateparution desc"
            },
            {
              "name": "where",
              "value": "=dateparution >= \"{{ $now.minus({days: $('Get config').item.json['Période']}).format('yyyy-MM-dd') }}\""
            },
            {
              "name": "limit",
              "value": "100"
            },
            {
              "name": "offset",
              "value": "={{ $json.Offset }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            },
            {
              "name": "User-Agent",
              "value": "n8n-workflow-veille"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "fe0416bf-3443-4d8d-a209-303a03dca6b3",
      "name": "处理响应",
      "type": "n8n-nodes-base.code",
      "position": [
        1728,
        -304
      ],
      "parameters": {
        "jsCode": "// Récupérer la réponse de l'API\nconst response = $input.first().json;\nconst results = response.results || [];\nconst totalCount = response.total_count || 0;\n\n// Récupérer l'offset actuel depuis le nœud \"Get Offset\"\nconst currentOffset = $('Get Offset').first().json.Offset || 0;\nconst limit = 100;\n\n// Calculer le prochain offset\nconst nextOffset = currentOffset + limit;\n\n// Vérifier s'il y a encore des données à récupérer\nconst hasMoreData = nextOffset < totalCount && results.length > 0;\n\nreturn {\n  records: results,\n  currentOffset: currentOffset,\n  nextOffset: nextOffset,\n  totalCount: totalCount,\n  hasMoreData: hasMoreData,\n  recordsInBatch: results.length\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "80744cd7-4e88-4a6e-8935-6798605fda88",
      "name": "检查继续循环",
      "type": "n8n-nodes-base.if",
      "position": [
        2352,
        -304
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "115caab4-3fbe-492f-aefc-b7b639832df8",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $('Process Response').item.json.hasMoreData }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ee8d1ff9-7df2-49f0-a8ce-120c6ce252ac",
      "name": "在表格中追加行",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1376,
        -320
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [
            {
              "id": "Parution",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Parution",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Objet",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Objet",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Acheteur",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Acheteur",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Date limite",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Date limite",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "_metadata",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "_metadata",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit#gid=0",
          "cachedResultName": "All"
        },
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit?gid=0#gid=0"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "wBRLUCktxqXE6DVJ",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "fadbf1a6-d255-47fc-9a9c-26ceaa88c69f",
      "name": "格式化结果",
      "type": "n8n-nodes-base.code",
      "position": [
        1168,
        -320
      ],
      "parameters": {
        "jsCode": "// Récupérer la réponse HTTP brute\nconst response = $input.first().json;\nconst results = response.results || [];\n\n// Fonction pour formater la date en dd/MM/yyyy\nfunction formatDate(dateString) {\n  if (!dateString) return null;\n  \n  try {\n    const date = new Date(dateString);\n    const day = date.getDate().toString().padStart(2, '0');\n    const month = (date.getMonth() + 1).toString().padStart(2, '0');\n    const year = date.getFullYear();\n    return `${day}/${month}/${year}`;\n  } catch (error) {\n    return dateString;\n  }\n}\n\n// Fonction pour générer l'URL du PDF\nfunction generatePdfUrl(urlAvis, dateParution) {\n  if (!urlAvis || !dateParution) return null;\n  \n  try {\n    // Extraire l'idweb de l'URL d'annonce\n    const match = urlAvis.match(/idweb:(%22)?([^%\"&]+)(%22)?/);\n    if (!match) return null;\n    \n    const idweb = match[2]; // 25-102635\n    \n    // Extraire le mois de la date de parution\n    const date = new Date(dateParution);\n    const month = (date.getMonth() + 1).toString().padStart(2, '0');\n    const year = date.getFullYear();\n    \n    // Construire l'URL PDF\n    return `https://www.boamp.fr/telechargements/FILES/PDF/${year}/${month}/${idweb}.pdf`;\n    \n  } catch (error) {\n    return null;\n  }\n}\n\n// Créer un item séparé pour chaque appel d'offre\nreturn results.map(item => ({\n  json: {\n    \"Parution\": item.dateparution,\n    \"Objet\": item.objet,\n    \"URL\": generatePdfUrl(item.url_avis, item.dateparution),\n    \"Acheteur\": item.nomacheteur,\n    \"Date limite\": formatDate(item.datelimitereponse)\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "69b51766-63c7-4e2d-8906-5f29ae0e92e4",
      "name": "获取偏移量",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -144,
        -304
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 862285542,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit#gid=862285542",
          "cachedResultName": "Offset"
        },
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit?gid=862285542#gid=862285542"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "wBRLUCktxqXE6DVJ",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "adf9bd65-b1af-453f-aa91-7b3a9c4f7fed",
      "name": "计划触发器",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -960,
        928
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 10 1 * *"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "1d7e0c25-b6a7-468d-99d4-3efbad6dac2e",
      "name": "获取全部",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -160,
        928
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit#gid=0",
          "cachedResultName": "All"
        },
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit?gid=862285542#gid=862285542"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "wBRLUCktxqXE6DVJ",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "fac28307-193c-4b9f-9e51-a3fd759084e3",
      "name": "查询匹配?",
      "type": "n8n-nodes-base.if",
      "position": [
        2144,
        960
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "081889ef-d60f-4ea5-8771-484603075cc2",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $('Get query').item.json.hasMatch }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "f68fe0b2-7ccd-4a29-a74d-a8c62936d02b",
      "name": "循环遍历招标",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        384,
        928
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "0074c6c1-6ec4-4036-9213-4e1caf6023cf",
      "name": "获取查询",
      "type": "n8n-nodes-base.code",
      "position": [
        1216,
        928
      ],
      "parameters": {
        "jsCode": "// Récupération des mots-clés depuis l'objet\nconst keywordData = $('Get keyword').first().json['Mot clé'];\nconst keywordsString = keywordData[0]['Mots clés'] || '';\n\n// Transformation de la chaîne en tableau et nettoyage\nconst termesToSearch = keywordsString\n  .split(',')\n  .map(term => term.trim()) // Supprime les espaces avant/après\n  .filter(term => term.length > 0); // Supprime les éléments vides\n\n// Récupération du texte d'entrée depuis le champ \"text\" de la node précédente\nconst inputText = $input.first().json.text || '';\n\n// Fonction pour normaliser le texte (supprime accents, met en minuscules)\nfunction normalizeText(text) {\n  return text\n    .toLowerCase()\n    .normalize('NFD')\n    .replace(/[\\u0300-\\u036f]/g, ''); // Supprime les accents\n}\n\n// Normalisation du texte d'entrée\nconst normalizedInput = normalizeText(inputText);\n\n// Recherche des correspondances\nconst foundTerms = [];\nlet hasMatch = false;\n\ntermesToSearch.forEach(term => {\n  const normalizedTerm = normalizeText(term);\n  \n  // Recherche du terme exact ou avec variations\n  if (normalizedInput.includes(normalizedTerm)) {\n    foundTerms.push(term);\n    hasMatch = true;\n  }\n});\n\n// Construction du résultat\nconst result = {\n  hasMatch: hasMatch,\n  foundTerms: foundTerms,\n  totalMatches: foundTerms.length,\n  searchedTerms: termesToSearch,\n  originalText: inputText.substring(0, 200) + (inputText.length > 200 ? '...' : '') // Extrait pour debug\n};\n\n// Ajout de détails sur les correspondances trouvées\nif (hasMatch) {\n  result.matchDetails = foundTerms.map(term => {\n    const normalizedTerm = normalizeText(term);\n    const index = normalizedInput.indexOf(normalizedTerm);\n    return {\n      term: term,\n      position: index,\n      context: inputText.substring(Math.max(0, index - 50), index + term.length + 50)\n    };\n  });\n}\n\nreturn [{ json: result }];"
      },
      "typeVersion": 2
    },
    {
      "id": "b6b5f98f-4894-4979-b0e9-8d968559fd93",
      "name": "目标招标",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2496,
        928
      ],
      "parameters": {
        "columns": {
          "value": {
            "URL": "={{ $('Loop Over offres').item.json.URL }}",
            "Match": "={{ $('Get query').item.json.foundTerms }}",
            "Objet": "={{ $('Loop Over offres').item.json.Objet }}",
            "Acheteur": "={{ $('Loop Over offres').item.json.Acheteur }}",
            "Parution": "={{ $('Loop Over offres').item.json.Parution }}",
            "Date ajout": "={{ DateTime.now().toFormat('dd/MM/yyyy') }}",
            "Date limite": "={{ $('Loop Over offres').item.json['Date limite'] }}"
          },
          "schema": [
            {
              "id": "Date ajout",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Date ajout",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Parution",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Parution",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Objet",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Objet",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Acheteur",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Acheteur",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Date limite",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Date limite",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Match",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Match",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 151804153,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit#gid=151804153",
          "cachedResultName": "Target"
        },
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit?gid=151804153#gid=151804153"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "wBRLUCktxqXE6DVJ",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "10e05eea-708b-4460-9482-fab208fc3632",
      "name": "等待",
      "type": "n8n-nodes-base.wait",
      "position": [
        2800,
        272
      ],
      "webhookId": "63dee3e9-695f-42dc-8689-6ef472d8f0d3",
      "parameters": {
        "amount": 10
      },
      "typeVersion": 1.1
    },
    {
      "id": "6bd78099-8371-43a0-82e7-84a3890afe3a",
      "name": "重置偏移量",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2880,
        -320
      ],
      "parameters": {
        "columns": {
          "value": {
            "Offset": "0",
            "row_number": 2
          },
          "schema": [
            {
              "id": "Offset",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Offset",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "row_number"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 862285542,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit#gid=862285542",
          "cachedResultName": "Offset"
        },
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit?gid=862285542#gid=862285542"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "wBRLUCktxqXE6DVJ",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "cbb7af10-393b-459b-99dc-6b806c5f4f92",
      "name": "HTTP 请求4",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        624,
        928
      ],
      "parameters": {
        "url": "={{ $json.URL }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "332d9b79-208b-42a5-84f0-cb64f2cef73e",
      "name": "从文件提取",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        832,
        928
      ],
      "parameters": {
        "options": {},
        "operation": "pdf"
      },
      "typeVersion": 1
    },
    {
      "id": "122a1cb2-4a35-4120-b693-aae378b8f841",
      "name": "等待1",
      "type": "n8n-nodes-base.wait",
      "position": [
        2896,
        1024
      ],
      "webhookId": "8dfc6b2f-d2e9-49db-952b-98e1ee306b4c",
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "6e69ac57-2209-4e5f-9a45-957c3b2df683",
      "name": "过滤器",
      "type": "n8n-nodes-base.filter",
      "position": [
        96,
        928
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "16a31f78-cd21-430f-ac05-9b4a4379d67d",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.Ok }}",
              "rightValue": "Ok"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "088b07af-f113-49b0-be85-f2d3f508e963",
      "name": "确定",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1776,
        928
      ],
      "parameters": {
        "columns": {
          "value": {
            "Ok": "Ok",
            "row_number": "={{ $('Loop Over offres').item.json.row_number }}"
          },
          "schema": [
            {
              "id": "Parution",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Parution",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Objet",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Objet",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Acheteur",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Acheteur",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Date limite",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Date limite",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Ok",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Ok",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "row_number"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit#gid=0",
          "cachedResultName": "All"
        },
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit?gid=0#gid=0"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "wBRLUCktxqXE6DVJ",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "08d027bd-ad2b-4f00-8fde-b1900aca6b7d",
      "name": "更新偏移量",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2000,
        -304
      ],
      "parameters": {
        "columns": {
          "value": {
            "Offset": "={{ $json.nextOffset }}",
            "row_number": 2
          },
          "schema": [
            {
              "id": "Offset",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Offset",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "row_number"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 862285542,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit#gid=862285542",
          "cachedResultName": "Offset"
        },
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": "https://docs.google.com/spreadsheets/d/1WCVR7NOEvsM5CnPKD6R1aLuNHKaQbIpAUvoK1wS3bgY/edit?gid=862285542#gid=862285542"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "wBRLUCktxqXE6DVJ",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "6c7e50b2-c920-405b-9382-638ef5a4c620",
      "name": "获取配置",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -624,
        -304
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 966659321,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1wapLLWjwzo7SfG_YEsUlFaPRs1MmjxPRhRc6BlwBUAY/edit#gid=966659321",
          "cachedResultName": "Config"
        },
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": "https://docs.google.com/spreadsheets/d/1wapLLWjwzo7SfG_YEsUlFaPRs1MmjxPRhRc6BlwBUAY/edit?gid=966659321#gid=966659321"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "wBRLUCktxqXE6DVJ",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "049a5165-d7bb-4c82-a631-37c448c6f8d2",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1040,
        560
      ],
      "parameters": {
        "color": 3,
        "width": 4320,
        "height": 1136,
        "content": "# 大阶段 2:根据关键词过滤招标"
      },
      "typeVersion": 1
    },
    {
      "id": "435daded-cb60-462a-bf29-5182beb34fa9",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1040,
        -592
      ],
      "parameters": {
        "color": 5,
        "width": 4320,
        "height": 1088,
        "content": "# 大阶段 1:使用配置获取所有招标"
      },
      "typeVersion": 1
    },
    {
      "id": "4e23fa14-6253-4456-90d8-0b9f99cf873e",
      "name": "便签2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -816,
        -448
      ],
      "parameters": {
        "width": 480,
        "height": 320,
        "content": "# 阶段 1.1:获取配置"
      },
      "typeVersion": 1
    },
    {
      "id": "3533cc22-225d-4e7e-b139-1db9c8266a91",
      "name": "便签3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -304,
        -448
      ],
      "parameters": {
        "width": 400,
        "height": 320,
        "content": "# 阶段 1.2:获取偏移量"
      },
      "typeVersion": 1
    },
    {
      "id": "c08dcdb6-dad2-41c6-a3b1-c787662f0dbd",
      "name": "便签4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        128,
        -448
      ],
      "parameters": {
        "width": 416,
        "height": 320,
        "content": "# 阶段 1.3:获取招标(API 调用)"
      },
      "typeVersion": 1
    },
    {
      "id": "265b9db5-c103-4fcf-943f-18f42908264f",
      "name": "便签5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        576,
        -448
      ],
      "parameters": {
        "width": 432,
        "height": 320,
        "content": "# 阶段 1.4:招标排序(市场类型过滤器)"
      },
      "typeVersion": 1
    },
    {
      "id": "3a1ebce4-785d-4558-a5bf-5a2aeb94886e",
      "name": "便签6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1024,
        -464
      ],
      "parameters": {
        "width": 624,
        "height": 336,
        "content": "# 阶段 1.5:将数据导入表格"
      },
      "typeVersion": 1
    },
    {
      "id": "53cdf26f-baa7-4c10-9281-614134d12058",
      "name": "便签7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1664,
        -464
      ],
      "parameters": {
        "width": 496,
        "height": 336,
        "content": "# 阶段 1.6:计算偏移量(分页)+ 更新偏移量"
      },
      "typeVersion": 1
    },
    {
      "id": "180969ad-bedf-4f5f-8b9e-68a82b8dd883",
      "name": "便签8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2176,
        -464
      ],
      "parameters": {
        "width": 496,
        "height": 336,
        "content": "# 阶段 1.7:偏移量完成了吗?(循环控制)"
      },
      "typeVersion": 1
    },
    {
      "id": "e5342f99-2545-47c4-a80b-26d979cd7828",
      "name": "便签9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2704,
        -464
      ],
      "parameters": {
        "width": 496,
        "height": 336,
        "content": "# 阶段 1.8:如果完成则重置偏移量"
      },
      "typeVersion": 1
    },
    {
      "id": "3d9d0e75-5699-406d-9926-54d190ca4672",
      "name": "计划触发器1",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -992,
        -304
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 1 * *"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "59fcdbd5-2a44-472b-a6f0-24edcbc4ad74",
      "name": "便签10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -256,
        816
      ],
      "parameters": {
        "width": 528,
        "height": 304,
        "content": "# 阶段 2.2:获取所有招标并过滤已处理的"
      },
      "typeVersion": 1
    },
    {
      "id": "62d988fa-e756-4d2c-a4d4-7776af88ed67",
      "name": "便利贴11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        336,
        784
      ],
      "parameters": {
        "width": 624,
        "height": 336,
        "content": "# 阶段 2.3:循环遍历招标并提取内容"
      },
      "typeVersion": 1
    },
    {
      "id": "93025880-cce5-49d9-b66b-06471a10665c",
      "name": "便利贴12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1024,
        816
      ],
      "parameters": {
        "width": 512,
        "height": 304,
        "content": "# 阶段 2.4:分析招标内容(关键词匹配)"
      },
      "typeVersion": 1
    },
    {
      "id": "cb5a8329-12a0-49f1-8a0e-04a55ce169f8",
      "name": "获取关键词",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -608,
        928
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 966659321,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1wapLLWjwzo7SfG_YEsUlFaPRs1MmjxPRhRc6BlwBUAY/edit#gid=966659321",
          "cachedResultName": "Config"
        },
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": "https://docs.google.com/spreadsheets/d/1wapLLWjwzo7SfG_YEsUlFaPRs1MmjxPRhRc6BlwBUAY/edit?gid=966659321#gid=966659321"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "wBRLUCktxqXE6DVJ",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "6d56dc34-92ae-4d30-96be-76123b9f1c46",
      "name": "便签13",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -816,
        816
      ],
      "parameters": {
        "width": 528,
        "height": 304,
        "content": "# 阶段 2.1:根据关键词过滤招标"
      },
      "typeVersion": 1
    },
    {
      "id": "7881bd24-3287-4689-8e3c-6436807d030a",
      "name": "便签15",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2112,
        816
      ],
      "parameters": {
        "width": 576,
        "height": 304,
        "content": "# 阶段 2.6:将匹配的招标保存到目标表格"
      },
      "typeVersion": 1
    },
    {
      "id": "f4302f99-e21f-4a95-836c-49e9b6cb03e7",
      "name": "招标排序",
      "type": "n8n-nodes-base.code",
      "position": [
        752,
        -304
      ],
      "parameters": {
        "jsCode": "// Node code pour filtrer les appels d'offres selon les types définis dans la config (n8n)\n// Récupération de la configuration depuis le node \"Get config\"\nconst configData = $node[\"Get config\"].json;\nconsole.log(\"Configuration:\", JSON.stringify(configData, null, 2));\n\n// Extraction des types de marché activés (checkbox à true) depuis la config\nlet marketTypes = [];\n\nif (Array.isArray(configData)) {\n    // Si c'est un tableau, on prend le premier élément\n    const config = configData[0];\n    if (config.Travaux === true) marketTypes.push(\"TRAVAUX\");\n    if (config.Services === true) marketTypes.push(\"SERVICES\");\n    if (config.Fournitures === true) marketTypes.push(\"FOURNITURES\");\n} else {\n    // Si c'est un objet unique\n    if (configData.Travaux === true) marketTypes.push(\"TRAVAUX\");\n    if (configData.Services === true) marketTypes.push(\"SERVICES\");\n    if (configData.Fournitures === true) marketTypes.push(\"FOURNITURES\");\n}\n\nconsole.log(\"Types de marché à filtrer:\", marketTypes);\n\n// Vérification qu'au moins un type est sélectionné\nif (marketTypes.length === 0) {\n    throw new Error(\"Aucun type de marché sélectionné dans la configuration\");\n}\n\n// Récupération des données depuis l'input\nconst inputData = $input.all();\n\n// Debug : afficher la structure des données\nconsole.log(\"inputData:\", JSON.stringify(inputData, null, 2));\n\n// Vérification que les données sont bien structurées\nif (!inputData || inputData.length === 0) {\n    throw new Error(\"Aucune donnée en entrée\");\n}\n\n// Récupération du premier item\nconst firstItem = inputData[0];\nconsole.log(\"firstItem:\", JSON.stringify(firstItem, null, 2));\n\nconst data = firstItem.json;\nconsole.log(\"data:\", JSON.stringify(data, null, 2));\n\n// La structure semble être directement un tableau avec total_count et results\nlet payload;\nif (Array.isArray(data)) {\n    payload = data[0];\n} else if (data && data.total_count !== undefined && data.results) {\n    payload = data;\n} else {\n    throw new Error(\"Structure de données non reconnue\");\n}\n\nconsole.log(\"payload:\", JSON.stringify(payload, null, 2));\n\nif (!payload.results || !Array.isArray(payload.results)) {\n    throw new Error(\"Structure de données invalide - results non trouvé\");\n}\n\n// Filtrage des résultats pour ne garder que les appels d'offres correspondant aux types de la config\nconst filteredResults = payload.results.filter(item => {\n    if (!item.type_marche || !Array.isArray(item.type_marche)) {\n        return false;\n    }\n    \n    // Vérifie si au moins un des types de marché de l'item correspond à ceux de la config\n    return item.type_marche.some(type => \n        marketTypes.includes(type)\n    );\n});\n\n// Construction du tableau de sortie avec la même structure\nconst output = [{\n    total_count: payload.total_count, // Garde le total_count original\n    filtered_count: filteredResults.length, // Ajoute le nombre filtré\n    market_types_used: marketTypes, // Ajoute les types utilisés pour le filtre\n    results: filteredResults\n}];\n\n// Log pour debug\nconsole.log(`Nombre d'appels d'offres filtrés: ${filteredResults.length} sur ${payload.total_count} total`);\nconsole.log(`Types de marché filtrés: ${marketTypes.join(', ')}`);\n\n// Retour du résultat\nreturn output;"
      },
      "typeVersion": 2
    },
    {
      "id": "f54308af-1c9e-477a-bca2-4b9ede90d33b",
      "name": "便签16",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -816,
        -80
      ],
      "parameters": {
        "width": 480,
        "height": 368,
        "content": "### 系统功能:"
      },
      "typeVersion": 1
    },
    {
      "id": "aca85321-0d84-4522-b21b-1500d709320d",
      "name": "便签17",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1680,
        -608
      ],
      "parameters": {
        "color": 4,
        "width": 592,
        "height": 1504,
        "content": "# 阶段 0:初始配置设置 您需要做的:"
      },
      "typeVersion": 1
    },
    {
      "id": "9ef13b1e-cfd7-4f58-90d7-3c6200b63fb5",
      "name": "便签18",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -304,
        -80
      ],
      "parameters": {
        "width": 400,
        "height": 304,
        "content": "### 系统功能:"
      },
      "typeVersion": 1
    },
    {
      "id": "09ff5fd8-ecf2-49c4-a0a6-192a924c30dd",
      "name": "便签19",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        128,
        -80
      ],
      "parameters": {
        "width": 416,
        "height": 448,
        "content": "### 系统功能:"
      },
      "typeVersion": 1
    },
    {
      "id": "4a43bad3-a932-4d7e-a66c-be7e10aa83d1",
      "name": "便签20",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        576,
        -80
      ],
      "parameters": {
        "width": 432,
        "height": 352,
        "content": "### 系统功能:"
      },
      "typeVersion": 1
    },
    {
      "id": "ce64a66d-9cbb-41ad-8226-cfd6c2646f1f",
      "name": "便签21",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1024,
        -80
      ],
      "parameters": {
        "width": 624,
        "height": 384,
        "content": "### 系统功能:"
      },
      "typeVersion": 1
    },
    {
      "id": "d721845b-d424-4671-8141-9db827f4b9cb",
      "name": "便签22",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1664,
        -80
      ],
      "parameters": {
        "width": 496,
        "height": 368,
        "content": "### 系统功能:"
      },
      "typeVersion": 1
    },
    {
      "id": "a2fea90a-160c-4605-a08a-5ea12b9d6c88",
      "name": "便签23",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2176,
        -80
      ],
      "parameters": {
        "width": 496,
        "height": 432,
        "content": "### 系统功能:"
      },
      "typeVersion": 1
    },
    {
      "id": "5d559d01-3175-49d5-a228-a087f3f082aa",
      "name": "便签24",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2704,
        -80
      ],
      "parameters": {
        "width": 496,
        "height": 272,
        "content": "### 系统功能:"
      },
      "typeVersion": 1
    },
    {
      "id": "ce07b9ef-230d-49e5-8c41-6bea96b194cd",
      "name": "便签 25",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -816,
        1136
      ],
      "parameters": {
        "width": 528,
        "height": 272,
        "content": "### 系统功能:"
      },
      "typeVersion": 1
    },
    {
      "id": "b041359a-cbee-462e-ba40-90c989cc527c",
      "name": "便签 26",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -256,
        1136
      ],
      "parameters": {
        "width": 528,
        "height": 304,
        "content": "### 系统功能:"
      },
      "typeVersion": 1
    },
    {
      "id": "b0dae467-4f37-4d99-a70d-76c53f0302df",
      "name": "便签27",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        336,
        1136
      ],
      "parameters": {
        "width": 624,
        "height": 304,
        "content": "### 系统功能:"
      },
      "typeVersion": 1
    },
    {
      "id": "7e4da99d-ab37-4c14-83f0-1bfdaebfca5e",
      "name": "便签28",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1024,
        1136
      ],
      "parameters": {
        "width": 512,
        "height": 448,
        "content": "### 系统功能:"
      },
      "typeVersion": 1
    },
    {
      "id": "63e86445-b28e-4695-9d64-cc23e786cfe9",
      "name": "便签14",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1568,
        816
      ],
      "parameters": {
        "width": 512,
        "height": 304,
        "content": "# 阶段 2.5:将招标标记为已处理"
      },
      "typeVersion": 1
    },
    {
      "id": "cf19c558-d1c0-4643-8bf4-c1dbcc3c8305",
      "name": "便签29",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1568,
        1136
      ],
      "parameters": {
        "width": 512,
        "height": 288,
        "content": "### 系统功能:"
      },
      "typeVersion": 1
    },
    {
      "id": "0d1d396d-9c99-4cfb-b70d-222673eb5cd2",
      "name": "便签30",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2112,
        1136
      ],
      "parameters": {
        "width": 576,
        "height": 432,
        "content": "### 系统功能:"
      },
      "typeVersion": 1
    }
  ],
  "pinData": {},
  "connections": {
    "Ok": {
      "main": [
        [
          {
            "node": "Query match ?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait": {
      "main": [
        [
          {
            "node": "Get Offset",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait1": {
      "main": [
        [
          {
            "node": "Loop Over offres",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter": {
      "main": [
        [
          {
            "node": "Loop Over offres",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get All": {
      "main": [
        [
          {
            "node": "Filter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get query": {
      "main": [
        [
          {
            "node": "Ok",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Offset": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get config": {
      "main": [
        [
          {
            "node": "Get Offset",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get keyword": {
      "main": [
        [
          {
            "node": "Get All",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "node": "Tenders sorting",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Target offre": {
      "main": [
        [
          {
            "node": "Loop Over offres",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request4": {
      "main": [
        [
          {
            "node": "Extract from File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Query match ?": {
      "main": [
        [
          {
            "node": "Target offre",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update offset": {
      "main": [
        [
          {
            "node": "Check Continue Loop",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Results": {
      "main": [
        [
          {
            "node": "Append row in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tenders sorting": {
      "main": [
        [
          {
            "node": "Format Results",
            "type": "main",
            "index": 0
          },
          {
            "node": "Process Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over offres": {
      "main": [
        [],
        [
          {
            "node": "HTTP Request4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Response": {
      "main": [
        [
          {
            "node": "Update offset",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get keyword",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract from File": {
      "main": [
        [
          {
            "node": "Get query",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger1": {
      "main": [
        [
          {
            "node": "Get config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Continue Loop": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Reset Offset",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

高级 - 内容创作, 多模态 AI

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
高级
节点数量55
分类2
节点类型10
难度说明

适合高级用户,包含 16+ 个节点的复杂工作流

作者
Growth AI

Growth AI

@growthai

n8n automation expert eliminating repetitive tasks for businesses. Specializing in native API integrations (HubSpot, Pipedrive, Notion, Gmail, Slack) with custom workflows tailored to your tech stack. GDPR-compliant European hosting with AI agents. From lead qualification to content generation - I architect battle-tested automations that run 24/7. Your manual routines disappear without pain.

外部链接
在 n8n.io 查看

分享此工作流