Module · WS đào tạo · 15/7/2026

zalochat — Tư vấn tuyển sinh qua Zalo OA MVP Painkiller NEW

Inbox chat 2 chiều với khách tuyển sinh qua Zalo OA ngay trên web Vĩnh An: khách quét QR/nhắn OA → hội thoại vào inbox → NV tuyển sinh tư vấn → chốt → 1 nút tạo hồ sơ học viên (đổ sẵn tên/SĐT vào form tiếp nhận WS1). Đối tượng chat là khách vãng lai (lead) — không phải user OpenGate.

⚠️ Chưa có trong code — scaffold mới (store module opengate-app-zalochat, cài cho vinhan). Nền LỚP 1: spec controlplane/docs/design/zalo-oa-integration.md (đã chốt 12.1 app riêng per-ws · 12.2 conversation-centric · domain vinhan verify 14/7).
1

Kiến trúc 3 lớp

LỚP 1 system/integrations (built-in) config zalo.oa · OAuth · refresh token lazy · webhook · sendZaloMessage LỚP 2 zalochat (module này) conversation · message · attachment · assign · inbox UI LỚP 3 student WS1 "Tạo hồ sơ từ hội thoại" → form tiếp nhận đổ sẵn tên/SĐT · nguồn "Zalo OA"

zalochat KHÔNG giữ token/secret — đọc config + gửi tin qua hàm chung lớp 1.

2

Feature / BO / Action

FeatureBO chínhAction
conversationHội thoại (1 khách Zalo = 1 hội thoại)vieweditassignclose
messageTin nhắn 2 chiều + đính kèmviewsend

Đã chốt 15/7: đợt đầu mọi NV tuyển sinh thấy tất cả hội thoại + cột "người phụ trách"; data-scope theo assigned_to để giai đoạn sau.

3

Dữ liệu chính (DB silo — prefix zalochat_)

BO · bảngTrường chính
Hội thoại
zalochat_conversation
zalo_user_id (UNIQUE, khóa từ webhook) · tên/avatar Zalo · SĐT (nhập tay khi khách cho — khóa nối lớp 3) · last_user_msg_at (mốc cửa sổ 48h) · assigned_to (employee, bridge users.employee_id) · trạng thái (mới/đang tư vấn/đã chốt/đóng) · unread + preview · lead_ref (hồ sơ WS1 khi chốt — đo hiệu quả kênh)
Tin nhắn
zalochat_message
hướng in/out · loại (text/image/file/sticker/khac) · nội dung · attachment_path (ảnh CCCD/khám SK — tải về silo ngay, URL Zalo có hạn) · zalo_msg_id UNIQUE (webhook retry không nhân đôi) · sender_employee_id · trạng thái gửi ok/lỗi (+lý do, vd ngoài 48h)
4

Quy tắc · màn hình · liên kết

Quy tắc nghiệp vụ

  • Cửa sổ 48h Zalo OA (quan trọng nhất): chỉ reply được trong 48h từ tin cuối của khách — UI hiện "còn X giờ / ĐÃ ĐÓNG" (disable ô nhập kèm giải thích); gửi ngoài cửa sổ → tin trạng thái lỗi, lý do rõ
  • Webhook idempotent theo zalo_msg_id
  • Attachment tải về silo ngay · allowlist + cap dung lượng
  • Gửi qua hàm chung lớp 1 (tự refresh token) — module không giữ secret
  • Realtime = SSE (express sẵn có); fallback polling
  • Hội thoại da_chot phải có lead_ref
  • Tin nhắn giữ vĩnh viễn — lịch sử tư vấn là bằng chứng cam kết

Màn hình MVP — inbox 3 cột

  • Trái: danh sách hội thoại — lọc trạng thái/người phụ trách, badge unread, preview
  • Giữa: khung chat bubble 2 chiều, ảnh/file, ô nhập + thanh trạng thái cửa sổ 48h
  • Phải: panel khách — tên/avatar/SĐT, người phụ trách, trạng thái, nút Tạo hồ sơ / Nhận tư vấn / Đóng

Liên kết chéo · ⚠ câu hỏi mở

  • system/integrations — config + send + webhook forward
  • student WS1 — nút Tạo hồ sơ đổ sẵn · lead_ref
  • hrm — assigned_to/sender qua bridge employee
  • notify (P2) — nhắc hội thoại chưa trả lời
  • ⚠ Auto-reply ngoài giờ — ai soạn nội dung? (đợt 3)
  • ⚠ Quota tin tư vấn theo gói OA (đã xác thực? gói nào?) → có cần đếm tin
  • ⚠ Khách nhắn từ 2 tài khoản Zalo → gộp hội thoại thủ công?
4b

Cấu hình Zalo nằm ở đâu — CP hay WS? (đã chốt 15/7)

Ở WS, không phải CP — tại Quản trị của WS (module core system, feature admin.integrations), KHÔNG nằm trong zalochat. Lưu key zalo.oa trong system_settings thuộc DB silo từng WS; WS owner tự cấu hình (auto có quyền qua module grant); OAuth/webhook chạy trên domain từng WS (https://<slug>.sunstech.vn/api/integrations/zalo/... — mỗi WS 1 app Zalo riêng — 12.1); zalochat không giữ token/secret, chỉ gọi helper lớp 1.
Lý do: secret là dữ liệu vận hành riêng từng doanh nghiệp (gom về CP = gom secret mọi tenant, trái mô hình silo) · redirect_uri + webhook bắt buộc theo domain WS · đường chạy tin nằm trọn trong instance WS. Ngoại lệ duy nhất về CP: nếu đổi sang "1 app Zalo chung nền tảng" (đã bác 12.1) thì app_id/secret mới thành global, oa_id+token vẫn per-WS.
5

Lộ trình & phân rã task phát triển

ĐợtNội dung
1 — nền (lớp 1)Bước 11.1→11.4 spec nền: system_settings + quyền integrations + form config + OAuth connect. Prerequisite: app Zalo riêng Vĩnh An (domain đã verify 14/7)
2 — chat MVPwebhook message → conversation/message · inbox 3 cột · gửi text · cửa sổ 48h · assign · mọi NV thấy tất cả
3attachment 2 chiều · SSE · nút "Tạo hồ sơ" nối WS1 · auto-reply ngoài giờ · thống kê (hội thoại/NV, tỉ lệ chốt)
SauZNS template (nhắc lịch khai giảng/thi — cần zalo_user_link) · data-scope theo assigned_to

▸ Bảng task (tạo task phát triển theo từng dòng — repo WS runtime trừ khi ghi khác)

#TaskNơiNghiệm thu
Z0Owner tạo app developers.zalo.me, gắn OA, khai redirect_uri + webhook URL theo domain vinhanthao tác tayCó app_id/secret; domain verify xong 14/7
Z1.1Bảng system_settings + helper get/setcore/built-in/systemGhi/đọc key qua helper; tạo bằng migration
Z1.2Feature integrations vào manifest system; backfill + bump epochcore/built-in/systemOwner có quyền; API config không 403
Z1.3GET/PUT config + form FE (secret mask) trong Quản trị → Tích hợpsystem + /adminLưu/reload OK; secret không lộ full
Z1.4OAuth connect/callback → token, connected=true, oa_namesystemKết nối → "Đã kết nối: <oa_name>"
Z1.5Refresh token lazy (refresh_token XOAY VÒNG — ghi đè mỗi lần)systemÉp hết hạn → lần gọi kế tự refresh + lưu
Z1.6Webhook + verify X-ZEvent-Signature + forward event messagesystemChữ ký sai → 401; đúng → handler nhận
Z2.1Scaffold opengate-app-zalochat: manifest + migration 2 bảngmodules/ (store)Cài qua Store; bảng + quyền hiện RBAC
Z2.2Handler event → upsert conversation + insert message (idempotent zalo_msg_id)zalochat BETin thật vào OA → hiện; retry không nhân đôi
Z2.3API list/get/assign/close/đã-đọc (guard đủ)zalochat BEToken TUYEN_SINH dùng được; thiếu quyền 403
Z2.4Gửi text qua lớp 1 + chặn ngoài cửa sổ 48h (tin lỗi + lý do)zalochat BETrong cửa sổ: khách nhận; ngoài: lỗi rõ
Z2.5FE inbox 3 cột + thanh cửa sổ 48h + assign; polling 5szalochat FEChat 2 chiều với khách thật; cửa sổ đóng → input disable
Z3.1Attachment 2 chiều (tải về silo, allowlist+cap)zalochatẢnh CCCD hiện trong chat; gửi ảnh khách nhận
Z3.2SSE thay pollingzalochatTin mới hiện < 2s không reload
Z3.3Nút "Tạo hồ sơ" → form WS1 đổ sẵn, ghi lead_refzalochat + studentHồ sơ link ngược hội thoại; đếm nguồn Zalo
Z3.4Auto-reply ngoài giờ + thống kêzalochatNgoài giờ có lời chào; dashboard đúng số

Thứ tự bắt buộc: Z0 → Z1.1→Z1.4 → Z1.6 → Z2.x → Z3.x. Z1.5 làm trước Z2.4.

6

Ma trận phân quyền — ốp vào manifest & seed role

Permission key đầy đủ: zalochat.chat.<feature>.<action>. close/send khai customActions (tiền lệ shexchange).

[ { "group": "chat", "feature": "conversation", "label": "Hội thoại tuyển sinh", "actions": ["view", "edit", "assign", "close"] }, { "group": "chat", "feature": "message", "label": "Tin nhắn", "actions": ["view", "send"] } ]
FeatureQTHTGIAM_DOCTUYEN_SINHQL_DAO_TAO
conversationRRW·assign·closeR
messageRRR·sendR

R xem · W tạo/sửa. Đợt đầu TUYEN_SINH thấy tất cả hội thoại; data-scope assigned_to = ctx.current_employee_id giai đoạn sau (plan data-scope).