Има много начини за решаване на проблем и такъв е случаят с администрирането на роли и потребителски статуси в софтуерни системи. В тази статия ще намерите проста еволюция на тази идея, както и някои полезни съвети и примерни кодове.
Основна идея
В повечето системи обикновено има нужда отроли и потребителскистатуси .
Ролите са свързани справа които потребителите имат, докато използват система след успешно влизане. Примери за роли са „служител на кол център“, „мениджър на кол център“, „служител на бек офис“, „бек офис мениджър“ или „мениджър“. Обикновено това означава, че потребителят ще има достъп до някаква функционалност, ако има съответната роля. Разумно е да се предположи, че потребителят може да има няколко роли едновременно.
Статусите са много по-строги и определят дали потребителят има права да влезе в системата или не. Потребител може да има само едно състояние на време. Примери за статуси биха били:„работи“, „в отпуск“, „в отпуск по болест“, „договорът приключи“.
Когато променим статуса на потребителя, ние все още можем да запазим всички роли, свързани с този потребител, непроменени. Това е много полезно, защото през повечето време искаме да променим само статуса на потребителя. Ако потребител, който работи като служител на кол център, отиде в отпуск, можем просто да променим статуса му на „в отпуска“ и да го върнем в състояние „работи“, когато се върне.
Тестването на роли и статуси по време на влизане ни позволява да решим какво ще се случи. Например, може би искаме да забраним влизането, дори ако потребителското име и паролата са правилни. Бихме могли да го направим, ако текущото състояние на потребителя не означава, че той работи или ако потребителят няма никаква роля в системата.
Във всички модели, дадени по-долу, таблиците status
и role
са еднакви.
Таблица status
има полетата id
и status_name
и атрибута is_active
. Ако атрибутът is_active
е зададен на „Вярно“, което означава, че потребителят, който има това състояние, в момента работи. Например, състоянието „работи“ би имало атрибута is_active
със стойност True, докато други („в отпуск“, „в отпуск по болест“, „договорът приключи“) ще имат стойност False.
Таблицата с роли има само две полета:id
и role_name
.
user_account
таблицата е същата като user_account
таблица, представена в тази статия. Само в първия модел има user_account
таблицата съдържа два допълнителни атрибута (role_id
и status_id
).
Ще бъдат представени няколко модела. Всички те работят и могат да се използват, но имат своите предимства и недостатъци.
Прост модел
Първата идея може да бъде, че просто добавяме връзки с външни ключове към user_account
таблица, препратка към таблици status
и role
. И двете role_id
и status_id
са задължителни.
Това е доста лесно за проектиране, а също и за обработка на данни със заявки, но има няколко недостатъка:
-
Ние не съхраняваме никакви исторически (или бъдещи) данни.
Когато променим състоянието или ролята, ние просто актуализираме
status_id
иrole_id
вuser_account
маса. Това ще работи добре засега, така че когато направим промяна, тя ще се отрази в системата. Това е добре, ако не е необходимо да знаем как статусите и ролите са се променили исторически. Също така има проблем в това, че не можем да добавим бъдеще роля или статус без добавяне на допълнителни таблици към този модел. Една ситуация, в която вероятно бихме искали да имаме тази опция, е, когато знаем, че някой ще бъде във ваканция от следващия понеделник. Друг пример е, когато имаме нов служител; може би искаме да влезем в неговия статут и роля сега и той да стане валиден в някакъв момент в бъдеще.Има и усложнение в случай, че имаменасрочени събития които използват роли и статуси. Събитията, които подготвят данни за следващия работен ден, обикновено се изпълняват, докато повечето потребители не използват системата (например през нощта). Така че, ако някой няма да работи утре, ще трябва да изчакаме до края на текущия ден и след това да променим ролите и статуса му според случая. Например, ако имаме служители, които в момента работят и имат ролята „служител на кол центъра“, те ще получат списък с клиенти, на които трябва да се обадят. Ако някой по погрешка е имал този статут и роля, той също ще получи своите клиенти и ще трябва да отделим време, за да го коригираме.
-
Потребителят може да има само една роля в даден момент.
По принцип потребителите трябва да могат да имат повече от една роля в системата. Може би в момента, в който проектирате базата данни, няма нужда от нещо подобно. Имайте предвид, че могат да се случат промени в работния процес/процеса. Например, в даден момент клиентът може да реши да обедини две роли в една. Едно възможно решение е да създадете нова роля и да й присвоите всички функции от предишните роли. Другото решение (ако потребителите могат да имат повече от една роля) би било, че клиентът просто присвоява и двете роли на потребители, които се нуждаят от тях. Разбира се второто решение е по-практично и дава на клиента възможността да настрои системата според нуждите си по-бързо (което не се поддържа от този модел).
От друга страна, този модел има и едно голямо предимство пред останалите. Това е просто и така заявките за промяна на статуси и роли също биха били лесни. Също така, заявка, която проверява дали потребителят има права да влезе в системата, е много по-проста, отколкото в други случаи:
изберете user_account.id, user_account.role_id от user_accountleft състояние на присъединяване на user_account.status_id =status.idwhere status.is_user_working =True и user_account.user_name =@user_name и user_account.password_hash_algorithm =@presword;>парола@user_name и @password са променливи от формуляр за въвеждане, докато заявката връща идентификатора на потребителя и идентификатора на ролята, който той има. В случаите, когато user_name или парола не са валидни, сдвояване user_name и парола не съществува или потребителят има присвоен статус, който не е активен, заявката няма да върне никакви резултати. По този начин можем да забраним влизането.
Този модел може да се използва в случаите, когато:
- сигурни сме, че няма да има промени в процеса, които изискват потребителите да имат повече от една роля
- не е необходимо да проследяваме промените в ролите/статуса в историята
- не очакваме да имаме много администриране на роля/статус.
Добавен е компонент за време
Ако трябва да проследяваме ролята и историята на състоянието на потребителя, трябва да добавим много към много връзки между user_account
и role
и user_account
и status
. Разбира се, ще премахнем role_id
и status_id
от user_account
маса. Новите таблици в модела са user_has_role
и user_has_status
и всички полета в тях, с изключение на крайните времена, са задължителни.
Таблицата user_has_role
съдържа данни за всички роли, които потребителите са имали някога в системата. Алтернативният ключ е (user_account_id
, role_id
, role_start_time
), защото няма смисъл да присвоявате една и съща роля по едно и също време на потребител повече от веднъж.
Таблицата user_has_status
съдържа данни за всички състояния, които потребителите са имали някога в системата. Алтернативният ключ тук е (user_account_id
, status_start_time
), защото потребителят не може да има две състояния, които започват по едно и също време.
Началният час не може да бъде нулев, защото когато вмъкнем нова роля/статус, ние знаем момента, от който ще започне. Крайният час може да бъде нулев, в случай че не знаем кога ролята/статусът ще приключи (напр. ролята е валидна от утре, докато нещо не се случи в бъдеще).
Освен че имаме пълна история, вече можем да добавяме статуси и роли в бъдеще. Но това създава усложнения, защото трябва да проверяваме за припокриване, когато правим вмъкване или актуализиране.
Например потребителят може да има само едно състояние наведнъж. Преди да вмъкнем ново състояние, трябва да сравним началния и крайния час на ново състояние с всички съществуващи статуси за този потребител в базата данни. Можем да използваме заявка като тази:
изберете *от user_has_statuswhere user_has_status.user_account_id =@user_account_idand (# тест дали @start_time е включен в интервал от някакво предишно състояние(user_has_status.status_start_time <=@start_time и ifnull(user_has2_status_0-1) ") ". =@start_time)или# тествайте дали @end_time е включено в интервал на някакво предишно състояние (user_has_status.status_start_time <=@end_time и ifnull(user_has_status.status_end_time, "2200-01-01")>=ifnull(@end_time, "2199- 12-31")) или # ако @end_time е null, не можем да имаме никакви състояния след @start_time(@end_time е null и user_has_status.status_start_time>=@start_time) или# ново състояние "включва" старо състояние (@start_time <=user_has_status .status_start_time <=@end_time)(user_has_status.status_start_time>=@start_time и user_has_status.status_start_time <=ifnull(@end_time, "2199-12-31")) )
@start_time
и @end_time
са променливи, съдържащи началния и крайния час на състояние, което искаме да вмъкнем и @user_account_id
е потребителският идентификатор, за който го вмъкваме. @end_time
може да бъде нула и трябва да го обработваме в заявката. За тази цел нулевите стойности се тестват с ifnull()
функция. Ако стойността е нула, се присвоява висока стойност за дата (достатъчно висока, че когато някой забележи грешка в заявката, ние отдавна ще сме изчезнали :). Заявката проверява всички комбинации от начален и краен час за ново състояние в сравнение с началния и крайния час на съществуващите състояния. Ако заявката върне записи, тогава имаме припокриване със съществуващи състояния и трябва да забраним вмъкването на ново състояние. Също така би било хубаво да повдигнете персонализирана грешка.
Ако искаме да проверим списъка с текущи роли и състояния (потребителски права), просто тестваме, използвайки началния и крайния час.
изберете user_account.id, user_has_role.id от user_accountleft join user_has_role на user_has_role.user_account_id =user_account.idleft join user_has_status на user_account.id =user_has_status.user_account_status.user_account_ime.user_account_idle.u user_status_user_status_user_status_user_id_where password_hash_algorithm =@passwordand user_has_role.role_start_time <=@time и ifnull(user_has_role.role_end_time,"2200-01-01")>=@timeand user_has_status.status_start_time <=@time и ifnull_stat_, ifnull ")>=@timeand status.is_user_working =Вярно
@user_name
и @password
са променливи от формата за въвеждане, докато @time
може да бъде настроен на Now(). Когато потребител се опита да влезе, ние искаме да проверим правата му в този момент. Резултатът е списък с всички роли, които потребителят има в системата, в случай че потребителско име и парола съвпадат и потребителят в момента има активно състояние. Ако потребителят има активно състояние, но няма назначени роли, заявката няма да върне нищо.
Тази заявка е по-проста от тази в раздел 3 и този модел ни позволява да имаме история на статуси и роли. Освен това можем да управляваме статуси и роли за бъдещето и всичко ще работи добре.
Окончателен модел
Това е само идея как бихме могли да променим предишния модел, ако искаме да подобрим производителността. Тъй като потребителят може да има само едно активно състояние в даден момент, бихме могли да добавим status_id
в user_account
таблица (current_status_id
). По този начин можем да тестваме стойността на този атрибут и няма да се налага да се присъединяваме към user_has_status
маса. Променената заявка ще изглежда така:
изберете user_account.id, user_has_role.id от user_accountleft join user_has_role на user_has_role.user_account_id =user_account.idleft състояние на присъединяване на user_account.current_status_id =status.idwhere user_account.user_name =user_account.user_name =user_account.user_name =@user_word_user_ha. time и ifnull(user_has_role.role_end_time,"2200-01-01")>=@timeand status.is_user_working =Вярно
Очевидно това опростява заявката и води до по-добра производителност, но има по-голям проблем, който трябва да бъде решен. current_status_id
в user_account
таблицата трябва да бъде проверена и променена, ако е необходимо в следните ситуации:
- при всяко вмъкване/актуализиране/изтриване в
user_has_status
таблица - всеки ден в планирано събитие трябва да проверяваме дали състоянието на някого се е променило (текущо активно състояние е изтекло или/и някое бъдещо състояние е станало активно) и да го актуализираме съответно
Би било разумно да запазите стойности, които заявките ще използват често. По този начин ще избегнем да правим едни и същи проверки отново и отново и да разделим работата. Тук ще избегнем присъединяването към user_has_status
таблица и ще направим промени в current_status_id
само когато се случат (вмъкване/актуализиране/изтриване) или когато системата не се използва толкова много (планираните събития обикновено се изпълняват, когато повечето потребители не използват системата). Може би в този случай няма да спечелим много от current_status_id
но гледайте на това като на идея, която може да помогне в подобни ситуации.