WEBSOCKETcall

Chamar Pedido para Coleta

Evento WebSocket para chamar um pedido em standby para coleta, mudando seu status de STANDBY para IN_PROGRESS. O cliente emite este evento com o ID do pedido e opcionalmente o ID do usuário que vai coletar. O servidor valida se o usuário já está coletando outro paciente, atualiza o pedido e notifica todos os clientes conectados da mesma conta via broadcast. Este evento é diferente de outros porque retorna uma resposta imediata via callback (status: 'receive' ou 'refused'), permitindo que o frontend processe o resultado instantaneamente. Em caso de sucesso, o frontend remove o pedido da lista de standby e todos os clientes recebem o evento 'progress' para adicionar o pedido à lista de "Em Progresso".

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

idOBRIGATÓRIO
Tipo:number
ID do pedido a ser chamado para coleta

ID do pedido a ser chamado para coleta. É um identificador numérico único que identifica um pedido específico no sistema.

Para encontrar o ID do pedido, para acessar a página de listar todos os pedidos do laboratório.

acceptepUserIdOPCIONAL
Tipo:number
ID do usuário que vai coletar o pedido (opcional; se não enviado, usa o usuário logado)

ID do usuário que vai coletar o pedido (opcional). Se não enviado, usa o usuário logado. É um identificador numérico único que identifica um usuário específico no sistema.

Para encontrar o ID do usuário, para acessar a página de listar todos os usuários.

Fluxo passo a passo: call

1

Cliente emite a requisição via WebSocket com callback

O frontend emite o evento 'call' com o ID do pedido e opcionalmente o ID do usuário coletador:

// ListStandby.tsx
function onRemove(id: number, acceptepUserId?: number) {
  socket?.emit('call', { id, acceptepUserId }, (data: responseWs) => {
    if (data.status === 'receive') {
      // Remove da lista de standby
      socket?.emit('remove.standby', orders.find((o) => o.id === id));
    }
    if (data.status === 'refused') {
      // Mostra notificação de erro
      addNotification({
        title: 'Atenção!!!',
        message: data.message,
        bg: 'danger',
        className: 'text-white',
        autohide: true,
      });
    }
  });
}
Quem emite: frontend (ListStandby.tsx)
Quando: ao clicar no botão 'Chamar' para coletar um pedido
Payload: { id: number, acceptepUserId?: number }
Callback: processa resposta imediata (status: 'receive' ou 'refused')
Ação em sucesso: remove pedido da lista standby (emite remove.standby)
Ação em erro: exibe notificação de erro ao usuário
2

Servidor valida conflito de coleta

No backend (NestJS + Gateway), o servidor valida se o usuário já está coletando outro paciente:

@SubscribeMessage('call')
async handleMessage(
  @ConnectedSocket() client: Socket,
  @MessageBody() data: UpdateOrderDto,
) {
  try {
    const payload = client.data.payload as PayloadTokenDto;
    
    // Valida se o usuário já está coletando outro paciente
    const exists = await this.ordersService.existsOrder(
      OrdersStatusEnum.IN_PROGRESS,
      data.acceptepUserId ?? payload.sub, // Usa acceptepUserId se fornecido, senão usa usuário logado
      payload,
    );
    
    if (exists) {
      return {
        status: 'refused',
        message: 'O usuário já está coletando outro paciente',
        timestamp: new Date().toISOString(),
      };
    }
    // ... continua no próximo passo
  } catch (e) {
    // ... tratamento de erro
  }
}
Quem recebe: servidor (Gateway WS)
Validação: verifica se usuário já tem pedido em IN_PROGRESS
Usuário coletador: usa acceptepUserId se fornecido, senão usa payload.sub (usuário logado)
Se conflito: retorna erro imediato { status: 'refused', message: '...' }
3

Servidor atualiza pedido e notifica via broadcast

Se não houver conflito, o servidor atualiza o pedido de STANDBY para IN_PROGRESS e notifica todos os clientes:

// Continuação do handleMessage
// Atualiza o pedido (muda status de STANDBY para IN_PROGRESS)
const order = await this.ordersService.updateOrder(data, payload);

// Busca dados completos do pedido atualizado
const orderComplete = await this.ordersService.findById(order.id, payload);

// Notifica todos os clientes da mesma conta via broadcast
this.notifyOnEvent(
  'progress',
  orderComplete,
  client.data.payload,
);

// Retorna sucesso
return {
  status: 'receive',
  timestamp: new Date().toISOString(),
};
Atualização: muda status de STANDBY para IN_PROGRESS
Atribuição: define acceptUserId (usuário especificado ou usuário logado)
Validação: atualiza apenas pedidos com status STANDBY
Broadcast: notifica todos os clientes da mesma conta (não apenas quem solicitou)
Evento emitido: 'progress' com dados completos do pedido
Resposta: retorna { status: 'receive' } via callback
4

Servidor propaga evento 'progress' para todos da conta

O método `notifyOnEvent` envia o evento 'progress' 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 'progress'
Evento emitido: 'progress' (está na lista de eventos permitidos)
Dados enviados: objeto Order completo com todos os relacionamentos
Quem recebe: todos os clientes conectados da mesma accountId
Diferença importante: broadcast para todos (não apenas para quem solicitou)
5

Cliente processa resposta e remove da lista standby

No frontend, o callback processa a resposta. Se sucesso, remove o pedido da lista de standby:

// ListStandby.tsx - Callback do socket.emit
if (data.status === 'receive') {
  // Remove da lista de standby localmente
  socket?.emit('remove.standby', orders.find((o) => o.id === id));
}

if (data.status === 'refused') {
  // Exibe notificação de erro
  addNotification({
    title: 'Atenção!!!',
    message: data.message,
    bg: 'danger',
    className: 'text-white',
    autohide: true,
  });
  // Não remove da lista (pedido continua em standby)
}
Status 'receive': remove pedido da lista standby (emite remove.standby)
Status 'refused': exibe notificação de erro e mantém pedido na lista
Broadcast remove.standby: outros clientes também removem o pedido da lista
6

Outros clientes recebem evento 'progress' e adicionam à lista

Todos os clientes conectados da mesma conta recebem o evento 'progress' e adicionam o pedido à lista de 'Em Progresso':

// ListInProgress.tsx
socket.on('progress', (newData: Order) => {
  setOrders((prev) => {
    // Previne duplicatas
    if (prev.find((d) => d.id === newData.id)) return prev;
    // Adiciona no início da lista (pedidos mais recentes primeiro)
    return [newData, ...prev];
  });
});
Quem recebe: todos os clientes conectados da mesma conta
O que faz: adiciona pedido à lista de 'Em Progresso'
Prevenção de duplicatas: verifica se o pedido já existe antes de adicionar
Ordem: pedidos mais recentes aparecem primeiro
Sincronização: todos os clientes veem a atualização em tempo real

Request URL

ws://api-dev.imagemais.com
{
  "id": 123,
  "acceptepUserId": 456
  ...
{
  "id": 123,
  "acceptepUserId": 456
}

Respostas

{
  "status": "receive",
  "timestamp": "2025-01-23T10:30:00.000Z"
  ...
{
  "status": "receive",
  "timestamp": "2025-01-23T10:30:00.000Z"
}
{
  "status": "refused",
  "message": "O usuário já está coletando outro paciente",
  ...
{
  "status": "refused",
  "message": "O usuário já está coletando outro paciente",
  "timestamp": "2025-01-23T10:30:00.000Z"
}