Skip to main content

Áudio do exame — visão geral

O módulo de Áudio cobre a gravação de ditado por voz durante o laudo e a audição, listagem e exclusão dos áudios já anexados a um exame. É o recurso que permite a um médico laudista falar o laudo em vez de digitá-lo: ele grava pelo microfone do navegador, guarda a gravação no exame, e qualquer pessoa com permissão pode ouvir depois — no laudário ou direto na grade de exames.

:::caution Levantamento parcial — atualizado em 24/09/2026 após corrigir o ambiente de teste Este levantamento cobriu o código-fonte com profundidade e confirmou ao vivo, com prints reais, o acesso à grade de exames e o estado do botão de áudio quando o exame não tem imagem. Não foi possível confirmar ao vivo os estados internos do widget (lista carregada com item, gravação, exclusão), pelo motivo abaixo.

O único exame do tenant de teste ("Clínica Docs Portal 2"), criado propositalmente para destravar os levantamentos que dependem de exame, é fictício e não tem imagem nem série associada (imageCount/seriesCount = 0 na resposta da worklist).

Os prints abaixo foram capturados quando o ambiente task4 ainda rodava uma branch desatualizada (integration/all-features, ~100 commits atrás de develop); nela, os botões Abrir Laudário e Exibir áudio apareciam desabilitados, com o tooltip "Disponível em breve" — um comportamento que já registramos como incompatível com o código-fonte lido (que não tem nenhum binding [disabled] para esses botões). Depois de atualizar o task4 para develop, repeti o teste: o comportamento agora bate com o código. Confirmado ao vivo (administrator): sem imagem, o botão de áudio some da grade por completo (não aparece, não fica desabilitado — exatamente o que exam.audioCount > 0 no template já indicava), e Abrir Laudário aparece habilitado. Clicar nele ainda não abre o laudário de verdade, mas agora por um motivo diferente e documentado à parte: um bug real de schema GraphQL na própria branch develop (PreviousExamsFilter/ReportVariables), ver Laudário — não mais o botão desabilitado retratado nos prints abaixo.

Ou seja: não há, hoje, nenhum exame no ambiente de teste que permita abrir o laudário ou o modal de áudio de verdade — o bloqueio é real, só que a causa mudou (antes: ambiente desatualizado com UI incorreta; agora: bug de schema GraphQL na branch atual, mais a falta de imagem no exame). Isso continua impedindo as capturas de gravação, player com item e exclusão.

A confirmar — responsável: time de frontend; data: 24/09/2026. Os prints da lista com item, do gravador em ação e da exclusão continuam pendentes de um exame de teste com imagem e do bug de laudário corrigido. O restante desta página e da página de funcionalidade é baseado em código, testes unitários e nos prints reais já obtidos — não em suposição. Os prints abaixo mostrando os botões desabilitados ficam como registro histórico do estado do ambiente antigo, não do comportamento atual. :::

Onde a funcionalidade vive​

Áudio não é uma tela própria — é um widget de lista (ExamAudioListComponent, @widgets/exam-audio-list, seletor app-exam-audio-list) e um gravador (ReportAudioRecorderComponent, src/app/pages/exam-report/ui/report-audio-recorder, seletor app-report-audio-recorder) embutidos em duas telas que já existem por outro motivo:

SuperfícieComponente hospedeiroO que apareceExclusão?
Grade de exames (/exams)exam.component.ts:1185 (openAudioList)Só a lista/player, dentro de um modal (mm-dialog, título "Áudios do exame")Não — o modal nunca passa canDelete, então o botão de excluir nunca aparece aqui
Laudário (/exams/:examId/report)exam-report-page.component.html:438-448O gravador (grava e guarda no exame) com a lista projetada dentro dele, logo abaixo dos controlesSim, se o usuário tiver a permissão (ver abaixo)

O comentário do próprio componente resume a decisão de design: "a mesma lista serve como duas superfícies... widget não importa widget — quem monta o modal é a página" (exam-audio-list.component.ts:28-40).

Rotas​

RotaGuardObservação
/examsauthGuard, permissionGuard('exam:read')Grade de exames; o botão de áudio só aparece por exame, quando exam.audioCount > 0
/exams/:examId/reportherda o guard de /exams (rota filha); canDeactivate: unsavedChangesGuardLaudário, layout fullBleed; não há guard de rota específico para áudio

Não existe permissão de rota separada para "ver áudio" ou "gravar áudio" — qualquer pessoa que consiga abrir a grade de exames ou o laudário chega às duas superfícies. O controle de acesso real acontece dentro dos componentes, como descrito a seguir.

Permissões e acesso — o que o código decide, ação por ação​

AçãoOnde é decididoRegra
Ver o botão 🎤 na grade de examesTemplate do widget de grade (exam-worklist.component.html)Só aparece se exam.audioCount > 0 — sem áudio, o botão não é oferecido (nem desabilitado)
Carregar a lista de áudios (GET via ExamActionsService.getAudios)Reativo, depois da chamada — não há checagem préviaSe a API responder 403/401, a lista entra em estado "negado" (ver página de funcionalidade); qualquer outro erro entra em estado "falha", com botão de tentar de novo
Gravar e guardar um áudio no laudárioNenhuma permissão de IAM é checada no frontendO gravador só verifica se o navegador suporta captura (MediaRecorder) e se o usuário concedeu a permissão de microfone do sistema operacional/navegador — não existe uma checagem de exam:add-audio antes de habilitar o botão Gravar áudio. Se o ator não tiver essa permissão no backend, a falha só aparece ao clicar em Guardar áudio e fechar a janela (toast de erro genérico)
Excluir um áudiocanDeleteExamAudio(), exam-policy.ts:429-431, computado só na página do laudário (exam-report-page.component.ts:536) e passado como [canDelete]user.canSign (exam:sign) ou isExamManager(user) (isPlatformAdmin ou canUpdate e canDelete, ou seja exam:update e exam:delete)

Discrepância documentada no próprio código: a v1 (AngularJS) só deixava médico e proprietário excluírem áudio — gestor ficava de fora, o único caso do sistema em que gestor tem menos acesso que médico. O catálogo de permissões da v2 não separa "gestor" de "proprietário" no nível de papel, então a aproximação virou exam:sign (médico assina) ou isExamManager (proprietário/admin de plataforma) — comentário SPEC_DEVIATION em exam-policy.ts:418-428.

Lacuna observável: como a gravação não tem checagem própria de permissão no frontend, um perfil sem exam:add-audio consegue gravar, ver o cronômetro rodar e ouvir a própria gravação local — a rejeição só chega no momento de salvar no exame, quando a chamada ao backend (ver Gravar áudio — API) devolve 403. Isso é diferente do padrão usado na listagem, que trata a negativa como um estado dedicado da tela (loadDenied) em vez de deixar a pessoa realizar toda a ação primeiro.

Nenhum feature flag ou "módulo" (feature flag por tenant/unidade, ver Administração — Módulos) controla a exibição do áudio — a busca no código não encontrou nenhuma flag equivalente para esta capacidade, e o bloco do gravador+lista no laudário só é recolhido visualmente pelo painel de ferramentas, nunca removido do DOM por condição de modalidade de exame ou de tenant.

Quem acessa: o que este levantamento confirmou navegando​

Testamos 3 dos 9 perfis de docs-usuarios.md (administrator, doctor e read_only, todos na "Unidade Central Docs") contra o único exame do tenant — que, como o aviso no topo desta página explica, não tem imagem. Também testamos financial, para confirmar o caminho de acesso negado.

O que a pessoa tentaadministratordoctorread_onlyfinancial
Abrir a grade de exames (/exams)SimSimSimNegado — redireciona para /admin/tenant
Ver o nome do paciente na linhaNome completoNome completoMascarado (***)—
Botões Inserir comentário / Enviar anexo na linhaAparecemAparecemNão aparecem—
Botão Exibir áudio (exame sem imagem, audioCount = 0)Ausente (confirmado em develop, matching código)IdemIdem—
Botão Abrir Laudário (exame sem imagem)Habilitado, mas abrir falha pelo bug de schema GraphQL (ver Laudário)IdemIdem—
Abrir /exams/{id}/report por URL direta (exame sem imagem)Falha ao abrir (bug de schema GraphQL, ver Laudário)IdemNão testado—

Os três prints abaixo retratam o estado antigo (ambiente task4 desatualizado, botões desabilitados) — mantidos como registro histórico; o estado atual confirmado é o da tabela acima (botão ausente, não desabilitado).

Linha do exame de teste, perfil administrator, capturada no ambiente antigo (desatualizado): Abrir Laudário e Exibir áudio desabilitados, com o tooltip "Disponível em breve"

Mesma linha, perfil doctor, ambiente antigo: nome do paciente completo, mesmos botões desabilitados

Mesma linha, perfil read_only, ambiente antigo: nome do paciente mascarado, sem os botões de comentário/anexo, áudio ainda desabilitado

Perfil financial tentando abrir /exams: redirecionamento silencioso para a lista de Grupos, sem toast ou página de erro

O padrão observado​

  1. Resolvido: a primeira rodada deste levantamento (ambiente task4 desatualizado) mostrava "Exibir áudio"/"Abrir Laudário" desabilitados com tooltip para todo mundo, o que não batia com o código lido (exam-worklist.component.html:605: o botão de áudio deveria ficar ausente quando exam.audioCount === 0, nunca desabilitado). Depois de atualizar o task4 para develop, o comportamento ao vivo passou a bater com o código: o botão de áudio some por completo sem imagem, e Abrir Laudário aparece habilitado. Ou seja, a divergência era mesmo do ambiente de teste desatualizado, não do produto — confirmado, não é mais uma pendência.
  2. A permissão de leitura do exame em si (read_only) inclui mascarar dados do paciente e ocultar as ações de comentário/anexo — um padrão de LGPD que vale para a tela toda, não só para áudio.
  3. O redirecionamento negado de financial repete o padrão já documentado em Administração e Permissões: sem exam:read, a rota /exams nem chega a carregar — volta silenciosamente para /admin/tenant, sem toast.

Atalhos de teclado do player​

Com o foco em um player de áudio (ou no último que tocou), a lista responde a três teclas, sem deixar a página rolar:

TeclaAção
EspaçoPlay/pausa
← / →Volta/avança 2 segundos
rReinicia do zero e toca

O keydown é escutado no próprio elemento do widget (@HostListener), não no document — a v1 prendia a barra de espaço na página inteira mesmo depois de fechar o modal (historicoAudioExameCtrl.js:36-42); a reescrita corrige isso.

Cobertura de testes automatizados​

Ao contrário do backend (onde a visão geral da API registra ausência de teste automatizado no controller e no serviço), o frontend tem suíte de testes unitários explícita para os dois componentes:

  • exam-audio-list.component.spec.ts — 30 testes, cobrindo listagem, ordenação, estados de tela (carregando, negado, erro, vazio), atalhos de teclado e o fluxo de exclusão com confirmação.
  • report-audio-recorder.component.spec.ts — 19 testes, cobrindo captura real, descarte de tomada, permissão de microfone negada, navegador sem suporte, seletor de microfone e o envio ao exame.

Contexto de negócio (legado, não normativo)​

Os cards do CRM abaixo descrevem o comportamento da tela equivalente no sistema legado (AngularJS/portal-1) e serviram de referência histórica para a reescrita — não são fonte do comportamento atual, que é o que este levantamento confirmou no código do mm-pacs-portal-main-interface (branch integration/with-fixlaudo):

CardTítulo
652F-001 — Gravar áudio no exame - Front
656F-002 — Ouvir/listar áudios do exame - Front
660F-003 — Excluir áudio do exame (exclusão lógica) - Front

Páginas deste módulo​

PáginaCobre
Gravar, ouvir e excluir áudio do exameAs duas superfícies (grade de exames e laudário), passo a passo, estados de tela e permissões

Relacionado​