| 1 | --proceduri:
|
|---|
| 2 |
|
|---|
| 3 | --Kiko:
|
|---|
| 4 | --1-Регистрација на нов корисник со почетна претплата
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 | CREATE OR REPLACE PROCEDURE sp_register_user(
|
|---|
| 8 | p_first_name VARCHAR,
|
|---|
| 9 | p_last_name VARCHAR,
|
|---|
| 10 | p_username VARCHAR,
|
|---|
| 11 | p_email VARCHAR,
|
|---|
| 12 | p_password VARCHAR,
|
|---|
| 13 | p_subscription_id INT
|
|---|
| 14 | )
|
|---|
| 15 | LANGUAGE plpgsql AS $$
|
|---|
| 16 | DECLARE
|
|---|
| 17 | v_user_id INT;
|
|---|
| 18 | BEGIN
|
|---|
| 19 | -- Vmetni nov korisnik
|
|---|
| 20 | INSERT INTO "User" (FirstName, LastName, Username, Email, password, Date_registered)
|
|---|
| 21 | VALUES (p_first_name, p_last_name, p_username, p_email, p_password, CURRENT_DATE)
|
|---|
| 22 | RETURNING UserID INTO v_user_id;
|
|---|
| 23 |
|
|---|
| 24 | -- Kreira User_Subscription zapis za noviot korisnik
|
|---|
| 25 | INSERT INTO User_Subscription (UserUserID, SubscriptionSubscriptionID, Start_date, End_date, Status, Auto_renew)
|
|---|
| 26 | VALUES (v_user_id, p_subscription_id, CURRENT_DATE, CURRENT_DATE + INTERVAL '30 days', 'Active', 1);
|
|---|
| 27 |
|
|---|
| 28 | RAISE NOTICE 'Korisnikot % % uspeshno registriran so UserID = %', p_first_name, p_last_name, v_user_id;
|
|---|
| 29 |
|
|---|
| 30 | EXCEPTION
|
|---|
| 31 | WHEN unique_violation THEN
|
|---|
| 32 | RAISE EXCEPTION 'Email % veke postoi vo sistemot.', p_email;
|
|---|
| 33 | WHEN foreign_key_violation THEN
|
|---|
| 34 | RAISE EXCEPTION 'Subscription so ID % ne postoi.', p_subscription_id;
|
|---|
| 35 | END;
|
|---|
| 36 | $$;
|
|---|
| 37 |
|
|---|
| 38 | --Зошто би постоела во реален свет:
|
|---|
| 39 | --На Netflix/Spotify кога се регистрираш, тоа не е само еден INSERT — треба да се креира корисник, да му се додели план, да се постави почетен датум. Без процедура, фронтендот би морал да праќа 2 одделни барања, со ризик едното да успее а другото да не — процедурата го прави тоа атомски (или се, или ништо).
|
|---|
| 40 |
|
|---|
| 41 | --2-Додавање содржина во watchlist со проверка за дупликати
|
|---|
| 42 |
|
|---|
| 43 | CREATE OR REPLACE PROCEDURE sp_add_to_watchlist(
|
|---|
| 44 | p_user_id INT,
|
|---|
| 45 | p_content_id INT
|
|---|
| 46 | )
|
|---|
| 47 | LANGUAGE plpgsql AS $$
|
|---|
| 48 | DECLARE
|
|---|
| 49 | v_exists INT;
|
|---|
| 50 | BEGIN
|
|---|
| 51 | -- Proverka dali veke postoi vo watchlist-ot
|
|---|
| 52 | SELECT COUNT(*) INTO v_exists
|
|---|
| 53 | FROM Watchlist
|
|---|
| 54 | WHERE UserUserID = p_user_id AND ContentContentID = p_content_id;
|
|---|
| 55 |
|
|---|
| 56 | IF v_exists > 0 THEN
|
|---|
| 57 | RAISE NOTICE 'Sodrzhinata veke e vo watchlist-ot na korisnikot.';
|
|---|
| 58 | RETURN;
|
|---|
| 59 | END IF;
|
|---|
| 60 |
|
|---|
| 61 | -- Proverka dali sodrzhinata postoi vo Media
|
|---|
| 62 | IF NOT EXISTS (SELECT 1 FROM Media WHERE ContentID = p_content_id) THEN
|
|---|
| 63 | RAISE EXCEPTION 'Sodrzhinata so ID % ne postoi.', p_content_id;
|
|---|
| 64 | END IF;
|
|---|
| 65 |
|
|---|
| 66 | INSERT INTO Watchlist (UserUserID, ContentContentID, dateAdded)
|
|---|
| 67 | VALUES (p_user_id, p_content_id, CURRENT_DATE);
|
|---|
| 68 |
|
|---|
| 69 | RAISE NOTICE 'Sodrzhinata % dodadena vo watchlist za korisnik %.', p_content_id, p_user_id;
|
|---|
| 70 |
|
|---|
| 71 | EXCEPTION
|
|---|
| 72 | WHEN foreign_key_violation THEN
|
|---|
| 73 | RAISE EXCEPTION 'Korisnik % ili sodrzina % ne postoi.', p_user_id, p_content_id;
|
|---|
| 74 | END;
|
|---|
| 75 | $$;
|
|---|
| 76 |
|
|---|
| 77 | --Зошто би постоела во реален свет:
|
|---|
| 78 | --Кога кликаш "Add to My List" на Netflix, апликацијата не знае дали веќе го имаш додадено — процедурата тоа го решава на ниво на база, без фронтендот да прави extra SELECT прво.
|
|---|
| 79 |
|
|---|
| 80 | --3- Промена на план на претплата за постоечки корисник
|
|---|
| 81 |
|
|---|
| 82 | CREATE OR REPLACE PROCEDURE sp_record_watch(
|
|---|
| 83 | p_user_id INT,
|
|---|
| 84 | p_content_id INT,
|
|---|
| 85 | p_watchable_id INT,
|
|---|
| 86 | p_device_id INT,
|
|---|
| 87 | p_progress INT
|
|---|
| 88 | )
|
|---|
| 89 | LANGUAGE plpgsql AS $$
|
|---|
| 90 | BEGIN
|
|---|
| 91 | -- Proverka za progress range
|
|---|
| 92 | IF p_progress < 0 OR p_progress > 100 THEN
|
|---|
| 93 | RAISE EXCEPTION 'Progress mora da bide pomegu 0 i 100.';
|
|---|
| 94 | END IF;
|
|---|
| 95 |
|
|---|
| 96 | -- Vmetni vo WatchHistory (trigerot ke go handle 90%+ -> 100%)
|
|---|
| 97 | INSERT INTO WatchHistory (WatchedAt, Progress_percentage, UserUserID, ContentContentID, WatchableWatchableID, DevicesDeviceID)
|
|---|
| 98 | VALUES (CURRENT_DATE, p_progress, p_user_id, p_content_id, p_watchable_id, p_device_id);
|
|---|
| 99 |
|
|---|
| 100 | -- Ako e zavrseno gledanjeto i nema reting, isprati NOTICE (vo realna app = notification za rating)
|
|---|
| 101 | IF p_progress >= 90 THEN
|
|---|
| 102 | IF NOT EXISTS (
|
|---|
| 103 | SELECT 1 FROM Rating
|
|---|
| 104 | WHERE UserUserID = p_user_id AND ContentContentID = p_content_id
|
|---|
| 105 | ) THEN
|
|---|
| 106 | RAISE NOTICE 'Korisnik % ja zavrsil sodrzhinata %. Pokanete go da ostavi rejting.', p_user_id, p_content_id;
|
|---|
| 107 | END IF;
|
|---|
| 108 | END IF;
|
|---|
| 109 |
|
|---|
| 110 | EXCEPTION
|
|---|
| 111 | WHEN foreign_key_violation THEN
|
|---|
| 112 | RAISE EXCEPTION 'Nevaliden user, content, watchable ili device ID.';
|
|---|
| 113 | END;
|
|---|
| 114 | $$;
|
|---|
| 115 |
|
|---|
| 116 | --Зошто би постоела во реален свет:
|
|---|
| 117 | --Кога корисник на HBO Max upgrade-ува од Basic на Premium, треба да се затвори стариот план и да се отвори нов — а вашиот тригер trigger_log_subscription_status_change автоматски ќе го логира тоа. Процедурата + триgerот работат заедно совршено.
|
|---|
| 118 |
|
|---|
| 119 | --Damjan:
|
|---|
| 120 |
|
|---|
| 121 | --1-Снимање на гледање + автоматски рејтинг промпт логика
|
|---|
| 122 | CREATE OR REPLACE PROCEDURE sp_record_watch(
|
|---|
| 123 | p_user_id INT,
|
|---|
| 124 | p_content_id INT,
|
|---|
| 125 | p_watchable_id INT,
|
|---|
| 126 | p_device_id INT,
|
|---|
| 127 | p_progress INT
|
|---|
| 128 | )
|
|---|
| 129 | LANGUAGE plpgsql AS $$
|
|---|
| 130 | BEGIN
|
|---|
| 131 | -- Proverka za progress range
|
|---|
| 132 | IF p_progress < 0 OR p_progress > 100 THEN
|
|---|
| 133 | RAISE EXCEPTION 'Progress mora da bide pomegu 0 i 100.';
|
|---|
| 134 | END IF;
|
|---|
| 135 |
|
|---|
| 136 | -- Vmetni vo WatchHistory (trigerot ke go handle 90%+ -> 100%)
|
|---|
| 137 | INSERT INTO WatchHistory (WatchedAt, Progress_percentage, UserUserID, ContentContentID, WatchableWatchableID, DevicesDeviceID)
|
|---|
| 138 | VALUES (CURRENT_DATE, p_progress, p_user_id, p_content_id, p_watchable_id, p_device_id);
|
|---|
| 139 |
|
|---|
| 140 | -- Ako e zavrseno gledanjeto i nema reting, isprati NOTICE (vo realna app = notification za rating)
|
|---|
| 141 | IF p_progress >= 90 THEN
|
|---|
| 142 | IF NOT EXISTS (
|
|---|
| 143 | SELECT 1 FROM Rating
|
|---|
| 144 | WHERE UserUserID = p_user_id AND ContentContentID = p_content_id
|
|---|
| 145 | ) THEN
|
|---|
| 146 | RAISE NOTICE 'Korisnik % ja zavrsil sodrzhinata %. Pokanete go da ostavi rejting.', p_user_id, p_content_id;
|
|---|
| 147 | END IF;
|
|---|
| 148 | END IF;
|
|---|
| 149 |
|
|---|
| 150 | EXCEPTION
|
|---|
| 151 | WHEN foreign_key_violation THEN
|
|---|
| 152 | RAISE EXCEPTION 'Nevaliden user, content, watchable ili device ID.';
|
|---|
| 153 | END;
|
|---|
| 154 | $$;
|
|---|
| 155 |
|
|---|
| 156 | --Зошто би постоела во реален свет:
|
|---|
| 157 | --Секој пат кога паузираш или завршиш нешто на стриминг сервис, апликацијата снима прогрес. Процедурата работи со постоечкиот тригер trigger_auto_complete_progress (90%→100%) и додава логика за рејтинг нотификација — точно kako "How would you rate Stranger Things?" pop-up по завршување.
|
|---|
| 158 |
|
|---|
| 159 | --2-Поднесување или ажурирање на рејтинг за содржина
|
|---|
| 160 |
|
|---|
| 161 | CREATE OR REPLACE PROCEDURE sp_submit_rating(
|
|---|
| 162 | p_user_id INT,
|
|---|
| 163 | p_content_id INT,
|
|---|
| 164 | p_rating INT
|
|---|
| 165 | )
|
|---|
| 166 | LANGUAGE plpgsql AS $$
|
|---|
| 167 | DECLARE
|
|---|
| 168 | v_watched INT;
|
|---|
| 169 | BEGIN
|
|---|
| 170 | -- Proverka dali korisnikot vosopsto ja gledal sodrzhinata
|
|---|
| 171 | SELECT COUNT(*) INTO v_watched
|
|---|
| 172 | FROM WatchHistory
|
|---|
| 173 | WHERE UserUserID = p_user_id AND ContentContentID = p_content_id;
|
|---|
| 174 |
|
|---|
| 175 | IF v_watched = 0 THEN
|
|---|
| 176 | RAISE EXCEPTION 'Korisnikot ne ja gledal sodrzhinata i ne moze da ostavi rejting.';
|
|---|
| 177 | END IF;
|
|---|
| 178 |
|
|---|
| 179 | -- Ako veke postoi rejting, UPDATE; inaku INSERT
|
|---|
| 180 | IF EXISTS (SELECT 1 FROM Rating WHERE UserUserID = p_user_id AND ContentContentID = p_content_id) THEN
|
|---|
| 181 | UPDATE Rating
|
|---|
| 182 | SET RatingValue = p_rating, Rating_Date = CURRENT_DATE
|
|---|
| 183 | WHERE UserUserID = p_user_id AND ContentContentID = p_content_id;
|
|---|
| 184 |
|
|---|
| 185 | RAISE NOTICE 'Rejtingot azhurirani na % za sodrzina %.', p_rating, p_content_id;
|
|---|
| 186 | ELSE
|
|---|
| 187 | INSERT INTO Rating (Rating_Date, RatingValue, UserUserID, ContentContentID)
|
|---|
| 188 | VALUES (CURRENT_DATE, p_rating, p_user_id, p_content_id);
|
|---|
| 189 |
|
|---|
| 190 | RAISE NOTICE 'Nov rejting % vnesen za sodrzina %.', p_rating, p_content_id;
|
|---|
| 191 | END IF;
|
|---|
| 192 |
|
|---|
| 193 | -- Trigerot trigger_validate_rating ke go proveri rangot avtomatski
|
|---|
| 194 |
|
|---|
| 195 | EXCEPTION
|
|---|
| 196 | WHEN check_violation THEN
|
|---|
| 197 | RAISE EXCEPTION 'Rejtingot mora da bide pomegu 1 i 5.';
|
|---|
| 198 | END;
|
|---|
| 199 | $$;
|
|---|
| 200 |
|
|---|
| 201 | --Зошто би постоела во реален свет:
|
|---|
| 202 | --IMDB/Netflix не дозволуваат рејтинг ако не си гледал — процедурата го enforc-ува тоа. Исто така работи со постоечкиот тригер trigger_validate_rating за валидација на вредноста, и со вашиот индекс idx_rating_content за брзо пребарување.
|
|---|
| 203 |
|
|---|
| 204 | --3-Деактивирање на корисник — Cancel претплата + чистење на уреди
|
|---|
| 205 | CREATE OR REPLACE PROCEDURE sp_deactivate_user(
|
|---|
| 206 | p_user_id INT
|
|---|
| 207 | )
|
|---|
| 208 | LANGUAGE plpgsql AS $$
|
|---|
| 209 | DECLARE
|
|---|
| 210 | v_count INT;
|
|---|
| 211 | BEGIN
|
|---|
| 212 | -- Proverka dali korisnikot postoi
|
|---|
| 213 | IF NOT EXISTS (SELECT 1 FROM "User" WHERE UserID = p_user_id) THEN
|
|---|
| 214 | RAISE EXCEPTION 'Korisnik so ID % ne postoi.', p_user_id;
|
|---|
| 215 | END IF;
|
|---|
| 216 |
|
|---|
| 217 | -- Proverka kolku aktivni pretplati ima
|
|---|
| 218 | SELECT COUNT(*) INTO v_count
|
|---|
| 219 | FROM User_Subscription
|
|---|
| 220 | WHERE UserUserID = p_user_id AND Status = 'Active';
|
|---|
| 221 |
|
|---|
| 222 | -- Cancel site aktivni pretplati (trigerot ke gi logira site promeni)
|
|---|
| 223 | UPDATE User_Subscription
|
|---|
| 224 | SET Status = 'Cancelled', End_date = CURRENT_DATE
|
|---|
| 225 | WHERE UserUserID = p_user_id AND Status = 'Active';
|
|---|
| 226 |
|
|---|
| 227 | -- Izbrishi gi site uredi povrzani so negovite pretplati
|
|---|
| 228 | DELETE FROM Devices
|
|---|
| 229 | WHERE UserSubscriptionID IN (
|
|---|
| 230 | SELECT UserSubscriptionID FROM User_Subscription
|
|---|
| 231 | WHERE UserUserID = p_user_id
|
|---|
| 232 | );
|
|---|
| 233 |
|
|---|
| 234 | RAISE NOTICE 'Korisnik % deaktiviran. % pretplati otkazani, uredite izbrishani.', p_user_id, v_count;
|
|---|
| 235 |
|
|---|
| 236 | -- Zabeleska: DELETE od "User" ke go aktivira trigger_log_user_delete avtomatski
|
|---|
| 237 |
|
|---|
| 238 | EXCEPTION
|
|---|
| 239 | WHEN OTHERS THEN
|
|---|
| 240 | RAISE EXCEPTION 'Greshka pri deaktiviranje na korisnik %: %', p_user_id, SQLERRM;
|
|---|
| 241 | END;
|
|---|
| 242 | $$;
|
|---|
| 243 |
|
|---|
| 244 | --Зошто би постоела во реален свет:
|
|---|
| 245 | --"Cancel my account" на стриминг сервис не брише само еден ред — треба да се откажат претплати, да се отстранат уреди, да се логира се. Процедурата го координира сето тоа, а вашите два постоечки тригери (trigger_log_user_delete и trigger_log_subscription_status_change) автоматски снимаат сe во log табелите.
|
|---|
| 246 |
|
|---|
| 247 |
|
|---|
| 248 |
|
|---|
| 249 |
|
|---|
| 250 |
|
|---|