# Banco de Dados

## Tecnologia

- **SGBD**: PostgreSQL
- **ORM**: Prisma ORM `6.19.3`
- **Mapeamento de Tabelas**: Padrão `snake_case` para tabelas no banco (`@@map`) e `camelCase` para propriedades nos models em TypeScript.

---

## Diagrama de Entidades (ERD)

```mermaid
erDiagram
    User ||--o{ Sender : "possui"
    User ||--o{ Campaign : "cria"
    User ||--o{ Domain : "gerencia"
    User ||--o{ ContactList : "possui"
    User ||--o{ Tag : "possui"

    Domain ||--o{ Sender : "vincula"

    Sender ||--o{ Campaign : "dispara"

    ContactList ||--o{ Campaign : "alvo"
    ContactList ||--o{ ContactListMembership : "contém"

    Contact ||--o{ ContactListMembership : "pertence"
    Contact ||--o{ CampaignLog : "recebe"
    Contact ||--o{ EmailEvent : "gera"

    Campaign ||--o{ CampaignLog : "registra"
    Campaign ||--o{ EmailEvent : "gera"

    ContactImportJob ||--o{ ContactImportRow : "contém"
```

---

## Enums

| Enum | Valores | Descrição |
| :--- | :--- | :--- |
| `CampaignStatus` | `DRAFT`, `SENDING`, `SENT`, `COMPLETED_WITH_ERRORS` | Estado do ciclo de vida de uma campanha de e-mail |
| `ContactStatus` | `ACTIVE`, `UNSUBSCRIBED`, `BOUNCED` | Estado de engajamento/disponibilidade do contato |
| `LogStatus` | `PENDING`, `SUCCESS`, `FAILED` | Estado de envio de um e-mail individual da campanha |
| `DomainVerificationStatus` | `PENDING`, `VERIFIED`, `FAILED` | Estado da verificação DNS do domínio no AWS SES |
| `SenderProvider` | `SMTP`, `SES` | Provedor de infraestrutura de transporte do remetente |
| `SuppressionReason` | `BOUNCE`, `COMPLAINT`, `MANUAL` | Razão do bloqueio de um e-mail na lista de supressão |
| `EmailEventType` | `SEND`, `DELIVERY`, `BOUNCE`, `COMPLAINT`, `REJECT`, `OPEN`, `CLICK` | Tipo de evento capturado via webhook SNS/SES ou pixel |
| `MarketingChannel` | `EMAIL`, `SMS` | Canal de comunicação associado a uma lista de contatos |
| `ContactSource` | `MANUAL`, `IMPORT`, `API` | Origem do cadastro do contato |
| `ImportDuplicateStrategy` | `IGNORE`, `UPDATE`, `CREATE_ANYWAY` | Estratégia de resolução de contatos com e-mail duplicado |
| `ImportJobStatus` | `MAPPING`, `PROCESSING`, `COMPLETED`, `FAILED` | Status de execução de um job de importação CSV |
| `ImportRowStatus` | `PENDING`, `IMPORTED`, `UPDATED`, `SKIPPED`, `ERROR` | Status do processamento de uma linha individual do CSV |

---

## Models do Schema

### 1. `User` (`users`)
Armazena a conta do usuário/administrador da plataforma.

- `id` (String, CUID, PK)
- `name` (String)
- `email` (String, Unique)
- `passwordHash` (String)
- `createdAt` / `updatedAt` (DateTime)

### 2. `Sender` (`senders`)
Remetente configurado para envio de mensagens via SMTP ou AWS SES.

- `id` (String, CUID, PK)
- `name` (String)
- `fromEmail` (String)
- `provider` (`SenderProvider`, default `SMTP`)
- `smtpHost` / `smtpPort` / `smtpUser` (String/Int, Opcional)
- `smtpPass` (String, Opcional, Armazenado Criptografado com AES-256-GCM)
- `domainId` (FK -> `Domain.id`, `onDelete: SetNull`)
- `userId` (FK -> `User.id`, `onDelete: Cascade`)

### 3. `Campaign` (`campaigns`)
Campanha de e-mail marketing.

- `id` (String, CUID, PK)
- `title` (String)
- `subject` (String)
- `htmlBody` (Text)
- `status` (`CampaignStatus`, default `DRAFT`)
- `senderId` (FK -> `Sender.id`, `onDelete: Restrict`)
- `contactListId` (FK -> `ContactList.id`, `onDelete: SetNull`)
- `createdById` (FK -> `User.id`, `onDelete: Restrict`)

### 4. `Contact` (`contacts`)
Registro global de contatos/inscritos da plataforma.

- `id` (String, CUID, PK)
- `email` (String, Unique)
- `name`, `lastName`, `phone`, `bairro`, `cidade`, `uf`, `idioma`, `empresa`, `cep`, `codigoEstabelecimento`, `nomeEstabelecimento`, `cdate` (String, Opcionais)
- `tags` (String[], Array nativo PostgreSQL com índice GIN)
- `source` (`ContactSource`, default `MANUAL`)
- `status` (`ContactStatus`, default `ACTIVE`)

### 5. `ContactList` (`contact_lists`)
Lista de contatos/segmento para disparo de campanhas.

- `id` (String, CUID, PK)
- `name` (String)
- `description` (String, Opcional)
- `marketingChannel` (`MarketingChannel`, default `EMAIL`)
- `userId` (FK -> `User.id`, `onDelete: Cascade`)
- *Unique Constraint*: `[userId, name]`

### 6. `ContactListMembership` (`contact_list_memberships`)
Tabela de junção (N:M) entre `Contact` e `ContactList`.

- `id` (String, CUID, PK)
- `contactId` (FK -> `Contact.id`, `onDelete: Cascade`)
- `listId` (FK -> `ContactList.id`, `onDelete: Cascade`)
- *Unique Constraint*: `[contactId, listId]`

### 7. `Tag` (`tags`)
Tag cadastrada para organização de contatos por usuário.

- `id` (String, CUID, PK)
- `name` (String)
- `description` (String, Opcional)
- `userId` (FK -> `User.id`, `onDelete: Cascade`)
- *Unique Constraint*: `[userId, name]`

### 8. `CampaignLog` (`campaign_logs`)
Log individual de envio de mensagem para um contato em uma campanha.

- `id` (String, CUID, PK)
- `status` (`LogStatus`, default `PENDING`)
- `sentAt` (DateTime, Opcional)
- `errorMessage` (Text, Opcional)
- `retryCount` (Int, default `0`)
- `messageId` (String, Opcional, ID retornado pelo SMTP/SES)
- `campaignId` (FK -> `Campaign.id`, `onDelete: Cascade`)
- `contactId` (FK -> `Contact.id`, `onDelete: Cascade`)
- *Unique Constraint*: `[campaignId, contactId]`

### 9. `Domain` (`domains`)
Domínio registrado para autorização de disparo e verificação DNS (DKIM/SPF) no AWS SES.

- `id` (String, CUID, PK)
- `domain` (String, Unique)
- `verificationStatus` (`DomainVerificationStatus`, default `PENDING`)
- `dkimRecords` (Json, Opcional)
- `mailFromDomain`, `mailFromMxRecord`, `sesIdentityArn` (String, Opcionais)
- `verifiedAt`, `lastCheckedAt` (DateTime, Opcionais)
- `userId` (FK -> `User.id`, `onDelete: Cascade`)

### 10. `SuppressedEmail` (`suppressed_emails`)
E-mails bloqueados para recebimento de novas mensagens (bounces permanentes ou denúncias de SPAM).

- `id` (String, CUID, PK)
- `email` (String, Unique)
- `reason` (`SuppressionReason`)
- `source` (String)
- `createdAt` (DateTime)

### 11. `EmailEvent` (`email_events`)
Histórico auditável de eventos recepcionados via Webhook SES/SNS ou pixel.

- `id` (String, CUID, PK)
- `type` (`EmailEventType`)
- `messageId` (String)
- `payload` (Json)
- `campaignId` (FK -> `Campaign.id`, `onDelete: SetNull`)
- `contactId` (FK -> `Contact.id`, `onDelete: SetNull`)

### 12. `ContactImportJob` (`contact_import_jobs`)
Job de importação em lote de arquivo CSV de contatos.

- `id` (String, CUID, PK)
- `fileName` (String)
- `status` (`ImportJobStatus`, default `MAPPING`)
- `columnMapping` (Json, Opcional)
- `duplicateStrategy` (`ImportDuplicateStrategy`, Opcional)
- `targetListId` (String, Opcional)
- `totalRows`, `processedRows`, `importedCount`, `updatedCount`, `skippedCount`, `errorCount` (Int)
- `createdById` (FK -> `User.id`)

### 13. `ContactImportRow` (`contact_import_rows`)
Linha individual processada em um job de importação CSV.

- `id` (String, CUID, PK)
- `rowIndex` (Int)
- `rawData` (Json)
- `status` (`ImportRowStatus`, default `PENDING`)
- `errorMessage` (String, Opcional)
- `jobId` (FK -> `ContactImportJob.id`, `onDelete: Cascade`)

### 14. `ContactExportJob` (`contact_export_jobs`)
Job de exportação de contatos para arquivo CSV.

- `id` (String, CUID, PK)
- `filters` (Json)
- `columns` (String[])
- `totalRows` (Int)
- `csvContent` (Text)
- `createdById` (FK -> `User.id`)

---

## Migrations

### Em Desenvolvimento
Ao alterar o arquivo `prisma/schema.prisma`, gere e execute uma nova migração local:

```bash
npx prisma migrate dev --name nome_da_alteracao
```

### Em Produção
Em ambientes de CI/CD ou produção, execute apenas a aplicação de migrações compiladas:

```bash
npx prisma migrate deploy
```

---

## Seed do Banco

O arquivo `prisma/seed.ts` popula o banco de dados inicial com dados de teste para desenvolvimento:

- Cria um usuário padrão: `admin@newsletter.com` (senha: `admin123`).
- Cria um remetente SMTP inicial configurado para o servidor Mailpit local (`localhost:1025`).

Para executar manualmente:

```bash
npx prisma db seed
```

---

## Convenções de Banco

- **Soft vs Hard Delete**: 
  - Exclusão de `Contact`, `Tag`, `ContactList`, `Sender` e `Domain` utiliza hard delete no banco de dados com tratamento de integridade por FKs (`Cascade`, `SetNull` ou `Restrict`).
  - O descadastro de um contato é tratado alterando o campo `status` para `UNSUBSCRIBED` sem apagar o histórico do registro.
- **Campos de Auditoria**: Quase todas as tabelas principais contêm `createdAt` (timestamp de criação) e `updatedAt` (atualizado automaticamente via Prisma `@updatedAt`).
- **Índices Estratégicos**:
  - `contacts`: Índice GIN na coluna `tags` para buscas performáticas por array e índice em `createdAt`.
  - `campaign_logs`: Índices compostos por `[campaignId, status]` para relatórios rápidos.
  - `email_events`: Índices em `messageId` e `[contactId, type, createdAt]`.
