Sửa đổi dữ liệu của hệ thống theo ARCHITECTURE.md

This commit is contained in:
2026-06-13 19:52:08 +07:00
parent 7fc80a49d0
commit b54707823c
33 changed files with 655 additions and 275 deletions
+142
View File
@@ -0,0 +1,142 @@
-- CreateSchema
CREATE SCHEMA IF NOT EXISTS "public";
-- CreateEnum
CREATE TYPE "ParticipantRole" AS ENUM ('OWNER', 'MANAGER', 'MEMBER', 'MEMBER_NO_FINANCE', 'VIEWER_ONLY');
-- CreateEnum
CREATE TYPE "ExpenseCategory" AS ENUM ('ACCOMMODATION', 'FOOD', 'TRANSPORT', 'TICKET', 'OTHER');
-- CreateEnum
CREATE TYPE "LocationStatus" AS ENUM ('PENDING', 'COMPLETED');
-- CreateEnum
CREATE TYPE "LocationType" AS ENUM ('MOVE', 'VISIT', 'REST', 'EAT');
-- CreateEnum
CREATE TYPE "PrivacyLevel" AS ENUM ('PUBLIC', 'TOUR_ONLY', 'PRIVATE');
-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL,
"email" TEXT NOT NULL,
"passwordHash" TEXT NOT NULL,
"name" TEXT,
"avatar" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"isAdmin" BOOLEAN NOT NULL DEFAULT false,
"isBlocked" BOOLEAN NOT NULL DEFAULT false,
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Tour" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"startDate" TIMESTAMP(3),
"endDate" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"totalCost" DECIMAL(15,2) NOT NULL DEFAULT 0,
"createdById" TEXT NOT NULL,
CONSTRAINT "Tour_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "TourParticipant" (
"tourId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"role" "ParticipantRole" NOT NULL DEFAULT 'MEMBER',
CONSTRAINT "TourParticipant_pkey" PRIMARY KEY ("tourId","userId")
);
-- CreateTable
CREATE TABLE "Leg" (
"id" TEXT NOT NULL,
"tourId" TEXT NOT NULL,
"sequence" INTEGER NOT NULL,
"note" TEXT,
CONSTRAINT "Leg_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Location" (
"id" TEXT NOT NULL,
"legId" TEXT NOT NULL,
"name" TEXT NOT NULL,
"address" TEXT,
"latitude" DOUBLE PRECISION NOT NULL,
"longitude" DOUBLE PRECISION NOT NULL,
"plannedStart" TIMESTAMP(3),
"plannedEnd" TIMESTAMP(3),
"actualStart" TIMESTAMP(3),
"actualEnd" TIMESTAMP(3),
"status" "LocationStatus" NOT NULL DEFAULT 'PENDING',
"type" "LocationType" NOT NULL DEFAULT 'VISIT',
CONSTRAINT "Location_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Expense" (
"id" TEXT NOT NULL,
"legId" TEXT NOT NULL,
"locationId" TEXT,
"category" "ExpenseCategory" NOT NULL,
"amount" DECIMAL(15,2) NOT NULL,
"currency" TEXT NOT NULL DEFAULT 'VND',
"description" TEXT,
CONSTRAINT "Expense_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Photo" (
"id" TEXT NOT NULL,
"tourId" TEXT NOT NULL,
"locationId" TEXT,
"uploaderId" TEXT NOT NULL,
"imageUrl" TEXT NOT NULL,
"capturedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"metadata" JSONB,
"privacy" "PrivacyLevel" NOT NULL DEFAULT 'TOUR_ONLY',
CONSTRAINT "Photo_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
-- AddForeignKey
ALTER TABLE "Tour" ADD CONSTRAINT "Tour_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "TourParticipant" ADD CONSTRAINT "TourParticipant_tourId_fkey" FOREIGN KEY ("tourId") REFERENCES "Tour"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "TourParticipant" ADD CONSTRAINT "TourParticipant_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Leg" ADD CONSTRAINT "Leg_tourId_fkey" FOREIGN KEY ("tourId") REFERENCES "Tour"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Location" ADD CONSTRAINT "Location_legId_fkey" FOREIGN KEY ("legId") REFERENCES "Leg"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Expense" ADD CONSTRAINT "Expense_legId_fkey" FOREIGN KEY ("legId") REFERENCES "Leg"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Expense" ADD CONSTRAINT "Expense_locationId_fkey" FOREIGN KEY ("locationId") REFERENCES "Location"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Photo" ADD CONSTRAINT "Photo_tourId_fkey" FOREIGN KEY ("tourId") REFERENCES "Tour"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Photo" ADD CONSTRAINT "Photo_locationId_fkey" FOREIGN KEY ("locationId") REFERENCES "Location"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Photo" ADD CONSTRAINT "Photo_uploaderId_fkey" FOREIGN KEY ("uploaderId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+150
View File
@@ -0,0 +1,150 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
}
// --- Enums ---
enum ParticipantRole {
OWNER
MANAGER
MEMBER
MEMBER_NO_FINANCE
VIEWER_ONLY
}
enum ExpenseCategory {
ACCOMMODATION
FOOD
TRANSPORT
TICKET
OTHER
}
enum LocationStatus {
PENDING
COMPLETED
}
enum LocationType {
MOVE
VISIT
REST
EAT
}
enum PrivacyLevel {
PUBLIC
TOUR_ONLY
PRIVATE
}
// --- Models ---
model User {
id String @id @default(uuid())
email String @unique
passwordHash String
name String?
avatar String?
createdAt DateTime @default(now())
isAdmin Boolean @default(false)
isBlocked Boolean @default(false)
createdTours Tour[] @relation("TourCreator")
tourParticipations TourParticipant[]
uploadedPhotos Photo[]
}
model Tour {
id String @id @default(uuid())
title String
startDate DateTime?
endDate DateTime?
createdAt DateTime @default(now())
totalCost Decimal @default(0) @db.Decimal(15, 2)
createdById String
creator User @relation("TourCreator", fields: [createdById], references: [id])
participants TourParticipant[]
legs Leg[]
photos Photo[]
}
model TourParticipant {
tourId String
userId String
role ParticipantRole @default(MEMBER)
tour Tour @relation(fields: [tourId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@id([tourId, userId])
}
model Leg {
id String @id @default(uuid())
tourId String
sequence Int
note String? @db.Text
tour Tour @relation(fields: [tourId], references: [id], onDelete: Cascade)
locations Location[]
expenses Expense[]
}
model Location {
id String @id @default(uuid())
legId String
name String
address String?
latitude Float
longitude Float
plannedStart DateTime?
plannedEnd DateTime?
actualStart DateTime?
actualEnd DateTime?
status LocationStatus @default(PENDING)
type LocationType @default(VISIT)
leg Leg @relation(fields: [legId], references: [id], onDelete: Cascade)
expenses Expense[]
photos Photo[]
}
model Expense {
id String @id @default(uuid())
legId String
locationId String?
category ExpenseCategory
amount Decimal @db.Decimal(15, 2)
currency String @default("VND")
description String? @db.Text
leg Leg @relation(fields: [legId], references: [id], onDelete: Cascade)
location Location? @relation(fields: [locationId], references: [id], onDelete: Cascade)
}
model Photo {
id String @id @default(uuid())
tourId String
locationId String?
uploaderId String
imageUrl String
capturedAt DateTime @default(now())
metadata Json?
privacy PrivacyLevel @default(TOUR_ONLY)
tour Tour @relation(fields: [tourId], references: [id], onDelete: Cascade)
location Location? @relation(fields: [locationId], references: [id], onDelete: SetNull)
uploader User @relation(fields: [uploaderId], references: [id])
}