sql >> Database teknologi >  >> RDS >> Database

Håndtering af roller og statusser i et system

Der er mange måder at løse et problem på, og det er tilfældet med administration af roller og brugerstatus i softwaresystemer. I denne artikel finder du en enkel udvikling af den idé samt nogle nyttige tips og kodeeksempler.

Grundidé

I de fleste systemer er der normalt behov for at have roller og bruger statusser .

Roller er relateret til rettigheder som brugere har, mens de bruger et system efter vellykket login. Eksempler på roller er "callcentermedarbejder", "callcenterchef", "backofficemedarbejder", "backofficechef" eller "leder". Generelt betyder det, at en bruger vil have adgang til nogle funktioner, hvis han eller hun har den relevante rolle. Det er klogt at antage, at en bruger kan have flere roller på samme tid.

Statusser er meget strengere, og de bestemmer, om brugeren har rettigheder til at logge ind på systemet eller ej. En bruger kan kun have én status på et tidspunkt. Eksempler på status kan være:"arbejder", "på ferie", "sygemeldt", "kontrakt afsluttet".

Når vi ændrer en brugers status, kan vi stadig beholde alle roller relateret til denne bruger uændrede. Det er meget nyttigt, fordi vi det meste af tiden kun vil ændre brugerens status. Hvis en bruger, der arbejder som callcentermedarbejder, tager på ferie, kan vi blot ændre hans status til "på ferie" og vende tilbage til statussen "arbejder", når han kommer tilbage.

Test af roller og statusser under login gør os i stand til at beslutte, hvad der skal ske. For eksempel vil vi måske forbyde login, selvom brugernavnet og adgangskoden er korrekt. Vi kunne gøre det, hvis den aktuelle brugerstatus ikke antyder, at han arbejder, eller hvis brugeren ikke har nogen rolle i systemet.

I alle modeller angivet nedenfor er tabellerne status og role er de samme.

Tabel status har felterne id og status_name og attributten is_active . Hvis attributten is_active er sat til "True", det betyder, at den bruger, der har den status, arbejder i øjeblikket. For eksempel vil status "fungerer" have attributten is_active med værdien True, mens andre ("på ferie", "sygemeldt", "kontrakt afsluttet") ville have værdien False.

Rolletabellen har kun to felter:id og role_name .

user_account tabellen er den samme som user_account tabel præsenteret i denne artikel. Kun i den første model har user_account tabel indeholder to ekstra attributter (role_id og status_id ).

Et par modeller vil blive præsenteret. Alle virker og kan bruges, men har deres fordele og ulemper.

Simpel model

Den første idé kunne være, at vi blot tilføjer udenlandske nøglerelationer til user_account tabel, der henviser til tabeller status og role . Begge role_id og status_id er obligatoriske.




Dette er ret nemt at designe og også at håndtere data med forespørgsler, men har et par ulemper:

  1. Vi opbevarer ingen historie (eller fremtidige) data.

    Når vi ændrer status eller rolle, opdaterer vi blot status_id og role_id i user_account bord. Det vil fungere fint indtil videre, så når vi laver en ændring, vil det afspejle sig i systemet. Dette er ok, hvis vi ikke behøver at vide, hvordan statusser og roller har ændret sig historisk. Der er også et problem i, at vi ikke kan tilføje fremtid rolle eller status uden at tilføje ekstra tabeller til denne model. En situation, hvor vi sandsynligvis gerne vil have den mulighed, er, når vi ved, at nogen vil være på ferie fra næste mandag. Et andet eksempel er, når vi har fået en ny medarbejder; måske ønsker vi at gå ind i hans status og rolle nu, og at den bliver gyldig på et tidspunkt i fremtiden.

    Der er også en komplikation, hvis vi har planlagte begivenheder der bruger roller og statusser. Hændelser, der forbereder data til den næste arbejdsdag, kører normalt, mens de fleste brugere ikke bruger systemet (f.eks. om natten). Så hvis nogen ikke vil arbejde i morgen, bliver vi nødt til at vente til slutningen af ​​den aktuelle dag og derefter ændre hans roller og status efter behov. For eksempel, hvis vi har medarbejdere, der i øjeblikket arbejder og har rollen "call center-medarbejder", vil de få en liste over kunder, de skal ringe til. Hvis nogen ved en fejl havde den status og rolle, vil han også få sine kunder, og vi bliver nødt til at bruge tid på at rette op på det.

  2. Brugeren kan kun have én rolle ad gangen.

    Generelt bør brugere kunne have mere end én rolle i systemet. Måske er der ikke behov for sådan noget på det tidspunkt, hvor du designer databasen. Husk, at ændringer i arbejdsgang/proces kan ske. For eksempel kan klienten på et tidspunkt beslutte at slå to roller sammen til én. En mulig løsning er at oprette en ny rolle og tildele alle funktionaliteter fra de tidligere roller til den. Den anden løsning (hvis brugere kan have mere end én rolle) ville være, at klienten blot tildeler begge roller til brugere, der har brug for dem. Selvfølgelig er den anden løsning mere praktisk og giver klienten mulighed for hurtigere at tilpasse systemet til sine behov (hvilket ikke understøttes af denne model).

På den anden side har denne model også én stor fordel frem for andre. Det er enkelt, og forespørgsler om at ændre statusser og roller ville også være enkle. Desuden er en forespørgsel, der kontrollerer, om brugeren har rettigheder til at logge ind på systemet, meget enklere end i andre tilfælde:

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;

@brugernavn og @adgangskode er variabler fra en inputformular, mens forespørgslen returnerer brugerens id og det rolle_id, han har. I tilfælde, hvor brugernavn eller adgangskode ikke er gyldigt, parret brugernavn og adgangskode ikke eksisterer, eller brugeren har en tildelt status, der ikke er aktiv, vil forespørgslen ikke returnere nogen resultater. På den måde kan vi forbyde login.

Denne model kan bruges i tilfælde, hvor:

  • vi er sikre på, at der ikke vil ske ændringer i processen, der kræver, at brugere har mere end én rolle
  • vi behøver ikke spore roller/statusændringer i historikken
  • vi forventer ikke at have meget rolle-/statusadministration.

Tidskomponent tilføjet

Hvis vi skal spore en brugers rolle og statushistorik, skal vi tilføje mange til mange relationer mellem user_account og role og user_account og status . Selvfølgelig fjerner vi role_id og status_id fra user_account bord. Nye tabeller i modellen er user_has_role og user_has_status og alle felter i dem, undtagen sluttider, er obligatoriske.




Tabellen user_has_role indeholder data om alle roller, som brugere nogensinde har haft i systemet. Den alternative nøgle er (user_account_id , role_id , role_start_time ), fordi det ikke nytter noget at tildele den samme rolle på samme tid til en bruger mere end én gang.

Tabellen user_has_status indeholder data om alle statusser, som brugere nogensinde har haft i systemet. Den alternative nøgle her er (user_account_id , status_start_time ), fordi en bruger ikke kan have to statusser, der begynder på nøjagtig samme tidspunkt.

Starttidspunktet kan ikke være nul, fordi når vi indsætter en ny rolle/status, ved vi fra hvilket øjeblik den starter. Sluttidspunktet kan være nul, hvis vi ikke ved, hvornår rollen/status slutter (rollen er f.eks. gyldig fra i morgen, indtil der sker noget i fremtiden).

Udover at have en komplet historie, kan vi nu tilføje statusser og roller i fremtiden. Men dette skaber komplikationer, fordi vi skal tjekke for overlapning, når vi laver en indsættelse eller opdatering.

For eksempel kan brugeren kun have én status ad gangen. Før vi indsætter en ny status, skal vi sammenligne start- og sluttidspunktet for en ny status med alle eksisterende statusser for den pågældende bruger i databasen. Vi kan bruge en forespørgsel som denne:

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 og @end_time er variabler, der indeholder starttidspunktet og sluttidspunktet for en status, vi ønsker at indsætte og @user_account_id er det bruger-id, som vi indsætter det for. @end_time kan være nul, og vi skal håndtere det i forespørgslen. Til dette formål testes null-værdier med ifnull() fungere. Hvis værdien er null, tildeles en høj datoværdi (høj nok til, at når nogen bemærker en fejl i forespørgslen, er vi for længst væk :). Forespørgslen kontrollerer alle kombinationer af starttidspunkt og sluttidspunkt for en ny status sammenlignet med starttidspunkt og sluttidspunkt for eksisterende statusser. Hvis forespørgslen returnerer nogen poster, har vi overlapning med eksisterende statusser, og vi bør forbyde at indsætte den nye status. Det ville også være rart at rejse en brugerdefineret fejl.

Hvis vi ønsker at tjekke listen over aktuelle roller og statusser (brugerrettigheder), tester vi simpelthen ved at bruge starttidspunkt og sluttidspunkt.

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 og @password er variabler fra inputformen mens @time kunne indstilles til Now(). Når en bruger forsøger at logge ind, vil vi gerne kontrollere hans rettigheder på det tidspunkt. Resultatet er en liste over alle roller, som en bruger har i systemet, hvis brugernavn og adgangskode matcher, og brugeren i øjeblikket har en aktiv status. Hvis brugeren har en aktiv status, men ingen roller er tildelt, returnerer forespørgslen ikke noget.

Denne forespørgsel er enklere end den i afsnit 3, og denne model gør det muligt for os at have en historie med statusser og roller. Derudover kan vi administrere statusser og roller for fremtiden, og alt vil fungere fint.

Endelig model

Dette er blot en idé om, hvordan den tidligere model kunne ændres, hvis vi ønskede at forbedre ydeevnen. Da en bruger kun kan have én aktiv status ad gangen, kunne vi tilføje status_id ind i user_account tabel (current_status_id ). På den måde kan vi teste værdien af ​​denne attribut og behøver ikke at tilslutte os user_has_status bord. Den ændrede forespørgsel ville se sådan ud:

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




Dette forenkler naturligvis forespørgslen og fører til bedre ydeevne, men der er et større problem, der skal løses. current_status_id i user_account tabel bør kontrolleres og ændres om nødvendigt i følgende situationer:

  • på hver indsættelse/opdatering/sletning i user_has_status tabel
  • hver dag i en planlagt begivenhed bør vi kontrollere, om en persons status er ændret (aktuelt aktiv status udløb eller/og en fremtidig status blev aktiv) og opdatere den i overensstemmelse hermed

Det ville være klogt at gemme værdier, som forespørgsler vil bruge ofte. På den måde undgår vi at foretage de samme tjek igen og igen og dele jobbet. Her undgår vi at tilslutte os user_has_status tabel, og vi foretager ændringer på current_status_id kun når de sker (indsæt/opdater/slet) eller når systemet ikke er i brug så meget (planlagte hændelser kører normalt, når de fleste af brugerne ikke bruger systemet). Måske ville vi i dette tilfælde ikke få meget ud af current_status_id men se på dette som en idé, der kan hjælpe i lignende situationer.


  1. SQL Server - indre joinforbindelse ved opdatering

  2. Se og ryd Postgres caches/buffere?

  3. Oracle FOR LOOP SELECT-erklæringseksempel

  4. Introduktion til OPENJSON med eksempler (SQL-server)