# Concursos e PSS — Arquitetura

> Módulo existente **apenas na v5**. Referência: `v5/`.
> A v6 **não possui** controllers, models, requests, exports, views ou rotas deste módulo.

---

## 1. Visão geral das duas faces

O módulo tem duas entradas independentes que compartilham a mesma tabela:

```
┌──────────────────── CMS (autenticado) ────────────────────┐   ┌──────── Portal (público) ────────┐
│ /cms/concursos-pss                                        │   │ /concursos-pss                    │
│  └─BST► ConcursoPssController (Prefecture)                │   │  └─► ConcursosPssController (Portal)│
│      └─► ConcursoPssRequest → ConcursoPss (Eloquent)     │   │      └─► ConcursoPss (Eloquent)   │
│          └─► view prefecture.concursos-pss.*              │   │          └─► view portal.layouts.* │
│                                                           │   │              .concursos-pss.*      │
│ /cms/datatable/concurso-pss                                │   │  └─► ConcursosPssExport (xls/csv) │
│  └─► DatatableController@get_concurso_pss                 │   │  └─► dompdf (pdf) / Response (txt) │
│      └─► DB::table('concursos_pss_view')                   │   │                                   │
└───────────────────────────────────────────────────────────┘   └───────────────────────────────────┘
```

## 2. Fluxo de requisição

### 2.1 CMS — listagem (DataTable)

```
GET /cms/concursos-pss
  → middleware check.logged                (v5/routes/basepro.php:236)
  → middleware check.route.permission      (v5/app/Http/Controllers/Prefecture/Controller.php:22)
  → ConcursoPssController@index           (v5/app/Http/Controllers/Prefecture/ConcursoPssController.php:17)
  → view('prefecture.concursos-pss.index') (v5/resources/views/prefecture/concursos-pss/index.blade.php)
  → JS: assets/js/custom/config/concursos-pss/datatable.js
  → GET /cms/datatable/concurso-pss  (v5/routes/basepro.php:423)
      → DatatableController@get_concurso_pss (v5/app/Http/Controllers/Prefecture/DatatableController.php:194)
      → DB::table('concursos_pss_view')->get()
      → struct_views($data, 'concurso-pss')  → flags edit/delete/active/inactivate
      → DataTables::of(...)->make(true)       (Yajra, resposta JSON)
```

### 2.2 CMS — cadastro/edição

```
GET  /cms/concursos-pss/cadastrar  → ConcursoPssController@create → view create
POST /cms/concursos-pss            → ConcursoPssRequest → prepareData → upload PDFs
                                    → ConcursoPss::create() → flash → redirect index
GET  /cms/concursos-pss/editar/{id} → ConcursoPssController@edit   → view edit
PUT  /cms/concursos-pss/atualizar/{id} → ConcursoPssRequest → merge de arquivos → update()
```

### 2.3 Portal — listagem, detalhe e exportação

```
GET /concursos-pss?ano=&tipo=&busca=
  → middleware check.site.manutention (v5/routes/web.php:5)
  → ConcursosPssController@index (v5/app/Http/Controllers/Portal/ConcursosPssController.php:15)
  → query Eloquent com when(ano/tipo/busca) + ordenação
  → ?type=  → export(xls|pdf|csv|txt)
  → senão   → paginate($result->all(), 15, page)  (em memória)
  → view portal.layouts.{layout}.concursos-pss.index

GET /concurso-pss/{slug}
  → ConcursosPssController@show → ConcursoPss::where('slug',$slug)->firstOrFail()
  → view portal.layouts.{layout}.concursos-pss.show
```

## 3. Diagrama (Mermaid)

```mermaid
flowchart TD
    A[GET /cms/concursos-pss] --> B[check.logged]
    B --> C[check.route.permission]
    C --> D[ConcursoPssController@index]
    D --> E[view prefecture.concursos-pss.index]
    E --> F[datatable.js]
    F -->|GET /cms/datatable/concurso-pss| G[DatatableController@get_concurso_pss]
    G --> H[(concursos_pss_view)]

    I[POST /cms/concursos-pss] --> J[ConcursoPssRequest]
    J --> K[prepareData]
    K --> L[FileTrait::saveUpload → storage/docs]
    L --> M[(concursos_pss)]
    M --> N[redirect concurso-pss.index]

    O[GET /concursos-pss] --> P[check.site.manutention]
    P --> Q[ConcursosPssController@index]
    Q --> R[(concursos_pss)]
    Q --> S{?type=}
    S -->|xls/csv| T[ConcursosPssExport / FromView]
    S -->|pdf| U[dompdf loadView exports.concursos-pss]
    S -->|txt| V[Response::make]
    S -->|ausente| W[LengthAwarePaginator 15/página]
    W --> X[view portal.layouts.*.concursos-pss.index]
```

## 4. Classes envolvidas

| Classe | Caminho | Papel |
|---|---|---|
| `ConcursoPssController` | `v5/app/Http/Controllers/Prefecture/ConcursoPssController.php` | CRUD do CMS, upload/remoção de PDFs. Usa `FileTrait`. |
| `ConcursosPssController` | `v5/app/Http/Controllers/Portal/ConcursosPssController.php` | Listagem pública, filtros, detalhe, 4 exportações. Estende `App\Http\Controllers\Admin\Controller` (herda `getTemplateActived()`). |
| `ConcursoPss` | `v5/app/Http/Model/Prefecture/ConcursoPss.php` | Model Eloquent, `SoftDeletes`, `$table = 'concursos_pss'`, gera `slug` em `created`/`updating`. |
| `ConcursoPssRequest` | `v5/app/Http/Requests/ConcursoPssRequest.php` | Validação do formulário do CMS. |
| `ConcursosPssExport` | `v5/app/Exports/ConcursosPssExport.php` | Exportação Excel/CSV via `FromView` (Maatwebsite). |
| `DatatableController` | `v5/app/Http/Controllers/Prefecture/DatatableController.php` | Método `get_concurso_pss()` (linha 194) — fonte JSON do DataTable do CMS. |
| `Controller` (Prefecture) | `v5/app/Http/Controllers/Prefecture/Controller.php` | Aplica `check.route.permission` no construtor. |
| `Controller` (Admin) | `v5/app/Http/Controllers/Admin/Controller.php` | `getTemplateActived()` — lê `configurations.layout` e resolve o tema do portal. |
| `FileTrait` | `v5/app/Http/Traits/FileTrait.php` | `saveUpload()`, `getFileName()`, `deleteFile()`. |
| `MenuOrder` | `v5/app/Http/Model/Prefecture/MenuOrder.php` | Contador de cliques do menu (linha criada pela migration, nunca usada por este módulo). |

## 5. Funções auxiliares e dependências

| Símbolo | Origem | Uso no módulo |
|---|---|---|
| `flash()` | `v5/vendor/laracasts/flash/src/Laracasts/Flash/functions.php` | Mensagens de sucesso/erro no CMS (`->success()`, `->error()`). |
| `clean_slug()` | `v5/app/Helpers/functions.php:780` | Normaliza acentos da busca textual do portal. |
| `has_access()` | `v5/app/Library/Helper/appHelpers.php:22` | Exibe o botão "Cadastrar" e controla permissões. |
| `menuClicks()` | `v5/app/Helpers/functions.php:822` | **Não usada** neste módulo (ver `08-GAPS-E-DIVIDAS.md`). |
| `activity()` | `v5/vendor/spatie/laravel-activitylog/src/helpers.php` | **Não usada** neste módulo — não há trilha de auditoria. |
| `str_slug()` | Laravel 5.5 (`Illuminate\Support\Str`) | Geração do slug no model. |
| `clean_slug()` + `translate()` | SQL PostgreSQL | Busca sem acento no portal. |

### Pacotes Composer relevantes (`v5/composer.json`)

| Pacote | Versão | Uso |
|---|---|---|
| `laravel/framework` | 5.5.* | Base. |
| `cartalyst/sentinel` | 3.x | Autenticação e permissões (`hasAnyAccess`). |
| `maatwebsite/excel` | ^3.1 | Exportação `.xlsx`/`.csv`. |
| `barryvdh/laravel-dompdf` | ^0.8.7 | Exportação `.pdf`. |
| `yajra/laravel-datatables` | ^1.5 | DataTable server-side do CMS. |
| `laracasts/flash` | 3.0.* | Mensagens de sessão. |
| `spatie/laravel-activitylog` | ^2.8 | Disponível, **não usado** aqui. |

## 6. Middlewares aplicados

| Middleware | Escopo | Comportamento |
|---|---|---|
| `check.logged` | Rotas CMS (`v5/routes/basepro.php:236`) | Redireciona para `dashboard.index` se não autenticado. |
| `check.route.permission` | Todos os controllers `Prefecture\*` | `super-admin` passa sempre; demais precisam de `concurso-pss.<ação>`. |
| `check.site.manutention` | Todas as rotas do portal (`v5/routes/web.php:5`) | Exibe `portal.manutencao.index` se `configurations.pagina_em_manutencao == 1` e o visitante não estiver logado. |
| `active.landing` | Registrado no kernel, **não aplicado** a este módulo. | — |

## 7. Ausências estruturais (o que o módulo **não** tem)

- Nenhuma **Repository** ou **Service** — o CRUD fala direto com o Model.
- Nenhuma **API Resource** / **Presenter** — a saída é Blade + JSON do Yajra.
- Nenhuma **Policy** — a autorização é o par `resources`/`roles.permissions` do Sentinel.
- Nenhuma **Fila** (Job), **Evento** ou **Observer** além dos hooks `created`/`updating` do model.
- Nenhum **Teste** — `v5/tests/Feature/` contém apenas `ExampleTest.php`.
