Files
SLACK_COORD_RELATORIO/Resumo Diario GLPI - SQL Slack.n8n.json
T

128 lines
9.1 KiB
JSON

{
"name": "Resumo Diário GLPI - SQL Slack",
"nodes": [
{
"parameters": {},
"id": "manual-trigger-diario-sql",
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [180, 300]
},
{
"parameters": {
"assignments": {
"assignments": [
{"id": "data-alvo", "name": "data_alvo", "type": "string", "value": ""},
{"id": "slack-webhook-url", "name": "slack_webhook_url", "type": "string", "value": "https://hooks.slack.com/services/SEU/WEBHOOK/AQUI"},
{"id": "openai-api-key", "name": "openai_api_key", "type": "string", "value": "COLE_SUA_OPENAI_API_KEY_AQUI"},
{"id": "openai-model", "name": "openai_model", "type": "string", "value": "gpt-5.5"}
]
},
"options": {}
},
"id": "config-diario-sql",
"name": "Config - Diario SQL",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [420, 300]
},
{
"parameters": {
"jsCode": "function normalizarDataAlvo(valor) {\n const texto = String(valor || '').trim();\n\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(texto)) return texto;\n\n const matchPtBr = texto.match(/^(\\d{2})\\/(\\d{2})\\/(\\d{2}|\\d{4})$/);\n if (matchPtBr) {\n const [, dia, mes, ano] = matchPtBr;\n const anoCompleto = ano.length === 2 ? `20${ano}` : ano;\n return `${anoCompleto}-${mes}-${dia}`;\n }\n\n return new Date().toISOString().slice(0, 10);\n}\n\nconst config = $('Config - Diario SQL').first().json;\nconst dataAlvo = normalizarDataAlvo(config.data_alvo);\n\nconst filtroCategorias = `\n SELECT id FROM glpi_itilcategories\n WHERE JSON_CONTAINS(ancestors_cache, '{\"7\":7}') OR id = 7\n OR JSON_CONTAINS(ancestors_cache, '{\"16\":16}') OR id = 16\n OR JSON_CONTAINS(ancestors_cache, '{\"17\":17}') OR id = 17\n OR JSON_CONTAINS(ancestors_cache, '{\"18\":18}') OR id = 18\n`;\n\nconst query = `\nSELECT\n '${dataAlvo}' AS data_alvo,\n COALESCE(abertos.abertos, 0) AS abertos,\n COALESCE(fechados.fechados, 0) AS fechados,\n COALESCE(fechados.fechados, 0) - COALESCE(abertos.abertos, 0) AS saldo,\n COALESCE(pendentes.pendentes, 0) AS pendentes,\n COALESCE(alta_prioridade.alta_prioridade_pendentes, 0) AS alta_prioridade_pendentes,\n COALESCE(categorias.categorias, 'Sem registros') AS categorias_recorrentes,\n COALESCE(clientes.clientes, 'Sem registros') AS clientes_volume\nFROM\n (SELECT COUNT(*) AS abertos FROM glpi_tickets WHERE is_deleted = false AND DATE(date) = '${dataAlvo}' AND itilcategories_id IN (${filtroCategorias})) abertos\nCROSS JOIN\n (SELECT COUNT(*) AS fechados FROM glpi_tickets WHERE closedate IS NOT NULL AND is_deleted = false AND DATE(closedate) = '${dataAlvo}' AND itilcategories_id IN (${filtroCategorias})) fechados\nCROSS JOIN\n (SELECT COUNT(*) AS pendentes FROM glpi_tickets WHERE is_deleted = false AND status NOT IN (5, 6) AND itilcategories_id IN (${filtroCategorias})) pendentes\nCROSS JOIN\n (SELECT COUNT(*) AS alta_prioridade_pendentes FROM glpi_tickets WHERE is_deleted = false AND status NOT IN (5, 6) AND priority >= 4 AND itilcategories_id IN (${filtroCategorias})) alta_prioridade\nCROSS JOIN\n (SELECT COALESCE(GROUP_CONCAT(CONCAT(nome, ': ', total) ORDER BY total DESC SEPARATOR '\\n'), 'Sem registros') AS categorias FROM (SELECT COALESCE(c.completename, c.name, 'Nao informado') AS nome, COUNT(*) AS total FROM glpi_tickets t LEFT JOIN glpi_itilcategories c ON c.id = t.itilcategories_id WHERE t.is_deleted = false AND (DATE(t.date) = '${dataAlvo}' OR DATE(t.closedate) = '${dataAlvo}') AND t.itilcategories_id IN (${filtroCategorias}) GROUP BY t.itilcategories_id, c.completename, c.name ORDER BY total DESC LIMIT 5) ranking_categorias) categorias\nCROSS JOIN\n (SELECT COALESCE(GROUP_CONCAT(CONCAT(nome, ': ', total, ' chamados') ORDER BY total DESC SEPARATOR '\\n'), 'Sem registros') AS clientes FROM (SELECT COALESCE(e.completename, e.name, 'Nao informado') AS nome, COUNT(*) AS total FROM glpi_tickets t LEFT JOIN glpi_entities e ON e.id = t.entities_id WHERE t.is_deleted = false AND (DATE(t.date) = '${dataAlvo}' OR DATE(t.closedate) = '${dataAlvo}') AND t.itilcategories_id IN (${filtroCategorias}) GROUP BY t.entities_id, e.completename, e.name ORDER BY total DESC LIMIT 5) ranking_clientes) clientes;`;\n\nreturn [{ json: { ...config, data_alvo_normalizada: dataAlvo, query } }];"
},
"id": "code-montar-sql-diario",
"name": "Code - Montar SQL diario",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [680, 300]
},
{
"parameters": {
"operation": "executeQuery",
"query": "={{ $json.query }}",
"options": {}
},
"id": "mysql-diario-glpi",
"name": "MySQL - Indicadores diario GLPI",
"type": "n8n-nodes-base.mySql",
"typeVersion": 2.4,
"position": [940, 300],
"credentials": {"mySql": {"id": "", "name": "MySQL GLPI"}}
},
{
"parameters": {
"jsCode": "const row = $input.first().json;\n\nconst textoBase = [\n '*Resumo diário do suporte GLPI*',\n '',\n `Data do relatório: ${row.data_alvo}`,\n '',\n '*Resumo geral*',\n `- Chamados abertos no dia: ${row.abertos}`,\n `- Chamados fechados no dia: ${row.fechados}`,\n `- Pendentes atuais: ${row.pendentes}`,\n `- Alta prioridade ainda pendentes: ${row.alta_prioridade_pendentes}`,\n `- Saldo fechados - abertos: ${row.saldo}`,\n '',\n '*Categorias mais recorrentes*',\n String(row.categorias_recorrentes || 'Sem registros').split('\\n').map(l => `- ${l}`).join('\\n'),\n '',\n '*Clientes com maior volume*',\n String(row.clientes_volume || 'Sem registros').split('\\n').map(l => `- ${l}`).join('\\n'),\n '',\n '*Observação*',\n '- Chamados fechados no dia podem ter sido abertos em dias ou semanas anteriores.'\n].join('\\n');\n\nreturn [{ json: { ...row, texto_base: textoBase } }];"
},
"id": "code-preparar-diario",
"name": "Code - Preparar indicadores",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1200, 300]
},
{
"parameters": {
"method": "POST",
"url": "https://api.openai.com/v1/chat/completions",
"sendHeaders": true,
"headerParameters": {"parameters": [{"name": "Content-Type", "value": "application/json"}, {"name": "Authorization", "value": "={{ 'Bearer ' + $('Config - Diario SQL').first().json.openai_api_key }}"}]},
"sendBody": true,
"contentType": "json",
"specifyBody": "json",
"jsonBody": "={{ { model: $('Config - Diario SQL').first().json.openai_model || 'gpt-5.5', messages: [ { role: 'system', content: 'Você é um analista de suporte. Gere um resumo diário curto em português do Brasil para Slack. Use somente os números fornecidos.' }, { role: 'user', content: 'Dados diários GLPI:\\n' + JSON.stringify($json) + '\\n\\nExplique que fechados podem ter sido abertos em datas anteriores. Seja objetivo.' } ] } }}",
"options": {}
},
"id": "openai-diario-sql",
"name": "OpenAI - Gerar resumo",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [1460, 300],
"continueOnFail": true,
"retryOnFail": true,
"maxTries": 3,
"waitBetweenTries": 30000
},
{
"parameters": {
"jsCode": "const data = $input.first().json;\nconst textoIa = data.choices?.[0]?.message?.content;\nconst fallback = $('Code - Preparar indicadores').first().json.texto_base;\n\nreturn [{ json: { text: textoIa || fallback } }];"
},
"id": "code-slack-diario",
"name": "Code - Preparar mensagem Slack",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1720, 300]
},
{
"parameters": {
"method": "POST",
"url": "={{ $('Config - Diario SQL').first().json.slack_webhook_url }}",
"sendBody": true,
"contentType": "json",
"specifyBody": "keypair",
"bodyParameters": {"parameters": [{"name": "text", "value": "={{ $json.text }}"}]},
"options": {}
},
"id": "http-slack-diario-sql",
"name": "HTTP - Slack enviar mensagem",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [1980, 300]
}
],
"pinData": {},
"connections": {
"Manual Trigger": {"main": [[{"node": "Config - Diario SQL", "type": "main", "index": 0}]]},
"Config - Diario SQL": {"main": [[{"node": "Code - Montar SQL diario", "type": "main", "index": 0}]]},
"Code - Montar SQL diario": {"main": [[{"node": "MySQL - Indicadores diario GLPI", "type": "main", "index": 0}]]},
"MySQL - Indicadores diario GLPI": {"main": [[{"node": "Code - Preparar indicadores", "type": "main", "index": 0}]]},
"Code - Preparar indicadores": {"main": [[{"node": "OpenAI - Gerar resumo", "type": "main", "index": 0}]]},
"OpenAI - Gerar resumo": {"main": [[{"node": "Code - Preparar mensagem Slack", "type": "main", "index": 0}]]},
"Code - Preparar mensagem Slack": {"main": [[{"node": "HTTP - Slack enviar mensagem", "type": "main", "index": 0}]]}
},
"active": false,
"settings": {"executionOrder": "v1"},
"versionId": "",
"meta": {"templateCredsSetupCompleted": false},
"tags": []
}