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
MyConversation.gql
Variables
Response 200 OK
fetchHistory.ts
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
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)
{
"request": {
"agentCode": "buddy",
"beforeAt": null,
"limit": 20,
"sort": [{ "field": "sentAt", "direction": "desc" }]
}
}Phản Hồi Thành Công (JSON Response)
{
"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:
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ử
- Đả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. - Xử lý tin nhắn dở dang (
PENDING): Kiểm tra phần tử tin nhắn cuối cùng: nếusenderType === 'ASSISTANT'vàstatus === 'PENDING', ứng dụng hãy kích hoạt ngay listener Firebase Firestore theo mãidcủa tin nhắn đó để tiếp tục nhận câu trả lời.