Version 7 (modified by 2 weeks ago) ( diff ) | ,
---|
Развој на апликација (App Development)
На оваа страна докажуваме дека апликацијата ги користи DB можностите (погледи, транскации, складирани функции, индекси) преку HTTP рути, со JWT автентикација.
Важни рути
Login (JWT)
POST /api/login Body: {"type":"user","email":"...","password":"..."}
Враќа token (JWT) кој потоа се праќа во Authorization: Bearer <token>.
Транскација + складирана функција
POST /api/book-class
Body (со JWT или со userId во body): {"classId":7} или {"userId":12,"classId":7}
Користи BEGIN/COMMIT и SELECT book_class($1,$2); тригери блокираат преполнување и одржуваат seats_available.
Пример одговори:
{ "success": true, "code": "OK" }
или:
{ "success": false, "code": "CLASS_FULL" }
Извештаи од погледи (Views)
GET /api/reports/top-spenders → vw_user_spend GET /api/reports/class-utilization → vw_class_utilization GET /api/reports/training-pop-monthly → vw_training_pop_monthly
EXPLAIN/ANALYZE преку апликацијата
GET /api/debug/explain-events GET /api/debug/explain-class-join (Со мал сет, planner понекогаш избира Seq Scan; за принуден Index Scan може да се види на страната „Напредни извештаи“.)
Докази
1) JWT Login → добиваме токен
Команда:
curl -X POST http://localhost:5000/api/login ^ -H "Content-Type: application/json" ^ -d "{"type":"user","email":"newstudent@gmail.com ","password":"Newstudent!15"}"
Одговор:
{ "success": true, "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....(скратено)...", "role": "user", "userId": "17" }
2) Транскација + функција: book_class (со JWT)
Команда:
curl -X POST http://localhost:5000/api/book-class ^ -H "Content-Type: application/json" ^ -H "Authorization: Bearer eyJhbGciOi...X3E" ^ -d "{"classId":7}"
Одговор:
{ "success": true, "code": "OK" }
3) book_class без JWT (одбиено од апликацијата)
Команда:
curl -X POST http://localhost:5000/api/book-class ^ -H "Content-Type: application/json" ^ -d "{"classId":7}"
Одговор:
{ "success": false, "error": "Missing user identity" }
(Овие два излези заедно докажуваат: JWT заштита на критична рута и транскација + тригери во базата.)
4) Views → извештаи
GET /api/reports/top-spenders:
[{"user_id":"8","username":"mikiYoga","email":"miki@example.com ","spend_packages":"45.00","spend_merch":"12.00","total_spend":"57.00","spend_rank":"1"}, {"user_id":"13","username":"bojan","email":"bojan@example.com ","spend_packages":"0","spend_merch":"30.00","total_spend":"30.00","spend_rank":"2"}, {"user_id":"12","username":"ana","email":"ana@example.com ","spend_packages":"25.00","spend_merch":"0","total_spend":"25.00","spend_rank":"3"}, {"user_id":"9","username":"davidG","email":"david@example.com ","spend_packages":"0","spend_merch":"0","total_spend":"0","spend_rank":"4"}, {"user_id":"10","username":"proba1","email":"proba1@gmail.com ","spend_packages":"0","spend_merch":"0","total_spend":"0","spend_rank":"4"}, {"user_id":"14","username":"ciro","email":"ciro@example.com ","spend_packages":"0","spend_merch":"0","total_spend":"0","spend_rank":"4"}, {"user_id":"16","username":"proba2","email":"proba2@gmail.com ","spend_packages":"0","spend_merch":"0","total_spend":"0","spend_rank":"4"}, {"user_id":"17","username":"newstudent","email":"newstudent@gmail.com ","spend_packages":"0","spend_merch":"0","total_spend":"0","spend_rank":"4"}]
GET /api/reports/class-utilization:
[{"class_id":"5","date":"2025-06-09T22:00:00.000Z","start_time":"08:00:00","end_time":"09:00:00","location":"Studio A","capacity":20,"booked":"1","utilization_pct":"5.00","instructor_id":"5","daily_rank":"2"}, {"class_id":"6","date":"2025-06-09T22:00:00.000Z","start_time":"09:30:00","end_time":"10:30:00","location":"Studio B","capacity":15","booked":"1","utilization_pct":"6.67","instructor_id":"6","daily_rank":"1"}, {"class_id":"7","date":"2025-09-26T22:00:00.000Z","start_time":"18:00:00","end_time":"19:00:00","location":"Studio A","capacity":2,"booked":"2","utilization_pct":"100.00","instructor_id":"7","daily_rank":"1"}]
GET /api/reports/training-pop-monthly:
[{"training_id":"7","training_name":"Vinyasa","month":"2025-08-31T22:00:00.000Z","num_bookings":"2","rank_in_month":"1"}, {"training_id":"8","training_name":"Yin","month":"2025-08-31T22:00:00.000Z","num_bookings":"2","rank_in_month":"1"}, {"training_id":"6","training_name":"Hatha Basics","month":"2025-05-31T22:00:00.000Z","num_bookings":"1","rank_in_month":"1"}, {"training_id":"5","training_name":"Vinyasa Flow","month":"2025-05-31T22:00:00.000Z","num_bookings":"1","rank_in_month":"1"}]
5) EXPLAIN/ANALYZE преку апликацијата
GET /api/debug/explain-events (мал сет - Seq Scan) Извадок:
[{"Plan":{"Node Type":"Limit", "Plans":[ {"Node Type":"Sort", "Plans":[ {"Node Type":"Seq Scan", "Relation Name":"Event", "Filter":"(date >= CURRENT_DATE)"} ]} ]},"Planning Time":0.701,"Execution Time":0.108}]
GET /api/debug/explain-class-join (мал сет - Seq Scans) Извадок:
[{"Plan":{"Node Type":"Limit","Plans":[ {"Node Type":"Sort","Plans":[ {"Node Type":"Aggregate","Plans":[ {"Node Type":"Sort","Plans":[ {"Node Type":"Nested Loop","Plans":[ {"Node Type":"Seq Scan","Relation Name":"Class","Filter":"(date >= CURRENT_DATE)"}, {"Node Type":"Seq Scan","Relation Name":"User_Booked_Class"} ] } ] } ] } ] } ] },"Planning Time":1.033,"Execution Time":0.303}]
Забелешка: Овде planner не мора секогаш да избере индекс поради мал број редови. На страната „Напредни извештаи“ има EXPLAIN (FORCED) JSON каде се гледа "Index Scan" со имињата idx_event_date_time, idx_class_date_time, idx_ubc_class.
Како се тестира
Стартуваме backend:
cd backend node index.js
Примери за тест:
Login (JWT):
curl -X POST http://localhost:5000/api/login ^ -H "Content-Type: application/json" ^ -d "{"type":"user","email":"newstudent@gmail.com ","password":"Newstudent!15"}"
Book class (со JWT):
curl -X POST http://localhost:5000/api/book-class ^ -H "Content-Type: application/json" ^ -H "Authorization: Bearer <JWT>" ^ -d "{"classId":7}"
Book class (без JWT):
curl -X POST http://localhost:5000/api/book-class ^ -H "Content-Type: application/json" ^ -d "{"classId":7}"
Reports (во прелистувач):
http://localhost:5000/api/reports/top-spenders
http://localhost:5000/api/reports/class-utilization
http://localhost:5000/api/reports/training-pop-monthly
EXPLAIN (во прелистувач):