WEBSOCKETload.review

Carregar Revisão de Resultado

Evento WebSocket para carregar o histórico de mensagens de uma revisão de resultado de laboratório. O cliente emite este evento com o UUID do resultado da integração e o ID da revisão. O servidor busca a revisão no banco de dados, valida permissões e status, busca o histórico de mensagens no OpenAI via threadId e propaga os dados para todos os clientes conectados da mesma conta via broadcast. Este evento retorna uma resposta imediata via callback (status: true ou false) apenas para tratamento de erros. Os dados reais (thread com histórico de mensagens) são enviados via evento separado 'load.thread' para todos os clientes da mesma conta, garantindo sincronização automática entre componentes.

Access Token

Para realizar requisições GET, POST, PUT, DELETE e PATCH nos endpoints da API você precisa de uma chave de autorização. Chamamos essa chave de accessToken.

Para ter acesso ao accessToken, é necessário que o usuário master da licença efetue a liberação deste pela interface do ImageMais Clinic. O accessToken tem validade de 1 hora.

Headers

AuthorizationOBRIGATÓRIO
Tipo:string
Token de autenticação no formato Bearer {accessToken}. O token deve ser enviado durante o handshake de conexão WebSocket.

Parâmetros

resultsIntegrationUuidOBRIGATÓRIO
Tipo:string
UUID do resultado da integração de laboratório

UUID do resultado da integração de laboratório. É um identificador único que identifica um resultado específico de integração no sistema.

Para encontrar o UUID do resultado da integração, para acessar a página de listar todos os pedidos do laboratório. O UUID está disponível no campo resultsIntegration.uuid da resposta.

reviewIdOBRIGATÓRIO
Tipo:number
ID da revisão a ser carregada

ID da revisão a ser carregada. É um identificador numérico único que identifica uma revisão específica de um resultado no sistema.

Para encontrar o ID da revisão, primeiro você precisa do UUID do resultado da integração (obtido através da rota de listar pedidos). Com o UUID, para acessar a página de listar todos os históricos de revisão. O ID da revisão está disponível no campo id de cada item retornado pela rota.

Fluxo passo a passo: load.review

1

Cliente emite a requisição via WebSocket com callback

O frontend emite o evento 'load.review' com o UUID do resultado e o ID da revisão:

// page.tsx
socket.emit(
  'load.review',
  {
    resultsIntegrationUuid: uuid,
    reviewId: reviewId,
  },
  (response: any) => {
    // Callback apenas para tratar erros
    if (!response.status) {
      setHistory(undefined);
      setError(response.error);
    }
  }
);
Quem emite: frontend (page.tsx - componente Review)
Quando: quando componente monta e tem reviewId definido
Payload: { resultsIntegrationUuid: string, reviewId: number }
Callback: usado apenas para tratar erros (não recebe dados)
Dados reais: recebidos via evento separado 'load.thread'
2

Servidor busca revisão no banco de dados

No backend (NestJS + Gateway), o servidor busca a revisão com validações de permissão e status:

// accounts.gateways.ts
@SubscribeMessage('load.review')
async handleLoadReview(
  @ConnectedSocket() client: Socket,
  @MessageBody() data: {
    resultsIntegrationUuid: string;
    reviewId: number;
  },
) {
  try {
    const payload = client.data.payload as PayloadTokenDto;
    
    // Busca revisão e histórico de mensagens
    const thread = await this.laboratoriesService.findOneReview(
      data.resultsIntegrationUuid,
      data.reviewId,
      payload,
    );
    // ... continua no próximo passo
  } catch (e) {
    // ... tratamento de erro
  }
}
Quem recebe: servidor (Gateway WS)
O que faz: busca revisão no banco e histórico no OpenAI
Validações: permissões (accountId) e status (ANALYZING ou SENT)
3

Serviço valida revisão e busca histórico no OpenAI

O serviço busca a revisão no banco com validações e depois busca o histórico no OpenAI:

// laboratories.service.ts
async findOneReview(
  resultsIntegrationUuid: string,
  reviewId: number,
  payload: PayloadTokenDto,
) {
  // Busca revisão no banco com validações
  const review = await this.prisma.resultsReview.findFirst({
    where: {
      id: reviewId,
      resultsIntegration: {
        uuid: resultsIntegrationUuid,
        deletedAt: null, // Não deletado
        order: {
          accountId: payload.accountId, // Mesma conta
        },
        statusReview: {
          name: {
            in: [StatusReview.ANALYZING, StatusReview.SENT], // Status válido
          },
        },
      },
    },
    select: {
      id: true,
      threadId: true, // ID do thread no OpenAI
      createdAt: true,
      updatedAt: true,
    },
  });
  
  if (!review) {
    throw new NotFoundException('Revisão não encontrada');
  }
  
  try {
    // Busca histórico de mensagens no OpenAI via threadId
    const messages = await this.openAiService.findHistory(
      review.threadId,
    );
    
    // Filtra mensagens ocultas
    return {
      id: review.id,
      messages: messages.filter(
        (message) => message.metadata.hidden !== 'true',
      ),
      createdAt: review.createdAt,
      updatedAt: review.updatedAt,
    };
  } catch (e) {
    throw new InternalServerErrorException('Erro ao buscar histórico');
  }
}
Validação 1: verifica se revisão existe e corresponde ao UUID
Validação 2: verifica se resultado não está deletado
Validação 3: verifica se pertence à mesma conta (accountId)
Validação 4: verifica se status é ANALYZING ou SENT
Busca histórico: consulta OpenAI via threadId para obter mensagens
Filtragem: remove mensagens com metadata.hidden === 'true'
4

Servidor propaga thread via broadcast

O Gateway propaga o thread com histórico de mensagens para todos os clientes da mesma conta:

// accounts.gateways.ts - Continuação do handleLoadReview
// Propaga thread para todos os clientes da mesma conta
await this.notifyOnEvent('load.thread', thread, payload);

// Retorna sucesso via callback
return {
  status: true,
};
Evento emitido: 'load.thread' (está na lista de eventos permitidos)
Dados enviados: objeto Thread completo (id, messages, createdAt, updatedAt)
Quem recebe: todos os clientes conectados da mesma accountId
Resposta imediata: retorna { status: true } via callback
5

Servidor propaga evento 'load.thread' para todos da conta

O método `notifyOnEvent` envia o evento 'load.thread' para todos os sockets da mesma accountId:

async notifyOnEvent(
  event: string,
  data: any,
  payload: PayloadTokenDto,
): Promise<any> {
  if (this._events.includes(event)) {
    // Envia para todos os clientes da mesma conta
    this.server
      .to(`${this.room}${payload.accountId}`)
      .emit(event, data);
  }
}

// this.room = 'orders.account.'
// Todos os clientes conectados da mesma accountId recebem o evento 'load.thread'
Evento: 'load.thread' (está na lista de eventos permitidos)
Dados enviados: objeto Thread completo com histórico de mensagens
Quem recebe: todos os clientes conectados da mesma conta
Sincronização: todos os clientes recebem o histórico simultaneamente
6

Servidor trata erros e retorna via callback

Se houver erro durante o processamento, o servidor retorna via callback:

// accounts.gateways.ts - Continuação do handleLoadReview
} catch (e) {
  const { message } = e as Error;
  this.logger.debug(message);
  return {
    status: false,
    error: message,
  };
}
Tratamento de erro: captura exceções e retorna via callback
Erros possíveis: revisão não encontrada, sem permissão, status inválido, erro no OpenAI
Resposta: retorna { status: false, error: message } via callback
7

Cliente processa resposta de erro no callback

No frontend, o callback processa apenas erros (dados reais vêm via evento separado):

// page.tsx - Callback do socket.emit
(response: any) => {
  // Trata apenas erros
  if (!response.status) {
    setHistory(undefined);
    setError(response.error);
  }
  // Se status === true, aguarda evento 'load.thread' para receber dados
}
Tratamento: se status === false, limpa histórico e define erro
Aguardar dados: se status === true, aguarda evento 'load.thread'
8

Cliente recebe thread via evento 'load.thread'

Todos os clientes conectados da mesma conta recebem o evento 'load.thread' e atualizam o estado:

// page.tsx
socket.on('load.thread', (data: Thread) => {
  // Atualiza estado com histórico de mensagens
  setHistory(data);
  // Limpa erros
  setError(undefined);
});

// Remove listener ao desmontar
useEffect(() => {
  return () => {
    socket.off('load.thread');
  };
}, []);
Quem recebe: todos os clientes conectados da mesma conta
O que faz: atualiza estado com thread completo (id, messages, createdAt, updatedAt)
Limpeza: limpa estado de erro se houver
Sincronização: todos os clientes recebem o histórico simultaneamente
Cleanup: remove listener ao desmontar para prevenir memory leaks

Request URL

ws://api-dev.imagemais.com
{
  "resultsIntegrationUuid": "550e8400-e29b-41d4-a716-446655440000",
  "reviewId": 123
  ...
{
  "resultsIntegrationUuid": "550e8400-e29b-41d4-a716-446655440000",
  "reviewId": 123
}

Respostas

{
  "status": true
}
{
  "status": true
}
{
  "status": false,
  "error": "Revisão não encontrada"
  ...
{
  "status": false,
  "error": "Revisão não encontrada"
}