실시간 경쟁 건설 모니터링
이것은Market Research분야의자동화 워크플로우로, 13개의 노드를 포함합니다.주로 If, Code, Wait, EmailSend, HttpRequest 등의 노드를 사용하며. 이메일 알림 및 데이터 API가 포함된 자동화된 건설 프로젝트 경고
- •대상 API의 인증 정보가 필요할 수 있음
카테고리
{
"id": "tAZrn9nO8QUWfGQx",
"meta": {
"instanceId": "dd69efaf8212c74ad206700d104739d3329588a6f3f8381a46a481f34c9cc281",
"templateCredsSetupCompleted": true
},
"name": "Real-Time Competitive Construction Monitoring",
"tags": [],
"nodes": [
{
"id": "878dbf6e-dc80-457d-b212-03e98c69042f",
"name": "일정 트리거",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
0,
0
],
"parameters": {
"rule": {
"interval": [
{}
]
}
},
"typeVersion": 1.1
},
{
"id": "bba4ec2c-854e-4c51-ba14-f19ab0af919c",
"name": "이메일 트리거",
"type": "n8n-nodes-base.emailReadImap",
"position": [
0,
180
],
"parameters": {
"options": {}
},
"credentials": {
"imap": {
"id": "zTEGYssr7MSVeCs3",
"name": "IMAP-test"
}
},
"typeVersion": 2
},
{
"id": "46fdbb5a-478a-488d-ba1f-c752f1d708b9",
"name": "Check 이메일 Subject",
"type": "n8n-nodes-base.if",
"position": [
220,
180
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "condition1",
"operator": {
"type": "string",
"operation": "contains"
},
"leftValue": "={{ $json.subject }}",
"rightValue": "Construction Alert Request"
}
]
}
},
"typeVersion": 2
},
{
"id": "af98d975-c83d-4cf6-b060-a33f8c5d3211",
"name": "Extract Location Info",
"type": "n8n-nodes-base.code",
"position": [
440,
80
],
"parameters": {
"jsCode": "// Extract area/location from email body\nconst emailBody = $input.first().json.text || $input.first().json.html;\nconst lines = emailBody.split('\\n');\n\nlet area = '';\nlet city = '';\nlet state = '';\nlet zipcode = '';\n\n// Look for area information in email\nfor (const line of lines) {\n if (line.toLowerCase().includes('area:') || line.toLowerCase().includes('location:')) {\n area = line.split(':')[1]?.trim() || '';\n }\n if (line.toLowerCase().includes('city:')) {\n city = line.split(':')[1]?.trim() || '';\n }\n if (line.toLowerCase().includes('state:')) {\n state = line.split(':')[1]?.trim() || '';\n }\n if (line.toLowerCase().includes('zip:') || line.toLowerCase().includes('zipcode:')) {\n zipcode = line.split(':')[1]?.trim() || '';\n }\n}\n\n// If no structured data found, try to extract from general text\nif (!area && !city) {\n const addressRegex = /([A-Za-z\\s]+),\\s*([A-Z]{2})\\s*(\\d{5})?/;\n const match = emailBody.match(addressRegex);\n if (match) {\n city = match[1];\n state = match[2];\n zipcode = match[3] || '';\n }\n}\n\nreturn {\n json: {\n searchArea: area || city,\n city: city,\n state: state,\n zipcode: zipcode,\n originalEmail: $input.first().json,\n searchQuery: `${area || city} ${state} construction permits`.trim()\n }\n};"
},
"typeVersion": 2
},
{
"id": "85f1df36-93ce-4895-bbde-022ebe40a1e7",
"name": "Search Government Data",
"type": "n8n-nodes-base.httpRequest",
"position": [
660,
0
],
"parameters": {
"url": "https://api.usa.gov/jobs/search.json",
"options": {},
"sendQuery": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpQueryAuth",
"queryParameters": {
"parameters": [
{
"name": "keyword",
"value": "construction permit"
},
{
"name": "location_name",
"value": "={{ $json.searchArea }}"
},
{
"name": "size",
"value": "20"
}
]
}
},
"credentials": {
"httpQueryAuth": {
"id": "xA2e6hA40RZ8bzrI",
"name": "Query Auth account - test"
}
},
"typeVersion": 4.1
},
{
"id": "acc6341d-273f-4534-afb7-27771759a1df",
"name": "Search Construction Sites",
"type": "n8n-nodes-base.httpRequest",
"position": [
660,
160
],
"parameters": {
"url": "https://www.construction.com/api/search",
"options": {
"timeout": 10000
},
"sendQuery": true,
"sendHeaders": true,
"queryParameters": {
"parameters": [
{
"name": "q",
"value": "={{ $json.searchArea }} construction projects"
},
{
"name": "type",
"value": "projects"
},
{
"name": "limit",
"value": "15"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "User-Agent",
"value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
},
{
"name": "Accept",
"value": "application/json"
}
]
}
},
"typeVersion": 4.1
},
{
"id": "f8ca860e-b794-4749-9fa9-bb340e955845",
"name": "Process Construction Data",
"type": "n8n-nodes-base.code",
"position": [
900,
40
],
"parameters": {
"jsCode": "const governmentData = $input.all()[0]?.json?.results || [];\nconst constructionData = $input.all()[1]?.json?.projects || [];\nconst searchInfo = $('Extract Location Info').first().json;\n\n// Process government construction data\nconst govProjects = governmentData.map(item => ({\n title: item.position_title || 'Government Construction Project',\n location: item.locations?.[0] || searchInfo.searchArea,\n description: item.job_summary || 'No description available',\n source: 'Government Database',\n url: item.url || '#',\n startDate: item.start_date || 'TBD',\n type: 'Public Project'\n}));\n\n// Process construction industry data (fallback mock data if API fails)\nlet constructionProjects = [];\nif (constructionData.length > 0) {\n constructionProjects = constructionData.map(item => ({\n title: item.name || item.title || 'Construction Project',\n location: item.location || searchInfo.searchArea,\n description: item.description || 'Commercial construction project',\n source: 'Construction Industry',\n url: item.url || '#',\n startDate: item.start_date || 'Q2 2024',\n type: item.type || 'Private Project'\n }));\n} else {\n // Fallback mock data for demo purposes\n constructionProjects = [\n {\n title: `New Commercial Complex - ${searchInfo.searchArea}`,\n location: searchInfo.searchArea,\n description: 'Mixed-use commercial and residential development',\n source: 'Local Planning Department',\n url: '#',\n startDate: 'March 2024',\n type: 'Mixed Development'\n },\n {\n title: `Office Building Construction - ${searchInfo.city}`,\n location: `${searchInfo.city}, ${searchInfo.state}`,\n description: '5-story office building with retail space',\n source: 'Building Permits',\n url: '#',\n startDate: 'April 2024',\n type: 'Commercial'\n }\n ];\n}\n\n// Combine all projects\nconst allProjects = [...govProjects, ...constructionProjects];\n\n// Filter for recent/upcoming projects\nconst recentProjects = allProjects.filter(project => {\n const startDate = new Date(project.startDate);\n const now = new Date();\n const threeMonthsFromNow = new Date(now.getTime() + (90 * 24 * 60 * 60 * 1000));\n \n return startDate >= now || project.startDate.includes('2024') || project.startDate === 'TBD';\n});\n\nreturn {\n json: {\n searchArea: searchInfo.searchArea,\n totalProjects: recentProjects.length,\n projects: recentProjects.slice(0, 10), // Limit to top 10\n searchQuery: searchInfo.searchQuery,\n generatedAt: new Date().toISOString(),\n originalEmail: searchInfo.originalEmail\n }\n};"
},
"typeVersion": 2
},
{
"id": "5e85f511-81ac-42bf-af01-f465531874bb",
"name": "Check if Projects Found",
"type": "n8n-nodes-base.if",
"position": [
1320,
80
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "condition1",
"operator": {
"type": "number",
"operation": "gt"
},
"leftValue": "={{ $json.totalProjects }}",
"rightValue": 0
}
]
}
},
"typeVersion": 2
},
{
"id": "3a614463-2f2d-4d28-8761-9dcb4349b653",
"name": "Generate 이메일 Report",
"type": "n8n-nodes-base.code",
"position": [
1540,
0
],
"parameters": {
"jsCode": "const data = $input.first().json;\nconst projects = data.projects;\n\n// Create HTML email content\nlet htmlContent = `\n<html>\n<head>\n <style>\n body { font-family: Arial, sans-serif; margin: 20px; }\n .header { background-color: #f4f4f4; padding: 20px; border-radius: 5px; }\n .project { border: 1px solid #ddd; margin: 10px 0; padding: 15px; border-radius: 5px; }\n .project-title { color: #333; font-weight: bold; font-size: 16px; }\n .project-meta { color: #666; font-size: 12px; margin: 5px 0; }\n .project-desc { margin: 10px 0; }\n .summary { background-color: #e8f4fd; padding: 15px; border-radius: 5px; margin: 20px 0; }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <h2>🏗️ Construction Project Alert Report</h2>\n <p><strong>Search Area:</strong> ${data.searchArea}</p>\n <p><strong>Report Generated:</strong> ${new Date(data.generatedAt).toLocaleString()}</p>\n </div>\n \n <div class=\"summary\">\n <h3>📊 Summary</h3>\n <p><strong>Total Projects Found:</strong> ${data.totalProjects}</p>\n <p><strong>Search Query:</strong> ${data.searchQuery}</p>\n </div>\n \n <h3>🔍 Upcoming Construction Projects</h3>\n`;\n\nif (projects.length === 0) {\n htmlContent += '<p>No upcoming construction projects found in the specified area.</p>';\n} else {\n projects.forEach((project, index) => {\n htmlContent += `\n <div class=\"project\">\n <div class=\"project-title\">${project.title}</div>\n <div class=\"project-meta\">\n 📍 Location: ${project.location} | \n 📅 Start Date: ${project.startDate} | \n 🏢 Type: ${project.type}\n </div>\n <div class=\"project-desc\">\n <strong>Description:</strong> ${project.description}\n </div>\n <div class=\"project-meta\">\n <strong>Source:</strong> ${project.source}\n </div>\n </div>\n `;\n });\n}\n\nhtmlContent += `\n <div style=\"margin-top: 30px; padding: 15px; background-color: #f9f9f9; border-radius: 5px;\">\n <h4>💡 Next Steps</h4>\n <ul>\n <li>Review each project for potential competition</li>\n <li>Contact project owners for partnership opportunities</li>\n <li>Monitor progress and timeline changes</li>\n <li>Update your competitive analysis</li>\n </ul>\n </div>\n \n <p style=\"margin-top: 30px; color: #666; font-size: 12px;\">\n This report was automatically generated by your Construction Alert System.<br>\n To modify your alert preferences, reply to this email with your requirements.\n </p>\n</body>\n</html>\n`;\n\n// Create plain text version\nlet textContent = `Construction Project Alert Report\\n\\n`;\ntextContent += `Search Area: ${data.searchArea}\\n`;\ntextContent += `Total Projects Found: ${data.totalProjects}\\n`;\ntextContent += `Report Generated: ${new Date(data.generatedAt).toLocaleString()}\\n\\n`;\n\nif (projects.length === 0) {\n textContent += 'No upcoming construction projects found in the specified area.\\n';\n} else {\n textContent += 'UPCOMING CONSTRUCTION PROJECTS:\\n';\n textContent += '=' .repeat(40) + '\\n\\n';\n \n projects.forEach((project, index) => {\n textContent += `${index + 1}. ${project.title}\\n`;\n textContent += ` Location: ${project.location}\\n`;\n textContent += ` Start Date: ${project.startDate}\\n`;\n textContent += ` Type: ${project.type}\\n`;\n textContent += ` Description: ${project.description}\\n`;\n textContent += ` Source: ${project.source}\\n\\n`;\n });\n}\n\nreturn {\n json: {\n subject: `🏗️ Construction Alert: ${data.totalProjects} Projects Found in ${data.searchArea}`,\n htmlContent: htmlContent,\n textContent: textContent,\n recipientEmail: data.originalEmail.from,\n searchArea: data.searchArea,\n projectCount: data.totalProjects\n }\n};"
},
"typeVersion": 2
},
{
"id": "4782972d-6b57-4faf-9ce1-cedbee42ecff",
"name": "Send Alert 이메일",
"type": "n8n-nodes-base.emailSend",
"position": [
1760,
0
],
"webhookId": "9dae36d2-91a9-4ef4-90ae-60754055afd2",
"parameters": {
"html": "={{ $json.htmlContent }}",
"options": {},
"subject": "={{ $json.subject }}",
"toEmail": "={{ $json.recipientEmail }}",
"fromEmail": "alerts@yourcompany.com"
},
"credentials": {
"smtp": {
"id": "G1kyF8cSWTZ4vouN",
"name": "SMTP -test"
}
},
"typeVersion": 2.1
},
{
"id": "49d55c27-73f1-4436-99f9-f0e4ff2cf8ac",
"name": "Send No Results 이메일",
"type": "n8n-nodes-base.emailSend",
"position": [
1540,
200
],
"webhookId": "5375f687-193e-403e-a599-bbba10c97977",
"parameters": {
"text": "No upcoming construction projects were found for the requested area: {{ $('Extract Location Info').first().json.searchArea }}\\n\\nSearch was triggered at: {{ new Date().toLocaleString() }}\\n\\nOriginal request from: {{ $('Extract Location Info').first().json.originalEmail.from }}",
"options": {},
"subject": "No Construction Projects Found",
"toEmail": "admin@yourcompany.com",
"fromEmail": "alerts@yourcompany.com",
"emailFormat": "text"
},
"credentials": {
"smtp": {
"id": "G1kyF8cSWTZ4vouN",
"name": "SMTP -test"
}
},
"typeVersion": 2.1
},
{
"id": "9f9e205e-a6c6-4bf0-b25b-8d608eabc294",
"name": "대기 For Data",
"type": "n8n-nodes-base.wait",
"position": [
1120,
40
],
"webhookId": "fc75279b-7f48-421a-8222-b57f48dc06e2",
"parameters": {},
"typeVersion": 1.1
},
{
"id": "e5e98309-33a4-4b60-9cd6-f51925d77d7c",
"name": "메모",
"type": "n8n-nodes-base.stickyNote",
"position": [
220,
-360
],
"parameters": {
"width": 860,
"height": 300,
"content": "\n## **How it works**\n* **Email Trigger** - Detects new email requests with \"Construction Alert Request\" in the subject line\n* **Check Email Subject** - Validates that the email contains the correct trigger phrase\n* **Extract Location Info** - Parses the email body to extract area, city, state, and zip code information\n* **Search Government Data** - Queries government databases for public construction projects and permits\n* **Search Construction Sites** - Searches construction industry databases for private projects\n* **Process Construction Data** - Combines and filters results from both sources, removing duplicates\n* **Wait For Data** - Wait for Combines and filters results\n* **Check If Projects Found** - Determines whether to send a results report or no-results notification\n* **Generate Email Report** - Creates a professional HTML email with project details and summaries\n* **Send Alert Email** - Delivers the construction project report to the requester\n* **Send No Results Email** - Notifies when no projects are found in the specified area\n\nThe workflow also includes a **Schedule Trigger** that can run automatically on weekdays at 9 AM for regular monitoring."
},
"typeVersion": 1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "5e298be6-7660-412d-b5bb-15136626b35d",
"connections": {
"Email Trigger": {
"main": [
[
{
"node": "Check Email Subject",
"type": "main",
"index": 0
}
]
]
},
"Wait For Data": {
"main": [
[
{
"node": "5e85f511-81ac-42bf-af01-f465531874bb",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "af98d975-c83d-4cf6-b060-a33f8c5d3211",
"type": "main",
"index": 0
}
]
]
},
"Check Email Subject": {
"main": [
[
{
"node": "af98d975-c83d-4cf6-b060-a33f8c5d3211",
"type": "main",
"index": 0
}
]
]
},
"af98d975-c83d-4cf6-b060-a33f8c5d3211": {
"main": [
[
{
"node": "85f1df36-93ce-4895-bbde-022ebe40a1e7",
"type": "main",
"index": 0
},
{
"node": "acc6341d-273f-4534-afb7-27771759a1df",
"type": "main",
"index": 0
}
]
]
},
"Generate Email Report": {
"main": [
[
{
"node": "Send Alert Email",
"type": "main",
"index": 0
}
]
]
},
"85f1df36-93ce-4895-bbde-022ebe40a1e7": {
"main": [
[
{
"node": "f8ca860e-b794-4749-9fa9-bb340e955845",
"type": "main",
"index": 0
}
]
]
},
"5e85f511-81ac-42bf-af01-f465531874bb": {
"main": [
[
{
"node": "Generate Email Report",
"type": "main",
"index": 0
}
],
[
{
"node": "Send No Results Email",
"type": "main",
"index": 0
}
]
]
},
"f8ca860e-b794-4749-9fa9-bb340e955845": {
"main": [
[
{
"node": "Wait For Data",
"type": "main",
"index": 0
}
]
]
},
"acc6341d-273f-4534-afb7-27771759a1df": {
"main": [
[
{
"node": "f8ca860e-b794-4749-9fa9-bb340e955845",
"type": "main",
"index": 0
}
]
]
}
}
}이 워크플로우를 어떻게 사용하나요?
위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.
이 워크플로우는 어떤 시나리오에 적합한가요?
중급 - 시장 조사
유료인가요?
이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.
관련 워크플로우 추천
Oneclick AI Squad
@oneclick-aiThe AI Squad Initiative is a pioneering effort to build, automate and scale AI-powered workflows using n8n.io. Our mission is to help individuals and businesses integrate AI agents seamlessly into their daily operations from automating tasks and enhancing productivity to creating innovative, intelligent solutions. We design modular, reusable AI workflow templates that empower creators, developers and teams to supercharge their automation with minimal effort and maximum impact.
이 워크플로우 공유