323 lines
12 KiB
Markdown
323 lines
12 KiB
Markdown
# 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
|
|
// 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:
|
|
|
|
```bash
|
|
# 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
|
|
```
|