8
n8n 中文网amn8n.com

生成GLPI支持性能报告(含SLA跟踪与邮件发送)

高级

这是一个Content Creation, Multimodal AI领域的自动化工作流,包含 20 个节点。主要使用 Set, Code, Html, Gmail, SplitOut 等节点。 生成GLPI支持性能报告,包含SLA跟踪与邮件发送功能

前置要求
  • Google 账号和 Gmail API 凭证
  • 可能需要目标 API 的认证凭证
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "meta": {
    "instanceId": "6fe840c9f744c9e44a1c50b6467124995157353b1fc80f3a8dd173276e4fc270",
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "d17beade-5e8d-482f-ad6a-d3f692b3ddc0",
      "name": "变量",
      "type": "n8n-nodes-base.set",
      "position": [
        -448,
        32
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "ea290016-88f0-4287-be92-7a3f8c7046d6",
              "name": "Server URL",
              "type": "string",
              "value": "https://server_glpi.com/"
            },
            {
              "id": "5cd7f69c-f079-4155-b887-c4b5e6e1cb64",
              "name": "App Token",
              "type": "string",
              "value": "\"You app token\""
            },
            {
              "id": "4c2a3325-1547-4333-805a-c74b711e3bc5",
              "name": "Entity name",
              "type": "string",
              "value": "\"name_entity\""
            },
            {
              "id": "96765e53-2275-41e9-a297-b64207fa4a79",
              "name": "WORK_START",
              "type": "string",
              "value": "8"
            },
            {
              "id": "4c04dfd8-64ef-4ae0-8375-e3108222a4b8",
              "name": "LUNCH_START",
              "type": "string",
              "value": "12"
            },
            {
              "id": "b76f06aa-405f-421d-b64f-7468222ddfb8",
              "name": "LUNCH_END",
              "type": "string",
              "value": "13"
            },
            {
              "id": "1a1a5672-226e-439e-8121-bb48fbe46f67",
              "name": "WORK_END",
              "type": "string",
              "value": "18"
            },
            {
              "id": "b8ee7859-6533-4642-861b-d0e0136c3b39",
              "name": "SLA_LIMIT_HOURS",
              "type": "string",
              "value": "24"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "3042ce97-b7b8-47c0-b702-905a31114297",
      "name": "编辑字段",
      "type": "n8n-nodes-base.set",
      "position": [
        416,
        32
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "5a116716-658c-425a-9817-b84cfa462819",
              "name": "Ticket_id",
              "type": "string",
              "value": "={{ $json[\"2\"] }}"
            },
            {
              "id": "6deaea47-3c4a-4a2f-80de-a790f4dcd417",
              "name": "Title",
              "type": "string",
              "value": "={{ $json[\"1\"] }}"
            },
            {
              "id": "a710ff8c-c898-435d-91eb-c67e71ddc5f7",
              "name": "technical_id",
              "type": "string",
              "value": "={{ $json[\"5\"] }}"
            },
            {
              "id": "0ee2215d-760e-433e-9f1f-3237a8d0860c",
              "name": "Entity",
              "type": "string",
              "value": "={{ $json[\"80\"] }}"
            },
            {
              "id": "7b02dcbd-49df-43f9-b4e4-01b7d93a9a8e",
              "name": "Opening date",
              "type": "string",
              "value": "={{ $json[\"15\"] }}"
            },
            {
              "id": "62e29ecb-0dc2-4e94-ada5-c9e076dc596d",
              "name": "Solution date",
              "type": "string",
              "value": "={{ $json[\"17\"] }}"
            },
            {
              "id": "9741e21c-b42a-42e7-a58b-68947b24f29e",
              "name": "Closing date",
              "type": "string",
              "value": "={{ $json[\"16\"] }}"
            },
            {
              "id": "06abb1e0-7d26-4ac1-bd5e-a1c3b1823e3e",
              "name": "Status",
              "type": "string",
              "value": "={{ $json[\"12\"] }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "7cf4932f-9afd-4fb4-94e7-22f2f7c634a9",
      "name": "分离输出",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        192,
        32
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "data"
      },
      "typeVersion": 1
    },
    {
      "id": "e583f78f-0288-4166-b930-ebb39a88f7ef",
      "name": "发送消息",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1056,
        -176
      ],
      "webhookId": "4bee6d0c-8e87-46ff-90f0-a3539439b8cf",
      "parameters": {
        "sendTo": "info@example.com",
        "message": "={{ $json.html }}",
        "options": {},
        "subject": "=Report - {{ $('Date range').first().json.month }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "1vGUy7BPigbNRllM",
          "name": "Gmail account"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "0cd4a9c1-5e1e-47b8-ba4c-92ffbdfef6d2",
      "name": "获取工单",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -16,
        -176
      ],
      "parameters": {
        "url": "={{ $('Variables').item.json[\"Server URL\"] }}apirest.php/search/Ticket",
        "options": {},
        "sendQuery": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "criteria[0][field]",
              "value": "15"
            },
            {
              "name": "criteria[0][searchtype]",
              "value": "morethan"
            },
            {
              "name": "criteria[0][value]",
              "value": "={{ $('Date range').item.json.startDate }}"
            },
            {
              "name": "criteria[1][link]",
              "value": "AND"
            },
            {
              "name": "criteria[1][field]",
              "value": "15"
            },
            {
              "name": "criteria[1][searchtype]",
              "value": "lessthan"
            },
            {
              "name": "criteria[1][value]",
              "value": "={{ $('Date range').item.json.endDate }}"
            },
            {
              "name": "criteria[2][link]",
              "value": "AND"
            },
            {
              "name": "criteria[2][field]",
              "value": "80"
            },
            {
              "name": "criteria[2][searchtype]",
              "value": "contains"
            },
            {
              "name": "criteria[2][value]",
              "value": "={{ $('Variables').item.json[\"Entity name\"] }}"
            },
            {
              "name": "order",
              "value": "DESC"
            },
            {
              "name": "range",
              "value": "0-999"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Session-Token",
              "value": "={{ $json.session_token }}"
            },
            {
              "name": "App-Token",
              "value": "={{ $('Variables').item.json[\"App Token\"] }}"
            }
          ]
        }
      },
      "credentials": {
        "httpBasicAuth": {
          "id": "1ri8MpwPvPOW9M8n",
          "name": "Api GLPI"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "0dc82824-be50-485d-b49a-398fff0dd4e1",
      "name": "计划触发器",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -464,
        -544
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "months",
              "triggerAtDayOfMonth": 6
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "3b5061f5-6fb0-4620-8d1d-d699f973e755",
      "name": "获取会话令牌",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -208,
        32
      ],
      "parameters": {
        "url": "={{ $json[\"Server URL\"] }}apirest.php/initSession",
        "options": {},
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "App-Token",
              "value": "={{ $json[\"App Token\"] }}"
            }
          ]
        }
      },
      "credentials": {
        "httpBasicAuth": {
          "id": "1ri8MpwPvPOW9M8n",
          "name": "Api GLPI"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "06ea7bb1-1347-4e14-8499-8df9c4b0619b",
      "name": "结束会话",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1232,
        -176
      ],
      "parameters": {
        "url": "={{ $('Variables').first().json[\"Server URL\"] }}apirest.php/killSession",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Session-Token",
              "value": "={{ $('Get session token').first().json.session_token }}"
            },
            {
              "name": "App-Token",
              "value": "={{ $('Variables').first().json[\"App Token\"] }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "5af4ce0e-c957-49a0-a081-7e5c9b84a714",
      "name": "无操作,不执行任何操作",
      "type": "n8n-nodes-base.noOp",
      "position": [
        1408,
        -176
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "51e2a4d5-d133-4ba7-9013-e24e10529a15",
      "name": "日期范围",
      "type": "n8n-nodes-base.code",
      "position": [
        -464,
        -272
      ],
      "parameters": {
        "jsCode": "// Get the date from the Schedule Trigger\nconst today = new Date($input.item.json.timestamp);\n\n// Calculate the previous month\nconst previousMonth = new Date(today.getFullYear(), today.getMonth() - 1, 1);\n\n// Start date: last day of the month before the previous month\nconst startDate = new Date(previousMonth.getFullYear(), previousMonth.getMonth(), 0);\n\n// End date: first day of the month after the previous month\nconst endDate = new Date(previousMonth.getFullYear(), previousMonth.getMonth() + 1, 1);\n\n// Format as YYYY-MM-DD\nconst startDateStr = startDate.toISOString().split('T')[0];\nconst endDateStr = endDate.toISOString().split('T')[0];\n\n// Get the report month and year\nconst monthNames = [\n  'January', 'February', 'March', 'April', 'May', 'June',\n  'July', 'August', 'September', 'October', 'November', 'December'\n];\nconst reportMonth = monthNames[previousMonth.getMonth()];\nconst reportYear = previousMonth.getFullYear();\n\nreturn {\n  json: {\n    month: `${reportMonth} ${reportYear}`,\n    startDate: startDateStr,\n    endDate: endDateStr\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "1946b065-da80-413f-aaee-0d23ce11eb6f",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -864,
        -720
      ],
      "parameters": {
        "color": 4,
        "width": 544,
        "height": 304,
        "content": "## 计划安排"
      },
      "typeVersion": 1
    },
    {
      "id": "1d5b834b-be04-4c55-9282-33586fd9a6a6",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -864,
        -400
      ],
      "parameters": {
        "width": 544,
        "height": 288,
        "content": "## 数据范围"
      },
      "typeVersion": 1
    },
    {
      "id": "1284666f-52b4-49c0-b398-5e028eb80823",
      "name": "便签2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -864,
        -96
      ],
      "parameters": {
        "color": 3,
        "width": 544,
        "height": 400,
        "content": "## 变量"
      },
      "typeVersion": 1
    },
    {
      "id": "9ec74986-a8f0-43ec-935b-e26f16e45376",
      "name": "便签4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -256,
        -368
      ],
      "parameters": {
        "color": 5,
        "width": 480,
        "height": 320,
        "content": "## 获取工单"
      },
      "typeVersion": 1
    },
    {
      "id": "f38ad03a-8f49-4169-94da-ae3765796acc",
      "name": "便签3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        256,
        -576
      ],
      "parameters": {
        "width": 544,
        "height": 528,
        "content": "## 获取报告"
      },
      "typeVersion": 1
    },
    {
      "id": "15df2869-8142-46b3-98f5-ef58bb750794",
      "name": "指标",
      "type": "n8n-nodes-base.code",
      "position": [
        544,
        -176
      ],
      "parameters": {
        "jsCode": "// n8n Function Node - GLPI Report with Case Totals\n// =================================================\n\n// Business hours configuration\nconst WORK_START = $('Variables').first().json.WORK_START;   // 8 AM\nconst LUNCH_START = $('Variables').first().json.LUNCH_START; // 12 PM\nconst LUNCH_END = $('Variables').first().json.WORK_END;   // 1 PM\nconst WORK_END = $('Variables').first().json.WORK_END;    // 6 PM\n\n// SLA configuration\nconst SLA_LIMIT_HOURS = $('Variables').first().json.SLA_LIMIT_HOURS; // cambiar aquí según SLA\n\nfunction toDate(str) {\n  return str ? new Date(str.replace(\" \", \"T\")) : null;\n}\n\nfunction isWeekend(date) {\n  const day = date.getDay();\n  return day === 0 || day === 6;\n}\n\n// Calculate effective working hours within business schedule\nfunction businessHoursDiff(start, end) {\n  if (!start || !end) return 0;\n  if (end < start) return 0;\n\n  let totalHours = 0;\n  let current = new Date(start);\n\n  while (current < end) {\n    if (isWeekend(current)) {\n      current.setDate(current.getDate() + 1);\n      current.setHours(WORK_START, 0, 0, 0);\n      continue;\n    }\n\n    const workMorningStart = new Date(current.setHours(WORK_START, 0, 0, 0));\n    const workMorningEnd = new Date(current.setHours(LUNCH_START, 0, 0, 0));\n    const workAfternoonStart = new Date(current.setHours(LUNCH_END, 0, 0, 0));\n    const workAfternoonEnd = new Date(current.setHours(WORK_END, 0, 0, 0));\n\n    const dayStart = start > workMorningStart ? start : workMorningStart;\n    const dayEnd = end < workAfternoonEnd ? end : workAfternoonEnd;\n\n    if (dayStart < workMorningEnd) {\n      totalHours += Math.max(0, (Math.min(dayEnd, workMorningEnd) - dayStart) / 3600000);\n    }\n    if (dayEnd > workAfternoonStart) {\n      totalHours += Math.max(0, (dayEnd - Math.max(dayStart, workAfternoonStart)) / 3600000);\n    }\n\n    current.setDate(current.getDate() + 1);\n    current.setHours(WORK_START, 0, 0, 0);\n  }\n\n  return totalHours;\n}\n\n// Convert decimal → \"Xh Ym\"\nfunction decimalToHM(decimal) {\n  const hours = Math.floor(decimal);\n  const minutes = Math.round((decimal - hours) * 60);\n  return `${hours}h ${minutes}m`;\n}\n\n// Initialize metrics\nlet general = { open: 0, in_progress: 0, solved: 0, closed: 0, hours: 0, sla_ok: 0, total: 0 };\nlet byTechnician = {};\n\n// Process tickets\nfor (const item of items) {\n  const t = item.json;\n\n  const opening = toDate(t[\"Opening date\"]);\n  const solution = toDate(t[\"Solution date\"]);\n  const closing = toDate(t[\"Closing date\"]);\n  const technician = t.technical_id || \"Unassigned\";\n  const status = parseInt(t.Status, 10);\n\n  const end = solution || closing;\n  const hours = end ? businessHoursDiff(opening, end) : 0;\n  const sla_ok = solution ? (hours <= SLA_LIMIT_HOURS ? 1 : 0) : 0;\n\n  // General report\n  if (status === 1) general.open++;\n  if (status === 2) general.in_progress++;\n  if (status === 5) general.solved++;\n  if (status === 6) general.closed++;\n  general.hours += hours;\n  general.sla_ok += sla_ok;\n  general.total++;\n\n  // Report by technician\n  if (!byTechnician[technician]) {\n    byTechnician[technician] = { open: 0, in_progress: 0, solved: 0, closed: 0, hours: 0, sla_ok: 0, total: 0 };\n  }\n  if (status === 1) byTechnician[technician].open++;\n  if (status === 2) byTechnician[technician].in_progress++;\n  if (status === 5) byTechnician[technician].solved++;\n  if (status === 6) byTechnician[technician].closed++;\n  byTechnician[technician].hours += hours;\n  byTechnician[technician].sla_ok += sla_ok;\n  byTechnician[technician].total++;\n}\n\n// Function to build summary\nfunction summary(data) {\n  return {\n    \"Total cases\": data.total,\n    \"Open cases\": data.open,\n    \"Cases in progress\": data.in_progress,\n    \"Solved cases\": data.solved,\n    \"Closed cases\": data.closed,\n    \"Total hours (decimal)\": data.hours.toFixed(2),\n    \"Total hours (formatted)\": decimalToHM(data.hours),\n    \"Average hours (decimal)\": data.total > 0 ? (data.hours / data.total).toFixed(2) : 0,\n    \"Average hours (formatted)\": data.total > 0 ? decimalToHM(data.hours / data.total) : \"0h 0m\",\n    \"SLA compliance\": data.total > 0 ? ((data.sla_ok / data.total) * 100).toFixed(2) + \"%\" : \"0%\"\n  };\n}\n\n// Final output\nreturn [\n  {\n    json: {\n      \"General Report\": summary(general),\n      \"Report by Technician\": Object.fromEntries(\n        Object.entries(byTechnician).map(([tech, data]) => [tech, summary(data)])\n      )\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "4dc2fc78-aedc-44ac-9c12-8343dee0ef20",
      "name": "生成报告",
      "type": "n8n-nodes-base.html",
      "position": [
        880,
        -176
      ],
      "parameters": {
        "html": "<!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    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n    <title>Case Report - Technical Support</title>\n    <style>\n        /* Reset styles */\n        body { margin: 0; padding: 0; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }\n        table { border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; }\n        img { border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; -ms-interpolation-mode: bicubic; }\n        \n        /* Responsive styles */\n        @media only screen and (max-width: 620px) {\n            .wrapper { width: 100% !important; }\n            .container { width: 100% !important; min-width: 100% !important; }\n            .mobile-padding { padding-left: 15px !important; padding-right: 15px !important; }\n            .metric-card { display: block !important; width: 100% !important; margin-bottom: 10px !important; }\n            .metric-row td { display: block !important; width: 100% !important; padding-bottom: 10px !important; }\n            .hide-mobile { display: none !important; }\n            .table-responsive { font-size: 12px !important; }\n            .table-responsive td { padding: 8px 4px !important; }\n        }\n    </style>\n</head>\n<body style=\"margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; background-color: #f3f4f6;\">\n    <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" role=\"presentation\" style=\"background-color: #f3f4f6; padding: 20px;\" class=\"wrapper\">\n        <tr>\n            <td align=\"center\">\n                <table width=\"600\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" role=\"presentation\" style=\"background-color: #ffffff; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); max-width: 600px;\" class=\"container\">\n                    \n                    <!-- Header -->\n                    <tr>\n                        <td style=\"background: linear-gradient(135deg, #2F3F64 0%, #1a2744 100%); background-color: #2F3F64; padding: 40px 30px; text-align: center;\" class=\"mobile-padding\">\n                            <h1 style=\"margin: 0; color: #ffffff; font-size: 26px; font-weight: bold; line-height: 1.3;\">\n                                📊 Technical Support Case Report - {{ $('Date range').first().json.month }}\n                            </h1>\n                            <p style=\"margin: 8px 0 0 0; color: #e2e8f0; font-size: 14px;\">\n                                Complete analysis of performance and case status\n                            </p>\n                        </td>\n                    </tr>\n                    \n                    <!-- Content -->\n                    <tr>\n                        <td style=\"padding: 30px;\" class=\"mobile-padding\">\n                            \n                            <!-- Section Title -->\n                            <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" role=\"presentation\" style=\"margin-bottom: 20px;\">\n                                <tr>\n                                    <td style=\"border-left: 4px solid #2F3F64; padding-left: 12px;\">\n                                        <h2 style=\"margin: 0; font-size: 18px; color: #0f172a; font-weight: bold;\">\n                                            📈 General Summary\n                                        </h2>\n                                    </td>\n                                </tr>\n                            </table>\n                            \n                            <!-- Metrics Row 1 -->\n                            <table width=\"100%\" cellpadding=\"8\" cellspacing=\"0\" border=\"0\" role=\"presentation\" style=\"margin-bottom: 12px;\" class=\"metric-row\">\n                                <tr>\n                                    <!-- Total Cases -->\n                                    <td width=\"25%\" style=\"background-color: #f8fafc; border-left: 4px solid #3b82f6; border-radius: 8px; padding: 16px; vertical-align: top;\" class=\"metric-card\">\n                                        <div style=\"font-size: 10px; color: #64748b; font-weight: bold; text-transform: uppercase; margin-bottom: 8px; letter-spacing: 0.5px;\">\n                                            TOTAL CASES\n                                        </div>\n                                        <div style=\"font-size: 26px; color: #0f172a; font-weight: bold; line-height: 1;\">\n                                            {{$json[\"General Report\"][\"Total cases\"]}}\n                                        </div>\n                                    </td>\n                                    \n                                    <td width=\"2%\" class=\"hide-mobile\"></td>\n                                    \n                                    <!-- Open Cases -->\n                                    <td width=\"25%\" style=\"background-color: #f8fafc; border-left: 4px solid #f59e0b; border-radius: 8px; padding: 16px; vertical-align: top;\" class=\"metric-card\">\n                                        <div style=\"font-size: 10px; color: #64748b; font-weight: bold; text-transform: uppercase; margin-bottom: 8px; letter-spacing: 0.5px;\">\n                                            OPEN CASES\n                                        </div>\n                                        <div style=\"font-size: 26px; color: #0f172a; font-weight: bold; line-height: 1;\">\n                                            {{$json[\"General Report\"][\"Open cases\"]}}\n                                        </div>\n                                    </td>\n                                    \n                                    <td width=\"2%\" class=\"hide-mobile\"></td>\n                                    \n                                    <!-- In Progress with Alert -->\n                                    <td width=\"23%\" style=\"background-color: #f8fafc; border-left: 4px solid #8b5cf6; border-radius: 8px; padding: 16px; vertical-align: top; position: relative;\" class=\"metric-card\">\n                                        <div style=\"font-size: 10px; color: #64748b; font-weight: bold; text-transform: uppercase; margin-bottom: 8px; letter-spacing: 0.5px;\">\n                                            IN PROGRESS\n                                        </div>\n                                        <div style=\"font-size: 26px; color: #0f172a; font-weight: bold; line-height: 1;\">\n                                            {{$json[\"General Report\"][\"Cases in progress\"]}}\n                                        </div>\n                                        {{parseInt($json[\"General Report\"][\"Cases in progress\"]) > 100 ? `\n                                        <div style=\"margin-top: 8px; padding: 4px 8px; background-color: #fef3c7; border-radius: 4px; font-size: 10px; color: #92400e; font-weight: bold;\">\n                                            ⚠️ HIGH VOLUME\n                                        </div>\n                                        ` : ''}}\n                                    </td>\n                                    \n                                    <td width=\"2%\" class=\"hide-mobile\"></td>\n                                    \n                                    <!-- Resolved -->\n                                    <td width=\"23%\" style=\"background-color: #f8fafc; border-left: 4px solid #10b981; border-radius: 8px; padding: 16px; vertical-align: top;\" class=\"metric-card\">\n                                        <div style=\"font-size: 10px; color: #64748b; font-weight: bold; text-transform: uppercase; margin-bottom: 8px; letter-spacing: 0.5px;\">\n                                            RESOLVED\n                                        </div>\n                                        <div style=\"font-size: 26px; color: #0f172a; font-weight: bold; line-height: 1;\">\n                                            {{$json[\"General Report\"][\"Solved cases\"]}}\n                                        </div>\n                                    </td>\n                                </tr>\n                            </table>\n                            \n                            <!-- Metrics Row 2 -->\n                            <table width=\"100%\" cellpadding=\"8\" cellspacing=\"0\" border=\"0\" role=\"presentation\" style=\"margin-bottom: 30px;\" class=\"metric-row\">\n                                <tr>\n                                    <!-- Closed -->\n                                    <td width=\"25%\" style=\"background-color: #f8fafc; border-left: 4px solid #6b7280; border-radius: 8px; padding: 16px; vertical-align: top;\" class=\"metric-card\">\n                                        <div style=\"font-size: 10px; color: #64748b; font-weight: bold; text-transform: uppercase; margin-bottom: 8px; letter-spacing: 0.5px;\">\n                                            CLOSED\n                                        </div>\n                                        <div style=\"font-size: 26px; color: #0f172a; font-weight: bold; line-height: 1;\">\n                                            {{$json[\"General Report\"][\"Closed cases\"]}}\n                                        </div>\n                                    </td>\n                                    \n                                    <td width=\"2%\" class=\"hide-mobile\"></td>\n                                    \n                                    <!-- Total Hours -->\n                                    <td width=\"25%\" style=\"background-color: #f8fafc; border-left: 4px solid #3b82f6; border-radius: 8px; padding: 16px; vertical-align: top;\" class=\"metric-card\">\n                                        <div style=\"font-size: 10px; color: #64748b; font-weight: bold; text-transform: uppercase; margin-bottom: 8px; letter-spacing: 0.5px;\">\n                                            TOTAL HOURS\n                                        </div>\n                                        <div style=\"font-size: 20px; color: #0f172a; font-weight: bold; line-height: 1;\">\n                                            {{$json[\"General Report\"][\"Total hours (formatted)\"]}}\n                                        </div>\n                                    </td>\n                                    \n                                    <td width=\"2%\" class=\"hide-mobile\"></td>\n                                    \n                                    <!-- Average -->\n                                    <td width=\"23%\" style=\"background-color: #f8fafc; border-left: 4px solid #f59e0b; border-radius: 8px; padding: 16px; vertical-align: top;\" class=\"metric-card\">\n                                        <div style=\"font-size: 10px; color: #64748b; font-weight: bold; text-transform: uppercase; margin-bottom: 8px; letter-spacing: 0.5px;\">\n                                            AVERAGE\n                                        </div>\n                                        <div style=\"font-size: 20px; color: #0f172a; font-weight: bold; line-height: 1;\">\n                                            {{$json[\"General Report\"][\"Average hours (formatted)\"]}}\n                                        </div>\n                                    </td>\n                                    \n                                    <td width=\"2%\" class=\"hide-mobile\"></td>\n                                    \n                                    <!-- SLA Compliance with Dynamic Color -->\n                                    <td width=\"23%\" style=\"background-color: #f8fafc; border-radius: 8px; padding: 16px; vertical-align: top;\" class=\"metric-card\">\n                                        {{(() => {\n                                            const sla = parseFloat($json[\"General Report\"][\"SLA compliance\"]);\n                                            const borderColor = sla >= 80 ? '#10b981' : sla >= 50 ? '#f59e0b' : '#ef4444';\n                                            return `<div style=\"border-left: 4px solid ${borderColor}; padding-left: 12px;\">\n                                                <div style=\"font-size: 10px; color: #64748b; font-weight: bold; text-transform: uppercase; margin-bottom: 8px; letter-spacing: 0.5px;\">\n                                                    SLA COMPLIANCE\n                                                </div>\n                                                <div style=\"font-size: 22px; color: #0f172a; font-weight: bold; line-height: 1;\">\n                                                    ${$json[\"General Report\"][\"SLA compliance\"]}\n                                                </div>\n                                            </div>`;\n                                        })()}}\n                                    </td>\n                                </tr>\n                            </table>\n                            \n                            <!-- Alert Section for Critical SLA -->\n                            {{parseFloat($json[\"General Report\"][\"SLA compliance\"]) < 50 ? `\n                            <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" role=\"presentation\" style=\"margin-bottom: 20px;\">\n                                <tr>\n                                    <td style=\"background-color: #fef2f2; border-left: 4px solid #dc2626; border-radius: 8px; padding: 16px;\">\n                                        <div style=\"font-size: 14px; color: #991b1b; font-weight: bold; margin-bottom: 4px;\">\n                                            ⚠️ CRITICAL ALERT\n                                        </div>\n                                        <div style=\"font-size: 13px; color: #7f1d1d; line-height: 1.5;\">\n                                            SLA compliance is below 50%. Immediate action required to improve service levels.\n                                        </div>\n                                    </td>\n                                </tr>\n                            </table>\n                            ` : ''}}\n                            \n                            <!-- Divider -->\n                            <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" role=\"presentation\" style=\"margin: 30px 0;\">\n                                <tr>\n                                    <td style=\"border-bottom: 1px solid #e2e8f0;\"></td>\n                                </tr>\n                            </table>\n                            \n                            <!-- Section Title 2 -->\n                            <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" role=\"presentation\" style=\"margin-bottom: 20px;\">\n                                <tr>\n                                    <td style=\"border-left: 4px solid #2F3F64; padding-left: 12px;\">\n                                        <h2 style=\"margin: 0; font-size: 18px; color: #0f172a; font-weight: bold;\">\n                                            👥 Technician Details\n                                        </h2>\n                                    </td>\n                                </tr>\n                            </table>\n                            \n                            <!-- Table -->\n                            <table width=\"100%\" cellpadding=\"12\" cellspacing=\"0\" border=\"0\" role=\"presentation\" style=\"border: 1px solid #e2e8f0; border-radius: 8px; overflow: hidden;\" class=\"table-responsive\">\n                                <thead>\n                                    <tr style=\"background-color: #f8fafc;\">\n                                        <th style=\"text-align: left; font-size: 11px; color: #475569; font-weight: bold; text-transform: uppercase; padding: 12px; border-bottom: 1px solid #e2e8f0;\">Technician</th>\n                                        <th style=\"text-align: center; font-size: 11px; color: #475569; font-weight: bold; text-transform: uppercase; padding: 12px; border-bottom: 1px solid #e2e8f0;\">Total</th>\n                                        <th style=\"text-align: center; font-size: 11px; color: #475569; font-weight: bold; text-transform: uppercase; padding: 12px; border-bottom: 1px solid #e2e8f0;\" class=\"hide-mobile\">Open</th>\n                                        <th style=\"text-align: center; font-size: 11px; color: #475569; font-weight: bold; text-transform: uppercase; padding: 12px; border-bottom: 1px solid #e2e8f0;\">Progress</th>\n                                        <th style=\"text-align: center; font-size: 11px; color: #475569; font-weight: bold; text-transform: uppercase; padding: 12px; border-bottom: 1px solid #e2e8f0;\" class=\"hide-mobile\">Solved</th>\n                                        <th style=\"text-align: center; font-size: 11px; color: #475569; font-weight: bold; text-transform: uppercase; padding: 12px; border-bottom: 1px solid #e2e8f0;\">Closed</th>\n                                        <th style=\"text-align: center; font-size: 11px; color: #475569; font-weight: bold; text-transform: uppercase; padding: 12px; border-bottom: 1px solid #e2e8f0;\" class=\"hide-mobile\">Total Hrs</th>\n                                        <th style=\"text-align: center; font-size: 11px; color: #475569; font-weight: bold; text-transform: uppercase; padding: 12px; border-bottom: 1px solid #e2e8f0;\" class=\"hide-mobile\">Average</th>\n                                        <th style=\"text-align: center; font-size: 11px; color: #475569; font-weight: bold; text-transform: uppercase; padding: 12px; border-bottom: 1px solid #e2e8f0;\">SLA</th>\n                                    </tr>\n                                </thead>\n                                <tbody>\n                                    {{($json[\"Report by Technician\"] && Object.keys($json[\"Report by Technician\"]).length > 0) \n                                        ? Object.keys($json[\"Report by Technician\"]).map(techId => {\n                                            const tech = $json[\"Report by Technician\"][techId];\n                                            const sla = parseFloat(tech[\"SLA compliance\"]);\n                                            const bgColor = sla >= 80 ? '#d1fae5' : sla >= 50 ? '#fef3c7' : '#fee2e2';\n                                            const textColor = sla >= 80 ? '#065f46' : sla >= 50 ? '#92400e' : '#991b1b';\n                                            const inProgress = parseInt(tech[\"Cases in progress\"]);\n                                            const progressAlert = inProgress > 100 ? 'background-color: #fef3c7;' : '';\n                                            \n                                            return `\n                                            <tr style=\"${progressAlert}\">\n                                                <td style=\"padding: 12px; font-size: 14px; color: #0f172a; font-weight: bold; border-bottom: 1px solid #f1f5f9;\">\n                                                    Technician #${techId}\n                                                </td>\n                                                <td style=\"padding: 12px; font-size: 14px; color: #334155; border-bottom: 1px solid #f1f5f9; text-align: center;\">\n                                                    ${tech[\"Total cases\"]}\n                                                </td>\n                                                <td style=\"padding: 12px; font-size: 14px; color: #334155; border-bottom: 1px solid #f1f5f9; text-align: center;\" class=\"hide-mobile\">\n                                                    ${tech[\"Open cases\"]}\n                                                </td>\n                                                <td style=\"padding: 12px; font-size: 14px; border-bottom: 1px solid #f1f5f9; text-align: center;\">\n                                                    <span style=\"${inProgress > 100 ? 'font-weight: bold; color: #92400e;' : 'color: #334155;'}\">\n                                                        ${tech[\"Cases in progress\"]}${inProgress > 100 ? ' ⚠️' : ''}\n                                                    </span>\n                                                </td>\n                                                <td style=\"padding: 12px; font-size: 14px; color: #334155; border-bottom: 1px solid #f1f5f9; text-align: center;\" class=\"hide-mobile\">\n                                                    ${tech[\"Solved cases\"]}\n                                                </td>\n                                                <td style=\"padding: 12px; font-size: 14px; color: #334155; border-bottom: 1px solid #f1f5f9; text-align: center;\">\n                                                    ${tech[\"Closed cases\"]}\n                                                </td>\n                                                <td style=\"padding: 12px; font-size: 14px; color: #334155; border-bottom: 1px solid #f1f5f9; text-align: center;\" class=\"hide-mobile\">\n                                                    ${tech[\"Total hours (formatted)\"]}\n                                                </td>\n                                                <td style=\"padding: 12px; font-size: 14px; color: #334155; border-bottom: 1px solid #f1f5f9; text-align: center;\" class=\"hide-mobile\">\n                                                    ${tech[\"Average hours (formatted)\"]}\n                                                </td>\n                                                <td style=\"padding: 12px; font-size: 14px; border-bottom: 1px solid #f1f5f9; text-align: center;\">\n                                                    <span style=\"display: inline-block; padding: 6px 12px; background-color: ${bgColor}; color: ${textColor}; font-size: 12px; font-weight: bold; border-radius: 12px; white-space: nowrap;\">\n                                                        ${tech[\"SLA compliance\"]}\n                                                    </span>\n                                                </td>\n                                            </tr>\n                                            `;\n                                        }).join('')\n                                        : '<tr><td colspan=\"9\" style=\"padding: 30px; text-align: center; color: #64748b; font-style: italic; border-bottom: 1px solid #f1f5f9;\">No technician data available</td></tr>'\n                                    }}\n                                </tbody>\n                            </table>\n                            \n                        </td>\n                    </tr>\n                    \n                    <!-- Footer -->\n                    <tr>\n                        <td style=\"background-color: #f8fafc; padding: 20px; text-align: center; border-top: 1px solid #e2e8f0;\">\n                            <p style=\"margin: 0 0 8px 0; color: #64748b; font-size: 12px;\">\n                                Report automatically generated by the technical support system\n                            </p>\n                            <p style=\"margin: 0; color: #94a3b8; font-size: 11px;\">\n                                Generated on: {{new Date().toLocaleString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit', timeZone: 'America/Bogota' })}}\n                            </p>\n                        </td>\n                    </tr>\n                    \n                </table>\n            </td>\n        </tr>\n    </table>\n</body>\n</html>"
      },
      "typeVersion": 1.2
    },
    {
      "id": "b8578b10-7ce4-4bcc-945b-b938cfbd2fb7",
      "name": "便利贴5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -368,
        -1280
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 304,
        "content": "## 在 n8n 中创建 GLPI API 凭据"
      },
      "typeVersion": 1
    },
    {
      "id": "dcfed78e-cb52-4a20-87b9-41e1699743b8",
      "name": "便签 6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -864,
        -1280
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 304,
        "content": "## GLPI 工单面板"
      },
      "typeVersion": 1
    },
    {
      "id": "5623a271-7c94-40cb-9043-b9e6c2f35c71",
      "name": "便签 7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1008,
        -320
      ],
      "parameters": {
        "width": 192,
        "height": 128,
        "content": "## 邮箱"
      },
      "typeVersion": 1
    }
  ],
  "pinData": {},
  "connections": {
    "Metrics": {
      "main": [
        [
          {
            "node": "Generate report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Variables": {
      "main": [
        [
          {
            "node": "Get session token",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Date range": {
      "main": [
        [
          {
            "node": "Variables",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "End session": {
      "main": [
        [
          {
            "node": "No Operation, do nothing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get tickets": {
      "main": [
        [
          {
            "node": "Split Out",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send a message": {
      "main": [
        [
          {
            "node": "End session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate report": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Date range",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get session token": {
      "main": [
        [
          {
            "node": "Get tickets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

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

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

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

需要付费吗?

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

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

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

作者
Luis Hernandez

Luis Hernandez

@integropen

I like solving problems and have a true passion for no-code automation. I'm thoughtful by nature and enjoy finding ways to optimize processes, always guided by my principles and values.

外部链接
在 n8n.io 查看

分享此工作流