diff --git a/n8n-workflows/keyword-cloud-workflow.json b/n8n-workflows/keyword-cloud-workflow.json new file mode 100644 index 0000000..f54e6eb --- /dev/null +++ b/n8n-workflows/keyword-cloud-workflow.json @@ -0,0 +1,197 @@ +{ + "name": "Keyword Cloud Data Collection", + "nodes": [ + { + "parameters": { + "rule": { + "interval": [ + { + "cron": "0 0 23 28-31 * *" + } + ] + } + }, + "id": "schedule-trigger", + "name": "Schedule Trigger", + "type": "n8n-nodes-base.scheduleTrigger", + "typeVersion": 1.1, + "position": [250, 300] + }, + { + "parameters": { + "functionCode": "// 检查当前月份是否是季度末(3,6,9,12)\nconst now = new Date();\nconst month = now.getMonth() + 1; // 1-12\nconst isQuarterEnd = [3, 6, 9, 12].includes(month);\n\nif (!isQuarterEnd) {\n return [];\n}\n\n// 计算季度标识\nconst year = now.getFullYear();\nconst quarter = Math.ceil(month / 3);\nconst quarterString = `${year}-Q${quarter}`;\n\n// 计算季度的开始和结束日期\nconst quarterStart = new Date(year, (quarter - 1) * 3, 1);\nconst quarterEnd = new Date(year, quarter * 3, 0);\n\nreturn [{\n json: {\n quarter: quarterString,\n startDate: quarterStart.toISOString().split('T')[0],\n endDate: quarterEnd.toISOString().split('T')[0]\n }\n}];" + }, + "id": "calculate-quarter", + "name": "Calculate Quarter", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [450, 300] + }, + { + "parameters": { + "resource": "interestOverTime", + "operation": "get", + "searchTerms": "AI,artificial intelligence,machine learning,GPT,LLM,ChatGPT,transformer", + "timeRange": "={{ $json.startDate }} {{ $json.endDate }}", + "category": "0", + "geo": "GB" + }, + "id": "google-trends", + "name": "Google Trends", + "type": "@gamal.dev/n8n-nodes-google-trends", + "typeVersion": 1, + "position": [650, 300] + }, + { + "parameters": { + "functionCode": "// 从 Google Trends 数据中提取热门词汇\nconst trends = $input.all();\nconst keywords = [];\n\n// 假设 Google Trends 返回数据包含关键词和分数\nfor (const trend of trends) {\n const data = trend.json;\n\n if (data.timeline) {\n for (const [keyword, values] of Object.entries(data.timeline)) {\n if (Array.isArray(values) && values.length > 0) {\n // 计算平均分数\n const avgScore = values.reduce((a, b) => a + b, 0) / values.length;\n keywords.push({\n json: {\n word: keyword,\n trendScore: Math.round(avgScore),\n quarter: $('Calculate Quarter').item.json.quarter\n }\n });\n }\n }\n }\n}\n\nreturn keywords;" + }, + "id": "extract-keywords", + "name": "Extract Keywords", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [850, 300] + }, + { + "parameters": { + "values": { + "string": [ + { + "name": "apiKey", + "value": "={{ $env.API_KEY }}" + } + ] + }, + "options": {} + }, + "id": "set-api-key", + "name": "Set API Key", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [1050, 300] + }, + { + "parameters": { + "url": "={{ $env.API_URL }}/api/keyword-cloud/rules", + "options": {} + }, + "id": "get-visual-rules", + "name": "Get Visual Rules", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.1, + "position": [1250, 300] + }, + { + "parameters": { + "jsCode": "// 获取关键词和规则\nconst keywords = $('Extract Keywords').all();\nconst rulesData = $('Get Visual Rules').first().json;\nconst rules = rulesData.rules || [];\n\n// 规则按优先级排序\nrules.sort((a, b) => a.priority - b.priority);\n\n// 为每个关键词匹配规则\nconst processed = keywords.map(item => {\n const keyword = item.json;\n\n // 查找匹配的规则\n const matchedRule = rules.find(rule =>\n keyword.trendScore >= rule.minScore &&\n keyword.trendScore <= rule.maxScore\n );\n\n const visualConfig = matchedRule ? matchedRule.visualConfig : {\n color: 'gray',\n size: 'text-base',\n border: 'border-2',\n rotation: null\n };\n\n return {\n json: {\n ...keyword,\n visualConfig\n }\n };\n});\n\nreturn processed;" + }, + "id": "match-visual-rules", + "name": "Match Visual Rules", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [1450, 300] + }, + { + "parameters": { + "url": "={{ $env.API_URL }}/api/keyword-cloud/keywords", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={\n \"apiKey\": \"{{ $env.API_KEY }}\",\n \"quarter\": \"{{ $('Calculate Quarter').item.json.quarter }}\",\n \"keywords\": {{ $json.all().map(item => ({\n word: item.json.word,\n trendScore: item.json.trendScore,\n description: \"AI生成的描述\", // TODO: 使用 AI 节点生成\n detailPoints: [\"要点1\", \"要点2\", \"要点3\"] // TODO: 使用 AI 节点生成\n })) }}\n}", + "options": {} + }, + "id": "send-to-api", + "name": "Send to API", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.1, + "position": [1650, 300] + } + ], + "connections": { + "Schedule Trigger": { + "main": [ + [ + { + "node": "Calculate Quarter", + "type": "main", + "index": 0 + } + ] + ] + }, + "Calculate Quarter": { + "main": [ + [ + { + "node": "Google Trends", + "type": "main", + "index": 0 + } + ] + ] + }, + "Google Trends": { + "main": [ + [ + { + "node": "Extract Keywords", + "type": "main", + "index": 0 + } + ] + ] + }, + "Extract Keywords": { + "main": [ + [ + { + "node": "Set API Key", + "type": "main", + "index": 0 + } + ] + ] + }, + "Set API Key": { + "main": [ + [ + { + "node": "Get Visual Rules", + "type": "main", + "index": 0 + } + ] + ] + }, + "Get Visual Rules": { + "main": [ + [ + { + "node": "Match Visual Rules", + "type": "main", + "index": 0 + } + ] + ] + }, + "Match Visual Rules": { + "main": [ + [ + { + "node": "Send to API", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "pinData": {}, + "settings": { + "executionOrder": "v1" + }, + "staticData": null, + "tags": [], + "triggerCount": 0, + "updatedAt": "2026-01-25T00:00:00.000Z", + "versionId": "1" +}