Database
 sql >> Baza danych >  >> RDS >> Database

Zarządzanie rolami i statusami w systemie

Istnieje wiele sposobów na rozwiązanie problemu i tak jest w przypadku administrowania rolami i statusami użytkowników w systemach oprogramowania. W tym artykule znajdziesz prostą ewolucję tego pomysłu, a także kilka przydatnych wskazówek i przykładów kodu.

Podstawowy pomysł

W większości systemów zwykle istnieje potrzeba posiadania roli i statusy użytkowników .

Role są powiązane z prawami które użytkownicy mają podczas korzystania z systemu po pomyślnym zalogowaniu. Przykładami ról są „pracownik call center”, „menedżer call center”, „pracownik back office”, „menedżer back office” lub „menedżer”. Ogólnie oznacza to, że użytkownik będzie miał dostęp do niektórych funkcji, jeśli będzie miał odpowiednią rolę. Rozsądnie jest założyć, że użytkownik może jednocześnie pełnić wiele ról.

Statusy są znacznie bardziej rygorystyczne i określają, czy użytkownik ma uprawnienia do logowania się do systemu, czy nie. Użytkownik może mieć tylko jeden status na czas. Przykładowe statusy to:„pracujący”, „na urlopie”, „na zwolnieniu chorobowym”, „umowa zakończona”.

Gdy zmienimy status użytkownika, nadal możemy zachować wszystkie role związane z tym użytkownikiem bez zmian. Jest to bardzo pomocne, ponieważ przez większość czasu chcemy zmienić tylko status użytkownika. Jeśli użytkownik, który pracuje jako pracownik call center, wyjeżdża na urlop, możemy po prostu zmienić jego status na „na urlopie” i przywrócić go do statusu „pracujący”, gdy wróci.

Testowanie ról i statusów podczas logowania pozwala nam decydować, co się stanie. Na przykład może chcemy zabronić logowania, nawet jeśli nazwa użytkownika i hasło są poprawne. Moglibyśmy to zrobić, jeśli obecny status użytkownika nie sugeruje, że pracuje lub jeśli użytkownik nie ma żadnej roli w systemie.

We wszystkich modelach podanych poniżej tabele status i role są takie same.

Tabela status ma pola id i status_name oraz atrybut is_active . Jeśli atrybut is_active jest ustawiony na „True”, co oznacza, że ​​użytkownik, który ma ten status, aktualnie pracuje. Na przykład status „pracujący” miałby atrybut is_active z wartością Prawda, podczas gdy inne („na wakacjach”, „na zwolnieniu lekarskim”, „kontrakt zakończony”) miałyby wartość Fałsz.

Tabela ról ma tylko dwa pola:id i role_name .

user_account tabela jest taka sama jak user_account tabela prezentowana w tym artykule. Tylko w pierwszym modelu user_account tabela zawiera dwa dodatkowe atrybuty (role_id i status_id ).

Zaprezentowanych zostanie kilka modeli. Wszystkie działają i mogą być używane, ale mają swoje wady i zalety.

Prosty model

Pierwszym pomysłem może być to, że po prostu dodamy relacje klucza obcego do user_account tabela, odwołanie do tabel status i role . Oba role_id i status_id są obowiązkowe.




Jest to dość proste do zaprojektowania, a także do obsługi danych za pomocą zapytań, ale ma kilka wad:

  1. Nie przechowujemy żadnych danych historycznych (ani przyszłych).

    Kiedy zmieniamy status lub rolę, po prostu aktualizujemy status_id i role_id w user_account stół. Na razie będzie to działać dobrze, więc kiedy wprowadzimy zmianę, zostanie to odzwierciedlone w systemie. Jest to w porządku, jeśli nie musimy wiedzieć, jak historycznie zmieniały się statusy i role. Jest też problem polegający na tym, że nie możemy dodać przyszłości rolę lub status bez dodawania dodatkowych tabel do tego modelu. Jedną z sytuacji, w której prawdopodobnie chcielibyśmy mieć taką możliwość, jest sytuacja, gdy wiemy, że ktoś będzie na wakacjach począwszy od przyszłego poniedziałku. Innym przykładem jest sytuacja, w której mamy nowego pracownika; być może chcemy wejść w jego status i rolę teraz i żeby stało się ważne w pewnym momencie w przyszłości.

    Istnieje również komplikacja w przypadku, gdy mamy zaplanowane wydarzenia które wykorzystują role i statusy. Zdarzenia przygotowujące dane na kolejny dzień roboczy są zwykle uruchamiane, gdy większość użytkowników nie korzysta z systemu (np. w porze nocnej). Jeśli więc ktoś jutro nie będzie pracował, będziemy musieli poczekać do końca bieżącego dnia, a następnie odpowiednio zmienić jego role i status. Na przykład, jeśli mamy pracowników, którzy aktualnie pracują i pełnią rolę „pracownik call center”, otrzymają oni listę klientów, do których muszą zadzwonić. Jeśli ktoś przez pomyłkę miał ten status i rolę, zdobędzie również swoich klientów, a my będziemy musieli poświęcić czas na poprawienie tego.

  2. Użytkownik może mieć tylko jedną rolę na raz.

    Ogólnie użytkownicy powinni mieć więcej niż jedną rolę w systemie. Może w czasie, gdy projektujesz bazę danych, nie ma takiej potrzeby. Pamiętaj, że mogą wystąpić zmiany w przepływie pracy/procesie. Na przykład w pewnym momencie klient może zdecydować się na połączenie dwóch ról w jedną. Jednym z możliwych rozwiązań jest utworzenie nowej roli i przypisanie do niej wszystkich funkcjonalności z poprzednich ról. Innym rozwiązaniem (jeśli użytkownicy mogą mieć więcej niż jedną rolę) jest to, że klient po prostu przypisuje obie role użytkownikom, którzy ich potrzebują. Oczywiście to drugie rozwiązanie jest bardziej praktyczne i daje klientowi możliwość szybszego dostosowania systemu do jego potrzeb (co nie jest obsługiwane przez ten model).

Z drugiej strony ten model ma też jedną dużą przewagę nad innymi. To proste, więc zapytania o zmianę statusów i ról również byłyby proste. Również zapytanie sprawdzające, czy użytkownik ma uprawnienia do logowania się do systemu, jest znacznie prostsze niż w innych przypadkach:

select user_account.id, user_account.role_id
from user_account
left join status on user_account.status_id = status.id
where status.is_user_working = True
and user_account.user_name = @user_name
and user_account.password_hash_algorithm = @password;

@nazwa_użytkownika i @hasło to zmienne z formularza wejściowego, podczas gdy zapytanie zwraca identyfikator użytkownika i identyfikator roli, który posiada. W przypadkach, gdy nazwa_użytkownika lub hasło są nieprawidłowe, para nazwa_użytkownika i hasło nie istnieje lub użytkownik ma przypisany status, który nie jest aktywny, zapytanie nie zwróci żadnych wyników. W ten sposób możemy zabronić logowania.

Ten model może być używany w przypadkach, gdy:

  • jesteśmy pewni, że nie będzie żadnych zmian w procesie, które wymagają od użytkowników posiadania więcej niż jednej roli
  • nie musimy śledzić zmian ról/stanu w historii
  • nie oczekujemy, że będziemy mieć dużo administracji ról/stanów.

Dodano składnik czasu

Jeśli musimy śledzić rolę i historię statusu użytkownika, musimy dodać wiele do wielu relacji między user_account i role i user_account i status . Oczywiście usuniemy role_id i status_id z user_account stół. Nowe tabele w modelu to user_has_role i user_has_status a wszystkie pola w nich zawarte, z wyjątkiem godzin zakończenia, są obowiązkowe.




Tabela user_has_role zawiera dane o wszystkich rolach, jakie kiedykolwiek mieli użytkownicy w systemie. Alternatywny klucz to (user_account_id , role_id , role_start_time ), ponieważ nie ma sensu przypisywać tej samej roli użytkownikowi więcej niż raz.

Tabela user_has_status zawiera dane o wszystkich statusach, jakie użytkownicy mieli kiedykolwiek w systemie. Alternatywny klucz to (user_account_id , status_start_time ), ponieważ użytkownik nie może mieć dwóch statusów, które zaczynają się dokładnie w tym samym czasie.

Czas rozpoczęcia nie może być zerowy, ponieważ wstawiając nową rolę/status znamy moment, od którego rozpocznie się. Czas zakończenia może być pusty, jeśli nie wiemy, kiedy rola/status się skończy (np. rola jest ważna od jutra, aż coś się wydarzy w przyszłości).

Oprócz pełnej historii, możemy teraz dodawać statusy i role w przyszłości. Ale to powoduje komplikacje, ponieważ musimy sprawdzić nakładanie się podczas wstawiania lub aktualizacji.

Na przykład użytkownik może mieć jednocześnie tylko jeden status. Zanim wstawimy nowy status, musimy porównać czas rozpoczęcia i zakończenia nowego statusu ze wszystkimi istniejącymi statusami dla tego użytkownika w bazie danych. Możemy użyć takiego zapytania:

select *
from user_has_status
where user_has_status.user_account_id = @user_account_id
and 
(
# test if @start_time included in interval of some previous status
(user_has_status.status_start_time <= @start_time and ifnull(user_has_status.status_end_time, "2200-01-01") >= @start_time)
or
# test if @end_time included in interval of some previous status  
(user_has_status.status_start_time <= @end_time and ifnull(user_has_status.status_end_time, "2200-01-01") >= ifnull(@end_time, "2199-12-31"))  
or  
# if @end_time is null we cannot have any statuses after @start_time
(@end_time is null and user_has_status.status_start_time >= @start_time)  
or
# new status "includes" old satus (@start_time <= user_has_status.status_start_time <= @end_time)
(user_has_status.status_start_time >= @start_time and user_has_status.status_start_time <= ifnull(@end_time, "2199-12-31"))  
)

@start_time i @end_time to zmienne zawierające czas rozpoczęcia i czas zakończenia statusu, który chcemy wstawić oraz @user_account_id to identyfikator użytkownika, dla którego go wstawiamy. @end_time może być null i musimy go obsłużyć w zapytaniu. W tym celu wartości null są testowane za pomocą ifnull() funkcjonować. Jeśli wartość jest null, przypisywana jest wysoka wartość daty (na tyle wysoka, że ​​gdy ktoś zauważy błąd w zapytaniu, już dawno znikniemy :). Zapytanie sprawdza wszystkie kombinacje czasu rozpoczęcia i zakończenia pod kątem nowego statusu w porównaniu z czasem rozpoczęcia i zakończenia istniejących statusów. Jeśli zapytanie zwróci jakiekolwiek rekordy, oznacza to, że nakładamy się na istniejące statusy i powinniśmy zabronić wstawiania nowego statusu. Byłoby również miło zgłosić błąd niestandardowy.

Jeśli chcemy sprawdzić listę aktualnych ról i statusów (praw użytkownika), po prostu testujemy, używając czasu rozpoczęcia i zakończenia.

select user_account.id, user_has_role.id
from user_account
left join user_has_role on user_has_role.user_account_id = user_account.id
left join user_has_status on user_account.id = user_has_status.user_account_id
left join status on user_has_status.status_id = status.id
where user_account.user_name = @user_name
and user_account.password_hash_algorithm = @password
and user_has_role.role_start_time <= @time and ifnull(user_has_role.role_end_time,"2200-01-01") >= @time
and user_has_status.status_start_time <= @time and ifnull(user_has_status.status_end_time,"2200-01-01") >= @time
and status.is_user_working = True

@user_name i @password są zmiennymi z formularza wejściowego, podczas gdy @time można ustawić na Now(). Gdy użytkownik próbuje się zalogować, chcemy sprawdzić jego uprawnienia w tym czasie. Wynikiem jest lista wszystkich ról, które użytkownik ma w systemie w przypadku, gdy nazwa_użytkownika i hasło są zgodne, a użytkownik ma aktualnie aktywny status. Jeśli użytkownik ma status aktywny, ale nie ma przypisanych ról, zapytanie niczego nie zwróci.

To zapytanie jest prostsze niż to w sekcji 3, a ten model umożliwia nam posiadanie historii statusów i ról. Ponadto możemy zarządzać statusami i rolami na przyszłość i wszystko będzie działać dobrze.

Model końcowy

To tylko pomysł na to, jak można zmienić poprzedni model, gdybyśmy chcieli poprawić osiągi. Ponieważ użytkownik może mieć tylko jeden aktywny status na raz, możemy dodać status_id na user_account tabela (current_status_id ). W ten sposób możemy przetestować wartość tego atrybutu i nie będziemy musieli dołączać do user_has_status stół. Zmodyfikowane zapytanie wyglądałoby tak:

select user_account.id, user_has_role.id
from user_account
left join user_has_role on user_has_role.user_account_id = user_account.id
left join status on user_account.current_status_id = status.id
where user_account.user_name = @user_name
and user_account.password_hash_algorithm = @password
and user_has_role.role_start_time <= @time and ifnull(user_has_role.role_end_time,"2200-01-01") >= @time
and status.is_user_working = True




Oczywiście upraszcza to zapytanie i prowadzi do lepszej wydajności, ale istnieje większy problem, który wymaga rozwiązania. current_status_id w user_account tabelę należy sprawdzić i w razie potrzeby zmienić w następujących sytuacjach:

  • przy każdym wstawieniu/aktualizacji/usunięciu w user_has_status stół
  • każdego dnia w zaplanowanym wydarzeniu powinniśmy sprawdzać, czy czyjś status się zmienił (obecnie aktywny status wygasł lub/i jakiś przyszły status stał się aktywny) i odpowiednio go aktualizować

Rozsądnie byłoby zapisać wartości, których zapytania będą często używać. W ten sposób unikniemy powtarzania tych samych kontroli i dzielenia pracy. Tutaj unikniemy dołączenia do user_has_status tabeli i wprowadzimy zmiany w current_status_id tylko wtedy, gdy się wydarzą (wstaw/aktualizuj/usuń) lub gdy system nie jest tak często używany (zaplanowane zdarzenia zwykle są uruchamiane, gdy większość użytkowników nie korzysta z systemu). Może w tym przypadku niewiele zyskalibyśmy na current_status_id ale spójrz na to jako na pomysł, który może pomóc w podobnych sytuacjach.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Raport dotyczący bazy danych Open Source 2019:najlepsze bazy danych, chmura publiczna a lokalna, trwałość Polyglot

  2. Jak łatwo wdrożyć bazę danych Timescale?

  3. 9 najczęstszych błędów projektowania bazy danych

  4. Połącz aplikacje ODBC w systemie Windows z SugarCRM

  5. Silny kursor odniesienia PL/SQL ze zdefiniowanym przez użytkownika typem danych rekordu