Bổ sung một chế độ cấu hình action WhatsApp bằng template đã được phê duyệt trên Meta/ADA. Người dùng có thể:
Đây là một task backend. Tài liệu mô tả cả thao tác của người dùng và FE để làm rõ contract BE cần cung cấp, nhưng không bao gồm việc triển khai giao diện.
Kết quả cần đạt:
BE đã có:
GET /api/v1/whatsapp/message_templates
GET /api/v1/whatsapp/{id}
WhatsappService đã gọi Meta Graph API. Tuy nhiên API chi tiết hiện tại:
WhatsappData hiện chỉ lưu:
content, trackOpen, trackingLinks, brandId
Worker hiện render toàn bộ content, chèn tracking rồi gửi ADA bằng template name cố định trong biến môi trường:
{
"type": "template",
"templateName": "<fixed-template-name>",
"templateLang": "en",
"templateData": ["<rendered-content>"]
}
Vì vậy, chỉ thêm ô nhập Template ID trên FE là chưa đủ. BE phải resolve Template ID thành templateName, templateLanguage và danh sách parameter theo đúng thứ tự mà ADA yêu cầu.
Task này bao gồm:
Phiên bản đầu chỉ hỗ trợ parameter dạng text trong BODY, vì payload ADA hiện tại chỉ
thể hiện parameter bằng List
Header và button không có biến vẫn được phép. Template có biến trong HEADER, media, dynamic URL button, OTP hoặc Flow phải được trả về là không hỗ trợ. Không được âm thầm bỏ qua parameter rồi vẫn cho lưu Campaign.
Không thuộc task này:
FE không gọi Meta trực tiếp. Token, WABA ID và lỗi kỹ thuật từ Meta chỉ tồn tại ở BE.
Tái sử dụng endpoint hiện có:
GET /api/v1/whatsapp/{templateId}
id,name,language,status,category,components
Không coi name, language, status hoặc components do FE gửi lên là dữ liệu tin cậy.
Giữ wrapper ResponseDto hiện tại. Phần data:
{
"id": "123456789",
"name": "campaign_offer",
"language": "vi",
"status": "APPROVED",
"category": "MARKETING",
"supported": true,
"unsupportedReason": null,
"components": [
{
"type": "BODY",
"text": "Xin chào {{1}}, xem ưu đãi tại {{2}}"
}
],
"parameters": [
{
"key": "BODY.1",
"componentType": "BODY",
"position": 1,
"example": "Nguyễn Văn A",
"trackingAllowed": true
},
{
"key": "BODY.2",
"componentType": "BODY",
"position": 2,
"example": "https://example.com",
"trackingAllowed": true
}
]
}
key là định danh ổn định giữa FE và BE. BE tự parse placeholder; FE không tự regex {{n}} từ nội dung raw của Meta.
Mở rộng WhatsappData theo hướng tối thiểu:
{
"mode": "TEMPLATE_ID",
"templateId": "123456789",
"templateName": "campaign_offer",
"templateLanguage": "vi",
"templateParameters": [
{
"key": "BODY.1",
"position": 1,
"value": "{{fullName}}"
},
{
"key": "BODY.2",
"position": 2,
"value": "{{track.offerLink}}"
}
],
"trackingLinks": [
{
"key": "offerLink",
"originalUrl": "https://example.com/promotion",
"trackClick": true
}
],
"brandId": "..."
}
Quy tắc tương thích:
Không cần thêm enum cho từng loại nguồn parameter trong phiên bản đầu. Trường value tái sử dụng cú pháp placeholder hiện tại; tracking tiếp tục dùng TrackingLinkConfig.
Với TEMPLATE_ID, CampaignService phải:
OnlinePurchaseCustomerCareValidator hiện cũng yêu cầu trực tiếp whatsappData.content. Điều kiện này phải đổi thành:
CUSTOM_CONTENT -> content có giá trị
TEMPLATE_ID -> templateId và templateParameters hợp lệ
Nếu Meta tạm thời không truy cập được, không lưu một cấu hình mới dưới trạng thái đã xác minh. Campaign đã được lưu trước đó vẫn chạy bằng snapshot.
Tracking link không được tạo khi lấy template hoặc khi lưu Campaign. Mỗi recipient cần một short code riêng nên link chỉ được tạo tại worker.
Ví dụ:
Recipient A -> https://laoads.com/s/<shortCode-A>
Recipient B -> https://laoads.com/s/<shortCode-B>
Cần mở rộng logic tracking dùng chung để xử lý toàn bộ danh sách parameter và chỉ tạo một tracking record cho mỗi config/recipient. Không gọi applyWhatsappTracking() riêng cho từng parameter với toàn bộ trackingLinks, vì có thể tạo bản ghi trùng.
Giữ nguyên luồng hiện tại để không ảnh hưởng Campaign cũ.
{
"platform": "WA",
"from": "<resolved-brand-number>",
"to": "<recipient-phone>",
"type": "template",
"templateName": "campaign_offer",
"templateLang": "vi",
"templateData": [
"Nguyễn Văn A",
"https://laoads.com/s/AbCd1234"
]
}
Trong chế độ này, worker không dùng WHATSAPP_TEMPLATE_URL_* để chọn template. ADA nhận template name và language, không nhận Meta Template ID. Vì vậy Template ID phải được resolve khi validate và lưu Campaign.
Luồng check status, lưu pending, reconcile và phát delivery event giữ nguyên.
Map lỗi thành LaoAdsException và StatusCodeEnum; không trả nguyên message nội bộ của Meta cho client.
| Trường hợp | HTTP | Mã lỗi đề xuất |
|---|---|---|
| ID rỗng hoặc sai định dạng | 400 | WHATSAPP_TEMPLATE_INVALID_ID |
| Không tồn tại hoặc không có quyền truy cập | 404 | WHATSAPP_TEMPLATE_NOT_ACCESSIBLE |
| Không thuộc WABA đang cấu hình | 422 | WHATSAPP_TEMPLATE_ACCOUNT_MISMATCH |
| Template chưa APPROVED | 422 | WHATSAPP_TEMPLATE_NOT_APPROVED |
| Có component ADA chưa hỗ trợ | 422 | WHATSAPP_TEMPLATE_UNSUPPORTED |
| Thiếu, thừa hoặc sai parameter | 400 | WHATSAPP_TEMPLATE_PARAMETER_INVALID |
| Meta timeout hoặc lỗi 5xx | 502 | WHATSAPP_TEMPLATE_SERVICE_UNAVAILABLE |
| Worker không render đủ parameter | Result FAILED | WORKER_EVENT1001 hoặc mã riêng nếu cần |
Nếu Meta không phân biệt được “không tồn tại” và “không có quyền”, BE trả chung WHATSAPP_TEMPLATE_NOT_ACCESSIBLE, không suy đoán nguyên nhân.
laoads/.../controller/WhatsappController.java
laoads/.../service/WhatsappService.java
laoads/.../model/dto/response/WhatsappMessageTemplateDetailResponse.java
laoads/.../service/CampaignService.java
laoads/.../service/campaign/OnlinePurchaseCustomerCareValidator.java
common/.../campaign/action_data/WhatsappData.java
common/.../campaign/action_data/WhatsappTemplateParameter.java
common/.../enums/StatusCodeEnum.java
common/.../service/tracking/WorkerTrackingService.java
whatsapp-worker/.../WhatsappActionExecutor.java
whatsapp-worker/.../WhatsappClient.java
Không tạo controller hoặc service mới khi WhatsappController và WhatsappService hiện tại đã đúng ranh giới trách nhiệm.