ChegaMed é um sistema de controle e distribuição de medicamentos públicos para prefeituras e secretarias municipais de saúde. O objetivo do produto é garantir que pacientes cadastrados em uma unidade de saúde recebam seus medicamentos de uso contínuo ou de curto prazo no tempo correto, eliminando falhas de comunicação, retrabalho manual e falta de rastreabilidade entre a receita médica emitida, o estoque disponível e a efetiva entrega ao paciente.
A plataforma é composta por uma API central que concentra toda a regra de negócio e persistência, um aplicativo desktop de back-office para gestores e atendentes, um aplicativo mobile voltado para pacientes e entregadores, um site institucional/público para cadastro e páginas legais, e um motor de automação (n8n) que dá suporte ao assistente virtual do produto.
flowchart LR
subgraph Clientes
DESK[Desktop\nAngular + Tauri]
MOB[Mobile\nExpo / React Native]
WEB[Web\nNext.js]
end
subgraph Backend
API[API\nSpring Boot]
N8N[n8n\nAutomação / Assistente]
DB[(PostgreSQL)]
end
DESK -->|REST / JWT| API
MOB -->|REST / JWT| API
WEB -->|REST| API
API --> DB
API -->|webhook| N8N
N8N -->|resposta do assistente| API
Os três clientes (desktop, mobile e web) conversam exclusivamente com a API, que é a única aplicação com acesso ao banco de dados. A API delega o processamento de linguagem natural do assistente virtual para o n8n via webhook, e utiliza serviços externos (AWS S3 para armazenamento de imagens de receitas, Gemini para extração de dados das receitas fotografadas, Resend para e-mails transacionais e Expo Push para notificações).
Principais tecnologias
Backend: Java 21, Spring Boot 4, Spring Data JPA/Hibernate, Spring Security, Flyway, JWT (jjwt), springdoc-openapi (Swagger), AWS SDK (S3), Google API Client, Nimbus JOSE+JWT (Sign in with Apple)
O domínio central do ChegaMed gira em torno da receita médica (Prescription), que agrupa um ou mais itens de medicamento (PrescriptionItem). Cada item, quando dispensado, gera um registro de entrega (Delivery).
erDiagram
COMPANY ||--o{ PATIENT : possui
COMPANY ||--o{ MEDICINE : cataloga
COMPANY ||--o{ DELIVERY : escopa
COMPANY }o--o{ USER : emprega
PATIENT ||--o| USER : "conta no app (opcional)"
PATIENT ||--o{ PRESCRIPTION : possui
PATIENT ||--o{ DELIVERY : recebe
PRESCRIPTION ||--|{ PRESCRIPTION_ITEM : contém
PRESCRIPTION_ITEM }o--|| MEDICINE : referencia
PRESCRIPTION_ITEM ||--o| DELIVERY : "gera (1:1)"
USER ||--o{ DELIVERY : entrega
deliveryDate, nextAvailableDate, deliveryQuantity; único por prescriptionItem
Relacionamentos
Company1‑NPatient, Medicine, Delivery e N‑NUser (uma empresa tem vários usuários e um usuário pode atuar em várias empresas)
Patient1‑NPrescription e Delivery, e opcionalmente 1‑1 com um User (conta de acesso ao app)
Prescription1‑NPrescriptionItem (exclusão em cascata)
PrescriptionItemN‑1Medicine e 1‑1 (opcional) Delivery
DeliveryN‑1User (entregador, opcional)
Status da receita e do item (PrescriptionStatus)
PENDING → OUT_FOR_DELIVERY → DELIVERED (ou PARTIAL_DELIVERED), com possibilidade de CANCELED enquanto pendente ou em rota de entrega.
Despachável:PENDING
Entregável:PENDING, OUT_FOR_DELIVERY
Cancelável:PENDING, OUT_FOR_DELIVERY
Concluída:DELIVERED, PARTIAL_DELIVERED
O status agregado da receita é recalculado automaticamente a partir do status de seus itens a cada mudança (dispensação parcial, cancelamento de item, etc.).
Regras de negócio
Uma receita sempre nasce com status PENDING, assim como todos os seus itens (receivedQuantity e deliveredQuantity começam em 0).
Cada item deve referenciar um medicamento existente no catálogo (medicineId) ou enviar os dados para criação de um novo medicamento — nunca os dois ausentes (MEDICINE_REQUIRED).
O medicamento referenciado precisa pertencer à mesma empresa do paciente (MEDICINE_COMPANY_MISMATCH), garantindo isolamento entre prefeituras (multi-tenant).
Período de tratamento: se já existir uma entrega anterior do mesmo medicamento para o paciente com nextAvailableDate no futuro, uma nova solicitação é rejeitada (MEDICINE_STILL_IN_TREATMENT_PERIOD) — evita retirada antecipada de medicamentos de uso contínuo.
Apenas ADMIN, ou MANAGER/ASSISTANT vinculados à empresa do paciente, podem criar/atualizar receitas; exclusão é restrita a MANAGER/ADMIN.
O papel DELIVERER não tem acesso à listagem de receitas — apenas às entregas atribuídas a ele.
Um item só pode ser cancelado se estiver em PENDING ou OUT_FOR_DELIVERY (PRESCRIPTION_ITEM_NOT_CANCELABLE); o cancelamento recalcula o status agregado da receita e dispara uma notificação.
A data de emissão da receita (issueDate) não pode estar no futuro.
Caso de uso: cadastro de receitas
Fluxo completo de cadastro de uma receita médica, desde a captura até a entrega ao paciente:
sequenceDiagram
actor P as Paciente
actor A as Atendente/Gestor
participant M as Mobile / Desktop
participant S3 as Armazenamento (S3)
participant AI as Extração por IA
participant API as API
participant DB as PostgreSQL
P->>M: Fotografa a receita médica
M->>S3: Upload da imagem (URL pré-assinada)
M->>API: Solicita extração automática dos dados
API->>AI: Envia imagem para leitura
AI-->>API: Medicamentos, dosagens e quantidades sugeridas
API-->>M: Retorna rascunho da receita
A->>M: Revisa/completa dados e confirma
M->>API: POST /prescriptions
API->>API: Valida DTO e regras de negócio
API->>DB: Persiste Prescription + PrescriptionItem(s) (status PENDING)
API-->>M: 201 Created
API--)P: Notificação (push/e-mail) de receita cadastrada
O paciente fotografa a receita pelo aplicativo mobile (ou anexa a imagem pelo desktop), que é enviada ao S3 através de uma URL pré-assinada gerada pela API.
Opcionalmente, a API envia a imagem para o módulo de IA (Gemini), que extrai automaticamente medicamentos, dosagens e quantidades, retornando um rascunho para revisão.
Um atendente/gestor confirma os dados e envia a requisição de criação:
POST /prescriptions
{
"patientId": "8f14e45f-ceea-467a-8083-8a...",
"issueDate": "2026-08-10",
"imageUrls": ["https://cdn.chegamed.com.br/prescriptions/8f14e45f.png"],
"items": [
{
"medicineId": "3b241101-e2bb-4255-8caf-4136c566a...",
"dosage": "1 comprimido a cada 8 horas",
"prescribedQuantity": 30,
"unityType": "TABLET",
"treatmentType": "CONTINUOUS",
"treatmentDays": 30,
"observations": "Tomar após as refeições"
},
{
"medicine": { "name": "Dipirona 500mg" },
"dosage": "1 comprimido se dor",
"prescribedQuantity": 10,
"unityType": "TABLET",
"treatmentType": "SHORT_TERM",
"treatmentDays": 5
}
]
}
A API valida o payload (campos obrigatórios, datas, quantidades positivas), resolve cada medicamento (existente ou novo, sempre dentro da empresa do paciente) e verifica se o paciente está apto a receber cada medicamento (fora do período de tratamento vigente).
A receita é persistida com status PENDING, junto de seus itens, e uma notificação é disparada para os interessados (push, in-app e/ou e-mail).
O item segue o fluxo operacional: PENDING → OUT_FOR_DELIVERY (despacho) → DELIVERED/PARTIAL_DELIVERED (gera um registro em Delivery, com data da próxima retirada calculada para tratamentos contínuos).
Enquanto não finalizada, a receita (ou item) pode ser cancelada por um gestor/administrador.
Executando o ambiente completo
Os serviços de backend (API, banco de dados e automação) podem ser levantados via Docker Compose a partir da raiz do repositório:
cp .env.sample .env
# preencha as variáveis de ambiente necessárias
docker compose up -d
Isso sobe:
postgres — banco de dados PostgreSQL 18, na porta 5432
api — API Spring Boot, na porta 8080
n8n — motor de automação que atende os fluxos do assistente virtual, na porta 5678
Para desenvolvimento local, cada cliente (desktop, mobile, web) pode ser executado individualmente apontando para a API local — consulte o README de cada aplicação.
Variáveis de ambiente
O arquivo .env.sample documenta todas as variáveis usadas pelo ambiente. Principais grupos: