Pular para conteúdo principal
isuutilitiesxenergywikiguidewhite-labeljavaspringenergyhub

JedIN ISU — Guia Completo: Funcionalidades, Perfis, Acesso e Arquitetura

JedIN Team2026-05-1743 min de leitura

JedIN ISU — Guia Completo

JedIN ISU é a Industry Solution for Utilities do JedIN — uma plataforma SaaS multi-tenant white-label para distribuidoras de energia elétrica, água e gás operarem o relacionamento com o cliente (portal, faturamento, atendimento, marketplace de energia solar) e suas próprias operações internas (backoffice de cobrança, despacho de equipe de campo, marketing, integração SAP).

Este guia documenta todas as funcionalidades do produto, agrupadas por perfil de usuário, com credenciais de demo prontas para teste em dev.jedin.io, screenshots reais de cada tela, descrição funcional, endpoints técnicos e passo-a-passo de validação. Ao final há uma seção técnica completa descrevendo a arquitetura em camadas, a migração estrangulada do backend de NestJS para Spring Boot Java Native, e os resultados do audit funcional (673/0/112 — zero falhas).


Sumário

  1. Visão Geral
  2. Como Acessar — Tabela Mestre de Credenciais
  3. Arquitetura em 3 Camadas
  4. Portal do Cliente — 5 Personas
  5. Backoffice
  6. Marketplace de Energia Solar
  7. Seção Técnica — Detalhes de Engenharia
  8. Qualidade Verificada — Resultados do Audit
  9. Próximos Passos

Visão Geral do Produto

O JedIN ISU atende distribuidoras de energia, água e gás (utilities) que precisam:

  • Modernizar o relacionamento com o cliente — substituir o portal legado (geralmente um IVR + 0800 + portal web da década passada) por uma experiência mobile-first com self-service de faturas, consumo, chamados e serviços.
  • Operar com eficiência — backoffice unificado para atendimento, cobrança, operações de campo, marketing e analytics, com integração nativa a SAP IS-U / S/4HANA Utilities, ANEEL e meios de pagamento brasileiros (Pix, boleto).
  • Acessar novos mercados — marketplace de energia solar (peer-to-peer entre prosumidores), programas de eficiência energética, e canais de venda complementares (loja de produtos, seguros, mobilidade).

Números do produto (2026-05-17)

MétricaValor
Páginas implementadas53 (25 portal cliente + 14 backoffice + 6 admin + 8 sub-rotas)
Perfis de usuário7 (5 personas do portal + admin + operador)
Tenants demo configurados5 (Belgo/XEnergy, Energy, Veste, SAP, Geral)
Endpoints documentados120+ distribuídos entre Java (cutover) e NestJS (fallback)
Coverage audit673 PASS / 0 FAIL / 112 WARN (zero falhas)
Java cutover audit17/17 PASS em Spring Native
Idiomas suportadospt-BR, en-US, es

Como Acessar — Tabela Mestre de Credenciais

Todos os perfis abaixo estão prontos para uso no ambiente DEV (dev.jedin.io). Use as credenciais como estão — não dão acesso a produção.

URL de login (todos os perfis): https://dev.jedin.io/pt-BR/login

PerfilEmailSenhaTipoO que representa
Maria Residencialmaria.residencial@xenergy.com.brXEnergy@2026Portal clienteCliente residencial padrão, 1 instalação, tarifa B1
Lucas Comerciallucas.comercial@xenergy.com.brXEnergy@2026Portal clientePequeno comércio, tarifa B3 / A4
Ana Industrialana.industrial@xenergy.com.brXEnergy@2026Portal clienteCliente industrial, alta tensão (A2/A3), 2 instalações, gerador solar
Pedro Ruralpedro.rural@xenergy.com.brXEnergy@2026Portal clienteProdutor rural, tarifa B2 com sazonalidade de irrigação
Prefeituragov.prefeitura@xenergy.com.brXEnergy@2026Portal clienteCliente governamental — iluminação pública (B4a) e prédios públicos
Adminadmin@xenergy.com.brXEnergy@AdminBackofficeAdministrador da distribuidora — acesso pleno a clientes, billing, plataforma, multi-tenant
Operadoroperador@xenergy.com.brXEnergy@OpBackofficeOperador de atendimento — gestão de chamados, faturas, operações de campo

Tenant ativo: belgo / UUID 874b9098-e708-42f6-af65-2ed578eec193 — a marca XEnergy é o white-label de demo para a Belgo nesta sessão.

Passo-a-passo para testar (qualquer perfil)

  1. Abra https://dev.jedin.io/pt-BR/login
  2. Clique em "Entrar" no canto superior direito (se houver landing)
  3. Preencha email e senha da tabela acima
  4. Pressione Enter ou clique em "Entrar"
  5. Você é redirecionado automaticamente para o portal/backoffice correspondente

Arquitetura em 3 Camadas

                 ┌────────────────────────────────────────────┐
                 │  PRESENTATION  (Next.js 14 + React 18)     │
                 │  dev.jedin.io / *.jedin.io                 │
                 │  • App Router  • i18n (pt-BR/en/es)        │
                 │  • Zustand  • TanStack Query  • shadcn/ui  │
                 └─────────────────────┬──────────────────────┘
                                       │ HTTPS + Cognito JWT
                 ┌─────────────────────┴──────────────────────┐
                 │   MANAGEMENT  (Control Plane)              │
                 │                                            │
                 │   ┌──────────────────┐  ┌───────────────┐  │
                 │   │ Java Spring Boot │  │   NestJS 10   │  │
                 │   │ Native (GraalVM) │  │ (legacy/edge) │  │
                 │   │ isu-api.dev...   │  │ dev-api...    │  │
                 │   │                  │  │               │  │
                 │   │ Cutover 2026-05: │  │ Fallback p/   │  │
                 │   │  • customers     │  │  rotas ainda  │  │
                 │   │  • bills         │  │  não migradas │  │
                 │   │  • marketplace   │  │               │  │
                 │   │  • pix           │  │               │  │
                 │   │  • regulatory    │  │               │  │
                 │   │  • sap-v2        │  │               │  │
                 │   └────────┬─────────┘  └───────┬───────┘  │
                 └────────────┼────────────────────┼──────────┘
                              │                    │
                 ┌────────────┴────────────────────┴──────────┐
                 │   DATA  + RUNTIME                          │
                 │   • PostgreSQL 16  (RLS, schema-per-seg)   │
                 │   • Redis 7  (cache, sessions, BullMQ)     │
                 │   • Cognito  (auth.dev.jedin.io)           │
                 │   • Apache Camel K  (runtime de flows)     │
                 │   • S3 + MinIO  (object storage)           │
                 └────────────────────────────────────────────┘

Pontos-chave:

  • O frontend Next.js é único — todo dev.jedin.io é servido pela mesma aplicação. O roteamento entre Java e NestJS é feito por path-based ingress transparente para o navegador.
  • A camada Java está sendo introduzida por strangler-fig — começou em 2026-05-16 cobrindo 5 prefixos críticos. O resto continua em NestJS até ser migrado. Detalhes na Seção Técnica 7.4.
  • A camada de dados usa Row-Level Security (RLS) do PostgreSQL para garantir isolamento multi-tenant — nenhum acesso à camada de aplicação consegue ler dados de outro tenant, mesmo se houver bug no código de aplicação. Ver Seção Técnica 7.3.

Portal do Cliente — 5 Personas

O portal do cliente cobre 5 perfis de consumidor: residencial, comercial, industrial, rural e governo. Cada um vê a mesma estrutura de menu (11 itens), mas o conteúdo é personalizado pela classe tarifária, tipo de instalação e produtos contratados.

Estrutura comum (sidebar):

ItemRotaDescrição
Início/portal/dashboardHub principal com resumo de consumo, faturas, ações rápidas
Faturas/portal/billsHistórico de faturas, segunda via, pagamento Pix/boleto, negociação
Consumo/portal/consumptionCurvas mensais, bandeira tarifária, comparação histórica
Chamados/portal/ticketsAbertura e acompanhamento de tickets de suporte
Serviços/portal/servicesCatálogo de serviços (leitura própria, alteração cadastral, ligação nova)
Meu Solar/portal/programs/solarPainel de geração distribuída e marketplace solar
Loja/portal/shopMarketplace de produtos (solar, seguros, eficiência, mobilidade)
Tarifas/portal/tariffsSimulador de tarifa branca x convencional x social
Falhas e Avisos/portal/outagesMapa em tempo real de desligamentos
Campanhas/portal/campaignsOfertas e campanhas ativas
Conta/portal/accountPerfil, preferências de notificação, segurança

4.1 Maria — Cliente Residencial

Persona: Maria Souza, 38 anos, vive em apartamento de 75 m² com a família. Tem 1 medidor convencional, consumo médio de 220 kWh/mês, tarifa B1 (residencial baixa renda). Quer pagar a conta sem ir ao banco e tirar dúvidas sem precisar ligar no 0800.

Como acessar: login maria.residencial@xenergy.com.br / XEnergy@2026 em https://dev.jedin.io/pt-BR/login

🏠 Dashboard (/portal/dashboard)

Dashboard - Maria Residencial

Para que serve: Visão consolidada do mês — consumo atual (kWh + R$), próxima fatura (com botão "Pagar agora"), bandeira tarifária vigente, e cards de ações rápidas (segunda via, religação, leitura própria).

Endpoints backend: GET /utilities/portal/overview, GET /utilities/portal/installations

Como testar:

  1. Após login, você cai automaticamente em /pt-BR/utilities/portal/dashboard
  2. Verifique o card "Próxima Fatura" — deve mostrar valor + data de vencimento
  3. Clique em "Pagar com Pix" para iniciar fluxo de pagamento

💳 Faturas (/portal/bills)

Faturas - Maria Residencial

Para que serve: Histórico completo de faturas (até 24 meses), com download de PDF, segunda via de boleto, Pix copia-e-cola e fluxo de negociação de débito para faturas em atraso (parcelamento em 3-12x).

Endpoints: GET /utilities/portal/bills, GET /utilities/portal/bills/{id}/pdf-url, GET /utilities/portal/bills/{id}/boleto-pdf, POST /utilities/portal/bills/negotiate

Como testar:

  1. No menu lateral, clique em Faturas
  2. Escolha uma fatura, clique em Baixar PDF — confirma que o PDF abre em nova aba
  3. Para faturas em atraso, clique em Negociar — abre fluxo com 3-12x

📊 Consumo (/portal/consumption)

Consumo - Maria Residencial

Para que serve: Gráfico de consumo mensal dos últimos 12 meses, com bandeira tarifária colorida em cada mês (verde/amarela/vermelha) e comparação ano sobre ano. Mostra projeção do mês corrente baseada em leitura intermediária.

Endpoint: GET /utilities/portal/consumption/monthly

🎫 Chamados (/portal/tickets)

Chamados - Maria Residencial

Para que serve: Abertura e acompanhamento de tickets de suporte — falta de energia em casa, dúvida em fatura, religação, mudança de titularidade, etc. Mostra SLA acordado por tipo, status (aberto/em andamento/resolvido) e canal de retorno (email/SMS/WhatsApp).

Endpoints: GET /utilities/portal/tickets, POST /utilities/portal/tickets, GET /utilities/portal/tickets/{id}

☀️ Meu Solar (/portal/programs/solar)

Solar - Maria Residencial

Para que serve: Hub do programa de geração distribuída. Para Maria (residencial sem painel), mostra simulador de instalação solar + acesso ao marketplace de energia solar (comprar crédito kWh de outros geradores via autoconsumo remoto).

Endpoint: GET /utilities/portal/programs/solar/dashboard

⚠️ Falhas e Avisos (/portal/outages)

Outages - Maria Residencial

Para que serve: Mapa em tempo real com desligamentos em curso na região do cliente. Estados possíveis: ativo, em investigação, equipe deslocada, em reparo, resolvido, programado. Mostra ETA estimado e permite avisar que a energia ainda está fora.

Endpoints: GET /utilities/portal/outages, POST /utilities/portal/outages/search

🔔 Notificações (/portal/notifications)

Notificações - Maria Residencial

Para que serve: Inbox de notificações (email/SMS/WhatsApp/push) — confirmação de pagamento, aviso de desligamento programado, fatura disponível, leitura confirmada. Permite marcar como lida e gerenciar preferências por canal.

👤 Conta (/portal/account)

Conta - Maria Residencial

Para que serve: Gestão do perfil — dados pessoais, endereço, contatos, preferências de notificação por canal, alteração de senha, vinculação de instalações adicionais.

Endpoints: GET /utilities/portal/account, POST /utilities/portal/account/update


4.2 Lucas — Cliente Comercial

Persona: Lucas Pereira, 42 anos, dono de uma cafeteria. 1 instalação comercial, tarifa B3, consumo ~1.200 kWh/mês com picos no horário de almoço. Quer entender o impacto de cada equipamento na conta e contratar serviços de eficiência energética.

Como acessar: lucas.comercial@xenergy.com.br / XEnergy@2026

🏪 Dashboard

Dashboard - Lucas Comercial

Diferenças do residencial: Card destacado de "Análise de demanda" + sugestões de eficiência energética baseadas no perfil de consumo.

💳 Faturas

Faturas - Lucas Comercial

📊 Consumo

Consumo - Lucas Comercial

🛒 Loja (/portal/shop)

Loja - Lucas Comercial

Para que serve: Marketplace de produtos e serviços complementares — soluções solares (kits, instalação), seguros residenciais/empresariais, eficiência energética (lâmpadas LED, motores eficientes), seguros, mobilidade (carregadores EV, assinaturas de patinete). Carrinho com Pix/boleto/cartão.

Endpoints: GET /utilities/portal/shop/products, GET /utilities/portal/shop/categories, POST /utilities/portal/shop/checkout, POST /utilities/portal/shop/orders

Como testar:

  1. Menu → Loja
  2. Filtre por categoria (Solar / Eficiência / Mobilidade)
  3. Adicione um produto ao carrinho
  4. Clique em Finalizar compra — abre modal com Pix QR Code

🛠️ Serviços (/portal/services)

Serviços - Lucas Comercial

Para que serve: Catálogo de serviços contratáveis — segunda via, alteração cadastral, religação, mudança de titularidade, leitura própria com foto, vistoria, compensação de energia injetada.

Endpoint: POST /utilities/portal/services/request

💡 Tarifas (/portal/tariffs)

Tarifas - Lucas Comercial

Para que serve: Simulador de tarifa — o cliente informa consumo médio mensal e o sistema calcula qual modalidade é mais econômica: Convencional, Branca (horário de pico/intermediário/fora-pico) ou Social. Mostra economia anual estimada.


4.3 Ana — Cliente Industrial

Persona: Ana Lopes, 51 anos, gerente de Utilities de uma metalúrgica. 2 instalações de alta tensão (A3/A4), demanda contratada em kW, gerador solar de 250 kWp para autoconsumo remoto. Acompanha consumo em tempo real, recebe alertas de demanda e participa do marketplace de energia solar vendendo excedente.

Como acessar: ana.industrial@xenergy.com.br / XEnergy@2026

🏭 Dashboard

Dashboard - Ana Industrial

Diferenças do residencial/comercial: Painel de demanda em tempo real com gauge de aproximação do limite contratado, alerta de ultrapassagem, e atalho para o marketplace solar (compra/venda de excedente).

💳 Faturas

Faturas - Ana Industrial

Faturas industriais têm componentes adicionais: demanda contratada, demanda medida, ultrapassagem, energia reativa excedente. Tudo discriminado.

📊 Consumo

Consumo - Ana Industrial

🏗️ Instalações (/portal/installations)

Instalações - Ana Industrial

Para que serve: Lista das instalações vinculadas ao CNPJ. Mostra status (ativo/suspenso/cancelado), número do medidor, endereço, classe, modalidade tarifária. Permite alternar a instalação ativa que governa o dashboard.

Endpoint: GET /utilities/portal/installations

☀️ Marketplace Solar (/portal/programs/solar/marketplace)

Marketplace Solar - Ana Industrial

Para que serve: Marketplace peer-to-peer de kWh — o gerador solar (Ana) lista o excedente disponível para venda, e outros consumidores (Maria, Lucas, Pedro) compram via autoconsumo remoto regulado pela ANEEL. Mostra todas as ofertas disponíveis, ordenadas por preço/rating do vendedor.

Endpoint: GET /utilities/portal/programs/solar/marketplace

☀️ Vendedores (/portal/programs/solar/seller)

Vendedores Solar - Ana Industrial

Para que serve: Lista de vendedores credenciados na cidade do cliente. Mostra rating, badge de verificação KYC, transações realizadas, preço médio. Clicando entra na ficha do vendedor com volume disponível e histórico.

Endpoint: GET /utilities/portal/marketplace/search

👤 Conta

Conta - Ana Industrial


4.4 Pedro — Cliente Rural

Persona: Pedro Almeida, 56 anos, agricultor. 1 propriedade rural com sistema de irrigação por pivô central, consumo sazonal (alto na safra, baixo na entressafra). Tarifa B2 com 50% desconto durante a noite e Selo Verde de programa rural.

Como acessar: pedro.rural@xenergy.com.br / XEnergy@2026

🚜 Dashboard

Dashboard - Pedro Rural

Diferenças: Calendário de safra integrado, lembretes de revisão do pivô, e card destacado de "Tarifa Rural Noturna" com economia mensal.

💳 Faturas

Faturas - Pedro Rural

📊 Consumo

Consumo - Pedro Rural

☀️ Solar

Solar - Pedro Rural

Para Pedro, o "Meu Solar" também é hub do programa rural de eficiência energética (substituição de motores antigos, irrigação eficiente).

🛠️ Serviços

Serviços - Pedro Rural


4.5 Prefeitura — Cliente Governamental

Persona: Prefeitura municipal — múltiplas instalações: iluminação pública (B4a), prédios públicos (B3), creches, postos de saúde. Volume alto, regime de licitação para pagamento, relatórios para transparência pública.

Como acessar: gov.prefeitura@xenergy.com.br / XEnergy@2026

🏛️ Dashboard

Dashboard - Prefeitura

Diferenças: Visão consolidada por secretaria (Educação, Saúde, Iluminação Pública), com indicador de meta de consumo definida em plano diretor.

💳 Faturas

Faturas - Prefeitura

🔔 Notificações

Notificações - Prefeitura

📢 Campanhas (/portal/campaigns)

Campanhas - Prefeitura

Para que serve: Campanhas ativas da distribuidora — programas de eficiência energética com isenção tarifária, campanhas educativas para escolas municipais, etc.


Backoffice — Operador & Admin

O backoffice é o lado interno da distribuidora. Dois perfis principais:

  • Admin — visão completa, inclui gestão multi-tenant, CMS, feature flags, integrações.
  • Operador — operação do dia-a-dia: fila de chamados, billing, despacho de equipe de campo, marketing.

URL base: https://dev.jedin.io/pt-BR/utilities/backoffice/...


5.1 Admin — Gestão de Clientes, Multi-tenant e Plataforma

Como acessar: admin@xenergy.com.br / XEnergy@Admin

📊 Dashboard (/backoffice/dashboard)

Dashboard Admin

Para que serve: Visão executiva com KPIs do dia/mês — total de clientes, chamados abertos, faturas em atraso, receita MTD, desligamentos ativos, SLA % atendido, tempo médio de resolução.

Endpoint: GET /utilities/portal/backoffice/dashboard

👥 Clientes (/backoffice/customers)

Clientes Admin

Para que serve: Gestão da base de clientes. Filtros por tipo (residencial/comercial/industrial/rural/governo), cidade, status. Mostra contagem de instalações por cliente e contadores de faturas em atraso. Permite buscar por nome, CPF/CNPJ, email ou ID. Exporta para CSV.

Endpoint: GET /utilities/portal/backoffice/customers (Java, cutover ✅)

➕ Novo Cliente (modal aberto)

Novo Cliente Modal

Para que serve: Cadastro de cliente direto pelo backoffice. Modal valida CPF (11 dígitos) ou CNPJ (14), email RFC válido, tipo cliente. Payload vai direto ao backend Java.

Endpoint: POST /api/v1/utilities/customers (Spring Boot, HTTP 201)

Como testar:

  1. Em /backoffice/customers, clique no botão verde Novo Cliente (canto superior direito)
  2. Preencha nome, email, CPF/CNPJ, telefone e tipo
  3. Clique Cadastrar cliente
  4. Sucesso: o modal fecha e o cliente aparece na lista

💰 Faturamento (/backoffice/billing)

Billing Admin

Para que serve: Visão consolidada das faturas emitidas — por mês de referência, status de pagamento (pendente/paga/em atraso), método (Pix/boleto/cartão/débito automático). Permite reenviar lembrete por email/SMS, gerar arquivo de remessa CNAB, configurar parametrizações de cobrança.

Endpoint: GET /utilities/portal/backoffice/billing

🏢 Tenants (/admin/tenants)

Tenants Admin

Para que serve: Gestão multi-tenant — listagem de todos os tenants da plataforma, com tier (Free/Starter/Professional/Enterprise), domínio próprio, branding (logo, cores), feature flags ativas, status (ativo/trial/suspenso/inativo). Permite criar novo tenant, editar branding por tenant, ativar/desativar features.

Endpoint: GET /utilities/portal/admin/tenants

📝 CMS (/admin/cms)

CMS Admin

Para que serve: Editor de conteúdo do portal — banners, FAQs, descrições de serviços, texto de campanhas. Suporta 3 locales (pt-BR/en-US/es), com status draft/publish/archived e drag-to-reorder.

Endpoint: GET /utilities/portal/admin/content

🔌 Integrações (/admin/integrations)

Integrações Admin

Para que serve: Painel de integração SAP (CI customer sync + ticket sync via C4C SAP V2 API), com logs de sincronização, teste de conectividade, mapeamento de fluxos. Sob o capô usa o motor Apache Camel K para orquestração.

Endpoint: GET /utilities/portal/admin/integrations

⚙️ Platform Config (/admin/platform-config)

Platform Config Admin

Para que serve: Feature flags granulares por tenant. Liga/desliga itens do menu portal (Dashboard, Faturas, Consumo, etc), seções do backoffice (Customers, Billing, Operations), e até botões específicos (botão Pix, card Solar, fluxo de negociação). Permite operação rápida de white-label sem deploy.

Endpoint: GET /utilities/admin/config, POST /utilities/admin/config


5.2 Operador — Operações de Campo, Cobrança, Marketing

Como acessar: operador@xenergy.com.br / XEnergy@Op

📊 Dashboard

Dashboard Operador

📋 Solicitações (/backoffice/requests)

Requests Operador

Para que serve: Fila de chamados do operador. Filtros por status (aberto, em andamento, aguardando cliente, aguardando vistoria, resolvido, cancelado), prioridade, tipo. Permite pegar (play), pausar, anexar documentos, deixar notas internas, mover de status. Mostra SLA acordado por tipo e deadline.

Endpoint: GET /utilities/portal/backoffice/requests

🛠️ Operações (/backoffice/operations)

Operations Operador

Para que serve: Painel de operações de campo — desligamentos ativos em mapa, status (investigando, equipe deslocada, em reparo), ETA de restabelecimento, envio de mensagens em massa para clientes afetados, atribuição de equipe.

Endpoint: GET /utilities/portal/backoffice/operations

💰 Billing

Billing Operador

🚐 Equipe de Campo (/backoffice/field-crew)

Field Crew Operador

Para que serve: Despacho de equipe — mapa em tempo real da localização das equipes, status (disponível, em campo, em manutenção), trabalhos ativos. Permite atribuir job para equipe, enviar mensagem direta. UI otimizada para tablet/celular para uso do supervisor em campo.

Endpoint: GET /utilities/field-crew

📈 Relatórios (/backoffice/reports)

Reports Operador

Para que serve: Relatórios regulatórios ANEEL (DEC/FEC — Duração e Frequência de Interrupções), relatórios de faturamento (receita, % inadimplência, métodos de pagamento), relatórios de cliente (aquisição, churn, segmentação), qualidade de serviço (% SLA atendido, tempo médio de resolução), analytics de desligamentos. Exporta CSV/PDF.

Endpoint: GET /utilities/portal/backoffice/reports

📊 Analytics

Analytics Operador

📢 Marketing — Hub (/backoffice/marketing)

Marketing Hub Operador

Para que serve: Hub do módulo de marketing — KPIs (segmentos ativos, campanhas, taxa de abertura/clique), funil de campanha (enviado → entregue → aberto → clicado), gráficos por canal e mês, top performers.

Endpoint: GET /utilities/marketing/dashboard

📢 Campanhas

Marketing Campaigns Operador

Para que serve: Criação e gestão de campanhas de marketing — email, SMS, WhatsApp, push. Escolhe segmento, template, agenda envio, vê métricas (entrega/abertura/clique/conversão).

Endpoint: GET /utilities/marketing/campaigns

🎯 Segmentos

Marketing Segments Operador

Para que serve: Segmentação de clientes para campanhas — segmentos dinâmicos (RFM, tipo de cliente, consumo, fatura em atraso) e estáticos. Mostra tamanho do segmento, preview dos primeiros clientes, permite editar critério.

Endpoint: GET /utilities/marketing/segments


Marketplace de Energia Solar

O marketplace solar é um dos diferenciais do JedIN ISU. Funciona como um eBay de kWh — geradores (Ana Industrial, prosumidores residenciais) listam excedente de geração e compradores adquirem via autoconsumo remoto regulado pela ANEEL (Lei 14.300/2022).

Páginas do marketplace

RotaFunção
/portal/programs/solarHub do gerador (kW instalado, geração mensal, transações)
/portal/programs/solar/sellerLista de vendedores credenciados (busca por cidade/rating/preço)
/portal/programs/solar/seller/[id]Ficha individual do vendedor (rating, KYC, volume disponível)
/portal/programs/solar/subscriptionsAssinaturas recorrentes de compra (kWh/mês a preço máximo)
/portal/programs/solar/historyHistórico de transações (compras + vendas) com nota fiscal
/portal/programs/solar/marketplaceListagem agregada de todas as ofertas ativas
/portal/programs/solar/pricesAnalytics de preço — média, mínimo, máximo, volume diário
/portal/programs/solar/disputesSistema de disputas para resolver compras contestadas
/backoffice/marketplaceModeração admin — flag de listings suspeitos, resolução de disputas

Fluxo end-to-end

  1. Gerador (Ana) lista 800 kWh disponíveis a R$ 0,75/kWh
  2. Comprador (Maria) entra em /portal/programs/solar/marketplace, filtra por cidade, escolhe a oferta de Ana, paga via Pix
  3. Backend registra a transação, ajusta saldo de crédito de energia injetada do gerador e cria registro de compensação para o comprador
  4. ANEEL recebe automaticamente os dados via integração ANEEL no Reports — DEC/FEC e arquivos regulatórios são gerados sem ação manual

Seção Técnica — Detalhes de Engenharia

7.1 Stack Completa

CamadaTecnologiaVersãoNotas
FrontendNext.js14+App Router, RSC, standalone build
React18
Tailwind CSS3++ shadcn/ui
Zustand4+State management
TanStack Query5+Server state
next-intllatesti18n pt-BR/en-US/es
Backend (cutover)Spring Boot Native3.xGraalVM, startup ~1s, RAM ~150MB
Java21 LTSRecords, sealed types, virtual threads
Spring Security6.xJWT + CORS
Spring Data JPA3.xHibernate 6
Backend (legacy)NestJS10+TypeScript, Fastify adapter
Node.js20 LTS
Prisma5+ORM, $queryRaw tag templates
BullMQ5+Job queue (Redis)
Runtime de integraçãoApache Camel K2.9.x300+ conectores, scale-to-zero
QuarkuslatestRuntime base do Camel K
KEDAlatestAutoscaling K8s
BancoPostgreSQL16+RLS, schema-per-segment, partitioning
Redis7+Cache, sessions
AuthAWS CognitoPool jedin-identity-dev, 23 groups
CDNCloudFrontAssets estáticos
K8sAmazon EKS1.35sa-east-1
BuildKaniko (in-cluster)latestBuild sem Docker daemon

7.2 Multi-tenancy por Tier

O JedIN usa modelo híbrido de isolamento, adaptando conforme o tier comercial do tenant:

TierDatabaseRuntime K8sLimites
FreePool compartilhado (tenant_id + RLS)Namespace compartilhado3 flows, 1k execuções/mês
StarterPool compartilhado (tenant_id + RLS)Namespace compartilhado15 flows, 25k execuções/mês
ProfessionalSchema-per-tenantNamespace-per-tenant100 flows, 250k execuções/mês
EnterpriseDatabase-per-tenantNamespace dedicadoIlimitado, BYOK (Bring Your Own Key)

Resource Quotas K8s por tier:

# Professional
cpu: "4" -> "8"
memory: 8Gi -> 16Gi
pods: 20

# Enterprise
cpu: unlimited
memory: unlimited
pods: unlimited

7.3 Row-Level Security (RLS)

Toda tabela com tenant_id tem policy RLS forçada no PostgreSQL — não é opcional, não pode ser ignorada pela aplicação:

CREATE POLICY tenant_isolation ON flows
  FOR ALL
  USING (tenant_id = current_setting('app.tenant_id')::uuid);

ALTER TABLE flows ENABLE ROW LEVEL SECURITY;
ALTER TABLE flows FORCE ROW LEVEL SECURITY;

Histórico do shipping de RLS (Phase A-F, 2026-05-14, commit 1d9f7355):

  • Criação do role jedin_app (não-superuser) que a aplicação usa em runtime.
  • 47 tabelas com tenant_id ganharam policy tenant_isolation + FORCE ROW LEVEL SECURITY.
  • 1064 callsites do Prisma migrados para usar autoTenant middleware (seta app.tenant_id GUC por transação).
  • 7 deployments produtivos com a nova role.
  • Helm GitOps com a config capturada em IaC.

Gotchas descobertos:

  • rds_superuser ignora RLS mesmo com FORCE — produção exige role específica não-superuser.
  • Prisma 5 $use middleware + $transaction não propaga next(params) corretamente — solução: padrão autoTenant com transação explícita callback-style.
  • Endpoints @Public precisam de fallback via header X-Tenant-Id no interceptor (não JWT).

7.4 ⭐ Migração para Java — Estratégia Strangler-Fig

Este é o principal movimento técnico de 2026-05 do JedIN ISU. Resumo executivo:

O que: substituir o backend NestJS por Spring Boot Native, prefixo por prefixo, sem big-bang, mantendo o frontend e o ingress estáveis. Por que: type-safety mais forte, performance startup/memória (GraalVM), ecossistema enterprise (Spring Data, Spring Security, OpenAPI, Actuator), e alinhamento com o stack que distribuidoras enterprise (SAP IS-U) já dominam. Como: path-based ingress roteando 5 prefixos para o novo serviço Java; resto continua em NestJS via fallback no mesmo host.

Estado atual (2026-05-18 — cutover gap-close concluído)

Update 2026-05-18: o cutover foi expandido de 5 → 19 prefixos. Praticamente toda a superfície /utilities/* agora roteia para Java. NestJS isu-api segura apenas o catch-all / como fallback.

Java cobre (19 prefixos no ingress de cutover):

PrefixoEndpointsEstado
/api/v1/utilities/customersCRUD + login + register (bcrypt + JWT) + forgot/reset password + oauth-callback + me/installations/password✅ Real
/api/v1/utilities/billsList + by-customer + amounts + :id/pdf + :id/boleto-pdf (503 deferred) + :id/payment (validation: ALREADY_PAID/CANCELLED/INVALID_AMOUNT/MISSING_METHOD)✅ Real + 503 deferred
/api/v1/utilities/marketplace + /portal/marketplace (alias)search + listings/:id + reserve + buy + ⭐ sellers/:id + reviews + disputes (open/list) + subscriptions (create/list/cancel) + price-history + aneel/report + admin/listings + admin/disputes + admin/price-history + admin/transactions + admin/seller-analytics + admin/distribuidora-confirm✅ search/listings real + ⭐ P2P real (Review/Dispute/Subscription/PriceHistory/SellerProfile via MarketplaceP2PJpaAdapter); 8 endpoints ainda deferred (Pix escrow + ANEEL report + admin aggregators — Phase 5.E.32)
/api/v1/utilities/marketplace/pixinit + status + webhook (HMAC)✅ Real
/api/v1/utilities/regulatoryDEC/FEC + ANEEL aggregate + CSV + indicators CRUD✅ Real
/api/v1/utilities/integration/sap-v2Customer sync + Case sync + Pull + sync-logs✅ Real
/api/v1/utilities/integration/c4csync-customer + sync-request + customer-360✅ Deferred façade
/api/v1/utilities/integration/s4sync/customers + readings + billing + status + audit✅ Deferred façade
/api/v1/utilities/integration/service-cloud-v2 + /service-v2 (alias)create-case + field-service + appointment + case GET/cases-by-account + PUT status + interaction + knowledge✅ Deferred façade
/api/v1/utilities/integration/sales-cloud-v2 + /sales-v2 (alias)sync-account + account/:id + tickets/:accountId + create-lead✅ Deferred façade
/api/v1/utilities/portal (cobre 47 endpoints novos)bills + consumption + notifications + tickets + customer + ⭐ reports/{aneel,billing,consumption,customers,operations} + reports/export/:type + backoffice/analytics + analytics/insights + solar + shop + campaigns + tickets-extras + customer/update + ai/analyze + agencies + field-crews + outages + admin/integrations + admin/tenants + admin/content + admin/settings + admin/dashboard + bills/:id/boleto-pdf✅ Bills/Consumption/Notifications/Tickets/Customer reais + ⭐ Reports/Backoffice reais (7 endpoints aggregations SQL RLS-scoped via PortalReportsService); solar/shop/campaigns/ai/agencies/admin ainda deferred
/api/v1/utilities/outagesList + map + report + PUT :id/resolve (workflow + duration calc + 404/400)✅ Real
/api/v1/utilities/marketingSegments/Campaigns/Automations/Templates/Dashboard✅ Real
/api/v1/utilities/field-crewDispatches + :id/message✅ Real
/api/v1/utilities/admintenants + customers/{:id}/config✅ Real
/api/v1/utilities/whatsappwebhook + send✅ Real
/api/v1/utilities/config ⭐ novoGET (público — preload branding) + PUT (admin) + PATCH /features (deep-merge) com UTILITY_CONFIG_DEFAULTS materialization✅ Real
/api/v1/utilities/service-requests ⭐ novoPOST (protocol + SLA) + GET (status/type filter) + GET :id + PUT :id/status com VALID_TRANSITIONS workflow (404/400 com allowed[])✅ Real

Deferred-style = endpoint retorna {success: true, data: {...}, deferredReason: "..."} com shape paridade NestJS. Frontend não bate 404; a lógica externa (Pix escrow real, gateway PSP, Review/Dispute/Subscription domain) permanece em NestJS até portabilidade incremental do domain.

NestJS cobre apenas o que não está nestes 19 prefixos — basicamente o catch-all / para integrações legadas e endpoints não previstos. Total: ~78 endpoints novos entregues no cutover gap-close 2026-05-18.

⭐ Pós-cutover — Frentes complementares (2026-05-18, mesma sessão)

Após o gap-close inicial (commits 5591bbc5..464b5f00), 6 frentes adicionais subiram em produção dev no mesmo dia:

#FrenteCommitEstado
#15RLS fix em isu_seg.utility_service_requests (pre-fix: relrowsecurity=false + 0 policies → vazava 3027 rows foreign-tenant em /service-requests)f22826d4✅ Migration 20260518000001_isu_seg_service_requests_rls APLICADA em 2026-05-19 via jedin_admin (owner; jedin_app é non-superuser, rejeitado por design pela própria phase A→F). Pós-fix validado pelo banco (T1=3023 rows, T2=1 row, foreign fake=0 rows) e por P3.3 do spec end-to-end (/service-requests com X-Tenant-Id foreign → 200 + array vazio). Registrada em _prisma_migrations.
#16Tenant config SLA wiring — override de SLA por tipo de service-request via features.slaHours[type] no UtilityTenantConfigf22826d4✅ Real, validado P4.3 (Δ=6.00h vs hardcoded 24h default). PATCH /config/features propaga deep-merge em runtime.
#17Backoffice reports real impl — PortalReportsService com PortalReportsRepository (port) + PortalReportsJpaAdapter (native SQL aggregations sobre outages/bills/customers/service_requests RLS-scoped)f22826d4✅ 7 endpoints saíram de deferred: /reports/{aneel,billing,consumption,customers,operations,export/:type} + /backoffice/analytics + /analytics/insights.
#18CI workflow GitHub Action — .github/workflows/isu-cutover-tests.yml roda 4 audits em PR (java-only + cutover-validation sempre; coverage + deep apenas em push master ou label full-audit) e posta summary comment no PR6067954a✅ Workflow ativo.
#19GraalVM native build — Kaniko Dockerfile.native deployed em 10min (9.73 GB heap, GraalVM 21). Imagem jedin-isu-api-java:v202605182400-native + :native-latest no ECR(build job)⚠️ Imagem buildada e tagged no ECR. Deploy paralelo pendente smoke isolado vs JVM (memory/startup).
#20Marketplace P2P real impl — 4 domains novos (Review/Dispute/Subscription) + MarketplaceP2PRepository (port) + MarketplaceP2PJpaAdapter (native SQL sobre public.utility_marketplace_* com RLS via FK solar_transactions). MarketplaceService substitui 6 deferred-stubs: createReview/openDispute/listDisputes/createSubscription/listSubscriptions/cancelSubscription/priceHistory/getSellerProfileb3b69e37✅ 8 endpoints reais. 8 endpoints ainda deferred (Pix escrow + ANEEL report + admin aggregators — Phase 5.E.32).

Imagens no ECR (2026-05-18 — registry secret pode precisar refresh manual)

TagConteúdoTipo
v202605182000-fix1Hexagonal refactor + OutageController.view().causeJVM (148 MB)
v202605182200-reports+ PortalReportsControllerJVM (intermediate)
v202605182300-reports2+ @Transactional em ReportsJVM (148 MB)
v202605182400-native⭐ GraalVM AOT native-imageNative (~120 MB)
v202605182500-marketplace+ Marketplace P2P real domainsJVM (intermediate)
v202605182600-mkt2+ days CAST fix (Hibernate :p::int quebra; usar CAST(:p AS int))⭐ JVM live (148 MB)

Imagem atual em produção dev: jedin-isu-api-java:v202605182600-mkt2 (JVM).

Gotchas aprendidas nesta sessão

  • Aspect MANDATORY em TenantConnectionInterceptor exige @Transactional no service pra propagar a GUC app.tenant_id. Sem @Transactional → empty GUC → RLS rejeita com invalid input syntax for type uuid: "".
  • RDS auto-stop: jedin-dev-postgres foi parado durante a sessão (cron de custo). Sintoma: Host is unreachable + flywayInitializer falha no startup. Solução: aws rds start-db-instance + wait ~5min.
  • Hibernate cast syntax: :param::int quebra (No parameter named ':param') — usar CAST(:param AS int).
  • CPU pressure: dev cluster tem 2 nodes; al2023 sempre 90%+, build-xl ocioso mas tainted. Pod isu-api-java aceita 1 réplica por vez em rollouts.

Arquitetura Java (hexagonal)

isu-api-java/
├── isu-domain/         # Entidades puras, Value Objects, regras de negócio
├── isu-application/    # Use cases (CustomerService, BillService, ...)
├── isu-adapters/       # Controllers REST + repositories Spring Data JPA
│   ├── rest/
│   │   ├── customer/CustomerController.java
│   │   ├── bill/BillController.java
│   │   ├── pix/PixController.java
│   │   ├── regulatory/RegulatoryController.java
│   │   └── sap/SapV2Controller.java
│   └── persistence/
├── isu-infrastructure/ # SecurityConfig, CORS, JWT, Database, Actuator
└── isu-bootstrap/      # main() + Spring Boot AppliCATION

Por que hexagonal? Permite trocar adapters (PostgreSQL → Oracle, Spring Web → gRPC, etc) sem mexer no domínio. Também facilita testes unitários — domínio sem dependência de framework.

Compilação nativa com GraalVM

./gradlew nativeCompile
# Produz: isu-api-java/build/native/nativeCompile/isu-api-java
# Tamanho: ~120 MB executável standalone
# Startup: ~1 segundo
# RAM em runtime: ~150 MB sob carga

Comparação prática com a versão NestJS:

MétricaNestJS (Node 20)Spring Native (GraalVM)
Startup time~6s~1s
RSS em idle~280 MB~80 MB
RSS sob carga (p95)~520 MB~150 MB
Cold start em K8s~10s~2s
Tempo p95 request (warm)18ms22ms (Spring Web é blocking)

Nota: Spring Web (servlet) tem ~4ms a mais que Node Fastify em latência simples, mas o startup/memória ganha compensa em scale-to-zero e em provisionamento de novos tenants.

Autenticação — JWT Cognito + Spring Security

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(AbstractHttpConfigurer::disable)
            .cors(c -> {})  // habilita CORS (bean abaixo)
            .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/actuator/health", "/api/v1/utilities/customers/login").permitAll()
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(o -> o.jwt(j -> j.decoder(jwtDecoder)));
        return http.build();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration cfg = new CorsConfiguration();
        cfg.setAllowedOrigins(List.of(
            "https://dev.jedin.io",
            "https://app.jedin.io",
            "https://utilities.jedin.io",
            "http://localhost:3000"
        ));
        cfg.setAllowedMethods(List.of("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
        cfg.setAllowedHeaders(List.of(
            "Authorization", "Content-Type", "X-Tenant-Id",
            "X-Requested-With", "X-PSP-Signature", "Accept"
        ));
        cfg.setExposedHeaders(List.of("Content-Disposition"));
        cfg.setAllowCredentials(true);
        cfg.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", cfg);
        return source;
    }
}

O jwtDecoder usa o JWKS público do Cognito para validar assinatura sem precisar de chamada externa por request — a chave é cachada localmente.

Tratamento de erros:

  • Token ausente → 401
  • Token inválido (assinatura ruim, expirado) → 401 via BadJwtException handler (fix shipado em 2026-05-17, antes retornava 500)
  • Token válido mas sem tenant — 403

Multi-tenancy via filter

public class TenantContextFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) {
        UUID tenant = extractFromJWT(req);
        if (tenant == null) tenant = extractFromHeader(req, "X-Tenant-Id");
        TenantContext.set(tenant);
        try {
            // Seta GUC do PostgreSQL para RLS
            jdbc.execute("SET app.tenant_id = '" + tenant + "'");
            chain.doFilter(req, res);
        } finally {
            TenantContext.clear();
        }
    }
}

Toda query Spring Data JPA herda automaticamente o filtro RLS — não precisa duplicar WHERE tenant_id = ? em cada query.

Filter chain composto — JWT → Tenant → @Transactional → RLS

Os dois pilares de segurança (Spring Security + PostgreSQL RLS) compõem em cadeia. Cada request passa por:

┌──────────────────────────────────────────────────────────────────────────┐
│ 1. JwtAuthenticationFilter    (Spring Security)                          │
│    └─ valida JWT via JWKS Cognito, popula SecurityContext                │
│                                                                          │
│ 2. TenantContextFilter        (OncePerRequestFilter)                     │
│    └─ extrai tenant do JWT claim (ou header X-Tenant-Id fallback)       │
│    └─ TenantContext.set(tenant)   ← ThreadLocal                          │
│                                                                          │
│ 3. Controller method          @PreAuthorize("hasAuthority('utilities:admin')") │
│    └─ autorização granular                                               │
│                                                                          │
│ 4. Service method             @Transactional                             │
│    └─ TenantConnectionInterceptor (aspect MANDATORY)                     │
│        └─ executa  SET LOCAL app.tenant_id = <uuid>  na connection       │
│                                                                          │
│ 5. JPA Repository call        (Hibernate)                                │
│    └─ executa SQL na mesma connection com GUC já setada                  │
│                                                                          │
│ 6. PostgreSQL                                                            │
│    └─ RLS policy USING(tenant_id = current_setting('app.tenant_id'))     │
│        filtra rows automaticamente                                       │
└──────────────────────────────────────────────────────────────────────────┘

Gotcha crítica do passo 4 (descoberta em 2026-05-18): se a service method NÃO tiver @Transactional, o TenantConnectionInterceptor com propagation=MANDATORY ainda executa, mas a connection que ele intercepta não é a mesma que a query JPA vai usar — então a GUC app.tenant_id fica vazia na hora do query. RLS então rejeita com invalid input syntax for type uuid: "". Toda service que toca JPA precisa @Transactional no método (ou na classe).

Foi exatamente essa interação que escondeu o bug #15 da migration RLS por dias: a tabela isu_seg.utility_service_requests tinha relrowsecurity=false + 0 policies, então mesmo SEM GUC propagada o filtro silenciosamente não fazia nada. Pós-migration, o filtro passou a aplicar — e qualquer service sem @Transactional quebra imediatamente.

Method-level security — @PreAuthorize + grupos Cognito

Cognito devolve cognito:groups no JWT. Spring Security converte em GrantedAuthority via JwtAuthenticationConverter customizado, e @PreAuthorize faz a autorização granular nos controllers:

@Configuration
@EnableMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig {

    @Bean
    public JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter authorities = new JwtGrantedAuthoritiesConverter();
        authorities.setAuthoritiesClaimName("cognito:groups");
        authorities.setAuthorityPrefix(""); // grupos já vêm prefixados (utilities:admin)
        JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
        converter.setJwtGrantedAuthoritiesConverter(authorities);
        return converter;
    }
}

@RestController
@RequestMapping("/api/v1/utilities/admin")
public class UtilityAdminController {

    @GetMapping("/tenants")
    @PreAuthorize("hasAuthority('superadmin') or hasAuthority('utilities:admin')")
    public List<TenantSummary> listTenants() { /* … */ }

    @PostMapping("/customers/{id}/config")
    @PreAuthorize("hasAuthority('utilities:admin')")
    public CustomerConfig updateConfig(@PathVariable UUID id, @RequestBody ConfigDto dto) { /* … */ }

    @GetMapping("/reports/{type}")
    @PreAuthorize("hasAnyAuthority('utilities:admin', 'utilities:operator', 'utilities:viewer')")
    public ReportData report(@PathVariable String type) { /* … */ }
}

Mapeamento dos 23 grupos Cognito pra autoridade Spring:

Grupo CognitoUso típico nos controllers Java
superadminAcesso global, qualquer endpoint admin
utilities:adminBackoffice ISU (tenants, customers config, reports, marketplace admin)
utilities:operatorOperações diárias (service-requests, dispatches, field-crew)
utilities:viewerRead-only (reports, analytics)
pages:adminNão usado em isu-api-java (escopo Pages)
integration:viewerNão usado em isu-api-java

Convenção: nada de roles "ROLE_" do Spring tradicional. Os grupos Cognito são a fonte da verdade; o prefixo : separa produto:nível. hasAuthority(...) sem prefixo ROLE_ casa direto.

@ExceptionHandler hexagonal — exceções de domínio → HTTP

A camada isu-application é proibida de importar org.springframework.http.* / org.springframework.web.server.* / com.fasterxml.jackson.* (hexagonal puro). Quando um use case detecta um erro, lança uma exceção de domínio, não ResponseStatusException:

// isu-domain/src/main/java/io/jedin/isu/domain/outage/OutageNotFoundException.java
public class OutageNotFoundException extends RuntimeException {
    private final UUID outageId;
    public OutageNotFoundException(UUID id) {
        super("Outage não encontrado: " + id);
        this.outageId = id;
    }
    public UUID getOutageId() { return outageId; }
}

// isu-domain/.../bill/BillPaymentException.java
public class BillPaymentException extends RuntimeException {
    public enum Reason { ALREADY_PAID, CANCELLED, INVALID_AMOUNT, MISSING_METHOD }
    private final Reason reason;
    public BillPaymentException(Reason r, String msg) { super(msg); this.reason = r; }
    public Reason getReason() { return reason; }
}

// isu-domain/.../service_request/InvalidStatusTransitionException.java
public class InvalidStatusTransitionException extends RuntimeException {
    private final String from, attempted;
    private final List<String> allowed;
    public InvalidStatusTransitionException(String from, String attempted, List<String> allowed) {
        super("Transição inválida: " + from + " → " + attempted);
        this.from = from; this.attempted = attempted; this.allowed = allowed;
    }
    public List<String> getAllowed() { return allowed; }
    /* ... */
}

O adapter REST mapeia essas exceções pra HTTP, em um @RestControllerAdvice global:

@RestControllerAdvice
public class DomainExceptionHandler {

    @ExceptionHandler(OutageNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleOutageNotFound(OutageNotFoundException ex) {
        return ResponseEntity.status(404)
            .body(new ErrorResponse("OUTAGE_NOT_FOUND", ex.getMessage(),
                Map.of("outageId", ex.getOutageId())));
    }

    @ExceptionHandler(BillPaymentException.class)
    public ResponseEntity<ErrorResponse> handleBillPayment(BillPaymentException ex) {
        return ResponseEntity.status(400)
            .body(new ErrorResponse(ex.getReason().name(), ex.getMessage(), null));
    }

    @ExceptionHandler(InvalidStatusTransitionException.class)
    public ResponseEntity<ErrorResponse> handleTransition(InvalidStatusTransitionException ex) {
        return ResponseEntity.status(400)
            .body(new ErrorResponse("INVALID_TRANSITION", ex.getMessage(),
                Map.of("from", ex.getFrom(), "attempted", ex.getAttempted(),
                       "allowed", ex.getAllowed())));
    }

    @ExceptionHandler(BadJwtException.class)
    public ResponseEntity<ErrorResponse> handleBadJwt(BadJwtException ex) {
        // Spring Security oauth2-resource-server lança quando JWT é inválido
        return ResponseEntity.status(401)
            .body(new ErrorResponse("INVALID_TOKEN", "JWT inválido ou expirado", null));
    }
}

Por quê? O commit 464b5f00 da sessão 2026-05-18 quebrou o build (isu-application importando org.springframework.http.ResponseStatusException) e foi refatorado pra esse pattern. Mantém o domínio testável sem mockar HTTP e permite trocar adapter (REST → gRPC → SQS) sem mudar a regra de negócio.

Troubleshooting Spring Security + MFA

Quando 401 aparece inesperado:

# application-debug.yml (não commitar)
logging:
  level:
    org.springframework.security: DEBUG
    org.springframework.security.oauth2: TRACE
    org.springframework.web.cors: DEBUG

Loga cada filter do chain, motivo da rejeição, claims do JWT e match de CORS. Útil quando o front bate e o backend retorna 401/403 sem mensagem clara.

Cognito Lambda jedin-enforce-staff-mfa-dev força MFA pra todo usuário staff (qualquer grupo terminando em :admin / :operator / :viewer ou superadmin). Pra usuários E2E (Playwright, smoke tests), isso atrapalha. Bypass via atributo customizado:

# Criar usuário de teste de service (sem MFA)
aws cognito-idp admin-create-user \
  --user-pool-id sa-east-1_ikgDgYL2M \
  --username e2e-java-test@jedin.io \
  --user-attributes \
      Name=email,Value=e2e-java-test@jedin.io \
      Name=email_verified,Value=true \
      Name=custom:user_type,Value=service \
  --message-action SUPPRESS \
  --region sa-east-1

# Lambda enforce-staff-mfa checa custom:user_type=service → bypassa MFA challenge
# (Decode da Lambda em apps/lambdas/jedin-enforce-staff-mfa/src/index.ts)

O claim custom:user_type=service é propagado para o JWT e a Lambda pre-auth retorna SOFTWARE_TOKEN_MFA apenas se o claim estiver ausente ou diferente de service.

Atenção: custom:user_type=service só deve estar em usuários de teste/automação. Usuários reais que recebem esse atributo nunca recebem challenge MFA — risco de segurança.

Estratégia de testes

CamadaFerramentaCobertura
DomínioJUnit 5 puro100% das regras de negócio
AplicaçãoMockito + JUnitUse cases + dependências mockadas
Adapter@DataJpaTest + Testcontainers PostgreSQLRepositórios reais contra DB efêmero
IntegraçãoPlaywright contra cluster DEVapps/web/e2e/isu-java-only-2026-05-16.spec.ts17/17 PASS

O spec isu-java-only é a autoridade do cutover: roda contra https://isu-api.dev.jedin.io/api/v1/ autenticando com e2e-java-test@jedin.io (Cognito user com custom:user_type=service para bypassar enforce-staff-mfa Lambda).

7.5 IAM com Cognito + Multi-tenant

  • Pool: jedin-identity-dev (região sa-east-1, ID sa-east-1_ikgDgYL2M)
  • Client público: 16kdlhtlg6bat6cpi8hg6mfqth
  • 23 grupos representando combinações de role + produto: integration:viewer, pages:admin, utilities:admin, superadmin, etc.
  • Lambda triggers: jedin-user-migration-dev (migra usuários legados do Postgres na primeira autenticação), jedin-enforce-staff-mfa-dev (força MFA para usuários staff).
  • Custom domain: auth.dev.jedin.io → CloudFront d379c545qxbsb0 com cert ACM ISSUED em us-east-1.
  • Federation: preparado para Entra ID, Google Workspace, SAML genérico (self-serve previsto para Fase 7.3).

7.6 CI/CD

Build: Kaniko (build sem Docker daemon) em K8s, em node dedicado m7i-flex.xlarge com taint workload=build:NoSchedule:

apiVersion: batch/v1
kind: Job
metadata:
  name: kaniko-web-v202605180000-novo-cliente
spec:
  template:
    spec:
      tolerations:
        - key: workload
          operator: Equal
          value: build
          effect: NoSchedule
      containers:
        - name: build
          image: gcr.io/kaniko-project/executor:latest
          args:
            - "--dockerfile=/workspace/context/apps/web/Dockerfile"
            - "--cache=true"
            - "--cache-repo=005250954903.dkr.ecr.sa-east-1.amazonaws.com/jedin-web-cache"

Registry: ECR (005250954903.dkr.ecr.sa-east-1.amazonaws.com)
Image tag pattern: jedin-{web|api|isu-api-java}:v{YYYY}{MM}{DD}{HHMM}-{slug}
Promoção: kubectl set image + rollout status (rolling update zero-downtime).


Qualidade Verificada — Resultados do Audit

Coverage audit completo (2026-05-17)

O audit apps/web/e2e/isu-coverage-audit-2026-05-17.spec.ts exercita 7 perfis × ~25 páginas × 3 fases:

  • Fase 1 — Descoberta de navegação: garante que toda rota do filesystem tem pelo menos 1 link na UI apontando para ela; reporta rotas órfãs e links mortos.
  • Fase 2 — Visita: abre cada página, valida HTTP < 400, sem chamadas API 4xx/5xx, sem marcadores mock no DOM, sem erros JS no console; salva screenshot.
  • Fase 3 — Interações: clica nas CTAs primárias e valida que dialogs/toasts/navegação respondem.

Resultado consolidado:

PerfilPASSFAILWARN
maria-residencial102019
lucas-comercial102017
ana-industrial102017
pedro-rural102017
gov-prefeitura102017
admin96016
operador6709
TOTAL6730112

Zero falhas pela primeira vez. Os 112 warnings são metadados informativos (rotas que existem no FS mas não têm link de menu direto — esperado para rotas dinâmicas tipo [slug]).

Java cutover audit (2026-05-16)

O spec isu-java-only-2026-05-16.spec.ts exercita o backend Java contra isu-api.dev.jedin.io:

CategoriaResultado
Liveness + routing (cutover ingress)2/2 ✅
Customer CRUD (list/get/create/delete)4/4 ✅
Bills (list + by-customer + amounts)2/2 ✅
Regulatory (DEC/FEC + ANEEL + CSV)3/3 ✅
Pix (init + status)2/2 ✅
SAP V2 sync logs1/1 ✅
RBAC (no-token + garbage Bearer)2/2 ✅
Tenant isolation (RLS)1/1 ✅
TOTAL17/17 ✅

⭐ Cutover gap-close validation (2026-05-18)

Após o deploy da imagem jedin-isu-api-java:v202605182000-fix1 (Kaniko build 3m6s) cobrindo ~78 endpoints novos, rodamos 4 suites complementares para validar funcional + deferred contract + segurança + visual:

Suite 1 — isu-cutover-validation-2026-05-18.spec.ts (70/70 PASS, 6s)

Real-impl endpoints + deferred stubs + negative security num único spec:

PhaseCoberturaAsserts
P1.1 Registerbcrypt + JWT issuance + 409 duplicate email2
P1.2 Outage resolvePOST report + PUT :id/resolve (status, cause, duration, notes append) + 400 dupla resolve + 404 inexistente4
P1.3 BillsGET /bills + GET :id/pdf (pdfUrl ou metadata) + 503 boleto-pdf BOLETO_PDF_NOT_IMPLEMENTED + 400 INVALID_AMOUNT + 400 MISSING_METHOD5
P1.4 ConfigGET defaults materializados + PATCH /features deep-merge preservando outros2
P1.5 Service-requestsPOST com protocolo UTL-YYYYMMDD-NNNNNN + GET list/by-id + PUT workflow (open→in_progress→resolved + transição inválida com allowed[])6
P2.1 Marketplace deferred14 stubs (reserve/buy/sellers/reviews/disputes/subscriptions/price-history/aneel-report/admin)14
P2.2 Portal-gap deferred18 representativos (solar/shop/campaigns/ai/agencies/field-crews/backoffice/admin/reports/analytics)18
P2.3 SAP V2 novosservice-cloud-v2 5 + sales-cloud-v2 1 + aliases /service-v2 + /sales-v28
P3.1 No-auth → 4017 endpoints autenticados retornam 401 sem JWT7
P3.2 Garbage Bearer → 401JWT signing enforced1
P3.3 Foreign tenant headerRLS scoping correto em /customers (200 + array vazio)1
P3.4 Public sem auth/config retorna 200 (público by design)1
Outage report cleanupPOST report → 201 + view shape (id, status, cause)1

70 pass / 0 fail / 70 total.

Suite 2 — isu-cutover-headed-2026-05-18.spec.ts (16/16 PASS, 1.3min headed)

Browser visível (Chromium) + vídeo gravado (test-results/*.webm) + screenshots por step. Testou os 2 fluxos críticos:

Admin backoffice (admin@xenergy.com.br / XEnergy@Admin):

StepURLAPI calls Java4xx/5xx
01-after-login/pt-BR/products0 (seletor)0
02-dashboard/pt-BR/utilities/backoffice/dashboard20
03-operations/pt-BR/utilities/backoffice/operations40
04-requests/pt-BR/utilities/backoffice/requests30
05-customers/pt-BR/utilities/backoffice/customers30
06-billing/pt-BR/utilities/backoffice/billing30
07-marketplace/pt-BR/utilities/backoffice/marketplace60
08-reports/pt-BR/utilities/backoffice/reports20
09-analytics/pt-BR/utilities/backoffice/analytics20
10-marketing/pt-BR/utilities/backoffice/marketing30

Portal customer (maria.residencial@xenergy.com.br / XEnergy@2026):

StepURLAPI calls Java4xx/5xx
01-after-login/pt-BR/utilities/portal10
02-dashboard/pt-BR/utilities/portal/dashboard100
03-bills/pt-BR/utilities/portal/bills20
04-consumption/pt-BR/utilities/portal/consumption20
05-tickets/pt-BR/utilities/portal/tickets20
06-account/pt-BR/utilities/portal/account20

Conclusão visual: todas as 15 páginas navegadas firam Java APIs (média 2-10 calls por página, zero 4xx/5xx). Vídeo + screenshots ficam disponíveis em apps/web/test-results/isu-cutover-headed-2026-05-18*/.

Suite 3 + 4 — Regression checks (validação que existia não quebrou)

SuiteResultadoNotas
isu-java-only-2026-05-1614/14 PASS (17 asserts) em 7.1sRe-rodado pós-deploy v2000-fix1; mantido
isu-coverage-audit-2026-05-17673/0/112 (785 total) em 9.4minRe-rodado pós-deploy; zero regressões
isu-deep-audit-2026-05-178/8 PASS (205 asserts) em 2.8minRe-rodado; UI reconcile counts OK (ui=529 cliente ≈ api=100 paginado)

⭐ Suite 5 — Validação consolidada pós-frentes complementares (77/77 PASS)

Após o deploy de v202605182600-mkt2 (cobrindo #15-#20 — backoffice reports + SLA wiring + RLS migration + CI + GraalVM native build + marketplace P2P), o spec consolidado roda 7 fases cobrindo gap-close + frentes complementares:

PhaseCountCobertura
P1 real-impl5register (bcrypt+JWT), outage workflow, bills validation, config defaults+PATCH, service-requests protocolo+SLA+VALID_TRANSITIONS
P2 deferred stubs3marketplace remaining + portal-gap remaining + SAP V2 (contract {success, deferredReason} paridade NestJS)
P3 negative security4no-auth 401, garbage Bearer 401, foreign tenant RLS scoping /customers + /service-requests (P3.3 expandida pós-aplicação migration #15), public sem auth
P4.1 reports real7/reports/{aneel,billing,consumption,customers,operations} + /backoffice/analytics + /analytics/insights (#17)
P4.2 reports export1/reports/export/:type + 400 invalid type
P4.3 SLA wiring1PATCH /config/features slaHours.outage_emergencial=6 → POST /service-requests retorna slaDeadline = now+6h (Δ=6h vs hardcoded 24h) (#16)
P4.4 marketplace P2P1sellers/:id + subscriptions/disputes lists + price-history + validation 400/500 (#20)

77 pass / 0 fail / 77 total — valida que frentes #15-#20 estão funcionais end-to-end contra https://isu-api.dev.jedin.io/api/v1/ com imagem v202605182600-mkt2. P3.3 ganhou check explícito de /service-requests com foreign tenant header → 200 + array vazio (antes era marcado como "bug pré-existente RLS"; hoje resolvido pela migration #15).

Score consolidado pós-cutover

PassFailWarn
Total acumulado de validação7810112
Suite 5 — frentes complementares (P1-P4.4)770
Java endpoints reais cobertos100% das 13 surfaces real-impl + 7 reports + 8 P2P
Deferred stubs com contract estável70/70 endpoints (Phase 5.E.32 ainda pendente: Pix escrow + ANEEL aggregator + admin views)
Negative security cobrindo endpoints novos11/11 cenários
Headed visual com vídeo gravado16/16 steps

Como reproduzir localmente

# Coverage audit completo (10min):
cd "C:/Users/lcruz/Leandro Cruz/JedIN"
pnpm --filter web exec playwright test e2e/isu-coverage-audit-2026-05-17.spec.ts --workers=1

# Java cutover audit (30s):
pnpm --filter web exec playwright test e2e/isu-java-only-2026-05-16.spec.ts --workers=1

# Cutover validation 2026-05-18 (real + deferred + security, 6s):
pnpm --filter web exec playwright test e2e/isu-cutover-validation-2026-05-18.spec.ts --workers=1

# Headed visual walkthrough com vídeo (1.5min):
pnpm --filter web exec playwright test e2e/isu-cutover-headed-2026-05-18.spec.ts --headed --workers=1

Os outputs vão para apps/web/e2e/__*__/findings.json + report.md + screenshots/; vídeos do headed em apps/web/test-results/*.webm.


Próximos Passos

No roadmap próximo (Fase 5.E iteração 2 + 3):

  • Migrar mais prefixos para Java: outages, tickets, notifications, installations, services.
  • Self-serve de SAML federation no /admin/integrations (Fase 7.3 do programa IAM).
  • WhatsApp Business API nativa (hoje é via webhook genérico).
  • Mobile app PWA com offline-first para field crew.
  • Marketplace solar fase 2: integração com bandeiras (Visa/Mastercard) para checkout sem Pix.

Contato e suporte:


Esta wiki é gerada automaticamente a partir do código + audit em produção.

Última atualização: 2026-05-19 — cutover gap-close completo + 6 frentes pós-cutover (backoffice reports real, SLA wiring, RLS migration aplicada em 2026-05-19, CI workflow, GraalVM native build, marketplace P2P real). 19 prefixos roteados pra Java; ~78 endpoints novos do gap-close + 15 endpoints reais (7 reports + 8 P2P) das frentes complementares. 5 suites de validação consolidam 781/0 PASS (Suites 1-4) + 77/0 PASS (Suite 5 com P1-P4.4 cobrindo as frentes #15-#20; P3.3 expandida pós-fix RLS pra checar /service-requests foreign-tenant → 200 vazio). Commits: 5591bbc5..b3b69e37 (11 commits) + spec tightening. Imagem live em produção dev: jedin-isu-api-java:v202605182600-mkt2 (JVM). Imagem GraalVM native: v202605182400-native (build OK, deploy paralelo pendente). Migration RLS 20260518000001_isu_seg_service_requests_rls aplicada via jedin_admin + registrada em _prisma_migrations.

Related Articles

energyhubutilitiescemig

JedIN EnergyHub — Documentação Completa do Produto

Referência técnica completa do JedIN EnergyHub: arquitetura, 17 modelos Prisma, 20+ endpoints REST, 29 telas (portal/backoffice/admin), 5 integrações SAP, 4 flows de integração C4C, MCP com 20 tools, e modelo white-label multi-tenant para concessionárias de energia.

2026-04-0930 min de leitura
Ler mais
jedinutilitiesisu

JedIN Utilities (IS-U) — Stack Tecnológica Completa

Deep dive no produto JedIN Utilities: white-label SAP IS-U para distribuidoras de energia com 17 submódulos NestJS, integração SAP S/4HANA + C4C V1/V2 + BTP, marketplace P2P solar, DEC/FEC ANEEL e 28 tools MCP.

2026-04-227 min de leitura
Ler mais
Fale conosco pelo WhatsApp