@warriorteam/redai-zalo-sdk 1.3.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +565 -550
  2. package/dist/index.d.ts +0 -2
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -5
  5. package/dist/index.js.map +1 -1
  6. package/dist/services/article.service.d.ts +1 -1
  7. package/dist/services/article.service.d.ts.map +1 -1
  8. package/dist/services/article.service.js +24 -16
  9. package/dist/services/article.service.js.map +1 -1
  10. package/dist/services/auth.service.d.ts +1 -0
  11. package/dist/services/auth.service.d.ts.map +1 -1
  12. package/dist/services/auth.service.js +23 -9
  13. package/dist/services/auth.service.js.map +1 -1
  14. package/dist/services/consultation.service.d.ts +63 -16
  15. package/dist/services/consultation.service.d.ts.map +1 -1
  16. package/dist/services/consultation.service.js +264 -49
  17. package/dist/services/consultation.service.js.map +1 -1
  18. package/dist/services/general-message.service.d.ts +2 -25
  19. package/dist/services/general-message.service.d.ts.map +1 -1
  20. package/dist/services/general-message.service.js +11 -112
  21. package/dist/services/general-message.service.js.map +1 -1
  22. package/dist/services/group-management.service.d.ts +1 -1
  23. package/dist/services/group-management.service.d.ts.map +1 -1
  24. package/dist/services/group-management.service.js +59 -27
  25. package/dist/services/group-management.service.js.map +1 -1
  26. package/dist/services/group-message.service.d.ts +1 -1
  27. package/dist/services/group-message.service.d.ts.map +1 -1
  28. package/dist/services/group-message.service.js +49 -23
  29. package/dist/services/group-message.service.js.map +1 -1
  30. package/dist/services/message-management.service.d.ts +21 -2
  31. package/dist/services/message-management.service.d.ts.map +1 -1
  32. package/dist/services/message-management.service.js +83 -7
  33. package/dist/services/message-management.service.js.map +1 -1
  34. package/dist/services/oa.service.d.ts +1 -0
  35. package/dist/services/oa.service.d.ts.map +1 -1
  36. package/dist/services/oa.service.js +23 -5
  37. package/dist/services/oa.service.js.map +1 -1
  38. package/dist/services/user.service.d.ts +108 -18
  39. package/dist/services/user.service.d.ts.map +1 -1
  40. package/dist/services/user.service.js +260 -59
  41. package/dist/services/user.service.js.map +1 -1
  42. package/dist/services/video-upload.service.d.ts +1 -1
  43. package/dist/services/video-upload.service.d.ts.map +1 -1
  44. package/dist/services/video-upload.service.js +11 -8
  45. package/dist/services/video-upload.service.js.map +1 -1
  46. package/dist/services/zns.service.d.ts +88 -21
  47. package/dist/services/zns.service.d.ts.map +1 -1
  48. package/dist/services/zns.service.js +157 -42
  49. package/dist/services/zns.service.js.map +1 -1
  50. package/dist/types/group.d.ts +5 -5
  51. package/dist/types/group.d.ts.map +1 -1
  52. package/dist/types/user.d.ts +155 -12
  53. package/dist/types/user.d.ts.map +1 -1
  54. package/dist/types/user.js.map +1 -1
  55. package/dist/types/webhook.d.ts +8 -0
  56. package/dist/types/webhook.d.ts.map +1 -1
  57. package/dist/types/webhook.js.map +1 -1
  58. package/dist/types/zns.d.ts +67 -33
  59. package/dist/types/zns.d.ts.map +1 -1
  60. package/dist/zalo-sdk.d.ts +3 -11
  61. package/dist/zalo-sdk.d.ts.map +1 -1
  62. package/dist/zalo-sdk.js +0 -10
  63. package/dist/zalo-sdk.js.map +1 -1
  64. package/docs/CONSULTATION_SERVICE.md +512 -330
  65. package/docs/GROUP_MANAGEMENT.md +2 -2
  66. package/docs/USER_MANAGEMENT.md +481 -1248
  67. package/docs/WEBHOOK_EVENTS.md +55 -3
  68. package/package.json +1 -1
  69. package/dist/services/tag.service.d.ts +0 -144
  70. package/dist/services/tag.service.d.ts.map +0 -1
  71. package/dist/services/tag.service.js +0 -184
  72. package/dist/services/tag.service.js.map +0 -1
  73. package/dist/services/user-management.service.d.ts +0 -117
  74. package/dist/services/user-management.service.d.ts.map +0 -1
  75. package/dist/services/user-management.service.js +0 -239
  76. package/dist/services/user-management.service.js.map +0 -1
@@ -1,1248 +1,481 @@
1
- # RedAI Zalo SDK - User Management Guide
2
-
3
- ## Tổng quan
4
-
5
- User Management trong RedAI Zalo SDK cung cấp các công cụ toàn diện để quản lý người dùng, bao gồm:
6
-
7
- - 👥 **UserService** - Truy cập thông tin user social OA followers
8
- - 🏷️ **UserManagementService** - Quản lý user profiles interactions
9
- - 📊 **User Analytics** - Phân tích hành vi tương tác
10
- - 🔍 **User Search** - Tìm kiếm lọc users
11
- - 📋 **Bulk Operations** - Xử lý hàng loạt users
12
-
13
- ---
14
-
15
- ## UserService
16
-
17
- Truy cập thông tin user từ Social API và OA followers.
18
-
19
- ### Khởi tạo
20
-
21
- ```typescript
22
- import { ZaloSDK } from "@warriorteam/redai-zalo-sdk";
23
-
24
- const zalo = new ZaloSDK({
25
- appId: "your-app-id",
26
- appSecret: "your-app-secret"
27
- });
28
-
29
- // Access user service
30
- const userService = zalo.user;
31
- ```
32
-
33
- ### 1. Lấy thông tin user Social
34
-
35
- ```typescript
36
- // Lấy thông tin user từ Social API
37
- const socialUserInfo = await zalo.getSocialUserInfo(
38
- socialAccessToken,
39
- "id,name,picture,birthday,gender,locale"
40
- );
41
-
42
- console.log("User ID:", socialUserInfo.id);
43
- console.log("Name:", socialUserInfo.name);
44
- console.log("Avatar:", socialUserInfo.picture?.data.url);
45
- console.log("Birthday:", socialUserInfo.birthday);
46
- ```
47
-
48
- ### 2. Lấy thông tin user OA
49
-
50
- ```typescript
51
- // Lấy thông tin user đã tương tác với OA
52
- const userInfo = await zalo.user.getUserInfo(
53
- oaAccessToken,
54
- "zalo-user-id"
55
- );
56
-
57
- console.log("User Info:", {
58
- userId: userInfo.user_id,
59
- displayName: userInfo.display_name,
60
- avatar: userInfo.avatar,
61
- userGender: userInfo.user_gender,
62
- userAlias: userInfo.user_alias
63
- });
64
- ```
65
-
66
- ### 3. Lấy danh sách users
67
-
68
- ```typescript
69
- // Lấy danh sách users với filter
70
- const userList = await zalo.user.getUserList(oaAccessToken, {
71
- offset: 0,
72
- count: 50,
73
- tag_name: "VIP_CUSTOMER", // Lọc theo tag (tùy chọn)
74
- is_follower: true // Chỉ lấy followers (tùy chọn)
75
- });
76
-
77
- console.log("Total users:", userList.total);
78
- userList.data.forEach(user => {
79
- console.log(`${user.display_name} - ${user.user_id}`);
80
- });
81
- ```
82
-
83
- ### 4. Lấy danh sách followers
84
-
85
- ```typescript
86
- // Lấy tất cả followers của OA
87
- const followers = await zalo.user.getFollowers(oaAccessToken, {
88
- offset: 0,
89
- count: 100
90
- });
91
-
92
- console.log("Total followers:", followers.total);
93
- followers.data.forEach(follower => {
94
- console.log(`Follower: ${follower.display_name}`);
95
- console.log(`Follow time: ${new Date(follower.user_id_by_app).toLocaleString()}`);
96
- });
97
- ```
98
-
99
- ---
100
-
101
- ## UserManagementService
102
-
103
- Quản user profiles chi tiết interactions.
104
-
105
- ### Khởi tạo
106
-
107
- ```typescript
108
- const userManagement = zalo.userManagement;
109
- ```
110
-
111
- ### 1. Lấy user profile chi tiết
112
-
113
- ```typescript
114
- // Lấy profile đầy đủ của user
115
- const userProfile = await zalo.userManagement.getUserProfile(
116
- oaAccessToken,
117
- "user-zalo-id"
118
- );
119
-
120
- console.log("User Profile:", {
121
- userId: userProfile.user_id,
122
- displayName: userProfile.display_name,
123
- avatar: userProfile.avatar,
124
- phone: userProfile.shared_info?.phone,
125
- address: userProfile.shared_info?.address,
126
- tags: userProfile.tags,
127
- notes: userProfile.notes,
128
- lastInteraction: userProfile.last_interaction_time
129
- });
130
- ```
131
-
132
- ### 2. Cập nhật user profile
133
-
134
- ```typescript
135
- // Cập nhật thông tin user
136
- const updatedProfile = await zalo.userManagement.updateUserProfile(
137
- oaAccessToken,
138
- "user-zalo-id",
139
- {
140
- notes: "Customer quan tâm sản phẩm cao cấp",
141
- custom_fields: {
142
- customer_tier: "VIP",
143
- preferred_contact: "zalo",
144
- last_purchase_date: "2024-12-01",
145
- total_spent: "5000000"
146
- }
147
- }
148
- );
149
- ```
150
-
151
- ### 3. Lấy lịch sử tương tác
152
-
153
- ```typescript
154
- // Lấy tất cả interactions với user
155
- const interactions = await zalo.userManagement.getUserInteractions(
156
- oaAccessToken,
157
- "user-zalo-id",
158
- {
159
- from_time: Date.now() - (30 * 24 * 60 * 60 * 1000), // 30 days ago
160
- to_time: Date.now(),
161
- interaction_type: "all", // message, call, order, etc.
162
- limit: 100
163
- }
164
- );
165
-
166
- interactions.forEach(interaction => {
167
- console.log(`${interaction.type}: ${interaction.content} at ${new Date(interaction.time).toLocaleString()}`);
168
- });
169
- ```
170
-
171
- ### 4. Phân tích user analytics
172
-
173
- ```typescript
174
- // Lấy analytics của user
175
- const userAnalytics = await zalo.userManagement.getUserAnalytics(
176
- oaAccessToken,
177
- "user-zalo-id"
178
- );
179
-
180
- console.log("User Analytics:", {
181
- totalMessages: userAnalytics.total_messages_sent,
182
- totalMessagesReceived: userAnalytics.total_messages_received,
183
- avgResponseTime: userAnalytics.avg_response_time,
184
- engagementScore: userAnalytics.engagement_score,
185
- lastSeenTime: userAnalytics.last_seen_time,
186
- preferredTime: userAnalytics.most_active_time
187
- });
188
- ```
189
-
190
- ### 5. Tìm kiếm users
191
-
192
- ```typescript
193
- // Tìm kiếm users theo điều kiện
194
- const searchResult = await zalo.userManagement.searchUsers(
195
- oaAccessToken,
196
- {
197
- query: "Nguyễn", // Tên hoặc phone
198
- tags: ["VIP", "Premium"], // Tags
199
- interaction_period: 30, // Tương tác trong 30 ngày qua
200
- min_order_value: 1000000, // Đơn hàng tối thiểu
201
- location: "Ho Chi Minh", // Địa điểm
202
- gender: "male", // Giới tính
203
- age_range: "25-35", // Độ tuổi
204
- limit: 50,
205
- offset: 0
206
- }
207
- );
208
-
209
- console.log(`Found ${searchResult.total} users matching criteria`);
210
- searchResult.users.forEach(user => {
211
- console.log(`${user.display_name} - Score: ${user.match_score}`);
212
- });
213
- ```
214
-
215
- ---
216
-
217
- ## User Segmentation & Analytics
218
-
219
- ### 1. User Segmentation Service
220
-
221
- ```typescript
222
- class UserSegmentationService {
223
- constructor(private zalo: ZaloSDK, private accessToken: string) {}
224
-
225
- // Phân đoạn users theo hành vi mua hàng
226
- async segmentUsersByPurchaseBehavior(): Promise<UserSegments> {
227
- const allUsers = await this.getAllUsers();
228
- const segments = {
229
- highValue: [], // > 10M VND
230
- mediumValue: [], // 1M - 10M VND
231
- lowValue: [], // < 1M VND
232
- inactive: [] // Không mua trong 90 ngày
233
- };
234
-
235
- for (const user of allUsers) {
236
- const analytics = await this.zalo.userManagement.getUserAnalytics(
237
- this.accessToken,
238
- user.user_id
239
- );
240
-
241
- const totalSpent = analytics.total_spent || 0;
242
- const daysSinceLastPurchase = analytics.days_since_last_purchase || 999;
243
-
244
- if (daysSinceLastPurchase > 90) {
245
- segments.inactive.push(user);
246
- } else if (totalSpent > 10000000) {
247
- segments.highValue.push(user);
248
- } else if (totalSpent > 1000000) {
249
- segments.mediumValue.push(user);
250
- } else {
251
- segments.lowValue.push(user);
252
- }
253
- }
254
-
255
- return segments;
256
- }
257
-
258
- // Phân đoạn theo engagement
259
- async segmentUsersByEngagement(): Promise<EngagementSegments> {
260
- const users = await this.getAllUsers();
261
- const segments = {
262
- champions: [], // High value + High engagement
263
- loyalists: [], // High engagement
264
- potential: [], // Medium engagement
265
- atRisk: [], // Low recent engagement
266
- lost: [] // No recent engagement
267
- };
268
-
269
- for (const user of users) {
270
- const analytics = await this.zalo.userManagement.getUserAnalytics(
271
- this.accessToken,
272
- user.user_id
273
- );
274
-
275
- const engagementScore = analytics.engagement_score || 0;
276
- const daysSinceLastInteraction = analytics.days_since_last_interaction || 999;
277
- const totalSpent = analytics.total_spent || 0;
278
-
279
- if (engagementScore > 80 && totalSpent > 5000000) {
280
- segments.champions.push(user);
281
- } else if (engagementScore > 70) {
282
- segments.loyalists.push(user);
283
- } else if (engagementScore > 40) {
284
- segments.potential.push(user);
285
- } else if (daysSinceLastInteraction < 30) {
286
- segments.atRisk.push(user);
287
- } else {
288
- segments.lost.push(user);
289
- }
290
- }
291
-
292
- return segments;
293
- }
294
-
295
- private async getAllUsers(): Promise<UserProfile[]> {
296
- const allUsers = [];
297
- let offset = 0;
298
- const limit = 100;
299
-
300
- while (true) {
301
- const userList = await this.zalo.user.getUserList(this.accessToken, {
302
- offset,
303
- count: limit
304
- });
305
-
306
- allUsers.push(...userList.data);
307
-
308
- if (userList.data.length < limit) break;
309
- offset += limit;
310
- }
311
-
312
- return allUsers;
313
- }
314
- }
315
- ```
316
-
317
- ### 2. Customer Lifetime Value Analysis
318
-
319
- ```typescript
320
- class CLVAnalysisService {
321
- constructor(private zalo: ZaloSDK, private accessToken: string) {}
322
-
323
- async calculateUserCLV(userId: string): Promise<CLVMetrics> {
324
- const analytics = await this.zalo.userManagement.getUserAnalytics(
325
- this.accessToken,
326
- userId
327
- );
328
-
329
- const interactions = await this.zalo.userManagement.getUserInteractions(
330
- this.accessToken,
331
- userId,
332
- { interaction_type: "purchase", limit: 1000 }
333
- );
334
-
335
- const purchases = interactions.filter(i => i.type === "purchase");
336
-
337
- if (purchases.length === 0) {
338
- return { clv: 0, tier: "inactive", recommendations: [] };
339
- }
340
-
341
- // Tính toán CLV
342
- const totalSpent = purchases.reduce((sum, p) => sum + (p.value || 0), 0);
343
- const avgOrderValue = totalSpent / purchases.length;
344
- const purchaseFrequency = this.calculatePurchaseFrequency(purchases);
345
- const customerLifespan = this.calculateCustomerLifespan(purchases);
346
-
347
- const clv = avgOrderValue * purchaseFrequency * customerLifespan;
348
-
349
- return {
350
- clv,
351
- avgOrderValue,
352
- purchaseFrequency,
353
- customerLifespan,
354
- totalOrders: purchases.length,
355
- totalSpent,
356
- tier: this.determineTier(clv),
357
- recommendations: this.generateRecommendations(clv, analytics)
358
- };
359
- }
360
-
361
- private calculatePurchaseFrequency(purchases: any[]): number {
362
- if (purchases.length < 2) return 0;
363
-
364
- const timespan = purchases[0].time - purchases[purchases.length - 1].time;
365
- const days = timespan / (24 * 60 * 60 * 1000);
366
-
367
- return purchases.length / (days / 365); // Purchases per year
368
- }
369
-
370
- private calculateCustomerLifespan(purchases: any[]): number {
371
- if (purchases.length < 2) return 1;
372
-
373
- const timespan = purchases[0].time - purchases[purchases.length - 1].time;
374
- return Math.max(1, timespan / (365 * 24 * 60 * 60 * 1000)); // Years
375
- }
376
-
377
- private determineTier(clv: number): string {
378
- if (clv > 50000000) return "diamond";
379
- if (clv > 20000000) return "gold";
380
- if (clv > 5000000) return "silver";
381
- if (clv > 1000000) return "bronze";
382
- return "standard";
383
- }
384
-
385
- private generateRecommendations(clv: number, analytics: any): string[] {
386
- const recommendations = [];
387
-
388
- if (clv > 20000000) {
389
- recommendations.push("Assign dedicated account manager");
390
- recommendations.push("Offer exclusive products and early access");
391
- recommendations.push("Provide VIP customer service");
392
- } else if (clv > 5000000) {
393
- recommendations.push("Implement loyalty program");
394
- recommendations.push("Send personalized offers");
395
- recommendations.push("Prioritize customer support");
396
- } else if (clv < 1000000) {
397
- recommendations.push("Focus on engagement and education");
398
- recommendations.push("Offer entry-level products");
399
- recommendations.push("Encourage repeat purchases");
400
- }
401
-
402
- return recommendations;
403
- }
404
- }
405
- ```
406
-
407
- ---
408
-
409
- ## Bulk Operations
410
-
411
- ### 1. Bulk User Operations
412
-
413
- ```typescript
414
- // Thực hiện bulk operations trên nhiều users
415
- const bulkResult = await zalo.userManagement.bulkUserOperation(
416
- oaAccessToken,
417
- {
418
- operation: "update_tags",
419
- user_ids: ["user1", "user2", "user3"],
420
- data: {
421
- add_tags: ["FLASH_SALE_2024"],
422
- remove_tags: ["OLD_CAMPAIGN"]
423
- }
424
- }
425
- );
426
-
427
- console.log(`Updated ${bulkResult.successful_count} users`);
428
- console.log(`Failed: ${bulkResult.failed_count}`);
429
- ```
430
-
431
- ### 2. Bulk Message Service
432
-
433
- ```typescript
434
- class BulkMessageService {
435
- constructor(private zalo: ZaloSDK, private accessToken: string) {}
436
-
437
- async sendBulkConsultationMessages(
438
- userMessages: Array<{userId: string, message: any}>
439
- ): Promise<BulkMessageResult> {
440
- const results = {
441
- successful: 0,
442
- failed: 0,
443
- errors: [] as Array<{userId: string, error: string}>
444
- };
445
-
446
- const batchSize = 10; // Process 10 users at a time
447
-
448
- for (let i = 0; i < userMessages.length; i += batchSize) {
449
- const batch = userMessages.slice(i, i + batchSize);
450
-
451
- const promises = batch.map(async ({userId, message}) => {
452
- try {
453
- await this.zalo.consultation.sendTextMessage(
454
- this.accessToken,
455
- { user_id: userId },
456
- message
457
- );
458
- return { userId, success: true };
459
- } catch (error) {
460
- return { userId, success: false, error: error.message };
461
- }
462
- });
463
-
464
- const batchResults = await Promise.all(promises);
465
-
466
- batchResults.forEach(result => {
467
- if (result.success) {
468
- results.successful++;
469
- } else {
470
- results.failed++;
471
- results.errors.push({
472
- userId: result.userId,
473
- error: result.error || 'Unknown error'
474
- });
475
- }
476
- });
477
-
478
- // Delay between batches to avoid rate limiting
479
- if (i + batchSize < userMessages.length) {
480
- await this.delay(1000);
481
- }
482
- }
483
-
484
- return results;
485
- }
486
-
487
- // Gửi tin nhắn theo segments
488
- async sendSegmentedCampaign(campaign: Campaign): Promise<CampaignResult> {
489
- const segments = await this.getUserSegments(campaign.targetSegments);
490
- const results = new Map<string, BulkMessageResult>();
491
-
492
- for (const [segmentName, users] of segments) {
493
- console.log(`Sending campaign to ${segmentName}: ${users.length} users`);
494
-
495
- const segmentMessage = this.personalizeMessageForSegment(
496
- campaign.message,
497
- segmentName
498
- );
499
-
500
- const userMessages = users.map(user => ({
501
- userId: user.user_id,
502
- message: this.personalizeMessage(segmentMessage, user)
503
- }));
504
-
505
- const segmentResult = await this.sendBulkConsultationMessages(userMessages);
506
- results.set(segmentName, segmentResult);
507
- }
508
-
509
- return this.aggregateResults(results);
510
- }
511
-
512
- private async getUserSegments(targetSegments: string[]): Promise<Map<string, UserProfile[]>> {
513
- const segments = new Map();
514
-
515
- for (const segment of targetSegments) {
516
- const users = await this.zalo.userManagement.searchUsers(
517
- this.accessToken,
518
- { tags: [segment], limit: 1000 }
519
- );
520
- segments.set(segment, users.users);
521
- }
522
-
523
- return segments;
524
- }
525
-
526
- private personalizeMessageForSegment(template: string, segment: string): any {
527
- // Customize message based on segment
528
- const customizations = {
529
- 'VIP': {
530
- greeting: 'Kính chào Quý khách VIP',
531
- offer: 'ưu đãi đặc biệt dành riêng cho VIP'
532
- },
533
- 'Premium': {
534
- greeting: 'Xin chào khách hàng Premium',
535
- offer: 'chương trình ưu đãi Premium'
536
- },
537
- 'Standard': {
538
- greeting: 'Xin chào',
539
- offer: 'chương trình khuyến mại'
540
- }
541
- };
542
-
543
- const custom = customizations[segment] || customizations['Standard'];
544
-
545
- return {
546
- type: "text",
547
- text: template
548
- .replace('{greeting}', custom.greeting)
549
- .replace('{offer}', custom.offer)
550
- };
551
- }
552
-
553
- private personalizeMessage(template: any, user: UserProfile): any {
554
- return {
555
- ...template,
556
- text: template.text.replace('{name}', user.display_name || 'bạn')
557
- };
558
- }
559
-
560
- private delay(ms: number): Promise<void> {
561
- return new Promise(resolve => setTimeout(resolve, ms));
562
- }
563
- }
564
- ```
565
-
566
- ---
567
-
568
- ## User Journey Tracking
569
-
570
- ### 1. Journey Mapping Service
571
-
572
- ```typescript
573
- class UserJourneyService {
574
- constructor(private zalo: ZaloSDK, private accessToken: string) {}
575
-
576
- async mapUserJourney(userId: string): Promise<UserJourney> {
577
- const interactions = await this.zalo.userManagement.getUserInteractions(
578
- this.accessToken,
579
- userId,
580
- { limit: 1000 }
581
- );
582
-
583
- const journey = this.analyzeJourneyStages(interactions);
584
- const touchpoints = this.identifyTouchpoints(interactions);
585
- const conversion = this.calculateConversionMetrics(interactions);
586
-
587
- return {
588
- userId,
589
- currentStage: journey.currentStage,
590
- stages: journey.stages,
591
- touchpoints,
592
- conversion,
593
- recommendations: this.generateJourneyRecommendations(journey, conversion)
594
- };
595
- }
596
-
597
- private analyzeJourneyStages(interactions: any[]): JourneyStages {
598
- const stages = {
599
- awareness: [],
600
- consideration: [],
601
- purchase: [],
602
- retention: [],
603
- advocacy: []
604
- };
605
-
606
- const currentStage = this.determineCurrentStage(interactions);
607
-
608
- // Classify interactions by journey stage
609
- interactions.forEach(interaction => {
610
- const stage = this.classifyInteractionStage(interaction);
611
- stages[stage].push(interaction);
612
- });
613
-
614
- return { currentStage, stages };
615
- }
616
-
617
- private identifyTouchpoints(interactions: any[]): Touchpoint[] {
618
- const touchpointMap = new Map();
619
-
620
- interactions.forEach(interaction => {
621
- const touchpoint = this.getTouchpointFromInteraction(interaction);
622
-
623
- if (touchpointMap.has(touchpoint.type)) {
624
- touchpointMap.get(touchpoint.type).count++;
625
- touchpointMap.get(touchpoint.type).lastInteraction = interaction.time;
626
- } else {
627
- touchpointMap.set(touchpoint.type, {
628
- ...touchpoint,
629
- count: 1,
630
- lastInteraction: interaction.time
631
- });
632
- }
633
- });
634
-
635
- return Array.from(touchpointMap.values()).sort((a, b) => b.count - a.count);
636
- }
637
-
638
- private calculateConversionMetrics(interactions: any[]): ConversionMetrics {
639
- const totalInteractions = interactions.length;
640
- const purchases = interactions.filter(i => i.type === 'purchase');
641
- const inquiries = interactions.filter(i => i.type === 'product_inquiry');
642
-
643
- return {
644
- conversionRate: purchases.length / totalInteractions,
645
- inquiryToPurchase: purchases.length / (inquiries.length || 1),
646
- avgTimeToConversion: this.calculateAvgTimeToConversion(interactions),
647
- totalPurchases: purchases.length,
648
- totalValue: purchases.reduce((sum, p) => sum + (p.value || 0), 0)
649
- };
650
- }
651
-
652
- private generateJourneyRecommendations(
653
- journey: JourneyStages,
654
- conversion: ConversionMetrics
655
- ): string[] {
656
- const recommendations = [];
657
-
658
- switch (journey.currentStage) {
659
- case 'awareness':
660
- recommendations.push('Send educational content about products');
661
- recommendations.push('Showcase customer testimonials and reviews');
662
- break;
663
-
664
- case 'consideration':
665
- recommendations.push('Provide detailed product comparisons');
666
- recommendations.push('Offer consultation sessions');
667
- recommendations.push('Send limited-time offers to encourage decision');
668
- break;
669
-
670
- case 'purchase':
671
- recommendations.push('Streamline checkout process');
672
- recommendations.push('Offer multiple payment options');
673
- recommendations.push('Provide immediate support');
674
- break;
675
-
676
- case 'retention':
677
- recommendations.push('Send onboarding and education materials');
678
- recommendations.push('Implement loyalty program');
679
- recommendations.push('Request feedback and reviews');
680
- break;
681
-
682
- case 'advocacy':
683
- recommendations.push('Encourage referrals with incentives');
684
- recommendations.push('Feature as case study');
685
- recommendations.push('Invite to exclusive events');
686
- break;
687
- }
688
-
689
- if (conversion.conversionRate < 0.1) {
690
- recommendations.push('Focus on engagement improvement');
691
- recommendations.push('Personalize content based on interests');
692
- }
693
-
694
- return recommendations;
695
- }
696
- }
697
- ```
698
-
699
- ---
700
-
701
- ## User Retention & Re-engagement
702
-
703
- ### 1. Retention Analysis
704
-
705
- ```typescript
706
- class UserRetentionService {
707
- constructor(private zalo: ZaloSDK, private accessToken: string) {}
708
-
709
- async analyzeUserRetention(period: 'weekly' | 'monthly'): Promise<RetentionAnalysis> {
710
- const cohorts = await this.getCohorts(period);
711
- const retentionRates = new Map();
712
-
713
- for (const [cohortDate, users] of cohorts) {
714
- const retention = await this.calculateCohortRetention(users, cohortDate, period);
715
- retentionRates.set(cohortDate, retention);
716
- }
717
-
718
- return {
719
- period,
720
- cohorts: Array.from(retentionRates.entries()),
721
- averageRetention: this.calculateAverageRetention(retentionRates),
722
- insights: this.generateRetentionInsights(retentionRates)
723
- };
724
- }
725
-
726
- // Identify users at risk of churning
727
- async identifyChurnRisk(): Promise<ChurnRiskAnalysis> {
728
- const allUsers = await this.getAllActiveUsers();
729
- const riskUsers = [];
730
-
731
- for (const user of allUsers) {
732
- const riskScore = await this.calculateChurnRiskScore(user.user_id);
733
-
734
- if (riskScore > 0.7) {
735
- riskUsers.push({
736
- ...user,
737
- riskScore,
738
- riskFactors: await this.identifyRiskFactors(user.user_id)
739
- });
740
- }
741
- }
742
-
743
- return {
744
- totalUsers: allUsers.length,
745
- highRiskUsers: riskUsers.filter(u => u.riskScore > 0.9),
746
- mediumRiskUsers: riskUsers.filter(u => u.riskScore > 0.7 && u.riskScore <= 0.9),
747
- recommendations: this.generateChurnPreventionStrategies(riskUsers)
748
- };
749
- }
750
-
751
- private async calculateChurnRiskScore(userId: string): Promise<number> {
752
- const analytics = await this.zalo.userManagement.getUserAnalytics(
753
- this.accessToken,
754
- userId
755
- );
756
-
757
- let score = 0;
758
-
759
- // Days since last interaction
760
- const daysSinceLastInteraction = analytics.days_since_last_interaction || 0;
761
- if (daysSinceLastInteraction > 30) score += 0.3;
762
- if (daysSinceLastInteraction > 60) score += 0.2;
763
- if (daysSinceLastInteraction > 90) score += 0.3;
764
-
765
- // Declining engagement
766
- const engagementTrend = analytics.engagement_trend || 0;
767
- if (engagementTrend < -0.2) score += 0.2;
768
-
769
- // Reduced purchase frequency
770
- const purchaseTrend = analytics.purchase_frequency_trend || 0;
771
- if (purchaseTrend < -0.3) score += 0.3;
772
-
773
- // Support issues
774
- const supportIssues = analytics.recent_support_issues || 0;
775
- if (supportIssues > 2) score += 0.2;
776
-
777
- return Math.min(1, score);
778
- }
779
-
780
- // Re-engagement campaign for inactive users
781
- async runReengagementCampaign(
782
- inactiveUsers: string[],
783
- campaignType: 'win_back' | 'survey' | 'special_offer'
784
- ): Promise<ReengagementResult> {
785
- const results = {
786
- contacted: 0,
787
- responded: 0,
788
- reactivated: 0,
789
- errors: []
790
- };
791
-
792
- for (const userId of inactiveUsers) {
793
- try {
794
- const message = this.createReengagementMessage(campaignType, userId);
795
-
796
- await this.zalo.consultation.sendTextMessage(
797
- this.accessToken,
798
- { user_id: userId },
799
- message
800
- );
801
-
802
- results.contacted++;
803
-
804
- // Track if user responds within campaign period
805
- setTimeout(async () => {
806
- const isReactivated = await this.checkUserReactivation(userId);
807
- if (isReactivated) {
808
- results.reactivated++;
809
- }
810
- }, 7 * 24 * 60 * 60 * 1000); // Check after 7 days
811
-
812
- } catch (error) {
813
- results.errors.push({ userId, error: error.message });
814
- }
815
- }
816
-
817
- return results;
818
- }
819
-
820
- private createReengagementMessage(
821
- campaignType: string,
822
- userId: string
823
- ): any {
824
- const messages = {
825
- win_back: {
826
- type: "text",
827
- text: "Chúng tôi nhớ bạn! 🥺\nBạn đã không tương tác với chúng tôi một thời gian rồi. Có gì chúng tôi có thể giúp bạn không?"
828
- },
829
- survey: {
830
- type: "text",
831
- text: "Xin chào! 👋\nChúng tôi muốn cải thiện dịch vụ. Bạn có thể chia sẻ lý do tại sao ít tương tác với chúng tôi gần đây không?"
832
- },
833
- special_offer: {
834
- type: "text",
835
- text: "🎁 Ưu đãi đặc biệt dành cho bạn!\nGiảm 50% cho lần mua hàng tiếp theo. Mã: COMEBACK50\nChỉ còn 3 ngày!"
836
- }
837
- };
838
-
839
- return messages[campaignType] || messages.win_back;
840
- }
841
- }
842
- ```
843
-
844
- ---
845
-
846
- ## Data Privacy & GDPR Compliance
847
-
848
- ### 1. User Consent Management
849
-
850
- ```typescript
851
- class UserConsentService {
852
- constructor(private zalo: ZaloSDK, private accessToken: string) {}
853
-
854
- async recordUserConsent(
855
- userId: string,
856
- consentType: ConsentType,
857
- granted: boolean
858
- ): Promise<void> {
859
- await this.zalo.userManagement.updateUserProfile(
860
- this.accessToken,
861
- userId,
862
- {
863
- custom_fields: {
864
- [`consent_${consentType}`]: granted.toString(),
865
- [`consent_${consentType}_date`]: new Date().toISOString(),
866
- [`consent_${consentType}_ip`]: this.getCurrentIP()
867
- }
868
- }
869
- );
870
- }
871
-
872
- async getUserConsents(userId: string): Promise<UserConsents> {
873
- const profile = await this.zalo.userManagement.getUserProfile(
874
- this.accessToken,
875
- userId
876
- );
877
-
878
- return {
879
- marketing: profile.custom_fields?.consent_marketing === 'true',
880
- analytics: profile.custom_fields?.consent_analytics === 'true',
881
- data_processing: profile.custom_fields?.consent_data_processing === 'true',
882
- third_party_sharing: profile.custom_fields?.consent_third_party === 'true'
883
- };
884
- }
885
-
886
- async exportUserData(userId: string): Promise<UserDataExport> {
887
- const profile = await this.zalo.userManagement.getUserProfile(
888
- this.accessToken,
889
- userId
890
- );
891
-
892
- const interactions = await this.zalo.userManagement.getUserInteractions(
893
- this.accessToken,
894
- userId,
895
- { limit: 10000 }
896
- );
897
-
898
- const analytics = await this.zalo.userManagement.getUserAnalytics(
899
- this.accessToken,
900
- userId
901
- );
902
-
903
- return {
904
- personal_data: {
905
- user_id: profile.user_id,
906
- display_name: profile.display_name,
907
- avatar: profile.avatar,
908
- phone: profile.shared_info?.phone,
909
- address: profile.shared_info?.address
910
- },
911
- interaction_history: interactions,
912
- analytics_data: analytics,
913
- consent_records: await this.getUserConsents(userId),
914
- export_date: new Date().toISOString(),
915
- retention_period: "36_months"
916
- };
917
- }
918
-
919
- async deleteUserData(userId: string): Promise<DeletionResult> {
920
- // Implement GDPR-compliant data deletion
921
- const deletionTasks = [
922
- this.deleteUserProfile(userId),
923
- this.deleteUserInteractions(userId),
924
- this.deleteUserAnalytics(userId),
925
- this.removeFromMarketingLists(userId)
926
- ];
927
-
928
- const results = await Promise.allSettled(deletionTasks);
929
-
930
- return {
931
- userId,
932
- deleted: results.every(r => r.status === 'fulfilled'),
933
- deletion_date: new Date().toISOString(),
934
- retention_logs: this.createDeletionLog(userId, results)
935
- };
936
- }
937
- }
938
- ```
939
-
940
- ---
941
-
942
- ## Testing User Management
943
-
944
- ### 1. Unit Tests
945
-
946
- ```typescript
947
- // user-management.test.ts
948
- import { ZaloSDK } from '@warriorteam/redai-zalo-sdk';
949
-
950
- describe('User Management', () => {
951
- const zalo = new ZaloSDK({
952
- appId: 'test_app_id',
953
- appSecret: 'test_app_secret'
954
- });
955
-
956
- it('should get user profile', async () => {
957
- const mockProfile = {
958
- user_id: 'test_user',
959
- display_name: 'Test User',
960
- avatar: 'https://example.com/avatar.jpg'
961
- };
962
-
963
- jest.spyOn(zalo.userManagement, 'getUserProfile').mockResolvedValue(mockProfile);
964
-
965
- const profile = await zalo.userManagement.getUserProfile('test_token', 'test_user');
966
-
967
- expect(profile.user_id).toBe('test_user');
968
- expect(profile.display_name).toBe('Test User');
969
- });
970
-
971
- it('should search users with filters', async () => {
972
- const mockResult = {
973
- total: 5,
974
- users: [
975
- { user_id: '1', display_name: 'User 1' },
976
- { user_id: '2', display_name: 'User 2' }
977
- ]
978
- };
979
-
980
- jest.spyOn(zalo.userManagement, 'searchUsers').mockResolvedValue(mockResult);
981
-
982
- const result = await zalo.userManagement.searchUsers('test_token', {
983
- query: 'test',
984
- tags: ['VIP']
985
- });
986
-
987
- expect(result.total).toBe(5);
988
- expect(result.users).toHaveLength(2);
989
- });
990
- });
991
- ```
992
-
993
- ---
994
-
995
- ## Performance Optimization
996
-
997
- ### 1. Caching User Data
998
-
999
- ```typescript
1000
- class UserDataCache {
1001
- private cache = new Map<string, CachedUserData>();
1002
- private readonly ttl = 10 * 60 * 1000; // 10 minutes
1003
-
1004
- async getUserProfile(
1005
- zalo: ZaloSDK,
1006
- accessToken: string,
1007
- userId: string
1008
- ): Promise<UserProfile> {
1009
- const cacheKey = `profile_${userId}`;
1010
- const cached = this.get(cacheKey);
1011
-
1012
- if (cached) {
1013
- return cached.data;
1014
- }
1015
-
1016
- const profile = await zalo.userManagement.getUserProfile(accessToken, userId);
1017
- this.set(cacheKey, profile);
1018
-
1019
- return profile;
1020
- }
1021
-
1022
- private get(key: string): CachedUserData | null {
1023
- const item = this.cache.get(key);
1024
-
1025
- if (!item) return null;
1026
-
1027
- if (Date.now() - item.timestamp > this.ttl) {
1028
- this.cache.delete(key);
1029
- return null;
1030
- }
1031
-
1032
- return item;
1033
- }
1034
-
1035
- private set(key: string, data: any): void {
1036
- this.cache.set(key, {
1037
- data,
1038
- timestamp: Date.now()
1039
- });
1040
- }
1041
-
1042
- // Batch load multiple users
1043
- async batchLoadUsers(
1044
- zalo: ZaloSDK,
1045
- accessToken: string,
1046
- userIds: string[]
1047
- ): Promise<Map<string, UserProfile>> {
1048
- const results = new Map();
1049
- const uncachedIds = [];
1050
-
1051
- // Check cache first
1052
- for (const userId of userIds) {
1053
- const cached = this.get(`profile_${userId}`);
1054
- if (cached) {
1055
- results.set(userId, cached.data);
1056
- } else {
1057
- uncachedIds.push(userId);
1058
- }
1059
- }
1060
-
1061
- // Batch load uncached users
1062
- if (uncachedIds.length > 0) {
1063
- const batchSize = 5; // API rate limiting
1064
-
1065
- for (let i = 0; i < uncachedIds.length; i += batchSize) {
1066
- const batch = uncachedIds.slice(i, i + batchSize);
1067
-
1068
- const promises = batch.map(async (userId) => {
1069
- try {
1070
- const profile = await zalo.userManagement.getUserProfile(accessToken, userId);
1071
- this.set(`profile_${userId}`, profile);
1072
- return { userId, profile };
1073
- } catch (error) {
1074
- console.error(`Failed to load user ${userId}:`, error);
1075
- return { userId, profile: null };
1076
- }
1077
- });
1078
-
1079
- const batchResults = await Promise.all(promises);
1080
-
1081
- batchResults.forEach(({ userId, profile }) => {
1082
- if (profile) {
1083
- results.set(userId, profile);
1084
- }
1085
- });
1086
-
1087
- // Delay between batches
1088
- if (i + batchSize < uncachedIds.length) {
1089
- await new Promise(resolve => setTimeout(resolve, 1000));
1090
- }
1091
- }
1092
- }
1093
-
1094
- return results;
1095
- }
1096
- }
1097
- ```
1098
-
1099
- ---
1100
-
1101
- ## Best Practices
1102
-
1103
- ### 1. Data Management Best Practices
1104
-
1105
- ```typescript
1106
- // ✅ Good practices
1107
- class UserDataBestPractices {
1108
- // Always validate user IDs
1109
- private validateUserId(userId: string): boolean {
1110
- return /^[0-9]+$/.test(userId) && userId.length > 0;
1111
- }
1112
-
1113
- // Implement proper error handling
1114
- async safeGetUserProfile(
1115
- zalo: ZaloSDK,
1116
- accessToken: string,
1117
- userId: string
1118
- ): Promise<UserProfile | null> {
1119
- if (!this.validateUserId(userId)) {
1120
- throw new Error('Invalid user ID format');
1121
- }
1122
-
1123
- try {
1124
- return await zalo.userManagement.getUserProfile(accessToken, userId);
1125
- } catch (error) {
1126
- if (error.code === -233) {
1127
- console.log(`User ${userId} not found`);
1128
- return null;
1129
- }
1130
- throw error;
1131
- }
1132
- }
1133
-
1134
- // Always paginate large datasets
1135
- async getAllUsersWithPagination(
1136
- zalo: ZaloSDK,
1137
- accessToken: string
1138
- ): Promise<UserProfile[]> {
1139
- const allUsers = [];
1140
- let offset = 0;
1141
- const limit = 50; // Reasonable page size
1142
-
1143
- while (true) {
1144
- const page = await zalo.user.getUserList(accessToken, {
1145
- offset,
1146
- count: limit
1147
- });
1148
-
1149
- allUsers.push(...page.data);
1150
-
1151
- if (page.data.length < limit) break;
1152
- offset += limit;
1153
-
1154
- // Rate limiting
1155
- await new Promise(resolve => setTimeout(resolve, 500));
1156
- }
1157
-
1158
- return allUsers;
1159
- }
1160
-
1161
- // Implement data validation
1162
- private validateUserData(userData: any): boolean {
1163
- const required = ['user_id', 'display_name'];
1164
- return required.every(field => userData[field]);
1165
- }
1166
- }
1167
- ```
1168
-
1169
- ### 2. Privacy-First Approach
1170
-
1171
- ```typescript
1172
- class PrivacyCompliantUserService {
1173
- // Always check consent before processing
1174
- async sendMarketingMessage(
1175
- userId: string,
1176
- message: any
1177
- ): Promise<boolean> {
1178
- const consents = await this.getUserConsents(userId);
1179
-
1180
- if (!consents.marketing) {
1181
- console.log(`User ${userId} has not consented to marketing messages`);
1182
- return false;
1183
- }
1184
-
1185
- // Proceed with sending message
1186
- return true;
1187
- }
1188
-
1189
- // Minimize data collection
1190
- async updateUserProfile(
1191
- userId: string,
1192
- updates: Partial<UserProfile>
1193
- ): Promise<void> {
1194
- // Only update necessary fields
1195
- const allowedFields = ['notes', 'tags', 'preferences'];
1196
- const filteredUpdates = Object.keys(updates)
1197
- .filter(key => allowedFields.includes(key))
1198
- .reduce((obj, key) => {
1199
- obj[key] = updates[key];
1200
- return obj;
1201
- }, {});
1202
-
1203
- if (Object.keys(filteredUpdates).length === 0) {
1204
- throw new Error('No valid fields to update');
1205
- }
1206
-
1207
- // Proceed with update
1208
- }
1209
- }
1210
- ```
1211
-
1212
- ---
1213
-
1214
- ## Troubleshooting
1215
-
1216
- ### Common Issues
1217
-
1218
- **Q: "User not found" error khi lấy profile**
1219
- ```
1220
- A: User có thể đã unfollow OA hoặc chặn OA.
1221
- Kiểm tra danh sách followers trước khi truy cập profile.
1222
- ```
1223
-
1224
- **Q: Không lấy được thông tin phone number**
1225
- ```
1226
- A: Phone number chỉ có sẵn nếu user đã share với OA.
1227
- Cần yêu cầu user cung cấp thông tin qua request_user_info.
1228
- ```
1229
-
1230
- **Q: Search results không accurate**
1231
- ```
1232
- A: Zalo search có giới hạn. Implement local caching và filtering
1233
- để có kết quả search tốt hơn.
1234
- ```
1235
-
1236
- ---
1237
-
1238
- ## Next Steps
1239
-
1240
- Sau khi nắm vững User Management:
1241
-
1242
- 1. **[Tag Management](./TAG_MANAGEMENT.md)** - User tagging và segmentation
1243
- 2. **[Group Management](./GROUP_MANAGEMENT.md)** - Quản lý Zalo groups
1244
- 3. **[Webhook Events](./WEBHOOK_EVENTS.md)** - Xử lý user events
1245
- 4. **[Error Handling](./ERROR_HANDLING.md)** - Xử lý lỗi toàn diện
1246
- 5. **[Video Upload](./VIDEO_UPLOAD.md)** - Upload và manage media
1247
-
1248
- Tham khảo **[API Reference](./API_REFERENCE.md)** để biết chi tiết về tất cả user management methods.
1
+ # User Management - Hướng Dẫn Sử Dụng
2
+
3
+ ## Tổng Quan
4
+
5
+ `UserService` cung cấp các công cụ toàn diện để quản lý người dùng Zalo Official Account, bao gồm:
6
+
7
+ - 👥 **User Information** - Lấy thông tin chi tiết người dùng
8
+ - 📋 **User Lists** - Quản lý danh sách người dùng với phân trang
9
+ - 🏷️ **Tag Management** - Gắn quản tags cho users
10
+ - 📊 **Custom Info** - Quản thông tin tùy chỉnh
11
+ - 🔄 **Bulk Operations** - Xử lý hàng loạt users
12
+
13
+ **Endpoints sử dụng:**
14
+ - User Info: `https://openapi.zalo.me/v3.0/oa/user/detail`
15
+ - User List: `https://openapi.zalo.me/v3.0/oa/user/getlist`
16
+ - Update User: `https://openapi.zalo.me/v3.0/oa/user/update`
17
+ - Custom Info: `https://openapi.zalo.me/v3.0/oa/user/getcustominfo`
18
+ - Tags: `https://openapi.zalo.me/v3.0/oa/tag/gettagsofoa`
19
+
20
+ ## Khởi Tạo Service
21
+
22
+ ```typescript
23
+ import { UserService } from "@warriorteam/redai-zalo-sdk";
24
+ import { ZaloClient } from "@warriorteam/redai-zalo-sdk";
25
+
26
+ const client = new ZaloClient();
27
+ const userService = new UserService(client);
28
+ ```
29
+
30
+ ## Các Phương Thức Chính
31
+
32
+ ### 1. Lấy Thông Tin User Chi Tiết
33
+
34
+ ```typescript
35
+ // Lấy thông tin chi tiết của một user
36
+ const userInfo = await userService.getUserInfo(
37
+ accessToken,
38
+ "user-id-here"
39
+ );
40
+
41
+ console.log("User Info:", {
42
+ userId: userInfo.user_id,
43
+ displayName: userInfo.display_name,
44
+ avatar: userInfo.avatar,
45
+ userGender: userInfo.user_gender,
46
+ userAlias: userInfo.user_alias,
47
+ isFollower: userInfo.is_follower,
48
+ sharedInfo: userInfo.shared_info
49
+ });
50
+ ```
51
+
52
+ **Tham số:**
53
+ - `accessToken`: Access token của OA
54
+ - `userId`: ID người dùng cần lấy thông tin
55
+
56
+ ### 2. Lấy Danh Sách Users
57
+
58
+ ```typescript
59
+ // Lấy danh sách users với filter
60
+ const userList = await userService.getUserList(accessToken, {
61
+ offset: 0,
62
+ count: 50,
63
+ tag_name: "VIP_CUSTOMER", // Lọc theo tag (tùy chọn)
64
+ is_follower: true, // Chỉ lấy followers (tùy chọn)
65
+ last_interaction_period: "L7D" // Tương tác trong 7 ngày qua
66
+ });
67
+
68
+ console.log("Total users:", userList.total);
69
+ console.log("Count:", userList.count);
70
+ console.log("Offset:", userList.offset);
71
+
72
+ userList.users.forEach(user => {
73
+ console.log(`${user.display_name} - ${user.user_id}`);
74
+ });
75
+ ```
76
+
77
+ **Tham số:**
78
+ - `offset`: Vị trí bắt đầu (0-based)
79
+ - `count`: Số lượng users mỗi page (tối đa 50)
80
+ - `tag_name`: Lọc theo tag (tùy chọn)
81
+ - `is_follower`: true/false để lọc followers
82
+ - `last_interaction_period`: "TODAY", "YESTERDAY", "L7D", "L30D"
83
+
84
+ ### 3. Lấy Tất Cả Users (Auto Pagination)
85
+
86
+ ```typescript
87
+ // Lấy tất cả users với phân trang tự động
88
+ const allUsers = await userService.getAllUsers(accessToken, {
89
+ is_follower: true,
90
+ tag_name: "PREMIUM"
91
+ });
92
+
93
+ console.log(`Total users fetched: ${allUsers.length}`);
94
+ ```
95
+
96
+ **Lưu ý:**
97
+ - Zalo giới hạn max offset = 9951 (tương ứng ~10000 users)
98
+ - Method này tự động handle pagination và warning khi vượt giới hạn
99
+
100
+ ### 4. Lấy Danh Sách User IDs (Tối Ưu)
101
+
102
+ ```typescript
103
+ // Chỉ lấy user IDs, không lấy thông tin chi tiết (nhanh hơn)
104
+ const userIds = await userService.getAllUserIds(accessToken, {
105
+ is_follower: true
106
+ });
107
+
108
+ console.log(`Total user IDs: ${userIds.length}`);
109
+ ```
110
+
111
+ ### 5. Helper Methods
112
+
113
+ ```typescript
114
+ // Lấy chỉ followers
115
+ const followers = await userService.getFollowers(accessToken, 0, 50);
116
+
117
+ // Lấy users theo tag
118
+ const vipUsers = await userService.getUsersByTag(accessToken, "VIP", 0, 50);
119
+
120
+ // Lấy users theo thời gian tương tác
121
+ const recentUsers = await userService.getUsersByInteraction(
122
+ accessToken,
123
+ "L7D", // 7 ngày qua
124
+ 0,
125
+ 50
126
+ );
127
+ ```
128
+
129
+ ## User Management Operations
130
+
131
+ ### 6. Cập Nhật Thông Tin User
132
+
133
+ ```typescript
134
+ // Cập nhật thông tin user
135
+ const success = await userService.updateUser(accessToken, {
136
+ user_id: "user-id-here",
137
+ notes: "Customer quan tâm sản phẩm cao cấp",
138
+ // Các field khác có thể cập nhật
139
+ });
140
+
141
+ console.log("Update success:", success);
142
+ ```
143
+
144
+ ### 7. Xóa Thông Tin User
145
+
146
+ ```typescript
147
+ // Xóa thông tin user khỏi OA (không ảnh hưởng tài khoản Zalo)
148
+ const success = await userService.deleteUserInfo(accessToken, "user-id-here");
149
+ console.log("Delete success:", success);
150
+ ```
151
+
152
+ ### 8. Quản Lý Custom Info
153
+
154
+ ```typescript
155
+ // Lấy custom info của user
156
+ const customInfo = await userService.getUserCustomInfo(accessToken, {
157
+ user_id: "user-id-here",
158
+ fields_to_export: ["customer_tier", "total_spent"] // Tùy chọn
159
+ });
160
+
161
+ console.log("Custom Info:", customInfo.custom_info);
162
+
163
+ // Cập nhật custom info
164
+ const updateSuccess = await userService.updateUserCustomInfo(accessToken, {
165
+ user_id: "user-id-here",
166
+ custom_info: {
167
+ customer_tier: "VIP",
168
+ total_spent: "10000000",
169
+ last_purchase: "2024-12-01"
170
+ }
171
+ });
172
+ ```
173
+
174
+ **Lưu ý:** Cấu trúc `custom_info` phụ thuộc vào thiết lập OA
175
+
176
+ ## Tag Management
177
+
178
+ ### 9. Quản Lý Tags
179
+
180
+ ```typescript
181
+ // Lấy danh sách tất cả tags của OA
182
+ const tags = await userService.getLabels(accessToken);
183
+ console.log("Available tags:", tags);
184
+
185
+ // Thêm tag cho user
186
+ const addSuccess = await userService.addTagToUser(accessToken, {
187
+ user_id: "user-id-here",
188
+ tag_name: "VIP_CUSTOMER"
189
+ });
190
+
191
+ // Xóa tag khỏi user
192
+ const removeSuccess = await userService.removeTagFromUser(accessToken, {
193
+ user_id: "user-id-here",
194
+ tag_name: "OLD_TAG"
195
+ });
196
+
197
+ // Xóa tag khỏi OA (xóa hoàn toàn)
198
+ const deleteSuccess = await userService.deleteLabel(accessToken, "UNUSED_TAG");
199
+ ```
200
+
201
+ ### 10. Bulk Operations
202
+
203
+ ```typescript
204
+ // Bulk add tag cho nhiều users
205
+ const bulkAddResult = await userService.bulkAddTag(
206
+ accessToken,
207
+ ["user1", "user2", "user3"],
208
+ "FLASH_SALE_2024"
209
+ );
210
+
211
+ console.log("Success:", bulkAddResult.success);
212
+ console.log("Failed:", bulkAddResult.failed);
213
+
214
+ // Bulk remove tag
215
+ const bulkRemoveResult = await userService.bulkRemoveTag(
216
+ accessToken,
217
+ ["user1", "user2", "user3"],
218
+ "OLD_CAMPAIGN"
219
+ );
220
+ ```
221
+
222
+ ## API Methods Summary
223
+
224
+ | Method | Description | Parameters |
225
+ |--------|-------------|------------|
226
+ | `getUserInfo` | Lấy thông tin chi tiết user | `accessToken`, `userId` |
227
+ | `getUserList` | Lấy danh sách users với pagination | `accessToken`, `request` |
228
+ | `getAllUsers` | Lấy tất cả users (auto pagination) | `accessToken`, `filters?` |
229
+ | `getAllUserIds` | Lấy tất cả user IDs (tối ưu) | `accessToken`, `filters?` |
230
+ | `updateUser` | Cập nhật thông tin user | `accessToken`, `request` |
231
+ | `deleteUserInfo` | Xóa thông tin user | `accessToken`, `userId` |
232
+ | `getUserCustomInfo` | Lấy custom info | `accessToken`, `request` |
233
+ | `updateUserCustomInfo` | Cập nhật custom info | `accessToken`, `request` |
234
+ | `getLabels` | Lấy danh sách tags | `accessToken` |
235
+ | `addTagToUser` | Thêm tag cho user | `accessToken`, `request` |
236
+ | `removeTagFromUser` | Xóa tag khỏi user | `accessToken`, `request` |
237
+ | `deleteLabel` | Xóa tag khỏi OA | `accessToken`, `tagName` |
238
+ | `getUsersByTag` | Lấy users theo tag | `accessToken`, `tagName`, `offset?`, `count?` |
239
+ | `getFollowers` | Lấy chỉ followers | `accessToken`, `offset?`, `count?` |
240
+ | `getUsersByInteraction` | Lấy users theo thời gian tương tác | `accessToken`, `period`, `offset?`, `count?` |
241
+ | `bulkAddTag` | Bulk add tag | `accessToken`, `userIds`, `tagName` |
242
+ | `bulkRemoveTag` | Bulk remove tag | `accessToken`, `userIds`, `tagName` |
243
+
244
+ ## Dụ Thực Tế
245
+
246
+ ### 1. Customer Support System
247
+
248
+ ```typescript
249
+ class CustomerSupportSystem {
250
+ constructor(private userService: UserService, private accessToken: string) {}
251
+
252
+ async handleNewCustomer(userId: string) {
253
+ // Lấy thông tin customer
254
+ const userInfo = await this.userService.getUserInfo(this.accessToken, userId);
255
+
256
+ // Thêm tag "NEW_CUSTOMER"
257
+ await this.userService.addTagToUser(this.accessToken, {
258
+ user_id: userId,
259
+ tag_name: "NEW_CUSTOMER"
260
+ });
261
+
262
+ // Cập nhật custom info
263
+ await this.userService.updateUserCustomInfo(this.accessToken, {
264
+ user_id: userId,
265
+ custom_info: {
266
+ registration_date: new Date().toISOString(),
267
+ customer_tier: "STANDARD",
268
+ total_interactions: "1"
269
+ }
270
+ });
271
+
272
+ console.log(`New customer ${userInfo.display_name} processed`);
273
+ }
274
+
275
+ async promoteToVIP(userId: string) {
276
+ // Remove old tags
277
+ await this.userService.removeTagFromUser(this.accessToken, {
278
+ user_id: userId,
279
+ tag_name: "STANDARD"
280
+ });
281
+
282
+ // Add VIP tag
283
+ await this.userService.addTagToUser(this.accessToken, {
284
+ user_id: userId,
285
+ tag_name: "VIP"
286
+ });
287
+
288
+ // Update custom info
289
+ await this.userService.updateUserCustomInfo(this.accessToken, {
290
+ user_id: userId,
291
+ custom_info: {
292
+ customer_tier: "VIP",
293
+ promotion_date: new Date().toISOString()
294
+ }
295
+ });
296
+ }
297
+ }
298
+
299
+ }
300
+ ```
301
+
302
+ ### 2. Bulk Campaign System
303
+
304
+ ```typescript
305
+ class BulkCampaignSystem {
306
+ constructor(private userService: UserService, private accessToken: string) {}
307
+
308
+ async runSegmentedCampaign(campaignConfig: {
309
+ segments: string[];
310
+ message: string;
311
+ batchSize?: number;
312
+ }) {
313
+ const results = {
314
+ totalSent: 0,
315
+ totalFailed: 0,
316
+ segmentResults: new Map<string, any>()
317
+ };
318
+
319
+ for (const segment of campaignConfig.segments) {
320
+ console.log(`Processing segment: ${segment}`);
321
+
322
+ // Get users by tag
323
+ const users = await this.userService.getUsersByTag(
324
+ this.accessToken,
325
+ segment,
326
+ 0,
327
+ 1000
328
+ );
329
+
330
+ // Bulk add campaign tag
331
+ const userIds = users.users.map(u => u.user_id);
332
+ const bulkResult = await this.userService.bulkAddTag(
333
+ this.accessToken,
334
+ userIds,
335
+ `CAMPAIGN_${Date.now()}`
336
+ );
337
+
338
+ results.segmentResults.set(segment, {
339
+ totalUsers: users.total,
340
+ tagged: bulkResult.success.length,
341
+ failed: bulkResult.failed.length
342
+ });
343
+
344
+ results.totalSent += bulkResult.success.length;
345
+ results.totalFailed += bulkResult.failed.length;
346
+ }
347
+
348
+ return results;
349
+ }
350
+ }
351
+ ```
352
+
353
+ ## Error Handling
354
+
355
+ ```typescript
356
+ import { ZaloSDKError } from "@warriorteam/redai-zalo-sdk";
357
+
358
+ try {
359
+ const userInfo = await userService.getUserInfo(accessToken, "user-id");
360
+ console.log("User found:", userInfo.display_name);
361
+ } catch (error) {
362
+ if (error instanceof ZaloSDKError) {
363
+ switch (error.code) {
364
+ case -216:
365
+ console.error("Access token không hợp lệ");
366
+ break;
367
+ case -233:
368
+ console.error("User không tồn tại hoặc đã unfollow");
369
+ break;
370
+ case -201:
371
+ console.error("Tham số không hợp lệ");
372
+ break;
373
+ default:
374
+ console.error("Lỗi khác:", error.message);
375
+ }
376
+ } else {
377
+ console.error("Unexpected error:", error);
378
+ }
379
+ }
380
+ ```
381
+
382
+ ## Giới Hạn API
383
+
384
+ ### Zalo API Limits
385
+
386
+ 1. **User List Pagination**:
387
+ - Max offset: 9951
388
+ - Max count per request: 50
389
+ - Tối đa ~10000 users có thể lấy được
390
+
391
+ 2. **Rate Limiting**:
392
+ - Nên delay giữa các requests
393
+ - Sử dụng batch operations khi có thể
394
+
395
+ 3. **Custom Info**:
396
+ - Cấu trúc phụ thuộc vào thiết lập OA
397
+ - Tất cả giá trị đều là string
398
+
399
+ ## Best Practices
400
+
401
+ ### 1. Performance Optimization
402
+
403
+ ```typescript
404
+ // ✅ Sử dụng getAllUserIds cho performance tốt hơn
405
+ const userIds = await userService.getAllUserIds(accessToken);
406
+
407
+ // ✅ Batch operations
408
+ const bulkResult = await userService.bulkAddTag(
409
+ accessToken,
410
+ userIds.slice(0, 100),
411
+ "NEW_TAG"
412
+ );
413
+
414
+ // Pagination với reasonable page size
415
+ const users = await userService.getUserList(accessToken, {
416
+ offset: 0,
417
+ count: 50 // Không quá 50
418
+ });
419
+ ```
420
+
421
+ ### 2. Error Handling
422
+
423
+ ```typescript
424
+ // ✅ Validate user ID format
425
+ function validateUserId(userId: string): boolean {
426
+ return /^[0-9]+$/.test(userId) && userId.length > 0;
427
+ }
428
+
429
+ // ✅ Safe operations
430
+ async function safeGetUser(userId: string) {
431
+ if (!validateUserId(userId)) {
432
+ throw new Error("Invalid user ID format");
433
+ }
434
+
435
+ try {
436
+ return await userService.getUserInfo(accessToken, userId);
437
+ } catch (error) {
438
+ if (error.code === -233) {
439
+ return null; // User not found
440
+ }
441
+ throw error;
442
+ }
443
+ }
444
+ ```
445
+
446
+ ## Troubleshooting
447
+
448
+ ### Common Issues
449
+
450
+ **Q: "User not found" error**
451
+ ```
452
+ A: User có thể đã unfollow OA hoặc chặn OA.
453
+ Kiểm tra is_follower field trước.
454
+ ```
455
+
456
+ **Q: Không lấy được phone number**
457
+ ```
458
+ A: Phone chỉ khi user đã share với OA.
459
+ Sử dụng request_user_info để yêu cầu.
460
+ ```
461
+
462
+ **Q: getAllUsers không lấy hết**
463
+ ```
464
+ A: Zalo giới hạn max offset = 9951.
465
+ Chỉ lấy được ~10000 users đầu tiên.
466
+ ```
467
+
468
+ **Q: Custom info không cập nhật**
469
+ ```
470
+ A: Kiểm tra cấu trúc custom_info trong OA settings.
471
+ Tất cả values phải là string.
472
+ ```
473
+
474
+ ## Tài Liệu Liên Quan
475
+
476
+ - [Zalo Official Account API](https://developers.zalo.me/docs/api/official-account-api)
477
+ - [User Management API](https://developers.zalo.me/docs/api/official-account-api/quan-ly-nguoi-dung)
478
+ - [Tag Management API](https://developers.zalo.me/docs/api/official-account-api/quan-ly-nhan)
479
+ - [Consultation Service](./CONSULTATION_SERVICE.md)
480
+ - [Tag Management](./TAG_MANAGEMENT.md)
481
+