npm package: @hed-hog/ticket
Description: O módulo Ticket centraliza as interações de suporte omnichannel para a plataforma Hcode. Ele oferece modelagem completa do banco de dados, interfaces administrativas estáticas e governança de acessos, canais e configurações. Atualmente, o backend possui a
Latest version: 0.0.303

# Módulo Ticket (@hed-hog/ticket)

## 1. Visão Geral do Módulo

O módulo Ticket centraliza as interações de suporte omnichannel para a plataforma Hcode. Ele oferece modelagem completa do banco de dados, interfaces administrativas estáticas e governança de acessos, canais e configurações. Atualmente, o backend possui apenas scaffolding e mocks, sem integrações reais ou lógica de negócio implementada.

## 2. Escopo e Responsabilidades

- Gerenciamento de tickets de atendimento (criação, atualização, respostas, notas internas).
- Administração de canais e contas de canais de comunicação.
- Configuração de parâmetros globais e específicos para canais.
- Controle de acesso baseado em papéis (roles).
- Visualização de dashboards, filas de revisão AI, relatórios e matrizes de permissão.
- Suporte a metadados, tags, categorias e contexto dos tickets.

## 3. Endpoints

### Ticket Data Controller

| Método | Path           | Autenticação | Descrição                          | Parâmetros / Body | Resposta                  | Erros Comuns          |
|--------|----------------|--------------|----------------------------------|-------------------|---------------------------|-----------------------|
| GET    | /ticket/dashboard | Sim (admin, admin-ticket, analyst-ticket) | Retorna dados do dashboard | -                 | `{ items: [] }`           | 401 Unauthorized      |
| GET    | /ticket/inbox     | Sim (admin, admin-ticket, operator-ticket, analyst-ticket) | Retorna a caixa de entrada | -                 | `{ items: [] }`           | 401 Unauthorized      |
| GET    | /ticket/ai-review | Sim (admin, admin-ticket, operator-ticket) | Retorna fila de revisão AI  | -                 | `{ items: [] }`           | 401 Unauthorized      |
| GET    | /ticket/reports   | Sim (admin, admin-ticket, analyst-ticket) | Retorna relatórios          | -                 | `{ items: [] }`           | 401 Unauthorized      |
| GET    | /ticket/roles     | Sim (admin, admin-ticket)         | Retorna matriz de papéis    | -                 | `{ items: [] }`           | 401 Unauthorized      |
| GET    | /ticket           | Sim (admin, admin-ticket, operator-ticket, analyst-ticket) | Lista tickets              | -                 | `{ items: [] }`           | 401 Unauthorized      |

---

### Ticket Items Controller

| Método | Path               | Autenticação | Descrição                          | Parâmetros / Body                              | Resposta                         | Erros Comuns          |
|--------|--------------------|--------------|----------------------------------|------------------------------------------------|---------------------------------|-----------------------|
| GET    | /ticket/:id         | Sim (admin, admin-ticket, operator-ticket, analyst-ticket) | Obtém ticket por ID                      | `id` (path)                              | `{ id: number }`                  | 401 Unauthorized, 404 Not Found |
| POST   | /ticket/:id/reply   | Sim (admin, admin-ticket, operator-ticket) | Cria resposta pública para ticket       | `{ subject?: string, body?: string }`    | `{ id, subject?, body? }`         | 401 Unauthorized, 400 Bad Request |
| POST   | /ticket/:id/internal-note | Sim (admin, admin-ticket, operator-ticket) | Cria nota interna para ticket           | `{ body?: string }`                      | `{ id, body? }`                   | 401 Unauthorized, 400 Bad Request |
| PATCH  | /ticket/:id/assign  | Sim (admin, admin-ticket, operator-ticket) | Atribui proprietário ao ticket          | `{ owner_person_id?: number }`            | `{ id, owner_person_id? }`        | 401 Unauthorized, 400 Bad Request |
| PATCH  | /ticket/:id/status  | Sim (admin, admin-ticket, operator-ticket) | Atualiza status do ticket                | `{ status?: string }`                      | `{ id, status? }`                 | 401 Unauthorized, 400 Bad Request |
| PATCH  | /ticket/:id/priority| Sim (admin, admin-ticket, operator-ticket) | Atualiza prioridade do ticket            | `{ priority?: string }`                    | `{ id, priority? }`               | 401 Unauthorized, 400 Bad Request |

---

### Ticket Channels Controller

| Método | Path               | Autenticação | Descrição                          | Parâmetros / Body                              | Resposta                         | Erros Comuns          |
|--------|--------------------|--------------|----------------------------------|------------------------------------------------|---------------------------------|-----------------------|
| GET    | /ticket/channels    | Sim (admin, admin-ticket)         | Lista canais                       | -                                              | `{ items: [] }`                 | 401 Unauthorized      |
| POST   | /ticket/channels    | Sim (admin, admin-ticket)         | Cria novo canal                   | `{ code?: string, name?: string, kind?: string, status?: string }` | Dados do canal criado           | 401 Unauthorized, 400 Bad Request |
| PATCH  | /ticket/channels/:id| Sim (admin, admin-ticket)         | Atualiza canal existente          | `id` (path), `{ name?: string, status?: string }` | Dados atualizados               | 401 Unauthorized, 400 Bad Request, 404 Not Found |
| DELETE | /ticket/channels/:id| Sim (admin, admin-ticket)         | Remove canal                     | `id` (path)                                    | `{ id, deleted: true }`          | 401 Unauthorized, 404 Not Found |

---

### Ticket Channel Accounts Controller

| Método | Path                      | Autenticação | Descrição                          | Parâmetros / Body                                                                 | Resposta                         | Erros Comuns          |
|--------|---------------------------|--------------|----------------------------------|----------------------------------------------------------------------------------|---------------------------------|-----------------------|
| GET    | /ticket/channel-accounts   | Sim (admin, admin-ticket)         | Lista contas de canais           | -                                                                                | `{ items: [] }`                 | 401 Unauthorized      |
| POST   | /ticket/channel-accounts   | Sim (admin, admin-ticket)         | Cria conta de canal             | `{ channel_id?: number, code?: string, name?: string, external_identifier?: string, status?: string, ai_enabled?: boolean, ai_model?: string }` | Dados da conta criada           | 401 Unauthorized, 400 Bad Request |
| PATCH  | /ticket/channel-accounts/:id | Sim (admin, admin-ticket)       | Atualiza conta de canal         | `id` (path), `{ name?: string, external_identifier?: string, status?: string, ai_enabled?: boolean, ai_model?: string }` | Dados atualizados               | 401 Unauthorized, 400 Bad Request, 404 Not Found |
| DELETE | /ticket/channel-accounts/:id | Sim (admin, admin-ticket)       | Remove conta de canal           | `id` (path)                                                                      | `{ id, deleted: true }`          | 401 Unauthorized, 404 Not Found |

---

### Ticket Settings Controller

| Método | Path             | Autenticação | Descrição                          | Parâmetros / Body                                                                 | Resposta                         | Erros Comuns          |
|--------|------------------|--------------|----------------------------------|----------------------------------------------------------------------------------|---------------------------------|-----------------------|
| GET    | /ticket/settings  | Sim (admin, admin-ticket)         | Obtém configurações gerais       | -                                                                                | `{ items: [] }`                 | 401 Unauthorized      |
| PATCH  | /ticket/settings  | Sim (admin, admin-ticket)         | Atualiza configurações gerais    | `{ enabled?: boolean, code_prefix?: string, default_ai_review_mode?: string, default_visibility?: string, auto_assign_enabled?: boolean, default_owner_person_id?: number, allow_public_reply?: boolean, allow_internal_notes?: boolean, default_reopen_on_customer_reply?: boolean, default_close_after_days_without_reply?: number }` | Dados atualizados               | 401 Unauthorized, 400 Bad Request |

---

### Ticket AI Review Controller

| Método | Path           | Autenticação | Descrição                          | Parâmetros / Body | Resposta                  | Erros Comuns          |
|--------|----------------|--------------|----------------------------------|-------------------|---------------------------|-----------------------|
| GET    | /ticket/ai-review | Sim (admin, admin-ticket, operator-ticket) | Retorna fila de revisão AI  | -                 | `{ items: [] }`           | 401 Unauthorized      |

---

## 4. Regras de Autenticação e Autorização

- Todos os endpoints requerem autenticação.
- Papéis (roles) definidos para controle de acesso:
  - **admin-ticket**: Administração completa, acesso a canais, configurações e governança.
  - **operator-ticket**: Operação em tickets, respostas, notas internas, status e atribuições.
  - **analyst-ticket**: Acesso somente leitura para visualização e relatórios.
- A matriz de permissões está disponível via endpoint `/ticket/roles` e na interface administrativa.

## 5. Estruturas de Request/Response

### DTOs principais

- **AssignTicketOwnerDto**
  ```ts
  {
    owner_person_id?: number;
  }
  ```

- **CreateTicketChannelAccountDto**
  ```ts
  {
    channel_id?: number;
    code?: string;
    name?: string;
    external_identifier?: string;
    status?: string;
    ai_enabled?: boolean;
    ai_model?: string;
  }
  ```

- **CreateTicketChannelDto**
  ```ts
  {
    code?: string;
    name?: string;
    kind?: string;
    status?: string;
  }
  ```

- **CreateTicketInternalNoteDto**
  ```ts
  {
    body?: string;
  }
  ```

- **CreateTicketReplyDto**
  ```ts
  {
    subject?: string;
    body?: string;
  }
  ```

- **UpdateTicketChannelAccountDto**
  ```ts
  {
    name?: string;
    external_identifier?: string;
    status?: string;
    ai_enabled?: boolean;
    ai_model?: string;
  }
  ```

- **UpdateTicketChannelDto**
  ```ts
  {
    name?: string;
    status?: string;
  }
  ```

- **UpdateTicketPriorityDto**
  ```ts
  {
    priority?: string;
  }
  ```

- **UpdateTicketSettingsDto**
  ```ts
  {
    enabled?: boolean;
    code_prefix?: string;
    default_ai_review_mode?: string;
    default_visibility?: string;
    auto_assign_enabled?: boolean;
    default_owner_person_id?: number;
    allow_public_reply?: boolean;
    allow_internal_notes?: boolean;
    default_reopen_on_customer_reply?: boolean;
    default_close_after_days_without_reply?: number;
  }
  ```

- **UpdateTicketStatusDto**
  ```ts
  {
    status?: string;
  }
  ```

## 6. Erros Comuns

- **401 Unauthorized**: Usuário não autenticado ou sem permissão para o recurso.
- **400 Bad Request**: Dados inválidos ou incompletos no corpo da requisição.
- **404 Not Found**: Recurso (ticket, canal, conta) não encontrado para o ID informado.

## 7. Banco de Dados (Tabelas YAML)

### channel

- **Finalidade**: Define canais de comunicação (ex: portal, email, whatsapp).
- **Colunas**:
  - `id` (PK)
  - `code` (varchar 64, único)
  - `name` (varchar 160)
  - `kind` (enum: portal, marketplace, social, form, email, whatsapp, api, other)
  - `status` (enum: active, inactive, default active)
  - `created_at`, `updated_at`
- **Índices**: `code` (único), `kind`, `status`

---

### channel_account

- **Finalidade**: Contas específicas vinculadas a canais.
- **Colunas**:
  - `id` (PK)
  - `channel_id` (FK para channel.id, RESTRICT)
  - `code` (varchar 80, único)
  - `name` (varchar 180)
  - `external_identifier` (varchar 255, nullable)
  - `status` (enum: active, inactive, default active)
  - `settings_json` (json, nullable)
  - `created_at`, `updated_at`
- **Índices**: `code` (único), `channel_id`, `status`, `external_identifier`

---

### ticket

- **Finalidade**: Registro principal dos tickets de atendimento.
- **Colunas**:
  - `id` (PK)
  - `code` (varchar 40, único)
  - `subject` (varchar 255)
  - `status` (enum: open, pending, waiting_customer, resolved, closed, spam; default open)
  - `priority` (enum: low, normal, high, urgent; default normal)
  - `visibility` (enum: private, public; default private)
  - `ai_review_mode` (enum: disabled, optional, required; default optional)
  - `requester_person_id` (FK para person.id, nullable, SET NULL)
  - `owner_person_id` (FK para person.id, nullable, SET NULL)
  - Datas de mensagens e status (`first_message_at`, `last_message_at`, etc.)
  - `created_at`, `updated_at`
- **Índices**: `code` (único), `status`, `priority`, `visibility`, `ai_review_mode`, `requester_person_id`, `owner_person_id`, combinações para performance

---

### ticket_ai_draft

- **Finalidade**: Rascunhos e revisões AI para respostas.
- **Colunas**:
  - `id` (PK)
  - `ticket_id` (FK para ticket.id, CASCADE)
  - `ticket_message_id` (FK para ticket_message.id, nullable, SET NULL)
  - `model_name` (varchar 120)
  - `status` (enum: draft, pending_review, approved, rejected, sent; default draft)
  - `suggestion_html`, `suggestion_text`, `feedback` (text, nullable)
  - `reviewed_by_person_id` (FK para person.id, nullable, SET NULL)
  - `reviewed_at` (datetime, nullable)
  - `created_at`, `updated_at`
- **Índices**: `ticket_id`, `ticket_message_id`, `status`, `reviewed_by_person_id`, `created_at`

---

### ticket_attachment

- **Finalidade**: Anexos vinculados a mensagens de tickets.
- **Colunas**:
  - `id` (PK)
  - `ticket_message_id` (FK para ticket_message.id, CASCADE)
  - `kind` (enum: image, file, link)
  - `storage` (enum: upload, external_url)
  - `file_name`, `mime_type`, `url` (varchar, nullable)
  - `size_bytes` (int, nullable)
  - `content` (text, nullable)
  - `created_at`, `updated_at`
- **Índices**: `ticket_message_id`, `kind`, `storage`, `created_at`

---

### ticket_category

- **Finalidade**: Associação de tickets a categorias.
- **Colunas**:
  - `id` (PK)
  - `ticket_id` (FK para ticket.id, CASCADE)
  - `category_id` (FK para category.id, CASCADE)
  - `created_at`, `updated_at`
- **Índices**: combinação única `ticket_id, category_id`, índices individuais

---

### ticket_context

- **Finalidade**: Contexto externo relacionado ao ticket (curso, vídeo, página etc).
- **Colunas**:
  - `id` (PK)
  - `ticket_id` (FK para ticket.id, CASCADE, único)
  - `channel_account_id` (FK para channel_account.id, nullable, SET NULL)
  - `context_type` (enum: course_lesson, course, video, page, email_thread, whatsapp_chat, contact_form, other)
  - Campos opcionais para nomes e URLs contextuais
  - `created_at`, `updated_at`
- **Índices**: `ticket_id` (único), `channel_account_id`, `context_type`, campos de nome e label

---

### ticket_message

- **Finalidade**: Mensagens trocadas em tickets.
- **Colunas**:
  - `id` (PK)
  - `ticket_id` (FK para ticket.id, CASCADE)
  - `author_person_id` (FK para person.id, nullable, SET NULL)
  - `parent_message_id` (FK para ticket_message.id, nullable, SET NULL)
  - `direction` (enum: inbound, outbound, internal, system)
  - `audience` (enum: public, internal; default public)
  - `body_html`, `body_text` (text, nullable)
  - `source_created_at` (datetime, nullable)
  - `created_at`, `updated_at`
- **Índices**: `ticket_id`, `author_person_id`, `parent_message_id`, `direction`, `audience`, `created_at`

---

### ticket_message_source

- **Finalidade**: Origem e status de entrega das mensagens.
- **Colunas**:
  - `id` (PK)
  - `ticket_message_id` (FK para ticket_message.id, CASCADE)
  - `channel_account_id` (FK para channel_account.id, nullable, SET NULL)
  - `provider_message_id`, `provider_thread_id` (varchar, nullable)
  - `direction` (enum: inbound, outbound)
  - `delivery_status` (enum: pending, sent, delivered, failed, received, read; default pending)
  - `payload_json` (json, nullable)
  - `sent_at`, `received_at` (datetime, nullable)
  - `created_at`, `updated_at`
- **Índices**: `ticket_message_id`, `channel_account_id`, `provider_message_id`, `provider_thread_id`, `direction`, `delivery_status`, `created_at`

---

### ticket_metadata

- **Finalidade**: Metadados customizados para tickets.
- **Colunas**:
  - `id` (PK)
  - `ticket_id` (FK para ticket.id, CASCADE)
  - `key` (varchar 120)
  - `value` (text, nullable)
  - `created_at`, `updated_at`
- **Índices**: `ticket_id`, `key`, combinação `ticket_id, key`

---

### ticket_participant

- **Finalidade**: Participantes associados a tickets com papéis.
- **Colunas**:
  - `id` (PK)
  - `ticket_id` (FK para ticket.id, CASCADE)
  - `person_id` (FK para person.id, CASCADE)
  - `role` (enum: requester, agent, collaborator, watcher)
  - `created_at`, `updated_at`
- **Índices**: combinação única `ticket_id, person_id`, índices por `ticket_id, role` e `person_id, role`

---

### ticket_source

- **Finalidade**: Origem externa do ticket em contas de canal.
- **Colunas**:
  - `id` (PK)
  - `ticket_id` (FK para ticket.id, CASCADE)
  - `channel_account_id` (FK para channel_account.id, nullable, SET NULL)
  - `external_thread_id`, `external_message_id` (varchar, nullable)
  - `external_url` (varchar 500, nullable)
  - `status` (enum: active, archived, sync_error; default active)
  - `metadata_json` (json, nullable)
  - `created_at`, `updated_at`
- **Índices**: `ticket_id`, `channel_account_id`, `status`, `external_thread_id`, `external_message_id`

---

### ticket_tag

- **Finalidade**: Associação de tags a tickets.
- **Colunas**:
  - `id` (PK)
  - `ticket_id` (FK para ticket.id, CASCADE)
  - `tag_id` (FK para tag.id, CASCADE)
  - `created_at`, `updated_at`
- **Índices**: combinação única `ticket_id, tag_id`, índices individuais

## 8. Regras de Negócio Relevantes

- Tickets possuem status, prioridade e visibilidade configuráveis.
- Tickets podem ser atribuídos a um proprietário (owner_person_id).
- Mensagens podem ser públicas ou internas, com direções inbound/outbound.
- Canais e contas de canais possuem status ativo/inativo para controle.
- Revisões AI são gerenciadas via rascunhos com status e feedback.
- Configurações globais e específicas por canal são armazenadas e gerenciadas.
- Papéis definem níveis de acesso e permissões para operações no módulo.

## 9. Guia Rápido de Uso (Exemplos)

### Listar tickets (GET /ticket)

```bash
curl -H "Authorization: Bearer <token>" https://api.seudominio.com/ticket
```

Resposta:

```json
{
  "items": []
}
```

---

### Criar canal (POST /ticket/channels)

```bash
curl -X POST -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{"code":"email","name":"Email","kind":"email","status":"active"}' \
  https://api.seudominio.com/ticket/channels
```

Resposta:

```json
{
  "code": "email",
  "name": "Email",
  "kind": "email",
  "status": "active"
}
```

---

### Atualizar status do ticket (PATCH /ticket/:id/status)

```bash
curl -X PATCH -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{"status":"resolved"}' \
  https://api.seudominio.com/ticket/123/status
```

Resposta:

```json
{
  "id": 123,
  "status": "resolved"
}
```

---

### Criar resposta pública (POST /ticket/:id/reply)

```bash
curl -X POST -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{"subject":"Re: Dúvida","body":"Olá, sua dúvida foi resolvida."}' \
  https://api.seudominio.com/ticket/123/reply
```

Resposta:

```json
{
  "id": 123,
  "subject": "Re: Dúvida",
  "body": "Olá, sua dúvida foi resolvida."
}
```

---

Este módulo é parte integrante do monorepo HedHog e segue as políticas de governança, roles e configurações definidas para o ecossistema.