รายงานการออกแบบและการเชื่อมต่อฐานข้อมูล Supabase (Supabase Integration & Optimization Report)
โครงการวิจัย: TaijiFlow AI - PhD Research (Phase 2 & Phase 3)
ประเภทรายงาน: เอกสารประวัติการเขียนโค้ดและการออกแบบระบบ (System Design & Implementation)
1. วัตถุประสงค์ (Objectives)
ย้ายสถาปัตยกรรมแอปพลิเคชันจากระบบประมวลผลโลคอลทั้งหมดแบบเดิม (Zero-Server Architecture) ซึ่งไม่มีการระบุตัวตนและบันทึกประวัติใดๆ ไปสู่ระบบคลาวด์แบบเซิร์ฟเวอร์เลส (Client-Server / Serverless Architecture) โดยมีวัตถุประสงค์ดังนี้:
- จัดเก็บโปรไฟล์ สรีระร่างกาย และสถิติพัฒนาการส่วนบุคคลของผู้เข้าร่วมการทดลอง
- บันทึกและบีบอัดข้อมูลพิกัดโครงร่างข้อต่อร่างกาย (Joint Coordinates Dataset) ตลอดช่วงการซ้อมรำ เพื่อนำมาประมวลผลต่อยอดเป็นชุดข้อมูลฝึกสอน (Labeled Training Dataset) สำหรับแบบจำลอง Deep Learning
- รับประกันความปลอดภัยและการเข้าถึงข้อมูลส่วนตัวตามนโยบายความเป็นส่วนตัวและสิทธิ์ความปลอดภัยสูงสุด (Data Privacy & RLS Storage)
2. การออกแบบโครงสร้างฐานข้อมูล (Database Schema Design)
เราได้ออกแบบตารางและระบบ Trigger บนระบบฐานข้อมูล PostgreSQL ของ Supabase ดังนี้:
2.1 ตารางประวัติและโปรไฟล์ผู้ใช้ (public.users)
ใช้เก็บข้อมูลโปรไฟล์และกายภาพเริ่มต้นที่จำเป็นสำหรับคำนวณและปรับเทียบอัตราส่วนโครงกระดูก:
id(uuid, Primary Key, referencesauth.users.idon delete cascade)display_name(text) — ชื่อแสดงผลexperience_level(text, default:'beginner') — ระดับทักษะผู้รำ (beginner, intermediate, advanced)height(numeric, nullable) — ส่วนสูง (หน่วยเซนติเมตร) ใช้ปรับสัดส่วนกล้องweight(numeric, nullable) — น้ำหนัก (หน่วยกิโลกรัม)consent_accepted(boolean, default:false) — สถานะยอมรับข้อตกลงความเป็นส่วนตัวและแพทย์ (IRB Consent)research_group(text, default:'experimental') — กลุ่มวิจัยสำหรับการทำ RCT (experimentalหรือcontrol)created_at/updated_at(timestamp with time zone)
2.2 ตารางสรุปเซสชันการซ้อมรำ (public.training_sessions)
ใช้บันทึกผลคะแนนและค่าผิดพลาดสะสมรายรอบการรำ เพื่อนำมาแสดงสถิติและดึงพาธไฟล์บันทึก:
id(uuid, Primary Key, default:gen_random_uuid())user_id(uuid, referencespublic.users.idon delete cascade)session_id(text, Unique) — รหัสเซสชันเฉพาะที่สร้างฝั่ง Clientexercise(text) — ชื่อท่ารำที่ฝึกซ้อม (เช่นcloud_hand,rollback_r)level(text) — ระดับเกณฑ์ความยาก (L1, L2, L3)score(numeric) — คะแนนเฉลี่ยความถูกต้อง (AQA Score, 0-100)grade(text) — เกรดประเมินผลสัมพัทธ์ (A, B, C, D, F)duration(numeric) — ระยะเวลาที่รำจริง (หน่วยวินาที)total_frames(integer) — จำนวนเฟรมทั้งหมดที่บันทึกfile_path(text) — ลิงก์ที่อยู่ไฟล์พิกัดละเอียดใน Storage (.json)category_scores(jsonb) — คะแนนเฉลี่ยแยกหลักการไท่จี๋ 6 ด้าน (Path, Balance, Continuity, Synchrony, Stability, Softness)created_at(timestamp with time zone)
2.3 ระบบสร้างโปรไฟล์อัตโนมัติ (Postgres Auto-Trigger)
เพื่อความสะดวกในการเก็บประวัติ ทันทีที่มีการสมัครสมาชิกในระบบ Auth ระบบ Database ของ Supabase จะรัน Function เพื่อสร้างโปรไฟล์เริ่มต้นลงตาราง public.users ทันที:
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS trigger AS $$
BEGIN
INSERT INTO public.users (id, display_name, experience_level, consent_accepted)
VALUES (
new.id,
COALESCE(new.raw_user_meta_data->>'display_name', 'Taiji Practitioner'),
'beginner',
COALESCE((new.raw_user_meta_data->>'consent_accepted')::boolean, false)
);
RETURN new;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();3. นโยบายความปลอดภัยและการเข้าถึงข้อมูล (Row Level Security - RLS)
เพื่อป้องกันความเสี่ยงด้านความเป็นส่วนตัวของข้อมูลผู้ป่วย/อาสาสมัคร ระบบได้เปิดใช้งาน RLS ในทุกตารางและโฟลเดอร์เก็บไฟล์:
- สิทธิ์ตาราง
public.users:- เลือกดูข้อมูล: เฉพาะผู้ใช้ที่ล็อกอินแล้วและเป็นเจ้าของบัญชีเท่านั้น (
auth.uid() = id) - ปรับปรุงข้อมูล: เฉพาะเจ้าของบัญชีเท่านั้น (
auth.uid() = id)
- เลือกดูข้อมูล: เฉพาะผู้ใช้ที่ล็อกอินแล้วและเป็นเจ้าของบัญชีเท่านั้น (
- สิทธิ์ตาราง
public.training_sessions:- เลือกดูข้อมูล: ดึงข้อมูลประวัติเฉพาะที่เป็นของตนเองเท่านั้น (
auth.uid() = user_id) - บันทึกข้อมูล: บันทึกประวัติเฉพาะของตนเองเท่านั้น (
auth.uid() = user_id)
- เลือกดูข้อมูล: ดึงข้อมูลประวัติเฉพาะที่เป็นของตนเองเท่านั้น (
- สิทธิ์จัดเก็บไฟล์พิกัดละเอียด (Supabase Storage Bucket:
training-sessions):- กำหนดสิทธิ์ให้โฟลเดอร์ไฟล์มีรูปแบบ
training-sessions/{user_id}/{session_id}.json - กำหนดนโยบาย RLS ให้ตรวจสอบสิทธิ์เข้าถึงโดยตรวจชื่อโฟลเดอร์แรกให้แมตช์กับ UID ของผู้ที่เข้าถึงเท่านั้น:sql
-- Policy สำหรับอัปเดตและอ่านไฟล์ (storage.foldername(name))[1] = auth.uid()::text
- กำหนดสิทธิ์ให้โฟลเดอร์ไฟล์มีรูปแบบ
4. ท่อส่งข้อมูลและการบีบอัดระดับเฟรม (Data Pipeline & Minimization)
เพื่อรองรับการดึงข้อมูลดิบไปประมวลผลต่อด้วย Machine Learning ระบบได้พัฒนาการลดรูปพิกัดและการสร้าง Pipeline ที่เสถียรดังนี้:
4.1 การบีบอัดข้อมูลร่างกาย (Landmark Minimization)
พิกัด MediaPipe Landmarks ทั้งหมด 33 จุด ประกอบด้วยแกน x, y, z และความน่าเชื่อถือ visibility ทศนิยมเดิมมีความยาวถึง 15 ตำแหน่ง ซึ่งกินพื้นที่มาก ไฟล์ซ้อม 5 นาทีจะมีขนาดมากกว่า 6-8 MB ระบบจึงแก้ไขดังนี้:
- ลดรูปทศนิยมเหลือ 4 ตำแหน่ง: ปรับลดโดยปัดเศษผ่าน Math helper:javascriptส่งผลให้ขนาดไฟล์ JSON สุทธิลดลงทันที 20-30% (เหลือเฉลี่ย ~1.2 MB ต่อรอบการซ้อม 5 นาที)
const round4 = (num) => Math.round(num * 10000) / 10000; - การบันทึกเวลาสัมพัทธ์แบบมิลลิวินาที (
t): แทนที่จะเก็บเวลาจริง (Epoch Timestamp) ระบบได้คำนวณระยะเวลาต่างตั้งแต่ออกตัวเริ่มรำในหน่วยมิลลิวินาที ทำให้ข้อมูลมีความเสถียร เหมาะกับการรันโมเดลอนุกรมเวลา (Temporal Sequence Model) เช่น LSTM และ Transformer - การคลีนป้ายข้อมูลความผิดพลาด (Heuristics Labeling): นำฟีดแบ็กความผิดพลาดรายเฟรมมาถอดรหัสลบอักขระพิเศษ บันทึกเป้นอาเรย์
labelsในไฟล์พิกัด เพื่อทำหน้าที่เป็นชุดข้อมูลที่มีคำตอบเฉลยสำเร็จรูป (Labeled Dataset) ในตัว
4.2 ระบบอัปโหลดอัตโนมัติและ Asynchronous Loop
แอปพลิเคชันจะรวบรวมประวัติและพิกัดลงใน RAM ระหว่างเล่น เมื่อผู้ฝึกสิ้นสุดการซ้อม:
- ข้อมูลทั้งหมดจะถูกประกอบเข้าชุด (Dataset Assembly)
- ระบบเรียก Asynchronous fetch เพื่ออัปโหลดไฟล์ JSON ขึ้น Supabase Storage และแทรกแถวสถิติลงตาราง Database ไปพร้อมๆ กัน โดยไม่ล็อกหน้าอินเตอร์เฟสผู้ใช้
- ระบบจะแสดงแถบข้อความ Toast Notification สีเขียวแจ้งเมื่ออัปโหลดคลาวด์สำเร็จ
- ระบบสำรอง (CORS / Network Fallback): หากขาดการเชื่อมต่ออินเทอร์เน็ตหรือติดปัญหาเครือข่าย ระบบจะเปลี่ยนเป็นแจ้งเตือนข้อความเตือน และดาวน์โหลดไฟล์ JSON บันทึกตรงลงโฟลเดอร์เครื่องคอมพิวเตอร์ของผู้ใช้ เพื่อรับประกันว่าข้อมูลวิจัยของอาสาสมัครจะไม่สูญหายเด็ดขาด