> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wethehivers.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Modèle de données

> Vue d'ensemble des entités et de leurs relations

# Modèle de données

THE HIVE s'appuie sur **PostgreSQL 16**. Les entités centrales sont `User`, `Candidat`, `Recruteur`, `Entreprise`, `Offre`, `Candidature`, `Vivier`, `AlerteEmploi`, `Article` (blog).

## Diagramme global (ER)

```mermaid theme={null}
erDiagram
    USER ||--o| CANDIDAT : "has"
    USER ||--o| RECRUTEUR : "has"
    USER ||--o| ADMIN : "has"
    ENTREPRISE ||--o{ RECRUTEUR : "employs"
    ENTREPRISE ||--o{ OFFRE : "publishes"
    RECRUTEUR ||--o{ OFFRE : "owns"
    OFFRE ||--o{ CANDIDATURE : "receives"
    CANDIDAT ||--o{ CANDIDATURE : "submits"
    CANDIDAT ||--o{ CV : "uploads"
    CANDIDATURE }|--|| CV : "attaches"
    RECRUTEUR ||--o{ VIVIER : "creates"
    VIVIER ||--o{ VIVIER_CANDIDAT : "contains"
    VIVIER_CANDIDAT }|--|| CANDIDAT : "external or registered"
    CANDIDAT ||--o{ ALERTE_EMPLOI : "subscribes"
    ENTREPRISE ||--o{ FAVORI : "favorited by"
    CANDIDAT ||--o{ FAVORI : "favorites"
    ADMIN ||--o{ ARTICLE : "authors"
    REGION ||--o{ VILLE : "contains"
    VILLE ||--o{ OFFRE : "located in"
    VILLE ||--o{ CANDIDAT : "resides in"
    VILLE ||--o{ ENTREPRISE : "located in"

    USER {
        long id PK
        string email UK
        string password_hash
        string role "CANDIDAT|RECRUTEUR|ADMIN|SUPER_ADMIN"
        string status "PENDING|ACTIVE|SUSPENDED|..."
        timestamp created_at
    }
    CANDIDAT {
        long id PK
        long user_id FK
        string first_name
        string last_name
        string phone
        long cv_id FK
        long ville_id FK
        string niveau_experience
    }
    RECRUTEUR {
        long id PK
        long user_id FK
        long entreprise_id FK
        string role "OWNER|MEMBER"
        string poste
    }
    ENTREPRISE {
        long id PK
        string nom
        string rccm UK
        string secteur
        long ville_id FK
        string site_web
        string logo_key
    }
    OFFRE {
        long id PK
        long recruteur_id FK
        long entreprise_id FK
        string titre
        text description
        string type_contrat
        string niveau_experience
        long ville_id FK
        int salaire_min
        int salaire_max
        string devise
        date date_expiration
        string status "DRAFT|PUBLISHED|CLOSED|ARCHIVED"
        tsvector search_vector
    }
    CANDIDATURE {
        long id PK
        long offre_id FK
        long candidat_id FK
        long cv_id FK
        text lettre_motivation
        string status "NEW|TO_MEET|IN_PROGRESS|..."
        timestamp applied_at
    }
    CV {
        long id PK
        long candidat_id FK
        string r2_key
        string file_name
        int file_size
        string mime_type
        string scan_status
    }
    VIVIER {
        long id PK
        long recruteur_id FK
        string nom
        string description
    }
    ALERTE_EMPLOI {
        long id PK
        long candidat_id FK
        json criteres
        string frequence "DAILY|WEEKLY"
        boolean active
    }
    ARTICLE {
        long id PK
        long author_id FK
        string slug UK
        string titre
        text contenu
        string status "DRAFT|PUBLISHED"
        timestamp published_at
    }
```

## Grandes familles

```mermaid theme={null}
flowchart LR
    subgraph Identite
        U[User]
        C[Candidat]
        R[Recruteur]
        A[Admin]
    end

    subgraph Emploi
        E[Entreprise]
        O[Offre]
        AP[Candidature]
    end

    subgraph Fichiers
        CV[CV]
        LOGO[Logo]
        PHOTO[Photo profil]
    end

    subgraph Engagement
        ALE[AlerteEmploi]
        FAV[Favori]
        VIV[Vivier]
    end

    subgraph Contenu
        ART[Article blog]
    end

    subgraph Reference
        REG[Région]
        VIL[Ville]
    end
```

## Cardinalités clefs

| Entité A    | Relation | Entité B     | Cardinalité                |
| ----------- | -------- | ------------ | -------------------------- |
| Entreprise  | publie   | Offre        | 1 → N                      |
| Entreprise  | emploie  | Recruteur    | 1 → N                      |
| Recruteur   | poste    | Offre        | 1 → N                      |
| Offre       | reçoit   | Candidature  | 1 → N                      |
| Candidat    | postule  | Candidature  | 1 → N                      |
| Candidat    | upload   | CV           | 1 → N (historique)         |
| Candidature | attache  | CV           | N → 1                      |
| Recruteur   | crée     | Vivier       | 1 → N                      |
| Vivier      | contient | Candidat     | N ↔ N (via VivierCandidat) |
| Candidat    | s'abonne | AlerteEmploi | 1 → N                      |
| Région      | contient | Ville        | 1 → N                      |

## Contraintes d'intégrité

```mermaid theme={null}
flowchart TD
    UNQ1[user.email UNIQUE]
    UNQ2[entreprise.rccm UNIQUE]
    UNQ3[offre slug UNIQUE]
    UNQ4[article.slug UNIQUE]
    UNQ5[candidature \n UNIQUE offreId + candidatId] --> STATE{NEW|ACTIVE}
    STATE -->|empêche double-postulation| UNQ5
```

## Tables techniques

| Table                      | Rôle                                     |
| -------------------------- | ---------------------------------------- |
| `refresh_tokens`           | Rotation refresh JWT                     |
| `offres_status_history`    | Audit transitions d'état offre           |
| `recruiter_status_history` | Audit transitions recruteur              |
| `candidature_events`       | Timeline candidature (statuts, messages) |
| `files_metadata`           | Métadonnées fichiers R2                  |
| `email_queue`              | File d'attente envois batch              |
| `audit_log`                | Actions admins sensibles                 |

## Indexes critiques

| Index                      | Cible                               | Type                       |
| -------------------------- | ----------------------------------- | -------------------------- |
| `idx_offres_search_vector` | `offres.search_vector`              | GIN                        |
| `idx_offres_status_pub`    | `offres(status, published_at DESC)` | BTREE composite            |
| `idx_candidatures_offre`   | `candidatures(offre_id, status)`    | BTREE composite            |
| `idx_users_email`          | `users.email`                       | BTREE unique               |
| `idx_entreprises_rccm`     | `entreprises.rccm`                  | BTREE unique               |
| `idx_offres_titre_trgm`    | `offres.titre`                      | GIN trigram (autocomplete) |

## Versioning du schéma

Les migrations suivent le format Flyway `V{YYYYMMDDHHMMSS}__{NNN}_{description}.sql`. Dernière migration : **023** (`_023_fulltext_search_vector.sql`).

```mermaid theme={null}
timeline
    title Migrations clefs
    001 : schéma initial users/candidats/recruteurs
    005 : ajout offres + candidatures
    008 : entreprises + RCCM + multi-recruteurs
    012 : blog (articles + medias)
    015 : vivier + invitations
    018 : alertes emploi JSON + cron
    020 : refresh tokens rotation
    023 : tsvector + GIN + trigger
```
