使用n8n API和邮件发送生成周度工作流分析报告
高级
这是一个DevOps领域的自动化工作流,包含 19 个节点。主要使用 N8n, Set, Code, Gmail, Merge 等节点。 使用n8n API和邮件发送生成周度工作流分析报告
前置要求
- •Google 账号和 Gmail API 凭证
分类
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"meta": {
"instanceId": "89249a8a187ba6e01e16112a0d334a3aa01d510ad8f88d223e12cc0a2a8beb6b"
},
"nodes": [
{
"id": "9c2f922e-2f23-417f-9e26-f2f91d719728",
"name": "计划触发器",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
912,
16
],
"parameters": {
"rule": {
"interval": [
{
"daysInterval": 7
}
]
}
},
"typeVersion": 1.1
},
{
"id": "ea56ce13-5b66-439c-8241-3c8eecb06eb8",
"name": "获取所有先前执行记录",
"type": "n8n-nodes-base.n8n",
"position": [
1824,
112
],
"parameters": {
"filters": {},
"options": {},
"resource": "execution",
"returnAll": true,
"requestOptions": {}
},
"notesInFlow": false,
"typeVersion": 1
},
{
"id": "22cefb90-bce7-402d-a996-6489bda7c136",
"name": "获取所有工作流",
"type": "n8n-nodes-base.n8n",
"position": [
1424,
-160
],
"parameters": {
"filters": {},
"requestOptions": {}
},
"typeVersion": 1
},
{
"id": "b6395b45-7c15-4ab4-9dc4-905ddf04d54b",
"name": "合并",
"type": "n8n-nodes-base.merge",
"position": [
2496,
0
],
"parameters": {
"mode": "combine",
"options": {
"clashHandling": {
"values": {
"resolveClash": "preferLast"
}
}
},
"advanced": true,
"joinMode": "enrichInput2",
"mergeByFields": {
"values": [
{
"field1": "workflowId",
"field2": "workflowId"
}
]
}
},
"typeVersion": 3.2
},
{
"id": "8e131881-f7ab-46cf-9ebc-f006a819d66d",
"name": "编辑字段 ID 为 workflowId",
"type": "n8n-nodes-base.set",
"position": [
2160,
-160
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "474e54af-e79e-4d58-8c11-dbd920f0511c",
"name": "workflowId",
"type": "string",
"value": "={{ $json.id }}"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "12a17b24-b3c7-4d40-8a03-3b84c932521b",
"name": "筛选上周的执行记录",
"type": "n8n-nodes-base.filter",
"position": [
2896,
0
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "31745f1d-793a-4674-80ab-77afede449d6",
"operator": {
"type": "dateTime",
"operation": "after"
},
"leftValue": "={{ $json.startedAt }}",
"rightValue": "={{ DateTime.now().minus({ days: 7 }) }}"
}
]
}
},
"typeVersion": 2,
"alwaysOutputData": false
},
{
"id": "2e18c677-62ae-4228-9aeb-ba665f8d05c8",
"name": "准备 HTML 报告",
"type": "n8n-nodes-base.code",
"position": [
3376,
0
],
"parameters": {
"jsCode": "// Calculate the date range (7 days ago to now)\nconst now = new Date();\nconst sevenDaysAgo = new Date(now.getTime() - (7 * 24 * 60 * 60 * 1000));\n\n// Group executions by workflow\nconst workflowStats = {};\n\nfor (const item of $input.all()) {\n const workflowId = item.json.workflowId;\n const workflowName = item.json.name;\n const status = item.json.status;\n const startedAt = new Date(item.json.startedAt);\n const stoppedAt = item.json.stoppedAt ? new Date(item.json.stoppedAt) : null;\n \n // Calculate runtime in seconds\n let runtime = 0;\n if (stoppedAt) {\n runtime = (stoppedAt - startedAt) / 1000; // Convert to seconds\n }\n \n // Initialize workflow stats if not exists\n if (!workflowStats[workflowId]) {\n workflowStats[workflowId] = {\n name: workflowName,\n id: workflowId,\n error: { count: 0, totalRuntime: 0 },\n success: { count: 0, totalRuntime: 0 },\n waiting: { count: 0, totalRuntime: 0 }\n };\n }\n \n // Update counts and runtime based on status\n if (status === 'error') {\n workflowStats[workflowId].error.count++;\n workflowStats[workflowId].error.totalRuntime += runtime;\n } else if (status === 'success') {\n workflowStats[workflowId].success.count++;\n workflowStats[workflowId].success.totalRuntime += runtime;\n } else if (status === 'waiting') {\n workflowStats[workflowId].waiting.count++;\n workflowStats[workflowId].waiting.totalRuntime += runtime;\n }\n}\n\n// Helper function to format date (without time)\nfunction formatDate(date) {\n const options = { \n year: 'numeric', \n month: 'long', \n day: 'numeric'\n };\n return date.toLocaleDateString('en-US', options);\n}\n\n// Helper function to format runtime\nfunction formatRuntime(seconds) {\n if (seconds < 60) {\n return `${seconds.toFixed(2)}s`;\n } else if (seconds < 3600) {\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = (seconds % 60).toFixed(0);\n return `${minutes}m ${remainingSeconds}s`;\n } else {\n const hours = Math.floor(seconds / 3600);\n const minutes = Math.floor((seconds % 3600) / 60);\n return `${hours}h ${minutes}m`;\n }\n}\n\n// Format date range for header and subject (7 days ago to now)\nconst fromDate = formatDate(sevenDaysAgo);\nconst toDate = formatDate(now);\nconst dateRangeText = `${fromDate} - ${toDate}`;\nconst subject = `n8n Execution Report ${fromDate} - ${toDate}`;\n\n// Build HTML report\nlet html = `\n<!DOCTYPE html>\n<html>\n<head>\n <style>\n body {\n font-family: Arial, sans-serif;\n margin: 20px;\n background-color: #f5f5f5;\n }\n .header {\n background-color: white;\n padding: 20px;\n margin-bottom: 20px;\n border-radius: 8px;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n }\n h1 {\n color: #333;\n margin: 0 0 10px 0;\n }\n .date-range {\n color: #666;\n font-size: 1.1em;\n margin: 10px 0;\n }\n .summary {\n display: flex;\n gap: 20px;\n margin-top: 15px;\n }\n .summary-item {\n padding: 10px 15px;\n border-radius: 4px;\n font-weight: bold;\n }\n .summary-error {\n background-color: #ffebee;\n color: #c62828;\n }\n .summary-success {\n background-color: #e8f5e9;\n color: #2e7d32;\n }\n .summary-waiting {\n background-color: #fff3e0;\n color: #ef6c00;\n }\n table {\n width: 100%;\n border-collapse: collapse;\n background-color: white;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n border-radius: 8px;\n overflow: hidden;\n }\n th {\n background-color: #4CAF50;\n color: white;\n padding: 12px;\n text-align: left;\n font-weight: bold;\n }\n td {\n padding: 10px 12px;\n border-bottom: 1px solid #ddd;\n }\n tr:hover {\n background-color: #f5f5f5;\n }\n .workflow-name {\n font-weight: bold;\n color: #333;\n }\n .workflow-id {\n color: #666;\n font-size: 0.9em;\n display: block;\n margin-top: 4px;\n }\n .status-cell {\n text-align: center;\n }\n .status-badge {\n display: inline-block;\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 0.85em;\n font-weight: bold;\n margin-bottom: 4px;\n }\n .error { background-color: #ffebee; color: #c62828; }\n .success { background-color: #e8f5e9; color: #2e7d32; }\n .waiting { background-color: #fff3e0; color: #ef6c00; }\n .runtime {\n font-size: 0.8em;\n color: #666;\n display: block;\n margin-top: 2px;\n }\n .no-data {\n color: #999;\n font-style: italic;\n }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <h1>n8n Execution Report</h1>\n <div class=\"date-range\">\n <strong>Period:</strong> ${dateRangeText}\n </div>\n`;\n\n// Calculate totals for summary\nlet totalError = 0;\nlet totalSuccess = 0;\nlet totalWaiting = 0;\n\nfor (const workflowId in workflowStats) {\n const stats = workflowStats[workflowId];\n totalError += stats.error.count;\n totalSuccess += stats.success.count;\n totalWaiting += stats.waiting.count;\n}\n\nhtml += `\n <div class=\"summary\">\n <div class=\"summary-item summary-error\">\n ✗ ${totalError} Errors\n </div>\n <div class=\"summary-item summary-success\">\n ✓ ${totalSuccess} Success\n </div>\n <div class=\"summary-item summary-waiting\">\n ⏳ ${totalWaiting} Waiting\n </div>\n </div>\n </div>\n \n <table>\n <thead>\n <tr>\n <th>Workflow</th>\n <th class=\"status-cell\">Error</th>\n <th class=\"status-cell\">Success</th>\n <th class=\"status-cell\">Waiting</th>\n </tr>\n </thead>\n <tbody>\n`;\n\n// Add rows for each workflow\nfor (const workflowId in workflowStats) {\n const stats = workflowStats[workflowId];\n \n html += `\n <tr>\n <td>\n <span class=\"workflow-name\">${stats.name}</span>\n <span class=\"workflow-id\">${stats.id}</span>\n </td>\n `;\n \n // Error column\n html += '<td class=\"status-cell\">';\n if (stats.error.count > 0) {\n const avgRuntime = stats.error.totalRuntime / stats.error.count;\n html += `\n <span class=\"status-badge error\">✗ ${stats.error.count}</span>\n <span class=\"runtime\">Avg: ${formatRuntime(avgRuntime)}</span>\n <span class=\"runtime\">Total: ${formatRuntime(stats.error.totalRuntime)}</span>\n `;\n } else {\n html += '<span class=\"no-data\">-</span>';\n }\n html += '</td>';\n \n // Success column\n html += '<td class=\"status-cell\">';\n if (stats.success.count > 0) {\n const avgRuntime = stats.success.totalRuntime / stats.success.count;\n html += `\n <span class=\"status-badge success\">✓ ${stats.success.count}</span>\n <span class=\"runtime\">Avg: ${formatRuntime(avgRuntime)}</span>\n <span class=\"runtime\">Total: ${formatRuntime(stats.success.totalRuntime)}</span>\n `;\n } else {\n html += '<span class=\"no-data\">-</span>';\n }\n html += '</td>';\n \n // Waiting column\n html += '<td class=\"status-cell\">';\n if (stats.waiting.count > 0) {\n const avgRuntime = stats.waiting.totalRuntime / stats.waiting.count;\n html += `\n <span class=\"status-badge waiting\">⏳ ${stats.waiting.count}</span>\n <span class=\"runtime\">Avg: ${formatRuntime(avgRuntime)}</span>\n <span class=\"runtime\">Total: ${formatRuntime(stats.waiting.totalRuntime)}</span>\n `;\n } else {\n html += '<span class=\"no-data\">-</span>';\n }\n html += '</td>';\n \n html += '</tr>';\n}\n\nhtml += `\n </tbody>\n </table>\n</body>\n</html>\n`;\n\nreturn [{ json: { html: html, subject: subject } }];"
},
"typeVersion": 2
},
{
"id": "4b1154e2-76bc-4b8c-a2b6-f4338a3c3f10",
"name": "通过 Gmail 发送消息",
"type": "n8n-nodes-base.gmail",
"position": [
3792,
-176
],
"webhookId": "c0581cd3-0b50-4ed9-844d-2e4c7af1b30a",
"parameters": {
"message": "={{ $json.html }}",
"options": {},
"subject": "={{ $json.subject }}"
},
"typeVersion": 2.1
},
{
"id": "2946c6b2-f93f-4e31-bca1-14995de72e7d",
"name": "通过 Outlook 发送消息",
"type": "n8n-nodes-base.microsoftOutlook",
"position": [
3792,
96
],
"webhookId": "87fa1111-29bb-471d-b68f-ae35f785dc6f",
"parameters": {
"subject": "={{ $json.subject }}",
"bodyContent": "={{ $json.html }}",
"additionalFields": {
"bodyContentType": "html"
}
},
"typeVersion": 2
},
{
"id": "0faa8265-c32e-479f-873d-c603722afe12",
"name": "便签 - 概述",
"type": "n8n-nodes-base.stickyNote",
"position": [
224,
-160
],
"parameters": {
"color": 3,
"width": 600,
"height": 1020,
"content": "## 记录 n8n 执行情况的工作流概览"
},
"typeVersion": 1
},
{
"id": "595171e1-c6b0-48b8-bf86-86ffb155e118",
"name": "便签 - 发送 Gmail 消息",
"type": "n8n-nodes-base.stickyNote",
"position": [
3664,
-1056
],
"parameters": {
"color": 7,
"width": 380,
"height": 860,
"content": "## 📧 发送 Gmail 消息"
},
"typeVersion": 1
},
{
"id": "7a12b760-b67c-47ff-995c-6ac53194a036",
"name": "便签 - 发送 Outlook 消息",
"type": "n8n-nodes-base.stickyNote",
"position": [
3680,
304
],
"parameters": {
"color": 7,
"width": 380,
"height": 1020,
"content": "## 📩 发送 Outlook 消息"
},
"typeVersion": 1
},
{
"id": "a85a215d-c1ef-4695-b965-9d3a56cfd9b1",
"name": "便签 - 准备 HTML 报告",
"type": "n8n-nodes-base.stickyNote",
"position": [
3248,
304
],
"parameters": {
"color": 7,
"width": 380,
"height": 1020,
"content": "## 📁 准备 HTML 报告"
},
"typeVersion": 1
},
{
"id": "d134b4f0-1fbc-404c-9edc-9f2884824226",
"name": "便签 - 过滤上周执行",
"type": "n8n-nodes-base.stickyNote",
"position": [
2848,
304
],
"parameters": {
"color": 7,
"width": 380,
"height": 1020,
"content": "## 过滤上周执行"
},
"typeVersion": 1
},
{
"id": "d6d8301c-b2f7-4e8b-b3ed-f32edd84c92b",
"name": "便签 - 合并",
"type": "n8n-nodes-base.stickyNote",
"position": [
2448,
304
],
"parameters": {
"color": 7,
"width": 380,
"height": 1020,
"content": "## 合并"
},
"typeVersion": 1
},
{
"id": "95f22f9d-86fa-4cc3-aeba-38d90320ca29",
"name": "便签 - 编辑字段 ID 为 workflowId",
"type": "n8n-nodes-base.stickyNote",
"position": [
2064,
304
],
"parameters": {
"color": 7,
"width": 380,
"height": 1020,
"content": "## 编辑字段 ID 为 workflowId"
},
"typeVersion": 1
},
{
"id": "9be4bac7-d68a-47d8-87c5-99df34830f13",
"name": "便签 - 获取所有先前执行",
"type": "n8n-nodes-base.stickyNote",
"position": [
1664,
304
],
"parameters": {
"color": 7,
"width": 380,
"height": 1020,
"content": "## 获取所有先前执行"
},
"typeVersion": 1
},
{
"id": "9946f889-e6c4-46e0-bd6e-b5fe41a6e991",
"name": "便签 - 获取所有工作流",
"type": "n8n-nodes-base.stickyNote",
"position": [
1264,
304
],
"parameters": {
"color": 7,
"width": 380,
"height": 1020,
"content": "## 获取所有工作流"
},
"typeVersion": 1
},
{
"id": "27a53d26-b49c-41f9-9cde-49c81e3a5454",
"name": "便签 - 计划触发器",
"type": "n8n-nodes-base.stickyNote",
"position": [
864,
304
],
"parameters": {
"color": 7,
"width": 380,
"height": 1020,
"content": "## 🗓️ 计划触发器"
},
"typeVersion": 1
}
],
"pinData": {},
"connections": {
"Merge": {
"main": [
[
{
"node": "Filter executions last week",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "Get all previous executions",
"type": "main",
"index": 0
},
{
"node": "Get all Workflows",
"type": "main",
"index": 0
}
]
]
},
"Get all Workflows": {
"main": [
[
{
"node": "Edit Field ID to workflowId",
"type": "main",
"index": 0
}
]
]
},
"Prepare html report": {
"main": [
[
{
"node": "Send a message outlook",
"type": "main",
"index": 0
},
{
"node": "Send a message gmail",
"type": "main",
"index": 0
}
]
]
},
"Edit Field ID to workflowId": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"Filter executions last week": {
"main": [
[
{
"node": "Prepare html report",
"type": "main",
"index": 0
}
]
]
},
"Get all previous executions": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
高级 - 开发运维
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
备份n8n工作流到OneDrive
自动备份n8n工作流到OneDrive,包含清理和邮件通知功能
N8n
Code
Merge
+7
29 节点Wessel Bulte
开发运维
将您的工作流保存到 GitHub 仓库
每日工作流备份至 GitHub 并发送 Slack 通知
If
N8n
Set
+10
18 节点Andrew
开发运维
GitHub 同步仪表板 - V2
具有提交历史和回滚功能的 GitHub 工作流版本控制仪表板
If
N8n
Set
+20
94 节点Eduard
开发运维
使用 TenderNed 自动化荷兰公共采购数据收集
使用 TenderNed 自动化荷兰公共采购数据收集
Xml
Code
Merge
+9
28 节点Wessel Bulte
市场调研
高级 n8n 工作流与 GitHub 同步
使用 GitHub 的智能变更检测自动化工作流备份
If
N8n
Set
+10
38 节点Maksym Brashenko
开发运维
自动化n8n工作流备份至GitHub并追踪删除
自动化n8n工作流备份至GitHub并追踪删除
If
N8n
Set
+13
31 节点Marcial Ambriz
开发运维
工作流信息
难度等级
高级
节点数量19
分类1
节点类型9
作者
Wessel Bulte
@uuesselCybersecurity and automation consultant specializing in n8n workflows for GDPR compliance, process optimization, and business integration. Helping teams streamline operations with secure, scalable automation solutions.
外部链接
在 n8n.io 查看 →
分享此工作流