WEBSOCKETdoctors.busy

Status de Ocupação do Médico

Evento WebSocket para notificar em tempo real quando o status de ocupação de um médico muda. Este evento é emitido automaticamente pelo servidor quando um agendamento muda de status, 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 atualizem o status do médico simultaneamente. Este evento não é iniciado pelo frontend; é um evento unidirecional (servidor → clientes) usado exclusivamente para sincronização de UI em tempo real. O evento é emitido em dois contextos: quando um agendamento muda para "Em atendimento" (médico fica ocupado - isBusy: true) ou quando um agendamento é finalizado (médico fica livre - isBusy: false).

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: doctors.busy

1

Listener emite evento quando status de agendamento muda

Quando um agendamento muda de status, os listeners emitem o evento 'doctors.busy' com o novo status de ocupação:

// in-service-appointment.ts (médico fica ocupado)
@OnEvent('appointments.status.change')
async handleAppointmentsStatusChange(event: AppointmentStatusChangeEvent) {
  if (event.action !== AppointmentStatusEnum.EM_ATENDIMENTO) return;
  
  // ... atualiza médico no banco (isBusy: true) ...
  
  // Emite evento de ocupado
  this.eventEmitter.emit('doctors.busy', {
    doctorId: event.doctorId ?? event.appointment.doctorId,
    isBusy: true, // Médico fica ocupado
    payload: event.payload,
  });
}

// finish-appointment.ts (médico fica livre)
@OnEvent('appointments.status.change')
async handleAppointmentsStatusChange(event: AppointmentStatusChangeEvent) {
  if (event.action !== AppointmentStatusEnum.FINALIZADO) return;
  
  // ... atualiza médico no banco (isBusy: false) ...
  
  // Emite evento de livre
  this.eventEmitter.emit('doctors.busy', {
    doctorId: event.doctorId ?? event.appointment.doctorId,
    isBusy: false, // Médico fica livre
    payload: event.payload,
  });
}
O que faz: emite evento interno quando status de agendamento muda para 'Em atendimento' ou 'Finalizado'
Contexto 1: quando agendamento muda para 'Em atendimento' → isBusy: true (médico fica ocupado)
Contexto 2: quando agendamento é finalizado → isBusy: false (médico fica livre)
Dados do evento: doctorId, isBusy (boolean) e payload
Quando é chamado: quando um agendamento muda de status para 'Em atendimento' ou 'Finalizado'
2

Listener propaga evento via WebSocket

O doctors.listener.ts escuta o evento interno e propaga via WebSocket para todos os clientes da conta:

// doctors.listener.ts
@OnEvent('doctors.busy')
async handleDoctorsBusy(data: {
  doctorId: number;
  isBusy: boolean;
  payload: PayloadTokenDto;
}) {
  try {
    // Propaga via WebSocket para todos os clientes da conta
    await this.gateway.notifyOnEvent(
      'doctors.busy',
      {
        doctorId: data.doctorId,
        isBusy: data.isBusy, // true (ocupado) ou false (livre)
      },
      data.payload,
    );
    
    this.logger.debug(
      `medico ${data.doctorId} esta ${data.isBusy ? 'ocupado' : 'livre'}`,
    );
  } catch (error) {
    this.logger.error(`Erro ao propagar evento doctors.busy:`, error);
  }
}
O que faz: escuta evento interno e propaga via WebSocket
Broadcast: notifica todos os clientes da mesma conta
Dados enviados: objeto com doctorId (number) e isBusy (boolean), não o objeto completo do médico
Log: registra status do médico (ocupado ou livre)
Tratamento de erro: loga erros sem quebrar o fluxo
3

Gateway faz broadcast para todos da conta

O método `notifyOnEvent` envia o evento 'doctors.busy' 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: 'doctors.busy' (está na lista de eventos permitidos - linha 104)
// Dados: objeto com doctorId (number) e isBusy (boolean)
// Todos os clientes conectados da mesma accountId recebem o evento
Evento emitido: 'doctors.busy' (está na lista de eventos permitidos)
Dados enviados: objeto com doctorId (number) e isBusy (boolean), não o objeto completo do médico
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 atualiza status do médico

Os componentes Doctors.tsx e List.tsx escutam o evento e atualizam o status de ocupação do médico na lista:

// Doctors.tsx
socket.on(
  'doctors.busy',
  (data: { doctorId: number; isBusy: boolean }) => {
    // Atualiza status do médico na lista
    setAvailableDoctorsState((prev) =>
      prev.map((doctor) =>
        Number(doctor.id) === data.doctorId
          ? { ...doctor, isBusy: data.isBusy } // Atualiza apenas isBusy
          : doctor
      )
    );
  }
);

// List.tsx (mesmo comportamento)
socket.on(
  'doctors.busy',
  (data: { doctorId: number; isBusy: boolean }) => {
    setListAvailableDoctors((prev) =>
      prev.map((doctor) =>
        Number(doctor.id) === data.doctorId
          ? { ...doctor, isBusy: data.isBusy }
          : doctor
      )
    );
  }
);

// Cleanup ao desmontar
return () => {
  socket.off('doctors.busy');
};
O que faz: escuta evento e atualiza status de ocupação do médico na lista
Atualiza na lista: setAvailableDoctorsState/setListAvailableDoctors atualiza apenas a propriedade isBusy
Mantém outros dados: mantém outros dados do médico inalterados (spread operator)
Conversão de tipos: Number(doctor.id) === data.doctorId converte e compara IDs (string vs number)
Atualiza UI: card/lista de médicos mostra 'Atendendo' ou 'Disponível' baseado no status
Múltiplos componentes: Doctors.tsx e List.tsx usam o mesmo comportamento
Sincronização: status é sincronizado em tempo real em todos os clientes
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

{
  "doctorId": 1,
  "isBusy": true
  ...
{
  "doctorId": 1,
  "isBusy": true
}