Files
petlink_final/docs/04-base-de-dados.md
2026-05-04 09:43:36 +01:00

12 KiB

4. Modelação da Base de Dados

4.1 Diagrama de Entidade-Relacionamento (ERD)

┌──────────────┐       ┌──────────────────┐       ┌──────────────┐
│     User     │       │      Animal      │       │   Shelter    │
├──────────────┤       ├──────────────────┤       ├──────────────┤
│ id (PK)      │       │ id (PK)          │       │ id (PK)      │
│ email        │       │ shelterId (FK) ──┼──────►│ name         │
│ password     │       │ name             │       │ district     │
│ name         │       │ species          │       │ address      │
│ birthdate    │       │ breed            │       │ latitude     │
│ district     │       │ age              │       │ longitude    │
│ role         │       │ sex              │       │ phone        │
│ darkMode     │       │ sterilized       │       │ email        │
│ createdAt    │       │ status           │       │ openHours    │
└──────┬───────┘       │ urgent           │       │ passwordHash │
       │               │ description      │       │ createdAt    │
       │               │ createdAt        │       └──────┬───────┘
       │               └────────┬─────────┘              │
       │                        │                        │
       │               ┌────────▼────────┐      ┌────────▼────────┐
       │               │   AnimalPhoto   │      │   ShelterNeed   │
       │               ├─────────────────┤      ├─────────────────┤
       │               │ id (PK)         │      │ id (PK)         │
       │               │ animalId (FK)   │      │ shelterId (FK)  │
       │               │ url             │      │ type            │
       │               │ isPrimary       │      │ description     │
       │               └─────────────────┘      │ urgent          │
       │                                        │ active          │
       │                                        └─────────────────┘
       │
       ├──────────────────────────────────────────────────────────┐
       │                                                          │
┌──────▼───────────────┐                              ┌──────────▼──────┐
│     Reservation      │                              │    Donation     │
├──────────────────────┤                              ├─────────────────┤
│ id (PK)              │                              │ id (PK)         │
│ userId (FK) ─────────┘                              │ userId (FK)     │
│ animalId (FK)        │                              │ shelterId (FK)  │
│ date                 │                              │ type            │
│ status               │                              │ details (JSON)  │
│ emailSent            │                              │ status          │
│ createdAt            │                              │ createdAt       │
└──────────────────────┘                              └────────┬────────┘
                                                               │
                                                      ┌────────▼────────┐
                                                      │    Payment      │
                                                      ├─────────────────┤
                                                      │ id (PK)         │
                                                      │ donationId (FK) │
                                                      │ stripeId        │
                                                      │ amount          │
                                                      │ currency        │
                                                      │ status          │
                                                      │ createdAt       │
                                                      └─────────────────┘

4.2 Descrição das Entidades

Entidade Descrição Relações
User Utilizador registado na plataforma Tem muitas Reservation e Donation
Shelter Canil ou associação de protecção animal Tem muitos Animal, recebe Donation, tem ShelterNeed
Animal Animal disponível para adopção Pertence a um Shelter, tem AnimalPhoto e Reservation
Reservation Reserva de adopção de um animal Pertence a User e Animal
Donation Doação (monetária, ração ou brinquedos) Pertence a User e Shelter, tem Payment opcional
Payment Registo do pagamento processado pelo Stripe Associado a Donation do tipo monetário
AnimalPhoto Fotografia de um animal Pertence a Animal
ShelterNeed Necessidade actual do canil Pertence a Shelter

4.3 Esquema Prisma Completo

// prisma/schema.prisma

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

// ─── Enums ──────────────────────────────────────────────────────

enum UserRole {
  USER
  SHELTER_ADMIN
  ADMIN
}

enum Species {
  DOG
  CAT
  OTHER
}

enum Sex {
  MALE
  FEMALE
}

enum AnimalStatus {
  AVAILABLE
  RESERVED
  ADOPTED
}

enum DonationType {
  MONETARY
  FOOD
  TOYS
}

enum ReservationStatus {
  PENDING
  CONFIRMED
  CANCELLED
  COMPLETED
}

enum DonationStatus {
  PENDING
  CONFIRMED
  DELIVERED
  CANCELLED
}

// ─── Models ─────────────────────────────────────────────────────

model User {
  id           String        @id @default(cuid())
  email        String        @unique
  password     String        // bcrypt hash — nunca em texto simples
  name         String
  birthdate    DateTime
  district     String
  role         UserRole      @default(USER)
  darkMode     Boolean       @default(false)
  emailVerified Boolean      @default(false)
  createdAt    DateTime      @default(now())
  updatedAt    DateTime      @updatedAt

  reservations Reservation[]
  donations    Donation[]

  @@index([email])
  @@index([district])
}

model Shelter {
  id           String        @id @default(cuid())
  name         String
  district     String
  address      String
  latitude     Float
  longitude    Float
  phone        String
  email        String        @unique
  passwordHash String
  description  String?
  website      String?
  // JSON: { mon: "09:00-18:00", tue: "09:00-18:00", ... , sun: null }
  openHours    Json
  verified     Boolean       @default(false)
  createdAt    DateTime      @default(now())
  updatedAt    DateTime      @updatedAt

  animals      Animal[]
  donations    Donation[]
  needs        ShelterNeed[]

  @@index([district])
}

model Animal {
  id           String        @id @default(cuid())
  shelterId    String
  shelter      Shelter       @relation(fields: [shelterId], references: [id])
  name         String
  species      Species
  breed        String?       // relevante para DOG e CAT
  ageMonths    Int           // idade em meses para precisão
  sex          Sex
  sterilized   Boolean
  status       AnimalStatus  @default(AVAILABLE)
  urgent       Boolean       @default(false)
  description  String?
  createdAt    DateTime      @default(now())
  updatedAt    DateTime      @updatedAt

  photos       AnimalPhoto[]
  reservations Reservation[]

  @@index([shelterId])
  @@index([species])
  @@index([status])
  @@index([district: false]) // via shelter — para queries por localidade
}

model AnimalPhoto {
  id        String  @id @default(cuid())
  animalId  String
  animal    Animal  @relation(fields: [animalId], references: [id], onDelete: Cascade)
  url       String
  isPrimary Boolean @default(false)
  createdAt DateTime @default(now())

  @@index([animalId])
}

model Reservation {
  id        String            @id @default(cuid())
  userId    String
  user      User              @relation(fields: [userId], references: [id])
  animalId  String
  animal    Animal            @relation(fields: [animalId], references: [id])
  date      DateTime
  status    ReservationStatus @default(PENDING)
  emailSent Boolean           @default(false)
  notes     String?
  createdAt DateTime          @default(now())
  updatedAt DateTime          @updatedAt

  @@index([userId])
  @@index([animalId])
  @@index([date])
}

model Donation {
  id        String         @id @default(cuid())
  userId    String
  user      User           @relation(fields: [userId], references: [id])
  shelterId String
  shelter   Shelter        @relation(fields: [shelterId], references: [id])
  type      DonationType
  // Para FOOD: { foodType: "dry|wet", animalType: "dog|cat", ageGroup: "adult|puppy" }
  // Para TOYS: { category: "chew|plush|interactive" }
  // Para MONETARY: { amount: 25.00, currency: "EUR" }
  // Partilhado: { deliveryMethod: "pickup|home_delivery", address?: "..." }
  details   Json
  status    DonationStatus @default(PENDING)
  emailSent Boolean        @default(false)
  createdAt DateTime       @default(now())
  updatedAt DateTime       @updatedAt

  payment   Payment?

  @@index([userId])
  @@index([shelterId])
  @@index([type])
}

model Payment {
  id             String   @id @default(cuid())
  donationId     String   @unique
  donation       Donation @relation(fields: [donationId], references: [id])
  stripePaymentId String  @unique
  amount         Int      // em cêntimos (ex: 2500 = 25,00€)
  currency       String   @default("eur")
  status         String   // stripe payment status
  receiptUrl     String?
  createdAt      DateTime @default(now())
  updatedAt      DateTime @updatedAt
}

model ShelterNeed {
  id          String   @id @default(cuid())
  shelterId   String
  shelter     Shelter  @relation(fields: [shelterId], references: [id])
  type        String   // "food_dry_dog", "toys", "blankets", etc.
  description String?
  urgent      Boolean  @default(false)
  active      Boolean  @default(true)
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  @@index([shelterId])
  @@index([active])
}

4.4 Estratégia de Indexação

Os índices foram definidos com base nos filtros mais comuns da aplicação:

Índice Tabela Motivo
email User Login — lookup por email frequente
district User, Shelter Filtros por localidade
shelterId Animal Listar animais de um canil
species, status Animal Filtros principais da listagem
userId Reservation, Donation Histórico do utilizador
animalId Reservation Verificar disponibilidade do animal
type, active ShelterNeed Listar necessidades activas por tipo

4.5 Migrações

As migrações são geridas automaticamente pelo Prisma:

# Criar nova migração após alterar schema.prisma
npx prisma migrate dev --name descricao_da_alteracao

# Aplicar migrações em produção
npx prisma migrate deploy

# Visualizar base de dados no browser (Prisma Studio)
npx prisma studio