Theo yêu cầu của bạn, tôi đã tách tài liệu làm 2 phần riêng biệt: Stored Procedures (SPs) chuyên dùng cho Thao tác dữ liệu (Insert, Update, Delete) và Views chuyên dùng cho Truy vấn dữ liệu (Select).
(Giữ nguyên toàn bộ nội dung và logic bảo mật của bạn ở các mục 1, 2, 3, 4, 5)
sp_RegisterUser: Nhận tham số email và password_hash (đã băm từ client/server). Không bao giờ nhận password rõ. sp_UpdateProfile: Cho phép cập nhật full_name, bio, avatar_url. Ràng buộc: Chỉ cập nhật đúng dòng có id trùng với ID của người đang đăng nhập. sp_UpdatePrivacySettings: Cho phép người dùng tùy chỉnh ai được nhắn tin, ai thấy trạng thái online. (Đoé làm)
Lưu ý bảo mật quan trọng: Các SP trong nhóm này tuyệt đối chỉ nhận chuỗi mã hóa (Ciphertext) cho nội dung tin nhắn, tuân thủ nguyên tắc "CSDL không biết người dùng chat gì". sp_SendMessage: Thêm tin nhắn mới. Logic bảo mật: Kiểm tra xem sender_id có bị receiver_id chặn (bảng UserBlocks) hay không. Nếu có, từ chối INSERT. sp_EditMessage: Cập nhật lại cột content (bản mã mới) và đổi cờ is_edited = TRUE. Logic bảo mật: Tham số truyền vào phải có @sender_id. Lệnh UPDATE phải có điều kiện WHERE id = @message_id AND sender_id = @sender_id để ngăn chặn hacker gọi API sửa tin nhắn của người khác. sp_RevokeMessage (Thu hồi tin nhắn): Logic bảo mật: Áp dụng "Soft Delete". Thay vì dùng lệnh DELETE, SP này chạy lệnh UPDATE messages SET is_deleted = TRUE, deleted_for_all = TRUE WHERE id = @message_id AND sender_id = @sender_id.
sp_CreateGroupConversation: Tạo nhóm mới, tự động gán người tạo làm owner trong bảng Participants. sp_AddGroupMember: Thêm thành viên mới. Logic bảo mật: Query kiểm tra role của người mời trong bảng Participants. Chỉ cho phép thực thi INSERT nếu người mời là owner hoặc admin. sp_KickGroupMember: Xóa thành viên. Logic bảo mật: Tương tự, kiểm tra quyền. Đảm bảo member không thể kích admin. sp_ChangeMemberRole: Trưởng nhóm thăng cấp/giáng cấp phó nhóm.
sp_PinMessage: Ghim tin nhắn. Cần tham gia logic RBAC để xem người ghim có quyền trong nhóm hay không trước khi INSERT vào bảng PinnedMessages. sp_LogCallHistory: Lưu lại siêu dữ liệu cuộc gọi (thời lượng, thời gian bắt đầu/kết thúc) vào bảng Calls. Chỉ lưu metadata, không lưu nội dung. sp_BlockUser: Thêm bản ghi vào bảng UserBlocks để chặn người dùng.
sp_AdminLockAccount / sp_AdminUnlockAccount: Thay đổi trạng thái is_active của User. Logic bảo mật bổ sung: Bên trong SP này có thể gọi thêm một lệnh INSERT INTO Audit_Log để ghi nhận lại nhật ký kiểm toán (như lý thuyết Auditing ở mục 2.2.5). Đéo biết làm =))
sp_CreateDirectConversation: Tạo cuộc trò chuyện 2 người. Logic: Tạo conversation mới type = 'direct' và thêm 2 bản ghi vào bảng participants.
sp_UpdateConversation: Cập nhật thông tin hội thoại. Logic: Cập nhật tên nhóm hoặc ảnh đại diện của nhóm trong bảng conversations.
sp_DeleteConversation: Xóa cuộc trò chuyện. Logic: Cập nhật trạng thái is_active = FALSE hoặc xóa bản ghi trong bảng conversations.
sp_CreateParticipant: Thêm thành viên vào cuộc trò chuyện. Logic: Thêm bản ghi mới vào bảng participants với user_id và conversation_id.
sp_CreateFriendRequest: Gửi lời mời kết bạn. Logic: Thêm bản ghi vào bảng friend_requests với status là 'pending'.
sp_UpdateFriendRequestStatus: Chấp nhận hoặc từ chối kết bạn. Logic: Cập nhật status trong bảng friend_requests. Nếu accepted thì thêm bản ghi vào bảng friends cho cả 2 phía.
sp_DeleteFriend: Hủy kết bạn. Logic: Xóa các bản ghi liên quan trong bảng friends cho cả 2 phía.
sp_AddMessageReaction: Thả cảm xúc (emoji) vào tin nhắn. Logic: INSERT vào bảng message_reactions.
sp_RemoveMessageReaction: Gỡ cảm xúc khỏi tin nhắn. Logic: DELETE từ bảng message_reactions theo reaction_id hoặc (message_id AND user_id).
sp_UnpinMessage: Bỏ ghim tin nhắn trong nhóm. Logic: DELETE từ bảng pinned_messages WHERE message_id = @message_id.
sp_UpdateParticipant: Cập nhật cài đặt của thành viên trong phòng chat (nick_name, is_muted, last_read_message_id). Logic: UPDATE bảng participants WHERE id = @participant_id.
sp_CreateGroupJoinRequest: Gửi yêu cầu tham gia nhóm. Logic: Thêm bản ghi vào bảng group_join_requests với status 'pending'.
sp_UpdateGroupJoinRequestStatus: Duyệt hoặc từ chối yêu cầu tham gia nhóm. Logic: Cập nhật status trong group_join_requests. Nếu duyệt thì thêm user vào bảng participants.
sp_UnblockUser: Bỏ chặn người dùng. Logic: DELETE từ bảng user_blocks WHERE blocker_id = @blocker_id AND blocked_id = @blocked_id.
sp_StartCall: Khởi tạo cuộc gọi. Logic: INSERT vào bảng calls với trạng thái 'ringing' hoặc 'started'.
sp_UpdateCallStatus: Cập nhật trạng thái cuộc gọi. Logic: UPDATE bảng calls SET status = @status, ended_at = NOW() (nếu kết thúc).
sp_UpdateUserStatus: Cập nhật trạng thái hoạt động (Online/Offline). Logic: UPDATE bảng users SET status = @status WHERE id = @user_id.
sp_UpdateFriend: Cập nhật ghi chú hoặc trạng thái yêu thích của bạn bè. Logic: UPDATE bảng friends SET notes = @notes, is_favorite = @is_favorite WHERE user_id = @user_id AND friend_id = @friend_id.
Note
Vì View trong SQL không nhận tham số trực tiếp như SP, nên khi gọi View từ code, bạn sẽ dùng lệnh SELECT kết hợp với mệnh đề WHERE để lọc dữ liệu theo nhu cầu (ví dụ: SELECT * FROM vw_GetMessages WHERE conversation_id = @id).
vw_GetConversations: Lấy danh sách cuộc trò chuyện. Logic: SELECT JOIN bảng conversations và participants để lấy danh sách các phòng chat mà user tham gia.
vw_GetMessages: Lấy danh sách tin nhắn trong một cuộc trò chuyện. Logic: SELECT từ bảng messages ORDER BY created_at DESC (Code sẽ tự thêm LIMIT/OFFSET).
vw_GetUnreadMessages: Lấy danh sách các tin nhắn chưa đọc. Logic: SELECT từ bảng messages JOIN participants để so sánh created_at với last_read_message_id.
vw_CountUnreadMessages: Đếm số tin nhắn chưa đọc của user. Logic: SELECT COUNT từ bảng messages lớn hơn mốc đã đọc.
vw_GetHomeMessagesMedia: Lấy tất cả file, ảnh, media trong cuộc trò chuyện. Logic: SELECT từ bảng messages WHERE message_type IN ('image', 'file', 'video').
vw_GetFriendRequests: Lấy danh sách lời mời kết bạn đã nhận (Đang chờ xử lý). Logic: SELECT từ bảng friend_requests WHERE status = 'pending'.
vw_GetSentFriendRequests: Lấy danh sách lời mời kết bạn đã gửi. Logic: SELECT từ bảng friend_requests.
vw_GetFriends: Lấy danh sách bạn bè của User. Logic: SELECT từ bảng friends JOIN với bảng users để lấy thông tin bạn bè.
vw_GetMessageReactions: Lấy danh sách cảm xúc của các tin nhắn. Logic: SELECT từ bảng message_reactions.
vw_GetPinnedMessages: Lấy danh sách tin nhắn đã ghim của cuộc trò chuyện. Logic: SELECT từ bảng pinned_messages JOIN messages.
vw_GetParticipants: Lấy danh sách thành viên của cuộc trò chuyện. Logic: SELECT từ bảng participants JOIN users.
vw_GetGroupJoinRequests: Lấy danh sách yêu cầu tham gia nhóm. Logic: SELECT từ bảng group_join_requests.
vw_GetBlockedUsers: Lấy danh sách người dùng đã chặn. Logic: SELECT từ bảng user_blocks.
vw_SearchUsersAndGroups: Tìm kiếm người dùng và nhóm. Logic: SELECT UNION từ bảng users và conversations (type='group').
vw_GetUsersByIds: Lấy thông tin chi tiết của nhiều người dùng cùng lúc.
Logic: SELECT từ bảng users. (Dùng WHERE id IN (...) khi gọi).
Tôi đã rà soát lại code trong các file Service (ví dụ usersService.js, messagesService.js) và so sánh với danh sách SP/View hiện có. Dưới đây là kết quả đánh giá cái nào Thiếu, Đủ, hoặc Cần sửa SP để khớp với code (vì nguyên tắc là giữ nguyên code, chỉ sửa SP):
-
Thao tác
createUser(Đăng ký/Tạo user):- SP hiện tại:
sp_RegisterUser - Đánh giá:
⚠️ Thiếu. Code hiện tại có thể truyền lên nhiều thông tin hơn (nhưfull_name,is_email_verifiedkhi đăng nhập bằng Google). Trong khisp_RegisterUserchỉ nhận@Emailvà@PasswordHash. - Đề xuất sửa SP: Cần sửa
sp_RegisterUserđể nhận thêm các tham số tùy chọn như@FullName NVARCHAR(250) = NULLvà@IsEmailVerified BIT = 0.
- SP hiện tại:
-
Thao tác
updateUser(Cập nhật Profile):- SP hiện tại:
sp_UpdateProfile - Đánh giá:
⚠️ Thiếu linh hoạt. Code dùng Sequelize có thể cập nhật bất kỳ trường nào đơn lẻ (ví dụ chỉ cập nhậtavatar_url). SPsp_UpdateProfilebắt buộc truyền đủ tất cả các trường. - Đề xuất sửa SP: Sửa các tham số trong
sp_UpdateProfilethành có giá trị mặc định làNULLvà dùng hàmISNULL(@Param, column_name)trong câu lệnhUPDATEđể chỉ cập nhật những trường được truyền vào.
- SP hiện tại:
-
Thao tác
deleteUser(Xóa user):- SP hiện tại:
sp_AdminLockAccount - Đánh giá: ✅ Đủ. Code chỉ đổi
is_active = false, SP cũng làm vậy.
- SP hiện tại:
-
Thao tác
getUserByEmail/getUserById:- View hiện tại:
vw_GetUsersByIds - Đánh giá:
⚠️ Thiếu. Cần một View chung để lấy thông tin user hoặc cụ thể theo Email. - Đề xuất sửa View: Tạo
vw_GetUsers(Lấy tất cả user) để code có thể tự thêmWHERE email = ...khi gọi.
- View hiện tại:
-
Thao tác
createMessage(Gửi tin nhắn):- SP hiện tại:
sp_SendMessage - Đánh giá:
⚠️ Thiếu. Hệ thống của bạn có tính năng E2EE (Mã hóa đầu cuối). Code cần lưuis_e2eevàkey_versionvào bảngmessages. SPsp_SendMessagehiện tại không nhận 2 tham số này. - Đề xuất sửa SP: Thêm
@IsE2EE BIT = 0và@KeyVersion INT = NULLvàosp_SendMessage.
- SP hiện tại:
-
Thao tác
getUnreadMessages(Lấy tin nhắn chưa đọc):- View hiện tại:
vw_GetUnreadMessages - Đánh giá: ✅ Đủ. View này join với participants để so sánh thời gian là hợp lý.
- View hiện tại:
-
Thao tác
getAllMessagesByConversationId(Lấy lịch sử tin nhắn):- View hiện tại:
vw_GetMessages - Đánh giá:
⚠️ Thiếu logic E2EE. Code có logic kiểm trakey_versioncủa user có khớp với tin nhắn E2EE hay không. - Đề xuất sửa View: Giữ nguyên View
vw_GetMessagesđơn giản, nhưng khi code gọi View này, cần truyền thêm mệnh đềWHEREphức tạp chứa logic kiểm trakey_version(như code hiện tại đang làm).
- View hiện tại:
-
Thao tác
updateKeyStatus(Cập nhật trạng thái Key E2EE):- SP hiện tại: Không có.
- Đánh giá: Code cần cập nhật
key_statuscho conversation khi thêm/xóa thành viên. - Đề xuất: Tạo mới
sp_UpdateKeyStatushoặc tích hợp vàosp_UpdateConversation.
-
Thao tác
resetPassword(Đổi mật khẩu):- SP hiện tại: Không có.
- Đánh giá: Code cần cập nhật
password_hashcho user.sp_UpdateProfilehiện tại không có tham số này. - Đề xuất: Tạo mới
sp_SetPasswordhoặc thêm@PasswordHashvàosp_UpdateProfile.
-
Thao tác
createFriendRequest(Gửi lời mời kết bạn):- SP hiện tại:
sp_CreateFriendRequest - Đánh giá:
⚠️ Thiếu. SP hiện tại không nhận tham sốnote(lời nhắn), và không xử lý trường hợp Upsert (nếu đã bị từ chối trước đó thì cập nhật lại thànhpending). - Đề xuất sửa SP: Thêm
@Note NVARCHAR(250)và sửa logic thành Upsert.
- SP hiện tại:
-
Thao tác
updateFriendRequestStatus(Duyệt kết bạn):- SP hiện tại:
sp_UpdateFriendRequestStatus - Đánh giá:
⚠️ Khác biệt tham số. Code tìm request theoid, trong khi SP tìm theosender_idvàreceiver_id. - Đề xuất sửa SP: Chuyển sang nhận
@RequestId UNIQUEIDENTIFIERđể khớp với code.
- SP hiện tại:
-
Thao tác
createFriendByUserId(Thêm bạn trực tiếp):- SP hiện tại: Không có SP tạo trực tiếp (thường tạo qua Duyệt kết bạn).
- Đánh giá: Code tạo thẳng bản ghi trong bảng
friendskèmnotesvàis_favorite. - Đề xuất: Tạo
sp_AddFriendhỗ trợ tạo trực tiếp kèm ghi chú.
-
Thao tác
createParticipant(Thêm/Tái kích hoạt thành viên):- SP hiện tại:
sp_AddGroupMember - Đánh giá:
⚠️ Thiếu logic Upsert. Code xử lý trường hợp user đã từng rời nhóm thì kích hoạt lại (left_at = null). SP hiện tại báo lỗi nếu user đã tồn tại. - Đề xuất sửa SP: Sửa logic
sp_AddGroupMemberthành Upsert (nếu đã tồn tại thì cập nhậtleft_at = null).
- SP hiện tại:
-
Thao tác
leaveConversation(Rời nhóm):- SP hiện tại: Không có.
- Đánh giá: Code cập nhật
left_at = nowvà đổikey_status. - Đề xuất: Tạo mới
sp_LeaveConversation.
-
Thao tác
kickMember(Kích thành viên):- SP hiện tại:
sp_KickGroupMember - Đánh giá:
⚠️ Sai logic xóa. SP hiện tại dùngDELETE(xóa cứng), trong khi code dùngUPDATE left_at(xóa mềm). - Đề xuất sửa SP: Sửa lệnh
DELETEthànhUPDATE participants SET left_at = CURRENT_TIMESTAMP.
- SP hiện tại:
-
Thao tác
deleteUserBlock(Bỏ chặn):- SP hiện tại:
sp_UnblockUser - Đánh giá:
⚠️ Khác biệt tham số. Code tìm bản ghi theoidđể xóa, trong khi SP tìm theoblocker_idvàblocked_id. - Đề xuất sửa SP: Thêm hỗ trợ nhận
@Idđể xóa.
- SP hiện tại:
-
Thao tác
deletePinnedMessage(Bỏ ghim):- SP hiện tại:
sp_UnpinMessage - Đánh giá:
⚠️ Khác biệt tham số. Code tìm bản ghi theoid(ID của pin record) để xóa, trong khi SP tìm theomessage_id. - Đề xuất sửa SP: Thêm hỗ trợ nhận
@Idđể xóa.
- SP hiện tại:
-
Thao tác
removeMessageReaction(Xóa cảm xúc):- SP hiện tại:
sp_RemoveMessageReaction - Đánh giá:
⚠️ Khác biệt tham số. Code tìm bản ghi theoid(ReactionID) để xóa, trong khi SP tìm theomessage_idvàuser_id. - Đề xuất sửa SP: Thêm hỗ trợ nhận
@Idđể xóa.
- SP hiện tại:
- Thao tác
updateStatusCall(Cập nhật trạng thái cuộc gọi):- SP hiện tại:
sp_UpdateCallStatus - Đánh giá:
⚠️ Thiếu logic. Code cập nhậtstarted_at = CURRENT_TIMESTAMPkhi trạng thái là 'ongoing'. SP hiện tại chỉ cập nhậtstatusvàupdated_atcho các trạng thái không phải kết thúc. - Đề xuất sửa SP: Thêm điều kiện nếu
@Status = 'ongoing'thì cập nhật cảstarted_at = CURRENT_TIMESTAMP.
- SP hiện tại: