# Database Current State Audit — Mangolê

> **Data da auditoria:** 2026-07-21
> **SGBD:** MySQL (InnoDB)
> **Base de dados:** `mangole`
> **Estado:** Pré-reconstrução — apenas leitura, sem alterações.

---

## A. Diagrama Textual das Relações

```
users (id)
  └─ <sem FK para outras tabelas>

stores (id) ──────────────────────────────────┐
  ├─ store_messages.store_id ──────────────────┘ (FK nullable)
  └─ <province_code: string, sem FK para tabela de províncias>

product_families (id)
  └─ <sem FK para products — category é string livre>

products (id)
  └─ <soft delete: deleted_at>

campaigns (id)
  ├─ campaign_assets.campaign_id (FK)
  └─ campaign_ctas.campaign_id (FK)

settings (id)
  └─ <key-value simples por grupo>

Tabelas órfãs estruturais (sem FK):
  news_articles, partners, vacancies, team_members, homepage_blocks, seo_metadata
```

---

## B. Inventário de Todas as Tabelas

| # | Tabela | Engine | Registos | Auto-inc |
|---|--------|--------|---------|----------|
| 1 | `cache` | InnoDB | 26 | N/A |
| 2 | `cache_locks` | InnoDB | 0 | N/A |
| 3 | `campaign_assets` | InnoDB | 0 | sim |
| 4 | `campaign_ctas` | InnoDB | 0 | sim |
| 5 | `campaigns` | InnoDB | 0 | sim |
| 6 | `failed_jobs` | InnoDB | 0 | sim |
| 7 | `homepage_blocks` | InnoDB | 0 | sim |
| 8 | `job_batches` | InnoDB | 0 | N/A |
| 9 | `jobs` | InnoDB | 0 | sim |
| 10 | `migrations` | InnoDB | 20 | sim |
| 11 | `news_articles` | InnoDB | 0 | sim |
| 12 | `partners` | InnoDB | 0 | sim |
| 13 | `password_reset_tokens` | InnoDB | 0 | N/A |
| 14 | `product_families` | InnoDB | **6** | sim |
| 15 | `products` | InnoDB | **5** | sim |
| 16 | `seo_metadata` | InnoDB | 0 | sim |
| 17 | `sessions` | InnoDB | 0 | N/A |
| 18 | `settings` | InnoDB | **6** | sim |
| 19 | `store_messages` | InnoDB | **2** | sim |
| 20 | `stores` | InnoDB | **9** | sim |
| 21 | `team_members` | InnoDB | **6** | sim |
| 22 | `users` | InnoDB | **2** | sim |
| 23 | `vacancies` | InnoDB | 0 | sim |

**Total: 23 tabelas, 9 com dados.**

---

## C. Classificação por Domínio

### CORE DOMAIN
| Tabela | Reg. | Descrição |
|--------|------|-----------|
| `products` | 5 | Catálogo de produtos (com soft delete) |
| `product_families` | 6 | Famílias de produtos (ex: Açúcar, Arroz, Farinhas...) |
| `stores` | 9 | Lojas físicas (com soft delete e geolocalização) |
| `store_messages` | 2 | Mensagens de contacto via loja |

### CMS
| Tabela | Reg. | Descrição |
|--------|------|-----------|
| `news_articles` | 0 | Artigos de notícias (com slug, SEO, status draft/published) |
| `homepage_blocks` | 0 | Blocos dinâmicos da homepage (JSON settings) |
| `partners` | 0 | Parceiros institucionais |
| `team_members` | 6 | Equipa (board + leadership) |
| `vacancies` | 0 | Vagas de emprego |

### GEOLOCATION
| Tabela | Reg. | Descrição |
|--------|------|-----------|
| *(embutido em stores)* | — | `province_code` (varchar 3), `municipality`, `neighborhood`, `latitude`, `longitude` |

### CAMPAIGNS
| Tabela | Reg. | Descrição |
|--------|------|-----------|
| `campaigns` | 0 | Campanhas promocionais com SEO próprio |
| `campaign_assets` | 0 | Assets multimédia por campanha |
| `campaign_ctas` | 0 | CTAs por campanha |

### USERS / AUTH
| Tabela | Reg. | Descrição |
|--------|------|-----------|
| `users` | 2 | Administradores (admin@mangole.ao, marketing@mangole.ao) |
| `password_reset_tokens` | 0 | Tokens de reset |
| `sessions` | 0 | Sessões |

### SYSTEM
| Tabela | Reg. | Descrição |
|--------|------|-----------|
| `cache` | 26 | Cache do Laravel (prefixo `mwangole-cache`) |
| `cache_locks` | 0 | Locks de cache |
| `jobs` | 0 | Queue jobs |
| `job_batches` | 0 | Job batches |
| `failed_jobs` | 0 | Falhas de jobs |
| `migrations` | 20 | Histórico de migrations |
| `settings` | 6 | Configurações chave-valor por grupo (home, about) |

### LEGACY / POSSIBLY OBSOLETE
| Tabela | Reg. | Motivo |
|--------|------|--------|
| *(nenhuma tabela identificada como obsoleta)* | | Todas as tabelas têm propósito definido |

---

## D. Dados que Devem Ser Preservados

### Users (2 registos)
| ID | Nome | Email | Verificado |
|----|------|-------|------------|
| 1 | Administrador Mangolê | admin@mangole.ao | Sim |
| 2 | Marketing Mangolê | marketing@mangole.ao | Sim |

### Product Families (6 registos)
| ID | Nome | Slug | Ordem | Ativo |
|----|------|------|-------|-------|
| 1 | Açúcar | acucar | 1 | Sim |
| 2 | Arroz | arroz | 2 | Sim |
| 3 | Farinhas | farinhas | 3 | Sim |
| 4 | Grãos | graos | 4 | Sim |
| 5 | Milho | milho | 5 | Sim |
| 6 | Óleo de Palma | oleo-de-palma | 6 | Sim |

### Products (5 registos)
| ID | Nome | Preço | Original | Badge | Categoria |
|----|------|-------|----------|-------|-----------|
| 11 | Azeite Tempero Boa Mesa Lata 500ml | 1.850 Kz | 2.350 Kz | Preço Bomba! | Azeite |
| 12 | Azeite Tempero Boa Mesa Plástico 0.5L | 950 Kz | 1.200 Kz | Preço Bomba! | Azeite |
| 13 | Azeite Tempero Boa Mesa Vidro 1L | 1.650 Kz | 2.100 Kz | Destaque | Azeite |
| 14 | Azeite Tempero Boa Mesa Plástico 3L | 3.950 Kz | 4.800 Kz | Economia Família | Azeite |
| 15 | Azeite Tempero Boa Mesa Plástico 5L | 5.950 Kz | 7.200 Kz | Pack Grosso | Azeite |

### Stores (9 registos)
| ID | Nome | Tipo | Província | Município | Ativo |
|----|------|------|-----------|-----------|-------|
| 1 | Hiper Mangolê – Morro Bento | hiper | LUA | Belas | Sim |
| 2 | Hiper Mangolê – Golf 2 | hiper | LUA | Kilamba Kiaxi | Sim |
| 3 | Hiper Mangolê – Calemba 2 | hiper | LUA | Kilamba Kiaxi | Sim |
| 4 | Hiper Mangolê – Cazenga | hiper | LUA | Cazenga | Sim |
| 5 | Hiper Mangolê – Cacuaco | hiper | LUA | Cacuaco | Sim |
| 6 | Hiper Mangolê – Luanda Sul | hiper | LUA | Viana | Sim |
| 7 | Hiper Mangolê – Rua do Coelho | hiper | LUA | Viana | Sim |
| 8 | Hiper Mangolê – Ponte Amarela (CAOP) | hiper | LUA | Viana | Sim |
| 9 | Mangolê Mix – Caxito | mix | BGO | Dande | Sim |

### Settings (6 registos)
- **Grupo `home`**: hero_title, hero_subtitle, hero_cta_label
- **Grupo `about`**: hero_title, hero_subtitle, quem_somos_title

### Team Members (6 registos)
| ID | Nome | Cargo | Categoria |
|----|------|-------|-----------|
| 1 | João dos Santos | Presidente do Conselho | board |
| 4 | Carla Mendes | CEO | leadership |
| 2 | Ana Paula Fernandes | Vice-Presidente | board |
| 5 | Pedro Afonso | Director de Operações | leadership |
| 3 | Miguel Bernardo | Conselheiro | board |
| 6 | Luciana Vicente | Directora Comercial | leadership |

### Store Messages (2 registos)
| ID | Nome | Email | Store | Status |
|----|------|-------|-------|--------|
| 14 | Teste Nome | teste@example.com | NULL (sem loja) | pending |
| 15 | Maria Teste | maria@teste.com | NULL (sem loja) | pending |

---

## E. Dados que Podem Ser Migrados

Todas as tabelas com dados têm conteúdo que deve ser migrado para os novos Models:

- **users** → Model `User` (com `Filament\Panel`)
- **stores** → Model `Store` (com soft delete, geolocalização)
- **store_messages** → Model `StoreMessage` (FK opcional para Store)
- **product_families** → Model `ProductFamily`
- **products** → Model `Product` (com soft delete)
- **settings** → Model `Setting` (key-value por grupo)
- **team_members** → Model `TeamMember` (ENUM board/leadership)
- **campaign_assets/campaign_ctas/campaigns** → estrutura pronta mas sem dados
- **news_articles, partners, vacancies** → estrutura pronta, sem dados

---

## F. Dados que Precisam de Normalização

### 1. Produtos sem FK para ProductFamily
- `products.category` é `varchar(100)` — string livre, sem FK
- Todas as famílias estão em `product_families`, mas produtos não referenciam a tabela
- **Sugestão:** adicionar `product_family_id` FK em `products`

### 2. Geolocalização sem tabela de lookup
- `stores.province_code` é `varchar(3)` — sem tabela de províncias/municípios
- Valores encontrados: `LUA` (Luanda), `BGO` (Bengo)
- **Sugestão:** criar tabela `provinces` e `municipalities` para normalizar

### 3. SEO embutido vs tabela separada
- `campaigns` tem 8 colunas de SEO embutidas
- Existe `seo_metadata` separada para páginas CMS
- **Sugestão:** unificar estratégia de SEO (trait polimórfica ou tabela separada)

### 4. Settings como key-value
- `settings` usa `group` + `key` com unique composite — funcional mas sem tipagem
- **Sugestão:** manter como está (é adequado para config dinâmica)

### 5. Tipos ENUM vs VARCHAR
- `team_members.category` usa `enum('board','leadership')` — tipo nativo MySQL
- Vários campos de status usam `varchar(20)` sem constraint (`status` em campaigns, news_articles, vacancies, store_messages)
- **Sugestão:** usar ENUM ou string com validação Laravel

---

## G. Problemas Detectados

### G.1. Store Messages Órfãos
- 2 registos em `store_messages` com `store_id = NULL` mas a FK é nullable
- Não são órfãos técnicos, mas são mensagens sem loja associada — possível dado de teste

### G.2. Gap na Sequência de IDs dos Produtos
- `products` auto-increment está em 15 mas só há 5 registos (IDs 11-15)
- Possível que produtos tenham sido inseridos e apagados (soft delete), ou houve resets na tabela

### G.3. Cache com Prefixo Antigo
- Cache usa prefixo `mwangole-cache-*` — se o `APP_NAME` mudar, cache será perdida (benigno)

### G.4. Ausência de FK Products → ProductFamilies
- A relação lógica existe nos dados da aplicação, mas não está refletida no schema
- Todas as famílias existem, todos os produtos têm `category`, mas não há integridade referencial

### G.5. Timestamps inconsistentes
- `failed_jobs` usa `failed_at` com default `current_timestamp()` em vez de `created_at`
- `password_reset_tokens` só tem `created_at` (sem `updated_at`)
- Cache e jobs usam timestamps UNIX (`bigint`) em vez de `timestamp` MySQL

### G.6. Sem índice único em `settings` para `(group, key)` além do composite
- Existe unique composite `settings_group_key_unique` — OK

### G.7. Sem soft delete em tabelas CMS
- `news_articles`, `partners`, `vacancies` não têm `deleted_at`
- `products` e `stores` têm — inconsistência de padrão

### G.8. Dados de Teste Misturados com Reais
- `store_messages` contém 2 registos de teste (teste@example.com, maria@teste.com)
- Devem ser avaliados se devem ser preservados ou removidos

---

## H. Recomendações

### Pré-migração
1. **Backup completo** da base de dados `mangole` (dump SQL)
2. Backup dos ficheiros de upload (imagens de produtos, logos, fotos de lojas, fotos de equipa)

### Ao recriar Models e Migrations
3. **Manter nomes de tabelas** iguais aos existentes para permitir migração direta de dados
4. **Adicionar `product_family_id` FK** em `products` — criar migration para popular e tornar obrigatório
5. **Criar tabelas `provinces` e `municipalities`** para normalizar geolocalização
6. **Adicionar soft delete** (`deleted_at`) a todas as tabelas CMS (news_articles, partners, vacancies)
7. **Manter settings como key-value** — simples e funcional
8. **Migrar dados** via script `php artisan db:seed --class=ImportLegacyData` que lê da BD existente

### Durante a reconstrução
9. **Não apagar a BD actual** até que a nova aplicação esteja funcional e validada
10. **Tabelas SYSTEM** (`cache`, `jobs`, `sessions`, `migrations`) podem ser recriadas do zero
11. **Tabelas vazias** (campaigns, news_articles, partners, vacancies, etc.) podem ser recriadas com o schema exacto

### Pós-migração
12. Validar contagem de registos entre BD antiga e nova
13. Validar integridade referencial após importação
14. Remover dados de teste identificados
15. Resetar sequências de auto-increment se necessário

---

## Apêndice: Histórico de Migrations (20 execuções)

```
Batch 1:
  0001_01_01_000000_create_users_table
  0001_01_01_000001_create_cache_table
  0001_01_01_000002_create_jobs_table
  2026_07_13_180205_create_stores_table
  2026_07_14_170001_create_partners_table
  2026_07_14_170002_create_vacancies_table
  2026_07_14_170003_create_news_articles_table
  2026_07_14_170004_create_settings_table
  2026_07_14_170005_create_seo_metadata_table
  2026_07_14_170006_evolve_portal_cms_schema
  2026_07_14_170007_remove_legacy_publication_booleans
  2026_07_14_180001_create_campaigns_table
  2026_07_14_180002_create_campaign_assets_table
  2026_07_14_180003_create_campaign_ctas_table
  2026_07_15_100001_create_homepage_blocks_table
  2026_07_17_190001_create_products_table
  2026_07_17_200001_add_fields_to_stores_table
  2026_07_17_200002_add_services_to_stores_table
  2026_07_18_100001_create_product_families_table
  2026_07_20_100001_create_store_messages_table

Batch 2:
  2026_07_20_200000_create_team_members_table
```

---

## Apêndice: Tabelas sem Dados (12)

- `cache_locks`, `campaign_assets`, `campaign_ctas`, `campaigns`, `failed_jobs`, `homepage_blocks`, `job_batches`, `jobs`, `news_articles`, `partners`, `seo_metadata`, `vacancies`, `password_reset_tokens`, `sessions`

> Nota: Estas tabelas têm o schema pronto mas nunca foram populadas, ou os dados foram limpos.
