progressPedido em Progresso
Evento WebSocket recebido quando um pedido entra em status "IN_PROGRESS" (em coleta). Emitido em dois contextos: 1. **Broadcast**: Quando um pedido é chamado via 'call', o evento é enviado para todos os clientes da conta. 2. **Cliente específico**: Quando o cliente solicita a lista inicial via 'get.orders.progress', recebe apenas os pedidos existentes. O frontend adiciona o pedido à lista "Em Progresso", evitando duplicatas. Pedidos mais recentes 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
| Nome | Tipo | Obrig. | Descrição |
|---|---|---|---|
Authorization | string | Sim | Token de autenticação no formato Bearer {accessToken}. O token deve ser enviado durante o handshake de conexão WebSocket. |
AuthorizationOBRIGATÓRIOParâmetros
Este método não possui parâmetros.
Fluxo passo a passo: progress
Contexto 1: Pedido chamado para coleta (broadcast)
Quando um usuário clica para chamar um pedido da lista de 'Standby' para coleta, o frontend emite o evento 'call' com o ID do pedido. O gateway recebe esta solicitação e executa uma série de validações antes de atualizar o pedido e propagar o evento 'progress' para todos os clientes conectados da mesma conta. Este é o primeiro contexto onde o evento 'progress' é emitido, usando broadcast para garantir que todos os usuários vejam o pedido sendo movido para 'Em Progresso' simultaneamente:
// accounts.gateways.ts
@SubscribeMessage('call')
async handleMessage(
@ConnectedSocket() client: Socket,
@MessageBody() data: UpdateOrderDto,
) {
const payload = client.data.payload as PayloadTokenDto;
// Valida se usuário já está coletando outro paciente
const exists = await this.ordersService.existsOrder(
OrdersStatusEnum.IN_PROGRESS,
data.acceptepUserId ?? payload.sub,
payload,
);
if (exists) {
return {
status: 'refused',
message: 'O usuário já está coletando outro paciente',
};
}
// Atualiza order para IN_PROGRESS
const order = await this.ordersService.updateOrder(data, payload);
// Emite evento progress via broadcast
this.notifyOnEvent(
'progress',
await this.ordersService.findById(order.id, payload),
client.data.payload,
);
return {
status: 'receive',
timestamp: new Date().toISOString(),
};
}Validação e atualização do pedido
O servidor valida se o usuário já está coletando outro paciente. Se passar, atualiza o pedido: status muda de STANDBY para IN_PROGRESS e vincula o usuário coletor (acceptUser):
// orders.service.ts
async updateOrder(updateOrderDto: UpdateOrderDto, payload: PayloadTokenDto) {
// Valida permissões se atribuir para outro usuário
if (updateOrderDto.acceptepUserId) {
const check = await this.permissionsService.hasPermissions(
payload,
[ASSIGN_LABORATORY],
);
if (!check) throw new UnauthorizedException();
}
// Atualiza pedido no banco
const order = await this.prisma.orders.update({
where: {
id: updateOrderDto.id,
accountId: payload.accountId,
status: { name: OrdersStatusEnum.STANDBY },
},
data: {
status: { connect: { name: OrdersStatusEnum.IN_PROGRESS } },
acceptUser: {
connect: { id: updateOrderDto.acceptepUserId ?? payload.sub },
},
},
});
return order;
}Servidor propaga evento 'progress' via broadcast
O servidor envia o evento 'progress' via broadcast para todos os clientes na room 'orders.account.{accountId}'. Todos recebem o pedido completo simultaneamente:
// 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: 'progress'
// Dados: objeto Order completoContexto 2: Carregamento inicial da lista
Quando o componente é montado, emite 'get.orders.progress' para carregar a lista inicial. Configura listeners para 'progress' e 'remove.progress':
// ListInProgress.tsx
useEffect(() => {
if (!socket) return;
// Solicita lista inicial
socket.emit('get.orders.progress');
// Escuta eventos
socket.on('progress', (newData: Order) => {
setOrders((prev) => {
if (prev.find((d) => d.id === newData.id)) return prev;
return [newData, ...prev];
});
});
return () => {
socket.off('progress');
socket.off('remove.progress');
};
}, [socket]);Gateway recebe solicitação e emite evento interno
O gateway recebe 'get.orders.progress', retorna confirmação e emite evento interno 'load.orders.progress' com client.id para enviar apenas ao cliente solicitante:
// accounts.gateways.ts
@SubscribeMessage('get.orders.progress')
async handleOrdersProgress(@ConnectedSocket() client: Socket) {
this.eventEmitter.emit(
'load.orders.progress',
new LoadOrdersEvent(client.id, client.data.payload),
);
return {
status: 'receive',
};
}Listener busca pedidos e envia via 'progress'
O listener busca todos os pedidos IN_PROGRESS e envia cada um individualmente via 'progress' apenas para o cliente que solicitou:
// orders.listener.ts
@OnEvent('load.orders.progress')
async handleOnLoadOrdersProgress({ to, payload }: LoadOrdersEvent) {
try {
// Busca todos os pedidos IN_PROGRESS
const orders = await this.loadOrdersByStatus(
OrdersStatusEnum.IN_PROGRESS,
payload,
);
// Envia cada pedido individualmente para o cliente específico
orders.map((order) =>
this.gateway.notifyOnEventClient(to, 'progress', order),
);
} catch (e) {
this.logger.debug('Erro ao buscar pedidos em progresso', e);
}
}Método notifyOnEventClient envia para cliente específico
Diferente do broadcast, este método envia apenas para o socket específico (client.id). Garante que só quem solicitou receba os pedidos:
// 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: 'progress'
// Dados: objeto Order completoFrontend recebe evento e atualiza a lista
O frontend verifica se o pedido já existe (evita duplicatas) e adiciona no início da lista. Pedidos mais recentes aparecem primeiro:
// ListInProgress.tsx
socket.on('progress', (newData: Order) => {
setOrders((prev) => {
// Verifica se pedido já existe (evita duplicatas)
if (prev.find((d) => d.id === newData.id)) return prev;
// Adiciona no início da lista (pedidos mais recentes primeiro)
return [newData, ...prev];
});
});Resumo: Diferenças entre os contextos
Contexto 1 (via 'call'): broadcast para todos os clientes quando pedido é chamado. Contexto 2 (via 'get.orders.progress'): apenas para quem solicitou, para carregar lista inicial:
// Contexto 1: Via 'call' (broadcast)
// - Emitido quando pedido é chamado para coleta
// - Enviado para TODOS os clientes da conta
// - Usa: notifyOnEvent('progress', order, payload)
// - Todos os clientes recebem o novo pedido em tempo real
// Contexto 2: Via 'get.orders.progress' (cliente específico)
// - Emitido quando cliente solicita lista inicial
// - Enviado apenas para o CLIENTE que solicitou
// - Usa: notifyOnEventClient(to, 'progress', order)
// - Cliente recebe todos os pedidos IN_PROGRESS existentes{
"id": 123,
"ulid": "01J8X9K2M3N4P5Q6R7S8T9U0V",
...{
"id": 123,
"ulid": "01J8X9K2M3N4P5Q6R7S8T9U0V",
"accountId": 1,
"status": {
"id": 2,
"name": "IN_PROGRESS",
"color": "#3b82f6"
},
"acceptUser": {
"id": 456,
"name": "João Silva"
},
"patientExams": {
"id": 789,
"prefix": "EX",
"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": "IN_PROGRESS"
}
}