Cơ sở dữ liệu
Thiết kế Multi-Schema
SmartCK Hub sử dụng PostgreSQL multi-schema để phân tách dữ liệu theo miền nghiệp vụ. Prisma hỗ trợ tính năng này qua preview feature multiSchema.
apps/backend/prisma/schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
schemas = ["accounting", "auth", "operations", "organization", "public"]
}
| Schema | Mô tả | Số models |
|---|---|---|
auth | Xác thực, phiên đăng nhập, token | ~7 |
organization | Phòng ban, chức vụ, nhân viên, POD | ~8 |
operations | Dự án, sprint, task, post, shooting | ~12 |
accounting | Tài chính POD, ngân sách media | ~4 |
public | Chung: settings, audit, permissions, menus | ~12 |
Tổng quan Models
Hệ thống có 43 models phân bổ qua 5 schemas. Các model chính:
Auth & Users
| Model | Schema | Mô tả |
|---|---|---|
User | auth | Tài khoản người dùng |
Profile | auth | Thông tin cá nhân chi tiết |
UserSession | auth | Phiên đăng nhập (JWT tracking) |
PasswordResetToken | auth | Token reset mật khẩu |
LoginAttempt | auth | Lịch sử đăng nhập (brute-force) |
Organization
| Model | Schema | Mô tả |
|---|---|---|
Department | organization | Phòng ban (hỗ trợ hierarchy) |
Position | organization | Chức vụ (gắn phòng ban + level) |
Pod | organization | Nhóm Agile POD |
PodMember | organization | Thành viên POD (role trong POD) |
UserDepartment | organization | Liên kết user-department (N:N) |
Operations
| Model | Schema | Mô tả |
|---|---|---|
Project | operations | Dự án |
Sprint | operations | Sprint trong dự án |
Task | operations | Công việc (gắn sprint, assign) |
Post | operations | Bài đăng (content calendar) |
ShootingSession | operations | Phiên chụp/quay |
Contract | operations | Hợp đồng |
Customer / CustomerBrand | operations | Khách hàng + brand |
SupportTicket | operations | Phiếu hỗ trợ |
NonBillableDeliverableRequest | operations | Yêu cầu ngoài hợp đồng |
Accounting
| Model | Schema | Mô tả |
|---|---|---|
PodFinancialReport | accounting | Báo cáo tài chính POD |
PodExpense | accounting | Chi phí POD |
MediaBudgetCategory | accounting | Danh mục ngân sách media |
MediaBudgetItem | accounting | Chi tiết ngân sách media |
Quan hệ chính
User ──┬── Profile (1:1)
├── UserDepartment ── Department (N:N)
├── UserRole ── Role ── RolePermission ── Permission
├── PodMember ── Pod
└── Task (assigned)
Contract ── Customer
── Project ── Sprint ── Task
── Post
── DeliverableService
Department ── Position (1:N)
── Department (self-ref hierarchy)
Lệnh quản lý Database
# Generate Prisma Client (chạy sau khi sửa schema)
pnpm db:generate
# Đẩy schema thay đổi lên DB (dev - không tạo migration)
pnpm db:push
# Tạo migration file (khi cần track thay đổi)
pnpm db:migrate
# Reset toàn bộ database + chạy lại seed
pnpm db:reset
# Mở Prisma Studio (GUI browser)
pnpm db:studio
# Seed dữ liệu mẫu
pnpm db:seed
db:push vs db:migrate
db:push: Nhanh, dùng khi phát triển. Không tạo migration file.db:migrate: Dùng khi cần lưu lịch sử thay đổi schema (staging/production).
Seed Data
Hệ thống có 22 seed files tại apps/backend/prisma/seeds/, chạy theo thứ tự:
permissions → roles → role-permissions → settings → menus
→ departments → positions → users → customers → customer-brands
→ service-packages → contracts → projects → sprints → task-types
→ tasks → posts → pods → shooting-sessions → support-tickets
→ media-budget-categories
Indexing Strategy
Prisma schema định nghĩa index tại từng model. Chiến lược indexing:
model Position {
// ...
@@index([departmentId]) // FK lookup
@@index([level]) // Filter theo cấp bậc
@@index([sortOrder], map: "idx_positions_sort") // Sắp xếp
@@index([level, departmentId]) // Composite index
@@index([status]) // Filter theo trạng thái
}
Nguyên tắc:
- Index trên mọi foreign key (FK lookup)
- Composite index cho các query filter phổ biến
- Index trên cột
status,createdAtcho filtering/sorting - Sử dụng
@@mapđể đặt tên index có ý nghĩa
Audit Trail
Model Audit ghi lại mọi thay đổi quan trọng:
model Audit {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
tableName String @map("table_name")
recordId String @map("record_id")
action String // CREATE, UPDATE, DELETE
oldData Json? @map("old_data")
newData Json? @map("new_data")
userId String? @map("user_id") @db.Uuid
createdAt DateTime @default(now())
}
Service AuditService tại src/shared/common/services/audit.service.ts tự động ghi log khi có thay đổi dữ liệu quan trọng.