# Notícias (V2) — Arquitetura

> **Slug:** `publicacoes-noticias` · **Versão analisada:** V2 do repositório `lotep/`.
>
> Todos os caminhos são relativos à raiz de `lotep/`.

---

## 1. Fluxo da requisição

### 1.1 CMS — gravação de notícia

```mermaid
sequenceDiagram
    participant U as Redator
    participant R as Rota cms/noticias
    participant A as CmsAuthenticate
    participant P as V2PermissionMiddleware
    participant C as NoticiasCmsController
    participant V as NewsRequest
    participant M as News (Eloquent)
    participant S as NewsSearchSender
    participant W as v2-noticias::cms.*

    U->>R: POST /cms/noticias/novo
    R->>A: v2.authenticate
    A->>P: delega autorização
    P-->>A: write no módulo Noticias
    A->>C: store(NewsRequest)
    C->>V: valida título/status/content JSON
    V-->>C: dados válidos
    C->>M: new News + save()
    M->>M: saving (slug único, datas, token)
    M->>S: saved -> syncToSearch()
    S-->>M: indexa CMS + (se publicado) portal
    C->>W: redirect route(v2.noticias.cms.index)
```

### 1.2 Portal — leitura de matéria

```mermaid
sequenceDiagram
    participant Cid as Cidadão
    participant R as /noticia/{slug}
    participant MW as web + MaintenanceMiddleware
    participant C as NoticiasPortalController
    participant M as News
    participant E as ContentRichEditorRendererService
    participant SEO as SEOService
    participant W as v2-noticias::portal.show

    Cid->>R: GET /noticia/{slug}
    R->>MW: sessão + manutenção
    MW->>C: show($slug)
    C->>M: published()->where(slug)->with(capa,categoria,tags,galeria)
    M-->>C: News ou 404
    C->>E: toHtml(content AST)
    E-->>C: HTML semântico
    C->>SEO: setTitle/setDescription/setKeywords/setCoverImage
    C->>M: resolveAdjacentNews(prev/next)
    C->>W: view(news, htmlContent, related, prevNews, nextNews)
```

O layout `V2/layouts/portal/app-layout.blade.php` recebe `schemas` da própria view; o JSON-LD é montado em Blade, não pelo `SEOService`.

## 2. Rotas principais

A tabela completa está em `03-ROTAS-E-CONTROLADORES.md`. Resumo por superfície:

| Superfície | Arquivo | Middleware efetivo |
|---|---|---|
| CMS | `V2/Modules/Publicacoes/Noticias/Routes/cms.php` | `web` + `module.active:Noticias` (provider) + `v2.authenticate` + `v2.permission:noticias` (arquivo) |
| Portal | `V2/Modules/Publicacoes/Noticias/Routes/web.php` | `web` + `MaintenanceMiddleware` (provider) + `web` (arquivo, duplicado) |

O `V2ServiceProvider` aplica o namespace `V2\Modules\Publicacoes\Noticias\Controllers` e agrupa as rotas por domínio configurado (`V2/Core/Providers/V2ServiceProvider.php:508-535`).

## 3. Classes envolvidas

| Classe | Caminho | Papel |
|---|---|---|
| `NoticiasCmsController` | `V2/Modules/Publicacoes/Noticias/Controllers/NoticiasCmsController.php` | CRUD, workflow, destaques, tags, categorias, SEO, cache de página. |
| `NoticiasPortalController` | `V2/Modules/Publicacoes/Noticias/Controllers/NoticiasPortalController.php` | Listagem, matéria, preview por token, relacionadas, prev/next. |
| `NoticiasSitemapController` | `V2/Modules/Publicacoes/Noticias/Controllers/NoticiasSitemapController.php` | XML do Google News, cache de 6 h. |
| `NoticiasConfigController` | `V2/Modules/Publicacoes/Noticias/Controllers/NoticiasConfigController.php` | Aprovadores e configuração de sidebar. |
| `News` | `V2/Modules/Publicacoes/Noticias/Models/News.php` | Model central; hooks de slug, status, token, indexação e SEO. |
| `NewsCategory` | `V2/Modules/Publicacoes/Noticias/Models/NewsCategory.php` | Categoria hierárquica e ordem. |
| `NewsTag` | `V2/Modules/Publicacoes/Noticias/Models/NewsTag.php` | Tag. |
| `NewsApprover` | `V2/Modules/Publicacoes/Noticias/Models/NewsApprover.php` | Aprovador com vigência. |
| `NewsConfig` | `V2/Modules/Publicacoes/Noticias/Models/NewsConfig.php` | Configuração chave/valor. |
| `NewsRequest` | `V2/Modules/Publicacoes/Noticias/Requests/NewsRequest.php` | Validação de criação/edição. |
| `NewsNotificationService` | `V2/Modules/Publicacoes/Noticias/Services/NewsNotificationService.php` | E-mail para aprovadores ativos. |
| `NewsSearchSender` | `V2/Modules/Publicacoes/Noticias/Services/NewsSearchSender.php` | Traduz `News` em DTOs de indexação. |
| `MarkdownToTiptapConverter` | `V2/Modules/Publicacoes/Noticias/Services/MarkdownToTiptapConverter.php` | Conversão auxiliar Markdown → AST (não usada nos controllers). |
| `ModuleProvider` | `V2/Modules/Publicacoes/Noticias/ModuleProvider.php` | Registra singleton do sender e configura sidebar. |

Services consumidos (não pertencem ao módulo):

| Service | Caminho | Uso no módulo |
|---|---|---|
| `SEOService` | `V2/Services/SEO/Services/SEOService.php` | `bindModel()` no preview CMS; setters no portal. |
| `ContentRichEditorRendererService` | `V2/Services/ContentRichEditor/Services/ContentRichEditorRendererService.php` | AST → HTML no portal e no iframe de preview. |
| `PublicIndexerService` / `CMSIndexerService` | `V2/Services/Search/Services/PublicIndexerService.php`, `V2/Services/Search/Services/CMSIndexerService.php` | Upsert/remoção nos índices. |
| `GalleryMedia` | `V2/Services/Gallery/Models/GalleryMedia.php` | Capa e galeria (`v2_gallery_media`). |
| `MediaInputer` / componentes | `V2/Services/Media/Services/MediaInputer.php` | Upload e render `<picture>`. |
| `MailService` | `V2/Services/MailService/MailService.php` | Envio da notificação de submissão. |
| `V2AuditLogger` | `V2/Core/Services/V2AuditLogger.php` | `activity($model, $action, $props)`. |

## 4. Middleware aplicado

| Middleware | Onde é registrado | Efeito observável |
|---|---|---|
| `web` | provider (`V2/Core/Providers/V2ServiceProvider.php:516`) e arquivo de rotas | Sessão, CSRF, cookies. |
| `V2\Core\Middleware\MaintenanceMiddleware` | provider (`:516`) | Portal em manutenção responde com bloqueio. |
| `module.active:Noticias` | provider (`:522`) | Módulo desativado → 404 no portal e redirect no CMS (`V2/Core/Middleware/ModuleAccessMiddleware.php:45-71`). |
| `v2.authenticate` (`CmsAuthenticate`) | `V2/Modules/Publicacoes/Noticias/Routes/cms.php:12` | Exige login Sentinel e ao menos um papel; depois delega ao middleware de permissão (`V2/Core/Middleware/CmsAuthenticate.php:44`). |
| `v2.permission:noticias` | `V2/Modules/Publicacoes/Noticias/Routes/cms.php:12` | Lê `v2.modules.configs`, resolve a permissão do usuário e bloqueia escrita para `read` (`V2/Core/Middleware/V2PermissionMiddleware.php:79-127`). |

O parâmetro `noticias` do middleware **não é usado**: o middleware casa o módulo por `active_pattern` (`V2/Core/Middleware/V2PermissionMiddleware.php:40-48`). A chave efetiva é `Noticias`, com resolução por aliases/`resolvePermission`.

## 5. Dependências externas

| Pacote | Para que é usado aqui |
|---|---|
| `cartalyst/sentinel` | Login do CMS, usuário autor/revisor (`Sentinel::getUser()`). |
| `erusev/parsedown` | `MarkdownToTiptapConverter` (importação/coleções legadas). |
| Bootstrap 5 + Bootstrap Icons | Markup do CMS e do portal (classes `bi`, `card`, `btn`). |
| Redis (facade) | Cache de página e cache do sitemap. |
| PostgreSQL | `ilike`, `jsonb`, `tsvector`, comparação composta de UUID/data. |

## 6. Acoplamento com outros módulos e services

| Origem/destino | Tipo | Onde acontece |
|---|---|---|
| Notícias → Search | Chamada direta de service | `News::saved` → `NewsSearchSender` → indexadores. |
| Notícias → SEO | Chamada direta | `NoticiasPortalController::show/preview`, `NoticiasCmsController::preview`. |
| Notícias → Gallery/Media | FK lógica por ID | `cover_image_id`, pivot `v2_news_gallery_media`; componentes `media-input`/`media-group-input`. |
| Notícias → ContentRichEditor | AST + renderer | `content` jsonb e `toHtml()`. |
| Notícias → MailService | Chamada direta | `NewsNotificationService::notifyApprovers()`. |
| Notícias → layouts | Views | `v2-cms::layouts.app`, `v2-portal::app-layout`. |
| Notícias → LayoutBase | Leitura de configuração | Logo do e-mail lido de `v2_layout_base_configs` (`V2/Modules/Publicacoes/Noticias/Services/NewsNotificationService.php:145-169`). |
| Notícias → BasicPage | Conflito de rota | A rota catch-all `/{slug}` é registrada depois; os testes chamam o controller direto para não colidir. |
| Notícias ← BasicPage/SEO | Link | `robots.txt` anuncia `/news-sitemap.xml` (`V2/Services/SEO/Controllers/SeoPortalController.php:24`). |

Não há import de outros **módulos**; o módulo conversa com services e Core, o que está aderente à fronteira do V7 (ADR-013).

## 7. Pontos de entrada alternativos

- **Eventos de model** (`News::boot`): `creating`, `saving`, `saved` — efeitos colaterais de slug, datas, token e indexação. Não há observers, jobs nem eventos nomeados.
- **Comandos artisan:** nenhum específico do módulo. Importação legada usa colunas `v1_id`/`imported_at`, mas o importador não vive nesta pasta.
- **Tarefas agendadas:** nenhuma; o sitemap expira por TTL e por janela de horário no próprio request.
- **Webhooks/callbacks:** nenhum.
- **AJAX:** `toggle-highlight` (JSON), tags (`/tags`), categorias (`/categorias/json`), e o editor envia `postMessage` para o iframe de preview.

## 8. Diagrama de dependências internas do model

```text
News
├── author()   -> App\Admin\User (author_id, sem FK)
├── reviewer() -> App\Admin\User (reviewer_id, sem FK)
├── coverImage()-> GalleryMedia  (cover_image_id, sem FK)
├── category() -> NewsCategory
├── tags()     -> NewsTag   (v2_news_tag_pivot)
└── gallery()  -> GalleryMedia (v2_news_gallery_media + sort_order)
```

Os três vínculos sem FK (`author_id`, `reviewer_id`, `cover_image_id`) são intencionais: `users.id` é inteiro e os IDs do módulo são UUID; `cover_image_id` é `bigint` em `v2_gallery_media` e string em `v2_news`. A integridade é mantida apenas na aplicação — ver `02-MODELAGEM.md` e `08-GAPS-E-DIVIDAS.md`.
