{
  "name": "VietlinhAI | Trợ lý Zalo tra số liệu bán hàng kèm biểu đồ",
  "nodes": [
    {
      "parameters": {
        "promptType": "define",
        "text": "={{ $('Webhook').item.json.body.message.text }}",
        "options": {
          "systemMessage": "=Bạn là Bot Trợ lý [TÊN DOANH NGHIỆP CỦA BẠN], đảm nhận công việc quản trị nội bộ cho chủ nhân.\n\n---\n\nBƯỚC 1 - PHÂN LOẠI CÂU HỎI\n\nNếu câu hỏi là chào hỏi, hỏi thăm, hoặc không liên quan đến dữ liệu:\n- KHÔNG gọi bất kỳ tool nào\n- Trả về ngay: {\"message\": \"Xin chào! Tôi là Bot Trợ lý [TÊN DOANH NGHIỆP CỦA BẠN], đảm nhận công việc quản trị nội bộ cho chủ nhân của tôi. Bạn cần tra cứu thông tin gì về sản phẩm, đơn hàng hoặc khách hàng?\", \"chartConfig\": null}\n\nNếu câu hỏi liên quan đến dữ liệu → chuyển sang BƯỚC 2.\n\n---\n\nBƯỚC 2 - TRUY VẤN DỮ LIỆU\n\nTruy cập Tool \"Postgresql Execute\" để tra cứu thông tin.\n\nQuy tắc SQL:\n- Chỉ dùng SELECT\n- Không dùng DELETE, UPDATE, INSERT\n- Nếu không có GROUP BY thì LIMIT tối đa 50\n\n---\n\nBƯỚC 3 - XỬ LÝ KẾT QUẢ\n\nNếu kết quả có từ 2 bản ghi trở lên với ít nhất 1 cột số → BẮT BUỘC tạo biểu đồ.\nKHÔNG được trả danh sách text khi có thể vẽ biểu đồ.\n\nChỉ trả text (chartConfig: {}) khi:\n- Kết quả chỉ có đúng 1 giá trị duy nhất\n- Câu hỏi là chào hỏi hoặc không liên quan đến data\n\nNếu kết quả phù hợp để vẽ biểu đồ → chuyển sang BƯỚC 4.\n\n---\n\nBƯỚC 4 - TẠO BIỂU ĐỒ\n\nChọn loại biểu đồ:\n- Dữ liệu có trường thời gian (date, day, month, year) → \"line\"\n- So sánh giữa các nhóm hoặc top ranking → \"bar\"\n- Tỷ trọng / phần trăm → \"doughnut\"\n- Dữ liệu có 2 cột (label + value) → dùng trực tiếp\n- Dữ liệu có hơn 2 cột → chọn 1 cột làm label, 1 cột làm value\n- Nếu có hơn 10 labels → chỉ lấy top 10\n\nQuy tắc chart config:\n- Line chart: thêm tension = 0.4\n- Bar chart: thêm borderRadius = 5\n- Doughnut chart: thêm cutout = \"60%\"\n- Luôn có title phù hợp với câu hỏi\n- Chỉ dùng dấu ngoặc kép \", không dùng nháy đơn '\n- Không có dấu phẩy dư\n- Không dùng syntax JavaScript\n\n---\n\nĐỊNH DẠNG OUTPUT BẮT BUỘC\n\nKhông có biểu đồ:\n{\"message\": \"nội dung trả lời\", \"chartConfig\": null}\n\nCó biểu đồ:\n{\"message\": \"Giải thích ngắn gọn kết quả\", \"chartConfig\": {...}}\n\nQuy tắc tuyệt đối:\n- Chỉ trả JSON thuần\n- Không dùng markdown\n- Không dùng backtick\n- Không tạo URL QuickChart\n- Không thêm bất kỳ văn bản nào ngoài JSON"
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 3.1,
      "position": [
        -1648,
        -240
      ],
      "id": "bd4b1ec1-5cb1-4d32-a0c0-679c2f447dff",
      "name": "AI Agent"
    },
    {
      "parameters": {
        "modelName": "models/gemini-2.5-flash",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "typeVersion": 1.1,
      "position": [
        -1776,
        464
      ],
      "id": "a62f0118-7bb5-4101-a751-18accf85cf94",
      "name": "Google Gemini Chat Model"
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "={{ $('Webhook').item.json.body.message.chat.id }}"
      },
      "type": "@n8n/n8n-nodes-langchain.memoryPostgresChat",
      "typeVersion": 1.4,
      "position": [
        -1616,
        464
      ],
      "id": "30826728-59d3-48bf-9649-c8f590dcf78c",
      "name": "Postgres Chat Memory"
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Execute a SQL query in Postgres\nTable: categories (danh mục sản phẩm)\n\nSchema:\n- id\n- name\n- created_at",
        "operation": "executeQuery",
        "query": "{{ $fromAI('sql_query') }}",
        "options": {}
      },
      "type": "n8n-nodes-base.postgresTool",
      "typeVersion": 2.6,
      "position": [
        -1456,
        464
      ],
      "id": "e481031e-5fd3-4a76-b4c8-287578ceecfe",
      "name": "Truy xuất categories in Postgres"
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Execute a SQL query in Postgres\nTable: products (danh sách sản phẩm)\n\nSchema:\n\n- id\n- name\n- category_id\n- price\n- cost\n- created_at",
        "operation": "executeQuery",
        "query": "{{ $fromAI('sql_query') }}",
        "options": {}
      },
      "type": "n8n-nodes-base.postgresTool",
      "typeVersion": 2.6,
      "position": [
        -1280,
        464
      ],
      "id": "cf6ad2fd-ba99-48c9-a521-19163ba651a7",
      "name": "Truy xuất products in Postgres"
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Execute a SQL query in Postgres\nTable: product_variants (biến thể sản phẩm)\n\nSchema:\n- id\n- product_id\n- size\n- color\n- stock (số lượng tồn kho)",
        "operation": "executeQuery",
        "query": "{{ $fromAI('sql_query') }}",
        "options": {}
      },
      "type": "n8n-nodes-base.postgresTool",
      "typeVersion": 2.6,
      "position": [
        -1104,
        464
      ],
      "id": "2f94f320-41b1-4234-9091-9bed688d941e",
      "name": "Truy xuất product_variants in Postgres"
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Execute a SQL query in Postgres\nTable: customers (danh sách khách hàng)\n\nSchema:\n- id\n- name\n- city\n- created_at",
        "operation": "executeQuery",
        "query": "{{ $fromAI('sql_query') }}",
        "options": {}
      },
      "type": "n8n-nodes-base.postgresTool",
      "typeVersion": 2.6,
      "position": [
        -928,
        464
      ],
      "id": "1e469d67-f8fa-46fd-b2d1-1c12aa16f2d9",
      "name": "Truy xuất customers in Postgres"
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Execute a SQL query in Postgres\nTable: orders (đơn hàng)\n\nSchema:\n- id\n- customer_id\n- order_date\n- status\n- total_amount",
        "operation": "executeQuery",
        "query": "{{ $fromAI('sql_query') }}",
        "options": {}
      },
      "type": "n8n-nodes-base.postgresTool",
      "typeVersion": 2.6,
      "position": [
        -752,
        464
      ],
      "id": "e4406fd0-d17a-433e-87f3-f313ed74bab3",
      "name": "Truy xuất orders in Postgres"
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Execute a SQL query in Postgres\nTable: order_items (chi tiết đơn hàng)\n\nSchema:\n- id\n- order_id\n- product_id\n- qty\n- price",
        "operation": "executeQuery",
        "query": "{{ $fromAI('sql_query') }}",
        "options": {}
      },
      "type": "n8n-nodes-base.postgresTool",
      "typeVersion": 2.6,
      "position": [
        -576,
        464
      ],
      "id": "91b85f40-a6f0-4db1-b1b1-99bd22c73853",
      "name": "Execute a SQL query in Postgres"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://bot-api.zaloplatforms.com/botYOUR_BOT_TOKEN/setWebhook",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "url",
              "value": "https://your-n8n.example.com/webhook/YOUR_PATH"
            },
            {
              "name": "secret_token",
              "value": "YOUR_SECRET_TOKEN"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        -2880,
        -608
      ],
      "id": "fb418f2a-bd0b-4e11-aacb-c2000bcc26d1",
      "name": "Đăng ký webhook Zalo (chạy 1 lần)"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "4cbc70d0-d04a-4098-b088-e58ccfa77d08",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -2880,
        -224
      ],
      "id": "8a47e9ad-2df8-472c-8c7e-cfd5f283a052",
      "name": "Webhook",
      "webhookId": "be5d4383-2f51-4749-add4-10b2bf65e411"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 3
          },
          "conditions": [
            {
              "id": "45364981-06ac-4839-916c-ca8004a49f2c",
              "leftValue": "= {{ Object.keys($json.chartConfig).length }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        -1120,
        -240
      ],
      "id": "8060849a-7cac-4ffa-9b0c-08c697072114",
      "name": "If"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://bot-api.zaloplatforms.com/botYOUR_BOT_TOKEN/sendMessage",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "chat_id",
              "value": "={{ $('Webhook').item.json.body.message.chat.id }}"
            },
            {
              "name": "text",
              "value": "={{ $json.message }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        -576,
        -64
      ],
      "id": "3fd6dcd5-96b1-4273-a8e6-9e86a55ae9b0",
      "name": "Gửi Text"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://bot-api.zaloplatforms.com/botYOUR_BOT_TOKEN/sendPhoto",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "chat_id",
              "value": "={{ $('Webhook').item.json.body.message.chat.id }}"
            },
            {
              "name": "caption",
              "value": "={{ $json.message }}"
            },
            {
              "name": "photo",
              "value": "={{ $json.url }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        -560,
        -544
      ],
      "id": "69d979f9-ca5c-412a-a32e-92838fd01e38",
      "name": "Gửi chart"
    },
    {
      "parameters": {
        "jsCode": "let raw = $input.item.json.output;\n\n// Parse lần 1\nlet step1 = JSON.parse(raw);\n\n// step1 đã là object có message và chartConfig trực tiếp\nreturn [{\n  json: {\n    message: step1.message || '',\n    chartConfig: step1.chartConfig || {}\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1312,
        -240
      ],
      "id": "99195322-9f9b-4547-b976-6e8583e258ce",
      "name": "Code in JavaScript"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://quickchart.io/chart/create",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"chart\": {{ JSON.stringify($json.chartConfig) }}\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        -800,
        -544
      ],
      "id": "f771a71f-7817-41d4-aad1-1af715ab39be",
      "name": "Short link để hiển thị Mobile"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "5f97a20c-56f3-45f9-aa66-e27ac08e884a",
              "leftValue": "={{ $json.body.message.chat.id }}",
              "rightValue": "YOUR_CHAT_ID",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        -2656,
        -224
      ],
      "id": "4ef1c43e-2ae6-421c-9b38-5dbe8c72f93f",
      "name": "If1"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://bot-api.zaloplatforms.com/botYOUR_BOT_TOKEN/sendMessage",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "chat_id",
              "value": "={{ $('Webhook').item.json.body.message.chat.id }}"
            },
            {
              "name": "text",
              "value": "=🔒 Đây là chatbot Trợ lý [TÊN DOANH NGHIỆP CỦA BẠN]. Bạn phải nhập mật khẩu mới có thể sử dụng các chức năng."
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        -2480,
        32
      ],
      "id": "deb07c3f-308e-4983-a1e6-2d474eae9e8c",
      "name": "HTTP Request"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "12cf7352-d84a-4caf-9c39-c90f59d114c7",
              "leftValue": "={{ $('Webhook').item.json.body.message.text }}",
              "rightValue": "MAT_KHAU_CUA_BAN",
              "operator": {
                "type": "string",
                "operation": "equals",
                "name": "filter.operator.equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        -2256,
        32
      ],
      "id": "529f4088-feef-4114-9117-53278d2ead0d",
      "name": "If2"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://bot-api.zaloplatforms.com/botYOUR_BOT_TOKEN/sendMessage",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "chat_id",
              "value": "={{ $('Webhook').item.json.body.message.chat.id }}"
            },
            {
              "name": "text",
              "value": "=🔒 Sai mật khẩu"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        -2064,
        224
      ],
      "id": "637cbfb9-623d-4389-9f71-c75175afa15e",
      "name": "HTTP Request2"
    }
  ],
  "pinData": {},
  "connections": {
    "AI Agent": {
      "main": [
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Postgres Chat Memory": {
      "ai_memory": [
        [
          {
            "node": "AI Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Truy xuất categories in Postgres": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Truy xuất products in Postgres": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Truy xuất product_variants in Postgres": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Truy xuất customers in Postgres": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Truy xuất orders in Postgres": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Execute a SQL query in Postgres": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "If1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If": {
      "main": [
        [
          {
            "node": "Short link để hiển thị Mobile",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Gửi Text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Short link để hiển thị Mobile": {
      "main": [
        [
          {
            "node": "Gửi chart",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If1": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "node": "If2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If2": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "HTTP Request2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "availableInMCP": false
  },
  "nodeGroups": [],
  "tags": []
}