通过Slack和Email监控和报告Monday.com和Jira的OKR差异

高级

这是一个自动化工作流,包含 25 个节点。主要使用 Set、Code、Jira、Merge、Slack 等节点。 通过Slack和Email监控和报告Monday.com与Jira的OKR差异

前置要求
  • Slack Bot Token 或 Webhook URL

分类

未分类
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "BwSY1V0DCKKvUUeq",
  "meta": {
    "instanceId": "8443f10082278c46aa5cf3acf8ff0f70061a2c58bce76efac814b16290845177",
    "templateCredsSetupCompleted": true
  },
  "name": "通过Slack和Email监控和报告Monday.com和Jira的OKR差异",
  "tags": [],
  "nodes": [
    {
      "id": "c13ed242-024f-40c5-ba72-488ebbd23236",
      "name": "工作流概览",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2240,
        -16
      ],
      "parameters": {
        "color": 4,
        "width": 386.39887445887445,
        "height": 464.8876404494382,
        "content": "## 🎯 OKR同步与差异跟踪系统"
      },
      "typeVersion": 1
    },
    {
      "id": "c403d5d0-9c79-43f4-affc-bee172972745",
      "name": "注意:计划设置",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1808,
        -128
      ],
      "parameters": {
        "color": 7,
        "width": 313,
        "height": 297,
        "content": "## ⏰ 计划配置"
      },
      "typeVersion": 1
    },
    {
      "id": "bb79b478-58ce-407c-ae29-1f8139aa5129",
      "name": "每日OKR同步触发器",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1600,
        208
      ],
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "3ebd5103-a47b-4b06-8786-9cf757e0b149",
      "name": "注意:Monday看板配置",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1472,
        -224
      ],
      "parameters": {
        "color": 7,
        "width": 330,
        "height": 416,
        "content": "## 📋 Monday.com看板设置"
      },
      "typeVersion": 1
    },
    {
      "id": "966a0ad7-2f85-44bc-9c69-40fdf833473a",
      "name": "从Monday.com获取OKR",
      "type": "n8n-nodes-base.mondayCom",
      "position": [
        -1376,
        208
      ],
      "parameters": {
        "boardId": "={{ $env.MONDAY_BOARD_ID }}",
        "groupId": "={{ $env.MONDAY_GROUP_ID }}",
        "resource": "boardItem",
        "operation": "getAll",
        "returnAll": true
      },
      "credentials": {
        "mondayComApi": {
          "id": "v9QkK1x0MHK2ULvk",
          "name": "Monday.com account ch"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "dbde1957-fd81-496f-884c-7e72a54c95b3",
      "name": "注意:字段映射逻辑",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1264,
        384
      ],
      "parameters": {
        "color": 7,
        "width": 329.4096385542169,
        "height": 294.3975903614458,
        "content": "## 🔄 字段映射"
      },
      "typeVersion": 1
    },
    {
      "id": "f37d5fa2-af93-48ea-b087-b8b21ee268f9",
      "name": "映射Monday字段 → 标准KR模式",
      "type": "n8n-nodes-base.set",
      "position": [
        -1152,
        208
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "5c6f16b4-1a4d-433c-a71f-e0af839639a3",
              "name": "id",
              "type": "string",
              "value": "={{ $json.id }}"
            },
            {
              "id": "91918ab5-08d3-48a1-86bb-d09ba12e4c97",
              "name": "name",
              "type": "string",
              "value": "={{ $json.name }}"
            },
            {
              "id": "f9f404d8-4991-41a3-a695-9b8870b94c34",
              "name": "created_at",
              "type": "string",
              "value": "={{ $json.created_at }}"
            },
            {
              "id": "d68a0661-a879-44c7-b4bd-841529559c82",
              "name": "Objective Name ",
              "type": "string",
              "value": "={{ $json.column_values[0].text }}"
            },
            {
              "id": "7b03a663-d59c-4cd1-b355-713a24f033f2",
              "name": "Jira Epic Keys",
              "type": "string",
              "value": "={{ $json.column_values[1].text }}"
            },
            {
              "id": "072ba34c-fedd-434d-97cf-0c9075d5e6f9",
              "name": "Target Progress",
              "type": "string",
              "value": "={{ $json.column_values[2].text }}"
            },
            {
              "id": "2cc1ca2f-f47f-4851-a12f-465eb6a43851",
              "name": "Current Progress",
              "type": "string",
              "value": "={{ $json.column_values[3].text }}"
            },
            {
              "id": "fa86ebe8-3e4b-48bd-a2b7-5ad326b4dedb",
              "name": "Threshold",
              "type": "string",
              "value": "={{ $json.column_values[4].text }}"
            },
            {
              "id": "bb2da321-7351-42f2-b7a0-11ec279b23a9",
              "name": "Owners Email",
              "type": "string",
              "value": "={{ $json.column_values[5].text }}"
            },
            {
              "id": "107f05c0-69f3-43a9-bd60-f0bf5212a54b",
              "name": "Status",
              "type": "string",
              "value": "={{ $json.column_values[6].text }}"
            },
            {
              "id": "080e6adf-80d4-46a9-a11f-358e65fd37e1",
              "name": "Date",
              "type": "string",
              "value": "={{ $json.column_values[7].text }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "51ce7b43-0b86-413e-80c7-e5762a33ab46",
      "name": "注意:史诗拆分",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1024,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 324.2168674698795,
        "height": 294.3975903614458,
        "content": "## 🔀 史诗拆分逻辑"
      },
      "typeVersion": 1
    },
    {
      "id": "263d82ad-50d6-4632-877a-034975d5729b",
      "name": "拆分KR → 史诗映射器",
      "type": "n8n-nodes-base.code",
      "position": [
        -928,
        208
      ],
      "parameters": {
        "jsCode": "const out = [];\nfor (const item of items) {\n  const epics = item.json[\"Jira Epic Keys\"]\n    .split(',')\n    .map(e => e.trim())\n    .filter(Boolean);\n  for (const epic of epics) {\n    out.push({\n      json: {\n        kr_id: item.json.id,\n        kr_name: item.json.name,\n        objective_name: item.json[\"Objective Name\"],\n        epic_key: epic,\n        target_progress: Number(item.json[\"Target Progress\"]),\n        current_progress: Number(item.json[\"Current Progress\"]),\n        threshold: Number(item.json[\"Threshold\"]),\n        owner_email: item.json[\"Owners Email\"]\n      }\n    });\n  }\n}\nreturn out;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "c07d2061-b07c-4a1c-868b-edc013cacf90",
      "name": "注意:Jira史诗获取",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -896,
        416
      ],
      "parameters": {
        "color": 7,
        "width": 324.2168674698795,
        "height": 280,
        "content": "## 🔗 Jira集成"
      },
      "typeVersion": 1
    },
    {
      "id": "0411be4a-6381-4f13-9808-57a2f7b07644",
      "name": "获取Jira史诗详情",
      "type": "n8n-nodes-base.jira",
      "position": [
        -704,
        272
      ],
      "parameters": {
        "issueKey": "={{ $json.epic_key }}",
        "operation": "get",
        "additionalFields": {}
      },
      "credentials": {
        "jiraSoftwareCloudApi": {
          "id": "Q6d7sLBVOfGWmaLw",
          "name": "Jira SW Cloud account vivek"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4992f272-d055-41d4-958c-b9691e3ebb96",
      "name": "注意:Jira响应清理",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -560,
        400
      ],
      "parameters": {
        "color": 7,
        "width": 324.2168674698795,
        "height": 280,
        "content": "## 🧹 响应标准化"
      },
      "typeVersion": 1
    },
    {
      "id": "9de61cfd-ba27-4023-8528-3ba279502d8f",
      "name": "标准化Jira响应",
      "type": "n8n-nodes-base.code",
      "position": [
        -480,
        272
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\n\nconst formatted = items.flatMap(item => {\n  const data = item.json;\n  const issues = Array.isArray(data) ? data : [data];\n\n  return issues.map(issue => {\n    const f = issue.fields;\n    return {\n      json: {\n        key: issue.key,\n        name: f.summary,\n        status: f.status.name,\n        assignee: f.assignee ? f.assignee.displayName : 'Unassigned',\n        dueDate: f.duedate || 'N/A',\n        created: f.created,\n        updated: f.updated,\n        description: f.description || 'No description'\n      }\n    };\n  });\n});\n\nreturn formatted;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "d0de1271-2a54-4a4c-9650-b84e1c3193ac",
      "name": "注意:数据合并",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -384,
        -80
      ],
      "parameters": {
        "color": 7,
        "width": 324.2168674698795,
        "height": 280,
        "content": "## 🔗 SQL合并策略"
      },
      "typeVersion": 1
    },
    {
      "id": "ba395420-aa63-4277-8d7d-fb02c9d978b2",
      "name": "连接KR + 史诗数据(SQL合并)",
      "type": "n8n-nodes-base.merge",
      "position": [
        -256,
        208
      ],
      "parameters": {
        "mode": "combineBySql",
        "query": "SELECT \n  a.kr_id,\n  a.kr_name,\n  a.epic_key,\n  a.target_progress,\n  a.current_progress,\n  a.threshold,\n  a.owner_email,\n  b.name AS epic_name,\n  b.status AS epic_status,\n  b.assignee AS epic_assignee,\n  b.updated AS epic_updated\nFROM input1 a\nLEFT JOIN input2 b\nON a.epic_key = b.key\n",
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "5316f900-d4b5-4543-b407-449434ea15d3",
      "name": "注意:差异计算",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -48,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 324.2168674698795,
        "height": 320,
        "content": "## 📊 进度计算逻辑"
      },
      "typeVersion": 1
    },
    {
      "id": "1a5c998f-e607-412f-baa0-361c223260a5",
      "name": "计算KR进度和差异",
      "type": "n8n-nodes-base.code",
      "position": [
        -32,
        208
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst grouped = {};\n\nfor (const i of items) {\n  const krId = i.json.kr_id;\n  if (!grouped[krId]) grouped[krId] = { meta: i.json, epics: [] };\n  grouped[krId].epics.push(i.json);\n}\n\nconst out = [];\n\nfor (const [krId, data] of Object.entries(grouped)) {\n  const { target_progress, threshold, owner_email, kr_name } = data.meta;\n  const epics = data.epics;\n\n  const statusWeight = { \"To Do\": 0, \"In Progress\": 50, \"Done\": 100 };\n  const progressValues = epics.map(e => statusWeight[e.epic_status] ?? 0);\n\n  const actual_progress = progressValues.length\n    ? progressValues.reduce((a, b) => a + b, 0) / progressValues.length\n    : 0;\n\n  const variance = Number((actual_progress - target_progress).toFixed(2));\n  const variance_abs = Math.abs(variance);\n\n  const status =\n    variance_abs > threshold\n      ? variance < 0\n        ? \"At Risk\"\n        : \"Ahead\"\n      : \"On Track\";\n\n  out.push({\n    json: {\n      kr_id: krId,\n      kr_name,\n      owner_email,\n      target_progress,\n      actual_progress: Number(actual_progress.toFixed(2)),\n      variance,\n      variance_abs,\n      status,\n      epic_count: epics.length,\n      last_updated: new Date().toISOString()\n    }\n  });\n}\n\nreturn out;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "e2e5308f-5ba4-4eab-a565-fb2e23dc5c6e",
      "name": "注意:Monday看板更新",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        288,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 324.2168674698795,
        "height": 300,
        "content": "## ✍️ Monday.com更新"
      },
      "typeVersion": 1
    },
    {
      "id": "6c262e7a-b86e-48f0-80bb-419a55d98e73",
      "name": "在Monday上更新KR状态",
      "type": "n8n-nodes-base.mondayCom",
      "position": [
        208,
        96
      ],
      "parameters": {
        "itemId": "={{ $json.kr_id }}",
        "boardId": "={{ $env.MONDAY_BOARD_ID }}",
        "resource": "boardItem",
        "operation": "changeMultipleColumnValues",
        "columnValues": "={\n  \"{{ $env.MONDAY_COL_ACTUAL_PROGRESS }}\": {{$json.actual_progress}},\n  \"{{ $env.MONDAY_COL_VARIANCE }}\": {{$json.variance}},\n  \"{{ $env.MONDAY_COL_STATUS }}\": { \"label\": \"{{$json.status}}\" }\n}\n"
      },
      "credentials": {
        "mondayComApi": {
          "id": "v9QkK1x0MHK2ULvk",
          "name": "Monday.com account ch"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "f961b9b2-5ea4-4566-ad39-db01faa85c1a",
      "name": "注意:数据聚合",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        464
      ],
      "parameters": {
        "color": 7,
        "width": 324.2168674698795,
        "height": 260,
        "content": "## 📦 报告聚合"
      },
      "typeVersion": 1
    },
    {
      "id": "f312abe7-993b-42ee-8a4b-7c30073209b0",
      "name": "聚合最终结果用于报告",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        208,
        304
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData"
      },
      "typeVersion": 1
    },
    {
      "id": "fafc7791-2e28-4a61-a692-37f29b01efd1",
      "name": "注意:Slack配置",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        592,
        224
      ],
      "parameters": {
        "color": 7,
        "width": 324.2168674698795,
        "height": 280,
        "content": "## 💬 Slack通知"
      },
      "typeVersion": 1
    },
    {
      "id": "48a17cb4-e54e-4120-8df9-f196598d4dca",
      "name": "发布Slack差异报告",
      "type": "n8n-nodes-base.slack",
      "position": [
        432,
        208
      ],
      "webhookId": "2e46b987-9ef4-4aaf-a78a-29046574c742",
      "parameters": {
        "text": "=*⚠️ OKR Variance Report — {{$now}}*\n\n{{ $json.data.map((item, i) =>   \n`*${i+1}. ${item.kr_name}*\n👤 *Owner:* ${item.owner_email}\n🎯 *Target:* ${item.target_progress}%\n📊 *Actual:* ${item.actual_progress}%\n📈 *Variance:* ${item.variance}%\n🧮 *Epics Linked:* ${item.epic_count}\n🕒 *Last Updated:* ${item.last_updated}\n*Status:* ${item.status}`\n).join('\\n\\n') }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $env.SLACK_CHANNEL_ID }}"
        },
        "otherOptions": {
          "mrkdwn": true
        }
      },
      "credentials": {
        "slackApi": {
          "id": "rNqvWj9TfChPVRYY",
          "name": "Slack account vivek"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "0e5a6aae-6537-49d7-80de-cf62d6913eed",
      "name": "注意:邮箱配置",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        352,
        592
      ],
      "parameters": {
        "color": 7,
        "width": 324.2168674698795,
        "height": 280,
        "content": "## 📧 邮箱通知"
      },
      "typeVersion": 1
    },
    {
      "id": "550a77ca-7e03-454e-8fa0-76902b6215fe",
      "name": "邮箱差异摘要(Outlook)",
      "type": "n8n-nodes-base.microsoftOutlook",
      "position": [
        432,
        400
      ],
      "webhookId": "90432cba-1978-43c7-b79b-8fc8c733db6f",
      "parameters": {
        "subject": "=OKR Variance Report — {{$now}}",
        "bodyContent": "=<h2 style=\"color:#1263FF; font-family:Arial, sans-serif;\">⚙️ OKR Variance Report — {{$now}}</h2>\n\n{{ $json.data.map((item, i) => `\n  <table width=\"100%\" style=\"border-collapse:collapse; margin-bottom:15px; border:1px solid #dcdcdc; border-radius:8px; font-family:Arial, sans-serif;\">\n    <tr>\n      <td style=\"padding:10px;\">\n        <h3 style=\"margin:0; color:#333333; font-size:16px;\">${i+1}. ${item.kr_name}</h3>\n        <p style=\"margin:4px 0;\"><strong>Owner:</strong> ${item.owner_email}</p>\n        <p style=\"margin:4px 0;\"><strong>Target:</strong> ${item.target_progress}%</p>\n        <p style=\"margin:4px 0;\"><strong>Actual:</strong> ${item.actual_progress}%</p>\n        <p style=\"margin:4px 0;\"><strong>Variance:</strong> \n          <span style=\"color:${item.variance < 0 ? '#e63946' : '#2a9d8f'}; font-weight:bold;\">\n            ${item.variance}%\n          </span>\n        </p>\n        <p style=\"margin:4px 0;\"><strong>Epics Linked:</strong> ${item.epic_count}</p>\n        <p style=\"margin:4px 0;\"><strong>Status:</strong> ${item.status}</p>\n        <p style=\"margin:4px 0;\"><strong>Last Updated:</strong> ${item.last_updated}</p>\n      </td>\n    </tr>\n  </table>\n`).join('') }}\n",
        "toRecipients": "={{ $env.REPORT_RECIPIENT_EMAIL }}",
        "additionalFields": {
          "bodyContentType": "html"
        }
      },
      "credentials": {
        "microsoftOutlookOAuth2Api": {
          "id": "dVY4LPJeMsLJMtJZ",
          "name": "Microsoft Outlook account"
        }
      },
      "typeVersion": 2
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "24728ac7-8785-428e-b5d2-c70dd6d5c25b",
  "connections": {
    "Daily OKR Sync Trigger": {
      "main": [
        [
          {
            "node": "Fetch OKRs from Monday.com",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Jira Epic Details": {
      "main": [
        [
          {
            "node": "Normalize Jira Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Jira Response": {
      "main": [
        [
          {
            "node": "Join KR + Epic Data (SQL Merge)",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Split KR → Epics Mapper": {
      "main": [
        [
          {
            "node": "Join KR + Epic Data (SQL Merge)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch Jira Epic Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch OKRs from Monday.com": {
      "main": [
        [
          {
            "node": "Map Monday Fields → Standard KR Schema",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compute KR Progress & Variance": {
      "main": [
        [
          {
            "node": "Update KR Status on Monday",
            "type": "main",
            "index": 0
          },
          {
            "node": "Aggregate Final Results for Reporting",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Join KR + Epic Data (SQL Merge)": {
      "main": [
        [
          {
            "node": "Compute KR Progress & Variance",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Final Results for Reporting": {
      "main": [
        [
          {
            "node": "Post Slack Variance Report",
            "type": "main",
            "index": 0
          },
          {
            "node": "Email Variance Digest (Outlook)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Map Monday Fields → Standard KR Schema": {
      "main": [
        [
          {
            "node": "Split KR → Epics Mapper",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

这是一个高级难度的通用自动化工作流。适合高级用户,包含 16+ 个节点的复杂工作流

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
高级
节点数量25
分类-
节点类型10
难度说明

适合高级用户,包含 16+ 个节点的复杂工作流

作者
Rahul Joshi

Rahul Joshi

@rahul08

Rahul Joshi is a seasoned technology leader specializing in the n8n automation tool and AI-driven workflow automation. With deep expertise in building open-source workflow automation and self-hosted automation platforms, he helps organizations eliminate manual processes through intelligent n8n ai agent automation solutions.

外部链接
在 n8n.io 上查看 →

分享此工作流