Heritage Voice
Nền tảng thuyết minh di sản văn hóa Việt Nam qua âm thanh — Progressive Web App hỗ trợ 6 ngôn ngữ, GPS geofencing, AI assistant, Tour Guide Mode và hệ thống quản lý nội dung đa cấp.
1. Giới thiệu dự án
1.1 Mục tiêu
Heritage Voice giải quyết bài toán thiếu hướng dẫn viên chất lượng tại các di tích lịch sử Việt Nam bằng cách số hóa nội dung thuyết minh, cho phép du khách tự khám phá qua điện thoại — không cần cài app, không rào cản ngôn ngữ.
1.2 Domain URL
# Production Frontend: https://disannoi.vn/ Admin CMS: https://disannoi.vn/admin/ API Base: https://disannoi.vn/api/ AI Proxy: https://hv-ai-proxy.hiepntt-moet.workers.dev/
1.3 Các trang chính (FE)
| File | Mô tả | Auth |
|---|---|---|
home.html | Trang chủ: Resume, Nearby, Featured, AI chat, User menu | Optional |
detail.html | Chi tiết di sản: Hero, Stats, Tabs, AI float pill | Optional |
map.html | Bản đồ: GPS, Geofence, Audio points, Explore mode | Optional |
profile.html | Hồ sơ: Hoạt động, Yêu thích, HDV, Cài đặt ngôn ngữ | Required |
player.html | Trình phát audio: Controls, Transcript, Rating | Optional |
tour-host.html | Màn hình Guide: Tạo phiên, PIN, điều khiển audio | Required + can_guide |
tour-guest.html | Màn hình Khách: Join bằng PIN, chọn ngôn ngữ | Optional |
qr.html | Quét QR code → redirect đến điểm nghe | Optional |
login.html | Đăng nhập / Đăng ký | — |
chat.html | Trang AI chat riêng (planned) | Optional |
2. Actors & Roles
Hệ thống có 6 role theo thứ tự quyền tăng dần:
2.1 Phân quyền theo site (user_site_permissions)
Ngoài role toàn hệ thống, mỗi user có thể được cấp quyền chi tiết theo từng heritage_site:
| Permission | Mô tả |
|---|---|
can_edit | Tạo/sửa audio points và voice tours tại site này |
can_review | Phê duyệt nội dung tại site này |
can_create_qr | Tạo QR code tại site này |
can_guide | Dẫn tour (Tour Guide Mode) tại site này |
can_guide không phụ thuộc vào role — user có role member vẫn có thể dẫn tour nếu được cấp can_guide tại site cụ thể. Admin có thể cấp trực tiếp hoặc duyệt đơn đăng ký từ user.
3. Tech Stack
| Layer | Technology | Chi tiết |
|---|---|---|
| Frontend | Vanilla HTML/CSS/JS | PWA, Service Worker, Web Push API, Geolocation API |
| Backend | PHP 8 | File-based routing qua index.php, PDO/MySQL |
| Database | MySQL (tranhiep_tads) | Host: localhost, User: tranhiep_tads |
| Map | Leaflet.js + OpenStreetMap | Geofencing, GPS tracking, Custom divIcon |
| AI | Claude API (claude-sonnet-4) | Proxy qua Cloudflare Workers, RAG từ site data |
| Auth | Custom token (base64 + HMAC) | Access token 7 ngày, Refresh token 30 ngày |
| Upload | PHP move_uploaded_file | Audio → /audio/, Image → /images/, Avatar → /images/avatars/ |
| PHPMailer | Notification khi submit/approve/reject review | |
| TTS | Web Speech API | Client-side TTS tiếng Việt (fallback) |
| i18n UI | Custom lang.js | 6 ngôn ngữ, data-i18n attribute, HV_applyLang() |
4. Kiến trúc hệ thống
4.1 Request Flow
4.2 API Router Pattern
Tất cả API đi qua /api/index.php — file này require_once tất cả handler files và dùng PHP match() để route:
// Pattern: $r = segment[0], $id = segment[1], $sub = segment[2], $sub2 = segment[3] GET /api/heritage-sites → sites_list() POST /api/heritage-sites → site_create() GET /api/heritage-sites/{id} → site_detail($id) GET /api/tour-guide/create → tg_create() POST /api/guide/applications/1/approve → guide_approve($sub) // dùng $sub làm ID
PDO không cho phép dùng cùng tên named parameter (:lang) 2 lần trong 1 query. Dùng :lang1, :lang2, :lang3 cho các JOIN khác nhau.
4.3 Token Structure
// Format: base64(payload).HMAC_SHA256 payload: {uid, type, exp, jti} type: 'access' (7 ngày) | 'refresh' (30 ngày) // Lưu trong localStorage hv_token → access token hv_refresh_token → refresh token hv_user → {id, email, display_name, role, avatar_url} hv_lang → ngôn ngữ UI (vi/en/zh/ja/ko/fr)
5. Database Schema
5.1 Core Tables
5.2 i18n Tables
5.3 Tour Guide Tables
5.4 Guide Registration Tables
6. API Endpoints
Heritage Sites
Audio Points
Authentication
Review (Thẩm định)
Tour Guide
Guide Registration
i18n / Languages
7. Authentication Flow
8. Frontend App Modules
8.1 Chọn ngôn ngữ (i18n UI)
8.2 GPS & Geofencing
8.3 Audio Ngôn ngữ trong Detail
9. Admin CMS
9.1 Các tab Admin
| Tab | Route key | Quyền | Mô tả |
|---|---|---|---|
| 📊 Analytics | analytics | editor+ | KPI, biểu đồ, fullscreen mode |
| 🏛 Điểm đến | sites | editor+ | CRUD heritage_sites, nút HDV theo site |
| 🎧 Điểm nghe | audio | editor+ | CRUD audio_points, tab ngôn ngữ, badge review, gửi thẩm định |
| 🗺 Tuyến nghe | tours | editor+ | CRUD voice_tours |
| 📷 QR Codes | qr | editor+ | Tạo/quản lý QR theo site permission |
| ✅ Thẩm định | review | reviewer+ | Danh sách pending, approve/reject + lý do |
| 👥 Người dùng | users | admin+ | CRUD users, phân quyền 4 loại theo site, search |
| 🏷 Loại hình | types | admin+ | Quản lý 11 loại hình di sản |
| 🌐 Dịch thuật | i18n | editor+ | Badge ngôn ngữ, upload audio đa ngôn ngữ, bản dịch text |
| 🎙 HDV | guides | admin+ | Danh sách đơn đăng ký, duyệt/từ chối, top HDV, analytics |
| 📄 Trang tĩnh | pages | admin+ | FAQ, About, Terms... |
| ⚙️ Cài đặt | settings | admin+ | Cấu hình hệ thống |
9.2 Form Điểm nghe — Tab ngôn ngữ
Form tạo/sửa audio_point có tab ngôn ngữ VI | EN | ZH | JA | KO | FR:
- Tab VI: dùng fields gốc (af_url, af_transcript, af_tts_intro)
- Tab EN-FR: fields riêng (af_loc_en, af_transcript_en...) → lưu vào audio_localizations + content_translations
- Tên file audio upload:
{lang}_{audio_point_id}_{timestamp}.mp3 - Upload API:
POST /api/upload.php?type=audio&lang={lang}&site={site_id}&point={point_id}
10. Tour Guide Mode
10.1 Kiến trúc
Server relay (polling 2s) — không dùng WebSocket/SSE để đơn giản và tương thích tốt:
10.2 Fallback ngôn ngữ
11. Hướng dẫn viên — Registration Flow
12. Đa ngôn ngữ
12.1 Hai lớp ngôn ngữ
| Lớp | Phạm vi | Lưu ở đâu | Cơ chế |
|---|---|---|---|
| UI Language | Nút, label, placeholder, thông báo | lang.js (client) | data-i18n attribute + HV_applyLang() |
| Content Language | Tên di sản, mô tả, audio, transcript | DB: content_translations, audio_localizations | API ?lang=xx + COALESCE fallback |
12.2 Fallback Chain
12.3 Tên file audio
# Format: {lang}_{audio_point_id}_{timestamp}.ext en_gioi_thieu_chung_1779638946.mp3 zh_pho_co_hoi_an_chua_cau_1779639100.mp3 vi_dinh_vinh_binh_dvb_ap_01_1779349817.mp3 # Upload endpoint POST /api/upload.php?type=audio&lang=en&site=dinh_hoe_thi&point=gioi_thieu_chung
13. Workflow Thẩm định
Khi editor sửa bất kỳ content field nào (title, audio_url, transcript, latitude, longitude...), hệ thống tự động reset review_status về draft và xóa thông tin reviewer. Editor phải gửi thẩm định lại từ đầu.
14. AI Assistant
14.1 Kiến trúc RAG
14.2 Proxy (Cloudflare Workers)
API key Claude được lưu trong Cloudflare Workers secret — không expose ra client. Worker forward request đến api.anthropic.com và trả về response.
15. Key User Flows
15.1 Du khách khám phá di sản
15.2 Hướng dẫn viên dẫn tour đoàn
15.3 Editor tạo nội dung
16. Permission Matrix
| Action | guest | member | reviewer | editor | admin | superadmin |
|---|---|---|---|---|---|---|
| Xem di sản/audio | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Lưu yêu thích | — | ✅ | ✅ | ✅ | ✅ | ✅ |
| Đăng ký HDV | — | ✅ | ✅ | ✅ | ✅ | ✅ |
| Dẫn tour (can_guide) | — | ✅* | ✅* | ✅* | ✅ | ✅ |
| Tạo/sửa audio points | — | — | — | ✅* | ✅ | ✅ |
| Upload audio/ảnh | — | — | — | ✅ | ✅ | ✅ |
| Gửi thẩm định | — | — | — | ✅ | ✅ | ✅ |
| Phê duyệt nội dung | — | — | ✅* | — | ✅ | ✅ |
| Quản lý user | — | — | — | — | ✅ | ✅ |
| Duyệt đơn HDV | — | — | — | — | ✅ | ✅ |
| Export data | — | — | — | — | — | ✅ |
* Cần có user_site_permissions tương ứng tại site đó
17. Cấu trúc file server
/home/tranhiep/disannoi.vn/ ├── index.html # Redirect về home.html ├── home.html # Trang chủ FE ├── detail.html # Chi tiết di sản ├── map.html # Bản đồ ├── profile.html # Hồ sơ người dùng ├── player.html # Trình phát audio ├── tour-host.html # Guide interface ├── tour-guest.html # Guest interface ├── qr.html # QR scanner/redirect ├── login.html # Đăng nhập ├── sw.js # Service Worker (CACHE_NAME: hv-v6) ├── manifest.json # PWA manifest ├── mailer.php # PHPMailer wrapper (root level) │ ├── admin/ │ └── index.html # Admin CMS (single page app) │ ├── api/ │ ├── index.php # Router chính │ ├── config.php # DB constants, TOKEN_SECRET │ ├── db.php # PDO singleton db() │ ├── helpers.php # json_ok/error, cors, auth helpers, geo, pagination │ ├── auth.php # Token create/verify, login/register/me/refresh │ ├── users.php # CRUD users + site permissions │ ├── sites.php # heritage_sites + nearby search │ ├── audio.php # audio_points CRUD + i18n support │ ├── tours.php # voice_tours CRUD │ ├── tour.php # Tour Guide Mode (tg_* functions) │ ├── guide.php # Guide registration + analytics │ ├── languages.php # audio_localizations + content_translations API │ ├── review.php # Submit/approve/reject + email notify │ ├── qr.php # QR code management │ ├── analytics.php # Listen events + summary │ ├── favorites.php # User favorites │ ├── ratings.php # Site ratings │ ├── upload.php # File upload (audio/image/avatar) │ ├── ai.php # AI proxy endpoint │ ├── offline.php # Offline packs │ └── mailer.php # Email sending │ ├── assets/ │ ├── style.css # Design system (brand: #8B3E18, #C0622A) │ ├── lang.js # i18n UI strings (6 ngôn ngữ) │ ├── pwa.js # SW register, TTS, install prompt, token refresh │ ├── logo-white.svg # Logo trắng (dùng trên nền tối) │ └── favicon.svg │ ├── audio/ # File audio upload ({lang}_{point_id}_{ts}.mp3) ├── images/ # File ảnh di sản │ └── avatars/ # Avatar người dùng └── PHPMailer/ # PHPMailer library
18. Backlog & Roadmap
18.1 Đã hoàn thành
| Module | Status | Ghi chú |
|---|---|---|
| PWA core (home, detail, map, profile, player) | Done | Service Worker, offline mode |
| Auth system | Done | Custom token, auto refresh |
| GPS Geofencing | Done | Haversine, auto-trigger audio |
| QR Code system | Done | Create/scan/resolve |
| Admin CMS | Done | 12 tabs, single page app |
| Review workflow | Done | draft→pending→active→rejected + email |
| Tour Guide Mode | Done | PIN, sync audio, đa ngôn ngữ |
| Guide Registration | Done | Đăng ký, duyệt, analytics |
| Đa ngôn ngữ 6 tiếng | Done | UI + Content + Audio localizations |
| AI Assistant RAG | Done | Claude API, Cloudflare Workers proxy |
| Rating & Favorites | Done | Stars, yêu thích, lịch sử |
18.2 Backlog (Planned)
| Feature | Priority | Effort | Mô tả |
|---|---|---|---|
| chat.html — Trang AI riêng | High | S | Trang chat AI đầy đủ, lịch sử chat |
| TTS đa ngôn ngữ tự động | High | M | Google/Azure TTS → tạo audio từ transcript |
| Offline audio download | Medium | M | Cache audio cho toàn bộ di sản để dùng offline |
| Rating & Review FE | Medium | S | User viết review sau khi nghe |
| Push notification địa lý | Medium | M | Notify khi gần di sản yêu thích |
| Route planning | Medium | L | Gợi ý lộ trình tham quan tối ưu |
| AR Mode (WebXR) | Low | XL | Camera nhận diện di tích, overlay AR |
| Native App (Capacitor) | Low | L | iOS + Android native packaging |
| API mở cho đối tác | Low | L | OAuth2, rate limiting, docs |
| Analytics nâng cao | Low | M | Heatmap, export Excel/PDF, so sánh kỳ |
18.3 Known Issues / Tech Debt
| Issue | Ảnh hưởng | Giải pháp đề xuất |
|---|---|---|
| Token format không phải JWT chuẩn | Low | Migrate sang JWT library khi scale |
| PWA flash khi chọn ngôn ngữ lần đầu | Low UX | Prerender overlay trước khi load content |
| Tour Guide polling 2s gây load DB | Medium khi scale | Chuyển sang SSE hoặc WebSocket |
| Audio file không CDN | Medium performance | Tích hợp Cloudflare R2 hoặc S3 |
| content_translations chưa có index full-text | Low | Thêm FULLTEXT index khi data lớn |
Toàn bộ hệ thống được thiết kế stateless, file-based routing dễ deploy trên bất kỳ shared hosting nào. Database schema chuẩn hóa, API RESTful sẵn sàng tích hợp mobile app native hoặc đối tác bên thứ ba.