Generar informes de investigación de mercado a partir de reseñas de Google Maps usando Gemini AI
Este es unMarket Research, AI Summarizationflujo de automatización del dominio deautomatización que contiene 15 nodos.Utiliza principalmente nodos como Set, Code, Gmail, Aggregate, HttpRequest. Usar Gemini AI para generar informes de investigación de mercado a partir de reseñas de Google Maps
- •Cuenta de Google y credenciales de API de Gmail
- •Pueden requerirse credenciales de autenticación para la API de destino
- •Clave de API de Google Gemini
Nodos utilizados (15)
{
"meta": {
"instanceId": "fec3a82e5888606db7f18bce6d6a86acc72be4b93a270dd22cfc357c585bfc28"
},
"nodes": [
{
"id": "7a561138-c149-4f92-97b7-087771017521",
"name": "Iniciar Flujo de Trabajo",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-608,
288
],
"parameters": {},
"typeVersion": 1
},
{
"id": "acc1a472-587e-4dc2-b215-e986ca907163",
"name": "Configuración de Entrada del Usuario",
"type": "n8n-nodes-base.set",
"position": [
-416,
288
],
"parameters": {
"fields": {
"values": [
{
"name": "search_query",
"stringValue": "nhà hàng quận 1 TP.HCM"
},
{
"name": "search_location",
"stringValue": "@10.8231,106.6297,12z"
},
{
"name": "language_code",
"stringValue": "vi"
},
{
"name": "analysis_focus",
"stringValue": "restaurant"
},
{
"name": "city_name",
"stringValue": "TP. Hồ Chí Minh"
}
]
},
"options": {}
},
"typeVersion": 3.2
},
{
"id": "9c3ced66-5ec2-4e70-8150-e11711bc3f90",
"name": "Búsqueda Dinámica de Lugares",
"type": "n8n-nodes-base.httpRequest",
"position": [
-208,
288
],
"parameters": {
"url": "https://serpapi.com/search.json",
"options": {},
"jsonQuery": "={\n \"engine\": \"google_maps\",\n \"q\": \"{{ $('User Input Configuration').item.json.search_query }}\",\n \"ll\": \"{{ $('User Input Configuration').item.json.search_location }}\",\n \"hl\": \"{{ $('User Input Configuration').item.json.language_code }}\",\n \"api_key\": \"ffbea3b40ea41b0e10dbceb1302b81209669f83c5b9b75ed8309dbc3f2ae624d\"\n}",
"sendQuery": true,
"specifyQuery": "json"
},
"typeVersion": 4.2
},
{
"id": "0062335c-2c19-4132-938c-37a7013668ed",
"name": "Extracción Mejorada de Datos",
"type": "n8n-nodes-base.code",
"position": [
-16,
288
],
"parameters": {
"jsCode": "// Enhanced extraction with better filtering and validation\nconst localResults = $input.first().json.local_results || [];\nconst userConfig = $('User Input Configuration').item.json;\n\nconsole.log(`Processing ${localResults.length} search results for: ${userConfig.search_query}`);\n\n// Extract and filter places with more comprehensive data\nconst places = localResults.map(place => {\n // Extract additional data that might be useful for analysis\n const placeData = {\n place_name: place.title || 'Unknown',\n address: place.address || 'No address',\n rating: place.rating || 'No rating',\n reviews_count: place.reviews || 0,\n reviews_link: place.reviews_link || null,\n data_id: place.data_id || null,\n type: place.type || 'Unknown',\n place_id: place.place_id || null,\n lsig: place.lsig || null,\n phone: place.phone || null,\n website: place.website || null,\n thumbnail: place.thumbnail || null,\n operating_hours: place.operating_hours || null,\n price_level: place.price_level || null,\n plus_code: place.plus_code || null,\n coordinates: {\n lat: place.gps_coordinates?.latitude || null,\n lng: place.gps_coordinates?.longitude || null\n },\n // Add context about what we're analyzing\n search_context: {\n original_query: userConfig.search_query,\n analysis_focus: userConfig.analysis_focus,\n city: userConfig.city_name,\n search_timestamp: new Date().toISOString()\n }\n };\n \n return placeData;\n}).filter(place => {\n // Enhanced filtering - keep places that have either reviews_link OR sufficient basic data\n return place.reviews_link || (place.rating && place.rating !== 'No rating') || place.reviews_count > 0;\n});\n\nconsole.log(`Filtered to ${places.length} places with review data or sufficient information`);\nconsole.log(`Analysis focus: ${userConfig.analysis_focus} in ${userConfig.city_name}`);\n\n// Return array of places with enhanced metadata\nreturn places.map(place => ({ json: place }));"
},
"typeVersion": 2
},
{
"id": "136b7156-e38c-43a2-95d5-38a76c8e067a",
"name": "Bucle sobre Lugares",
"type": "n8n-nodes-base.splitInBatches",
"position": [
192,
80
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "31112897-af1c-4476-85b9-81a7f287a7ca",
"name": "Obtener Contenido de Reseñas",
"type": "n8n-nodes-base.httpRequest",
"position": [
400,
240
],
"parameters": {
"url": "={{ $json.reviews_link }}",
"options": {
"timeout": 30000,
"allowUnauthorizedCerts": true
},
"jsonQuery": "{\n \"api_key\": \"ffbea3b40ea41b0e10dbceb1302b81209669f83c5b9b75ed8309dbc3f2ae624d\"\n}",
"sendQuery": true,
"specifyQuery": "json"
},
"typeVersion": 4.2
},
{
"id": "c9f7f981-8476-4ba1-8448-5f29ae13d989",
"name": "Combinación Mejorada de Datos",
"type": "n8n-nodes-base.code",
"position": [
592,
384
],
"parameters": {
"jsCode": "// Enhanced data combination with error handling\nconst currentItem = $('Loop Over Places').item;\nconst reviewsResponse = $input.first().json;\nconst userConfig = $('User Input Configuration').item.json;\n\n// Enhanced combined data with more comprehensive information\nconst combinedData = {\n // Basic place information\n place_name: currentItem.json.place_name,\n address: currentItem.json.address,\n rating: currentItem.json.rating,\n reviews_count: currentItem.json.reviews_count,\n data_id: currentItem.json.data_id,\n type: currentItem.json.type,\n phone: currentItem.json.phone,\n website: currentItem.json.website,\n coordinates: currentItem.json.coordinates,\n operating_hours: currentItem.json.operating_hours,\n price_level: currentItem.json.price_level,\n \n // Reviews content with error handling\n reviews_content: reviewsResponse || { error: 'No reviews data available' },\n \n // Enhanced metadata for analysis\n analysis_metadata: {\n search_query: userConfig.search_query,\n analysis_focus: userConfig.analysis_focus,\n target_city: userConfig.city_name,\n data_collection_time: new Date().toISOString(),\n has_reviews: !!(reviewsResponse && reviewsResponse.reviews),\n review_source: currentItem.json.reviews_link || 'No review link',\n place_completeness_score: calculateCompletenessScore(currentItem.json)\n }\n};\n\n// Function to calculate how complete the place data is\nfunction calculateCompletenessScore(placeData) {\n const fields = ['place_name', 'address', 'rating', 'phone', 'website', 'operating_hours'];\n const filledFields = fields.filter(field => \n placeData[field] && placeData[field] !== 'Unknown' && placeData[field] !== 'No address'\n );\n return Math.round((filledFields.length / fields.length) * 100);\n}\n\nconsole.log(`Processing ${combinedData.analysis_metadata.analysis_focus}: ${combinedData.place_name}`);\nconsole.log(`Data completeness: ${combinedData.analysis_metadata.place_completeness_score}%`);\n\nreturn { json: combinedData };"
},
"typeVersion": 2
},
{
"id": "68e789cd-2df5-4783-8891-5cd4d6eb0553",
"name": "Recopilar Todos los Datos",
"type": "n8n-nodes-base.aggregate",
"position": [
800,
288
],
"parameters": {
"options": {},
"aggregate": "aggregateAllItemData"
},
"typeVersion": 1
},
{
"id": "d1084cb2-0d34-4f6a-8a51-f87b1c01c6c3",
"name": "Preparar Datos para Análisis",
"type": "n8n-nodes-base.code",
"position": [
992,
288
],
"parameters": {
"jsCode": "// Enhanced data preparation with flexible analysis structure\nconst allPlaces = $input.all();\nconst userConfig = $('User Input Configuration').item.json;\n\nconsole.log(`Preparing analysis for ${allPlaces.length} ${userConfig.analysis_focus} locations in ${userConfig.city_name}`);\n\n// Create comprehensive analysis dataset\nlet analysisPrompt = `MARKET RESEARCH DATA ANALYSIS\\n`;\nanalysisPrompt += `Research Focus: ${userConfig.analysis_focus}\\n`;\nanalysisPrompt += `Location: ${userConfig.city_name}\\n`;\nanalysisPrompt += `Search Query: \"${userConfig.search_query}\"\\n`;\nanalysisPrompt += `Total Locations Found: ${allPlaces.length}\\n`;\nanalysisPrompt += `Analysis Date: ${new Date().toLocaleDateString('vi-VN')}\\n\\n`;\n\n// Enhanced data summary\nanalysisPrompt += `=== DATASET OVERVIEW ===\\n`;\nlet totalReviews = 0;\nlet avgRating = 0;\nlet placesWithReviews = 0;\nlet completenessScores = [];\n\nallPlaces.forEach((place, index) => {\n const data = place.json;\n \n // Calculate statistics\n if (data.reviews_count && typeof data.reviews_count === 'number') {\n totalReviews += data.reviews_count;\n }\n \n if (data.rating && data.rating !== 'No rating') {\n avgRating += parseFloat(data.rating) || 0;\n }\n \n if (data.reviews_content && data.reviews_content.reviews) {\n placesWithReviews++;\n }\n \n if (data.analysis_metadata && data.analysis_metadata.place_completeness_score) {\n completenessScores.push(data.analysis_metadata.place_completeness_score);\n }\n \n // Add detailed place information\n analysisPrompt += `\\n${index + 1}. ${data.place_name}\\n`;\n analysisPrompt += ` Address: ${data.address}\\n`;\n analysisPrompt += ` Rating: ${data.rating}/5 (${data.reviews_count || 0} reviews)\\n`;\n \n // Add contact and operational info if available\n if (data.phone) analysisPrompt += ` Phone: ${data.phone}\\n`;\n if (data.website) analysisPrompt += ` Website: ${data.website}\\n`;\n if (data.operating_hours) analysisPrompt += ` Hours: ${JSON.stringify(data.operating_hours)}\\n`;\n if (data.price_level) analysisPrompt += ` Price Level: ${data.price_level}\\n`;\n \n // Add review samples if available\n if (data.reviews_content && data.reviews_content.reviews) {\n analysisPrompt += ` Recent Reviews:\\n`;\n data.reviews_content.reviews.slice(0, 5).forEach(review => {\n const reviewText = review.snippet || review.text || review.comment || 'No text available';\n const reviewRating = review.rating || 'No rating';\n analysisPrompt += ` - \"${reviewText.substring(0, 200)}...\" (${reviewRating}/5)\\n`;\n });\n }\n \n analysisPrompt += ` Data Completeness: ${data.analysis_metadata?.place_completeness_score || 'N/A'}%\\n`;\n analysisPrompt += `\\n${'-'.repeat(60)}\\n`;\n});\n\n// Add summary statistics\nconst avgRatingCalculated = allPlaces.length > 0 ? (avgRating / allPlaces.length).toFixed(2) : 0;\nconst avgCompleteness = completenessScores.length > 0 ? \n (completenessScores.reduce((a, b) => a + b, 0) / completenessScores.length).toFixed(1) : 'N/A';\n\nanalysisPrompt += `\\n=== SUMMARY STATISTICS ===\\n`;\nanalysisPrompt += `Total Places Analyzed: ${allPlaces.length}\\n`;\nanalysisPrompt += `Places with Review Data: ${placesWithReviews}\\n`;\nanalysisPrompt += `Total Reviews Collected: ${totalReviews}\\n`;\nanalysisPrompt += `Average Rating: ${avgRatingCalculated}/5\\n`;\nanalysisPrompt += `Average Data Completeness: ${avgCompleteness}%\\n`;\n\nreturn {\n json: {\n analysis_prompt: analysisPrompt,\n raw_data: allPlaces.map(place => place.json),\n summary_stats: {\n total_places: allPlaces.length,\n places_with_reviews: placesWithReviews,\n total_reviews: totalReviews,\n average_rating: avgRatingCalculated,\n average_completeness: avgCompleteness,\n search_context: userConfig\n }\n }\n};"
},
"typeVersion": 2
},
{
"id": "0cdd33f4-447d-4246-98bf-410e562686c1",
"name": "Análisis de Mercado con IA",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
1168,
288
],
"parameters": {
"text": "=Bạn là chuyên gia phân tích và nghiên cứu thị trường chuyên sâu với khả năng tạo ra các báo cáo insights chi tiết và actionable.\n\nBạn đã nhận được dữ liệu thị trường thực tế về \"{{ $('User Input Configuration').item.json.analysis_focus }}\" tại {{ $('User Input Configuration').item.json.city_name }} với đầy đủ thông tin từ khách hàng thực tế.\n\n=== DỮ LIỆU PHÂN TÍCH ===\n{{ JSON.stringify($json.raw_data, null, 2) }}\n\n=== NHIỆM VỤ PHÂN TÍCH ===\n\nHãy tạo một báo cáo phân tích thị trường toàn diện với cấu trúc sau:\n\n**I. TÓM TẮT ĐIỀU HÀNH (EXECUTIVE SUMMARY)**\n- Insights chính trong 3-4 câu\n- Cơ hội kinh doanh lớn nhất\n- Khuyến nghị hành động ngay lập tức\n\n**II. PHÂN TÍCH TỔNG QUAN THỊ TRƯỜNG**\n- Quy mô và mật độ thị trường {{ $('User Input Configuration').item.json.analysis_focus }} tại {{ $('User Input Configuration').item.json.city_name }}\n- Phân khúc giá và chất lượng dịch vụ\n- Phân bố địa lý và khu vực hot\n- Mức độ cạnh tranh và gap thị trường\n\n**III. PHÂN TÍCH TRẢI NGHIỆM KHÁCH HÀNG**\n- Top 5 yếu tố được đánh giá cao nhất (với % khách hàng mention)\n- Top 5 vấn đề phổ biến nhất (với tần suất xuất hiện)\n- Sentiment analysis tổng thể\n- Customer journey và pain points chính\n\n**IV. COMPETITIVE LANDSCAPE**\n- Phân tích 3-5 player mạnh nhất\n- Điểm mạnh/yếu của từng competitor\n- Pricing strategy và value proposition\n- Market positioning gaps\n\n**V. INSIGHTS VÀ XU HƯỚNG**\n- Behavioral patterns của target customers\n- Emerging trends và changing preferences\n- Seasonal/temporal patterns nếu có\n- Technology adoption và digital readiness\n\n**VI. KHUYẾN NGHỊ CHIẾN LƯỢC**\n- Business model tối ưu cho thị trường này\n- Pricing strategy và revenue streams\n- Marketing approach và customer acquisition\n- Operational excellence priorities\n- Investment areas và resource allocation\n\n**VII. ACTION PLAN**\n- 3 hành động ngay lập tức (next 30 days)\n- 3 initiatives trung hạn (3-6 months)\n- Long-term strategy (6-12 months)\n\nYêu cầu:\n- Sử dụng số liệu cụ thể từ dữ liệu thực tế\n- Đưa ra insights độc đáo, không chỉ mô tả\n- Tập trung vào actionable recommendations\n- Viết bằng tiếng Việt chuyên nghiệp nhưng dễ hiểu\n- Highlight key metrics và success factors\n- Đề xuất KPIs để theo dõi success",
"options": {},
"promptType": "define"
},
"typeVersion": 2.1
},
{
"id": "82c701cb-a88a-410c-bda4-aaaf4c5227ef",
"name": "Google Gemini Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
1152,
480
],
"parameters": {
"options": {}
},
"credentials": {
"googlePalmApi": {
"id": "MT1SUsx0Hy6XtV22",
"name": "Google Gemini(PaLM) Api account"
}
},
"typeVersion": 1
},
{
"id": "2ec72fea-962e-45d4-aa48-abf02c0d80de",
"name": "Preparar Contenido del Correo",
"type": "n8n-nodes-base.code",
"position": [
1536,
176
],
"parameters": {
"jsCode": "// Simplified email preparation - Analysis content only\nconst reportContent = $input.first().json.output;\nconst userConfig = $('User Input Configuration').item.json;\nconst summaryStats = $('Prepare Analysis Data').item.json.summary_stats;\n\n// Generate dynamic subject based on analysis focus\nconst analysisType = userConfig.analysis_focus;\nconst cityName = userConfig.city_name;\nconst currentDate = new Date().toLocaleDateString('vi-VN');\n\nconst emailSubject = `📊 Báo Cáo Phân Tích Thị Trường ${analysisType} ${cityName} - ${currentDate}`;\n\n// Enhanced HTML formatting function\nfunction formatToHTML(content) {\n let html = content\n // Convert markdown-style formatting\n .replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')\n .replace(/\\*([^*]+)\\*/g, '<em>$1</em>')\n \n // Convert main sections (I., II., III., etc.)\n .replace(/\\*\\*([IVX]+\\. [^*]+)\\*\\*/g, '<h2 style=\"color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 8px; margin-top: 30px;\">$1</h2>')\n \n // Convert subsections\n .replace(/\\*\\*([^*]+:)\\*\\*/g, '<h3 style=\"color: #34495e; margin-top: 20px; margin-bottom: 10px;\">$1</h3>')\n \n // Convert numbered lists with enhanced styling\n .replace(/^(\\d+\\. )/gm, '<div style=\"margin: 8px 0;\"><strong style=\"color: #3498db;\">$1</strong>')\n \n // Convert bullet points with different levels\n .replace(/^- /gm, '<div style=\"margin-left: 20px; margin: 5px 0;\">• ')\n .replace(/^ - /gm, '<div style=\"margin-left: 40px; margin: 5px 0;\">◦ ')\n .replace(/^ - /gm, '<div style=\"margin-left: 60px; margin: 5px 0;\">▪ ')\n \n // Close div tags and handle line breaks\n .replace(/(• [^<\\n]+)(?=\\n|$)/g, '$1</div>')\n .replace(/(◦ [^<\\n]+)(?=\\n|$)/g, '$1</div>')\n .replace(/(▪ [^<\\n]+)(?=\\n|$)/g, '$1</div>')\n .replace(/\\n\\n/g, '</p><p>')\n .replace(/\\n/g, '<br>');\n\n return '<p>' + html + '</p>'.replace(/<p><\\/p>/g, '');\n}\n\n// Create comprehensive HTML email template - Analysis focused\nconst htmlContent = `\n<!DOCTYPE html>\n<html lang=\"vi\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${emailSubject}</title>\n <style>\n body {\n font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n line-height: 1.6;\n color: #333;\n max-width: 900px;\n margin: 0 auto;\n padding: 20px;\n background-color: #f8f9fa;\n }\n .container {\n background-color: white;\n padding: 40px;\n border-radius: 12px;\n box-shadow: 0 4px 20px rgba(0,0,0,0.1);\n }\n .header {\n text-align: center;\n margin-bottom: 40px;\n padding: 30px;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n border-radius: 10px;\n }\n .header h1 {\n margin: 0;\n font-size: 26px;\n font-weight: bold;\n }\n .header .subtitle {\n font-size: 16px;\n margin-top: 10px;\n opacity: 0.95;\n }\n .stats-overview {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 20px;\n margin: 30px 0;\n padding: 25px;\n background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);\n border-radius: 10px;\n }\n .stat-item {\n text-align: center;\n padding: 15px;\n background: white;\n border-radius: 8px;\n box-shadow: 0 2px 10px rgba(0,0,0,0.1);\n }\n .stat-number {\n font-size: 24px;\n font-weight: bold;\n color: #3498db;\n }\n .stat-label {\n font-size: 12px;\n color: #7f8c8d;\n margin-top: 5px;\n }\n h2 {\n color: #2c3e50;\n border-bottom: 2px solid #3498db;\n padding-bottom: 8px;\n margin-top: 30px;\n }\n h3 {\n color: #34495e;\n margin-top: 20px;\n margin-bottom: 10px;\n }\n .highlight {\n background: #fff3cd;\n border: 1px solid #ffeaa7;\n padding: 15px;\n border-radius: 5px;\n margin: 15px 0;\n }\n .footer {\n margin-top: 40px;\n padding: 25px;\n background: #34495e;\n color: white;\n text-align: center;\n border-radius: 10px;\n font-size: 14px;\n }\n @media (max-width: 600px) {\n body { padding: 10px; }\n .container { padding: 20px; }\n .stats-overview { grid-template-columns: 1fr; }\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"header\">\n <h1>📊 BÁO CÁO PHÂN TÍCH THỊ TRƯỜNG</h1>\n <div class=\"subtitle\">${analysisType} tại ${cityName}</div>\n <div style=\"font-size: 14px; margin-top: 15px; opacity: 0.9;\">\n Ngày phân tích: ${new Date().toLocaleDateString('vi-VN', { \n year: 'numeric', \n month: 'long', \n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit'\n })}\n </div>\n </div>\n \n ${formatToHTML(reportContent)}\n \n <div class=\"footer\">\n <p><strong>🚀 Market Research Analytics System</strong></p>\n <p>📧 Báo cáo được tạo tự động từ dữ liệu thực tế \n <p>🎯 Phân tích: ${analysisType} | 📍 Địa điểm: ${cityName}</p>\n <p style=\"margin-top: 15px; font-size: 12px; opacity: 0.8;\">\n Query gốc: \"${userConfig.search_query}\"\n </p>\n </div>\n </div>\n</body>\n</html>\n`;\n\n// Create simplified plain text version - Analysis only\nconst plainTextContent = `\nBÁO CÁO PHÂN TÍCH THỊ TRƯỜNG - ${analysisType.toUpperCase()} ${cityName.toUpperCase()}\n${'='.repeat(60)}\n\nSỐ LIỆU TỔNG QUAN:\n- Địa điểm phân tích: ${summaryStats.total_places}\n- Tổng đánh giá: ${summaryStats.total_reviews}\n- Rating TB: ${summaryStats.average_rating}/5\n- Có data chi tiết: ${summaryStats.places_with_reviews}\n\n${reportContent.replace(/\\*\\*([^*]+)\\*\\*/g, '$1').replace(/\\*/g, '')}\n\n${'='.repeat(60)}\nBáo cáo tự động - ${currentDate}\n`;\n\nreturn {\n json: {\n htmlContent: htmlContent,\n plainTextContent: plainTextContent,\n subject: emailSubject,\n analysis_metadata: {\n analysis_type: analysisType,\n city: cityName,\n total_locations: summaryStats.total_places,\n total_reviews: summaryStats.total_reviews,\n average_rating: summaryStats.average_rating,\n search_query: userConfig.search_query,\n generation_time: new Date().toISOString()\n }\n }};"
},
"typeVersion": 2
},
{
"id": "7dd14586-d25d-4b12-baef-2be0c6987456",
"name": "Enviar Informe por Correo",
"type": "n8n-nodes-base.gmail",
"position": [
1728,
384
],
"webhookId": "4632d0f8-2acf-4508-acf5-f2d1e9a77bbf",
"parameters": {
"sendTo": "truong11062002@gmail.com",
"message": "={{ $json.htmlContent }}",
"options": {
"ccList": "",
"bccList": "",
"replyTo": ""
},
"subject": "={{ $json.subject }}"
},
"credentials": {
"gmailOAuth2": {
"id": "9hqpp5Ew9HbGDqNu",
"name": "Gmail account"
}
},
"typeVersion": 2
},
{
"id": "1ecc0e85-5ec2-4f9f-a6e8-23a3f7f826a8",
"name": "Estado Final y Registro",
"type": "n8n-nodes-base.code",
"position": [
1936,
384
],
"parameters": {
"jsCode": "// Final workflow status and comprehensive logging\nconst emailResult = $input.first().json;\nconst userConfig = $('User Input Configuration').item.json;\nconst summaryStats = $('Prepare Analysis Data').item.json.summary_stats;\nconst analysisMetadata = $('Prepare Email Content').item.json.analysis_metadata;\n\nconst completionTime = new Date().toISOString();\n\nconsole.log('🎉 ========= FLEXIBLE MARKET ANALYSIS COMPLETED =========');\nconsole.log(`📊 Analysis Type: ${userConfig.analysis_focus}`);\nconsole.log(`🏙️ Target City: ${userConfig.city_name}`);\nconsole.log(`🔍 Search Query: \"${userConfig.search_query}\"`);\nconsole.log(`📍 Total Locations Analyzed: ${summaryStats.total_places}`);\nconsole.log(`💬 Total Reviews Processed: ${summaryStats.total_reviews}`);\nconsole.log(`⭐ Average Rating: ${summaryStats.average_rating}/5`);\nconsole.log(`📧 Email Report Sent Successfully`);\nconsole.log(`⏰ Completion Time: ${completionTime}`);\nconsole.log('=========================================================');\n\n// Generate comprehensive final report\nconst workflowSummary = {\n workflow_status: 'COMPLETED_SUCCESSFULLY',\n workflow_type: 'FLEXIBLE_MARKET_RESEARCH_ANALYSIS',\n execution_metadata: {\n analysis_focus: userConfig.analysis_focus,\n target_location: userConfig.city_name,\n search_query: userConfig.search_query,\n language_code: userConfig.language_code,\n completion_time: completionTime\n },\n data_collection_results: {\n total_places_found: summaryStats.total_places,\n places_with_review_data: summaryStats.places_with_reviews,\n total_reviews_collected: summaryStats.total_reviews,\n average_rating_calculated: summaryStats.average_rating,\n data_completeness_average: summaryStats.average_completeness\n },\n analysis_outputs: {\n ai_model_used: 'Google_Gemini',\n report_format: 'Comprehensive_Market_Analysis',\n email_delivery_status: emailResult.messageId ? 'SUCCESS' : 'UNKNOWN',\n email_subject: analysisMetadata.subject,\n email_recipient: 'truong11062002@gmail.com'\n },\n workflow_capabilities: {\n flexible_search_query: true,\n dynamic_location_targeting: true,\n multi_language_support: true,\n comprehensive_data_extraction: true,\n ai_powered_analysis: true,\n automated_report_generation: true,\n email_delivery: true\n },\n next_steps_recommendations: [\n 'Review the detailed market analysis report in your email',\n 'Customize search parameters for different market segments',\n 'Set up recurring analysis for market monitoring',\n 'Expand analysis to additional cities or business categories',\n 'Integrate insights into business strategy planning'\n ]\n};\n\nreturn { json: workflowSummary };"
},
"typeVersion": 2
},
{
"id": "599485d6-b773-4462-83d4-e0124c6450d6",
"name": "Nota Adhesiva",
"type": "n8n-nodes-base.stickyNote",
"position": [
-832,
-1248
],
"parameters": {
"width": 944,
"height": 1392,
"content": "# 🚀 Market Research Analytics System\n\n> **Transform Google Maps data into actionable business insights with AI-powered analysis**\n\n## 📋 Overview\n\nThis n8n workflow automatically collects business data from Google Maps, analyzes customer reviews using AI, and generates comprehensive market research reports delivered straight to your inbox.\n\n---\n\n## ⚡ Quick Start Guide\n\n### Step 1: Configure Your Search Parameters\n\nNavigate to the **\"Configuration Variables\"** node and update these fields:\n\n```json\n{\n \"search_query\": \"restaurants downtown\", // Your target business type + location\n \"search_location\": \"@40.7589,-73.9851,12z\", // Coordinates (lat,lng,zoom)\n \"language_code\": \"en\", // Language: en, vi, es, fr, etc.\n \"analysis_focus\": \"restaurant\", // Business category for analysis\n \"city_name\": \"New York City\" // Target city name\n}\n```\n\n**🔍 Need coordinates?** Use [LatLong.net](https://www.latlong.net/) to find your target location coordinates.\n\n---\n\n### Step 2: Setup API Keys\n\n#### 🔑 SerpAPI (Google Maps Data)\n1. **Get API Key:** Visit [SerpAPI Google Maps Reviews](https://serpapi.com/google-maps-reviews-api)\n2. **Sign up** for free account (100 searches/month included)\n3. **Copy your API key** from the dashboard\n4. **Update workflow:** Replace `YOUR_SERPAPI_KEY_HERE` in both HTTP nodes\n\n#### 🤖 Google Gemini AI (Analysis)\n1. **Get API Key:** Visit [Google AI Studio](https://ai.google.dev/gemini-api/docs/api-key)\n2. **Create new API key** (free tier available)\n3. **Setup in n8n:** Add credentials in \"Google Gemini Chat Model\" node\n\n---\n\n### Step 3: Configure Email Delivery\n\n1. **Open** the **\"Send Email Report\"** node\n2. **Update recipient:** Change `your-email@example.com` to your email\n3. **Gmail setup:** Connect your Gmail account in n8n credentials\n\n---\n\n### Step 4: Execute & Enjoy! 🎉"
},
"typeVersion": 1
}
],
"pinData": {
"Start Workflow": [
{}
]
},
"connections": {
"7a561138-c149-4f92-97b7-087771017521": {
"main": [
[
{
"node": "acc1a472-587e-4dc2-b215-e986ca907163",
"type": "main",
"index": 0
}
]
]
},
"68e789cd-2df5-4783-8891-5cd4d6eb0553": {
"main": [
[
{
"node": "d1084cb2-0d34-4f6a-8a51-f87b1c01c6c3",
"type": "main",
"index": 0
}
]
]
},
"136b7156-e38c-43a2-95d5-38a76c8e067a": {
"main": [
[
{
"node": "68e789cd-2df5-4783-8891-5cd4d6eb0553",
"type": "main",
"index": 0
}
],
[
{
"node": "31112897-af1c-4476-85b9-81a7f287a7ca",
"type": "main",
"index": 0
}
]
]
},
"7dd14586-d25d-4b12-baef-2be0c6987456": {
"main": [
[
{
"node": "1ecc0e85-5ec2-4f9f-a6e8-23a3f7f826a8",
"type": "main",
"index": 0
}
]
]
},
"0cdd33f4-447d-4246-98bf-410e562686c1": {
"main": [
[
{
"node": "2ec72fea-962e-45d4-aa48-abf02c0d80de",
"type": "main",
"index": 0
}
]
]
},
"31112897-af1c-4476-85b9-81a7f287a7ca": {
"main": [
[
{
"node": "c9f7f981-8476-4ba1-8448-5f29ae13d989",
"type": "main",
"index": 0
}
]
]
},
"9c3ced66-5ec2-4e70-8150-e11711bc3f90": {
"main": [
[
{
"node": "0062335c-2c19-4132-938c-37a7013668ed",
"type": "main",
"index": 0
}
]
]
},
"c9f7f981-8476-4ba1-8448-5f29ae13d989": {
"main": [
[
{
"node": "136b7156-e38c-43a2-95d5-38a76c8e067a",
"type": "main",
"index": 0
}
]
]
},
"0062335c-2c19-4132-938c-37a7013668ed": {
"main": [
[
{
"node": "136b7156-e38c-43a2-95d5-38a76c8e067a",
"type": "main",
"index": 0
}
]
]
},
"d1084cb2-0d34-4f6a-8a51-f87b1c01c6c3": {
"main": [
[
{
"node": "0cdd33f4-447d-4246-98bf-410e562686c1",
"type": "main",
"index": 0
}
]
]
},
"2ec72fea-962e-45d4-aa48-abf02c0d80de": {
"main": [
[
{
"node": "7dd14586-d25d-4b12-baef-2be0c6987456",
"type": "main",
"index": 0
}
]
]
},
"82c701cb-a88a-410c-bda4-aaaf4c5227ef": {
"ai_languageModel": [
[
{
"node": "0cdd33f4-447d-4246-98bf-410e562686c1",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"acc1a472-587e-4dc2-b215-e986ca907163": {
"main": [
[
{
"node": "9c3ced66-5ec2-4e70-8150-e11711bc3f90",
"type": "main",
"index": 0
}
]
]
}
}
}¿Cómo usar este flujo de trabajo?
Copie el código de configuración JSON de arriba, cree un nuevo flujo de trabajo en su instancia de n8n y seleccione "Importar desde JSON", pegue la configuración y luego modifique la configuración de credenciales según sea necesario.
¿En qué escenarios es adecuado este flujo de trabajo?
Intermedio - Investigación de mercado, Resumen de IA
¿Es de pago?
Este flujo de trabajo es completamente gratuito, puede importarlo y usarlo directamente. Sin embargo, tenga en cuenta que los servicios de terceros utilizados en el flujo de trabajo (como la API de OpenAI) pueden requerir un pago por su cuenta.
Flujos de trabajo relacionados recomendados
Charles
@charlesnguyenI help Sales & Marketing teams save time with custom n8n workflows. With over 5 years of automation experience, I offer free consultations—book now!
Compartir este flujo de trabajo