Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Quản lý các vai trò và trạng thái trong hệ thống

Có nhiều cách để giải quyết vấn đề, và đó là trường hợp của việc quản lý các vai trò và trạng thái người dùng trong hệ thống phần mềm. Trong bài viết này, bạn sẽ tìm thấy sự phát triển đơn giản của ý tưởng đó cũng như một số mẹo và mẫu mã hữu ích.

Ý tưởng cơ bản

Trong hầu hết các hệ thống, thường cần phải có vai trò trạng thái của người dùng .

Các vai trò liên quan đến quyền mà người dùng có khi sử dụng hệ thống sau khi đăng nhập thành công. Ví dụ về các vai trò là “nhân viên trung tâm cuộc gọi”, “người quản lý trung tâm cuộc gọi”, “nhân viên văn phòng”, “người quản lý văn phòng” hoặc “người quản lý”. Nói chung, điều đó có nghĩa là người dùng sẽ có quyền truy cập vào một số chức năng nếu họ có vai trò thích hợp. Thật khôn ngoan khi giả định rằng một người dùng có thể có nhiều vai trò cùng một lúc.

Các trạng thái nghiêm ngặt hơn nhiều và chúng xác định xem người dùng có quyền đăng nhập vào hệ thống hay không. Người dùng chỉ có thể có một trạng thái tại một thời điểm. Ví dụ về các trạng thái sẽ là:"đang làm việc", "đang đi nghỉ", "nghỉ ốm", "hợp đồng đã kết thúc".

Khi thay đổi trạng thái của người dùng, chúng tôi vẫn có thể giữ nguyên tất cả các vai trò liên quan đến người dùng đó. Điều đó rất hữu ích vì hầu hết thời gian chúng tôi chỉ muốn thay đổi trạng thái của người dùng. Nếu người dùng làm nhân viên trung tâm cuộc gọi đi nghỉ, chúng tôi có thể chỉ cần thay đổi trạng thái của họ thành "đang đi nghỉ" và chuyển trạng thái đó về trạng thái "đang làm việc" khi anh ta trở lại.

Kiểm tra các vai trò và trạng thái trong khi đăng nhập cho phép chúng tôi quyết định điều gì sẽ xảy ra. Ví dụ:có thể chúng tôi muốn cấm đăng nhập ngay cả khi tên người dùng và mật khẩu chính xác. Chúng tôi có thể làm như vậy nếu trạng thái người dùng hiện tại không ngụ ý rằng anh ta đang làm việc hoặc nếu người dùng không có bất kỳ vai trò nào trong hệ thống.

Trong tất cả các mô hình được cung cấp bên dưới, các bảng statusrole đều giống nhau.

Bảng status có các trường idstatus_name và thuộc tính is_active . Nếu thuộc tính is_active được đặt thành “Đúng”, điều đó có nghĩa là người dùng có trạng thái đó hiện đang làm việc. Ví dụ:trạng thái “đang hoạt động” sẽ có thuộc tính is_active có giá trị là Đúng, trong khi các giá trị khác (“đi nghỉ”, “nghỉ ốm”, “hợp đồng đã kết thúc”) sẽ có giá trị là Sai.

Bảng vai trò chỉ có hai trường:idrole_name .

user_account bảng giống với user_account bảng được trình bày trong bài báo này. Chỉ trong mô hình đầu tiên mới có user_account bảng chứa hai thuộc tính bổ sung (role_idstatus_id ).

Một vài mô hình sẽ được trình bày. Tất cả chúng đều hoạt động và có thể được sử dụng nhưng có những ưu điểm và nhược điểm của chúng.

Mô hình đơn giản

Ý tưởng đầu tiên có thể là chúng tôi chỉ cần thêm các mối quan hệ khóa ngoại vào user_account bảng, tham chiếu đến bảng statusrole . Cả role_idstatus_id là bắt buộc.




Điều này khá đơn giản để thiết kế và cũng để xử lý dữ liệu với các truy vấn nhưng có một số nhược điểm:

  1. Chúng tôi không lưu giữ bất kỳ dữ liệu lịch sử (hoặc tương lai) nào.

    Khi chúng tôi thay đổi trạng thái hoặc vai trò, chúng tôi chỉ cần cập nhật status_idrole_id trong user_account bàn. Điều đó sẽ hoạt động tốt ngay bây giờ, vì vậy khi chúng tôi thực hiện thay đổi, nó sẽ phản ánh trong hệ thống. Điều này không sao cả nếu chúng ta không cần biết trạng thái và vai trò đã thay đổi như thế nào trong lịch sử. Ngoài ra, có một vấn đề là chúng tôi không thể thêm tương lai vai trò hoặc trạng thái mà không cần thêm bảng phụ vào mô hình này. Một tình huống mà chúng tôi có thể muốn có tùy chọn đó là khi chúng tôi biết rằng ai đó sẽ đi nghỉ bắt đầu từ thứ Hai tới. Một ví dụ khác là khi chúng tôi có một nhân viên mới; có thể chúng tôi muốn nhập địa vị và vai trò của anh ấy ngay bây giờ và để nó trở nên hợp lệ vào một thời điểm nào đó trong tương lai.

    Cũng có một sự phức tạp trong trường hợp chúng tôi có sự kiện đã lên lịch sử dụng các vai trò và trạng thái. Các sự kiện chuẩn bị dữ liệu cho ngày làm việc tiếp theo thường chạy trong khi hầu hết người dùng không sử dụng hệ thống (ví dụ:vào ban đêm). Vì vậy, nếu ai đó không làm việc vào ngày mai, chúng tôi sẽ phải đợi đến cuối ngày hiện tại và sau đó thay đổi vai trò và trạng thái của người đó nếu thích hợp. Ví dụ:nếu chúng tôi có nhân viên hiện đang làm việc và có vai trò “nhân viên trung tâm cuộc gọi”, họ sẽ nhận được danh sách khách hàng mà họ phải gọi. Nếu ai đó nhầm lẫn có trạng thái và vai trò đó, anh ta cũng sẽ có được khách hàng của mình và chúng tôi sẽ phải dành thời gian để sửa chữa nó.

  2. Người dùng chỉ có thể có một vai trò tại một thời điểm.

    Nói chung, người dùng có thể có nhiều hơn một vai trò trong hệ thống. Có thể tại thời điểm bạn đang thiết kế cơ sở dữ liệu, không cần những thứ như vậy. Hãy nhớ rằng những thay đổi trong quy trình / quy trình làm việc có thể xảy ra. Ví dụ, tại một thời điểm nào đó, khách hàng có thể quyết định hợp nhất hai vai trò thành một. Một giải pháp khả thi là tạo một vai trò mới và gán tất cả các chức năng từ các vai trò trước đó cho nó. Giải pháp khác (nếu người dùng có thể có nhiều hơn một vai trò) là máy khách chỉ cần gán cả hai vai trò cho những người dùng cần chúng. Tất nhiên giải pháp thứ hai đó thực tế hơn và mang lại cho khách hàng khả năng điều chỉnh hệ thống theo nhu cầu của mình nhanh hơn (điều này không được mô hình này hỗ trợ).

Mặt khác, mô hình này cũng có một lợi thế lớn so với các mô hình khác. Thật đơn giản và vì vậy, các truy vấn để thay đổi trạng thái và vai trò cũng sẽ đơn giản. Ngoài ra, một truy vấn kiểm tra xem người dùng có quyền đăng nhập vào hệ thống hay không đơn giản hơn nhiều so với các trường hợp khác:

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;

@ user_name và @password là các biến từ biểu mẫu đầu vào trong khi truy vấn trả về id của người dùng và role_id mà anh ta có. Trong trường hợp khi user_name hoặc mật khẩu không hợp lệ, cặp user_name và mật khẩu không tồn tại hoặc người dùng có trạng thái được chỉ định không hoạt động, thì truy vấn sẽ không trả về bất kỳ kết quả nào. Bằng cách đó, chúng tôi có thể cấm đăng nhập.

Mô hình này có thể được sử dụng trong các trường hợp:

  • chúng tôi chắc chắn rằng sẽ không có thay đổi nào trong quy trình yêu cầu người dùng phải có nhiều hơn một vai trò
  • chúng tôi không cần theo dõi các thay đổi về vai trò / trạng thái trong lịch sử
  • chúng tôi không mong đợi có nhiều vai trò / quản lý trạng thái.

Đã thêm thành phần thời gian

Nếu chúng tôi cần theo dõi vai trò và lịch sử trạng thái của người dùng, chúng tôi phải thêm nhiều vào nhiều mối quan hệ giữa user_accountroleuser_accountstatus . Tất nhiên, chúng tôi sẽ xóa role_idstatus_id từ user_account bàn. Các bảng mới trong mô hình là user_has_roleuser_has_status và tất cả các trường trong đó, ngoại trừ thời gian kết thúc, là bắt buộc.




Bảng user_has_role chứa dữ liệu về tất cả các vai trò mà người dùng từng có trong hệ thống. Khóa thay thế là (user_account_id , role_id , role_start_time ) vì không có ích gì khi gán cùng một vai trò cùng lúc cho một người dùng nhiều hơn một lần.

Bảng user_has_status chứa dữ liệu về tất cả các trạng thái mà người dùng đã từng có trong hệ thống. Khóa thay thế ở đây là (user_account_id , status_start_time ) bởi vì người dùng không thể có hai trạng thái bắt đầu cùng một lúc.

Thời gian bắt đầu không được để trống vì khi chúng ta chèn một vai trò / trạng thái mới, chúng ta biết thời điểm mà nó sẽ bắt đầu. Thời gian kết thúc có thể là trống trong trường hợp chúng tôi không biết khi nào vai trò / trạng thái sẽ kết thúc (ví dụ:vai trò có hiệu lực từ ngày mai cho đến khi điều gì đó xảy ra trong tương lai).

Bên cạnh việc có một lịch sử hoàn chỉnh, giờ đây chúng ta có thể thêm các trạng thái và vai trò trong tương lai. Nhưng điều này tạo ra sự phức tạp vì chúng tôi phải kiểm tra chồng chéo khi chúng tôi thực hiện chèn hoặc cập nhật.

Ví dụ, người dùng chỉ có thể có một trạng thái tại một thời điểm. Trước khi chèn một trạng thái mới, chúng ta phải so sánh thời gian bắt đầu và thời gian kết thúc của một trạng thái mới với tất cả các trạng thái hiện có cho người dùng đó trong cơ sở dữ liệu. Chúng ta có thể sử dụng một truy vấn như sau:

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@end_time là các biến chứa thời gian bắt đầu và thời gian kết thúc của một trạng thái mà chúng ta muốn chèn và @user_account_id là id người dùng mà chúng tôi chèn nó. @end_time có thể là null và chúng ta phải xử lý nó trong truy vấn. Vì mục đích này, các giá trị null được kiểm tra với ifnull() hàm số. Nếu giá trị là null, giá trị ngày cao sẽ được chỉ định (đủ cao để khi ai đó nhận thấy lỗi trong truy vấn, chúng tôi sẽ bỏ qua :). Truy vấn kiểm tra tất cả các kết hợp thời gian bắt đầu và thời gian kết thúc cho một trạng thái mới so với thời gian bắt đầu và thời gian kết thúc của các trạng thái hiện có. Nếu truy vấn trả về bất kỳ bản ghi nào, thì chúng ta có chồng chéo với các trạng thái hiện có và chúng ta nên cấm chèn trạng thái mới. Ngoài ra, sẽ rất tốt nếu bạn nêu ra một lỗi tùy chỉnh.

Nếu chúng tôi muốn kiểm tra danh sách các vai trò và trạng thái hiện tại (quyền người dùng), chúng tôi chỉ cần kiểm tra bằng cách sử dụng thời gian bắt đầu và thời gian kết thúc.

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@password là các biến từ biểu mẫu đầu vào while @time có thể được đặt thành Bây giờ (). Khi người dùng cố gắng đăng nhập, chúng tôi muốn kiểm tra các quyền của anh ta tại thời điểm đó. Kết quả là danh sách tất cả các vai trò mà người dùng có trong hệ thống trong trường hợp user_name và password khớp nhau và người dùng hiện có trạng thái hoạt động. Nếu người dùng có trạng thái hoạt động nhưng không có vai trò nào được chỉ định, truy vấn sẽ không trả về bất kỳ thứ gì.

Truy vấn này đơn giản hơn truy vấn trong phần 3 và mô hình này cho phép chúng tôi có lịch sử về các trạng thái và vai trò. Ngoài ra, chúng tôi có thể quản lý các trạng thái và vai trò trong tương lai và mọi thứ sẽ hoạt động tốt.

Mô hình cuối cùng

Đây chỉ là một ý tưởng về cách mô hình trước đó có thể được thay đổi nếu chúng tôi muốn cải thiện hiệu suất. Vì người dùng chỉ có thể có một trạng thái hoạt động tại một thời điểm nên chúng tôi có thể thêm status_id vào user_account bảng (current_status_id ). Bằng cách đó, chúng tôi có thể kiểm tra giá trị của thuộc tính đó và sẽ không phải tham gia user_has_status bàn. Truy vấn đã sửa đổi sẽ trông giống như sau:

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




Rõ ràng điều này đơn giản hóa truy vấn và dẫn đến hiệu suất tốt hơn nhưng có một vấn đề lớn hơn cần được giải quyết. current_status_id trong user_account bảng nên được kiểm tra và thay đổi nếu cần thiết trong các trường hợp sau:

  • trên mỗi lần chèn / cập nhật / xóa trong user_has_status bảng
  • hàng ngày trong một sự kiện đã lên lịch, chúng tôi nên kiểm tra xem trạng thái của ai đó có thay đổi không (trạng thái hiện hoạt động đã hết hạn hoặc / và một số trạng thái trong tương lai trở thành hoạt động) và cập nhật trạng thái đó cho phù hợp

Sẽ là khôn ngoan nếu lưu các giá trị mà các truy vấn sẽ sử dụng thường xuyên. Bằng cách đó, chúng tôi sẽ tránh thực hiện lặp đi lặp lại các lần kiểm tra giống nhau và chia nhỏ công việc. Ở đây, chúng tôi sẽ tránh tham gia user_has_status bảng và chúng tôi sẽ thực hiện các thay đổi trên current_status_id chỉ khi chúng xảy ra (chèn / cập nhật / xóa) hoặc khi hệ thống không được sử dụng nhiều (các sự kiện đã lên lịch thường chạy khi hầu hết người dùng không sử dụng hệ thống). Có thể trong trường hợp này, chúng tôi sẽ không thu được nhiều từ current_status_id nhưng hãy xem đây là một ý tưởng có thể hữu ích trong những trường hợp tương tự.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Ngưỡng tối ưu hóa - Dữ liệu nhóm và tổng hợp, Phần 1

  2. Sao lưu cơ sở dữ liệu SQL với VDP Advanced SQL Agent

  3. Lần cuối cùng, KHÔNG, bạn không thể tin tưởng IDENT_CURRENT ()

  4. Sai lầm về hiệu suất:Các biến bảng luôn ở trong bộ nhớ

  5. Kiểm tra tác động hiệu suất của khối lượng công việc Adhoc