8
n8n 中文网amn8n.com

会议纪要和行动项跟踪器

高级

这是一个Content Creation, Multimodal AI领域的自动化工作流,包含 38 个节点。主要使用 If, Set, Code, Gmail, Merge 等节点。 基于AI的会议纪要:使用GPT-4、任务分配和多渠道分发

前置要求
  • Google 账号和 Gmail API 凭证
  • Slack Bot Token 或 Webhook URL
  • HTTP Webhook 端点(n8n 会自动生成)
  • Google Drive API 凭证
  • 可能需要目标 API 的认证凭证
  • OpenAI API Key
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "MU89lcZkW3CjD9nU",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "会议纪要和行动项跟踪器",
  "tags": [],
  "nodes": [
    {
      "id": "022280e9-fc0f-4148-827f-14143ba4c11a",
      "name": "接收会议数据",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -160,
        0
      ],
      "webhookId": "9e0d22e1-8b66-4438-a6b5-4748a168cc11",
      "parameters": {
        "path": "meeting-minutes",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "lastNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "81191e4b-f548-46b1-b8ca-8560653a33ab",
      "name": "提取会议元数据",
      "type": "n8n-nodes-base.set",
      "position": [
        112,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "1ccc5df3-8c52-4ac8-a4b9-19e037022817",
              "name": "meetingTitle",
              "type": "string",
              "value": "={{ $json.body.title|| \"Untitled Meeting\" }}"
            },
            {
              "id": "ca5a8fed-69dc-4c24-85d5-d6860f192b05",
              "name": "meetingDate",
              "type": "string",
              "value": "={{ $json.body.date  || $now.format('yyyy-MM-dd') }}"
            },
            {
              "id": "f44440f2-80ab-4f1e-a79d-8d4d5b347703",
              "name": "participants",
              "type": "array",
              "value": "={{$json.body.participants || [] }}"
            },
            {
              "id": "46dff8c2-8c56-4d9c-9508-b9a280fca602",
              "name": "duration",
              "type": "string",
              "value": "={{ $json.duration || \"Not specified\" }}"
            },
            {
              "id": "87b3fa3a-a5fd-4cf9-8436-7a5a0870a15a",
              "name": "rawTranscript",
              "type": "string",
              "value": "={{ $json.transcript || $json.body.transcript || \"\" }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "1d1d508e-f9b2-4175-83fc-3ab883990d8b",
      "name": "清理转录文本",
      "type": "n8n-nodes-base.code",
      "position": [
        416,
        0
      ],
      "parameters": {
        "jsCode": "// Get the input data\nconst items = $input.all();\n\n// Process each item\nconst processedItems = items.map(item => {\n  const data = item.json;\n  \n  // Clean the transcript\n  let cleanedTranscript = data.rawTranscript || '';\n  \n  // Remove multiple spaces\n  cleanedTranscript = cleanedTranscript.replace(/\\s+/g, ' ');\n  \n  // Remove common timestamp patterns (e.g., [00:01:23] or (00:01:23))\n  cleanedTranscript = cleanedTranscript.replace(/[\\[\\(]\\d{2}:\\d{2}:\\d{2}[\\]\\)]/g, '');\n  \n  // Remove speaker labels if formatted as \"Speaker 1:\", \"Speaker 2:\", etc.\n  cleanedTranscript = cleanedTranscript.replace(/Speaker \\d+:/g, '');\n  \n  // Trim whitespace\n  cleanedTranscript = cleanedTranscript.trim();\n  \n  // Count words for context\n  const wordCount = cleanedTranscript.split(/\\s+/).length;\n  \n  // Estimate reading time (average 200 words per minute)\n  const estimatedMinutes = Math.ceil(wordCount / 200);\n  \n  return {\n    json: {\n      ...data,\n      cleanedTranscript: cleanedTranscript,\n      transcriptWordCount: wordCount,\n      estimatedReadingTime: `${estimatedMinutes} minutes`,\n      processingTimestamp: new Date().toISOString()\n    }\n  };\n});\n\nreturn processedItems;"
      },
      "typeVersion": 2
    },
    {
      "id": "2f05b28e-8aab-4f5f-b6ec-a61e96ec12de",
      "name": "验证转录文本长度",
      "type": "n8n-nodes-base.if",
      "position": [
        720,
        0
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "296bb329-fa1e-4660-9976-5462a0f3ae21",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.cleanedTranscript }}",
              "rightValue": ""
            },
            {
              "id": "c754bcd2-dd4e-4c2b-b3ae-a024c71bf6de",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json.transcriptWordCount }}",
              "rightValue": 50
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "8ce8541c-951c-4c43-9471-3c55f8fe0be1",
      "name": "生成会议摘要",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        960,
        -128
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4",
          "cachedResultName": "GPT-4"
        },
        "options": {
          "topP": 1,
          "maxTokens": 1000,
          "temperature": 0.3,
          "presence_penalty": 0,
          "frequency_penalty": 0
        },
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "=You are an expert executive assistant specializing in creating professional meeting summaries. \n\nYour summaries should:\n1. Be concise but comprehensive (200-300 words)\n2. Highlight key discussion points in order of importance\n3. Note important decisions made during the meeting\n4. Capture the overall meeting context and objectives\n5. Use clear, professional business language\n6. Organize information logically with smooth flow\n\nFormat your summary in 3-4 clear paragraphs, NOT bullet points. Write in third person (e.g., \"The team discussed...\" not \"We discussed...\")."
            },
            {
              "content": "=Create a professional meeting summary for the following:\n\nMeeting Title: {{ $json.meetingTitle }}\nDate: {{ $json.meetingDate }}\nDuration: {{ $json.duration }}\nParticipants: {{ $json.participants.join(', ') }}\n\nTranscript:\n{{ $json.cleanedTranscript }}\n\nGenerate a comprehensive summary that captures the essence of this meeting."
            }
          ]
        }
      },
      "credentials": {
        "openAiApi": {
          "id": "5n856jvI80lSEErZ",
          "name": "Klinsman OpenAI"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "89d1da2f-2eb8-4c87-b10d-74cccf6c0c2e",
      "name": "提取行动项",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        960,
        128
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4",
          "cachedResultName": "GPT-4"
        },
        "options": {
          "maxTokens": 1500,
          "temperature": 0.2
        },
        "messages": {
          "values": [
            {
              "content": "=Extract all action items from this meeting transcript:\n\nMeeting Title: {{ $json.meetingTitle }}\nMeeting Date: {{ $json.meetingDate }}\n\nTranscript:\n{{ $json.cleanedTranscript }}\n\nReturn ONLY the JSON object with the action_items array. Do not include any other text, markdown formatting, or code blocks."
            },
            {
              "role": "system",
              "content": "=You are an expert at extracting action items from meeting transcripts. \n\nFor each action item, identify:\n1. **task**: Clear, actionable description of what needs to be done\n2. **assignee**: Person responsible (use their exact name from transcript)\n3. **deadline**: Due date if mentioned (format: YYYY-MM-DD), or null if not specified\n4. **priority**: Either 'urgent', 'high', 'normal', or 'low' based on context\n5. **context**: Brief note about why this task matters (1 sentence)\n\nRules:\n- Only extract clear, actionable tasks (not discussion points or general notes)\n- If no assignee is mentioned, set to \"Unassigned\"\n- Detect urgency from keywords: \"ASAP\", \"urgent\", \"immediately\", \"critical\", \"by end of day\"\n- If deadline isn't specific, infer from context clues (e.g., \"next week\" = meeting date + 7 days)\n- If someone says \"I'll do X\", that person is the assignee for X\n\nReturn ONLY a valid JSON object (no markdown, no code blocks) in this exact format:\n{\n  \"action_items\": [\n    {\n      \"task\": \"string\",\n      \"assignee\": \"string\",\n      \"deadline\": \"YYYY-MM-DD or null\",\n      \"priority\": \"urgent|high|normal|low\",\n      \"context\": \"string\"\n    }\n  ]\n}"
            }
          ]
        }
      },
      "credentials": {
        "openAiApi": {
          "id": "5n856jvI80lSEErZ",
          "name": "Klinsman OpenAI"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "d5564407-0f3b-4128-aa3a-3db3f6b271a5",
      "name": "合并 AI 响应",
      "type": "n8n-nodes-base.merge",
      "position": [
        1360,
        0
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "9a4f0d20-75e6-4c83-8cf0-4d374485bc03",
      "name": "解析和丰富数据",
      "type": "n8n-nodes-base.code",
      "position": [
        1632,
        0
      ],
      "parameters": {
        "jsCode": "// Get ALL items from Merge (Append gives us an array of items)\nconst items = $input.all();\n\nconsole.log('=== STARTING PARSE ===');\nconsole.log('Total items:', items.length);\n\n// Get metadata from earlier nodes\nlet codeData = {};\ntry {\n  codeData = $('Clean Transcript').first().json;\n  console.log('✓ Got metadata from Code node');\n} catch (error) {\n  console.log('✗ Could not access Code node:', error.message);\n}\n\n// Initialize with metadata\nlet meetingData = {\n  meetingTitle: codeData.meetingTitle || 'Untitled Meeting',\n  meetingDate: codeData.meetingDate || new Date().toISOString().split('T')[0],\n  participants: codeData.participants || [],\n  duration: codeData.duration || 'Not specified',\n  rawTranscript: codeData.rawTranscript || '',\n  cleanedTranscript: codeData.cleanedTranscript || '',\n  transcriptWordCount: codeData.transcriptWordCount || 0,\n  estimatedReadingTime: codeData.estimatedReadingTime || 'Unknown',\n  processingTimestamp: codeData.processingTimestamp || new Date().toISOString(),\n  summary: '',\n  actionItems: []\n};\n\nconsole.log('\\n=== PROCESSING', items.length, 'OPENAI RESPONSES ===');\n\n// Process each item from Merge\nitems.forEach((item, index) => {\n  console.log(`\\nItem ${index}:`);\n  \n  const data = item.json;\n  \n  // OpenAI node returns: { message: { role, content } }\n  if (data.message && data.message.content) {\n    const content = data.message.content;\n    console.log('  Content length:', content.length);\n    console.log('  Preview:', content.substring(0, 80));\n    \n    // Check if action items JSON\n    if (content.includes('\"action_items\"')) {\n      console.log('  → ACTION ITEMS');\n      try {\n        const parsed = JSON.parse(content);\n        meetingData.actionItems = parsed.action_items || [];\n        console.log('  ✓ Parsed:', meetingData.actionItems.length, 'items');\n      } catch (error) {\n        console.error('  ✗ Parse error:', error.message);\n      }\n    } else {\n      console.log('  → SUMMARY');\n      meetingData.summary = content.trim();\n      console.log('  ✓ Length:', meetingData.summary.length);\n    }\n  }\n});\n\nconsole.log('\\n=== RESULTS ===');\nconsole.log('Summary:', meetingData.summary ? `${meetingData.summary.length} chars` : 'EMPTY');\nconsole.log('Action Items:', meetingData.actionItems.length);\n\n// Enrich action items\nmeetingData.actionItems = meetingData.actionItems.map((item, index) => {\n  let formattedDeadline = 'TBD';\n  let daysUntil = null;\n  let deadlineStatus = 'no-deadline';\n  \n  if (item.deadline && item.deadline !== 'null' && item.deadline !== null) {\n    try {\n      const deadlineDate = new Date(item.deadline);\n      if (!isNaN(deadlineDate.getTime())) {\n        formattedDeadline = deadlineDate.toISOString().split('T')[0];\n        const today = new Date();\n        today.setHours(0, 0, 0, 0);\n        deadlineDate.setHours(0, 0, 0, 0);\n        daysUntil = Math.ceil((deadlineDate - today) / (1000 * 60 * 60 * 24));\n        \n        if (daysUntil < 0) deadlineStatus = 'overdue';\n        else if (daysUntil === 0) deadlineStatus = 'today';\n        else if (daysUntil <= 3) deadlineStatus = 'urgent';\n        else if (daysUntil <= 7) deadlineStatus = 'upcoming';\n        else deadlineStatus = 'future';\n      }\n    } catch (e) {}\n  }\n  \n  return {\n    id: `AI-${Date.now()}-${index}`,\n    number: index + 1,\n    task: item.task || 'No task description',\n    assignee: item.assignee || 'Unassigned',\n    deadline: formattedDeadline,\n    deadlineOriginal: item.deadline,\n    daysUntilDeadline: daysUntil,\n    deadlineStatus: deadlineStatus,\n    priority: (item.priority || 'normal').toLowerCase(),\n    context: item.context || '',\n    status: 'pending',\n    createdAt: new Date().toISOString(),\n    meetingReference: meetingData.meetingTitle,\n    meetingDate: meetingData.meetingDate\n  };\n});\n\n// Sort\nconst priorityOrder = { urgent: 0, high: 1, normal: 2, low: 3 };\nconst statusOrder = { overdue: 0, today: 1, urgent: 2, upcoming: 3, future: 4, 'no-deadline': 5 };\n\nmeetingData.actionItems.sort((a, b) => {\n  const priorityDiff = priorityOrder[a.priority] - priorityOrder[b.priority];\n  if (priorityDiff !== 0) return priorityDiff;\n  const statusDiff = statusOrder[a.deadlineStatus] - statusOrder[b.deadlineStatus];\n  if (statusDiff !== 0) return statusDiff;\n  if (a.deadline === 'TBD' && b.deadline !== 'TBD') return 1;\n  if (a.deadline !== 'TBD' && b.deadline === 'TBD') return -1;\n  if (a.deadline !== 'TBD' && b.deadline !== 'TBD') {\n    return new Date(a.deadline) - new Date(b.deadline);\n  }\n  return 0;\n});\n\nmeetingData.actionItems = meetingData.actionItems.map((item, index) => ({\n  ...item,\n  number: index + 1\n}));\n\n// Generate metadata\nconst urgentCount = meetingData.actionItems.filter(i => i.priority === 'urgent').length;\nconst highCount = meetingData.actionItems.filter(i => i.priority === 'high').length;\nconst normalCount = meetingData.actionItems.filter(i => i.priority === 'normal').length;\nconst lowCount = meetingData.actionItems.filter(i => i.priority === 'low').length;\nconst assigneeBreakdown = meetingData.actionItems.reduce((acc, item) => {\n  acc[item.assignee] = (acc[item.assignee] || 0) + 1;\n  return acc;\n}, {});\nconst overdueCount = meetingData.actionItems.filter(i => i.deadlineStatus === 'overdue').length;\nconst todayCount = meetingData.actionItems.filter(i => i.deadlineStatus === 'today').length;\nconst urgentDeadlineCount = meetingData.actionItems.filter(i => i.deadlineStatus === 'urgent').length;\nconst upcomingCount = meetingData.actionItems.filter(i => i.deadlineStatus === 'upcoming').length;\n\nmeetingData.summaryMetadata = {\n  generatedAt: new Date().toISOString(),\n  actionItemCount: meetingData.actionItems.length,\n  participantCount: meetingData.participants.length,\n  hasUrgentItems: urgentCount > 0,\n  hasHighPriorityItems: highCount > 0,\n  hasUnassignedItems: meetingData.actionItems.some(i => i.assignee === 'Unassigned'),\n  hasOverdueItems: overdueCount > 0,\n  hasTodayItems: todayCount > 0,\n  priorityBreakdown: { urgent: urgentCount, high: highCount, normal: normalCount, low: lowCount },\n  assigneeBreakdown: assigneeBreakdown,\n  itemsWithDeadlines: meetingData.actionItems.filter(i => i.deadline !== 'TBD').length,\n  itemsWithoutDeadlines: meetingData.actionItems.filter(i => i.deadline === 'TBD').length,\n  deadlineStatus: {\n    overdue: overdueCount,\n    today: todayCount,\n    urgent: urgentDeadlineCount,\n    upcoming: upcomingCount,\n    future: meetingData.actionItems.filter(i => i.deadlineStatus === 'future').length,\n    noDeadline: meetingData.actionItems.filter(i => i.deadlineStatus === 'no-deadline').length\n  },\n  upcomingDeadlines: upcomingCount + urgentDeadlineCount + todayCount,\n  overdueItems: overdueCount\n};\n\nconsole.log('\\n=== COMPLETE ===');\nconsole.log('Meeting:', meetingData.meetingTitle);\nconsole.log('Summary:', meetingData.summary.length, 'chars');\nconsole.log('Action Items:', meetingData.actionItems.length);\n\nreturn [{ json: meetingData }];"
      },
      "typeVersion": 2
    },
    {
      "id": "fc46776e-2b89-4a64-9145-7755101a5138",
      "name": "生成 PDF 文档",
      "type": "n8n-nodes-htmlcsstopdf.htmlcsstopdf",
      "position": [
        2000,
        -144
      ],
      "parameters": {
        "css_content": "=* { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif; line-height: 1.6; color: #1f2937; background: #ffffff; } .container { max-width: 900px; margin: 0 auto; padding: 60px 40px; } .header { border-bottom: 4px solid #3b82f6; padding-bottom: 30px; margin-bottom: 40px; background: linear-gradient(135deg, #3b82f6 0%, #1e40af 100%); padding: 40px; border-radius: 12px 12px 0 0; color: white; margin: -60px -40px 40px -40px; } .header h1 { font-size: 32px; font-weight: 700; margin-bottom: 12px; color: white; } .header .subtitle { font-size: 16px; opacity: 0.95; font-weight: 400; } .meta-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-top: 30px; padding: 25px; background: rgba(255, 255, 255, 0.1); border-radius: 8px; } .meta-item { display: flex; flex-direction: column; } .meta-label { font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px; opacity: 0.8; margin-bottom: 6px; font-weight: 600; } .meta-value { font-size: 15px; font-weight: 500; } .section { margin: 50px 0; } .section-header { display: flex; align-items: center; margin-bottom: 20px; padding-bottom: 12px; border-bottom: 2px solid #e5e7eb; } .section-header h2 { font-size: 24px; color: #1e40af; font-weight: 700; margin: 0; } .section-header .icon { font-size: 28px; margin-right: 12px; } .summary-box { background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%); padding: 30px; border-radius: 10px; line-height: 1.8; font-size: 15px; color: #1f2937; border-left: 5px solid #3b82f6; box-shadow: 0 2px 8px rgba(59, 130, 246, 0.1); } .participants-box { background: #f9fafb; padding: 20px; border-radius: 8px; margin-bottom: 30px; } .participants-label { font-weight: 600; color: #374151; margin-bottom: 12px; font-size: 14px; text-transform: uppercase; letter-spacing: 0.5px; } .participant-tag { background: #f3f4f6; padding: 6px 12px; border-radius: 6px; font-size: 13px; color: #374151; margin-right: 8px; display: inline-block; margin-bottom: 6px; } table { width: 100%; border-collapse: collapse; background: white; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); border-radius: 8px; overflow: hidden; } thead { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); color: white; } th { padding: 18px 12px; text-align: left; font-weight: 600; font-size: 13px; text-transform: uppercase; letter-spacing: 0.5px; } tbody tr { border-bottom: 1px solid #e5e7eb; } tbody tr:hover { background: #f9fafb; } td { padding: 16px 12px; } .task-cell { font-weight: 500; color: #1f2937; margin-bottom: 4px; } .task-context { font-size: 13px; color: #6b7280; font-style: italic; } .assignee-badge { background: #eff6ff; color: #1e40af; padding: 6px 12px; border-radius: 6px; font-size: 13px; font-weight: 500; display: inline-block; } .priority-urgent { background: #ef4444; color: white; padding: 4px 10px; border-radius: 4px; font-size: 11px; font-weight: 600; text-transform: uppercase; } .priority-high { background: #f97316; color: white; padding: 4px 10px; border-radius: 4px; font-size: 11px; font-weight: 600; text-transform: uppercase; } .priority-normal { background: #3b82f6; color: white; padding: 4px 10px; border-radius: 4px; font-size: 11px; font-weight: 600; text-transform: uppercase; } .priority-low { background: #6b7280; color: white; padding: 4px 10px; border-radius: 4px; font-size: 11px; font-weight: 600; text-transform: uppercase; } .deadline-overdue { color: #dc2626; font-weight: 600; } .deadline-today { color: #ea580c; font-weight: 600; } .deadline-urgent { color: #f59e0b; font-weight: 600; } .deadline-upcoming { color: #3b82f6; } .deadline-future { color: #6b7280; } .urgent-banner { background: #fef2f2; border-left: 4px solid #ef4444; padding: 16px 20px; border-radius: 6px; margin-bottom: 20px; display: flex; align-items: center; } .urgent-banner-icon { font-size: 24px; margin-right: 12px; } .urgent-banner-text { color: #991b1b; font-weight: 600; font-size: 14px; } .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 20px; margin-top: 30px; } .stat-card { background: white; padding: 24px; border-radius: 10px; border: 2px solid #e5e7eb; text-align: center; } .stat-number { font-size: 36px; font-weight: 700; color: #3b82f6; margin-bottom: 8px; } .stat-label { font-size: 13px; color: #6b7280; text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600; } .footer { margin-top: 60px; padding-top: 30px; border-top: 2px solid #e5e7eb; text-align: center; color: #6b7280; font-size: 13px; }",
        "html_content": "=<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>{{ $json.meetingTitle }} - Meeting Minutes</title>\n</head>\n<body>\n  <div class=\"container\">\n    <div class=\"header\">\n      <h1>📋 {{ $json.meetingTitle }}</h1>\n      <div class=\"subtitle\">Official Meeting Minutes & Action Items</div>\n      <div class=\"meta-grid\">\n        <div class=\"meta-item\">\n          <div class=\"meta-label\">📅 Date</div>\n          <div class=\"meta-value\">{{ $json.meetingDate }}</div>\n        </div>\n        <div class=\"meta-item\">\n          <div class=\"meta-label\">⏱️ Duration</div>\n          <div class=\"meta-value\">{{ $json.duration }}</div>\n        </div>\n        <div class=\"meta-item\">\n          <div class=\"meta-label\">👥 Attendees</div>\n          <div class=\"meta-value\">{{ $json.participants.length }} participants</div>\n        </div>\n        <div class=\"meta-item\">\n          <div class=\"meta-label\">📊 Action Items</div>\n          <div class=\"meta-value\">{{ $json.actionItems.length }} tasks</div>\n        </div>\n      </div>\n    </div>\n\n    {{ $json.summaryMetadata.hasOverdueItems || $json.summaryMetadata.hasTodayItems ? '<div class=\"urgent-banner\"><div class=\"urgent-banner-icon\">⚠️</div><div class=\"urgent-banner-text\">This meeting contains urgent action items requiring immediate attention!</div></div>' : '' }}\n\n    <div class=\"participants-box\">\n      <div class=\"participants-label\">Meeting Participants</div>\n      {{ $json.participants.map(p => `<span class=\"participant-tag\">${p}</span>`).join('') }}\n    </div>\n\n    <div class=\"section\">\n      <div class=\"section-header\">\n        <span class=\"icon\">📝</span>\n        <h2>Meeting Summary</h2>\n      </div>\n      <div class=\"summary-box\">{{ $json.summary }}</div>\n    </div>\n\n    <div class=\"section\">\n      <div class=\"section-header\">\n        <span class=\"icon\">✅</span>\n        <h2>Action Items</h2>\n      </div>\n      <table>\n        <thead>\n          <tr>\n            <th style=\"width:60px;text-align:center;\">#</th>\n            <th>Task</th>\n            <th style=\"width:180px;\">Assignee</th>\n            <th style=\"width:120px;text-align:center;\">Deadline</th>\n            <th style=\"width:120px;text-align:center;\">Priority</th>\n          </tr>\n        </thead>\n        <tbody>\n          {{ $json.actionItems.map(item => `\n            <tr>\n              <td style=\"text-align:center;font-weight:600;color:#3b82f6;\">#${item.number}</td>\n              <td>\n                <div class=\"task-cell\">${item.task}</div>\n                ${item.context ? `<div class=\"task-context\">${item.context}</div>` : ''}\n              </td>\n              <td><span class=\"assignee-badge\">${item.assignee}</span></td>\n              <td style=\"text-align:center;\">\n                <span class=\"deadline-${item.deadlineStatus}\">\n                  ${item.deadlineStatus === 'overdue' ? '⚠️ OVERDUE' : \n                    item.deadlineStatus === 'today' ? '📅 TODAY' :\n                    item.deadlineStatus === 'urgent' ? `⏰ ${item.daysUntilDeadline} days` :\n                    item.daysUntilDeadline ? `${item.daysUntilDeadline} days` : 'TBD'}\n                </span>\n              </td>\n              <td style=\"text-align:center;\">\n                <span class=\"priority-${item.priority}\">\n                  ${item.priority === 'urgent' ? '🔥 URGENT' :\n                    item.priority === 'high' ? '⚡ HIGH' :\n                    item.priority === 'normal' ? '📋 NORMAL' : 'LOW'}\n                </span>\n              </td>\n            </tr>\n          `).join('') }}\n        </tbody>\n      </table>\n    </div>\n\n    <div class=\"section\">\n      <div class=\"section-header\">\n        <span class=\"icon\">📊</span>\n        <h2>Summary Statistics</h2>\n      </div>\n      <div class=\"stats-grid\">\n        <div class=\"stat-card\">\n          <div class=\"stat-number\">{{ $json.summaryMetadata.actionItemCount }}</div>\n          <div class=\"stat-label\">Total Tasks</div>\n        </div>\n        <div class=\"stat-card\">\n          <div class=\"stat-number\">{{ $json.summaryMetadata.priorityBreakdown.high }}</div>\n          <div class=\"stat-label\">High Priority</div>\n        </div>\n        <div class=\"stat-card\">\n          <div class=\"stat-number\">{{ $json.summaryMetadata.upcomingDeadlines }}</div>\n          <div class=\"stat-label\">Due This Week</div>\n        </div>\n        <div class=\"stat-card\">\n          <div class=\"stat-number\">{{ $json.transcriptWordCount }}</div>\n          <div class=\"stat-label\">Transcript Words</div>\n        </div>\n      </div>\n    </div>\n\n    <div class=\"footer\">\n      <div style=\"font-size:20px;margin-bottom:12px;\">📋</div>\n      Generated automatically on {{ new Date().toLocaleString() }}<br>\n      Meeting Minutes Tracker · Powered by AI\n    </div>\n  </div>\n</body>\n</html>",
        "response_format_html": "base64"
      },
      "credentials": {
        "htmlcsstopdfApi": {
          "id": "i5YZc3zx5V2RWZsk",
          "name": "HtmlcsstopdfApi account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "dd362d07-117f-4069-9028-761e4105bf57",
      "name": "邮件通知所有参与者",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2336,
        -144
      ],
      "webhookId": "e607a916-4f74-406a-9cc8-892c0b6b7568",
      "parameters": {
        "sendTo": "={{ $('Parse and Enrich Data').item.json.participants.join(',') }}",
        "message": "=<div style=\"font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; color: #1f2937;\">\n  <div style=\"background: linear-gradient(135deg, #3b82f6 0%, #1e40af 100%); padding: 30px; border-radius: 8px 8px 0 0; color: white;\">\n    <h2 style=\"margin: 0; font-size: 24px;\">📋 {{ $('Parse and Enrich Data').item.json.meetingTitle }} </h2>\n    <p style=\"margin: 10px 0 0 0; opacity: 0.9;\">Meeting Minutes & Action Items</p>\n  </div>\n  \n  <div style=\"background: #ffffff; padding: 30px; border: 1px solid #e5e7eb; border-top: none;\">\n    <p style=\"margin-top: 0;\">Hi team,</p>\n    \n    <p>The minutes from our meeting held on <strong>{{ $('Parse and Enrich Data').item.json.meetingDate }}</strong> are now available.</p>\n    \n    <div style=\"text-align: center; margin: 30px 0;\">\n      <a href=\"{{ $json.pdf_url }}\" style=\"display: inline-block; background: #3b82f6; color: white; padding: 14px 32px; text-decoration: none; border-radius: 6px; font-weight: 600; font-size: 16px;\">\n        📥 Download Meeting Minutes PDF\n      </a>\n    </div>\n    \n    <div style=\"background: #f9fafb; border-left: 4px solid #3b82f6; padding: 20px; margin: 30px 0; border-radius: 4px;\">\n      <h3 style=\"color: #1e40af; margin-top: 0; font-size: 18px;\">Action Items Summary</h3>\n      <ul style=\"line-height: 2; margin: 15px 0; padding-left: 20px;\">\n        {{ $('Parse and Enrich Data').item.json.actionItems.map(item => `\n          <li style=\"margin-bottom: 10px;\">\n            <strong>${item.task}</strong><br>\n            <span style=\"color: #6b7280; font-size: 14px;\">\n              Assigned to: ${item.assignee} | \n              Due: ${item.deadline} | \n              Priority: <span style=\"color: ${item.priority === 'high' || item.priority === 'urgent' ? '#ef4444' : '#3b82f6'};\">${item.priority.toUpperCase()}</span>\n            </span>\n          </li>\n        `).join('') }}\n      </ul>\n    </div>\n    \n    <div style=\"background: #eff6ff; padding: 15px; border-radius: 6px; margin: 20px 0;\">\n      <p style=\"margin: 0; font-size: 14px; color: #1e40af;\">\n        <strong>📊 Meeting Stats:</strong> \n        {{ $('Parse and Enrich Data').item.json.actionItems.length }} action items | \n        {{ $('Parse and Enrich Data').item.json.summaryMetadata.priorityBreakdown.high}} high priority | \n        {{$('Parse and Enrich Data').item.json.participants.length }} participants\n      </p>\n    </div>\n    \n    <p style=\"margin-top: 30px;\">The complete meeting minutes with full context and details are available in the PDF.</p>\n    \n    <p>Please review your assigned action items and reach out if you have any questions.</p>\n    \n    <p style=\"margin-bottom: 0;\">Best regards,<br><strong>Meeting Minutes Tracker</strong></p>\n  </div>\n  \n  <div style=\"background: #f9fafb; padding: 20px; border: 1px solid #e5e7eb; border-top: none; border-radius: 0 0 8px 8px; text-align: center;\">\n    <p style=\"margin: 0; color: #6b7280; font-size: 13px;\">\n      Generated automatically on {{ $now.format('MMMM d, yyyy [at] h:mm a') }}<br>\n      Powered by AI\n    </p>\n  </div>\n</div>",
        "options": {},
        "subject": "=📋 Meeting Minutes - {{ $('Parse and Enrich Data').item.json.meetingTitle }} ({{ $('Parse and Enrich Data').item.json.meetingDate }})"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "6XrYdrG9HaDZyzGH",
          "name": "Gmail account - Akshita"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "fd293c09-5c23-49eb-9766-c95ee3eb88df",
      "name": "从 URL 下载 PDF",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2352,
        144
      ],
      "parameters": {
        "url": "={{ $json.pdf_url }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "64259b9e-7983-4239-aae2-07542f928e6a",
      "name": "保存 PDF 到 Drive",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        2640,
        144
      ],
      "parameters": {
        "name": "=Meeting_Minutes_{{ $('Parse and Enrich Data').item.json.meetingTitle.replace(/ /g, '_') }}_{{ $('Parse and Enrich Data').item.json.meetingDate }}.pdf",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "list",
          "value": "1bYMdPQZm3Z4ChhZ6UjLMJb5BJKFOhp8R",
          "cachedResultUrl": "https://drive.google.com/drive/folders/1bYMdPQZm3Z4ChhZ6UjLMJb5BJKFOhp8R",
          "cachedResultName": "Minutes of Meeting"
        },
        "inputDataFieldName": "=data"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "cHZ7WuviBMkjVwrb",
          "name": "Google Drive account"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "994f49a2-c94d-4982-b827-a9be5c56b5a1",
      "name": "添加 PDF 下载链接",
      "type": "n8n-nodes-base.set",
      "position": [
        2912,
        144
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "9c0c1e1f-c4e4-4db6-add0-e97e768ec0d9",
              "name": "pdfUrl",
              "type": "string",
              "value": "={{ $json.webViewLink }}"
            },
            {
              "id": "6efe7494-9032-4a94-a3f8-c35d5f80865b",
              "name": "pdfFileName",
              "type": "string",
              "value": "={{ $json.name }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "49568bb8-c40b-4229-873d-d3bb2695b95b",
      "name": "通知 Slack 频道",
      "type": "n8n-nodes-base.slack",
      "position": [
        3280,
        -64
      ],
      "webhookId": "1dcd2233-c899-440c-ab08-1c6fb69f484b",
      "parameters": {
        "text": "=:clipboard: *New Meeting Minutes Available*\n\n*Meeting:* {{ $('Parse and Enrich Data').item.json.meetingTitle }}\n*Date:* {{ $('Parse and Enrich Data').item.json.meetingDate }}\n*Participants:* {{ $('Parse and Enrich Data').item.json.participants .length }} attendees\n\n*Action Items Summary:*\n{{ $('Parse and Enrich Data').item.json.actionItems.map((item, i) => `${i + 1}. ${item.task} - ${item.assignee} (${item.deadline}) [${item.priority.toUpperCase()}]`).join('\\n') }}\n\n:link: *Download PDF:* {{ $json.pdfUrl }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09GHQ8CL82",
          "cachedResultName": "leads"
        },
        "otherOptions": {
          "includeLinkToWorkflow": true
        },
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "id": "iEf25ruXA6CT1PJH",
          "name": "Slack account 3 - Akshita"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "76e76682-b710-4239-910f-9d464e04479b",
      "name": "按负责人分组任务",
      "type": "n8n-nodes-base.code",
      "position": [
        3264,
        304
      ],
      "parameters": {
        "jsCode": "const parseData = $('Parse and Enrich Data').first().json;\nconst actionItems = parseData.actionItems || [];\nconst pdfUrl = $json.pdfUrl || $json.webViewLink || '';\n\nif (actionItems.length === 0) {\n  return [];\n}\n\n// Group tasks by assignee\nconst grouped = {};\nactionItems.forEach(item => {\n  if (!grouped[item.assignee]) {\n    grouped[item.assignee] = [];\n  }\n  grouped[item.assignee].push(item);\n});\n\n// Return one item per assignee\nreturn Object.keys(grouped).map(assignee => ({\n  json: {\n    assignee: assignee,\n    tasks: grouped[assignee],\n    taskCount: grouped[assignee].length,\n    meetingTitle: parseData.meetingTitle,\n    meetingDate: parseData.meetingDate,\n    pdfUrl: pdfUrl\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "b4a682ee-58a8-4bf4-a915-a4ac51d0f8dc",
      "name": "发送个人任务邮件",
      "type": "n8n-nodes-base.gmail",
      "position": [
        3632,
        304
      ],
      "webhookId": "229e1653-aac1-4a82-b2f8-a95d9536ea85",
      "parameters": {
        "sendTo": "={{ $json.assignee }}@yourcompany.com",
        "message": "=<div style=\"font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;\">\n  <div style=\"background: linear-gradient(135deg, #3b82f6 0%, #1e40af 100%); padding: 30px; border-radius: 8px 8px 0 0; color: white;\">\n    <h2 style=\"margin: 0; font-size: 24px;\">📋 {{ $json.meetingTitle }}</h2>\n    <p style=\"margin: 10px 0 0 0; opacity: 0.9;\">Action Items Assigned to You</p>\n  </div>\n  \n  <div style=\"background: white; padding: 30px; border: 1px solid #e5e7eb; border-top: none;\">\n    <p style=\"margin-top: 0;\">Hi <strong>{{ $json.assignee }}</strong>,</p>\n    \n    <p>You have <strong>{{ $json.taskCount }}</strong> action {{ $json.taskCount === 1 ? 'item' : 'items' }} from the meeting held on <strong>{{ $json.meetingDate }}</strong>:</p>\n    \n    {{ $json.tasks.map((task, i) => `\n      <div style=\"background: ${task.priority === 'high' || task.priority === 'urgent' ? '#fef2f2' : '#f0f9ff'}; border-left: 4px solid ${task.priority === 'high' || task.priority === 'urgent' ? '#ef4444' : '#3b82f6'}; padding: 20px; margin: 20px 0; border-radius: 4px;\">\n        <div style=\"display: flex; justify-content: space-between; align-items: start; margin-bottom: 10px;\">\n          <h3 style=\"margin: 0; color: #1e40af; font-size: 16px;\">Task ${i + 1}</h3>\n          <span style=\"background: ${task.priority === 'high' || task.priority === 'urgent' ? '#ef4444' : task.priority === 'normal' ? '#3b82f6' : '#6b7280'}; color: white; padding: 4px 10px; border-radius: 4px; font-size: 11px; font-weight: 600; text-transform: uppercase;\">\n            ${task.priority === 'high' ? '⚡ HIGH' : task.priority === 'urgent' ? '🔥 URGENT' : task.priority === 'normal' ? '📋 NORMAL' : 'LOW'}\n          </span>\n        </div>\n        \n        <p style=\"font-size: 15px; font-weight: 600; color: #1f2937; margin: 10px 0; line-height: 1.5;\">\n          ${task.task}\n        </p>\n        \n        <div style=\"margin: 12px 0; padding: 12px; background: white; border-radius: 4px;\">\n          <p style=\"margin: 5px 0; color: #374151; font-size: 14px;\">\n            <strong>📅 Deadline:</strong> ${task.deadline} \n            ${task.daysUntilDeadline !== null ? `<span style=\"color: ${task.deadlineStatus === 'overdue' ? '#dc2626' : task.deadlineStatus === 'urgent' ? '#f59e0b' : '#6b7280'};\">(${task.daysUntilDeadline} day${task.daysUntilDeadline === 1 ? '' : 's'})</span>` : ''}\n          </p>\n          <p style=\"margin: 5px 0; color: #374151; font-size: 14px;\">\n            <strong>📌 Status:</strong> ${task.status}\n          </p>\n        </div>\n        \n        ${task.context ? `<p style=\"margin-top: 12px; font-style: italic; color: #6b7280; font-size: 13px; line-height: 1.5; padding-top: 12px; border-top: 1px dashed #d1d5db;\">${task.context}</p>` : ''}\n      </div>\n    `).join('') }}\n    \n    <div style=\"text-align: center; margin: 35px 0;\">\n      <a href=\"{{ $json.pdfUrl }}\" style=\"display: inline-block; background: #3b82f6; color: white; padding: 14px 28px; text-decoration: none; border-radius: 6px; font-weight: 600; font-size: 15px; box-shadow: 0 2px 4px rgba(59, 130, 246, 0.3);\">\n        📥 Download Full Meeting Minutes\n      </a>\n    </div>",
        "options": {},
        "subject": "=⚡ {{ $json.taskCount }} Action {{ $json.taskCount === 1 ? 'Item' : 'Items' }} from {{ $json.meetingTitle }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "6XrYdrG9HaDZyzGH",
          "name": "Gmail account - Akshita"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "a75840d6-4c1f-4940-8a9b-9f05f121c754",
      "name": "拆分任务以创建",
      "type": "n8n-nodes-base.code",
      "position": [
        2000,
        528
      ],
      "parameters": {
        "jsCode": "const actionItems = $json.actionItems || [];\nconst meetingTitle = $json.meetingTitle;\nconst meetingDate = $json.meetingDate;\nconst pdfUrl = $json.pdfUrl || '';\n\nif (actionItems.length === 0) {\n  return [];\n}\n\n// Return one item per action item for task creation\nreturn actionItems.map(item => ({\n  json: {\n    task: item.task,\n    assignee: item.assignee,\n    deadline: item.deadline,\n    priority: item.priority,\n    context: item.context,\n    meetingTitle: meetingTitle,\n    meetingDate: meetingDate,\n    status: item.status,\n    daysUntilDeadline: item.daysUntilDeadline\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "59146f07-6b6c-4c0b-a31c-37afa7b01ef7",
      "name": "在 Google Tasks 中创建任务",
      "type": "n8n-nodes-base.googleTasks",
      "position": [
        2304,
        528
      ],
      "parameters": {
        "task": "MTYxODQ3NTU3ODAzODc5Njg1NTk6MDow",
        "title": "={{ $json.task }}",
        "additionalFields": {
          "notes": "={{ $json.context }}  \nMeeting: {{ $json.meetingTitle }} \nDate: {{ $json.meetingDate }} \nAssignee: {{ $json.assignee }} \nPriority: {{ $json.priority }}",
          "dueDate": "={{ $json.deadline ? new Date($json.deadline + 'T12:00:00Z').toISOString() : null }}"
        }
      },
      "credentials": {
        "googleTasksOAuth2Api": {
          "id": "2DSvIyWd57G4NTCB",
          "name": "Google Tasks account 2 - Akshita"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "063a6dc6-cf1c-4a4a-9994-2fb1e9e8f291",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -304,
        -224
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 352,
        "content": "## WEBHOOK 端点"
      },
      "typeVersion": 1
    },
    {
      "id": "22ca0f0e-3fc2-4dbc-b5d2-86fcc508c93b",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        16,
        -288
      ],
      "parameters": {
        "color": 7,
        "width": 304,
        "height": 464,
        "content": "## 数据规范化"
      },
      "typeVersion": 1
    },
    {
      "id": "5173480f-55d9-4786-a46d-8f9ce7a7ef5d",
      "name": "便签2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        352,
        -336
      ],
      "parameters": {
        "color": 7,
        "height": 480,
        "content": "## 转录文本清理"
      },
      "typeVersion": 1
    },
    {
      "id": "ba992adf-ec87-48bb-b130-b41af85ad4c4",
      "name": "便签3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        624,
        -256
      ],
      "parameters": {
        "color": 7,
        "width": 272,
        "height": 400,
        "content": "## 质量门控"
      },
      "typeVersion": 1
    },
    {
      "id": "66f5fafa-740b-4a27-893e-d4bcc4db9fcb",
      "name": "便签4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        928,
        -336
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 320,
        "content": "## OPENAI 摘要 (GPT-4)"
      },
      "typeVersion": 1
    },
    {
      "id": "5b0681a7-fd69-4f62-b881-2f27891871ef",
      "name": "便签5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        928,
        112
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 400,
        "content": "## OPENAI 行动项提取 (GPT-4)"
      },
      "typeVersion": 1
    },
    {
      "id": "0723ad57-23fb-41dc-9b95-71b4efda99d2",
      "name": "便签6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1296,
        -240
      ],
      "parameters": {
        "color": 7,
        "height": 384,
        "content": "## 合并 AI 响应"
      },
      "typeVersion": 1
    },
    {
      "id": "b63e42ec-dd33-4b4b-853c-126a905ef376",
      "name": "便签7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1568,
        -368
      ],
      "parameters": {
        "color": 7,
        "width": 272,
        "height": 496,
        "content": "## 数据结构化"
      },
      "typeVersion": 1
    },
    {
      "id": "96e5ac65-f844-45f2-b598-2f2d23667122",
      "name": "便签8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1904,
        -448
      ],
      "parameters": {
        "color": 7,
        "width": 304,
        "height": 464,
        "content": "## PDF 转换"
      },
      "typeVersion": 1
    },
    {
      "id": "9e64cb89-011d-407f-b046-66f2b2723f04",
      "name": "便签9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2288,
        -368
      ],
      "parameters": {
        "color": 7,
        "width": 352,
        "height": 368,
        "content": "## 批量邮件通知"
      },
      "typeVersion": 1
    },
    {
      "id": "e0f7c258-a987-4667-8eee-702336525b23",
      "name": "便签10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2016,
        80
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 208,
        "content": "## PDF URL 获取"
      },
      "typeVersion": 1
    },
    {
      "id": "5c473a51-227c-4483-b064-d12c20482212",
      "name": "便签11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2544,
        128
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 432,
        "content": "## 云存储"
      },
      "typeVersion": 1
    },
    {
      "id": "649ba897-6e40-42c8-bbb1-e4dc1545f383",
      "name": "便签12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2848,
        -80
      ],
      "parameters": {
        "color": 7,
        "height": 384,
        "content": "## URL 提取"
      },
      "typeVersion": 1
    },
    {
      "id": "2c634a33-5e61-4a88-b6ba-1488dca2cab8",
      "name": "便签13",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3184,
        -304
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 384,
        "content": "## 团队频道更新"
      },
      "typeVersion": 1
    },
    {
      "id": "ff75e3fe-540c-4c7d-be9e-3c6b4b4d03b1",
      "name": "便签14",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3152,
        288
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 416,
        "content": "## 负责人分组"
      },
      "typeVersion": 1
    },
    {
      "id": "d7d7e9b6-16b5-4db2-be87-20501af66bef",
      "name": "便签15",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3568,
        16
      ],
      "parameters": {
        "color": 7,
        "width": 336,
        "height": 464,
        "content": "## 个性化任务通知"
      },
      "typeVersion": 1
    },
    {
      "id": "b26bb8e7-6955-42e2-997a-139105fcd227",
      "name": "便签16",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1888,
        512
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 400,
        "content": "## 任务项分离"
      },
      "typeVersion": 1
    },
    {
      "id": "b7dd2f52-cc18-4636-846b-0038e3b23eb8",
      "name": "便签17",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2240,
        512
      ],
      "parameters": {
        "color": 7,
        "width": 272,
        "height": 544,
        "content": "## 任务管理集成"
      },
      "typeVersion": 1
    },
    {
      "id": "756c055c-ef44-4368-8531-44faa8ebb6a1",
      "name": "便签18",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -816,
        -352
      ],
      "parameters": {
        "color": 7,
        "width": 464,
        "height": 1184,
        "content": "## 会议纪要和行动项跟踪器 - 概述"
      },
      "typeVersion": 1
    },
    {
      "id": "2828c58b-106b-4131-8e52-7baf1478b85b",
      "name": "便签19",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -288,
        512
      ],
      "parameters": {
        "color": 7,
        "width": 592,
        "height": 1136,
        "content": "## 设置说明"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "ff824dc2-123e-4310-a036-d320361fffe1",
  "connections": {
    "Clean Transcript": {
      "main": [
        [
          {
            "node": "Validate Transcript Length",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save PDF to Drive": {
      "main": [
        [
          {
            "node": "Add PDF Download Link",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine AI Responses": {
      "main": [
        [
          {
            "node": "Parse and Enrich Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Action Items": {
      "main": [
        [
          {
            "node": "Combine AI Responses",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Notify Slack Channel": {
      "main": [
        []
      ]
    },
    "Receive Meeting Data": {
      "main": [
        [
          {
            "node": "Extract Meeting Metadata",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add PDF Download Link": {
      "main": [
        [
          {
            "node": "Notify Slack Channel",
            "type": "main",
            "index": 0
          },
          {
            "node": "Group Tasks by Assignee",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download PDF from URL": {
      "main": [
        [
          {
            "node": "Save PDF to Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate PDF Document": {
      "main": [
        [
          {
            "node": "Email All Participants",
            "type": "main",
            "index": 0
          },
          {
            "node": "Download PDF from URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse and Enrich Data": {
      "main": [
        [
          {
            "node": "Generate PDF Document",
            "type": "main",
            "index": 0
          },
          {
            "node": "Split Tasks for Creation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email All Participants": {
      "main": [
        []
      ]
    },
    "Group Tasks by Assignee": {
      "main": [
        [
          {
            "node": "Send Individual Task Emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Meeting Metadata": {
      "main": [
        [
          {
            "node": "Clean Transcript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Meeting Summary": {
      "main": [
        [
          {
            "node": "Combine AI Responses",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Tasks for Creation": {
      "main": [
        [
          {
            "node": "Create Task in Google Tasks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Transcript Length": {
      "main": [
        [
          {
            "node": "Generate Meeting Summary",
            "type": "main",
            "index": 0
          },
          {
            "node": "Extract Action Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Individual Task Emails": {
      "main": [
        []
      ]
    }
  }
}
常见问题

如何使用这个工作流?

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

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

高级 - 内容创作, 多模态 AI

需要付费吗?

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

工作流信息
难度等级
高级
节点数量38
分类2
节点类型13
难度说明

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

作者
Jitesh Dugar

Jitesh Dugar

@jiteshdugar

AI Automation Specialist - OpenAI, CRM & Automation Expert with a solid understanding of various tools that include Zapier, Make, Zoho CRM, Hubspot, Google Sheets, Airtable, Pipedrive, Google Analytics, and more.

外部链接
在 n8n.io 查看

分享此工作流