# 01 — Arquitetura do Módulo Diário Oficial

---

## 1. Fluxo da requisição

### 1.1 CMS — criação/edição de uma edição

```
POST /cms/diario-oficial                      (store)
  ├─ group 'cms' + middleware check.route.permission
  ├─ check.logged (global do grupo /cms)
  ├─ csrf
  ▼
Prefecture\DiarioController@store
  ├─ DiarioOficialRequest  (edicao, autor, email, data_publicacao obrigatórios;
  │                         columns_number in 1,2,3; line_style; line_color; capa)
  ├─ merge status_workflow = WORKFLOW_RASCUNHO (1)
  ├─ merge created_by_user_id = Sentinel::getUser()->id
  ▼
DiarioOficialRepository@upInsert  (via App\Repositories\Repository)
  ▼
App\Http\Model\Prefecture\Diario  →  tabela `diarios`
  ├─ attachCoverImage()  → Archive + storage/diario-oficial/covers/{id}/
  ├─ dispatchGeneration() → generation_status = 'pending'
  └─ activity()->log('Criação de edição')
  ▼
redirect → /cms/diario-oficial/{id}/edit
```

No `edit`, o operador:

1. Adiciona **matérias** (texto HTML via Summernote ou anexo PDF) no modal
   `diario-oficial/modal/areas/modal.blade.php`, via `DiarioAreaController`.
2. Reordena as matérias (`PUT /cms/ajax/diario-oficial/{id}/sort`).
3. Clica em **"Processar PDF"** → `GET /refresh-diario-preview/{id}`.

### 1.2 CMS — geração do PDF (sob demanda, síncrona)

```
GET /refresh-diario-preview/{id}?force=1
  ▼
Prefecture\DiarioController@refresh_PDF_preview
  ├─ se generation_status == 'completed' e arquivo existe → 200 "PDF já gerado"
  ├─ generation_status = 'processing'; zera progress; started_at = now()
  ▼
DiarioOficialRepository@generatePdfFile($id)
  ├─ @set_time_limit(0)
  ├─ carrega Diario, DiarioConfig, DiarioCertificado (do diário tem precedência)
  ├─ monta QR Code → public/img/qrcode.png
  ├─ App\Services\FPdfService (extends Fpdi/TCPDF)
  │    ├─ App\Services\Pdf\PdfConverter   (PDF → JPEG via Imagick + Ghostscript)
  │    └─ App\Services\Pdf\ImageProcessor (dimensões em mm)
  ├─ escrita atômica: Output('S') → .tmp → move
  └─ cleanupMergedPdfs()
  ▼
update por query: generation_status = 'completed' | 'failed'
```

Durante a execução, o `progressCallback` grava `generation_progress` (JSON) em
`diarios.generation_progress`. O front faz polling em
`GET /cms/ajax/diario-generation-status/{id}`.

### 1.3 CMS — workflow de publicação

```
PUT /cms/diario-oficial/{id}/publish
  ▼
Prefecture\DiarioController@publish
  ├─ exige status_workflow == WORKFLOW_EM_REVISAO (2), senão 422
  ├─ exige que created_by_user_id e updated_by_user_id != usuário atual, senão 403
  ├─ status_workflow = 3 ; status = true ; permission_edit = false
  ├─ published_by_user_id ; published_at = now()
  └─ activity()->log('Homologação de publicação')
```

O botão "enviar para revisão" no `update()` grava
`status_workflow = WORKFLOW_EM_REVISAO` quando `acao = enviar_revisao`.

### 1.4 Portal público

```
GET /diario-oficial            → listagem paginada (10 por página)
GET /diario-oficial/filtro     (POST) → filtro por datas, termo e edição
GET /diario-oficial/{id}       → viewer; se o PDF não existe em disco,
                                 marca generation_status = 'pending'
GET /diario-oficial/viewer/search        → busca por termo entre edições
GET /diario-oficial/viewer/{id}/file-info → tamanho e nº de páginas
GET /diario-oficial/viewer/{id}/search-pdf → busca dentro do PDF (Ghostscript txtwrite)
GET /diario-oficial/{id}/auditoria        → trilha de auditoria pública (LGPD)
```

## 2. Diagrama do fluxo de geração

```
                    ┌────────────────────────────────────────┐
  DiariosPublicados │                                        │
  (diarios, status) │                                        ▼
                    │                        ┌───────────────────────────┐
                    │                        │ Repo de config do diário  │
                    │                        │ (1 registro, id=1)        │
                    │                        └─────────────┬─────────────┘
                    │                                      │ certificado padrão
                    ▼                                      ▼
        ┌───────────────────┐         ┌──────────────────────────────────┐
        │  diario_has_areas │         │  App\Services\FPdfService        │
        │  (matérias)       │────────▶│  (TCPDF + FPDI)                  │
        │  + diario_areas   │         │  Header / Footer / Capas /       │
        │  + diario_sub_areas│        │  Capítulos / Anexos / Assinatura │
        └───────────────────┘         └────────┬──────────────┬──────────┘
                                               │              │
                              ┌────────────────▼───┐   ┌──────▼──────────────┐
                              │ Pdf\PdfConverter    │   │ Pdf\ImageProcessor │
                              │ (Imagick+Ghostscript)│  │ (dimensões em mm)  │
                              └─────────────────────┘   └─────────────────────┘
                                               │
                                               ▼
                              storage/app/public/diario-oficial/content/
                                    {id}/pdf/diário-{edicao}.pdf
```

## 3. Classes envolvidas

### 3.1 Models — `v6/app/Http/Model/Prefecture/`

| Classe | Arquivo | Tabela | Papel |
|--------|---------|--------|-------|
| `Diario` | `Diario.php` | `diarios` | Edição do diário; relations `certificado`, `complemento`, `diario_areas`, `areas`, `subAreas`, `cover_image`; constantes `WORKFLOW_RASCUNHO/EM_REVISAO/PUBLICADO` |
| `DiarioArea` | `DiarioArea.php` | `diario_areas` | Área (categoria de seção) |
| `DiarioSubArea` | `DiarioSubArea.php` | `diario_sub_areas` | Subárea |
| `DiarioHasAreas` | `DiarioHasAreas.php` | `diario_has_areas` | Matéria (pivot diário ↔ área/subárea) com conteúdo, anexo, tipo e ordem |
| `DiarioCertificado` | `DiarioCerticado.php` | `diario_certificados` | Certificado digital (arquivo com **typo**; classe correta) |
| `DiarioConfig` | `DiarioConfig.php` | `diario_configs` | Configuração global (registro único) |

### 3.2 Controllers — `v6/app/Http/Controllers/`

| Classe | Arquivo | Papel |
|--------|---------|-------|
| `Prefecture\DiarioController` | `Prefecture/DiarioController.php` | CRUD da edição, workflow, capa, geração, configurações, ordenação |
| `Prefecture\DiarioAreaController` | `Prefecture/DiarioAreaController.php` | CRUD de **áreas** e de **matérias** (mesmo controller) |
| `Prefecture\DiarioSubAreaController` | `Prefecture/DiarioSubAreaController.php` | CRUD de subáreas |
| `Prefecture\DiarioCertificadosController` | `Prefecture/DiarioCertificadosController.php` | CRUD de certificados digitais |
| `Prefecture\DatatableController` | `Prefecture/DatatableController.php` | Fornece dados das DataTables do CMS (`get_diario_oficial`, `get_areas_diario`, `get_sub_areas_diario`) |
| `Portal\DiarioController` | `Portal/DiarioController.php` | Portal público: listagem, viewer, busca, auditoria |

### 3.3 Repositórios — `v6/app/Repositories/`

| Classe | Arquivo | Papel |
|--------|---------|-------|
| `DiarioOficialRepository` | `Eloquent/Prefecture/DiarioOficial/DiarioOficialRepository.php` | CRUD + **geração do PDF** + trilha de auditoria pública |
| `AreaRepository` | `Eloquent/Prefecture/DiarioOficial/Area/AreaRepository.php` | Matérias (`diario_has_areas`) |
| `DiarioAreaRepository` | `Eloquent/Prefecture/DiarioOficial/DiarioArea/DiarioAreaRepository.php` | Áreas |
| `DiarioSubAreaRepository` | `Eloquent/Prefecture/DiarioOficial/DiarioSubArea/DiarioSubAreaRepository.php` | Subáreas |
| `DiarioConfigRepository` | `Eloquent/Prefecture/DiarioConfig/DiarioConfigRepository.php` | Configurações |
| `IDiarioAreaRepository` | `Contracts/IDiarioAreaRepository.php` | Contrato |
| `IDiarioSubAreaRepository` | `Contracts/IDiarioSubAreaRepository.php` | Contrato |
| `IDiarioConfigRepository` | `Contracts/IDiarioConfigRepository.php` | Contrato |
| `Repository` (base) | `Repository.php` | `upInsert`, `destroy`, `destroyAll`, `filter_request`, `structureUpInsert` abstrato |

Bindings em `v6/app/Providers/RepositoryServiceProvider.php`.

### 3.4 Services — `v6/app/Services/`

| Classe | Arquivo | Papel |
|--------|---------|-------|
| `FPdfService` | `FPdfService.php` | Monólito de PDF (374 linhas): herda `setasign\Fpdi\Tcpdf\Fpdi`; header, footer, capa, capítulos, anexos, assinatura, colunas |
| `Pdf\PdfConverter` | `Pdf/PdfConverter.php` | PDF externo → JPEG (Imagick + Ghostscript); contagem de páginas com fallback FPDI → Imagick |
| `Pdf\ImageProcessor` | `Pdf/ImageProcessor.php` | Dimensionamento e embutimento de imagens do HTML (em mm) |

> No **v5** existiam ainda `v5/app/Services/Pdf/LayoutManager.php`,
> `v5/app/Services/Pdf/HeaderRenderer.php` e
> `v5/app/Services/Pdf/CoverHandler.php`. O v6 concentrou tudo em
> `FPdfService` (ver `90-LEGADO/20-SERVICOS_PDF.md`).

### 3.5 Requests, Resources, Presenters, Components

| Classe | Arquivo | Papel |
|--------|---------|-------|
| `DiarioOficialRequest` | `v6/app/Http/Requests/DiarioOficialRequest.php` | Valida o formulário da edição |
| `DiarioAreasRequest` | `v6/app/Http/Requests/DiarioAreasRequest.php` | Valida o nome da área/subárea |
| `CertificadoRequest` | `v6/app/Http/Requests/CertificadoRequest.php` | Valida upload do certificado + senha |
| `SortRequest` | `v6/app/Http/Requests/DiarioOficial/SortRequest.php` | Valida a reordenação das matérias |
| `SaveAreaRequest` | `v6/app/Http/Requests/DiarioOficial/SaveAreaRequest.php` | Valida matéria (texto ou PDF) |
| `SaveConfigRequest` | `v6/app/Http/Requests/DiarioOficial/SaveConfigRequest.php` | Valida as configurações globais |
| `GetDiarioResource` | `v6/app/Http/Resources/DiarioOficial/GetDiarioResource.php` | JSON de áreas + matérias do diário |
| `ListAreasAndSubareasResource` | `v6/app/Http/Resources/DiarioOficial/ListAreasAndSubareasResource.php` | Item de área/subárea |
| `ListAreaContentResource` | `v6/app/Http/Resources/DiarioOficial/ListAreaContentResource.php` | Item de matéria |
| `DiarioPresenter` | `v6/app/Http/Presenters/DiarioPresenter.php` | Formata `data_publicacao` em `dd/mm/aaaa` |
| `DiarioOficialComponent` | `v6/app/Http/Components/Prefecture/Contents/Preview/DiarioOficialComponent.php` | Componente de preview no CMS |

### 3.6 Regras e traits

| Item | Arquivo | Papel |
|------|---------|-------|
| `SelectRule` | `v6/app/Rules/SelectRule.php` | Regra usada em `id_certificado` |
| `FileTrait` | `v6/app/Http/Traits/FileTrait.php` | Compressão de imagens (`compress`) |
| `AppTrait` | `v6/app/Http/Traits/AppTrait.php` | Template ativo do portal, formatação de data, flags |

## 4. Dependências externas

| Pacote / extensão | Versão | Uso no módulo |
|-------------------|--------|---------------|
| `setasign/fpdi` | `^2.3` | Importação de páginas de PDF (anexos `file_in = false`) |
| `tecnickcom/tcpdf` | `^6.3` | Motor de renderização do diário (texto, colunas, cabeçalho, capa) |
| `simplesoftwareio/simple-qrcode` | `^2.0` | QR Code impresso no cabeçalho |
| `imagick` (extensão PHP) | — | Rasterização de PDF e contagem de páginas |
| **Ghostscript** (binário do SO) | — | Conversão PDF → texto e PDF → imagem |
| OpenSSL (extensão + CLI) | — | Leitura do `.pfx`/`.p12` e extração de CN/CNPJ |
| `cartalyst/sentinel` | — | Autenticação e permissões |
| `yajra/laravel-datatables` | — | Listagens do CMS |
| `spatie/laravel-activitylog` (via `activity()`) | — | Trilha de auditoria editorial e pública |
| `laracasts/presenter` (`PresentableTrait`) | — | `DiarioPresenter` |

Dependências de infra: `public/img/qrcode.png` (gravável), diretórios
`storage/app/public/diario-oficial/{covers,headers,content,adendos}` e
`storage/app/diario-oficial/pdf-text` (cache do texto extraído do PDF).

## 5. Onde o módulo encosta em outros módulos

| Módulo externo | Ponto de encosto |
|----------------|------------------|
| **Configurações da instituição** | `Configuration` (`titulo_site`, `cidade`, `logradouro`, `numero`, `bairro`, `estado`, `cep`, `favicon`) e a chave `diario_oficial_segregacao_funcoes` |
| **Arquivos (`archives`)** | Imagens de capa (`cover_image_id`) e de cabeçalho (`diario_configs.header_img`) |
| **Usuários** | `created_by_user_id`, `updated_by_user_id`, `published_by_user_id` (FK `users.id`) |
| **Categorias de conteúdo** | `CategoryContent` com slug `diario-oficial` (`v6/database/seeds/CategoryContentTableSeeder.php`) |
| **Menu e recursos (CMS)** | `MenuServiceProvider` (case `diario-oficial`), `ResourcesTableSeeder` (slug `diario-oficial`, `diario-area`, `diario-subarea`, `certificado`), `MenuOrderSeeder` |
| **Dashboard** | `v6/app/Services/Dashboard/Widgets/DiarioWidget.php` e `WidgetRegistry.php` (widget `diario`) |
| **Auditoria / atividade** | `activity()` em criação, edição, reordenação, publicação e visualização no portal |

## 6. Módulos do sistema (tabela `modulos`)

O módulo é identificado por **dois** registros na tabela `modulos`, criados por
`v6/database/migrations/2024_11_01_150206_create_modulos_table.php`:

| id | name | slug | status | Observação |
|---:|------|------|:------:|------------|
| 16 | Diario Oficial | `diario-ofifical` | 0 | **Typo** ("ofifical"). Só aparece nesta migration; nenhum código-fonte referencia esse slug |
| 37 | Diário Oficial | `diario-oficial` | 0 | Slug **real**, usado por `ResourcesTableSeeder`, `MenuServiceProvider`, `Dashboard\WidgetRegistry` |

Consequências verificadas:

- `v6/database/seeds/ResourcesTableSeeder.php` aponta `modulo_id = 37` para os
  recursos `diario-oficial`, `diario-area`, `diario-subarea` e `certificado`.
- `v6/database/seeds/MenuOrderSeeder.php` aponta `modulo_id = 16` para os
  `menu_orders` de slug `certificado` e `diario-oficial`.
- O menu do CMS (`v6/app/Providers/MenuServiceProvider.php:215`) monta o
  submenu pelo `case 'diario-oficial'`, com os itens "Diários",
  "Áreas do Diário" e "Subáreas do Diário", cada um guardado por
  `has_access()`.

Detalhamento em `08-GAPS-E-DIVIDAS.md`.
