Changes between Version 9 and Version 10 of RelationalModel
- Timestamp:
- 04/25/26 19:52:37 (7 days ago)
Legend:
- Unmodified
- Added
- Removed
- Modified
-
RelationalModel
v9 v10 1 #Релационен модел1 = Релационен модел 2 2 3 ##ЕР дијаграм3 == ЕР дијаграм 4 4 5  5 [[Image(RelationalModel-drive-net.svg,1400px)]] 6 6 7 ##Архитектура и главни концепти7 == Архитектура и главни концепти 8 8 9 ###Користачи и нивни улоги9 === Користачи и нивни улоги 10 10 11 Табелите `users`, `drivers` и `passengers` функционираат по принцип на инхеритација. Базната табела `users` содржи основни информации за сите во системот, додека `drivers` и `passengers` се специјализирани и содржат само релевантни атрибути за та улога. Јавно еден кориснички профил може да има и две улоги истовремено — да е и возач и патник.11 Основна е поделбата меѓу `users` (сите во системот), `drivers` (возачи) и `passengers` (патници). Табелата `users` е како "мајка" — содржи основни информации (име, телефон, емајл). Потоа `drivers` и `passengers` додаваат своите специфични полиња. Една иста личност може да биде и возач и патник — нема никаква забрана за тоа. 12 12 13 ### Возила и нивна класификација 13 === Возила и производители 14 14 15 Возилата се организирани во три нивоа: `manufacturers` (производител), `vehicle_models` (модел) и `vehicles` (конкретно возило). Овој пристап спречува дупликување на податоци. Врската помеѓу возачите и возилата е моделирана преку табелата `vehicle_ownership`, која чува целосна историја на сопственост. Кога некој во возачот промени возило, новиот запис се додава, а старите се задржуваат — никогаш не бришеме историја.15 Возилата имаат три нивоа организирање. Прво `manufacturers` (Volkswagen, Mercedes, итн.), потоа `vehicle_models` (Golf, C-Class), потоа самите `vehicles` (твоја лична кола). Вакво расчленување спречува да пишувале "Volkswagen" стоди пати во базата. 16 16 17 ### Рути и возења 17 Возачот има много возила, возило мора да припаѓа на возач. За тоа го имаме `vehicle_ownership` — табела која чува каде возачот имал кола. Кога возачот купи ново возило, едноставно ј додаваме нов ред во `vehicle_ownership`. Старото возило останува во историјата, не го бришеме. Вака видиме точно кој возач кога имал кола. 18 18 19 Табелата `routes` е шаблон — претставува дефинирана патека со нејзиниот редослед на застанувања. Табелата `rides` пак претставува конкретно возење кое возачот го организира врз база на таа рута — сакај определен датум, час и возило. 19 === Рути и возења 20 20 21 Застанувањата на рутата се чувуваат во `route_stops` со редоследен број, што значи рутата може да има колку сакаш застанувања. Истата локација може да се појави повеќе пати (на пример, круговна рута). 21 `Routes` е шаблон — замисли рута "Скопје → Куманово → Пријепа" дефинирана веќе во системот. `Rides` е конкретно возење: „Во понеделник во 08:00 возачот Марко ја возеше оваа рута со Volkswagen Golf". 22 22 23 ### Сегментирање на возења и наплата 23 Застанувањата на рутата се во `route_stops` со редоследни броеви (1, 2, 3...). Рутата може да има 2 застанувања или 10 застанувања. Исто така, на иста рута можеш да направиш и круг (почеток и крај иста локација). 24 24 25 За прецизна пресметка на цена, возењата се делат на сегменти: 25 === Сегменти — како се делат возењата 26 26 27 - **`route_segments`** — ја дели рутата на делови помеѓу два последователни застанувања 28 - **`passenger_segments`** — чува колку патници биле на секој сегмент и колку треба да плати секој од нив 27 Возењето од Скопје до Пријепа преку Куманово не е "целина" во пресметките. Поделено е на сегменти: 28 - Скопје → Куманово 29 - Куманово → Пријепа 29 30 30 Финалната сума за секоја резервација се чува во `booking_final_fare`.31 Патниците не мора да патуваат целата рута. Еден патник можеш да влезе во Куманово и да излезе во Пријепа. За тоа `route_segments` дели рутата на делови меѓу два стопа, а `passenger_segments` чува колку патници беа на секој сегмент. Вака точно можеме да изразунаме колку требало да плати секој патник според каде вле и каде слезе. 31 32 32 ### Условни полиња (nullable foreign keys) 33 === Патарини 33 34 34 Неколку табели имаат nullable foreign keys, а нивното значење зависи од контекст: 35 Не можеме едноставно да кажеме "возење чини X денари". Некои возења минуваат наплатни рампи. За тоа имаме: 36 - `toll_points` — каде се наплатните рампи и колку чинат по тип возило 37 - `ride_tolls` — евидентира дека возењето Х минало низ рампата Y 38 - `toll_passenger_split` — раздела на патарината меѓу патниците кои беа присутни 35 39 36 - **`bookings.pickup_stop_id` и `bookings.dropoff_stop_id`** — го дефинираат сегментот што го резервира патникот. Ако патникот патува на целата рута, тие се почетниот и крајниот стоп. 37 - **`rides.recurrence_days` и `rides.recurrence_end_date`** — се пополнуваат само кога `is_recurring = TRUE`. За еднократни возења, овие полиња се NULL. 40 === Готовина, не картички 38 41 39 ### Система за патарини 42 Апликацијата е готовина-само. Нема онлајн плаќања, нема дигитални портфели. Возачот собира готовина од патниците. Табелите `fare_splits` и `booking_final_fare` служат само да покажат возачу колку собира од кого — ништо повеќе. 40 43 41 Три табели работат заедно за управување со патаринските рампи: 44 === Оценувања 42 45 43 - **`toll_points`** — наплатни рампи со цена по тип возило 44 - **`ride_tolls`** — евидентира кои рампи се поминати во одредено возење 45 - **`toll_passenger_split`** — ја дели цената рамномерно меѓу патниците кои биле присутни на тој делот 46 Патниците оценуваат возачи, возачи оценуваат патници. Табелата `ratings` чува сите оценувања. Има CHECK constraint кој вели: "Човек не може да го оцени самиот себеси". За просечната оцена не пишуваме `avg_rating` поле директно. Наместо тоа, правиме view-и `driver_ratings` и `passenger_ratings` кои пресметуваат просек на лету. Вака ако додадеш нова оценка, просекот се ажурира веднаш без дополнителна логика. 46 47 47 ### Готовинско плаќање 48 === История и следливост 48 49 49 Апликацијата работи исклучиво со готовина, така што **нема табела за онлајн трансакции**. Табелите `fare_splits` и `booking_final_fare` служат како референцијални алатки — возачот ги користи за да знае колку да собере од секој патник, исё.50 Кога возење промени статус (scheduled → in_progress → completed), промената оди во `ride_status_history` со временска марка. Исто и резервациите во `booking_status_history`. Вака администраторот има целосна историја — кога точно се случи што. 50 51 51 ### Оценување (Рејтинги) 52 === Бришење на податоци 52 53 53 Оценувањето функционира преку табелата `ratings` со CHECK constraint кој спречува самооценување. Оценката на возач/патник не се чува како просто поле, туку се пресметува динамички преку `driver_ratings` и `passenger_ratings` views за да останува во синхрона.54 Претпоставка: администраторот избрише користачки профил. Но возачот има 50 завршени возења! Ние не ги бришеме поврзаните возења — користиме `ON DELETE RESTRICT`. За статистика, користиме резервиран корисник со `id = 0` наречен "избришан корисник". Вака можеме да видиме "некој возач" направил возење, дури и кога профилот е избришан. 54 55 55 ### Историја и следливост 56 == Прегледи (Views) 56 57 57 Две табели чуваат целосна историја на промени: 58 Сите овие табели се напалени со JOIN-ови. Наместо апликацијата да пишува `SELECT ... FROM rides JOIN drivers JOIN vehicles ...` низ целиот код, ми направивме 9 views. Секој view е спремена SQL "забелешка" која делува како табела. 58 59 59 - **`ride_status_history`** — сите промени на статусот на возење 60 - **`booking_status_history`** — сите промени на статусот на резервација 60 === v_available_rides — Возења отворени за резервирање 61 61 62 Секоја промена има временска марка, што овозможува целосна следливост. 62 Ова е она што патникот вижи кога пребарува возење. Показува: 63 - Кога поаѓа возењето 64 - Каде почнува, каде завршува 65 - Име на возач 66 - Марка и модел на возило 67 - Проценета цена 68 - Просечна оцена на возачот 63 69 64 ### Бришење на податоци 70 VIEW вика JOIN меѓу `rides`, `drivers`, `vehicles`, `vehicle_models`, `manufacturers`, `routes`, `route_stops`, `ratings` и пресметува просечна оцена на лету. Филтрира само возења со статус `scheduled`. 65 71 66 За да се задржи статистика и обврски дури и кога кориснички профили се избришат, сите foreign keys кон `users` користат `ON DELETE SET NULL` или се поврзуваат со резервиран корисник со `id = 0` ("избришан кориснік"). Foreign keys кон критични табели (`rides`, `routes`, `bookings`) користат `ON DELETE RESTRICT` — не смее ништо да се избрише ако е поврзано. 72 Апликацијата го користи вака: `SELECT * FROM v_available_rides WHERE origin_city = 'Скопје' AND departure_time >= NOW()`. Едноставно! 67 73 68 --- 74 === v_driver_profile — Профилот на возачот 69 75 70 ## Прегледи (Views) 76 Возачот кога си ажурира профилот, вижи ова. Исто и патникот пред да резервира место ако сака да види со кого ќе возеше. 71 77 72 Базата содржи девет прегледи кои го поедностављуваат најчестиот код во апликацијата. 78 Содржи: 79 - Лични податоци (име, телефон) 80 - Информации за возачка дозвола (број, датум на издавање) 81 - Примарното возило на возачот (ако има повеќе, земаме оно со најмал ID) 82 - Статистика: вкупно возења, завршени, откажани 83 - Просечна оцена 73 84 74 ### `v_available_rides` 85 VIEW прави JOIN меѓу `drivers`, `users`, `driver_licenses`, `vehicles`, `vehicle_ownership`, `rides` и `ratings`. Пресметува број на возења по статус и просек на оценувања. 75 86 76 Ги прикажува сите возења со статус `scheduled` кои се отворени за резервирање. За секое возење ја обединува информацијата за возачот, возилото, марката, моделот, рутата и градовите, плус пресметана проценета цена врз база на должина на рута и цена по км. Просечната оцена на возачот е веќе вклучена. 87 === v_passenger_trip_history — Моја патна историја 77 88 78 Апликацијата го користи при пребарување — просто добива WHERE за град на почеток, час, итн. 89 Патникот кога кликне на "Мои патувања" вижи: 90 - Кога беше возењето 91 - Каде почнало, каде завршило 92 - Име на возач 93 - Потврда дека је качен (pickup_confirmed_at) 94 - Потврда дека је слезен (dropoff_confirmed_at) 95 - Колку платил 96 - Неговата оценка за возачот (ако ја дал) 79 97 80 ### `v_driver_profile` 98 Секоја резервација е редок во view-ot. Апликацијата може лесно да филтрира по статус (`completed`, `canceled`) или да го пагинира. 81 99 82 Комплетна слика на возачкиот профил — лични податоци, возачка дозвола, примарното возило, укупно/завршено/откажано возења и просечна оцена. Koristi ga возачот при ажурирање на своја страна и патникот при проверка со кого се сокува пред резервирање. 100 === v_ride_manifest — Список на патници за возење 83 101 84 ### `v_passenger_trip_history` 102 Возачот одлучи да поаѓа во 08:00. Пред да ја запали машината, отворува ја оваа листа за да вижи: 103 - Кои патници се качуваат 104 - Имиња и телефонски броеви 105 - Каде секој вле (punkt_A_stop) 106 - Каде секој слезе (punkt_B_stop) 107 - Колку собира од секој 85 108 86 Целосна историја на патувања за секој патник — резервации со статус, име на возач, почеток/крај, потврди за качување/слегување, финална цена и оценка. Секоја резервација е редок (не групиран), што го едностави филтерирањето и пагинирањето во апликацијата.109 Прикажува само резервации со статус `confirmed`, `picked_up` или `completed`. Откажаните резервации не се прикажуваат — нема смисла да ги вижи. 87 110 88 ### `v_ride_manifest` 111 === v_booking_details — Целосен детај на резервација 89 112 90 Список на сите потврдени, активни и завршени патници за одредено возење — имиња, телефони, места на качување/слегување, финална цена. Возачот го гледа пред поаѓање за да знае кого чека, каде да застане и колку да наплати. 113 Патникот кликна на една резервација. Вижи "сё на едно место": 114 - Кој е возачот (име, телефон, оцена) 115 - Какво возило (марка, модел, регистарски табички) 116 - Точна локација за качување (Централна станица, Скопје) 117 - Точна локација за слегување (Куманово центар) 118 - Точен час на поаѓање 119 - Финална цена 120 - Статус на резервација 91 121 92 ### `v_driver_earnings` 122 Содржи толку многу JOIN-ови што апликацијата СЕКОГАШ го вика со филтер: `WHERE booking_id = 123`. Никогаш не го вика без филтер — би ја спалил базата од JOIN-ови! 93 123 94 Месечен преглед на приходите по возач. За секој месец прикажува број возења, број наплатени резервации, вкупен приход, просек и min/max по резервација. Корисно е и за возачот (лична финансиска контрола) и за администраторот (анализа на активност). 124 === v_unread_notifications — Мои нови нотификации 95 125 96 ### `v_route_popularity` 126 Патникот или возачот отворува го "звончето". Вижи само непрочитаните нотификации: 127 - Каде е написано нотификацијата (текст) 128 - Од кого (некој возач, администратор, итн.) 129 - Кога (точна време или "пред 5 минути") 97 130 98 Ги рангира рутите по број на резервации. Прикажува просечна цена по резервација и просечна цена по км. Служи за препораки на почетниот екран и за анализа — ако рута има многу возења но малку резервации, сигнал е дека нешто не е окај (премногу скапо, лоша достапност).131 VIEW-ot пресметува "старост во минути" за да покаже "пред 5 мин" без дополнителна логика во апликацијата. Апликацијата го вика со `WHERE user_id = 42 AND read_at IS NULL`. 99 132 100 ### `v_booking_details` 133 === v_driver_earnings — Мои приходи во месец 101 134 102 Целосна слика на една резервација — патник, возач, возило со регистарски табички, места (со имиња на локации и градови), време поаѓање, статус, финална цена. Користи се при потврда и при генерирање на сметка. Апликацијата секогаш го вика со `WHERE booking_id = ?` — никогаш без филтер. 135 Возачот кликне "Финансиски извештај". Вижи: 136 - За секој месец колку возења направил 137 - Колку резервации наплатил (можеби некој отказал во последниот момент) 138 - Вкупен приход 139 - Просечен приход по резервација 140 - Максимален приход 141 - Минимален приход 103 142 104 ### `v_unread_notifications` 143 Администраторот го користи и за анализа — ако возачот нема приход три месеца, веројатно е неактивен. 105 144 106 Само непрочитаните нотификации (каде `read_at IS NULL`), збогатени со име на корисник и „старост во минути" за лесна приказ (нпр. „пред 5 минути"). Апликацијата го вика со `WHERE user_id = ?` за конкретниот логиран корисник.145 Пресметува на основа на `bookings` со статус `completed` (само завршени) и нивните финални суми. 107 146 108 ### `v_incident_summary` 147 === v_route_popularity — Кои рути се најпопуларни 109 148 110 Административен преглед на пријавени инциденти со целосен контекст — тип, опис, време пријава, лични податоци на пријавувач и возач (вклучувајќи возачка дозвола) и рутата. Наменет исклучиво за администраторот. Прикажува инциденти без оглед на статус на возење, бидејќи инцидент може да се пријави и по завршено возење. 149 Апликацијата на почетниот екран прикажува популарни рути за да го вдахновува патникот. Ова view-ot ги рангира: 150 - По број на вкупни резервации 151 - Просечна цена по резервација 152 - Просечна цена по километар 153 154 Служи и како сигнал за возачите. Ако рутата Скопје↔Куманово има 50 резервации месечно, но возачот направил 10 возења и не продал ништо — сигнал е дека нешто не е окај. Можеби е превиско скапо, или нема добро рекламирано возење. 155 156 === v_incident_summary — Безбедносни инциденти 157 158 Само администраторот видува ова. Прикажува: 159 - Тип на инцидент (невежливост, несреќа, кршење на правила) 160 - Опис на инцидентот 161 - Кога е пријавен 162 - Лични податоци на пријавувачот (патник или возач) 163 - Лични податоци на другата страна 164 - Возачка дозвола број 165 - Рутата на возењето 166 167 Инцидентите могат да будат пријавени дури и после завршено возење, така да view-ot не филтрира по статус на возење. 168 169 == Важно: NULL вредности со логика 170 171 Неколку полиња се NULL базирано на логика: 172 173 * `rides.recurrence_days` и `rides.recurrence_end_date` — се пополнуваат САМО ако `is_recurring = TRUE`. За еднократни возења се NULL. 174 175 * `bookings.pickup_stop_id` и `bookings.dropoff_stop_id` — ако патникот резервира целата рута, стоповите се почеток и крај. Ако резервира дел, се полнат конкретни стопови. 176 177 Апликацијата треба да проверува ова пред да користи вредностите.
