WEBSOCKETcollect

Pedido Aguardando Coleta

Evento WebSocket recebido pelo frontend quando um pedido entra em status "STANDBY" (aguardando coleta). Este evento é emitido pelo servidor em dois contextos diferentes: 1. **Via broadcast (todos os clientes)**: Quando um pedido é criado através de uma integração (ex: Diagnostics Brazil) ou quando PatientExams são criados para um pedido existente, o servidor propaga o evento 'collect' para todos os clientes conectados da mesma conta, permitindo sincronização automática entre componentes. 2. **Via cliente específico**: Quando o cliente solicita a lista inicial de pedidos em standby via 'get.orders.standby', o servidor busca todos os pedidos STANDBY e envia cada um individualmente via evento 'collect' apenas para o cliente que solicitou. O frontend escuta este evento e adiciona o pedido à lista de "Aguardando Coleta", evitando duplicatas e mantendo a lista atualizada em tempo real. Pedidos são adicionados no final da lista (pedidos mais antigos aparecem primeiro).

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: collect

1

Contexto 1: Pedido criado via integração (broadcast)

Quando um orçamento é enviado para integração com Diagnostics Brazil, o serviço cria um novo pedido em uma transação atômica. Após criar o pedido, vincular ao paciente, aos procedimentos e ao orçamento, o servidor emite o evento interno 'send.orders' com o evento 'collect' para propagar o novo pedido para todos os clientes conectados da mesma conta. Este é o primeiro contexto onde o evento 'collect' é emitido, usando broadcast para garantir que todos os usuários vejam o novo pedido simultaneamente:

// integration-diagnostics-brazil.service.ts
async send(payload: PayloadTokenDto, budget: Budget) {
    await this.prisma.$transaction(async (tx) => {
        // Busca status STANDBY
        const status = await this.ordersService.findByStatusOrder(
            OrdersStatusEnum.STANDBY,
        );

        // Cria order no banco
        const order = await tx.orders.create({
            data: {
                ulid: this.idService.nextUlid(),
                accountId: payload.accountId,
                createUserId: payload.sub,
                statusId: status.id,
            },
        });

        // Busca fornecedores dos procedimentos
        const procedureSuppliers =
            await this.laboratoriesService.findManyProceduresSuppliers(
                budget.budgetsProcedures.map((bp) => bp.procedure.id),
                payload,
            );

        // Cria PatientExams vinculado ao order
        await tx.patientsExams.create({
            data: {
                prefix: 'IMGM',
                patientId: budget.patient.id,
                orderId: order.id,
                procedureSupplier: {
                    connect: procedureSuppliers.map((ps) => ({ id: ps.id })),
                },
            },
        });

        // Vincula order ao budget
        await tx.budgets.update({
            where: { id: budget.id },
            data: {
                orders: { connect: { id: order.id } },
            },
        });

        // Emite evento interno para propagar via WebSocket
        await this.eventEmitter.emitAsync(
            'send.orders',
            new SendOrderEvent(
                'collect',
                await this.ordersService.findById(order.id, payload),
                payload,
            ),
        );
    });
}
Quem emite: backend (integration-diagnostics-brazil.service.ts) após criar pedido
Modo de envio: broadcast (todos os clientes da conta)
Transação: todas as operações são atômicas (cria order, PatientExams e vincula ao budget)
2

Contexto 2: PatientExams criados para pedido existente (broadcast)

Quando PatientExams são criados para um pedido que já existe (através do evento interno 'patient-exams.create'), o listener cria os exames do paciente e, em seguida, emite o evento 'send.orders' com 'collect' para propagar o pedido atualizado. Este é o segundo contexto onde o evento 'collect' é emitido via broadcast, garantindo que todos os clientes vejam quando um pedido recebe seus exames vinculados:

// patient-exams.listener.ts
@OnEvent('patient-exams.create')
async handlePatientExamsCreate(orderEvent: OrderEvent) {
    try {
        // Cria PatientExams para o pedido
        await this.laboratoriesService.createPatientExams(
            orderEvent.sendDto,
            orderEvent.payload,
        );

        // Busca pedido completo e emite evento para propagar
        this.eventEmitter.emitAsync(
            'send.orders',
            new SendOrderEvent(
                'collect',
                await this.ordersService.findById(
                    orderEvent.sendDto.orderId,
                    orderEvent.payload,
                ),
                orderEvent.payload,
            ),
        );
    } catch (e) {
        this.logger.debug('Erro ao criar PatientExams', e);
    }
}
Quem emite: backend (patient-exams.listener.ts) após criar PatientExams
Modo de envio: broadcast (todos os clientes da conta)
Quando: quando PatientExams são criados para um pedido existente
3

Listener central processa e propaga via WebSocket

O listener central 'send.orders' recebe o evento interno de ambos os contextos anteriores. Ele verifica se o order recebido é um número (ID) ou um objeto completo. Se for número, busca o pedido completo no banco; se for objeto, usa diretamente. Em seguida, propaga o evento 'collect' via broadcast para todos os clientes da conta usando o método notifyOnEvent. Este listener centraliza a lógica de propagação, permitindo que múltiplos pontos de entrada emitam o mesmo evento de forma consistente:

// orders.listener.ts
@OnEvent('send.orders')
async handleOnOrdersByCollect(sendEvent: SendOrderEvent) {
    // Verifica se order é número ou objeto
    const order =
        typeof sendEvent.order === 'number'
            ? await this.ordersService.findById(
                  sendEvent.order,
                  sendEvent.payload,
              )
            : sendEvent.order;

    // Propaga via broadcast para todos os clientes da conta
    await this.gateway.notifyOnEvent(
        sendEvent.event, // 'collect' neste caso
        order,
        sendEvent.payload,
    );
}
Flexibilidade: aceita order como número (ID) ou objeto completo
Método usado: notifyOnEvent('collect', order, payload)
Modo de envio: broadcast (todos os clientes da conta)
4

Servidor propaga evento 'collect' via broadcast

O método `notifyOnEvent` verifica se o evento 'collect' está na lista de eventos permitidos e, em seguida, envia para todos os sockets conectados que estão na room específica da conta (formato: 'orders.account.{accountId}'). O broadcast garante que todos os clientes da mesma conta recebam o evento simultaneamente, permitindo sincronização em tempo real. Cada cliente recebe o objeto Order completo com todos os relacionamentos (paciente, procedimentos, status, etc.), permitindo que a interface seja atualizada imediatamente sem necessidade de requisições adicionais:

// accounts.gateways.ts
async notifyOnEvent(
    event: string,
    data: any,
    payload: PayloadTokenDto,
): Promise<any> {
    if (this._events.includes(event)) {
        this.server
            .to(`${this.room}${payload.accountId}`)
            .emit(event, data);
    }
}

// this.room = 'orders.account.'
// Envia para: orders.account.{accountId}
// Evento: 'collect'
// Dados: objeto Order completo
Room: orders.account.{accountId}
Evento: 'collect' (está na lista de eventos permitidos)
Modo: broadcast (todos os clientes da conta)
5

Contexto 3: Carregamento inicial da lista (cliente específico)

Quando o componente ListStandby.tsx é montado na tela (por exemplo, quando o usuário navega para a página de pedidos aguardando coleta), o frontend precisa carregar a lista inicial de pedidos que já estão em status STANDBY. Para isso, o componente emite o evento 'get.orders.standby' assim que o socket está disponível. Este é o terceiro contexto onde o evento 'collect' será recebido, mas desta vez apenas pelo cliente que solicitou, não via broadcast. O componente também configura os listeners para receber os eventos 'collect' e 'remove.standby' que serão usados para manter a lista atualizada em tempo real:

// ListStandby.tsx
useEffect(() => {
    if (!socket) return;
    
    // Solicita lista inicial
    socket.emit('get.orders.standby');
    
    // Escuta eventos
    socket.on('collect', (newData: Order) => {
        setOrders((prev) => {
            if (prev.find((d) => d.id === newData.id)) return prev;
            return [...prev, newData];
        });
    });
    
    socket.on('remove.standby', (remove: Order) => {
        setOrders((prev) => prev.filter((order) => order.id !== remove.id));
    });
    
    return () => {
        socket.off('collect');
        socket.off('remove.standby');
    };
}, [socket]);
Quem solicita: frontend (ListStandby.tsx) ao montar componente
Evento emitido: 'get.orders.standby'
6

Gateway recebe solicitação e emite evento interno

O gateway recebe a solicitação 'get.orders.standby' do frontend e imediatamente retorna uma confirmação ({ status: 'receive' }) para o cliente. Em paralelo, emite um evento interno 'load.orders.standby' através do EventEmitter do NestJS, passando o ID do socket do cliente (client.id) e o payload de autenticação. O uso do EventEmitter permite que o processamento seja assíncrono e não bloqueie a resposta imediata ao cliente. O client.id é crucial aqui, pois será usado posteriormente para enviar os pedidos apenas para este cliente específico, não para todos via broadcast:

// accounts.gateways.ts
@SubscribeMessage('get.orders.standby')
async handleOrdersStandby(@ConnectedSocket() client: Socket) {
    this.eventEmitter.emit(
        'load.orders.standby',
        new LoadOrdersEvent(client.id, client.data.payload),
    );
    
    return {
        status: 'receive',
    };
}
Evento interno: 'load.orders.standby'
Dados: client.id (para enviar resposta ao cliente específico)
7

Listener busca pedidos e envia cada um via 'collect'

O listener escuta o evento interno 'load.orders.standby' e busca todos os pedidos que estão em status STANDBY no banco de dados, filtrando apenas os pedidos da mesma conta do usuário. Em seguida, para cada pedido encontrado, envia individualmente via evento 'collect' usando o método notifyOnEventClient, que garante que apenas o cliente que solicitou (identificado pelo client.id) receba os dados. Cada pedido é enviado em uma mensagem separada, permitindo que o frontend adicione os pedidos incrementalmente à lista conforme recebe:

// orders.listener.ts
@OnEvent('load.orders.standby')
async handleOnLoadOrdersStandby({ to, payload }: LoadOrdersEvent) {
    try {
        // Busca todos os pedidos STANDBY
        const orders = await this.loadOrdersByStatus(
            OrdersStatusEnum.STANDBY,
            payload,
        );
        
        // Envia cada pedido individualmente para o cliente específico
        orders.map((order) =>
            this.gateway.notifyOnEventClient(to, 'collect', order),
        );
    } catch (e) {
        this.logger.debug('Erro ao buscar pedidos em standby', e);
    }
}
Método usado: notifyOnEventClient(to, 'collect', order)
Modo de envio: cliente específico (apenas quem solicitou)
Envio: cada pedido em uma mensagem separada
8

Método notifyOnEventClient envia para cliente específico

Diferente do método notifyOnEvent que faz broadcast para todos os clientes da conta, o notifyOnEventClient envia o evento apenas para um socket específico identificado pelo parâmetro 'to' (que contém o client.id). Este método verifica se o evento 'collect' está na lista de eventos permitidos e, em seguida, envia diretamente para o socket do cliente que solicitou a lista inicial. Isso garante que apenas quem solicitou receba os pedidos, evitando que outros clientes recebam dados desnecessários durante o carregamento inicial:

// accounts.gateways.ts
async notifyOnEventClient(to: string, event: string, data: any) {
    if (this._events.includes(event)) {
        this.server.to(to).emit(event, data);
    }
}

// to = client.id (ID do socket do cliente que solicitou)
// Evento: 'collect'
// Dados: objeto Order completo
Destino: client.id (cliente específico)
Evento: 'collect' (está na lista de eventos permitidos)
Modo: cliente específico (não broadcast)
9

Frontend recebe evento 'collect' e atualiza a lista

O frontend escuta o evento 'collect' através do listener configurado no useEffect. Quando recebe um pedido, primeiro verifica se ele já existe na lista atual (usando find() para comparar IDs), evitando duplicatas caso o mesmo pedido seja recebido múltiplas vezes. Se o pedido não existir, ele é adicionado no final da lista usando o spread operator ([...prev, newData]), garantindo que pedidos mais antigos apareçam primeiro (diferente do evento 'progress' que adiciona no início). A atualização do estado através do setOrders() dispara uma re-renderização automática do componente, atualizando a interface do usuário em tempo real:

// ListStandby.tsx
socket.on('collect', (newData: Order) => {
    setOrders((prev) => {
        // Verifica se pedido já existe (evita duplicatas)
        if (prev.find((d) => d.id === newData.id)) return prev;
        
        // Adiciona no final da lista (pedidos mais antigos primeiro)
        return [...prev, newData];
    });
});
Validação: verifica se pedido já existe antes de adicionar
Posição: adiciona no final da lista (push)
Atualização: UI atualiza automaticamente
10

Resumo: Diferenças entre os contextos de uso

O evento 'collect' é usado em três contextos distintos, cada um com propósito e comportamento diferentes. Nos contextos 1 e 2 (criação de pedido e PatientExams), o evento é emitido via broadcast quando um pedido é criado ou atualizado, garantindo que todos os clientes vejam a mudança simultaneamente. No contexto 3 (via 'get.orders.standby'), o evento é enviado apenas para o cliente que solicitou, permitindo o carregamento inicial da lista sem afetar outros clientes. A escolha entre broadcast e cliente específico depende do cenário: broadcast para sincronização em tempo real de ações, e cliente específico para carregamento inicial de dados. Diferente do evento 'progress', o 'collect' adiciona pedidos no final da lista (FIFO - primeiro a entrar, primeiro a aparecer):

// Contexto 1: Via integração (broadcast)
// - Emitido quando pedido é criado via integração
// - Enviado para TODOS os clientes da conta
// - Usa: notifyOnEvent('collect', order, payload)
// - Todos os clientes recebem o novo pedido em tempo real

// Contexto 2: Via PatientExams (broadcast)
// - Emitido quando PatientExams são criados
// - Enviado para TODOS os clientes da conta
// - Usa: notifyOnEvent('collect', order, payload)
// - Todos os clientes recebem o pedido atualizado

// Contexto 3: Via 'get.orders.standby' (cliente específico)
// - Emitido quando cliente solicita lista inicial
// - Enviado apenas para o CLIENTE que solicitou
// - Usa: notifyOnEventClient(to, 'collect', order)
// - Cliente recebe todos os pedidos STANDBY existentes
Broadcast: todos os clientes recebem novos pedidos em tempo real
Cliente específico: apenas quem solicitou recebe a lista inicial
Ordem na lista: adiciona no final (FIFO - primeiro a entrar, primeiro a aparecer)
{
  "id": 123,
  "ulid": "01J8X9K2M3N4P5Q6R7S8T9U0V",
  ...
{
  "id": 123,
  "ulid": "01J8X9K2M3N4P5Q6R7S8T9U0V",
  "accountId": 1,
  "status": {
    "id": 1,
    "name": "STANDBY",
    "color": "#f59e0b"
  },
  "patientExams": {
    "id": 789,
    "prefix": "IMGM",
    "uuid": "550e8400-e29b-41d4-a716-446655440000",
    "patient": {
      "id": 101,
      "name": "Maria Santos",
      "dateBirth": "1990-01-15"
    }
  }
}

Respostas

{
  "id": 123,
  "ulid": "01J8X9K2M3N4P5Q6R7S8T9U0V",
  ...
{
  "id": 123,
  "ulid": "01J8X9K2M3N4P5Q6R7S8T9U0V",
  "status": {
    "name": "STANDBY"
  }
}