8
n8n 中文网amn8n.com

客户支持工单工作流自动化

高级

这是一个Content Creation, Multimodal AI领域的自动化工作流,包含 28 个节点。主要使用 If, Set, Code, Slack, Webhook 等节点。 使用GPT-4、PDF和Google Drive将支持工单转换为AI文档

前置要求
  • Slack Bot Token 或 Webhook URL
  • HTTP Webhook 端点(n8n 会自动生成)
  • Google Drive API 凭证
  • 可能需要目标 API 的认证凭证
  • Google Sheets API 凭证
  • OpenAI API Key
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "support-ticket-documentation",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "客户支持工单工作流自动化",
  "tags": [],
  "nodes": [
    {
      "id": "61dc2aa7-ac6c-4397-bcbc-ad6014d1b117",
      "name": "Webhook - 接收工单",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -2352,
        240
      ],
      "webhookId": "",
      "parameters": {
        "path": "support-ticket-resolved",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 1
    },
    {
      "id": "3a08bb55-109b-4bce-ae0c-1a8bd6bd8233",
      "name": "📝 便签 - 触发器设置",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2688,
        48
      ],
      "parameters": {
        "color": 7,
        "width": 468,
        "height": 804,
        "content": "## 🎯 步骤 1:触发器"
      },
      "typeVersion": 1
    },
    {
      "id": "01277a43-c6fa-4a85-bb95-5e6fa1b096ff",
      "name": "提取工单详情",
      "type": "n8n-nodes-base.code",
      "position": [
        -2064,
        240
      ],
      "parameters": {
        "jsCode": "// Extract and normalize ticket data from webhook payload\nconst ticketData = $input.item.json.body || $input.item.json;\n\n// Handle different payload structures (Zendesk, Freshdesk, custom)\nconst ticket = ticketData.ticket || ticketData;\n\n// Extract ticket ID (handle various formats)\nconst extractTicketId = (ticket) => {\n  return ticket.id || ticket.ticket_id || ticket.number || 'UNKNOWN';\n};\n\n// Extract agent name with fallback\nconst extractAgentName = (ticket) => {\n  if (ticket.assignee?.name) return ticket.assignee.name;\n  if (ticket.agent?.name) return ticket.agent.name;\n  if (ticket.assigned_to) return ticket.assigned_to;\n  return 'Support Team';\n};\n\n// Extract customer info\nconst extractCustomerInfo = (ticket) => {\n  return {\n    name: ticket.requester?.name || ticket.customer?.name || 'Customer',\n    email: ticket.requester?.email || ticket.customer?.email || 'N/A'\n  };\n};\n\n// Extract resolution notes\nconst extractResolution = (ticket) => {\n  if (ticket.resolution_notes) return ticket.resolution_notes;\n  if (ticket.latest_comment?.body) return ticket.latest_comment.body;\n  if (ticket.internal_notes) return ticket.internal_notes;\n  if (Array.isArray(ticket.comments) && ticket.comments.length > 0) {\n    return ticket.comments[ticket.comments.length - 1].body;\n  }\n  return 'Resolution details not available';\n};\n\nconst customer = extractCustomerInfo(ticket);\n\n// Return normalized data structure\nreturn {\n  json: {\n    ticketId: extractTicketId(ticket),\n    subject: ticket.subject || 'No Subject Provided',\n    description: ticket.description || ticket.body || '',\n    resolution: extractResolution(ticket),\n    agentName: extractAgentName(ticket),\n    customerName: customer.name,\n    customerEmail: customer.email,\n    priority: (ticket.priority || 'normal').toLowerCase(),\n    createdAt: ticket.created_at || new Date().toISOString(),\n    resolvedAt: ticket.updated_at || ticket.resolved_at || new Date().toISOString(),\n    tags: ticket.tags || [],\n    category: ticket.category || ticket.type || 'General Support',\n    rawTicket: ticket // Keep original for reference\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "8e5339a1-47ac-466a-a5ab-ae753c10864a",
      "name": "📝 便签 - 数据提取",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2192,
        -528
      ],
      "parameters": {
        "color": 7,
        "width": 404,
        "height": 904,
        "content": "## 📋 步骤 2:数据提取"
      },
      "typeVersion": 1
    },
    {
      "id": "708ee78b-72c0-4a45-b334-8cdd845f9af8",
      "name": "AI 摘要生成 (OpenAI)",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        -1728,
        240
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4",
          "cachedResultName": "GPT-4"
        },
        "options": {
          "maxTokens": 1500,
          "temperature": 0.3
        },
        "messages": {
          "values": [
            {
              "content": "=You are a technical support documentation specialist. Your task is to analyze support tickets and create clear, professional case study summaries.\n\nCreate summaries that are:\n- Clear and professional\n- Structured with proper HTML formatting\n- Actionable and informative\n- Suitable for knowledge base articles\n- Include key takeaways and prevention tips"
            },
            {
              "content": "=Please analyze this support ticket and provide a comprehensive, structured summary:\n\n**TICKET INFORMATION**\n- Ticket ID: {{ $json.ticketId }}\n- Subject: {{ $json.subject }}\n- Customer: {{ $json.customerName }} ({{ $json.customerEmail }})\n- Priority: {{ $json.priority }}\n- Category: {{ $json.category }}\n- Tags: {{ $json.tags.join(', ') }}\n\n**ORIGINAL ISSUE REPORTED:**\n{{ $json.description }}\n\n**RESOLUTION PROVIDED:**\n{{ $json.resolution }}\n\n**RESOLVED BY:** {{ $json.agentName }}\n\n---\n\nPlease structure your response using the following HTML format:\n\n<h2>🔍 Issue Reported</h2>\n<div class=\"section\">\n<p>[Provide a clear, concise description of the problem the customer experienced. Include symptoms, error messages, and impact on the customer.]</p>\n</div>\n\n<h2>🛠️ Troubleshooting Steps</h2>\n<div class=\"section\">\n<ul>\n<li>[Step 1: What was checked or attempted first]</li>\n<li>[Step 2: Next diagnostic action taken]</li>\n<li>[Step 3: Additional troubleshooting performed]</li>\n</ul>\n</div>\n\n<h2>✅ Final Resolution</h2>\n<div class=\"section\">\n<p>[Describe the exact solution that resolved the issue. Be specific about what was done and why it worked.]</p>\n</div>\n\n<h2>💡 Key Takeaways</h2>\n<div class=\"section\">\n<ul>\n<li>[Important lesson or insight from this case]</li>\n<li>[Prevention tip or best practice]</li>\n<li>[Related knowledge or documentation reference]</li>\n</ul>\n</div>\n\nIMPORTANT: \n- Use only the HTML tags and structure shown above\n- Be specific and technical but keep language clear\n- Focus on actionable information\n- If information is missing, make reasonable inferences based on context\n- Keep the summary concise but comprehensive (aim for 200-400 words total)"
            }
          ]
        }
      },
      "credentials": {
        "openAiApi": {
          "id": "1",
          "name": "OpenAI Account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "aa238d8e-b227-4bf1-b3c7-218457f58e4c",
      "name": "📝 便签 - AI 摘要生成",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1760,
        -560
      ],
      "parameters": {
        "color": 7,
        "width": 452,
        "height": 916,
        "content": "## 🤖 步骤 3:AI 摘要生成"
      },
      "typeVersion": 1
    },
    {
      "id": "6ce3cdf9-7619-4f8a-973a-a0021a22e13d",
      "name": "格式化 HTML 文档",
      "type": "n8n-nodes-base.code",
      "position": [
        -1152,
        240
      ],
      "parameters": {
        "jsCode": "// Combine ticket data with AI summary and create professional HTML document\nconst ticket = $('Extract Ticket Details').item.json;\nconst aiResponse = $input.item.json;\n\n// Extract AI summary (handle different response formats)\nlet aiSummary = '';\nif (aiResponse.message?.content) {\n  aiSummary = aiResponse.message.content;\n} else if (aiResponse.choices?.[0]?.message?.content) {\n  aiSummary = aiResponse.choices[0].message.content;\n} else if (typeof aiResponse === 'string') {\n  aiSummary = aiResponse;\n} else {\n  aiSummary = JSON.stringify(aiResponse);\n}\n\n// Format dates nicely\nconst formatDate = (dateString) => {\n  const date = new Date(dateString);\n  return date.toLocaleDateString('en-US', { \n    year: 'numeric', \n    month: 'long', \n    day: 'numeric',\n    hour: '2-digit',\n    minute: '2-digit'\n  });\n};\n\n// Calculate resolution time\nconst calculateResolutionTime = (created, resolved) => {\n  const diff = new Date(resolved) - new Date(created);\n  const hours = Math.floor(diff / (1000 * 60 * 60));\n  const days = Math.floor(hours / 24);\n  \n  if (days > 0) return `${days} day${days > 1 ? 's' : ''}`;\n  if (hours > 0) return `${hours} hour${hours > 1 ? 's' : ''}`;\n  return 'Less than 1 hour';\n};\n\nconst resolutionTime = calculateResolutionTime(ticket.createdAt, ticket.resolvedAt);\nconst currentDate = new Date().toLocaleDateString('en-US', { \n  year: 'numeric', \n  month: 'long', \n  day: 'numeric' \n});\n\n// Create comprehensive HTML document\nconst html = `\n<!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>Support Ticket ${ticket.ticketId} - Case Documentation</title>\n    <style>\n        * {\n            margin: 0;\n            padding: 0;\n            box-sizing: border-box;\n        }\n        body {\n            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n            line-height: 1.6;\n            color: #333;\n            background-color: #f5f7fa;\n            padding: 0;\n        }\n        .page {\n            background-color: white;\n            max-width: 800px;\n            margin: 0 auto;\n            padding: 40px;\n            min-height: 100vh;\n        }\n        .header {\n            border-bottom: 4px solid #4CAF50;\n            padding-bottom: 25px;\n            margin-bottom: 35px;\n        }\n        .logo {\n            color: #4CAF50;\n            font-size: 28px;\n            font-weight: bold;\n            margin-bottom: 5px;\n            display: flex;\n            align-items: center;\n            gap: 10px;\n        }\n        .company-tagline {\n            color: #666;\n            font-size: 14px;\n            font-style: italic;\n            margin-bottom: 20px;\n        }\n        h1 {\n            color: #2c3e50;\n            margin: 10px 0;\n            font-size: 32px;\n            font-weight: 700;\n        }\n        .badges {\n            margin-top: 15px;\n        }\n        .badge {\n            display: inline-block;\n            padding: 6px 14px;\n            border-radius: 20px;\n            font-size: 11px;\n            font-weight: bold;\n            text-transform: uppercase;\n            letter-spacing: 0.5px;\n            margin-right: 8px;\n        }\n        .badge-resolved {\n            background-color: #4CAF50;\n            color: white;\n        }\n        .badge-priority-high {\n            background-color: #f44336;\n            color: white;\n        }\n        .badge-priority-urgent {\n            background-color: #d32f2f;\n            color: white;\n        }\n        .badge-priority-normal {\n            background-color: #ff9800;\n            color: white;\n        }\n        .badge-priority-low {\n            background-color: #2196F3;\n            color: white;\n        }\n        .meta-info {\n            display: grid;\n            grid-template-columns: repeat(2, 1fr);\n            gap: 20px;\n            background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);\n            padding: 25px;\n            border-radius: 8px;\n            margin-bottom: 35px;\n            border: 1px solid #dee2e6;\n        }\n        .meta-item {\n            font-size: 14px;\n        }\n        .meta-label {\n            font-weight: 700;\n            color: #495057;\n            display: block;\n            margin-bottom: 5px;\n            text-transform: uppercase;\n            font-size: 11px;\n            letter-spacing: 0.5px;\n        }\n        .meta-value {\n            color: #212529;\n            font-size: 15px;\n            font-weight: 500;\n        }\n        h2 {\n            color: #4CAF50;\n            border-left: 5px solid #4CAF50;\n            padding-left: 15px;\n            margin-top: 35px;\n            margin-bottom: 20px;\n            font-size: 22px;\n            display: flex;\n            align-items: center;\n            gap: 10px;\n        }\n        .section {\n            margin-bottom: 30px;\n            padding: 20px;\n            background-color: #fafbfc;\n            border-radius: 6px;\n            border-left: 3px solid #e9ecef;\n        }\n        .section p {\n            margin-bottom: 12px;\n            line-height: 1.8;\n        }\n        .section ul {\n            margin: 15px 0;\n            padding-left: 25px;\n        }\n        .section li {\n            margin-bottom: 10px;\n            line-height: 1.8;\n            padding-left: 5px;\n        }\n        .tags {\n            margin-top: 20px;\n            padding-top: 20px;\n            border-top: 1px solid #e9ecef;\n        }\n        .tag {\n            display: inline-block;\n            background-color: #e9ecef;\n            color: #495057;\n            padding: 5px 12px;\n            border-radius: 12px;\n            font-size: 12px;\n            margin-right: 8px;\n            margin-bottom: 8px;\n        }\n        .footer {\n            margin-top: 50px;\n            padding-top: 25px;\n            border-top: 2px solid #e9ecef;\n            text-align: center;\n            font-size: 12px;\n            color: #6c757d;\n        }\n        .footer-row {\n            margin-bottom: 8px;\n        }\n        .stats-box {\n            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n            color: white;\n            padding: 20px;\n            border-radius: 8px;\n            margin-bottom: 30px;\n            text-align: center;\n        }\n        .stats-box .stat-label {\n            font-size: 12px;\n            text-transform: uppercase;\n            letter-spacing: 1px;\n            opacity: 0.9;\n            margin-bottom: 5px;\n        }\n        .stats-box .stat-value {\n            font-size: 24px;\n            font-weight: bold;\n        }\n    </style>\n</head>\n<body>\n    <div class=\"page\">\n        <div class=\"header\">\n            <div class=\"logo\">\n                🎯 Your Company Support\n            </div>\n            <div class=\"company-tagline\">Excellence in Customer Service</div>\n            <h1>Support Case Documentation</h1>\n            <div class=\"badges\">\n                <span class=\"badge badge-resolved\">✓ RESOLVED</span>\n                <span class=\"badge badge-priority-${ticket.priority}\">${ticket.priority.toUpperCase()} PRIORITY</span>\n            </div>\n        </div>\n\n        <div class=\"stats-box\">\n            <div class=\"stat-label\">Resolution Time</div>\n            <div class=\"stat-value\">⚡ ${resolutionTime}</div>\n        </div>\n\n        <div class=\"meta-info\">\n            <div class=\"meta-item\">\n                <span class=\"meta-label\">Ticket ID</span>\n                <div class=\"meta-value\">#${ticket.ticketId}</div>\n            </div>\n            <div class=\"meta-item\">\n                <span class=\"meta-label\">Category</span>\n                <div class=\"meta-value\">${ticket.category}</div>\n            </div>\n            <div class=\"meta-item\">\n                <span class=\"meta-label\">Customer</span>\n                <div class=\"meta-value\">${ticket.customerName}</div>\n            </div>\n            <div class=\"meta-item\">\n                <span class=\"meta-label\">Customer Email</span>\n                <div class=\"meta-value\">${ticket.customerEmail}</div>\n            </div>\n            <div class=\"meta-item\">\n                <span class=\"meta-label\">Resolved By</span>\n                <div class=\"meta-value\">${ticket.agentName}</div>\n            </div>\n            <div class=\"meta-item\">\n                <span class=\"meta-label\">Resolution Date</span>\n                <div class=\"meta-value\">${formatDate(ticket.resolvedAt)}</div>\n            </div>\n        </div>\n\n        <h2>📋 Subject</h2>\n        <div class=\"section\">\n            <p><strong>${ticket.subject}</strong></p>\n        </div>\n\n        ${aiSummary}\n\n        ${ticket.tags && ticket.tags.length > 0 ? `\n        <div class=\"tags\">\n            <strong>Tags:</strong><br>\n            ${ticket.tags.map(tag => `<span class=\"tag\">#${tag}</span>`).join('')}\n        </div>\n        ` : ''}\n\n        <div class=\"footer\">\n            <div class=\"footer-row\">\n                <strong>Document Generated:</strong> ${currentDate}\n            </div>\n            <div class=\"footer-row\">\n                Case #${ticket.ticketId} | Ticket opened: ${formatDate(ticket.createdAt)}\n            </div>\n            <div class=\"footer-row\" style=\"margin-top: 15px;\">\n                © ${new Date().getFullYear()} Your Company - Support Documentation System\n            </div>\n            <div class=\"footer-row\" style=\"margin-top: 10px; font-style: italic;\">\n                This document was automatically generated by our AI-powered support documentation workflow\n            </div>\n        </div>\n    </div>\n</body>\n</html>\n`;\n\n// Generate filename with timestamp\nconst timestamp = new Date().toISOString().split('T')[0];\nconst sanitizedSubject = ticket.subject\n  .replace(/[^a-z0-9]/gi, '_')\n  .substring(0, 50);\nconst fileName = `Ticket_${ticket.ticketId}_${sanitizedSubject}_${timestamp}.pdf`;\n\nreturn {\n  json: {\n    ...ticket,\n    aiSummary: aiSummary,\n    htmlContent: html,\n    fileName: fileName,\n    resolutionTime: resolutionTime,\n    documentGeneratedAt: new Date().toISOString()\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "7d4c30c8-8872-44fd-88d4-5f9b160a7799",
      "name": "📝 便签 - HTML 格式化",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1296,
        -752
      ],
      "parameters": {
        "color": 7,
        "width": 436,
        "height": 1116,
        "content": "## 🎨 步骤 4:HTML 格式化"
      },
      "typeVersion": 1
    },
    {
      "id": "75a14225-69a2-4db7-8dd3-d3e03b172f4a",
      "name": "📝 便签 - PDF 转换",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -832,
        -272
      ],
      "parameters": {
        "color": 7,
        "width": 420,
        "height": 668,
        "content": "## 📄 步骤 5:PDF 转换"
      },
      "typeVersion": 1
    },
    {
      "id": "34506c7f-ea7b-4d6f-9213-940cd56d6d37",
      "name": "检查 PDF 成功",
      "type": "n8n-nodes-base.if",
      "position": [
        -240,
        240
      ],
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.success }}",
              "operation": "isNotEmpty"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "867a398d-8eca-4327-9555-6b153ba01c02",
      "name": "📝 便签 - 错误检查",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -368,
        -608
      ],
      "parameters": {
        "color": 7,
        "width": 372,
        "height": 980,
        "content": "## ⚠️ 步骤 6:错误检查 #1"
      },
      "typeVersion": 1
    },
    {
      "id": "726984c8-2e34-4881-9c99-4b07de711263",
      "name": "上传到 Google Drive",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        560,
        112
      ],
      "parameters": {
        "name": "={{ $('Format HTML Document').item.json.fileName }}",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_FOLDER_ID_HERE"
        }
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "3",
          "name": "Google Drive account"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "7eb6e69c-c2e4-4f9a-8b4b-e8e3a5cd8258",
      "name": "📝 便签 - Drive 上传",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        400,
        -944
      ],
      "parameters": {
        "color": 7,
        "width": 420,
        "height": 1208,
        "content": "## ☁️ 步骤 7:Google Drive 上传"
      },
      "typeVersion": 1
    },
    {
      "id": "56bcd853-4095-4b0c-9bb0-d24f22dd8ed9",
      "name": "捕获 Drive URL",
      "type": "n8n-nodes-base.set",
      "position": [
        880,
        112
      ],
      "parameters": {
        "fields": {
          "values": [
            {
              "name": "driveFileId",
              "stringValue": "={{ $json.id }}"
            },
            {
              "name": "driveFileUrl",
              "stringValue": "={{ $json.webViewLink }}"
            },
            {
              "name": "driveFileName",
              "stringValue": "={{ $json.name }}"
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "ecfdb162-21d7-4126-95fb-939aa0392065",
      "name": "检查上传成功",
      "type": "n8n-nodes-base.if",
      "position": [
        1264,
        112
      ],
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.driveFileUrl }}",
              "operation": "isNotEmpty"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "f6be4bde-7607-46a2-b720-e97dfeea3e77",
      "name": "📝 便签 - 上传检查",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1024,
        -864
      ],
      "parameters": {
        "color": 7,
        "width": 388,
        "height": 1120,
        "content": "## ⚠️ 步骤 8:错误检查 #2"
      },
      "typeVersion": 1
    },
    {
      "id": "d66117e3-612e-4d2e-8630-154076c8d07a",
      "name": "更新 Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1600,
        -96
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "Documented",
            "Subject": "={{ $('Extract Ticket Details').item.json.subject }}",
            "Category": "={{ $('Extract Ticket Details').item.json.category }}",
            "PDF Link": "={{ $json.driveFileUrl }}",
            "Priority": "={{ $('Extract Ticket Details').item.json.priority }}",
            "Ticket ID": "={{ $('Extract Ticket Details').item.json.ticketId }}",
            "Agent Name": "={{ $('Extract Ticket Details').item.json.agentName }}",
            "Customer Name": "={{ $('Extract Ticket Details').item.json.customerName }}",
            "Resolved Date": "={{ $('Extract Ticket Details').item.json.resolvedAt }}",
            "Customer Email": "={{ $('Extract Ticket Details').item.json.customerEmail }}",
            "Resolution Time": "={{ $('Format HTML Document').item.json.resolutionTime }}",
            "Document Generated": "={{ $now }}"
          },
          "schema": [
            {
              "id": "Ticket ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Ticket ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Subject",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Subject",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Customer Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Customer Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Customer Email",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Customer Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Agent Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Agent Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Priority",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Priority",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Category",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Category",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Resolved Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Resolved Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Resolution Time",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Resolution Time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "PDF Link",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "PDF Link",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Document Generated",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Document Generated",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_SHEET_ID_HERE"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "4",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "896eb68c-5576-4584-ace7-ad48a2826d28",
      "name": "📝 便签 - 数据库",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1456,
        -1440
      ],
      "parameters": {
        "color": 7,
        "width": 420,
        "height": 1528,
        "content": "## 步骤 9:数据库更新"
      },
      "typeVersion": 1
    },
    {
      "id": "3fbf74bf-4418-4109-94c2-196a3e760e69",
      "name": "发送 Slack 通知",
      "type": "n8n-nodes-base.slack",
      "position": [
        2016,
        -96
      ],
      "webhookId": "",
      "parameters": {
        "text": "=✅ *Support Ticket Documented Successfully*\n\n*Ticket Details:*\n• *ID:* #{{ $('Extract Ticket Details').item.json.ticketId }}\n• *Subject:* {{ $('Extract Ticket Details').item.json.subject }}\n• *Customer:* {{ $('Extract Ticket Details').item.json.customerName }}\n• *Priority:* {{ $('Extract Ticket Details').item.json.priority.toUpperCase() }}\n\n*Resolution Info:*\n• *Resolved by:* {{ $('Extract Ticket Details').item.json.agentName }}\n• *Resolution Time:* {{ $('Format HTML Document').item.json.resolutionTime }}\n\n📄 *Documentation:* <{{ $('Capture Drive URL').item.json.driveFileUrl }}|View PDF>\n\n_Automatically generated at {{ $now.format('MM/DD/YYYY h:mm A') }}_",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_CHANNEL_ID_HERE"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "id": "5",
          "name": "Slack account"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "f92ee144-07fe-474a-b3d8-6047b8fe1db3",
      "name": "📝 便签 - Slack",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1920,
        -1184
      ],
      "parameters": {
        "color": 7,
        "width": 420,
        "height": 1240,
        "content": "## 💬 步骤 10:Slack 通知"
      },
      "typeVersion": 1
    },
    {
      "id": "1259aa69-0636-4229-a0f9-072f024e068e",
      "name": "Error - PDF Failed",
      "type": "n8n-nodes-base.slack",
      "position": [
        208,
        464
      ],
      "webhookId": "",
      "parameters": {
        "text": "=🚨 *Support Documentation Workflow Failed*\n\n*Error:* PDF generation failed for ticket\n*Ticket ID:* #{{ $('Extract Ticket Details').item.json.ticketId }}\n*Subject:* {{ $('Extract Ticket Details').item.json.subject }}\n*Time:* {{ $now.format('MM/DD/YYYY h:mm A') }}\n\n*Action Required:*\nPlease check the n8n workflow execution logs and manually process this ticket.\n\n_Automated Error Alert_",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_CHANNEL_ID_HERE"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "id": "5",
          "name": "Slack account"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "8ed1117f-2e16-4718-bc33-ea313098dc94",
      "name": "Error - Upload Failed",
      "type": "n8n-nodes-base.slack",
      "position": [
        1584,
        240
      ],
      "webhookId": "",
      "parameters": {
        "text": "=🚨 *Support Documentation Workflow Failed*\n\n*Error:* Google Drive upload failed\n*Ticket ID:* #{{ $('Extract Ticket Details').item.json.ticketId }}\n*Subject:* {{ $('Extract Ticket Details').item.json.subject }}\n*Time:* {{ $now.format('MM/DD/YYYY h:mm A') }}\n\n*Possible Causes:*\n• Drive storage quota exceeded\n• Invalid folder permissions\n• Authentication expired\n• Network timeout\n\n*Action Required:*\nCheck Google Drive credentials and storage, then retry this ticket.\n\n_Automated Error Alert_",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_CHANNEL_ID_HERE"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "id": "5",
          "name": "Slack account"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "bed85817-521b-4b79-9205-549d1a4f348b",
      "name": "📝 Sticky Note - Error Handling",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1504,
        224
      ],
      "parameters": {
        "color": 7,
        "width": 372,
        "height": 1308,
        "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n## ERROR HANDLING\n\n**Purpose:** Catches and reports workflow failures\n\n**Error Scenarios Handled:**\n\n1. **PDF Generation Failed**\n   - Invalid HTML syntax\n   - Memory/timeout errors\n   - Corrupted data\n   - → Sends alert with ticket ID\n\n2. **Drive Upload Failed**\n   - Storage quota exceeded\n   - Permission issues\n   - Network problems\n   - Invalid credentials\n   - → Sends alert with diagnosis\n\n**Error Notification Contains:**\n- Clear error description\n- Ticket ID for tracking\n- Timestamp of failure\n- Possible causes\n- Suggested actions\n\n**Recovery Process:**\n1. Admin receives Slack alert\n2. Reviews execution logs in n8n\n3. Identifies root cause\n4. Fixes issue (credentials, permissions, etc.)\n5. Manually retriggers for failed ticket\n\n**Advanced Error Handling:**\n- Set up retry logic (max 3 attempts)\n- Store failed tickets in separate database\n- Queue for batch reprocessing\n- Send daily error summary\n- Track error patterns\n\n**Monitoring Best Practices:**\n- Set up execution alerts\n- Monitor error rates\n- Track failure patterns\n- Review logs weekly\n- Document common fixes\n\n**Error Workflow (Optional):**\n- Create separate error workflow\n- Trigger on any node failure\n- Centralized error logging\n- More detailed diagnostics\n- Automatic retry capabilities"
      },
      "typeVersion": 1
    },
    {
      "id": "48380fdf-d2e4-4a0a-9f1d-dd7213df7061",
      "name": "📝 Sticky Note - Completion Guide",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2672,
        -800
      ],
      "parameters": {
        "color": 7,
        "width": 604,
        "height": 1228,
        "content": "## 🎉 WORKFLOW COMPLETE!\n\n**Congratulations!** Your automated support ticket documentation system is ready.\n\n**What You've Built:**\n✅ Automatic ticket capture from support tool\n✅ AI-powered intelligent summarization\n✅ Professional PDF documentation\n✅ Cloud storage with organized filing\n✅ Centralized tracking database\n✅ Team notifications\n✅ Comprehensive error handling\n\n**ROI Benefits:**\n- ⏱️ **Time Saved:** 15-30 min per ticket\n- 📈 **Scalability:** Handles 100s of tickets/day\n- 🎯 **Consistency:** Every ticket documented the same way\n- 🔍 **Searchability:** All cases indexed and findable\n- 📊 **Analytics:** Track patterns and metrics\n- 🧠 **Knowledge Base:** Build from real cases\n\n**Next Steps:**\n\n1. **Configure Credentials:**\n   - OpenAI API key\n   - Google Drive OAuth\n   - Google Sheets OAuth\n   - Slack app token\n\n2. **Customize:**\n   - Replace \"Your Company\" with your brand\n   - Update color scheme (#4CAF50 → your color)\n   - Add logo URL in HTML\n   - Modify PDF template\n   - Adjust folder structure\n\n3. **Test Thoroughly:**\n   - Use manual trigger with sample data\n   - Verify each node output\n   - Check PDF formatting\n   - Confirm Drive upload\n   - Test Slack notifications\n\n4. **Deploy:**\n   - Activate workflow\n   - Configure webhook in support tool\n   - Monitor first few executions\n   - Fine-tune as needed\n\n5. **Enhance (Optional):**\n   - Add customer survey links\n   - Create public knowledge base\n   - Build analytics dashboard\n   - Multi-language support\n   - Confluence integration\n   - Auto-categorization with ML"
      },
      "typeVersion": 1
    },
    {
      "id": "df0d9c67-d252-45a0-91c5-ba6b73ec2b83",
      "name": "HTML to PDF",
      "type": "n8n-nodes-htmlcsstopdf.htmlcsstopdf",
      "position": [
        -704,
        240
      ],
      "parameters": {
        "html_content": "={{ $json.htmlContent }}"
      },
      "credentials": {
        "htmlcsstopdfApi": {
          "id": "2",
          "name": "HtmlcsstopdfApi account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "3e02523d-6776-466d-9233-2520e25c6647",
      "name": "HTTP 请求",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        160,
        112
      ],
      "parameters": {
        "url": "={{ $json.pdf_url }}",
        "options": {
          "response": {
            "response": {}
          }
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "b8cd13d5-994a-4c59-9c14-55f96cd44af4",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2688,
        896
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 1280,
        "content": "# Workflow Credentials Setup\n\n## Required Credentials\n\n### 1. OpenAI API\n- Get key from: https://platform.openai.com/api-keys\n- In n8n: Add credential → OpenAI API\n\n### 2. PDFMunk API\n- Get key from: https://pdfmunk.com (sign up free)\n- In n8n: Add credential → HtmlcsstopdfApi\n\n### 3. Google Drive OAuth2\n- Setup at: https://console.cloud.google.com\n- Enable Google Drive API\n- In n8n: Add credential → Google Drive OAuth2\n- **Needed:** Folder ID from Drive URL\n\n### 4. Google Sheets OAuth2\n- Same Google Cloud project as Drive\n- Enable Google Sheets API\n- In n8n: Add credential → Google Sheets OAuth2\n- **Needed:** Sheet ID from Sheets URL\n\n### 5. Slack OAuth2\n- Create app at: https://api.slack.com/apps\n- Scopes needed: `chat:write`, `chat:write.public`\n- In n8n: Add credential → Slack OAuth2 API\n- **Needed:** Channel ID\n\n## Configuration Checklist\n\n- [ ] OpenAI API key\n- [ ] PDFMunk API key\n- [ ] Google Drive OAuth2\n- [ ] Google Sheets OAuth2\n- [ ] Slack OAuth2\n- [ ] Drive Folder ID in upload node\n- [ ] Sheets ID in update node\n- [ ] Slack Channel ID in all 3 Slack nodes (1 success + 2 error nodes)\n\n## Quick IDs Reference\n\n**Folder ID:** `drive.google.com/drive/folders/[COPY_THIS]`\n\n**Sheet ID:** `docs.google.com/spreadsheets/d/[COPY_THIS]/edit`\n\n**Channel ID:** Right-click channel → View details → Copy ID"
      },
      "typeVersion": 1
    },
    {
      "id": "1595445b-f5b9-47ce-a36d-9930bbd5083a",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        48,
        -528
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 784,
        "content": "# PDF to Binary Conversion\n\n## Purpose\nDownloads PDF from PDFMunk URL and converts to binary data for Google Drive upload.\n\n## Configuration\n\n**Method:** GET\n\n**URL:** `{{ $json.pdf_url }}`\n\n**Authentication:** None\n\n**Options → Response:**\n- Response Format: `File`\n- Output Binary Data: `Yes`\n- Binary Property: `data`\n\n## Why Required\nPDFMunk returns a URL, but Google Drive needs binary file data. This node bridges the gap.\n "
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "HTML to PDF": {
      "main": [
        [
          {
            "node": "Check PDF Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "node": "Upload to Google Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Capture Drive URL": {
      "main": [
        [
          {
            "node": "Check Upload Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check PDF Success": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error - PDF Failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Upload Success": {
      "main": [
        [
          {
            "node": "Update Google Sheets",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error - Upload Failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format HTML Document": {
      "main": [
        [
          {
            "node": "HTML to PDF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Google Sheets": {
      "main": [
        [
          {
            "node": "Send Slack Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Ticket Details": {
      "main": [
        [
          {
            "node": "AI Summarization (OpenAI)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload to Google Drive": {
      "main": [
        [
          {
            "node": "Capture Drive URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook - Receive Ticket": {
      "main": [
        [
          {
            "node": "Extract Ticket Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Summarization (OpenAI)": {
      "main": [
        [
          {
            "node": "Format HTML Document",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

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

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

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

需要付费吗?

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

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

适合高级用户,包含 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 查看

分享此工作流