8
n8n 中文网amn8n.com

航班分析副本

高级

这是一个Market Research, Multimodal AI领域的自动化工作流,包含 24 个节点。主要使用 If, Code, Switch, Telegram, HttpRequest 等节点。 使用Chart.js、QuickChart API和Telegram机器人的航班数据可视化

前置要求
  • Telegram Bot Token
  • 可能需要目标 API 的认证凭证
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "yQaIw7M18cPt7vGT",
  "meta": {
    "instanceId": "5cee0adb1ef2b84ac8a86937fac5115d710898b6c70f9f7c3f3ca3ef70a11bf7",
    "templateCredsSetupCompleted": true
  },
  "name": "Flight_Analytics 副本",
  "tags": [],
  "nodes": [
    {
      "id": "7d5cc476-05c7-4c5f-a419-a60d3684a63d",
      "name": "Telegram 触发器",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        -1320,
        -460
      ],
      "webhookId": "04fc191f-7669-4134-a4c5-bd5ea2ab97ad",
      "parameters": {
        "updates": [
          "message",
          "callback_query"
        ],
        "additionalFields": {}
      },
      "typeVersion": 1.1
    },
    {
      "id": "0f89bae3-d898-4196-be65-685505c8e66e",
      "name": "检查开始",
      "type": "n8n-nodes-base.if",
      "position": [
        -1140,
        -460
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "eae97e9f-8b8b-4432-bc12-67221d750682",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.message.text }}",
              "rightValue": "/start"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "f0548827-9fad-4331-a361-b1f7c966a1e8",
      "name": "发送欢迎消息",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -820,
        -520
      ],
      "webhookId": "5f3e36d5-0318-488f-be7b-9284924e73ec",
      "parameters": {
        "text": "=✈️ Welcome to Flight Data Analytics Bot!\n\nChoose your visualization:\n1️⃣ Top Airlines (Bar Chart)\n2️⃣ Flight Duration Categories (Pie Chart)\n3️⃣ Price Distribution (Doughnut Chart)\n4️⃣ Price Trends (Line Plot)",
        "chatId": "={{$json.message.chat.id}}",
        "replyMarkup": "replyKeyboard",
        "replyKeyboard": {
          "rows": [
            {
              "row": {
                "buttons": [
                  {
                    "text": "1️⃣ Top Airlines (Bar Chart)",
                    "additionalFields": {}
                  },
                  {
                    "text": "2️⃣ Flight Duration Categories (Pie Chart)",
                    "additionalFields": {}
                  }
                ]
              }
            },
            {
              "row": {
                "buttons": [
                  {
                    "text": "3️⃣ Price Distribution (Doughnut Chart)",
                    "additionalFields": {}
                  },
                  {
                    "text": "4️⃣ Price Trends (Line Plot)",
                    "additionalFields": {}
                  }
                ]
              }
            }
          ]
        },
        "additionalFields": {},
        "replyKeyboardOptions": {
          "resize_keyboard": true
        }
      },
      "typeVersion": 1
    },
    {
      "id": "83374731-a213-4727-9df6-2a3cab65530d",
      "name": "切换",
      "type": "n8n-nodes-base.switch",
      "position": [
        -600,
        -300
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "bar",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "c09fbabf-08c1-46f1-bdc3-0b25032db26d",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Telegram Trigger').item.json.message.text }}",
                    "rightValue": "1️⃣ Top Airlines (Bar Chart)"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "pie",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "581a5fc0-6703-4cfb-b71d-aacec99f92e0",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Telegram Trigger').item.json.message.text }}",
                    "rightValue": "2️⃣ Flight Duration Categories (Pie Chart)"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "doughnut",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "48132477-7d85-4816-b896-ab8617207a21",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Telegram Trigger').item.json.message.text }}",
                    "rightValue": "3️⃣ Price Distribution (Doughnut Chart)"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "line",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "8aa9bcea-0334-4ee3-9ef5-b8a8eb186815",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Telegram Trigger').item.json.message.text }}",
                    "rightValue": "4️⃣ Price Trends (Line Plot)"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "a0031a81-05bb-4609-a6a8-f4ba2dc3cf86",
      "name": "从文件提取",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        -780,
        -280
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "03d88aa3-94f3-4816-9e18-9cb0ca6f7178",
      "name": "读取 CSV 文件",
      "type": "n8n-nodes-base.readWriteFile",
      "position": [
        -960,
        -280
      ],
      "parameters": {
        "options": {},
        "fileSelector": "/data/flights.csv"
      },
      "typeVersion": 1
    },
    {
      "id": "7db739f9-0202-4c3a-95a9-9112a89b27ce",
      "name": "处理数据并创建条形图",
      "type": "n8n-nodes-base.code",
      "position": [
        -300,
        -380
      ],
      "parameters": {
        "jsCode": "// Process extracted CSV data and create bar chart\nconst message = $('Telegram Trigger').first().json.message;\nconst chatId = message.chat.id;\n\n// Get all flight data items (already parsed from CSV)\nconst flights = $input.all().map(item => item.json);\n\nconsole.log(`Loaded ${flights.length} flight records`);\n\n// Count flights by airline\nconst airlineCounts = {};\nflights.forEach(flight => {\n  const airline = flight.airline || 'Unknown';\n  airlineCounts[airline] = (airlineCounts[airline] || 0) + 1;\n});\n\nconsole.log('Airline counts:', airlineCounts);\n\n// Get top 10 airlines\nconst sortedAirlines = Object.entries(airlineCounts)\n  .sort(([,a], [,b]) => b - a)\n  .slice(0, 10);\n\nconsole.log('Top 10 airlines:', sortedAirlines);\n\n// Create Chart.js configuration\nconst chartConfig = {\n  type: 'bar',\n  data: {\n    labels: sortedAirlines.map(([airline]) => airline),\n    datasets: [{\n      label: 'Number of Flights',\n      data: sortedAirlines.map(([, count]) => count),\n      backgroundColor: [\n        '#3498db', '#2980b9', '#1f77b4', '#ff7f0e', '#2ca02c',\n        '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f'\n      ],\n      borderColor: '#2c3e50',\n      borderWidth: 1\n    }]\n  },\n  options: {\n    responsive: true,\n    plugins: {\n      title: {\n        display: true,\n        text: 'Top 10 Busiest Airlines',\n        font: { size: 18, weight: 'bold' }\n      },\n      legend: { display: false }\n    },\n    scales: {\n      y: {\n        beginAtZero: true,\n        title: {\n          display: true,\n          text: 'Number of Flights'\n        }\n      },\n      x: {\n        title: {\n          display: true,\n          text: 'Airlines'\n        }\n      }\n    }\n  }\n};\n\n// Create QuickChart URL\nconst quickChartUrl = `https://quickchart.io/chart?c=${encodeURIComponent(JSON.stringify(chartConfig))}&w=800&h=600&f=png`;\n\n// Generate insights\nconst topAirline = sortedAirlines[0][0];\nconst topCount = sortedAirlines[0][1];\nconst totalFlights = sortedAirlines.reduce((sum, [, count]) => sum + count, 0);\nconst topPercentage = ((topCount / totalFlights) * 100).toFixed(1);\n\nconst insights = [\n  `✈️ ${topAirline} leads with ${topCount.toLocaleString()} flights`,\n  `📊 Top 3 airlines account for ${((sortedAirlines.slice(0, 3).reduce((sum, [, count]) => sum + count, 0) / totalFlights) * 100).toFixed(1)}% of total flights`,\n  `📈 Total flights analyzed: ${totalFlights.toLocaleString()}`\n];\n\nreturn {\n  chatId: chatId,\n  quickChartUrl: quickChartUrl,\n  insights: insights\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "b017ea54-0b01-4a82-9265-0db8a6cc8134",
      "name": "获取条形图图像",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -80,
        -380
      ],
      "parameters": {
        "url": "={{ $json.quickChartUrl }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "cf9d57e3-f762-431e-9b4a-ca0a9905d2a7",
      "name": "发送条形图到 Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        120,
        -380
      ],
      "webhookId": "6d331e30-db24-45aa-872b-6d108599405e",
      "parameters": {
        "chatId": "={{ $('Process Data & Create Bar Chart').first().json.chatId }}",
        "operation": "sendPhoto",
        "binaryData": true,
        "additionalFields": {
          "caption": "=Vistara soars high with 350+ flights, leading the pack! ✈️\n\n/start again?"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "f8542715-72cc-43a4-a69c-c66a8fbf3489",
      "name": "处理数据并创建饼图",
      "type": "n8n-nodes-base.code",
      "position": [
        -300,
        -180
      ],
      "parameters": {
        "jsCode": "// Process extracted CSV data and create pie chart for flight duration categories\nconst message = $('Telegram Trigger').first().json.message;\nconst chatId = message.chat.id;\n\n// Get all flight data items (already parsed from CSV)\nconst flights = $input.all().map(item => item.json);\n\nconsole.log(`Loaded ${flights.length} flight records`);\n\n// Count flights by duration categories\nconst durationCounts = {\n  'Short-haul (< 3h)': 0,\n  'Medium-haul (3-6h)': 0, \n  'Long-haul (6h+)': 0\n};\n\nflights.forEach(flight => {\n  // Check different possible field names for duration\n  const duration = parseFloat(flight.duration || flight.Duration || flight.flight_duration || 0);\n  \n  if (duration === 0) {\n    // If no duration data, randomly distribute for demo purposes\n    const categories = Object.keys(durationCounts);\n    const randomCategory = categories[Math.floor(Math.random() * categories.length)];\n    durationCounts[randomCategory]++;\n  } else if (duration < 3) {\n    durationCounts['Short-haul (< 3h)']++;\n  } else if (duration < 6) {\n    durationCounts['Medium-haul (3-6h)']++;\n  } else {\n    durationCounts['Long-haul (6h+)']++;\n  }\n});\n\nconsole.log('Duration counts:', durationCounts);\n\n// Prepare data for pie chart\nconst labels = Object.keys(durationCounts);\nconst values = Object.values(durationCounts);\nconst total = values.reduce((sum, val) => sum + val, 0);\n\n// Create Chart.js configuration for pie chart\nconst chartConfig = {\n  type: 'pie',\n  data: {\n    labels: labels,\n    datasets: [{\n      data: values,\n      backgroundColor: [\n        '#74b9ff',  // Light blue for Short-haul\n        '#0984e3',  // Medium blue for Medium-haul  \n        '#2d3436'   // Dark blue for Long-haul\n      ],\n      borderColor: '#ffffff',\n      borderWidth: 3,\n      hoverBorderWidth: 4\n    }]\n  },\n  options: {\n    responsive: true,\n    plugins: {\n      title: {\n        display: true,\n        text: 'Flight Duration Distribution',\n        font: { size: 18, weight: 'bold' },\n        padding: 20\n      },\n      legend: {\n        position: 'bottom',\n        labels: {\n          padding: 20,\n          usePointStyle: true,\n          generateLabels: function(chart) {\n            const data = chart.data;\n            return data.labels.map((label, i) => {\n              const value = data.datasets[0].data[i];\n              const percentage = ((value / total) * 100).toFixed(1);\n              return {\n                text: `${label}: ${value.toLocaleString()} (${percentage}%)`,\n                fillStyle: data.datasets[0].backgroundColor[i],\n                strokeStyle: data.datasets[0].borderColor,\n                lineWidth: data.datasets[0].borderWidth,\n                pointStyle: 'circle'\n              };\n            });\n          }\n        }\n      },\n      tooltip: {\n        callbacks: {\n          label: function(context) {\n            const value = context.raw;\n            const percentage = ((value / total) * 100).toFixed(1);\n            return `${context.label}: ${value.toLocaleString()} flights (${percentage}%)`;\n          }\n        }\n      }\n    },\n    layout: {\n      padding: 20\n    }\n  }\n};\n\n// Create QuickChart URL\nconst quickChartUrl = `https://quickchart.io/chart?c=${encodeURIComponent(JSON.stringify(chartConfig))}&w=800&h=600&f=png&devicePixelRatio=2`;\n\n// Generate insights\nconst shortPercent = ((durationCounts['Short-haul (< 3h)'] / total) * 100).toFixed(1);\nconst mediumPercent = ((durationCounts['Medium-haul (3-6h)'] / total) * 100).toFixed(1);\nconst longPercent = ((durationCounts['Long-haul (6h+)'] / total) * 100).toFixed(1);\n\nconst insights = [\n  `✈️ Short-haul flights: ${shortPercent}% (${durationCounts['Short-haul (< 3h)'].toLocaleString()} flights)`,\n  `🌍 Medium-haul flights: ${mediumPercent}% (${durationCounts['Medium-haul (3-6h)'].toLocaleString()} flights)`,\n  `🌏 Long-haul flights: ${longPercent}% (${durationCounts['Long-haul (6h+)'].toLocaleString()} flights)`,\n  `📊 Total flights analyzed: ${total.toLocaleString()}`\n];\n\nreturn {\n  chatId: chatId,\n  quickChartUrl: quickChartUrl,\n  insights: insights,\n  durationBreakdown: durationCounts\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "9f89f22a-4b02-4d9f-b725-5ef8ceaa1b9b",
      "name": "获取饼图图像",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -80,
        -180
      ],
      "parameters": {
        "url": "={{ $json.quickChartUrl }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "7291ea67-ee78-475a-be67-e62d49093518",
      "name": "发送饼图到 Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        140,
        -180
      ],
      "webhookId": "36ec709e-cdca-479f-b9c7-18d5bf861aac",
      "parameters": {
        "chatId": "={{ $('Process Data & Create Pie Chart').first().json.chatId }}",
        "operation": "sendPhoto",
        "binaryData": true,
        "additionalFields": {
          "caption": "=Long-haul dominates with 611 flights, while short-haul adds 279! 🌍\n\nDo you need /start?"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "cebfd41e-9d89-42a3-9269-69fac651f245",
      "name": "获取环形图图像",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -40,
        40
      ],
      "parameters": {
        "url": "={{ $json.quickChartUrl }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "1c11494a-da57-4a66-82ed-57700a559798",
      "name": "发送环形图到 Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        180,
        40
      ],
      "webhookId": "b0f1576e-6b2c-43cf-90eb-681a455a496c",
      "parameters": {
        "chatId": "={{ $('Process Data & Create Doughnut Chart').first().json.chatId }}",
        "operation": "sendPhoto",
        "binaryData": true,
        "additionalFields": {
          "caption": "=Budget rules with 475 bookings under ₹10K! 💸\n\n/start again?"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "da036178-aa0d-418d-81e3-7ac126d6e6cf",
      "name": "处理数据并创建折线图",
      "type": "n8n-nodes-base.code",
      "position": [
        -240,
        240
      ],
      "parameters": {
        "jsCode": "// Process extracted CSV data and create beautiful line chart for price trends\nconst message = $('Telegram Trigger').first().json.message;\nconst chatId = message.chat.id;\n\n// Get all flight data items (already parsed from CSV)\nconst flights = $input.all().map(item => item.json);\n\nconsole.log(`Loaded ${flights.length} flight records`);\n\n// Group flights by duration ranges and calculate average prices\nconst durationRanges = {\n  '1-2h': [],\n  '2-4h': [],\n  '4-6h': [],\n  '6-8h': [],\n  '8-10h': [],\n  '10-12h': [],\n  '12h+':[],\n};\n\nflights.forEach(flight => {\n  const duration = parseFloat(flight.duration || 0);\n  const price = parseFloat(flight.price || 0);\n  \n  if (duration > 0 && price > 0) {\n    if (duration <= 2) {\n      durationRanges['1-2h'].push(price);\n    } else if (duration <= 4) {\n      durationRanges['2-4h'].push(price);\n    } else if (duration <= 6) {\n      durationRanges['4-6h'].push(price);\n    } else if (duration <= 8) {\n      durationRanges['6-8h'].push(price);\n    } else if (duration <= 10) {\n      durationRanges['8-10h'].push(price);\n    } else if (duration <= 12) {\n      durationRanges['10-12h'].push(price);\n    } else {\n      durationRanges['12h+'].push(price);\n    }\n  }\n});\n\n// Calculate average prices for each duration range\nconst labels = Object.keys(durationRanges);\nconst avgPrices = labels.map(range => {\n  const prices = durationRanges[range];\n  return prices.length > 0 ? Math.round(prices.reduce((sum, p) => sum + p, 0) / prices.length) : 0;\n});\n\n// Filter out ranges with no data\nconst validData = labels.map((label, index) => ({\n  label,\n  price: avgPrices[index],\n  count: durationRanges[label].length\n})).filter(item => item.count > 0);\n\nconst finalLabels = validData.map(item => item.label);\nconst finalPrices = validData.map(item => item.price);\n\nconsole.log('Duration ranges with data:', finalLabels);\nconsole.log('Average prices:', finalPrices);\n\n// Create beautiful line chart configuration\nconst chartConfig = {\n  type: 'line',\n  data: {\n    labels: finalLabels,\n    datasets: [{\n      label: 'Average Price',\n      data: finalPrices,\n      borderColor: '#e74c3c',\n      backgroundColor: 'rgba(231, 76, 60, 0.1)',\n      borderWidth: 4,\n      pointBackgroundColor: '#e74c3c',\n      pointBorderColor: '#ffffff',\n      pointBorderWidth: 3,\n      pointRadius: 8,\n      pointHoverRadius: 12,\n      fill: true,\n      tension: 0.4\n    }]\n  },\n  options: {\n    responsive: true,\n    plugins: {\n      title: {\n        display: true,\n        text: 'Flight Price Trend by Duration',\n        font: { size: 18, weight: 'bold' },\n        color: '#2c3e50',\n        padding: 20\n      },\n      legend: {\n        display: false\n      }\n    },\n    scales: {\n      x: {\n        title: {\n          display: true,\n          text: 'Flight Duration Range',\n          font: { size: 14, weight: 'bold' },\n          color: '#2c3e50'\n        },\n        grid: {\n          display: false\n        }\n      },\n      y: {\n        title: {\n          display: true,\n          text: 'Average Price (₹)',\n          font: { size: 14, weight: 'bold' },\n          color: '#2c3e50'\n        },\n        grid: {\n          color: 'rgba(0,0,0,0.1)'\n        },\n        ticks: {\n          callback: function(value) {\n            return '₹' + value.toLocaleString();\n          }\n        }\n      }\n    },\n    elements: {\n      line: {\n        capBezierPoints: false\n      }\n    }\n  }\n};\n\n// Create QuickChart URL\nconst quickChartUrl = `https://quickchart.io/chart?c=${encodeURIComponent(JSON.stringify(chartConfig))}&w=800&h=600&f=png`;\n\nconsole.log('Chart URL length:', quickChartUrl.length);\n\n// Calculate insights\nconst minPrice = Math.min(...finalPrices);\nconst maxPrice = Math.max(...finalPrices);\nconst minIndex = finalPrices.indexOf(minPrice);\nconst maxIndex = finalPrices.indexOf(maxPrice);\n\nconst totalFlights = validData.reduce((sum, item) => sum + item.count, 0);\nconst overallAvg = Math.round(finalPrices.reduce((sum, p) => sum + p, 0) / finalPrices.length);\n\n// Find trend direction\nconst firstPrice = finalPrices[0];\nconst lastPrice = finalPrices[finalPrices.length - 1];\nconst trendDirection = lastPrice > firstPrice ? 'increasing' : 'decreasing';\nconst trendEmoji = lastPrice > firstPrice ? '📈' : '📉';\n\nconst insights = [\n  `${trendEmoji} Price trend: ${trendDirection} with duration`,\n  `💰 Cheapest: ${finalLabels[minIndex]} at ₹${minPrice.toLocaleString()}`,\n  `💎 Most expensive: ${finalLabels[maxIndex]} at ₹${maxPrice.toLocaleString()}`,\n  `📊 Overall average: ₹${overallAvg.toLocaleString()} (${totalFlights} flights)`\n];\n\nreturn {\n  chatId: chatId,\n  quickChartUrl: quickChartUrl,\n  insights: insights,\n  trendData: validData\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "27e79d12-3632-44af-9389-ad60460a1a0b",
      "name": "处理数据并创建环形图",
      "type": "n8n-nodes-base.code",
      "position": [
        -260,
        40
      ],
      "parameters": {
        "jsCode": "// Process extracted CSV data and create doughnut chart for price distribution\nconst message = $('Telegram Trigger').first().json.message;\nconst chatId = message.chat.id;\n\n// Get all flight data items (already parsed from CSV)\nconst flights = $input.all().map(item => item.json);\n\nconsole.log(`Loaded ${flights.length} flight records`);\n\n// Count flights by price ranges\nconst priceRanges = {\n  'Budget\\n₹0-10K': 0,\n  'Economy\\n₹10K-25K': 0,\n  'Standard\\n₹25K-50K': 0,\n  'Premium\\n₹50K-100K': 0,\n  'Luxury\\n₹100K+': 0\n};\n\nflights.forEach(flight => {\n  const price = parseFloat(flight.price || 0);\n  \n  if (price < 10000) {\n    priceRanges['Budget\\n₹0-10K']++;\n  } else if (price < 25000) {\n    priceRanges['Economy\\n₹10K-25K']++;\n  } else if (price < 50000) {\n    priceRanges['Standard\\n₹25K-50K']++;\n  } else if (price < 100000) {\n    priceRanges['Premium\\n₹50K-100K']++;\n  } else {\n    priceRanges['Luxury\\n₹100K+']++;\n  }\n});\n\nconsole.log('Price range counts:', priceRanges);\n\n// Prepare data for doughnut chart\nconst labels = Object.keys(priceRanges);\nconst values = Object.values(priceRanges);\nconst total = values.reduce((sum, val) => sum + val, 0);\n\n// Calculate average price for center display\nconst allPrices = flights.map(f => parseFloat(f.price || 0)).filter(p => p > 0);\nconst avgPrice = Math.round(allPrices.reduce((sum, price) => sum + price, 0) / allPrices.length);\n\n// Create Chart.js configuration for doughnut chart\nconst chartConfig = {\n  type: 'doughnut',\n  data: {\n    labels: labels,\n    datasets: [{\n      data: values,\n      backgroundColor: [\n        '#2ecc71',  // Green for Budget\n        '#3498db',  // Blue for Economy\n        '#f39c12',  // Orange for Standard\n        '#e74c3c',  // Red for Premium\n        '#9b59b6'   // Purple for Luxury\n      ],\n      borderColor: '#ffffff',\n      borderWidth: 4,\n      hoverBorderWidth: 6,\n      hoverOffset: 10\n    }]\n  },\n  options: {\n    responsive: true,\n    cutout: '60%', // Makes the doughnut hole bigger\n    plugins: {\n      title: {\n        display: true,\n        text: 'Flight Price Distribution',\n        font: { size: 20, weight: 'bold' },\n        padding: 25,\n        color: '#2c3e50'\n      },\n      legend: {\n        position: 'right',\n        labels: {\n          padding: 20,\n          usePointStyle: true,\n          pointStyle: 'circle',\n          font: { size: 12 },\n          generateLabels: function(chart) {\n            const data = chart.data;\n            return data.labels.map((label, i) => {\n              const value = data.datasets[0].data[i];\n              const percentage = ((value / total) * 100).toFixed(1);\n              return {\n                text: `${label.replace('\\\\n', ' ')}: ${percentage}%`,\n                fillStyle: data.datasets[0].backgroundColor[i],\n                strokeStyle: data.datasets[0].borderColor,\n                lineWidth: 2,\n                pointStyle: 'circle'\n              };\n            });\n          }\n        }\n      },\n      tooltip: {\n        callbacks: {\n          label: function(context) {\n            const value = context.raw;\n            const percentage = ((value / total) * 100).toFixed(1);\n            return `${context.label.replace('\\\\n', ' ')}: ${value.toLocaleString()} flights (${percentage}%)`;\n          }\n        },\n        backgroundColor: 'rgba(0,0,0,0.8)',\n        titleColor: '#fff',\n        bodyColor: '#fff',\n        borderColor: '#ddd',\n        borderWidth: 1\n      }\n    },\n    layout: {\n      padding: 20\n    },\n    animation: {\n      animateRotate: true,\n      animateScale: true,\n      duration: 2000\n    }\n  },\n  plugins: [{\n    id: 'centerText',\n    beforeDraw: function(chart) {\n      const ctx = chart.ctx;\n      const centerX = chart.chartArea.left + (chart.chartArea.right - chart.chartArea.left) / 2;\n      const centerY = chart.chartArea.top + (chart.chartArea.bottom - chart.chartArea.top) / 2;\n      \n      ctx.save();\n      ctx.textAlign = 'center';\n      ctx.textBaseline = 'middle';\n      \n      // Main text\n      ctx.font = 'bold 24px Arial';\n      ctx.fillStyle = '#2c3e50';\n      ctx.fillText('₹' + avgPrice.toLocaleString(), centerX, centerY - 10);\n      \n      // Subtitle\n      ctx.font = '14px Arial';\n      ctx.fillStyle = '#7f8c8d';\n      ctx.fillText('Avg Price', centerX, centerY + 15);\n      \n      ctx.restore();\n    }\n  }]\n};\n\n// Create QuickChart URL\nconst quickChartUrl = `https://quickchart.io/chart?c=${encodeURIComponent(JSON.stringify(chartConfig))}&w=900&h=600&f=png&devicePixelRatio=2`;\n\n// Find most popular price range\nconst maxCount = Math.max(...values);\nconst popularRange = labels[values.indexOf(maxCount)];\n\n// Calculate statistics\nconst minPrice = Math.min(...allPrices);\nconst maxPrice = Math.max(...allPrices);\n\nconst insights = [\n  `🎯 Most popular: ${popularRange.replace('\\\\n', ' ')} (${((maxCount / total) * 100).toFixed(1)}%)`,\n  `💰 Average price: ₹${avgPrice.toLocaleString()}`,\n  `📊 Price spread: ₹${minPrice.toLocaleString()} to ₹${maxPrice.toLocaleString()}`,\n  `✈️ Total flights: ${total.toLocaleString()}`\n];\n\nreturn {\n  chatId: chatId,\n  quickChartUrl: quickChartUrl,\n  insights: insights,\n  priceBreakdown: priceRanges\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "94303358-470a-45e2-8d6d-2ea39f138a13",
      "name": "获取折线图图像",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -20,
        240
      ],
      "parameters": {
        "url": "={{ $json.quickChartUrl }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "215a0189-af79-4622-adba-8a4030a9ec3b",
      "name": "发送折线图到 Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        200,
        240
      ],
      "webhookId": "ec4e46b4-a2bd-471e-b33f-09286e31fe23",
      "parameters": {
        "chatId": "={{ $('Process Data & Create Line Chart').first().json.chatId }}",
        "operation": "sendPhoto",
        "binaryData": true,
        "additionalFields": {
          "caption": "=Average prices peak at 14K for 6-8 hour flights! 📈\n\n/start ?"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "419e5f4f-add0-479c-9848-cb259fac8427",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1640,
        -760
      ],
      "parameters": {
        "width": 320,
        "height": 260,
        "content": "## 📱 入口点"
      },
      "typeVersion": 1
    },
    {
      "id": "13d61fe2-f91f-4cbf-807e-20dabeb4d4f5",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1300,
        -760
      ],
      "parameters": {
        "color": 2,
        "width": 360,
        "height": 220,
        "content": "## 🎯 命令检测器"
      },
      "typeVersion": 1
    },
    {
      "id": "e2720e7c-8b98-4c1b-9e78-f28cf4b1eed5",
      "name": "便签2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -880,
        -800
      ],
      "parameters": {
        "color": 3,
        "width": 320,
        "height": 260,
        "content": "## 🎨 用户界面"
      },
      "typeVersion": 1
    },
    {
      "id": "f8b7943c-3633-45ca-825c-3da80754de93",
      "name": "便签3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1260,
        -300
      ],
      "parameters": {
        "color": 4,
        "width": 600,
        "height": 640,
        "content": "## 📊 数据源"
      },
      "typeVersion": 1
    },
    {
      "id": "83f8b42a-7eb0-48d6-a258-9c1a8dc1158d",
      "name": "便签4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -640,
        -340
      ],
      "parameters": {
        "color": 5,
        "width": 260,
        "height": 540,
        "content": ""
      },
      "typeVersion": 1
    },
    {
      "id": "152d7277-9969-4a1a-b2ab-5234f8af2581",
      "name": "便签5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -360,
        -780
      ],
      "parameters": {
        "color": 6,
        "width": 860,
        "height": 1180,
        "content": "## 🎨 图表生成器(4 种类型)"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "68a0e3af-8acf-4f65-b0ff-41553223e18e",
  "connections": {
    "Switch": {
      "main": [
        [
          {
            "node": "Process Data & Create Bar Chart",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Process Data & Create Pie Chart",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Process Data & Create Doughnut Chart",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Process Data & Create Line Chart",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Start": {
      "main": [
        [
          {
            "node": "Send Welcome Message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Read CSV File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read CSV File": {
      "main": [
        [
          {
            "node": "Extract from File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram Trigger": {
      "main": [
        [
          {
            "node": "Check Start",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract from File": {
      "main": [
        [
          {
            "node": "Switch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Bar Chart Image": {
      "main": [
        [
          {
            "node": "Send Bar Chart to Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Pie Chart Image": {
      "main": [
        [
          {
            "node": "Send Pie Chart to Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Line Chart Image": {
      "main": [
        [
          {
            "node": "Send Line Chart to Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Doughnut Chart Image": {
      "main": [
        [
          {
            "node": "Send Doughnut Chart to Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Data & Create Bar Chart": {
      "main": [
        [
          {
            "node": "Fetch Bar Chart Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Data & Create Pie Chart": {
      "main": [
        [
          {
            "node": "Fetch Pie Chart Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Data & Create Line Chart": {
      "main": [
        [
          {
            "node": "Fetch Line Chart Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Data & Create Doughnut Chart": {
      "main": [
        [
          {
            "node": "Fetch Doughnut Chart Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

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

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

高级 - 市场调研, 多模态 AI

需要付费吗?

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

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

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

作者
DataMinex

DataMinex

@dataminex

Smart Connection Analysis from Open Data, Globally at Scale

外部链接
在 n8n.io 查看

分享此工作流