{ "name": "容量規劃與趨勢分析", "nodes": [ { "parameters": { "rule": { "interval": [ { "field": "cronExpression", "expression": "0 8 1 * *" } ] } }, "id": "schedule", "name": "每月 1 日 08:00", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.2, "position": [240, 300] }, { "parameters": { "url": "https://mo.wooo.work/api/system/db/stats", "options": { "timeout": 30000 } }, "id": "uat-db-stats", "name": "UAT DB 統計", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [460, 200], "continueOnFail": true }, { "parameters": { "url": "https://momo.wooo.work/api/system/db/stats", "options": { "timeout": 30000 } }, "id": "gcp-db-stats", "name": "GCP DB 統計", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [460, 400], "continueOnFail": true }, { "parameters": { "mode": "combine", "combineBy": "combineAll", "options": {} }, "id": "merge", "name": "合併結果", "type": "n8n-nodes-base.merge", "typeVersion": 3, "position": [680, 300] }, { "parameters": { "jsCode": "const results = $input.all();\nconst now = new Date();\nconst monthKey = now.toISOString().slice(0, 7);\n\n// 從 static data 讀取歷史數據\nconst history = $getWorkflowStaticData('global');\nif (!history.monthly) {\n history.monthly = [];\n}\n\nconst uatStats = results[0]?.json || {};\nconst gcpStats = results[1]?.json || {};\n\n// 記錄本月數據\nhistory.monthly.push({\n month: monthKey,\n uat: {\n dbSize: uatStats.database_size_mb || 0,\n connections: uatStats.connection_count || 0,\n tableCount: uatStats.table_count || 0\n },\n gcp: {\n dbSize: gcpStats.database_size_mb || 0,\n connections: gcpStats.connection_count || 0,\n tableCount: gcpStats.table_count || 0\n }\n});\n\n// 保留最近 12 個月\nif (history.monthly.length > 12) {\n history.monthly.shift();\n}\n\n// 計算成長趨勢\nconst monthsData = history.monthly;\nlet uatGrowthRate = 0;\nlet gcpGrowthRate = 0;\nlet uatPrediction = 0;\nlet gcpPrediction = 0;\n\nif (monthsData.length >= 2) {\n const lastMonth = monthsData[monthsData.length - 2];\n const thisMonth = monthsData[monthsData.length - 1];\n \n if (lastMonth.uat.dbSize > 0) {\n uatGrowthRate = ((thisMonth.uat.dbSize - lastMonth.uat.dbSize) / lastMonth.uat.dbSize * 100).toFixed(1);\n // 預測 6 個月後的大小\n uatPrediction = (thisMonth.uat.dbSize * Math.pow(1 + parseFloat(uatGrowthRate) / 100, 6)).toFixed(0);\n }\n \n if (lastMonth.gcp.dbSize > 0) {\n gcpGrowthRate = ((thisMonth.gcp.dbSize - lastMonth.gcp.dbSize) / lastMonth.gcp.dbSize * 100).toFixed(1);\n gcpPrediction = (thisMonth.gcp.dbSize * Math.pow(1 + parseFloat(gcpGrowthRate) / 100, 6)).toFixed(0);\n }\n}\n\n// 容量警告閾值\nconst DB_SIZE_WARNING = 8000; // 8GB\nconst DB_SIZE_CRITICAL = 9000; // 9GB\n\nconst uatWarning = (uatStats.database_size_mb || 0) > DB_SIZE_WARNING;\nconst gcpWarning = (gcpStats.database_size_mb || 0) > DB_SIZE_WARNING;\nconst uatPredictionWarning = parseFloat(uatPrediction) > DB_SIZE_CRITICAL;\nconst gcpPredictionWarning = parseFloat(gcpPrediction) > DB_SIZE_CRITICAL;\n\nreturn [{\n json: {\n uat: {\n currentSize: (uatStats.database_size_mb || 0) + ' MB',\n growthRate: uatGrowthRate + '%',\n prediction6m: uatPrediction + ' MB',\n warning: uatWarning,\n predictionWarning: uatPredictionWarning\n },\n gcp: {\n currentSize: (gcpStats.database_size_mb || 0) + ' MB',\n growthRate: gcpGrowthRate + '%',\n prediction6m: gcpPrediction + ' MB',\n warning: gcpWarning,\n predictionWarning: gcpPredictionWarning\n },\n historyMonths: monthsData.length,\n hasWarning: uatWarning || gcpWarning || uatPredictionWarning || gcpPredictionWarning\n }\n}];" }, "id": "analyze", "name": "分析趨勢", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [900, 300] }, { "parameters": { "jsCode": "const data = $input.first().json;\nconst now = new Date().toLocaleString('zh-TW', { timeZone: 'Asia/Taipei' });\n\nlet message = `📈 每月容量規劃報告\\n\\n`;\n\nmessage += `🟦 UAT 環境\\n`;\nmessage += `├ 目前大小: ${data.uat.currentSize}\\n`;\nmessage += `├ 月成長率: ${data.uat.growthRate}\\n`;\nmessage += `├ 6 個月預測: ${data.uat.prediction6m}\\n`;\nif (data.uat.warning) {\n message += `└ ⚠️ 警告:接近容量上限\\n`;\n} else if (data.uat.predictionWarning) {\n message += `└ ⚠️ 預測:6 個月內可能超過容量\\n`;\n} else {\n message += `└ ✅ 容量正常\\n`;\n}\n\nmessage += `\\n🟥 GCP 環境\\n`;\nmessage += `├ 目前大小: ${data.gcp.currentSize}\\n`;\nmessage += `├ 月成長率: ${data.gcp.growthRate}\\n`;\nmessage += `├ 6 個月預測: ${data.gcp.prediction6m}\\n`;\nif (data.gcp.warning) {\n message += `└ ⚠️ 警告:接近容量上限\\n`;\n} else if (data.gcp.predictionWarning) {\n message += `└ ⚠️ 預測:6 個月內可能超過容量\\n`;\n} else {\n message += `└ ✅ 容量正常\\n`;\n}\n\nmessage += `\\n📊 歷史數據: ${data.historyMonths} 個月\\n`;\nmessage += `⏰ 報告時間: ${now}`;\n\nreturn [{ json: { message } }];" }, "id": "format-report", "name": "格式化報告", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [1120, 300] }, { "parameters": { "url": "https://api.telegram.org/bot/sendMessage", "sendBody": true, "specifyBody": "json", "jsonBody": "={\n \"chat_id\": \"5619078117\",\n \"parse_mode\": \"HTML\",\n \"text\": \"{{ $json.message }}\"\n}", "options": {} }, "id": "telegram", "name": "發送報告", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [1340, 300] } ], "connections": { "每月 1 日 08:00": { "main": [ [ { "node": "UAT DB 統計", "type": "main", "index": 0 }, { "node": "GCP DB 統計", "type": "main", "index": 0 } ] ] }, "UAT DB 統計": { "main": [ [{ "node": "合併結果", "type": "main", "index": 0 }] ] }, "GCP DB 統計": { "main": [ [{ "node": "合併結果", "type": "main", "index": 1 }] ] }, "合併結果": { "main": [ [{ "node": "分析趨勢", "type": "main", "index": 0 }] ] }, "分析趨勢": { "main": [ [{ "node": "格式化報告", "type": "main", "index": 0 }] ] }, "格式化報告": { "main": [ [{ "node": "發送報告", "type": "main", "index": 0 }] ] } }, "settings": { "executionOrder": "v1" }, "staticData": null, "tags": [], "triggerCount": 0, "updatedAt": "2026-02-07T17:00:00.000Z", "versionId": "1" }