first commit

This commit is contained in:
2026-05-04 09:43:36 +01:00
commit dfae1b5335
37 changed files with 10343 additions and 0 deletions

322
docs/04-base-de-dados.md Normal file
View File

@@ -0,0 +1,322 @@
# 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
```