Circle & Invitations

circle module
Tính năng: Create circle · Send Invitation · Accept/Reject Invitation  ·  Table: Circle · CircleMember · CircleInvitation
3
Models
Circle / Member / Invitation
4
Routes
List / Create / Invite / Answer
TX
Transaction
transactionService
RBAC
Role Check
ADMIN | OWNER

Flow: Circle Invitation & Permissions


POST /invitation (Inviter) Gửi lời mời cho user khác Role in Circle? MEMBER/NONE Throw Error ADMIN/OWNER Already in? Throw Error Create Invitation PATCH /invitation (Invitee) Phản hồi lời mời (Accept/Reject) Decision? ACCEPT TX: Update Status = ACCEPTED + Create CircleMember REJECT TX: Update Status = REJECTED

Luồng cấp quyền: Chỉ ADMIN/OWNER của Circle mới được mời. Người được mời có quyền Accept (sẽ tạo member) hoặc Reject (chỉ đổi status).

Implementation Checklist (2 Recent Commits)


#TaskChi tiếtStatus
01
Refactor Database Schema
Cập nhật Prisma Schema: thêm enum InvitationRequestStatus, map lại các relations cho CircleInvitation.done
02
Cursor Pagination
Áp dụng `ICursorPagination` cho bảng CircleInvitation, dùng thư viện buildPagination.done
03
Transaction Support
Thay thế prisma.$transaction bằng transactionService.doInTransaction trong Service layer để đảm bảo quy tắc Clean Architecture.done
04
Circle Permission Check
Kiểm tra role (ADMIN/OWNER) trước khi cho phép tạo lời mời. Sử dụng guard clause hợp lý.done

🔍 Code Review — Bugs & Issues Found in Circle Invitation Logic


#SeverityIssueChi tiết & Cách FixStatus
C1fixed
Lỗ hổng Logic: REJECTED vẫn bị thêm vào nhóm
circle.service.ts : acceptInvitation
Controller có truyền status xuống Service, nhưng Service hoàn toàn phớt lờ biến này. Nó luôn gọi circleRepository.acceptInvitation (hardcode status = ACCEPTED) và luôn tạo CircleMember. Nếu user Reject, họ vẫn bị thêm vào Circle! ✅ Đã fix — Service giờ rẽ nhánh if/else dựa trên status, reject chỉ update status, accept mới tạo CircleMember.done
C2fixed
Thiếu return trong sendInvitation
circle.service.ts : sendInvitation
Block transactionService.doInTransactionawait create(...) nhưng thiếu chữ return đằng trước. Do đó API sẽ trả về dữ liệu undefined thay vì object invitation mới được tạo. ✅ Đã fix — upsert trả về giá trị qua return.done
C3fixed
Sai Responsibility Layer (Kiến trúc)
circle.repository.ts
Các hàm liên quan đến bảng Invitation (findInvitation, acceptInvitation) lại được định nghĩa trong circleRepository. Nên chuyển chúng sang circleInvitationRepository cho đúng domain. ✅ Đã tách — circleInvitationRepository giờ chứa toàn bộ logic invitation.done