Pango AIPango AI Team
Module 01: Chatbot Service (API)1.5 GraphQL API & Code Examples
Module 01 • API Gateway

4.4. Lấy Lịch Sử Hội Thoại (MyConversation)

QUERY Yêu cầu Bearer Token

Truy vấn danh sách các tin nhắn cũ trong toàn bộ cuộc hội thoại của người dùng theo agentCode. Hỗ trợ phân trang tiến lùi dựa trên mốc thời gian beforeAt và số lượng limit.


💻 Chi Tiết Truy Vấn & Mã Nguồn Mẫu

GraphQL Query & Payload:

GraphQL Query

MyConversation.gql
query MyConversation($request: ChatMessageSearchRequestDtoInput) {
  myConversation(request: $request) {
    items {
      id
      chatSessionId
      senderType
      senderId
      senderName
      isSessionClosed
      closedAt
      feedbacks {
        id
        type
        emotion
        rating
        comment
        reply
        createdAt
        updatedAt
      }
      message
      sentAt
      status
      attachments
    }
    page {
      currentPage
      pageSize
      totalRecords
      totalPages
    }
    total
  }
}

Variables (JSON)

Variables
{
  "request": {
    "agentCode": "buddy",
    "beforeAt": null,
    "limit": 20,
    "sort": [{ "field": "sentAt", "direction": "desc" }]
  }
}

Phản Hồi Thành Công (JSON Response)

Response 200 OK
{
  "data": {
    "myConversation": {
      "items": [
        {
          "id": "6a8913448201e708713c6e48",
          "chatSessionId": "6a7d45b5aa2c04f1151bee2b",
          "senderType": "ASSISTANT",
          "senderId": "bot-buddy",
          "senderName": "V-Buddy Assistant",
          "isSessionClosed": false,
          "closedAt": null,
          "feedbacks": [],
          "message": "Dạ, VUS hiện có các khóa học tiếng Anh mầm non SmartKids cho bé 4-6 tuổi...",
          "sentAt": 1740200015000,
          "status": "COMPLETED",
          "attachments": null
        },
        {
          "id": "6a8913448201e708713c6e47",
          "chatSessionId": "6a7d45b5aa2c04f1151bee2b",
          "senderType": "USER",
          "senderId": "737a445da12ddbd3d5d40a88ee314864",
          "senderName": "Nguyen Van A",
          "isSessionClosed": false,
          "closedAt": null,
          "feedbacks": [],
          "message": "VUS có những khóa học nào dành cho trẻ em?",
          "sentAt": 1740200010000,
          "status": "COMPLETED",
          "attachments": null
        }
      ],
      "page": {
        "currentPage": 1,
        "pageSize": 20,
        "totalRecords": 2,
        "totalPages": 1
      },
      "total": 2
    }
  }
}

TypeScript / Axios:

fetchHistory.ts
import axios from 'axios';

export interface ChatMessageItem {
  id: string;
  chatSessionId: string;
  senderType: 'USER' | 'ASSISTANT' | 'SYSTEM';
  senderId: string;
  senderName: string;
  isSessionClosed: boolean;
  closedAt?: number | null;
  feedbacks: Array<{
    id: string;
    rating: number;
    comment?: string;
  }>;
  message: string;
  sentAt: number;
  status: 'PENDING' | 'COMPLETED' | 'FAILED';
  attachments?: any;
}

/**
 * Tải lịch sử tin nhắn hội thoại
 */
export async function fetchConversationHistory(
  apiUrl: string = process.env.VITE_API_URL!,
  token: string,
  agentCode: string = 'buddy',
  beforeAt?: number | null,
  limit: number = 20
): Promise<ChatMessageItem[]> {
  const query = `
    query MyConversation($request: ChatMessageSearchRequestDtoInput) {
      myConversation(request: $request) {
        items {
          id
          chatSessionId
          senderType
          senderId
          senderName
          isSessionClosed
          closedAt
          feedbacks {
            id
            type
            emotion
            rating
            comment
            reply
            createdAt
            updatedAt
          }
          message
          sentAt
          status
          attachments
        }
        page {
          currentPage
          pageSize
          totalRecords
          totalPages
        }
        total
      }
    }
  `;

  const response = await axios.post(
    apiUrl,
    {
      query,
      variables: {
        request: {
          agentCode,
          beforeAt: beforeAt || null,
          limit,
          sort: [{ field: 'sentAt', direction: 'desc' }],
        },
      },
      operationName: 'MyConversation',
    },
    {
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`,
        'orgId': process.env.VITE_ORG_ID,
        'x-org-id': process.env.VITE_ORG_ID,
        'x-client-type': 'app',
        'x-external-private-key': process.env.VITE_EXTERNAL_PRIVATE_KEY,
      },
    }
  );

  return response.data?.data?.myConversation?.items || [];
}

💡 Best Practice Khi Tải Lịch Sử

  1. Đảo chiều mảng hiển thị: Dữ liệu trả về mặc định sắp xếp theo sentAt: desc (tin nhắn mới nhất ở đầu mảng). Khi render giao diện chat từ trên xuống dưới, hãy đảo ngược mảng (items.reverse()) để tin nhắn cũ ở trên và mới nhất ở dưới.
  2. Xử lý tin nhắn dở dang (PENDING): Kiểm tra phần tử tin nhắn cuối cùng: nếu senderType === 'ASSISTANT'status === 'PENDING', ứng dụng hãy kích hoạt ngay listener Firebase Firestore theo mã id của tin nhắn đó để tiếp tục nhận câu trả lời.