Épico 5 — Menu Lateral — Mapeamento de Banco de Dados (v1.7)

Documentos deste épico: 01 - Definição (v1.10) · 02 - Especificação (v1.8) · 03 - Protótipo (v1.8, código executável em 03 - Protótipo v1.8.html) · 04 - Mapeamento de Banco de Dados (v1.7). Este épico faz parte do documento guarda-chuva Check-in de Usuários.


SEÇÃO 4 — MAPEAMENTO DE BANCO DE DADOS

4.1 Endpoint: GET /api/v1/menu

Retorna, para o usuário autenticado, a árvore de grupos e itens de menu já filtrada pelo perfil de acesso vigente na clínica atual, com a marcação de quais itens estão favoritados (02 - Especificação, “Integração com Backend”).

Tabelas envolvidas

TabelaBancoFunção no endpoint
ClienteProcessoIpSegurancaHabilitação de cada Processo para o ClienteBD (tenant) do usuário — só processos habilitados entram na consulta
ClientePerfilAcessoUsuarioIpSegurancaResolve o perfil de acesso vigente do usuário na clínica atual (RemovidoEm nulo)
ClientePerfilAcessoIpSegurancaPerfil de acesso resolvido a partir de ClientePerfilAcessoUsuario
ClientePerfilAcessoProcessoIpSegurancaLiga o perfil de acesso a um ClienteProcesso; ExibeMenu e Ordem decidem quais itens aparecem e em que posição (RN-CIU-027)
ProcessoIpSegurancaNome, ícone e rota de cada item de menu
ProcessoGrupoIpSegurancaNome do grupo a que um item pertence, quando existe
ClienteUsuarioMenuFavoritoIpSegurancaMarca quais itens estão favoritados para este usuário/perfil/clínica (RN-CIU-028)
ClienteEmpresaIpSegurancaNome/logo da clínica atual — campo clinicaAtual da resposta; ClienteBDId leva ao Ambiente da clínica atual
ClienteBDIpSegurancaTipo do Ambiente da clínica atual — campo ambienteTipo da resposta (RN-CIU-032)

Mapa de campos

CampoTipoBancoObservação
clinicaAtual.clienteEmpresaIduuidClienteEmpresa.Id (uniqueidentifier)
clinicaAtual.nomestringClienteEmpresa.Nome (varchar(50))
clinicaAtual.caminhoLogostringClienteEmpresa.CaminhoLogo (varchar(100), nullable)
ambienteTipostring ("producao" | "homologacao")ClienteBD.Tipo (char(1), NOT NULL) — P → "producao", T → "homologacao", mesma tradução do E6ClienteBD alcançado por ClienteEmpresa.ClienteBDId da clínica atual (FK ClienteEmpresa_ClienteBD_FK); domínio abaixo
grupos[].processoGrupoIduuidProcessoGrupo.Id (uniqueidentifier)
grupos[].nomestringProcessoGrupo.Nome (varchar(20))grupo só entra em grupos se tiver ao menos um item que passou nos filtros abaixo (RN-CIU-026)
grupos[].itens[].clientePerfilAcessoProcessoIduuidClientePerfilAcessoProcesso.Id (uniqueidentifier)identifica o item já resolvido para o perfil vigente — é o id usado depois pelos endpoints de favoritar (4.2/4.3)
grupos[].itens[].processoIduuidProcesso.Id (uniqueidentifier)
grupos[].itens[].nomestringProcesso.Nome (varchar(50))
grupos[].itens[].iconestringProcesso.Icone (varchar(40), nullable)nome de ícone de lucide.dev/icons ou react-icons.github.io, nunca um arquivo próprio
grupos[].itens[].rotastringProcesso.Rota (varchar(128), nullable)
grupos[].itens[].favoritoboolcalculadoexiste linha ativa (RemovidoEm nulo) em ClienteUsuarioMenuFavorito para o par (ClientePerfilAcessoUsuarioId do usuário/clínica atual, ClientePerfilAcessoProcessoId do item)
itensSemGrupo[]mesmos 5 campos de grupos[].itens[] acimaProcesso.ProcessoGrupoId IS NULLmesma origem, apenas sem o agrupamento
favoritos[]mesmos 5 campos, sem favorito (a própria presença na lista já é o favorito)ClienteUsuarioMenuFavorito filtrado por ClientePerfilAcessoUsuarioId do usuário/clínica atual, RemovidoEm nulo, com INNER JOIN nos processos liberados do perfil vigente (ver Filtros)ordenado por Processo.Nome, collation da coluna SQL_Latin1_General_CP1_CI_AS (sem distinção de maiúsculas/minúsculas, com distinção de acentos) (RN-CIU-028)

Domínios

ExibeMenu (domínio de ClientePerfilAcessoProcesso.ExibeMenu, char(1), default 'S'):

ValorSignificado
SItem aparece no menu para este perfil de acesso
NItem habilitado para o cliente (ClienteProcesso) mas oculto do menu para este perfil especificamente

Tipo (domínio de ClienteBD.Tipo, char(1), NOT NULL, sem CHECK no DDL):

ValorSignificado
PAmbiente de produção — exatamente um por Cliente
TAmbiente de teste — zero ou mais por Cliente, sem distinção entre eles além de ClienteBD.Nome; liga a sinalização de RN-CIU-032

Domínio informado pelo solicitante (ver Log de Mudanças Estruturais do Banco de Dados, 07/10/2026). Como o DDL não tem CHECK, um valor fora de P/T não é impedido pelo banco, e ClienteEmpresa.ClienteBDId aceita NULL. [PROPOSTA] o endpoint devolve "homologacao" apenas quando o valor gravado, sem espaços e sem diferenciar maiúsculas de minúsculas, é T; e "producao" em qualquer outro caso — valor fora do domínio ou clínica atual sem ClienteBDId (nenhuma linha de ClienteBD). Nos dois casos de dado inválido, registra um aviso no log técnico do back-end (não exibido ao usuário), para que o cadastro seja corrigido. Motivo de tratar como produção: um dado inválido é erro de cadastro, e marcar como teste (e, em documentos impressos, como “sem valor”) um banco que pode ser o de produção é o erro mais grave dos dois.

Filtros do endpoint

  • ClienteProcesso.ClienteBDId = ClienteEmpresa.ClienteBDId da clínica atual da sessão AND ClienteProcesso.RemovidoEm IS NULL — apenas processos habilitados para o Ambiente da clínica atual (mesma fonte de ambienteTipo; um usuário pode ter vínculos em mais de um Ambiente, por isso a fonte é a clínica, não o usuário)
  • ClientePerfilAcessoUsuario referente ao (usuário logado, ClienteEmpresa atual da sessão), RemovidoEm IS NULL — resolve o ClientePerfilAcessoId vigente
  • ClientePerfilAcessoProcesso.ClientePerfilAcessoId = {perfil resolvido} AND ExibeMenu = 'S' AND RemovidoEm IS NULL — só os itens liberados para exibição no menu (RN-CIU-027)
  • Ordenação dos itens dentro de cada grupo (ou da lista sem grupo): ClientePerfilAcessoProcesso.Ordem ascendente
  • Grupos: ProcessoGrupo só entra na resposta por INNER JOIN a partir dos itens que passaram nos filtros acima — grupo sem nenhum item liberado não é devolvido (RN-CIU-026)
  • favoritos: ClienteUsuarioMenuFavorito.ClientePerfilAcessoUsuarioId = {vínculo usuário/perfil/clínica atual} AND ClienteUsuarioMenuFavorito.RemovidoEm IS NULL, sempre com INNER JOIN em ClientePerfilAcessoProcesso (Id = ClienteUsuarioMenuFavorito.ClientePerfilAcessoProcessoId, ClientePerfilAcessoId = {perfil resolvido}, ExibeMenu = 'S', RemovidoEm IS NULL) e daí em ClienteProcesso (mesmo filtro de Ambiente e RemovidoEm IS NULL do primeiro item desta lista) e Processo — os favoritos são sempre um subconjunto dos itens da árvore (RN-CIU-028). Ordenado por Processo.Nome, conforme a collation da coluna (SQL_Latin1_General_CP1_CI_AS, Biblioteca de Schema (DDL), IpSeguranca.sql). Consequência do INNER JOIN: a linha de ClienteUsuarioMenuFavorito de um item que deixou de ser liberado não é alterada — o favorito só deixa de ser devolvido, e volta a ser devolvido se o item for liberado de novo
  • ambiente: ClienteBD.Id = ClienteEmpresa.ClienteBDId da clínica atual da sessão (LEFT JOIN) — no máximo uma linha; sem linha, ambienteTipo = "producao" (ver Domínio acima)

4.2 Endpoint: POST /api/v1/menu/items/{clientePerfilAcessoProcessoId}/favorite

Favorita um item do menu para o usuário autenticado, na clínica atual (02 - Especificação, “Integração com Backend”).

Tabelas envolvidas

TabelaBancoFunção no endpoint
ClientePerfilAcessoProcessoIpSegurancaValida que o item existe, pertence ao perfil de acesso vigente e está com ExibeMenu = 'S' — senão 404
ClienteUsuarioMenuFavoritoIpSegurancaInsere (ou reativa, se já existia com RemovidoEm preenchido — RN-CIU-005) a linha do par usuário/item

Mapa de campos

CampoTipoBancoObservação
clientePerfilAcessoProcessoId (path)uuidClienteUsuarioMenuFavorito.ClientePerfilAcessoProcessoId (uniqueidentifier)metade da PK composta
——ClienteUsuarioMenuFavorito.ClientePerfilAcessoUsuarioId (uniqueidentifier)outra metade da PK composta — resolvido do usuário/clínica atual da sessão, não vem no request
favorito (response)boolfixo true

Filtros do endpoint

  • Valida que clientePerfilAcessoProcessoId pertence ao perfil de acesso vigente do usuário na clínica atual, com ExibeMenu = 'S' (mesmo filtro do GET, 4.1) — senão 404
  • Exclusão lógica (RN-CIU-005, transversal): se já existe uma linha para o par com RemovidoEm preenchido, o INSERT vira UPDATE limpando RemovidoEm, em vez de criar uma segunda linha — a PK composta (ClientePerfilAcessoUsuarioId, ClientePerfilAcessoProcessoId) não permite duplicidade

4.3 Endpoint: DELETE /api/v1/menu/items/{clientePerfilAcessoProcessoId}/favorite

Desfavorita um item do menu para o usuário autenticado, na clínica atual (02 - Especificação, “Integração com Backend”).

Tabelas envolvidas

TabelaBancoFunção no endpoint
ClienteUsuarioMenuFavoritoIpSegurancaExclusão lógica (RemovidoEm) da linha do par usuário/item

Mapa de campos

CampoTipoBancoObservação
clientePerfilAcessoProcessoId (path)uuidClienteUsuarioMenuFavorito.ClientePerfilAcessoProcessoId (uniqueidentifier)junto de ClientePerfilAcessoUsuarioId (resolvido da sessão), identifica a linha a remover
favorito (response)boolfixo false

Filtros do endpoint

  • ClienteUsuarioMenuFavorito.ClientePerfilAcessoUsuarioId = {vínculo usuário/perfil/clínica atual} AND ClientePerfilAcessoProcessoId = {path} AND RemovidoEm IS NULL — 404 se não existia favoritado
  • Exclusão lógica (RN-CIU-005, transversal): marca RemovidoEm, nunca remove a linha fisicamente

Tabelas existentes utilizadas (consolidado)

Consolidado por tabela, como referência rápida — o detalhamento de campos e uso já está nas seções de endpoint acima (4.1 a 4.3), não repetido aqui (Lição #10 da Skill Designer).

TabelaBancoEndpoints que usam
ProcessoIpSeguranca4.1
ProcessoGrupoIpSeguranca4.1
ClienteProcessoIpSeguranca4.1
ClientePerfilAcessoIpSeguranca4.1
ClientePerfilAcessoUsuarioIpSeguranca4.1
ClientePerfilAcessoProcessoIpSeguranca4.1, 4.2
ClienteUsuarioMenuFavoritoIpSeguranca4.1, 4.2, 4.3
ClienteEmpresaIpSeguranca4.1
ClienteBDIpSeguranca4.1

Nenhuma tabela nova. Toda a estrutura necessária — itens de menu, agrupamento, habilitação por cliente, liberação e ordem por perfil de acesso, favoritos, a identificação da clínica no cabeçalho do menu e o tipo de Ambiente da clínica atual — já existe em produção no banco IpSeguranca. Tipos e nullability confirmados na Biblioteca de Schema (DDL), IpSeguranca.sql, nesta versão (16/09/2026).

Separação de bancos: todas as tabelas utilizadas por este épico pertencem ao banco IpSeguranca.

Schema legado identificado e descartado

O banco de template GescomZeradoPadronizacao (mesmo arquivo de origem da Biblioteca de Schema) tem uma estrutura equivalente e mais antiga para menu: SegMenu, SegPastaMenu, SegMenuGrupo e SegMenuDetalhe — esta última com um campo PaginaInicial que corresponde, na estrutura legada, ao mesmo papel do campo ProcessoAbertura de ClienteUsuarioMenuFavorito na estrutura atual — correspondência que ajudou a localizar o campo, com o significado hoje confirmado na Seção 1 (RN-CIU-028).

Essa estrutura legada foi identificada e descartada em favor do schema moderno acima: pertence ao banco de template pré-Clean-Architecture, chaveado por identificadores numéricos locais (smallint) em vez de uniqueidentifier, e não tem nenhum dos conceitos centrais da arquitetura atual — não existe Cliente, ClienteBD (banco por tenant) nem ClienteEmpresa (clínica) na cadeia de chaves de SegMenuDetalhe, apenas um IdUsuario local. Não é uma base compatível com o modelo multi-tenant deste projeto.

Campo novo proposto: ClientePerfilAcessoUsuario.CodProfissional

A Seção 1 (RN-CIU-028) documenta que, na ausência de uma escolha manual de tela inicial, a tela inicial padrão do usuário é seu próprio cockpit, quando seu perfil de acesso tiver um indicado. Esse indicador não existe hoje em nenhuma base consultada (Biblioteca de Schema (DDL) nem EsquemaGemed21.csv — checado em 15/09/2026, reconfirmado em 16/09/2026) — confirmado pelo solicitante como campo novo a criar:

CampoTabelaTipoObrigatoriedadeValor padrãoDescrição
CodProfissionalClientePerfilAcessoUsuario (IpSeguranca)char(3)Nullable — confirmado pelo solicitante; ausência de código é o que produz “sem tela inicial” (RN-CIU-028) quando também não há escolha manualSem valor padrão (NULL)Código do tipo de cockpit/central padrão do usuário nessa combinação perfil × clínica — determina qual cockpit abre como tela inicial na ausência de uma escolha manual de tela inicial (RN-CIU-028). Nome, tabela, tipo, nullability e domínio de valores confirmados pelo solicitante. Domínio: MED, ENF, FAR, REC, ADM, BKO — cada valor aponta para a central (cockpit) correspondente; MED/ENF/FAR/BKO correspondem aos fluxos por profissional já documentados no projeto (médico, enfermagem, farmácia, backoffice), REC/ADM apontam para centrais ainda não especificadas em nenhum épico consultado até aqui.

Migração (estrutura confirmada pelo solicitante; execução real no banco ainda pendente): ALTER TABLE IPSeguranca.dbo.ClientePerfilAcessoUsuario ADD CodProfissional char(3) NULL — pendente de execução real no banco e de registro na Biblioteca de Schema (DDL) depois de aplicada. Ver Log de Mudanças Estruturais do Banco de Dados, entrada de 15/09/2026.

Nota: este campo não é consultado por nenhum dos 3 endpoints deste épico (4.1 a 4.3) — a resolução de tela inicial por CodProfissional acontece no roteamento pós-login (E1), não na montagem do menu em si. Permanece documentado aqui, e não numa seção de endpoint, por ser um achado de estrutura do épico como um todo.

Seeds da Funcionalidade

SeedTabela(s)Por cliente ou global?StatusDetalhe completo
Item de menu “Suporte Gemed” (link externo à aplicação de suporte técnico da Interprocess TI)Processo (mais ClienteProcesso e ClientePerfilAcessoProcesso, por cliente/perfil, para habilitá-lo)Global o cadastro do Processo; a habilitação por cliente e perfil de acesso é individual[PROPOSTA] — item de exemplo, existência e nome exatos a confirmar contra o mockup original do menu, hoje indisponível01 - Definição, “Lista consolidada de itens [PROPOSTA]”, item 2
Árvore completa de grupos e itens do menu (quais Processo existem, em quais ProcessoGrupo, com quais ícones e rotas)Processo, ProcessoGrupo, ClienteProcesso, ClientePerfilAcessoProcessoProcesso/ProcessoGrupo globais; ClienteProcesso e ClientePerfilAcessoProcesso por cliente/perfil[PROPOSTA] — pendente de um levantamento de produto que substitua o mockup perdido; sem essa árvore, nenhum cliente novo tem menu funcional além dos módulos já especificados individualmente (Central do Médico/Cockpit, Agenda do Médico)01 - Definição, “Lista consolidada de itens [PROPOSTA]”, item 1

Nenhum parâmetro de configuração (IpParametro/IpParametroChave ou equivalente) é necessário para esta funcionalidade — o comportamento do menu (filtragem, favoritos, busca, responsividade) não depende de nenhum valor configurável por cliente além da própria estrutura de Processo/ClientePerfilAcessoProcesso já listada acima.


Histórico de Versões

  • v1.0 (15/09/2026) — versão inicial do épico. Estrutura de dados localizada por completo na Biblioteca de Schema (DDL), banco IpSeguranca — nenhuma tabela nova. Schema legado equivalente (GescomZeradoPadronizacao.dbo.SegMenu/SegPastaMenu/SegMenuGrupo/SegMenuDetalhe) identificado e descartado, com a correspondência de SegMenuDetalhe.PaginaInicial registrada como base da interpretação [PROPOSTA] de ClienteUsuarioMenuFavorito.ProcessoAbertura. Dois seeds registrados como [PROPOSTA] — item “Suporte Gemed” e a árvore completa de grupos/itens do menu, ambos pendentes do mockup original perdido.
  • v1.1 (15/09/2026) — ClienteUsuarioMenuFavorito.ProcessoAbertura deixa de ser [PROPOSTA]: confirmado como o indicador da escolha do usuário de tela inicial do sistema (RN-CIU-028), sobrepondo o cockpit padrão quando presente. Novo achado: o campo que identifica o cockpit padrão do usuário em ClientePerfilAcessoUsuario, citado pelo solicitante, não foi localizado na Biblioteca de Schema (DDL) nem no EsquemaGemed21.csv — registrado como pendência técnica nesta seção e no Log de Mudanças Estruturais do Banco de Dados.
  • v1.2 (15/09/2026) — confirmada pelo solicitante a criação de ClientePerfilAcessoUsuario.CodProfissional, char(3) — a pendência técnica anterior (campo não localizado) vira proposta de estrutura: nova subseção “Campo novo proposto” substitui “Pendência técnica: campo do cockpit padrão”, com a migração sugerida. Nullability e domínio de valores do campo seguem [PROPOSTA], não confirmados pelo solicitante. Log de Mudanças Estruturais do Banco de Dados atualizado.
  • v1.3 (15/09/2026) — confirmados pelo solicitante os dois atributos de CodProfissional que seguiam [PROPOSTA]: nullable; domínio MED, ENF, FAR, REC, ADM, BKO (cada código aponta para a central/cockpit correspondente). Tabela de especificação do campo e migração sugerida atualizadas para refletir a estrutura confirmada — só a execução real da migração no banco segue pendente. Log de Mudanças Estruturais do Banco de Dados atualizado.
  • v1.4 (15/09/2026) — pedido do solicitante ao revisar o protótipo: ClienteEmpresa passa de “não consultada para montar o menu” para efetivamente consultada — Nome e CaminhoLogo alimentam o novo campo clinicaAtual ([PROPOSTA]) da resposta de GET /api/v1/menu, para o cabeçalho do menu exibir o nome da clínica atual (Seção 2, “Cabeçalho do Menu”). Mesmos campos de ClienteEmpresa já usados por E3/E6 para nome/logo de clínica — nenhuma estrutura nova, nenhuma tabela nova.
  • v1.5 (16/09/2026) — Reestruturação da Seção 4, a pedido do solicitante: a antiga tabela única “Tabelas existentes utilizadas” foi substituída por 3 subseções por endpoint (4.1 GET /api/v1/menu, 4.2 POST .../favorite, 4.3 DELETE .../favorite), cada uma com Tabelas envolvidas, Mapa de campos, Domínios (quando aplicável) e Filtros do endpoint — mesmo padrão (mais detalhado) já usado em Central do Médico (Cockpit) e Agenda do Médico, escolhido pelo solicitante em vez do padrão mais leve usado até então neste próprio épico e no E6 (uma linha “Tabelas envolvidas” dentro do endpoint, na Seção 2). Tipos de campo (uniqueidentifier, varchar(N), char(1), tinyint) confirmados na Biblioteca de Schema (DDL), IpSeguranca.sql, nesta versão. A tabela consolidada por tabela foi mantida, ao final, como referência rápida, sem repetir o detalhamento já dado por endpoint (Lição #10). “Schema legado identificado e descartado”, “Campo novo proposto: CodProfissional” e “Seeds da Funcionalidade” preservados sem alteração de conteúdo. Nenhuma tabela nova, nenhuma mudança de estrutura de dados — reorganização documental. Em consequência, a Seção 2 (02 - Especificação, v1.5) teve as linhas inline “Tabelas envolvidas: …” de cada endpoint removidas, substituídas por uma nota única apontando para esta Seção 4 — mesma convenção de Central do Médico/Agenda do Médico, evitando repetir a mesma informação em dois lugares (Lição #10).
  • v1.6 (07/10/2026) — ClienteBD passa a ser consultada por GET /api/v1/menu (4.1): ClienteBD.Tipo, alcançado por ClienteEmpresa.ClienteBDId da clínica atual, alimenta o novo campo ambienteTipo ([PROPOSTA], 02 - Especificação v1.6), que liga a sinalização de Ambiente de teste (RN-CIU-032, guarda-chuva v2.26). Domínio de ClienteBD.Tipo informado pelo solicitante: P (produção, único por Cliente) e T (teste, vários, distinguidos só pelo nome) — registrado em Log de Mudanças Estruturais do Banco de Dados, 07/10/2026; o DDL segue sem CHECK; o tratamento de um valor fora do domínio (devolvido como "producao") é escolha do Designer, marcada [PROPOSTA] no Domínio de 4.1. FK ClienteEmpresa_ClienteBD_FK e tipo char(1) NOT NULL conferidos na Biblioteca de Schema (DDL), IpSeguranca.sql. Nenhuma tabela nova, nenhum campo novo; nenhum seed novo (“Seeds da Funcionalidade” inalterada). Revisão por Dois Críticos (crítico técnico, subagente independente, mesma sessão; achados conferidos contra o DDL antes de aplicar): ClienteEmpresa.ClienteBDId é NULL no DDL — o mapeamento dizia “uma linha só” sem tratar a ausência de ClienteBD; passa a LEFT JOIN, com "producao" e aviso em log quando não há linha ([PROPOSTA]); a comparação com T ignora espaços e maiúsculas, e valor fora do domínio também gera aviso em log; o filtro de ClienteProcesso dizia “ClienteBD do usuário autenticado”, ambíguo para quem tem vínculos em mais de um Ambiente — passa a ser o Ambiente da clínica atual, a mesma fonte de ambienteTipo. Não corrigido: “Tipos e nullability confirmados … nesta versão (16/09/2026)” na seção consolidada, frase anterior a esta versão.
  • v1.7 (07/10/2026) — acompanha 01 - Definição v1.9 (decisões do solicitante). 4.1: favoritos passa a fazer, sempre, INNER JOIN com os processos liberados do perfil vigente (ClientePerfilAcessoProcesso com ExibeMenu = 'S' e RemovidoEm nulo, ClienteProcesso do Ambiente da clínica atual com RemovidoEm nulo, Processo) — corrige a falha apontada pelo Cenários de Teste (CT-E5-FAV-14): até a v1.6, o filtro de favoritos usava só ClientePerfilAcessoUsuarioId + RemovidoEm, e um favorito de item retirado do perfil continuaria sendo devolvido. Grupos passam a entrar só por INNER JOIN a partir dos itens liberados (grupo sem item não aparece). Ordenação de favoritos documentada com a collation real de Processo.Nome (SQL_Latin1_General_CP1_CI_AS, conferida na Biblioteca de Schema (DDL) nesta versão). Nenhuma tabela, campo ou endpoint novo; 4.2 e 4.3 inalterados (o DELETE continua aceitando desfavoritar um item já não liberado, porque não filtra por ExibeMenu).