Changes between Version 9 and Version 10 of RelationalModel


Ignore:
Timestamp:
04/25/26 19:52:37 (7 days ago)
Author:
231138
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • RelationalModel

    v9 v10  
    1 # Релационен модел
     1= Релационен модел
    22
    3 ## ЕР дијаграм
     3== ЕР дијаграм
    44
    5 ![Релационен модел DriveNet](RelationalModel-drive-net.svg)
     5[[Image(RelationalModel-drive-net.svg,1400px)]]
    66
    7 ## Архитектура и главни концепти
     7== Архитектура и главни концепти
    88
    9 ### Користачи и нивни улоги
     9=== Користачи и нивни улоги
    1010
    11 Табелите `users`, `drivers` и `passengers` функционираат по принцип на инхеритација. Базната табела `users` содржи основни информации за сите во системот, додека `drivers` и `passengers` се специјализирани и содржат само релевантни атрибути за та улога. Јавно еден кориснички профил може да има и две улоги истовремено — да е и возач и патник.
     11Основна е поделбата меѓу `users` (сите во системот), `drivers` (возачи) и `passengers` (патници). Табелата `users` е како "мајка" — содржи основни информации (име, телефон, емајл). Потоа `drivers` и `passengers` додаваат своите специфични полиња. Една иста личност може да биде и возач и патник — нема никаква забрана за тоа.
    1212
    13 ### Возила и нивна класификација
     13=== Возила и производители
    1414
    15 Возилата се организирани во три нивоа: `manufacturers` (производител), `vehicle_models` (модел) и `vehicles` (конкретно возило). Овој пристап спречува дупликување на податоци. Врската помеѓу возачите и возилата е моделирана преку табелата `vehicle_ownership`, која чува целосна историја на сопственост. Кога некој во возачот промени возило, новиот запис се додава, а старите се задржуваат — никогаш не бришеме историја.
     15Возилата имаат три нивоа организирање. Прво `manufacturers` (Volkswagen, Mercedes, итн.), потоа `vehicle_models` (Golf, C-Class), потоа самите `vehicles` (твоја лична кола). Вакво расчленување спречува да пишувале "Volkswagen" стоди пати во базата.
    1616
    17 ### Рути и возења
     17Возачот има много возила, возило мора да припаѓа на возач. За тоа го имаме `vehicle_ownership` — табела која чува каде возачот имал кола. Кога возачот купи ново возило, едноставно ј додаваме нов ред во `vehicle_ownership`. Старото возило останува во историјата, не го бришеме. Вака видиме точно кој возач кога имал кола.
    1818
    19 Табелата `routes` е шаблон — претставува дефинирана патека со нејзиниот редослед на застанувања. Табелата `rides` пак претставува конкретно возење кое возачот го организира врз база на таа рута — сакај определен датум, час и возило.
     19=== Рути и возења
    2020
    21 Застанувањата на рутата се чувуваат во `route_stops` со редоследен број, што значи рутата може да има колку сакаш застанувања. Истата локација може да се појави повеќе пати (на пример, круговна рута).
     21`Routes` е шаблон — замисли рута "Скопје → Куманово → Пријепа" дефинирана веќе во системот. `Rides` е конкретно возење: „Во понеделник во 08:00 возачот Марко ја возеше оваа рута со Volkswagen Golf".
    2222
    23 ### Сегментирање на возења и наплата
     23Застанувањата на рутата се во `route_stops` со редоследни броеви (1, 2, 3...). Рутата може да има 2 застанувања или 10 застанувања. Исто така, на иста рута можеш да направиш и круг (почеток и крај иста локација).
    2424
    25 За прецизна пресметка на цена, возењата се делат на сегменти:
     25=== Сегменти — како се делат возењата
    2626
    27 - **`route_segments`** — ја дели рутата на делови помеѓу два последователни застанувања
    28 - **`passenger_segments`** — чува колку патници биле на секој сегмент и колку треба да плати секој од нив
     27Возењето од Скопје до Пријепа преку Куманово не е "целина" во пресметките. Поделено е на сегменти:
     28- Скопје → Куманово
     29- Куманово → Пријепа
    2930
    30 Финалната сума за секоја резервација се чува во `booking_final_fare`.
     31Патниците не мора да патуваат целата рута. Еден патник можеш да влезе во Куманово и да излезе во Пријепа. За тоа `route_segments` дели рутата на делови меѓу два стопа, а `passenger_segments` чува колку патници беа на секој сегмент. Вака точно можеме да изразунаме колку требало да плати секој патник според каде вле и каде слезе.
    3132
    32 ### Условни полиња (nullable foreign keys)
     33=== Патарини
    3334
    34 Неколку табели имаат nullable foreign keys, а нивното значење зависи од контекст:
     35Не можеме едноставно да кажеме "возење чини X денари". Некои возења минуваат наплатни рампи. За тоа имаме:
     36- `toll_points` — каде се наплатните рампи и колку чинат по тип возило
     37- `ride_tolls` — евидентира дека возењето Х минало низ рампата Y
     38- `toll_passenger_split` — раздела на патарината меѓу патниците кои беа присутни
    3539
    36 - **`bookings.pickup_stop_id` и `bookings.dropoff_stop_id`** — го дефинираат сегментот што го резервира патникот. Ако патникот патува на целата рута, тие се почетниот и крајниот стоп.
    37 - **`rides.recurrence_days` и `rides.recurrence_end_date`** — се пополнуваат само кога `is_recurring = TRUE`. За еднократни возења, овие полиња се NULL.
     40=== Готовина, не картички
    3841
    39 ### Система за патарини
     42Апликацијата е готовина-само. Нема онлајн плаќања, нема дигитални портфели. Возачот собира готовина од патниците. Табелите `fare_splits` и `booking_final_fare` служат само да покажат возачу колку собира од кого — ништо повеќе.
    4043
    41 Три табели работат заедно за управување со патаринските рампи:
     44=== Оценувања
    4245
    43 - **`toll_points`** — наплатни рампи со цена по тип возило
    44 - **`ride_tolls`** — евидентира кои рампи се поминати во одредено возење
    45 - **`toll_passenger_split`** — ја дели цената рамномерно меѓу патниците кои биле присутни на тој делот
     46Патниците оценуваат возачи, возачи оценуваат патници. Табелата `ratings` чува сите оценувања. Има CHECK constraint кој вели: "Човек не може да го оцени самиот себеси". За просечната оцена не пишуваме `avg_rating` поле директно. Наместо тоа, правиме view-и `driver_ratings` и `passenger_ratings` кои пресметуваат просек на лету. Вака ако додадеш нова оценка, просекот се ажурира веднаш без дополнителна логика.
    4647
    47 ### Готовинско плаќање
     48=== История и следливост
    4849
    49 Апликацијата работи исклучиво со готовина, така што **нема табела за онлајн трансакции**. Табелите `fare_splits` и `booking_final_fare` служат како референцијални алатки — возачот ги користи за да знае колку да собере од секој патник, исё.
     50Кога возење промени статус (scheduled → in_progress → completed), промената оди во `ride_status_history` со временска марка. Исто и резервациите во `booking_status_history`. Вака администраторот има целосна историја — кога точно се случи што.
    5051
    51 ### Оценување (Рејтинги)
     52=== Бришење на податоци
    5253
    53 Оценувањето функционира преку табелата `ratings` со CHECK constraint кој спречува самооценување. Оценката на возач/патник не се чува како просто поле, туку се пресметува динамички преку `driver_ratings` и `passenger_ratings` views за да останува во синхрона.
     54Претпоставка: администраторот избрише користачки профил. Но возачот има 50 завршени возења! Ние не ги бришеме поврзаните возења — користиме `ON DELETE RESTRICT`. За статистика, користиме резервиран корисник со `id = 0` наречен "избришан корисник". Вака можеме да видиме "некој возач" направил возење, дури и кога профилот е избришан.
    5455
    55 ### Историја и следливост
     56== Прегледи (Views)
    5657
    57 Две табели чуваат целосна историја на промени:
     58Сите овие табели се напалени со JOIN-ови. Наместо апликацијата да пишува `SELECT ... FROM rides JOIN drivers JOIN vehicles ...` низ целиот код, ми направивме 9 views. Секој view е спремена SQL "забелешка" која делува како табела.
    5859
    59 - **`ride_status_history`** — сите промени на статусот на возење
    60 - **`booking_status_history`** — сите промени на статусот на резервација
     60=== v_available_rides — Возења отворени за резервирање
    6161
    62 Секоја промена има временска марка, што овозможува целосна следливост.
     62Ова е она што патникот вижи кога пребарува возење. Показува:
     63- Кога поаѓа возењето
     64- Каде почнува, каде завршува
     65- Име на возач
     66- Марка и модел на возило
     67- Проценета цена
     68- Просечна оцена на возачот
    6369
    64 ### Бришење на податоци
     70VIEW вика JOIN меѓу `rides`, `drivers`, `vehicles`, `vehicle_models`, `manufacturers`, `routes`, `route_stops`, `ratings` и пресметува просечна оцена на лету. Филтрира само возења со статус `scheduled`.
    6571
    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()`. Едноставно!
    6773
    68 ---
     74=== v_driver_profile — Профилот на возачот
    6975
    70 ## Прегледи (Views)
     76Возачот кога си ажурира профилот, вижи ова. Исто и патникот пред да резервира место ако сака да види со кого ќе возеше.
    7177
    72 Базата содржи девет прегледи кои го поедностављуваат најчестиот код во апликацијата.
     78Содржи:
     79- Лични податоци (име, телефон)
     80- Информации за возачка дозвола (број, датум на издавање)
     81- Примарното возило на возачот (ако има повеќе, земаме оно со најмал ID)
     82- Статистика: вкупно возења, завршени, откажани
     83- Просечна оцена
    7384
    74 ### `v_available_rides`
     85VIEW прави JOIN меѓу `drivers`, `users`, `driver_licenses`, `vehicles`, `vehicle_ownership`, `rides` и `ratings`. Пресметува број на возења по статус и просек на оценувања.
    7586
    76 Ги прикажува сите возења со статус `scheduled` кои се отворени за резервирање. За секое возење ја обединува информацијата за возачот, возилото, марката, моделот, рутата и градовите, плус пресметана проценета цена врз база на должина на рута и цена по км. Просечната оцена на возачот е веќе вклучена.
     87=== v_passenger_trip_history — Моја патна историја
    7788
    78 Апликацијата го користи при пребарување — просто добива WHERE за град на почеток, час, итн.
     89Патникот кога кликне на "Мои патувања" вижи:
     90- Кога беше возењето
     91- Каде почнало, каде завршило
     92- Име на возач
     93- Потврда дека је качен (pickup_confirmed_at)
     94- Потврда дека је слезен (dropoff_confirmed_at)
     95- Колку платил
     96- Неговата оценка за возачот (ако ја дал)
    7997
    80 ### `v_driver_profile`
     98Секоја резервација е редок во view-ot. Апликацијата може лесно да филтрира по статус (`completed`, `canceled`) или да го пагинира.
    8199
    82 Комплетна слика на возачкиот профил — лични податоци, возачка дозвола, примарното возило, укупно/завршено/откажано возења и просечна оцена. Koristi ga возачот при ажурирање на своја страна и патникот при проверка со кого се сокува пред резервирање.
     100=== v_ride_manifest — Список на патници за возење
    83101
    84 ### `v_passenger_trip_history`
     102Возачот одлучи да поаѓа во 08:00. Пред да ја запали машината, отворува ја оваа листа за да вижи:
     103- Кои патници се качуваат
     104- Имиња и телефонски броеви
     105- Каде секој вле (punkt_A_stop)
     106- Каде секој слезе (punkt_B_stop)
     107- Колку собира од секој
    85108
    86 Целосна историја на патувања за секој патник — резервации со статус, име на возач, почеток/крај, потврди за качување/слегување, финална цена и оценка. Секоја резервација е редок (не групиран), што го едностави филтерирањето и пагинирањето во апликацијата.
     109Прикажува само резервации со статус `confirmed`, `picked_up` или `completed`. Откажаните резервации не се прикажуваат — нема смисла да ги вижи.
    87110
    88 ### `v_ride_manifest`
     111=== v_booking_details — Целосен детај на резервација
    89112
    90 Список на сите потврдени, активни и завршени патници за одредено возење — имиња, телефони, места на качување/слегување, финална цена. Возачот го гледа пред поаѓање за да знае кого чека, каде да застане и колку да наплати.
     113Патникот кликна на една резервација. Вижи "сё на едно место":
     114- Кој е возачот (име, телефон, оцена)
     115- Какво возило (марка, модел, регистарски табички)
     116- Точна локација за качување (Централна станица, Скопје)
     117- Точна локација за слегување (Куманово центар)
     118- Точен час на поаѓање
     119- Финална цена
     120- Статус на резервација
    91121
    92 ### `v_driver_earnings`
     122Содржи толку многу JOIN-ови што апликацијата СЕКОГАШ го вика со филтер: `WHERE booking_id = 123`. Никогаш не го вика без филтер — би ја спалил базата од JOIN-ови!
    93123
    94 Месечен преглед на приходите по возач. За секој месец прикажува број возења, број наплатени резервации, вкупен приход, просек и min/max по резервација. Корисно е и за возачот (лична финансиска контрола) и за администраторот (анализа на активност).
     124=== v_unread_notifications — Мои нови нотификации
    95125
    96 ### `v_route_popularity`
     126Патникот или возачот отворува го "звончето". Вижи само непрочитаните нотификации:
     127- Каде е написано нотификацијата (текст)
     128- Од кого (некој возач, администратор, итн.)
     129- Кога (точна време или "пред 5 минути")
    97130
    98 Ги рангира рутите по број на резервации. Прикажува просечна цена по резервација и просечна цена по км. Служи за препораки на почетниот екран и за анализа — ако рута има многу возења но малку резервации, сигнал е дека нешто не е окај (премногу скапо, лоша достапност).
     131VIEW-ot пресметува "старост во минути" за да покаже "пред 5 мин" без дополнителна логика во апликацијата. Апликацијата го вика со `WHERE user_id = 42 AND read_at IS NULL`.
    99132
    100 ### `v_booking_details`
     133=== v_driver_earnings — Мои приходи во месец
    101134
    102 Целосна слика на една резервација — патник, возач, возило со регистарски табички, места (со имиња на локации и градови), време поаѓање, статус, финална цена. Користи се при потврда и при генерирање на сметка. Апликацијата секогаш го вика со `WHERE booking_id = ?` — никогаш без филтер.
     135Возачот кликне "Финансиски извештај". Вижи:
     136- За секој месец колку возења направил
     137- Колку резервации наплатил (можеби некој отказал во последниот момент)
     138- Вкупен приход
     139- Просечен приход по резервација
     140- Максимален приход
     141- Минимален приход
    103142
    104 ### `v_unread_notifications`
     143Администраторот го користи и за анализа — ако возачот нема приход три месеца, веројатно е неактивен.
    105144
    106 Само непрочитаните нотификации (каде `read_at IS NULL`), збогатени со име на корисник и „старост во минути" за лесна приказ (нпр. „пред 5 минути"). Апликацијата го вика со `WHERE user_id = ?` за конкретниот логиран корисник.
     145Пресметува на основа на `bookings` со статус `completed` (само завршени) и нивните финални суми.
    107146
    108 ### `v_incident_summary`
     147=== v_route_popularity — Кои рути се најпопуларни
    109148
    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Апликацијата треба да проверува ова пред да користи вредностите.