WEBSOCKETreports.complete

Laudos Criados Automaticamente

Evento WebSocket para notificar em tempo real quando laudos são criados automaticamente após um pedido ser finalizado. Este evento é emitido automaticamente pelo servidor após criar laudos para procedimentos que requerem laudo local (laudoLocal: true), propagando a notificação para todos os clientes conectados da mesma conta. O servidor faz broadcast do evento para todos os clientes da mesma accountId, permitindo que todos recarreguem suas listas de laudos pendentes simultaneamente. Este evento não é iniciado pelo frontend; é um evento unidirecional (servidor → clientes) usado exclusivamente para sincronização de UI em tempo real. Quando um pedido é finalizado com status COMPLETED e contém procedimentos que precisam de laudo local, o servidor cria automaticamente os laudos e emite este evento para notificar todos os clientes.

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

Este método não possui parâmetros.

Fluxo passo a passo: reports.complete

1

Cliente finaliza pedido e servidor emite evento interno

Quando um pedido é finalizado via WebSocket 'finish.order', o servidor valida e emite evento interno:

// accounts.gateways.ts
@SubscribeMessage('finish.order')
async handleOrderCompleted(
  @ConnectedSocket() client: Socket,
  @MessageBody() data: { orderId: number; status: string },
) {
  // ... valida order e status ...
  
  // Emite evento interno após validar
  this.eventEmitter.emitAsync(
    'orders.finish',
    new OrderFinishEvent(
      exists.id,
      status.name as OrdersStatusEnum.COMPLETED | OrdersStatusEnum.CANCELED,
      payload,
    ),
  );
  
  // Notifica order completado imediatamente
  this.notifyOnEvent('completed', exists, payload);
  
  return { status: 'receive', timestamp: new Date().toISOString() };
}
O que faz: recebe solicitação para finalizar pedido e emite evento interno para processamento
Validação: verifica se pedido existe e está com status IN_PROGRESS
Emite evento assíncrono: emitAsync('orders.finish') dispara processamento assíncrono
Notifica imediatamente: notifyOnEvent('completed') notifica que pedido foi completado
Quando é chamado: quando um pedido é finalizado via WebSocket (método finish.order)
2

Listener cria laudos automaticamente e emite evento

O order-finish.listener.ts escuta o evento interno, cria laudos para procedimentos que precisam e emite evento:

// order-finish.listener.ts
@OnEvent('orders.finish')
async handleOrderFinish(orderFinishEvent: OrderFinishEvent) {
  // Só processa se order foi completado (não cancelado)
  if (orderFinishEvent.status !== OrdersStatusEnum.COMPLETED) return;
  
  try {
    await this.prisma.$transaction(async (tx) => {
      // Busca order com procedimentos que têm laudoLocal: true
      const order = await tx.orders.findUnique({
        where: { id: orderFinishEvent.orderId },
        select: {
          ulid: true,
          budgets: {
            select: {
              budgetsProcedures: {
                where: {
                  procedure: { laudoLocal: true }, // Apenas procedimentos com laudo local
                },
                // ...
              },
            },
          },
        },
      });

      // Se não há procedimentos que precisam de laudo, retorna
      if (order.budgets[0].budgetsProcedures.length === 0) return;

      // Cria laudos para cada procedimento (paralelo)
      await Promise.all(
        order.budgets[0].budgetsProcedures.map(async (budgetProcedure) => {
          // Verifica se laudo já existe (evita duplicatas)
          const exists = await this.reportsService.exists(
            orderFinishEvent.payload,
            { orderUlid: order.ulid, procedureId: budgetProcedure.procedure.id },
          );
          
          if (exists.data) return; // Pula se já existe

          // Cria novo laudo
          await this.reportsService.create(
            orderFinishEvent.payload,
            { orderUlid: order.ulid, procedureId: budgetProcedure.procedure.id },
          );
        }),
      );
      
      // Emite evento após criar todos os laudos
      this.gateways.notifyOnEvent(
        'reports.complete',
        {}, // Objeto vazio (apenas notifica que laudos foram criados)
        orderFinishEvent.payload,
      );
    });
  } catch (e) {
    this.logger.error(e);
  }
}
O que faz: escuta evento interno e cria laudos automaticamente para procedimentos que precisam
Verifica status: só processa se order foi completado (não cancelado)
Filtra procedimentos: busca apenas procedimentos com laudoLocal: true
Criação paralela: Promise.all() cria laudos em paralelo para cada procedimento
Prevenção de duplicatas: verifica se laudo já existe antes de criar
Transação atômica: todas as operações dentro de uma transação
Dados do evento: objeto vazio {} (apenas notifica que laudos foram criados)
Quando é chamado: quando um pedido é finalizado com status COMPLETED e tem procedimentos que precisam de laudo local
3

Gateway faz broadcast para todos da conta

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

// accounts.gateways.ts
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.'
// Evento: 'reports.complete' (está na lista de eventos permitidos - linha 111)
// Dados: objeto vazio {} (apenas notifica que laudos foram criados)
// Todos os clientes conectados da mesma accountId recebem o evento
Evento emitido: 'reports.complete' (está na lista de eventos permitidos)
Dados enviados: objeto vazio {} (não envia dados dos laudos, apenas notifica)
Quem recebe: todos os clientes conectados da mesma accountId
Room format: orders.account.{accountId}
Sincronização: todos os clientes recebem a notificação simultaneamente
4

Cliente recebe evento e recarrega lista de laudos

O componente ReportIcon.tsx escuta o evento e recarrega a lista de laudos pendentes:

// ReportIcon.tsx
async function loadReports() {
  const { status, data, message } = await reports();
  
  if (status) {
    setOrders(data || []); // Atualiza lista de orders com laudos pendentes
  }
  
  if (!status) {
    setError(message || 'Erro ao buscar laudos');
  }
}

useEffect(() => {
  if (!socket) return;

  // Carrega lista inicial ao montar
  loadReports();

  // Escuta evento e recarrega lista quando laudos são criados
  socket.on('reports.complete', () => {
    loadReports(); // Recarrega lista de laudos pendentes
  });

  return () => {
    socket.off('reports.complete');
  };
}, [socket]);
O que faz: escuta evento e recarrega lista de laudos pendentes
Carrega lista inicial: loadReports() busca laudos pendentes ao montar o componente
Recarrega lista: loadReports() busca novamente quando recebe notificação
Atualiza estado: setOrders(data || []) atualiza a lista de orders com laudos pendentes
Atualiza badge: badge mostra quantidade de laudos pendentes
Atualiza UI: lista de laudos é atualizada automaticamente
Tratamento de erros: setError() exibe mensagem de erro se houver
Cleanup: remove listeners ao desmontar para prevenir memory leaks

Request URL

ws://api-dev.imagemais.com
{
  "message": "Este evento é emitido automaticamente pelo servidor (não requer request body)"
}
{
  "message": "Este evento é emitido automaticamente pelo servidor (não requer request body)"
}

Respostas

{}
{}