Sửa lỗi tái cấu trúc thư mục và khai báo import
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ParticipantRole" AS ENUM ('OWNER', 'MANAGER', 'MEMBER', 'MEMBER_NO_FINANCE', 'VIEWER_ONLY');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "JoinRequestStatus" AS ENUM ('PENDING', 'ACCEPTED', 'REJECTED');
|
||||
|
||||
-- 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,
|
||||
"phone" TEXT,
|
||||
"address" 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 "JoinRequest" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tourId" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"requestedById" TEXT NOT NULL,
|
||||
"status" "JoinRequestStatus" NOT NULL DEFAULT 'PENDING',
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "JoinRequest_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,
|
||||
"leg_id" TEXT NOT NULL,
|
||||
"location_id" TEXT,
|
||||
"category" "ExpenseCategory" NOT NULL,
|
||||
"amount" DECIMAL(15,2) NOT NULL,
|
||||
"description" TEXT,
|
||||
"note" TEXT,
|
||||
"paid_by_id" 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");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "JoinRequest_tourId_status_idx" ON "JoinRequest"("tourId", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "JoinRequest_userId_idx" ON "JoinRequest"("userId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Tour" ADD CONSTRAINT "Tour_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "JoinRequest" ADD CONSTRAINT "JoinRequest_tourId_fkey" FOREIGN KEY ("tourId") REFERENCES "Tour"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "JoinRequest" ADD CONSTRAINT "JoinRequest_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "JoinRequest" ADD CONSTRAINT "JoinRequest_requestedById_fkey" FOREIGN KEY ("requestedById") REFERENCES "User"("id") ON DELETE CASCADE 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_leg_id_fkey" FOREIGN KEY ("leg_id") REFERENCES "Leg"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Expense" ADD CONSTRAINT "Expense_location_id_fkey" FOREIGN KEY ("location_id") REFERENCES "Location"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Expense" ADD CONSTRAINT "Expense_paid_by_id_fkey" FOREIGN KEY ("paid_by_id") REFERENCES "User"("id") ON DELETE SET NULL 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;
|
||||
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (e.g., Git)
|
||||
provider = "postgresql"
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { PrismaPg } from '@prisma/adapter-pg';
|
||||
import { Pool } from 'pg';
|
||||
|
||||
@Injectable()
|
||||
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
|
||||
constructor() {
|
||||
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
||||
const adapter = new PrismaPg(pool);
|
||||
super({ adapter });
|
||||
}
|
||||
|
||||
async onModuleInit() { await this.$connect(); }
|
||||
async onModuleDestroy() { await this.$disconnect(); }
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
// 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 JoinRequestStatus {
|
||||
PENDING
|
||||
ACCEPTED
|
||||
REJECTED
|
||||
}
|
||||
|
||||
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?
|
||||
phone String?
|
||||
address String?
|
||||
avatar String?
|
||||
createdAt DateTime @default(now())
|
||||
isAdmin Boolean @default(false)
|
||||
isBlocked Boolean @default(false)
|
||||
|
||||
createdTours Tour[] @relation("TourCreator")
|
||||
tourParticipations TourParticipant[]
|
||||
requestedJoinRequests JoinRequest[] @relation("JoinRequestUser")
|
||||
receivedJoinRequests JoinRequest[] @relation("JoinRequester")
|
||||
uploadedPhotos Photo[]
|
||||
paidExpenses Expense[] @relation("ExpensePaidBy")
|
||||
}
|
||||
|
||||
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[]
|
||||
joinRequests JoinRequest[]
|
||||
legs Leg[]
|
||||
photos Photo[]
|
||||
}
|
||||
|
||||
model JoinRequest {
|
||||
id String @id @default(uuid())
|
||||
tourId String
|
||||
userId String
|
||||
requestedById String
|
||||
status JoinRequestStatus @default(PENDING)
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
tour Tour @relation(fields: [tourId], references: [id], onDelete: Cascade)
|
||||
user User @relation("JoinRequestUser", fields: [userId], references: [id], onDelete: Cascade)
|
||||
requestedBy User @relation("JoinRequester", fields: [requestedById], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([tourId, status])
|
||||
@@index([userId])
|
||||
}
|
||||
|
||||
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 @map("leg_id")
|
||||
locationId String? @map("location_id")
|
||||
category ExpenseCategory
|
||||
amount Decimal @db.Decimal(15, 2)
|
||||
description String? @db.Text
|
||||
note String? @db.Text
|
||||
paidById String? @map("paid_by_id")
|
||||
|
||||
leg Leg @relation(fields: [legId], references: [id], onDelete: Cascade)
|
||||
location Location? @relation(fields: [locationId], references: [id], onDelete: Cascade)
|
||||
paidBy User? @relation("ExpensePaidBy", fields: [paidById], references: [id], onDelete: SetNull)
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
-- Kích hoạt extension PostGIS để xử lý tọa độ địa lý
|
||||
CREATE EXTENSION IF NOT EXISTS postgis;
|
||||
|
||||
-- 3.1. Users
|
||||
CREATE TABLE users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
name VARCHAR(100),
|
||||
avatar TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 3.2. Tours & Members
|
||||
CREATE TABLE tours (
|
||||
id SERIAL PRIMARY KEY,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
start_date DATE,
|
||||
end_date DATE,
|
||||
creator_id INTEGER REFERENCES users(id),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TYPE member_role AS ENUM ('OWNER', 'EDITOR', 'MEMBER_PLAN_ONLY', 'MEMBER_PHOTO_ONLY', 'VIEWER_EXTERNAL');
|
||||
|
||||
CREATE TABLE tour_members (
|
||||
tour_id INTEGER REFERENCES tours(id),
|
||||
user_id INTEGER REFERENCES users(id),
|
||||
role member_role DEFAULT 'MEMBER_PLAN_ONLY',
|
||||
PRIMARY KEY (tour_id, user_id)
|
||||
);
|
||||
|
||||
-- 3.3. Itinerary & Map
|
||||
CREATE TABLE legs (
|
||||
id SERIAL PRIMARY KEY,
|
||||
tour_id INTEGER REFERENCES tours(id),
|
||||
sequence_number INTEGER NOT NULL,
|
||||
notes TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE places (
|
||||
id SERIAL PRIMARY KEY,
|
||||
leg_id INTEGER REFERENCES legs(id),
|
||||
name VARCHAR(255),
|
||||
address TEXT,
|
||||
geom GEOMETRY(Point, 4326), -- PostGIS coordinates
|
||||
sequence_in_leg INTEGER,
|
||||
arrival_time TIMESTAMP,
|
||||
departure_time TIMESTAMP
|
||||
);
|
||||
|
||||
-- 3.4. Expenses
|
||||
CREATE TYPE expense_category AS ENUM ('LODGING', 'DINING', 'TRANSPORT', 'OTHER');
|
||||
|
||||
CREATE TABLE expenses (
|
||||
id SERIAL PRIMARY KEY,
|
||||
leg_id INTEGER REFERENCES legs(id),
|
||||
place_id INTEGER REFERENCES places(id),
|
||||
category expense_category,
|
||||
amount DECIMAL(15, 2) NOT NULL,
|
||||
currency VARCHAR(10) DEFAULT 'VND',
|
||||
description TEXT
|
||||
);
|
||||
|
||||
-- 3.5. Tasks
|
||||
CREATE TYPE trigger_type AS ENUM ('AUTO_BY_TIME', 'MANUAL_BY_USER');
|
||||
|
||||
CREATE TABLE tasks (
|
||||
id SERIAL PRIMARY KEY,
|
||||
tour_id INTEGER REFERENCES tours(id),
|
||||
leg_id INTEGER REFERENCES legs(id),
|
||||
title VARCHAR(255),
|
||||
planned_timestamp TIMESTAMP,
|
||||
is_completed BOOLEAN DEFAULT FALSE,
|
||||
completed_at TIMESTAMP,
|
||||
trigger_type trigger_type DEFAULT 'MANUAL_BY_USER'
|
||||
);
|
||||
Reference in New Issue
Block a user