Changes between Version 10 and Version 11 of RelationalModel


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

--

Legend:

Unmodified
Added
Removed
Modified
  • RelationalModel

    v10 v11  
    77== Архитектура и главни концепти
    88
    9 === Користачи и нивни улоги
     9=== Корисници и нивни улоги
    1010
    11 Основна е поделбата меѓу `users` (сите во системот), `drivers` (возачи) и `passengers` (патници). Табелата `users` е како "мајка" — содржи основни информации (име, телефон, емајл). Потоа `drivers` и `passengers` додаваат своите специфични полиња. Една иста личност може да биде и возач и патник — нема никаква забрана за тоа.
     11Основна е поделбата меѓу `users` (сите во системот), `drivers` (возачи) и `passengers` (патници). Табелата `users` е како „мајка“ — ги содржи основните информации (име, телефон, е-пошта). Потоа `drivers` и `passengers` ги додаваат своите специфични полиња. Една иста личност може да биде и возач и патник — нема никаква забрана за тоа.
    1212
    1313=== Возила и производители
    1414
    15 Возилата имаат три нивоа организирање. Прво `manufacturers` (Volkswagen, Mercedes, итн.), потоа `vehicle_models` (Golf, C-Class), потоа самите `vehicles` (твоја лична кола). Вакво расчленување спречува да пишувале "Volkswagen" стоди пати во базата.
     15Возилата се организирани во три нивоа. Прво `manufacturers` (Volkswagen, Mercedes итн.), потоа `vehicle_models` (Golf, C-Class), па самите `vehicles` (твојата лична кола). Ваквото расчленување спречува да се пишува "Volkswagen" стотици пати во базата.
    1616
    17 Возачот има много возила, возило мора да припаѓа на возач. За тоа го имаме `vehicle_ownership` — табела која чува каде возачот имал кола. Кога возачот купи ново возило, едноставно ј додаваме нов ред во `vehicle_ownership`. Старото возило останува во историјата, не го бришеме. Вака видиме точно кој возач кога имал кола.
     17Возачот може да има многу возила, а секое возило мора да припаѓа на возач. За тоа ја користиме `vehicle_ownership` — табела која чува податоци за тоа кој возач кој автомобил го поседувал. Кога возачот ќе купи ново возило, едноставно додаваме нов ред во `vehicle_ownership`. Старото возило останува во историјата и не го бришеме. На овој начин точно гледаме кој возач во кој период поседувал одредена кола.
    1818
    1919=== Рути и возења
    2020
    21 `Routes` е шаблон — замисли рута "Скопје → Куманово → Пријепа" дефинирана веќе во системот. `Rides` е конкретно возење: „Во понеделник во 08:00 возачот Марко ја возеше оваа рута со Volkswagen Golf".
     21`Routes` е шаблон — замисли рута „Скопје → Куманово → Прилеп“ која е веќе дефинирана во системот. `Rides` е конкретно возење: „Во понеделник во 08:00 часот возачот Марко ја возеше оваа рута со Volkswagen Golf“.
    2222
    23 Застанувањата на рутата се во `route_stops` со редоследни броеви (1, 2, 3...). Рутата може да има 2 застанувања или 10 застанувања. Исто така, на иста рута можеш да направиш и круг (почеток и крај иста локација).
     23Застанувањата на рутата се во `route_stops` со редоследни броеви (1, 2, 3...). Рутата може да има 2 застанувања или 10 застанувања. Исто така, на иста рута може да се направи и круг (почетокот и крајот да се на иста локација).
    2424
    2525=== Сегменти — како се делат возењата
    2626
    27 Возењето од Скопје до Пријепа преку Куманово не е "целина" во пресметките. Поделено е на сегменти:
    28 - Скопје → Куманово
    29 - Куманово → Пријепа
     27Возењето од Скопје до Прилеп преку Куманово не е „целина“ во пресметките. Поделено е на сегменти:
     28* Скопје → Куманово
     29* Куманово → Прилеп
    3030
    31 Патниците не мора да патуваат целата рута. Еден патник можеш да влезе во Куманово и да излезе во Пријепа. За тоа `route_segments` дели рутата на делови меѓу два стопа, а `passenger_segments` чува колку патници беа на секој сегмент. Вака точно можеме да изразунаме колку требало да плати секој патник според каде вле и каде слезе.
     31Патниците не мора да ја патуваат целата рута. Еден патник може да влезе во Куманово и да излезе во Прилеп. Затоа `route_segments` ја дели рутата на делови меѓу два стопа, а `passenger_segments` чува податоци за тоа колку патници имало на секој сегмент. Вака точно можеме да пресметаме колку треба да плати секој патник според тоа каде влегол и каде слегол.
    3232
    3333=== Патарини
    3434
    35 Не можеме едноставно да кажеме "возење чини X денари". Некои возења минуваат наплатни рампи. За тоа имаме:
    36 - `toll_points` — каде се наплатните рампи и колку чинат по тип возило
    37 - `ride_tolls` — евидентира дека возењето Х минало низ рампата Y
    38 - `toll_passenger_split` — раздела на патарината меѓу патниците кои беа присутни
     35Не можеме едноставно да кажеме дека „возењето чини X денари“. Некои возења поминуваат низ наплатни рампи. За тоа имаме:
     36* `toll_points` — каде се наплатните рампи и колку чинат според типот на возило
     37* `ride_tolls` — евидентира дека возењето Х поминало низ рампата Y
     38* `toll_passenger_split` — поделба на патарината меѓу патниците кои биле присутни
    3939
    40 === Готовина, не картички
     40=== Готовина, а не картички
    4141
    42 Апликацијата е готовина-само. Нема онлајн плаќања, нема дигитални портфели. Возачот собира готовина од патниците. Табелите `fare_splits` и `booking_final_fare` служат само да покажат возачу колку собира од кого — ништо повеќе.
     42Апликацијата работи исклучиво со готовина. Нема онлајн плаќања, ниту дигитални паричници. Возачот ја собира готовината од патниците. Табелите `fare_splits` и `booking_final_fare` служат само за да му покажат на возачот колку пари треба да собере од кого — ништо повеќе.
    4343
    4444=== Оценувања
    4545
    46 Патниците оценуваат возачи, возачи оценуваат патници. Табелата `ratings` чува сите оценувања. Има CHECK constraint кој вели: "Човек не може да го оцени самиот себеси". За просечната оцена не пишуваме `avg_rating` поле директно. Наместо тоа, правиме view-и `driver_ratings` и `passenger_ratings` кои пресметуваат просек на лету. Вака ако додадеш нова оценка, просекот се ажурира веднаш без дополнителна логика.
     46Патниците ги оценуваат возачите, а возачите ги оценуваат патниците. Табелата `ratings` ги чува сите оценувања. Постои CHECK constraint кој вели: „Корисникот не може да се оцени самиот себеси“. За просечната оцена не користиме директно поле `avg_rating`. Наместо тоа, користиме погледи (views) `driver_ratings` и `passenger_ratings` кои го пресметуваат просекот во реално време (on the fly). Вака, ако додадеш нова оценка, просекот се ажурира веднаш без дополнителна логика.
    4747
    48 === История и следливост
     48=== Историја и следливост
    4949
    50 Кога возење промени статус (scheduled → in_progress → completed), промената оди во `ride_status_history` со временска марка. Исто и резервациите во `booking_status_history`. Вака администраторот има целосна историја — кога точно се случи што.
     50Кога возењето ќе го промени статусот (scheduled → in_progress → completed), промената се запишува во `ride_status_history` со временска ознака. Истото важи и за резервациите во `booking_status_history`. На овој начин администраторот има целосна историја — кога точно што се случило.
    5151
    5252=== Бришење на податоци
    5353
    54 Претпоставка: администраторот избрише користачки профил. Но возачот има 50 завршени возења! Ние не ги бришеме поврзаните возења — користиме `ON DELETE RESTRICT`. За статистика, користиме резервиран корисник со `id = 0` наречен "избришан корисник". Вака можеме да видиме "некој возач" направил возење, дури и кога профилот е избришан.
     54Претпоставка: администраторот брише кориснички профил. Но возачот има 50 завршени возења! Ние не ги бришеме поврзаните возења — користиме `ON DELETE RESTRICT`. За потребите на статистиката, користиме резервиран корисник со `id = 0` наречен „избришан корисник“. Така можеме да видиме дека „некој возач“ направил возење, дури и кога оригиналниот профил е избришан.
    5555
    5656== Прегледи (Views)
    5757
    58 Сите овие табели се напалени со JOIN-ови. Наместо апликацијата да пишува `SELECT ... FROM rides JOIN drivers JOIN vehicles ...` низ целиот код, ми направивме 9 views. Секој view е спремена SQL "забелешка" која делува како табела.
     58Сите овие табели се поврзани со JOIN-ови. Наместо апликацијата да пишува `SELECT ... FROM rides JOIN drivers JOIN vehicles ...` низ целиот код, направивме 9 погледи (views). Секој поглед е подготвена SQL „забелешка“ која се однесува како табела.
    5959
    6060=== v_available_rides — Возења отворени за резервирање
    6161
    62 Ова е она што патникот вижи кога пребарува возење. Показува:
    63 - Кога поаѓа возењето
    64 - Каде почнува, каде завршува
    65 - Име на возач
    66 - Марка и модел на возило
    67 - Проценета цена
    68 - Просечна оцена на возачот
     62Ова е она што патникот го гледа кога пребарува возење. Прикажува:
     63* Кога поаѓа возењето
     64* Каде почнува, а каде завршува
     65* Име на возачот
     66* Марка и модел на возилото
     67* Проценета цена
     68* Просечна оцена на возачот
    6969
    70 VIEW вика JOIN меѓу `rides`, `drivers`, `vehicles`, `vehicle_models`, `manufacturers`, `routes`, `route_stops`, `ratings` и пресметува просечна оцена на лету. Филтрира само возења со статус `scheduled`.
     70Овој VIEW прави JOIN меѓу `rides`, `drivers`, `vehicles`, `vehicle_models`, `manufacturers`, `routes`, `route_stops`, `ratings` и го пресметува просекот на оценките во реално време. Ги филтрира само возењата со статус `scheduled`.
    7171
    7272Апликацијата го користи вака: `SELECT * FROM v_available_rides WHERE origin_city = 'Скопје' AND departure_time >= NOW()`. Едноставно!
    7373
    74 === v_driver_profile — Профилот на возачот
     74=== v_driver_profile — Профил на возачот
    7575
    76 Возачот кога си ажурира профилот, вижи ова. Исто и патникот пред да резервира место ако сака да види со кого ќе возеше.
     76Возачот го гледа ова кога го ажурира својот профил. Исто така, патникот го гледа ова пред да резервира место ако сака да види со кого ќе се вози.
    7777
    7878Содржи:
    79 - Лични податоци (име, телефон)
    80 - Информации за возачка дозвола (број, датум на издавање)
    81 - Примарното возило на возачот (ако има повеќе, земаме оно со најмал ID)
    82 - Статистика: вкупно возења, завршени, откажани
    83 - Просечна оцена
     79* Лични податоци (име, телефон)
     80* Информации за возачка дозвола (број, датум на издавање)
     81* Примарното возило на возачот (ако има повеќе, го земаме она со најмал ID)
     82* Статистика: вкупно возења, завршени, откажани
     83* Просечна оцена
    8484
    85 VIEW прави JOIN меѓу `drivers`, `users`, `driver_licenses`, `vehicles`, `vehicle_ownership`, `rides` и `ratings`. Пресметува број на возења по статус и просек на оценувања.
     85VIEW-то прави JOIN меѓу `drivers`, `users`, `driver_licenses`, `vehicles`, `vehicle_ownership`, `rides` и `ratings`. Ги пресметува бројот на возења според статус и просекот на оценките.
    8686
    8787=== v_passenger_trip_history — Моја патна историја
    8888
    89 Патникот кога кликне на "Мои патувања" вижи:
    90 - Кога беше возењето
    91 - Каде почнало, каде завршило
    92 - Име на возач
    93 - Потврда дека је качен (pickup_confirmed_at)
    94 - Потврда дека је слезен (dropoff_confirmed_at)
    95 - Колку платил
    96 - Неговата оценка за возачот (ако ја дал)
     89Патникот, кога ќе кликне на „Мои патувања“, го гледа следново:
     90* Кога било возењето
     91* Каде почнало, а каде завршило
     92* Име на возачот
     93* Потврда дека е качен (pickup_confirmed_at)
     94* Потврда дека е слезен (dropoff_confirmed_at)
     95* Колку платил
     96* Неговата оценка за возачот (ако ја дал)
    9797
    98 Секоја резервација е редок во view-ot. Апликацијата може лесно да филтрира по статус (`completed`, `canceled`) или да го пагинира.
     98Секоја резервација е посебен ред во погледот. Апликацијата може лесно да филтрира по статус (`completed`, `canceled`) или да ги пагинира резултатите.
    9999
    100100=== v_ride_manifest — Список на патници за возење
    101101
    102 Возачот одлучи да поаѓа во 08:00. Пред да ја запали машината, отворува ја оваа листа за да вижи:
    103 - Кои патници се качуваат
    104 - Имиња и телефонски броеви
    105 - Каде секој вле (punkt_A_stop)
    106 - Каде секој слезе (punkt_B_stop)
    107 - Колку собира од секој
     102Возачот одлучува да тргне во 08:00 часот. Пред да го запали возилото, ја отвора оваа листа за да види:
     103* Кои патници се качуваат
     104* Имиња и телефонски броеви
     105* Каде влегува секој (point_A_stop)
     106* Каде слегува секој (point_B_stop)
     107* Колку пари треба да собере од секого
    108108
    109 Прикажува само резервации со статус `confirmed`, `picked_up` или `completed`. Откажаните резервации не се прикажуваат — нема смисла да ги вижи.
     109Ги прикажува само резервациите со статус `confirmed`, `picked_up` или `completed`. Откажаните резервации не се прикажуваат — нема смисла возачот да ги гледа.
    110110
    111 === v_booking_details — Целосен детај на резервација
     111=== v_booking_details — Целосни детали за резервација
    112112
    113 Патникот кликна на една резервација. Вижи "сё на едно место":
    114 - Кој е возачот (име, телефон, оцена)
    115 - Какво возило (марка, модел, регистарски табички)
    116 - Точна локација за качување (Централна станица, Скопје)
    117 - Точна локација за слегување (Куманово центар)
    118 - Точен час на поаѓање
    119 - Финална цена
    120 - Статус на резервација
     113Патникот кликнува на една резервација и гледа „сè на едно место“:
     114* Кој е возачот (име, телефон, оцена)
     115* Какво возило (марка, модел, регистарски таблички)
     116* Точна локација за качување (на пр. Централна станица, Скопје)
     117* Точна локација за слегување (на пр. Куманово центар)
     118* Точен час на поаѓање
     119* Финална цена
     120* Статус на резервацијата
    121121
    122 Содржи толку многу JOIN-ови што апликацијата СЕКОГАШ го вика со филтер: `WHERE booking_id = 123`. Никогаш не го вика без филтер — би ја спалил базата од JOIN-ови!
     122Содржи многу JOIN-ови, па затоа апликацијата СЕКОГАШ го повикува со филтер: `WHERE booking_id = 123`. Никогаш не се повикува без филтер за да не се преоптовари базата.
    123123
    124 === v_unread_notifications — Мои нови нотификации
     124=== v_unread_notifications — Мои нови известувања
    125125
    126 Патникот или возачот отворува го "звончето". Вижи само непрочитаните нотификации:
    127 - Каде е написано нотификацијата (текст)
    128 - Од кого (некој возач, администратор, итн.)
    129 - Кога (точна време или "пред 5 минути")
     126Патникот или возачот го отвораат „ѕвончето“. Ги гледаат само непрочитаните известувања:
     127* Текстот на известувањето
     128* Од кого е испратено (возач, администратор итн.)
     129* Кога е испратено (точно време или „пред 5 минути“)
    130130
    131 VIEW-ot пресметува "старост во минути" за да покаже "пред 5 мин" без дополнителна логика во апликацијата. Апликацијата го вика со `WHERE user_id = 42 AND read_at IS NULL`.
     131VIEW-то ја пресметува „староста во минути“ за да прикаже „пред 5 мин“ без дополнителна логика во апликацијата. Апликацијата го повикува со `WHERE user_id = 42 AND read_at IS NULL`.
    132132
    133 === v_driver_earnings — Мои приходи во месец
     133=== v_driver_earnings — Мои месечни приходи
    134134
    135 Возачот кликне "Финансиски извештај". Вижи:
    136 - За секој месец колку возења направил
    137 - Колку резервации наплатил (можеби некој отказал во последниот момент)
    138 - Вкупен приход
    139 - Просечен приход по резервација
    140 - Максимален приход
    141 - Минимален приход
     135Возачот кликнува на „Финансиски извештај“ и гледа:
     136* Колку возења направил за секој месец
     137* Колку резервации наплатил (некои можеби биле откажани во последен момент)
     138* Вкупен приход
     139* Просечен приход по резервација
     140* Максимален приход
     141* Минимален приход
    142142
    143 Администраторот го користи и за анализа — ако возачот нема приход три месеца, веројатно е неактивен.
    144 
    145 Пресметува на основа на `bookings` со статус `completed` (само завршени) и нивните финални суми.
     143Администраторот го користи ова и за анализа — ако возачот нема приход три месеци, веројатно е неактивен. Пресметката се базира на `bookings` со статус `completed` и нивните финални суми.
    146144
    147145=== v_route_popularity — Кои рути се најпопуларни
    148146
    149 Апликацијата на почетниот екран прикажува популарни рути за да го вдахновува патникот. Ова view-ot ги рангира:
    150 - По број на вкупни резервации
    151 - Просечна цена по резервација
    152 - Просечна цена по километар
     147Апликацијата на почетниот екран прикажува популарни рути за да го мотивира патникот. Овој поглед ги рангира рутите според:
     148* Број на вкупни резервации
     149* Просечна цена по резервација
     150* Просечна цена по километар
    153151
    154 Служи и како сигнал за возачите. Ако рутата Скопје↔Куманово има 50 резервации месечно, но возачот направил 10 возења и не продал ништо — сигнал е дека нешто не е окај. Можеби е превиско скапо, или нема добро рекламирано возење.
     152Служи и како сигнал за возачите. Ако рутата Скопје↔Куманово има 50 резервации месечно, а возачот направил 10 возења и не продал ништо — тоа е знак дека нешто не е во ред (можеби е прескапо или возењето не е добро рекламирано).
    155153
    156154=== v_incident_summary — Безбедносни инциденти
    157155
    158 Само администраторот видува ова. Прикажува:
    159 - Тип на инцидент (невежливост, несреќа, кршење на правила)
    160 - Опис на инцидентот
    161 - Кога е пријавен
    162 - Лични податоци на пријавувачот (патник или возач)
    163 - Лични податоци на другата страна
    164 - Возачка дозвола број
    165 - Рутата на возењето
     156Ова го гледа само администраторот. Прикажува:
     157* Тип на инцидент (некултурно однесување, несреќа, кршење правила)
     158* Опис на инцидентот
     159* Кога е пријавен
     160* Лични податоци на пријавувачот (патник или возач)
     161* Лични податоци на другата страна
     162* Број на возачка дозвола
     163* Рута на возењето
    166164
    167 Инцидентите могат да будат пријавени дури и после завршено возење, така да view-ot не филтрира по статус на возење.
     165Инцидентите можат да бидат пријавени дури и по завршено возење, па затоа овој поглед не филтрира според статусот на возењето.
    168166
    169167== Важно: NULL вредности со логика
    170168
    171 Неколку полиња се NULL базирано на логика:
     169Неколку полиња се NULL врз основа на одредена логика:
    172170
    173 * `rides.recurrence_days` и `rides.recurrence_end_date` — се пополнуваат САМО ако `is_recurring = TRUE`. За еднократни возења се NULL.
     171* `rides.recurrence_days` и `rides.recurrence_end_date` — се пополнуваат САМО ако `is_recurring = TRUE`. За еднократни возења тие се NULL.
     172* `bookings.pickup_stop_id` и `bookings.dropoff_stop_id` — ако патникот ја резервира целата рута, стоповите се почетната и крајната точка. Ако резервира само дел, се пополнуваат конкретните стопови.
    174173
    175 * `bookings.pickup_stop_id` и `bookings.dropoff_stop_id` — ако патникот резервира целата рута, стоповите се почеток и крај. Ако резервира дел, се полнат конкретни стопови.
    176 
    177 Апликацијата треба да проверува ова пред да користи вредностите.
     174Апликацијата треба да ги проверува овие услови пред да ги користи вредностите.