Quản lý Người dùng v1.0.0
Mã nguồn: src/services/user.service.ts
Mô hình dữ liệu
Cách dùng PolicyDefinition
| Mục đích | variant | subjectType | targetType |
|---|---|---|---|
| Gán role cho user | group | User | Role |
| Map user với organizer | group | User | Organizer |
| Map user với merchant | group | User | Merchant |
| Cấp permission cho role | policy | Role | Permission |
| Cấp permission cho user | policy | User | Permission |
PolicyDefinition thay thế các bảng cũ
UserRolevàUserMappingbằng một mô hình liên kết duy nhất theo phong cách Casbin.
Phụ thuộc của UserService
| Phụ thuộc | Mục đích |
|---|---|
JWKSIssuerTokenService | Các thao tác với JWT token |
UserConfigurationService | Cấu hình mặc định sau khi tạo |
UserRepository | CRUD và transaction cho User |
UserCredentialRepository | Lưu trữ mật khẩu |
UserIdentifierRepository | Kiểm tra trùng lặp identifier + CRUD |
UserProfileRepository | CRUD cho profile |
PolicyDefinitionRepository | Gán role/organizer/merchant |
Luồng tạo User
Xác thực Identifier khi tạo
| Scheme | verified | Có thể đăng nhập ngay |
|---|---|---|
USERNAME | true | Có |
EMAIL | false | Không — cần xác thực |
PHONE_NUMBER | false | Không — cần xác thực |
Xử lý Credential
- Nếu có
credential→ được hash bằngBun.password.hash()(Argon2id), lưu với schemeBASIC - Nếu
credentialkhông được cung cấp → bỏ qua bước này hoàn toàn (ví dụ: khách hàng không cần đăng nhập)
Luồng cập nhật User
Cập nhật được thực hiện theo transaction với các thao tác con chạy song song:
Logic cập nhật Identifier
| Hành động | Chi tiết |
|---|---|
| Thêm | Identifier mới chưa có trong DB → tạo mới với verified: false |
| Xóa | Identifier có trong DB nhưng không có trong request → soft-delete |
| Username | Không cập nhật được qua phương thức này |
Logic cập nhật Role
So sánh diff với các bản ghi PolicyDefinition:
- Lấy các policy GROUP (USER→ROLE) hiện tại
- Thêm: roleIds có trong request nhưng không có trong DB → tạo policy mới
- Xóa: policy có trong DB nhưng roleId không có trong request → soft-delete
Quản lý Employee
Mã nguồn: src/services/employee.service.ts
Tạo Employee
| Kiểm tra | Nội dung | Lỗi |
|---|---|---|
| Quyền truy cập organizer | CurrentUser có PolicyDef map tới organizerId | 403 |
| Quyền sở hữu merchant | Tất cả merchantIds thuộc về organizer | 403 |
Cập nhật Employee
- Ủy quyền các trường thuộc user cho
UserService.updateById() - Mapping merchant: chiến lược xóa hết rồi tạo lại thông qua PolicyDefinitionRepository
Schema Request
CreateEmployeeRequest = CreateUserRequest + { organizerId, merchantIds[] }
UpdateByIdEmployeeRequest = UpdateByIdUserRequest + { merchantIds[]? } (tất cả đều partial)
Phân quyền tại Employee Controller
Tất cả thao tác được giới hạn phạm vi qua UserRepository.findIdsByPoliciesForCurrentUser():
| Method | Phạm vi |
|---|---|
find / count / findOne | Lọc theo employee IDs đã giới hạn theo policy + tham số organizerId/merchantIds tùy chọn |
findById / deleteById | Kiểm tra ID có nằm trong tập đã giới hạn theo policy |
create | Xác thực quyền sở hữu organizer trong EmployeeService |
updateById | Xác thực quyền sở hữu organizer trong EmployeeService |
User Controller
| Route | Method | Mô tả |
|---|---|---|
/users | GET | Danh sách users (phân trang) |
/users/:id | GET | Lấy theo ID |
/users | POST | Tạo → UserService.create() |
/users/:id | PATCH | Cập nhật → UserService.updateById() |
/users/:id | DELETE | Soft-delete |
/users/count | GET | Đếm số lượng |
/users/profile | GET | Profile của user hiện tại |
/users/profile | PATCH | Cập nhật profile của user hiện tại |
Schema Request
CreateUserRequest
| Trường | Kiểu | Bắt buộc | Validation |
|---|---|---|---|
username | string | Không | 4–80 ký tự |
credential | string | Không | 4–80 ký tự, PASSWORD_PATTERN |
emails | string[] | Có | tối thiểu 1, email hợp lệ |
phones | string[] | Có | tối thiểu 1, định dạng E.164 |
status | enum | Có | ACTIVATED · DEACTIVATED · BLOCKED · UNKNOWN · ARCHIVED |
profile | object | Có | firstName, lastName, birthday?, locale? |
roleIds | string[] | Có | tối thiểu 1 |
UpdateByIdUserRequest (tất cả các trường đều .partial())
| Trường | Kiểu | Validation |
|---|---|---|
emails | string[] | tối thiểu 1 nếu cung cấp |
phones | string[] | tối thiểu 1 nếu cung cấp |
status | enum | tùy chọn |
profile | object | cập nhật một phần |
roleIds | string[] | tối thiểu 1 nếu cung cấp |
UpdateUserProfileRequest (cho PATCH /profile)
| Trường | Kiểu |
|---|---|
emails | string[]? |
phones | string[]? |
status | enum? |
profile | { firstName?, lastName?, birthday?, locale?, metadata? } |
UserConfiguration Service
Mã nguồn: src/services/user-configuration.service.ts
Được gọi trong _postCreate() sau khi tạo user:
UserConfiguration Controller
| Method | Path | Mô tả |
|---|---|---|
| CRUD | /user-configurations | Các thao tác chuẩn |
POST | /user-configurations/views | Tạo config từ template (kiểm tra trùng code/name theo từng user) |
Repositories
UserRepository
findIdsByPoliciesForCurrentUser(opts: {
currentUserId: string;
organizerId?: string;
merchantIds?: string[];
roleIdentifiers?: string[];
transaction?: ITransaction;
}): Promise<string[]>Logic SQL: PolicyDefinition (currentUser → Organizer) → JOIN PolicyDefinition (các user khác → cùng Organizer), tùy chọn lọc theo merchant/role → trả về danh sách User IDs distinct.
UserIdentifierRepository
validateIdentifierUniqueness(opts: {
identifiers: string[];
scheme?: string;
userId?: string; // exclude own identifiers during update
transaction?: ITransaction;
}): Promise<void> // throws if duplicates foundTrang liên quan
- Authentication — Luồng đăng ký/đăng nhập
- Customer Management — CRUD đặc thù cho Customer
- RBAC — Mô hình PolicyDefinition, phân cấp role
- Tổng quan Identity — Kiến trúc, các thành phần