CCM — Continuous Delivery & Architecture
แอปเพลง Cross-Platform (Flutter) · Clean Architecture · Firebase · CI/CD
CCM ⚡CRAZY⚡CRAZY MUSIC
1. ภาพรวมโปรเจกต์ (Portfolio Summary)
แอปเพลง Cross-Platform (Android/iOS/Web-PWA) ของช่อง YouTube CRAZY⚡CRAZY MUSIC — เล่นเพลงผ่าน YouTube IFrame
Player อย่างถูกกฎ ToS · ระบบแชท real-time (ห้องแชท/เพื่อน/DM) · ระบบสมาชิก VIP + COIN economy
(ซื้อ item ของขวัญ แลกเงิน 40%) · ชำระเงินด้วย PromptPay QR + สลิป + แอดมินอนุมัติ ·
ระบบแจ้งเตือน badge จริง · i18n 9 ภาษา · Light/Dark theme · CI/CD อัตโนมัติบน GitHub Actions
2. ฟีเจอร์ของแอปและการทำงานของระบบ (Features & System Behavior)
| ฟีเจอร์ | รายละเอียด & การทำงาน |
|---|---|
| 🚀 Splash + Onboarding + ล็อกอิน | Splash โหลด ~2 วิ → เช็ค SharedPreferences (onboarded) → Onboarding 3 หน้า (แสดงครั้งแรกเท่านั้น) → HomeShell 6 แท็บ · ล็อกอินด้วย Google Sign-In → Firebase Auth (GoogleAuthService ตัวกลาง + สลับ/เพิ่มบัญชีได้) · ยังไม่ล็อกอินดูเพลงได้ แต่แชท/รายการโปรด/ประวัติใช้ไม่ได้ |
| 🏠 หน้าแรก + เพลง | ส่วน "แนะนำ" + "เพลงใหม่" (10 วันล่าสุด, แสดงจำนวน) เป็นการ์ดเลื่อนแนวนอนใช้ปก YouTube เป็นพื้นหลัง (fallback ไล่สีม่วง) · รายการเพลงแบ่งส่วน ทั้งหมด/Short/Live กำลังเล่น/Live จบแล้ว (ส่วนว่างซ่อน) · ทั้งหมดเป็น Stream จาก Firestore — ข้อมูลเปลี่ยน จอเปลี่ยนทันทีไม่ต้อง refresh |
| 🎵 เล่นเพลง (YouTube embed) | กดเพลง → YouTube IFrame Player (bottom sheet / หน้าเต็มจอ PlayerPage พร้อมคอมเมนต์จาก YouTube API เรียง top สูงสุด 20) · รองรับลิงก์ watch/youtu.be/shorts/embed/live/v · Mini player โหมดเสียงเหนือเมนูล่าง · ห้ามดึง stream/แปลง MP3 (ToS) · กดถูกใจ/คอมเมนต์/แชทสด = เปิดแอป YouTube (ประหยัด quota — โค้ด like ผ่าน API พร้อมใช้ ปิดไว้เพราะ 50 units/ครั้ง) |
| 📱 Shorts | เพลงหมวด short เป็น grid แนวตั้ง 9:16 → เปิดหน้าแบบ YouTube Shorts: วิดีโอบน + แถบ description/ปุ่ม 👍 💬 🔗 ล่าง · ปัดขึ้นลงเปลี่ยนเพลง · description จริงจาก YouTube API · overlay mode เฉพาะ Web (Android/iOS เป็น native WebView วางทับ touch ซ้อนบนวิดีโอไม่ได้ — ข้อจำกัดของ plugin) |
| 🔴 Live | เช็คสถานะผ่าน YouTube Data API v3 อัตโนมัติเมื่อโหลด (ไม่ต้องกด refresh) + ปุ่ม refresh · badge ● LIVE / จบไปแล้ว / กำลังจะมา · จบแล้วแอดมินย้ายเข้า "ประวัติ Live" (ย้ายกลับได้) · แชทสด = ปุ่มเปิดบน YouTube (แพง 5 units/ครั้งถ้า poll) |
| ♥️ รายการโปรด + ประวัติรับชม | ปุ่ม ♥ ทุกที่ที่มีเพลง — แยกตาม uid แต่ละบัญชี Google สลับบัญชีโหลดชุดใหม่อัตโนมัติ (auth-aware) ไม่ล็อกอิน = เคลียร์ทั้งหมด · ประวัติรับชม: บันทึกทุกช่องทางที่เล่น, dedupe ด้วย videoId, เรียงล่าสุดบนสุด limit 50 (Firestore users/{uid}/history) |
| 💬 ระบบแชท (ห้อง/เพื่อน/DM) | ห้องแชท real-time: ปกติจำกัด 10 คน · VIP จำกัด 20 คน + รหัสผ่าน (ตรวจฝั่ง client) · สร้างห้องเฉพาะแอดมิน+VIP · เข้าห้อง = join members/{uid}, ออก = คืนที่ว่าง · เพื่อน: คำขอ → ตอบรับ → friendships · DM เก็บ dms/{uidA_uidB}/messages (chatId เรียง uid) ไม่จำกัดข้อความ · สถานะอ่าน seenBy (arrayUnion ไม่นับซ้ำ) + เวลาส่ง · ส่งเพลงเข้าแชทได้ (SongPicker + ▶ เล่นฟรีก่อน) · badge แดง = ยังไม่อ่าน (per เพื่อน + รวม) |
| 🎁 ของขวัญ + Wallet (COIN economy) | ปุ่ม 🎁 ใน DM → เลือก item 6 แบบ (🌹 10 → 👑 2,500) → Firestore runTransaction หัก COIN ผู้ส่ง (bought ก่อน → received) + เพิ่ม received ผู้รับ + การ์ดของขวัญในแชท · ผู้รับกด "รับเงิน" แลกคืน 40% (kGiftCashRate, giftConverted กันแลกซ้ำใน transaction เดียว) · coin ไม่พอ → เปิดหน้าซื้อ COIN |
| 👑 สมาชิก VIP + COIN (แพ็กเกจ) | popup 2 แท็บ (vip_packages_sheet): 👑 สมาชิก VIP รายเดือน ฿99 / รายปี ฿899 (สิทธิ: สร้างห้อง VIP 5 ห้อง + badge + แชทไม่จำกัด) · 🪙 COIN 100/500/1,200 โค้ดราคาตายตัวใน chat_repository (kVipPlans/kVipPackages — unit test ตรวจ) |
| 💳 ชำระเงิน PromptPay + สลิป | สั่งซื้อ → คำขอ vip_requests/{uid} (awaiting_slip) → หน้าเต็มจอ: QR พร้อมเพย์ generate ในแอป (EMVCo payload + CRC16, ฝังยอดเงินใน QR — unit test ครบ) → แนบสลิป (image_picker Photo Picker → ย่อ 540px/q30 → base64 ≤950KB ใน Firestore — ไม่ใช้ Storage) → กด "ส่ง" (slip_sent) → popup รออนุมัติ 5-15 นาที → สลิป lock ดูอย่างเดียว (ซูมเต็มจอได้) |
| 🛡️ แอดมินอนุมัติ + Audit | แถบแจ้งในหน้าแชท + หน้าแจ้งเตือน (กด item = dialog สลิป+อนุมัติ/ปฏิเสธ) · อนุมัติสมาชิก → vips/{uid} · COIN → wallets.bought increment · ปฏิเสธ → ลบคำขอ · ทั้งสองกรณี: เขียน noti ผู้ใช้ + payment_history (รายการประวัติแอดมิน + ค้นหาชื่อ/อีเมล + ดูรายการปฏิเสธ) |
| 🔔 หน้าแจ้งเตือน | กระดิ่ง 🔔 → NotificationsScreen · badge ตัวเลขแดง real-time: ผู้ใช้ = notis read:false · แอดมิน = คำขอ slip_sent ยังไม่ seen · กด item = mark read/seen แล้ว badge ลด · ผู้ใช้เห็นสถานะคำขอ + ผลอนุมัติ/ปฏิเสธ (แพ็กเกจ+ราคา+เวลา) |
| 🛡️ สถานะ Admin/VIP โชว์ทุกที่ | VipNameText (ชื่อ + 🛡/👑) · UserAvatar กรอบ "อลังการ": วงแหวนไล่สีหมุน (SweepGradient, VIP ทอง-แดง / Admin ฟ้า-ม่วง) + มงกุฎ/โล่บนหัว + ป้าย VIP/ADMIN + glow — resolve จาก stream (adminUids + vips) สถานะเปลี่ยนทุกจุดเปลี่ยนตาม · ใช้ใน popup/ห้องแชท/DM/list เพื่อน/หน้าคุณ |
| 👤 หน้า "คุณ" + ตั้งค่า | การ์ดสถานะ+wallet แยก 4 สถานะ real-time (🛡 Admin 🪙 ∞ / 👑 VIP / ⏳ รออนุมัติ / ✨ ทั่วไป) แสดง 🪙 รวม/ซื้อ/ได้รับ/แลกแล้ว · เมนู: เพื่อน, ห้องของฉัน, รายการโปรด, ประวัติรับชม, ช่องในเครือ, รายการเพลง (แอดมิน) · ⚙ หน้าตั้งค่าเต็มจอ: ภาษา (9 ภาษา + ค้นหา), ธีม, เพิ่มลิงก์เพลง (แอดมิน), ความช่วยเหลือ/ข้อกำหนด/เกี่ยวกับ |
| 🌐 i18n + ธีม | 9 ภาษา (ไทย/EN/中文/日本語/한국어/ລາວ/ဗမာ/Tiếng Việt/Melayu) — dictionary 250+ keys, S.t()/context.tr() ทั้งแอป (ห้าม hardcode), default ตามอุปกรณ์ fallback ไทย, เลือกแล้วเปลี่ยนทันที · ธีม Light/Dark ตาม system หรือเลือกเอง — สีทุก widget ใช้ colorScheme (CI สแกน banned สีเข้ม hardcode) |
| 📺 ช่องในเครือ + กฎเพลง (ToS) | เพิ่มเพลงได้เฉพาะแอดมิน · วางลิงก์ → ชื่อดึงอัตโนมัติจาก oEmbed + ประเภทตรวจอัตโนมัติ (shorts→Short, live→Live) + ห้ามซ้ำ (เช็ค videoId) · loading เต็มจอบล็อกการกด · ช่องในเครือ: เพิ่มด้วย URL ทุกรูปแบบ ตรวจจริงผ่าน API, แอดซ้ำไม่ได้, ปิด/ลบช่อง = ซ่อนเพลงช่องนั้นทันที (เพิ่มกลับ = เพลงกลับมา) · validate เจ้าของวิดีโอตอนเพิ่มเท่านั้น |
3. โครงสร้าง Architecture — Clean Architecture with three layers, feature-first
lib/
├── main.dart # entry point: Firebase init, runApp
├── firebase_options.dart # generated Firebase config
├── core/ # cross-feature utilities
│ ├── app_config.dart # API keys, PromptPay ID (single source of config)
│ ├── i18n.dart # dictionary i18n — 250+ keys × 9 languages, S.t()
│ ├── theme_controller.dart # ThemeMode ValueNotifier (SharedPreferences)
│ ├── logger.dart # ccsLog() — ทุก error ต้องเห็นใน terminal
│ └── promptpay.dart # PromptPay QR payload (EMVCo + CRC16, ฝังยอดเงิน)
├── models/ # shared domain models
│ ├── song.dart # Song (videoId parser, thumbnails, category labels)
│ └── channel.dart # Channel (handle, enabled, isOwner)
├── data/ # data layer — Firebase อยู่เฉพาะนี้
│ ├── repositories/
│ │ ├── song_repository.dart # songs CRUD/streams, live history
│ │ ├── channel_repository.dart # affiliate channels CRUD, owner seeding
│ │ └── chat_repository.dart # rooms/messages, friends, DMs, VIP/COIN,
│ │ # wallets (transactions), receipts, notis, badges
│ └── services/
│ ├── youtube_live.dart # YouTube Data API (live, comments, validation)
│ ├── google_auth_service.dart # Google sign-in → Firebase, YouTube OAuth
│ ├── song_editor.dart # add/edit/delete song + confirm dialog + isAdmin
│ ├── history_service.dart # watch history per uid (dedupe by videoId)
│ ├── favorite_service.dart # favorites per uid (auth-aware)
│ └── account_store.dart # account switcher storage
└── features/ # presentation layer, feature-first
├── shell/presentation/ # splash, onboarding, home shell (nav + top bar)
├── songs/presentation/ # home, category, shorts, songs, favorites,
│ └── widgets/ # history, live + song list/picker, mini player
├── chat/presentation/ # rooms, chat room, DM, friends, notifications,
│ └── widgets/ # VIP history + packages sheet, payment page,
│ # status name text, fancy status avatar
└── settings/presentation/ # you page, settings menu, channels, language,
# help, theme, account page
4. Architecture decisions (7 ข้อ)
| การตัดสินใจ | เหตุผล / รายละเอียด |
|---|---|
| 1. Stream-first data layer | ทุก real-time feature (เพลง แชท ห้อง แจ้งเตือน) เป็น Stream จาก repository — screens ใช้ StreamBuilder ไม่มี manual refresh · screens ไม่แตะ Firestore ตรง ๆ ผ่าน repository เท่านั้น |
| 2. Feature-first presentation | แบ่ง 4 features (shell/songs/chat/settings) — cross-feature reuse ผ่าน models/ + data/ เท่านั้น เช่น chat ส่งเพลงโดยใช้ Song model + SongPicker ไม่ depend หน้าเพลง |
| 3. Shared core/ | config (จุดเดียวของ keys + เลขพร้อมเพย์) · i18n dictionary · theme · logger · PromptPay generator — framework-free ทุก feature เรียกใช้ได้ |
| 4. Transactions ที่การเงิน | ส่งของขวัญ/แลกเงิน ใช้ Firestore runTransaction — หัก wallet ผู้ส่ง (bought→received) + ตั้ง giftConverted ใน transaction เดียว กันยอดติดลบ/แลกซ้ำ |
| 5. Payment เป็น request workflow | ไม่มี paid backend — คำขอ = 1 doc (awaiting_slip → slip_sent) สลิปเป็น base64 ≤950KB (ตรวจก่อนเขียน) · อนุมัติ = เขียน vips/wallets + noti + payment_history — audit ได้จาก Firestore ล้วน |
| 6. Status identity ผ่าน streams | UserAvatar/StatusIcon resolve uid → Admin (adminUids) / VIP (vips snapshot) แล้ว render วงแหวนหมุน + มงกุฎ/โล่ + ป้าย — สถานะเปลี่ยน ทุกจุดเปลี่ยนตาม real-time |
| 7. Defensive parsing + visible errors | อ่าน Firestore ด้วย map access + ?? fallback (field หายไม่ throw) · error ผ่าน ccsLog เห็นใน terminal เสมอ + SnackBar · destructive actions มี confirm dialog |
5. CD Pipeline — จากโค้ดถึงผู้ใช้
1. Code scananalyze · format · secret · patterns · test
→
2. BuildWeb · APK (release)
→
3. Deploy WebFirebase Hosting + rules
→
4. Release APKGitHub Releases (ccm.apk)
→
5. Noti ผู้ใช้หน้าติดตั้งได้เวอร์ชันใหม่
6. CI — Code scan / Clean code gate (5 ด่าน บังคับทุก push/PR)
| ด่าน | ตรวจอะไร | fail เมื่อ / วิธีแก้ |
|---|---|---|
| flutter analyze | static analysis ทั้ง lib/ | มี error/warning แม้ 1 อัน → แก้ตาม report |
| dart format check | โค้ดต้อง format ระเบียบทุกไฟล์ | `dart format lib` แล้ว commit ใหม่ |
| secret scan | private key / client_secret / API key นอก app_config.dart | ย้ายไป app_config จุดเดียว หรือ env |
| banned patterns | print() ตรง ๆ · hardcode สีเข้มเก่า (0xFF212121/2A2A2A) | ใช้ ccsLog() / colorScheme |
| flutter test | unit tests 31+ ชุด: PromptPay payload (EMVCo+CRC16) · Song videoId parse · i18n (ตรวจครบ 9 ภาษาทุก key) · chat constants (dmChatId/gifts/packages) | test fail → แก้ก่อน merge |
7. CD — Deploy สภาพแวดล้อม
| สภาพแวดล้อม | URL | มาจากไหน | ใครใช้ |
|---|---|---|---|
| Production Web | ccs-crazycrazymusic.web.app | deploy อัตโนมัติจาก Develop/main | ผู้ใช้ทุกคน (PWA) |
| Android APK | github.com/ProgramZa2560/ccs-releases/releases/latest | อัปเดตอัตโนมัติทุก deploy | ผู้ใช้ Android |
| Dev | emulator Pixel_9_Pro · Xiaomi 2b3b02ff · iOS Sim | build debug ติดตั้งเอง | ทีมพัฒนา |
🔗 ลิงก์สด: หน้าติดตั้ง https://ccs-crazycrazymusic.web.app/install.html ·
เว็บแอป (PWA) https://ccs-crazycrazymusic.web.app ·
APK https://github.com/ProgramZa2560/ccs-releases/releases/latest/download/ccm.apk
8. Rollback
- Web: Firebase Console → Hosting → Rollback version ก่อนหน้า (ทันที)
- APK: อัปโหลดเวอร์ชันเก่าทับใน GitHub Release
- Rules: deploy ไฟล์ rules จาก commit ก่อนหน้า
- ข้อมูล: Firestore ไม่ rollback — schema ต้อง backward-compatible เสมอ
9. Smoke test หลัง deploy
- หน้าแรกโหลดเพลง · /install.html ปุ่ม APK ใช้ได้
- ล็อกอิน Google → แชทส่งข้อความ → กระดิ่ง/สถานะ VIP
- ตรวจ log: Firebase Console / adb logcat
10. Tech stack
| Flutter / Dart | app 3 แพลตฟอร์มจาก codebase เดียว |
| Firebase Auth | Google Sign-In |
| Cloud Firestore | real-time + transactions + security rules |
| Firebase Hosting | web app + หน้าติดตั้ง |
| YouTube Data API v3 | live status, comments, validation |
| GitHub Actions | CI/CD (scan → build → deploy) |
| qr_flutter · image_picker | QR พร้อมเพย์ · สลิป (Photo Picker) |
11. สิ่งที่ต้องตั้งค่าล่วงหน้า
| ✓ | รายการ |
|---|---|
| ✅ | GitHub Actions ci.yml ใน repo |
| ✅ | Repo แจก APK ccs-releases (public) |
| ⬜ | Secret FIREBASE_TOKEN (firebase login:ci) |
| ⬜ | เลขพร้อมเพย์จริง (app_config.dart) |
| ⬜ | Google Play Billing / App Store (จ่ายเงินจริง) |
ข้อจำกัดที่รู้: Firebase Hosting ฟรี (Spark) ห้ามโฮสต์ .apk → แจกผ่าน GitHub Releases ·
Firestore field จำกัด 1MB (สลิปย่อ 540px/q30) · iOS Simulator เล่นวิดีโอไม่ได้ ·
ห้ามดึง stream/แปลง MP3 (YouTube ToS) — เล่นผ่าน IFrame embed เท่านั้น
Next steps: Google Play internal testing (แจก APK ผ่านสโตร์) · เพิ่ม tests ฝั่ง data/ (mock Firestore — ปัจจุบันมี unit tests 31 ชุด: PromptPay/Song/i18n/chat constants แล้ว) ·
Firebase App Check + API key → Cloud Functions · Privacy Policy + App Privacy labels ·
แจ้งเตือนหลัง deploy (Slack/LINE)