2.JPA Relaciones
1. Cita
Crear un sistema de gestión de citas médicas donde se permita registrar citas entre pacientes y médicos.
Cada cita debe contener fecha, motivo de la consulta y relación con un paciente y un médico.
El sistema debe implementar arquitectura Controller - Service - Repository usando Spring Boot y JPA.
package es.pulsoft.spring_hospital.controller;
import es.pulsoft.spring_hospital.model.Cita;
import es.pulsoft.spring_hospital.model.enums.EstadoCita;
import es.pulsoft.spring_hospital.service.CitaService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.util.List;
// Controlador REST encargado de gestionar las operaciones relacionadas con Cita
@RestController
@RequestMapping("/citas")
public class CitaController {
// Servicio que contiene la lógica de negocio de citas
private final CitaService citaService;
// Inyección del servicio de citas
public CitaController(CitaService citaService) {
this.citaService = citaService;
}
// Obtiene listado de citas con filtros opcionales
// Permite filtrar por motivo, estado y rango de fechas
@GetMapping
public List<Cita> listarCitas(
@RequestParam(required = false) String motivo,
@RequestParam(required = false) EstadoCita estado,
@RequestParam(required = false) LocalDate inicio,
@RequestParam(required = false) LocalDate fin) {
return citaService.listarCitas(motivo, estado, inicio, fin);
}
// Obtiene una cita por su identificador
@GetMapping("/{id}")
public Cita buscarPorId(@PathVariable Long id) {
return citaService.buscarPorId(id);
}
// Crea una nueva cita en el sistema
@PostMapping
public Cita crearCita(@RequestBody Cita cita) {
return citaService.crearCita(cita);
}
// Actualiza completamente una cita existente
// Reemplaza todos los campos de la entidad
@PutMapping("/{id}")
public ResponseEntity<Cita> actualizarCita(
@PathVariable Long id,
@RequestBody Cita cita) {
return citaService.actualizarCita(id, cita);
}
// Elimina una cita por su identificador
@DeleteMapping("/{id}")
public ResponseEntity<Void> borrarCita(@PathVariable Long id) {
return citaService.borrarCita(id);
}
// Actualización parcial de una cita
// Solo modifica los campos enviados en la petición
@PatchMapping("/{id}")
public ResponseEntity<Cita> actualizacionParcial(
@PathVariable Long id,
@RequestBody Cita cita) {
return citaService.actualizacionParcial(id, cita);
}
}
package es.pulsoft.spring_hospital.model;
import es.pulsoft.spring_hospital.model.enums.EstadoCita;
import jakarta.persistence.*;
import java.time.LocalDate;
// Entidad JPA que representa una cita médica en el sistema
@Entity
@Table(name="cita")
public class Cita {
// Identificador único de la cita en base de datos
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Fecha en la que se realiza la cita
private LocalDate fecha;
// Motivo o descripción de la cita médica
private String motivo;
// Estado actual de la cita (PENDIENTE, CONFIRMADA, etc.)
@Enumerated(EnumType.STRING)
private EstadoCita estado;
// Relación ManyToOne: muchas citas pueden pertenecer a un mismo paciente
@ManyToOne
@JoinColumn(name="paciente_id")
private Paciente paciente;
// Relación ManyToOne: muchas citas pueden estar asignadas al mismo médico
@ManyToOne
@JoinColumn(name="medico_id")
private Medico medico;
// Constructor vacío requerido por JPA
public Cita(){
}
// Constructor para crear una cita con datos básicos
// La fecha se asigna automáticamente a la fecha actual
public Cita(String motivo, EstadoCita estado, Paciente paciente, Medico medico) {
this.fecha = LocalDate.now();
this.motivo = motivo;
this.estado = estado;
this.paciente = paciente;
this.medico = medico;
}
// Obtiene el identificador de la cita
public Long getId() {
return id;
}
// Obtiene la fecha de la cita
public LocalDate getFecha() {
return fecha;
}
// Establece la fecha de la cita
public void setFecha(LocalDate fecha) {
this.fecha = fecha;
}
// Obtiene el motivo de la cita
public String getMotivo() {
return motivo;
}
// Establece el motivo de la cita
public void setMotivo(String motivo) {
this.motivo = motivo;
}
// Obtiene el estado actual de la cita
public EstadoCita getEstado() {
return estado;
}
// Establece el estado de la cita
public void setEstado(EstadoCita estado) {
this.estado = estado;
}
// Obtiene el paciente asociado a la cita
public Paciente getPaciente() {
return paciente;
}
// Asigna un paciente a la cita
public void setPaciente(Paciente paciente) {
this.paciente = paciente;
}
// Obtiene el médico asignado a la cita
public Medico getMedico() {
return medico;
}
// Asigna un médico a la cita
public void setMedico(Medico medico) {
this.medico = medico;
}
}
package es.pulsoft.spring_hospital.repository;
import es.pulsoft.spring_hospital.model.Cita;
import es.pulsoft.spring_hospital.model.enums.EstadoCita;
import org.springframework.data.jpa.repository.JpaRepository;
import java.time.LocalDate;
import java.util.List;
// Repositorio de acceso a datos para la entidad Cita
// Extiende JpaRepository para operaciones CRUD automáticas
public interface CitaRepository extends JpaRepository<Cita, Long> {
// Busca citas cuyo motivo contenga el texto indicado (ignore case)
List<Cita> findByMotivoContainingIgnoreCase(String motivo);
// Filtra citas por estado (PENDIENTE, CONFIRMADA, etc.)
List<Cita> findByEstado(EstadoCita estado);
// Filtra por motivo y estado a la vez
List<Cita> findByMotivoContainingIgnoreCaseAndEstado(String motivo, EstadoCita estado);
// Obtiene citas dentro de un rango de fechas
List<Cita> findByFechaBetween(LocalDate inicio, LocalDate fin);
// Busca citas por fecha exacta
List<Cita> findByFecha(LocalDate inicio);
}
package es.pulsoft.spring_hospital.service;
import es.pulsoft.spring_hospital.model.Cita;
import es.pulsoft.spring_hospital.model.Medico;
import es.pulsoft.spring_hospital.model.Paciente;
import es.pulsoft.spring_hospital.model.enums.EstadoCita;
import es.pulsoft.spring_hospital.repository.CitaRepository;
import es.pulsoft.spring_hospital.repository.MedicoRepository;
import es.pulsoft.spring_hospital.repository.PacienteRepository;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
import java.time.LocalDate;
import java.util.List;
@Service
public class CitaService {
// Repositorio de citas
private final CitaRepository citaRepository;
// Repositorio de pacientes
private final PacienteRepository pacienteRepository;
// Repositorio de médicos
private final MedicoRepository medicoRepository;
// Constructor con inyección de dependencias
public CitaService(CitaRepository citaRepository,
PacienteRepository pacienteRepository,
MedicoRepository medicoRepository) {
this.citaRepository = citaRepository;
this.pacienteRepository = pacienteRepository;
this.medicoRepository = medicoRepository;
}
// Listado de citas con filtros opcionales
public List<Cita> listarCitas(String motivo,
EstadoCita estado,
LocalDate inicio,
LocalDate fin) {
if (inicio != null && fin != null) {
return citaRepository.findByFechaBetween(inicio, fin);
}
if (motivo != null && estado != null) {
return citaRepository.findByMotivoContainingIgnoreCaseAndEstado(motivo, estado);
}
if (motivo != null) {
return citaRepository.findByMotivoContainingIgnoreCase(motivo);
}
if (estado != null) {
return citaRepository.findByEstado(estado);
}
if (inicio != null) {
return citaRepository.findByFecha(inicio);
}
return citaRepository.findAll();
}
// Buscar cita por ID
public Cita buscarPorId(Long id) {
return citaRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(
HttpStatus.NOT_FOUND,
"Cita no encontrada"));
}
// Crear cita
public Cita crearCita(Cita cita) {
validaciones(cita);
return citaRepository.save(cita);
}
// Actualizar cita completa
public ResponseEntity<Cita> actualizarCita(Long id, Cita cita) {
Cita c = buscarPorId(id);
validaciones(cita);
c.setFecha(cita.getFecha());
c.setEstado(cita.getEstado());
c.setMotivo(cita.getMotivo());
c.setMedico(cita.getMedico());
c.setPaciente(cita.getPaciente());
return ResponseEntity.ok(citaRepository.save(c));
}
// Eliminar cita
public ResponseEntity<Void> borrarCita(Long id) {
Cita c = buscarPorId(id);
citaRepository.delete(c);
return ResponseEntity.noContent().build();
}
// Validaciones de negocio
private void validaciones(Cita cita) {
if (cita.getMotivo() == null) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"El motivo de la cita no puede quedar sin rellenar.");
}
if (cita.getEstado() == null) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"El estado de la cita no puede quedar sin rellenar.");
}
if (cita.getFecha() == null) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"La fecha de la cita no puede quedar sin rellenar.");
}
Paciente p = pacienteRepository.findById(cita.getPaciente().getId())
.orElseThrow(() -> new ResponseStatusException(
HttpStatus.NOT_FOUND,
"Paciente no encontrado"));
Medico m = medicoRepository.findById(cita.getMedico().getId())
.orElseThrow(() -> new ResponseStatusException(
HttpStatus.NOT_FOUND,
"Médico no encontrado"));
cita.setPaciente(p);
cita.setMedico(m);
}
// Actualización parcial
public ResponseEntity<Cita> actualizacionParcial(Long id, Cita cita) {
Cita c = citaRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(
HttpStatus.NOT_FOUND,
"La cita no existe"));
if (cita.getFecha() != null) {
if (cita.getFecha().isBefore(LocalDate.now())) {
throw new ResponseStatusException(
HttpStatus.BAD_REQUEST,
"La fecha debe ser hoy o futura");
}
c.setFecha(cita.getFecha());
}
if (cita.getMotivo() != null) {
c.setMotivo(cita.getMotivo());
}
if (cita.getEstado() != null) {
c.setEstado(cita.getEstado());
}
if (cita.getPaciente() != null && cita.getPaciente().getId() != null) {
Paciente p = pacienteRepository.findById(cita.getPaciente().getId())
.orElseThrow(() -> new ResponseStatusException(
HttpStatus.NOT_FOUND,
"Paciente no encontrado"));
c.setPaciente(p);
}
if (cita.getMedico() != null && cita.getMedico().getId() != null) {
Medico m = medicoRepository.findById(cita.getMedico().getId())
.orElseThrow(() -> new ResponseStatusException(
HttpStatus.NOT_FOUND,
"Médico no encontrado"));
c.setMedico(m);
}
return ResponseEntity.ok(citaRepository.save(c));
}
}
2. Departamento
Desarrollar una API REST para la gestión de departamentos de un hospital.
Cada departamento tendrá un nombre, ubicación y relación con los médicos que pertenecen a él.
Se debe permitir el CRUD completo utilizando Spring Boot con arquitectura en capas.
package es.pulsoft.spring_hospital.controller;
import es.pulsoft.spring_hospital.model.Departamento;
import es.pulsoft.spring_hospital.service.DepartamentoService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/departamentos")
public class DepartamentoController {
// Servicio de departamentos
private final DepartamentoService departamentoService;
// Constructor con inyección de dependencias
public DepartamentoController(DepartamentoService departamentoService) {
this.departamentoService = departamentoService;
}
// Lista departamentos con filtro opcional por nombre
@GetMapping
public List<Departamento> filtrar(
@RequestParam(required = false) String nombre) {
return departamentoService.filtrar(nombre);
}
// Busca departamento por ID
@GetMapping("/{id}")
public Departamento buscarPorId(@PathVariable Long id) {
return departamentoService.buscarPorId(id);
}
// Crea un nuevo departamento
@PostMapping
public Departamento crearDepartamento(@RequestBody Departamento departamento) {
return departamentoService.crearDepartamento(departamento);
}
// Actualiza completamente un departamento existente
@PutMapping("/{id}")
public ResponseEntity<Departamento> actualizarDepartamento(
@PathVariable Long id,
@RequestBody Departamento departamento) {
return departamentoService.actualizarDepartamento(id, departamento);
}
// Elimina un departamento por ID
@DeleteMapping("/{id}")
public ResponseEntity<Void> borrarDepartamento(@PathVariable Long id) {
return departamentoService.borrarDepartamento(id);
}
// Actualización parcial del departamento
@PatchMapping("/{id}")
public ResponseEntity<Departamento> actualizacionParcial(
@PathVariable Long id,
@RequestBody Departamento departamento) {
return departamentoService.actualizacionParcial(id, departamento);
}
}
package es.pulsoft.spring_hospital.model;
import jakarta.persistence.*;
import java.util.List;
// Entidad que representa un departamento dentro del hospital
@Entity
@Table(name = "departamento")
public class Departamento {
// Identificador único del departamento
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Nombre del departamento (ej: Cardiología, Traumatología, etc.)
private String nombre;
// Relación OneToMany: un departamento puede tener muchos médicos
// mappedBy indica que la relación está gestionada desde la entidad Medico
@OneToMany(mappedBy = "departamento")
private List<Medico> medicos;
// Constructor vacío requerido por JPA
public Departamento() {
}
// Constructor para crear un departamento con sus datos básicos
public Departamento(String nombre, List<Medico> medicos) {
this.nombre = nombre;
this.medicos = medicos;
}
// Obtiene el identificador del departamento
public Long getId() {
return id;
}
// Obtiene el nombre del departamento
public String getNombre() {
return nombre;
}
// Establece el nombre del departamento
public void setNombre(String nombre) {
this.nombre = nombre;
}
// Obtiene la lista de médicos del departamento
public List<Medico> getMedicos() {
return medicos;
}
// Asigna la lista de médicos al departamento
public void setMedicos(List<Medico> medicos) {
this.medicos = medicos;
}
}
package es.pulsoft.spring_hospital.repository;
import es.pulsoft.spring_hospital.model.Departamento;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
// Repositorio de acceso a datos para la entidad Departamento
// Proporciona operaciones CRUD básicas mediante JpaRepository
public interface DepartamentoRepository extends JpaRepository<Departamento, Long> {
// Busca departamentos cuyo nombre contenga el texto indicado (ignore case)
List<Departamento> findByNombreContainingIgnoreCase(String nombre);
}
package es.pulsoft.spring_hospital.service;
import es.pulsoft.spring_hospital.model.Departamento;
import es.pulsoft.spring_hospital.repository.DepartamentoRepository;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
import java.util.List;
@Service
public class DepartamentoService {
private final DepartamentoRepository departamentoRepository;
// Inyección del repositorio de departamentos
public DepartamentoService(DepartamentoRepository departamentoRepository) {
this.departamentoRepository = departamentoRepository;
}
// Filtra departamentos por nombre (búsqueda parcial, case insensitive)
// Si no se proporciona nombre, devuelve todos los registros
public List<Departamento> filtrar(String nombre) {
if (nombre == null) {
return departamentoRepository.findAll();
} else {
return departamentoRepository.findByNombreContainingIgnoreCase(nombre);
}
}
// Busca un departamento por su identificador
// Lanza excepción 404 si no existe
public Departamento buscarPorId(Long id) {
return departamentoRepository.findById(id).orElseThrow(() ->
new ResponseStatusException(HttpStatus.NOT_FOUND,
"Departamento no encontrado"));
}
// Crea un nuevo departamento aplicando validaciones de negocio
public Departamento crearDepartamento(Departamento departamento) {
validaciones(departamento);
return departamentoRepository.save(departamento);
}
// Actualiza completamente un departamento existente
public ResponseEntity<Departamento> actualizarDepartamento(Long id, Departamento departamento) {
Departamento d = buscarPorId(id);
d.setNombre(departamento.getNombre());
return ResponseEntity.ok(departamentoRepository.save(d));
}
// Elimina un departamento por su identificador
public ResponseEntity<Void> borrarDepartamento(Long id) {
Departamento d = buscarPorId(id);
departamentoRepository.delete(d);
return ResponseEntity.noContent().build();
}
// Validaciones de negocio para creación de departamentos
// Verifica que el nombre no sea nulo ni vacío
private void validaciones(Departamento departamento) {
if (departamento.getNombre() == null || departamento.getNombre().trim().isEmpty()) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"El nombre del departamento no puede quedar sin rellenar.");
}
}
// Actualización parcial de un departamento
// Solo modifica los campos enviados en la petición
public ResponseEntity<Departamento> actualizacionParcial(Long id, Departamento departamento) {
Departamento d = departamentoRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND,
"Departamento no encontrado"));
if (departamento.getNombre() != null) {
d.setNombre(departamento.getNombre());
}
return ResponseEntity.ok(departamentoRepository.save(d));
}
}
3. HistoriaClinica
Implementar un sistema de historias clínicas asociadas a pacientes.
Cada historia clínica debe contener diagnósticos, observaciones y estar vinculada a un único paciente.
Se debe usar relación uno a uno con JPA y estructura Controller - Service - Repository.
package es.pulsoft.spring_hospital.controller;
import es.pulsoft.spring_hospital.model.HistoriaClinica;
import es.pulsoft.spring_hospital.service.HistoriaClinicaService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.util.List;
@RestController
@RequestMapping("/historias")
public class HistoriaClinicaController {
// Servicio de historias clínicas
private final HistoriaClinicaService historiaClinicaService;
// Inyección del servicio
public HistoriaClinicaController(HistoriaClinicaService historiaClinicaService) {
this.historiaClinicaService = historiaClinicaService;
}
// Listado con filtros opcionales
@GetMapping
public List<HistoriaClinica> filtrar(
@RequestParam(required = false) String descripcion,
@RequestParam(required = false) LocalDate inicio,
@RequestParam(required = false) LocalDate fin) {
return historiaClinicaService.filtrar(descripcion, inicio, fin);
}
// Buscar por ID
@GetMapping("/{id}")
public HistoriaClinica buscarPorId(@PathVariable Long id) {
return historiaClinicaService.buscarPorId(id);
}
// Crear historia clínica
@PostMapping
public HistoriaClinica crearHistoria(@RequestBody HistoriaClinica historiaClinica) {
return historiaClinicaService.crearHistoria(historiaClinica);
}
// Actualización completa
@PutMapping("/{id}")
public ResponseEntity<HistoriaClinica> actualizarHistoria(
@PathVariable Long id,
@RequestBody HistoriaClinica historiaClinica) {
return historiaClinicaService.actualizarHistoria(id, historiaClinica);
}
// Eliminar historia clínica
@DeleteMapping("/{id}")
public ResponseEntity<Void> borrarHistoria(@PathVariable Long id) {
return historiaClinicaService.borrarHistoria(id);
}
// Actualización parcial
@PatchMapping("/{id}")
public ResponseEntity<HistoriaClinica> actualizacionParcial(
@PathVariable Long id,
@RequestBody HistoriaClinica historiaClinica) {
return historiaClinicaService.actualizacionParcial(id, historiaClinica);
}
}
package es.pulsoft.spring_hospital.model;
import jakarta.persistence.*;
import java.time.LocalDate;
// Entidad que representa la historia clínica de un paciente
@Entity
@Table(name="historia_clinica")
public class HistoriaClinica {
// Identificador único de la historia clínica
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Descripción médica o resumen de la historia clínica del paciente
private String descripcion;
// Fecha en la que se registra la historia clínica
private LocalDate fecha;
// Relación OneToOne: cada paciente tiene una única historia clínica
// La clave foránea se almacena en esta tabla (paciente_id)
@OneToOne
@JoinColumn(name="paciente_id", unique = true)
private Paciente paciente;
// Constructor vacío requerido por JPA
public HistoriaClinica(){
}
// Constructor para crear una historia clínica con datos iniciales
public HistoriaClinica(String descripcion, LocalDate fecha, Paciente paciente) {
this.descripcion = descripcion;
this.fecha = fecha;
this.paciente = paciente;
}
// Obtiene el identificador de la historia clínica
public Long getId() {
return id;
}
// Obtiene la descripción de la historia clínica
public String getDescripcion() {
return descripcion;
}
// Establece la descripción de la historia clínica
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
// Obtiene la fecha de registro de la historia clínica
public LocalDate getFecha() {
return fecha;
}
// Establece la fecha de la historia clínica
public void setFecha(LocalDate fecha) {
this.fecha = fecha;
}
// Obtiene el paciente asociado a la historia clínica
public Paciente getPaciente() {
return paciente;
}
// Asigna un paciente a la historia clínica
public void setPaciente(Paciente paciente) {
this.paciente = paciente;
}
}
package es.pulsoft.spring_hospital.repository;
import es.pulsoft.spring_hospital.model.HistoriaClinica;
import org.springframework.data.jpa.repository.JpaRepository;
import java.time.LocalDate;
import java.util.List;
// Repositorio de acceso a datos para la entidad HistoriaClinica
// Extiende JpaRepository para operaciones CRUD automáticas
public interface HistoriaClinicaRepository extends JpaRepository<HistoriaClinica, Long> {
// Obtiene historias clínicas dentro de un rango de fechas
List<HistoriaClinica> findByFechaBetween(LocalDate inicio, LocalDate fin);
// Busca historias clínicas por fecha exacta
List<HistoriaClinica> findByFecha(LocalDate inicio);
// Busca historias clínicas por descripción (búsqueda parcial, ignore case)
List<HistoriaClinica> findByDescripcionContainingIgnoreCase(String nombre);
}
package es.pulsoft.spring_hospital.service;
import es.pulsoft.spring_hospital.model.HistoriaClinica;
import es.pulsoft.spring_hospital.model.Paciente;
import es.pulsoft.spring_hospital.repository.HistoriaClinicaRepository;
import es.pulsoft.spring_hospital.repository.PacienteRepository;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
import java.time.LocalDate;
import java.util.List;
@Service
public class HistoriaClinicaService {
private final HistoriaClinicaRepository historiaClinicaRepository;
private final PacienteRepository pacienteRepository;
// Inyección de dependencias de repositorios
public HistoriaClinicaService(HistoriaClinicaRepository historiaClinicaRepository,
PacienteRepository pacienteRepository) {
this.historiaClinicaRepository = historiaClinicaRepository;
this.pacienteRepository = pacienteRepository;
}
// Filtra historias clínicas por descripción y rango de fechas
// - Si hay rango de fechas, se prioriza
// - Si solo hay fecha inicial, filtra por ese día
// - Si hay descripción, filtra por coincidencia parcial
// - Si no hay filtros, devuelve todas
public List<HistoriaClinica> filtrar(String descripcion,
LocalDate inicio,
LocalDate fin) {
if (inicio != null && fin != null) {
return historiaClinicaRepository.findByFechaBetween(inicio, fin);
}
if (inicio != null) {
return historiaClinicaRepository.findByFecha(inicio);
}
if (descripcion != null) {
return historiaClinicaRepository.findByDescripcionContainingIgnoreCase(descripcion);
}
return historiaClinicaRepository.findAll();
}
// Busca una historia clínica por ID
// Lanza excepción 404 si no existe
public HistoriaClinica buscarPorId(Long id) {
return historiaClinicaRepository.findById(id).orElseThrow(() ->
new ResponseStatusException(HttpStatus.NOT_FOUND,
"Historia clinicano encontrada"));
}
// Crea una nueva historia clínica con validaciones previas
public HistoriaClinica crearHistoria(HistoriaClinica historiaClinica) {
validaciones(historiaClinica);
return historiaClinicaRepository.save(historiaClinica);
}
// Actualiza completamente una historia clínica existente
public ResponseEntity<HistoriaClinica> actualizarHistoria(Long id,
HistoriaClinica historiaClinica) {
HistoriaClinica h = buscarPorId(id);
validaciones(historiaClinica);
h.setDescripcion(historiaClinica.getDescripcion());
h.setFecha(historiaClinica.getFecha());
Paciente p = pacienteRepository.findById(historiaClinica.getPaciente().getId())
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND,
"Paciente no encontrado"));
h.setPaciente(p);
return ResponseEntity.ok(historiaClinicaRepository.save(h));
}
// Elimina una historia clínica por ID
public ResponseEntity<Void> borrarHistoria(Long id) {
HistoriaClinica h = buscarPorId(id);
historiaClinicaRepository.delete(h);
return ResponseEntity.noContent().build();
}
// Validaciones de negocio para historia clínica
// - descripción obligatoria
// - fecha obligatoria
// - paciente obligatorio
private void validaciones(HistoriaClinica historiaClinica) {
if (historiaClinica.getDescripcion() == null ||
historiaClinica.getDescripcion().trim().isEmpty()) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"La descripcion del historial no puede quedar vacia");
}
if (historiaClinica.getFecha() == null) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"La fecha del historial no puede quedar sin asignar");
}
if (historiaClinica.getPaciente() == null) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"Una historia tiene que tener una paciente asignado.");
}
}
// Actualización parcial de historia clínica
// Solo modifica los campos enviados
public ResponseEntity<HistoriaClinica> actualizacionParcial(Long id,
HistoriaClinica historiaClinica) {
HistoriaClinica h = historiaClinicaRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND,
"Historia no encontrada"));
if (historiaClinica.getFecha() != null) {
h.setFecha(historiaClinica.getFecha());
}
if (historiaClinica.getDescripcion() != null) {
h.setDescripcion(historiaClinica.getDescripcion());
}
if (historiaClinica.getPaciente() != null) {
Paciente p = pacienteRepository.findById(historiaClinica.getPaciente().getId())
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND,
"Paciente no encontrado"));
h.setPaciente(historiaClinica.getPaciente());
}
return ResponseEntity.ok(historiaClinicaRepository.save(h));
}
}
4. Medico
Crear un módulo de gestión de médicos.
Cada médico tendrá nombre, especialidad y estará asociado a un departamento.
Además, podrá estar relacionado con múltiples citas de pacientes.
Implementar CRUD completo con Spring Boot.
package es.pulsoft.spring_hospital.controller;
import es.pulsoft.spring_hospital.model.Medico;
import es.pulsoft.spring_hospital.service.MedicoService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/medicos")
public class MedicoController {
// Servicio de médicos
private final MedicoService medicoService;
// Inyección del servicio
public MedicoController(MedicoService medicoService) {
this.medicoService = medicoService;
}
// Listado de médicos con filtros opcionales
@GetMapping
public List<Medico> listarMedicos(
@RequestParam(required = false) String nombre,
@RequestParam(required = false) String apellidos,
@RequestParam(required = false) String especialidad) {
return medicoService.filtrar(nombre, apellidos, especialidad);
}
// Buscar médico por ID
@GetMapping("/{id}")
public Medico buscarPorId(@PathVariable Long id) {
return medicoService.buscarPorId(id);
}
// Crear médico
@PostMapping
public Medico crearMedico(@RequestBody Medico medico) {
return medicoService.crearMedico(medico);
}
// Actualizar médico completo
@PutMapping("/{id}")
public ResponseEntity<Medico> actualizarMedico(
@PathVariable Long id,
@RequestBody Medico medico) {
return medicoService.actualizarMedico(id, medico);
}
// Eliminar médico
@DeleteMapping("/{id}")
public ResponseEntity<Void> borrarMedico(@PathVariable Long id) {
return medicoService.borrarMedico(id);
}
// Actualización parcial de médico
@PatchMapping("/{id}")
public ResponseEntity<Medico> actualizacionParcial(
@PathVariable Long id,
@RequestBody Medico medico) {
return medicoService.actualizacionParcial(id, medico);
}
}
package es.pulsoft.spring_hospital.model;
import jakarta.persistence.*;
import java.util.List;
// Entidad que representa a un médico dentro del sistema hospitalario
@Entity
@Table(name="medico")
public class Medico {
// Identificador único del médico
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Nombre del médico
private String nombre;
// Apellidos del médico
private String apellidos;
// Especialidad médica (ej: cardiología, pediatría, etc.)
private String especialidad;
// Relación ManyToOne: muchos médicos pueden pertenecer a un mismo departamento
@ManyToOne
@JoinColumn(name = "departamento_id")
private Departamento departamento;
// Relación OneToMany: un médico puede tener muchas citas
// mappedBy indica que la relación se gestiona desde la entidad Cita
@OneToMany(mappedBy = "medico")
private List<Cita> citas;
// Constructor vacío requerido por JPA
public Medico(){
}
// Constructor para crear un médico con sus datos básicos
public Medico(String nombre, String apellidos, String especialidad,
Departamento departamento, List<Cita> citas) {
this.nombre = nombre;
this.apellidos = apellidos;
this.especialidad = especialidad;
this.departamento = departamento;
this.citas = citas;
}
// Obtiene el identificador del médico
public Long getId() {
return id;
}
// Obtiene el nombre del médico
public String getNombre() {
return nombre;
}
// Establece el nombre del médico
public void setNombre(String nombre) {
this.nombre = nombre;
}
// Obtiene los apellidos del médico
public String getApellidos() {
return apellidos;
}
// Establece los apellidos del médico
public void setApellidos(String apellidos) {
this.apellidos = apellidos;
}
// Obtiene la especialidad del médico
public String getEspecialidad() {
return especialidad;
}
// Establece la especialidad del médico
public void setEspecialidad(String especialidad) {
this.especialidad = especialidad;
}
// Obtiene el departamento del médico
public Departamento getDepartamento() {
return departamento;
}
// Asigna un departamento al médico
public void setDepartamento(Departamento departamento) {
this.departamento = departamento;
}
// Obtiene la lista de citas del médico
public List<Cita> getCitas() {
return citas;
}
// Asigna la lista de citas al médico
public void setCitas(List<Cita> citas) {
this.citas = citas;
}
}
package es.pulsoft.spring_hospital.repository;
import es.pulsoft.spring_hospital.model.Medico;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
// Repositorio de acceso a datos para la entidad Medico
// Extiende JpaRepository para operaciones CRUD automáticas
public interface MedicoRepository extends JpaRepository<Medico, Long> {
// Filtros combinados (nombre + apellidos + especialidad)
List<Medico> findByNombreContainingIgnoreCaseAndApellidosContainingIgnoreCaseAndEspecialidadContainingIgnoreCase(
String nombre, String apellidos, String especialidad);
// Filtro por nombre + apellidos
List<Medico> findByNombreContainingIgnoreCaseAndApellidosContainingIgnoreCase(
String nombre, String apellidos);
// Filtro por nombre + especialidad
List<Medico> findByNombreContainingIgnoreCaseAndEspecialidadContainingIgnoreCase(
String nombre, String especialidad);
// Filtro por apellidos + especialidad
List<Medico> findByApellidosContainingIgnoreCaseAndEspecialidadContainingIgnoreCase(
String apellidos, String especialidad);
// Filtros individuales
List<Medico> findByNombreContainingIgnoreCase(String nombre);
List<Medico> findByApellidosContainingIgnoreCase(String apellidos);
List<Medico> findByEspecialidadContainingIgnoreCase(String especialidad);
}
package es.pulsoft.spring_hospital.service;
import es.pulsoft.spring_hospital.model.Departamento;
import es.pulsoft.spring_hospital.model.Medico;
import es.pulsoft.spring_hospital.repository.DepartamentoRepository;
import es.pulsoft.spring_hospital.repository.MedicoRepository;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
import java.util.List;
@Service
public class MedicoService {
private final MedicoRepository medicoRepository;
private final DepartamentoRepository departamentoRepository;
// Inyección de dependencias de repositorios
public MedicoService(MedicoRepository medicoRepository,
DepartamentoRepository departamentoRepository) {
this.medicoRepository = medicoRepository;
this.departamentoRepository = departamentoRepository;
}
// Filtra médicos por nombre, apellidos y especialidad
// Permite combinaciones múltiples de filtros
public List<Medico> filtrar(String nombre,
String apellidos,
String especialidad) {
if (nombre != null && apellidos != null && especialidad != null) {
return medicoRepository
.findByNombreContainingIgnoreCaseAndApellidosContainingIgnoreCaseAndEspecialidadContainingIgnoreCase(
nombre, apellidos, especialidad);
}
if (nombre != null && apellidos != null) {
return medicoRepository
.findByNombreContainingIgnoreCaseAndApellidosContainingIgnoreCase(
nombre, apellidos);
}
if (nombre != null && especialidad != null) {
return medicoRepository
.findByNombreContainingIgnoreCaseAndEspecialidadContainingIgnoreCase(
nombre, especialidad);
}
if (apellidos != null && especialidad != null) {
return medicoRepository
.findByApellidosContainingIgnoreCaseAndEspecialidadContainingIgnoreCase(
apellidos, especialidad);
}
if (nombre != null) {
return medicoRepository.findByNombreContainingIgnoreCase(nombre);
}
if (apellidos != null) {
return medicoRepository.findByApellidosContainingIgnoreCase(apellidos);
}
if (especialidad != null) {
return medicoRepository.findByEspecialidadContainingIgnoreCase(especialidad);
}
return medicoRepository.findAll();
}
// Busca un médico por su identificador
// Lanza excepción 404 si no existe
public Medico buscarPorId(Long id) {
return medicoRepository.findById(id).orElseThrow(() ->
new ResponseStatusException(HttpStatus.NOT_FOUND,
"Medico no encontrado"));
}
// Crea un nuevo médico
// Valida datos obligatorios y asigna departamento existente
public Medico crearMedico(Medico medico) {
validaciones(medico);
Departamento d = departamentoRepository.findById(medico.getDepartamento().getId())
.orElseThrow(() ->
new ResponseStatusException(HttpStatus.NOT_FOUND,
"Departamento no encontrado"));
medico.setDepartamento(d);
return medicoRepository.save(medico);
}
// Actualiza completamente un médico existente
public ResponseEntity<Medico> actualizarMedico(Long id, Medico medico) {
Medico m = buscarPorId(id);
validaciones(medico);
m.setNombre(medico.getNombre());
m.setApellidos(medico.getApellidos());
m.setEspecialidad(medico.getEspecialidad());
Departamento d = departamentoRepository.findById(medico.getDepartamento().getId())
.orElseThrow(() ->
new ResponseStatusException(HttpStatus.NOT_FOUND,
"Departamento no encontrado"));
m.setDepartamento(d);
return ResponseEntity.ok(medicoRepository.save(m));
}
// Elimina un médico por su identificador
public ResponseEntity<Void> borrarMedico(Long id) {
Medico m = buscarPorId(id);
medicoRepository.delete(m);
return ResponseEntity.noContent().build();
}
// Validaciones de negocio para médico
// - Nombre obligatorio
// - Apellidos obligatorios
// - Especialidad obligatoria
// - Departamento obligatorio
private void validaciones(Medico medico) {
if (medico.getNombre() == null || medico.getNombre().trim().isEmpty()) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"El nombre de medico no puede quedar sin rellenar");
}
if (medico.getApellidos() == null || medico.getApellidos().trim().isEmpty()) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"Los apellidos de medico no puede quedar sin rellenar");
}
if (medico.getEspecialidad() == null || medico.getEspecialidad().trim().isEmpty()) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"la especialidad de medico no puede quedar sin rellenar");
}
if (medico.getDepartamento() == null ||
medico.getDepartamento().getId() == null) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"Departamento obligatorio");
}
}
// Actualización parcial de médico
// Solo modifica los campos enviados en la petición
public ResponseEntity<Medico> actualizacionParcial(long id, Medico medico) {
Medico m = medicoRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND,
"Medico no encontrado"));
if (medico.getNombre() != null) {
m.setNombre(medico.getNombre());
}
if (medico.getEspecialidad() != null) {
m.setEspecialidad(medico.getEspecialidad());
}
if (medico.getApellidos() != null) {
m.setApellidos(medico.getApellidos());
}
if (medico.getDepartamento() != null) {
Departamento d = departamentoRepository.findById(medico.getDepartamento().getId())
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND,
"Departamento no ecnontrado"));
m.setDepartamento(d);
}
return ResponseEntity.ok(medicoRepository.save(m));
}
}
5. Paciente
Desarrollar un sistema de gestión de pacientes.
Cada paciente tendrá datos personales básicos y podrá tener múltiples citas y una historia clínica asociada.
Se deben implementar relaciones JPA y operaciones CRUD completas siguiendo arquitectura en capas.
package es.pulsoft.spring_hospital.controller;
import es.pulsoft.spring_hospital.model.Paciente;
import es.pulsoft.spring_hospital.service.PacienteService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/pacientes")
public class PacienteController {
// Servicio de pacientes
private final PacienteService pacienteService;
// Inyección del servicio
public PacienteController(PacienteService pacienteService) {
this.pacienteService = pacienteService;
}
// Listado de pacientes con filtros opcionales
@GetMapping
public List<Paciente> filtrar(
@RequestParam(required = false) String nombre,
@RequestParam(required = false) String apellidos,
@RequestParam(required = false) String dni,
@RequestParam(required = false) String telefono) {
return pacienteService.filtrar(nombre, apellidos, dni, telefono);
}
// Buscar paciente por ID
@GetMapping("/{id}")
public Paciente buscarPorId(@PathVariable Long id) {
return pacienteService.buscarPorId(id);
}
// Crear paciente
@PostMapping
public Paciente crearPaciente(@RequestBody Paciente paciente) {
return pacienteService.crearPaciente(paciente);
}
// Actualizar paciente completo
@PutMapping("/{id}")
public ResponseEntity<Paciente> actualizarPaciente(
@PathVariable Long id,
@RequestBody Paciente paciente) {
return pacienteService.actualizarPaciente(id, paciente);
}
// Eliminar paciente
@DeleteMapping("/{id}")
public ResponseEntity<Void> borrarPaciente(@PathVariable Long id) {
return pacienteService.borrarPaciente(id);
}
// Actualización parcial de paciente
@PatchMapping("/{id}")
public ResponseEntity<Paciente> actualizacionParcial(
@PathVariable Long id,
@RequestBody Paciente paciente) {
return pacienteService.actualizacionParcial(id, paciente);
}
}
package es.pulsoft.spring_hospital.model;
import jakarta.persistence.*;
import java.util.List;
// Entidad que representa a un paciente dentro del sistema hospitalario
@Entity
@Table(name="paciente")
public class Paciente {
// Identificador único del paciente
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Nombre del paciente
private String nombre;
// Apellidos del paciente
private String apellidos;
// DNI del paciente
private String dni;
// Número de teléfono del paciente
private String telefono;
// Relación OneToMany: un paciente puede tener muchas citas
@OneToMany(mappedBy = "paciente")
private List<Cita> citas;
// Relación OneToOne: cada paciente tiene una única historia clínica
// mappedBy indica que la relación está gestionada desde HistoriaClinica
@OneToOne(mappedBy = "paciente")
private HistoriaClinica historiaClinica;
// Constructor vacío requerido por JPA
public Paciente(){
}
// Constructor para crear un paciente con sus datos básicos
public Paciente(String nombre, String apellidos, String dni, String telefono,
List<Cita> citas, HistoriaClinica historiaClinica) {
this.nombre = nombre;
this.apellidos = apellidos;
this.dni = dni;
this.telefono = telefono;
this.citas = citas;
this.historiaClinica = historiaClinica;
}
// Obtiene el identificador del paciente
public Long getId() {
return id;
}
// Obtiene el nombre del paciente
public String getNombre() {
return nombre;
}
// Establece el nombre del paciente
public void setNombre(String nombre) {
this.nombre = nombre;
}
// Obtiene los apellidos del paciente
public String getApellidos() {
return apellidos;
}
// Establece los apellidos del paciente
public void setApellidos(String apellidos) {
this.apellidos = apellidos;
}
// Obtiene el DNI del paciente
public String getDni() {
return dni;
}
// Establece el DNI del paciente
public void setDni(String dni) {
this.dni = dni;
}
// Obtiene el teléfono del paciente
public String getTelefono() {
return telefono;
}
// Establece el teléfono del paciente
public void setTelefono(String telefono) {
this.telefono = telefono;
}
// Obtiene la lista de citas del paciente
public List<Cita> getCitas() {
return citas;
}
// Asigna la lista de citas al paciente
public void setCitas(List<Cita> citas) {
this.citas = citas;
}
// Obtiene la historia clínica del paciente
public HistoriaClinica getHistoriaClinica() {
return historiaClinica;
}
// Asigna la historia clínica al paciente
public void setHistoriaClinica(HistoriaClinica historiaClinica) {
this.historiaClinica = historiaClinica;
}
}
package es.pulsoft.spring_hospital.repository;
import es.pulsoft.spring_hospital.model.Paciente;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
// Repositorio de acceso a datos para la entidad Paciente
// Extiende JpaRepository para operaciones CRUD automáticas
public interface PacienteRepository extends JpaRepository<Paciente, Long> {
// Busca pacientes por nombre (búsqueda parcial, ignore case)
List<Paciente> findByNombreContainingIgnoreCase(String nombre);
// Busca pacientes por apellidos (búsqueda parcial, ignore case)
List<Paciente> findByApellidosContainingIgnoreCase(String apellidos);
// Busca pacientes por DNI (búsqueda parcial, ignore case)
List<Paciente> findByDniContainingIgnoreCase(String dni);
// Busca pacientes por teléfono (búsqueda parcial, ignore case)
List<Paciente> findByTelefonoContainingIgnoreCase(String telefono);
}
package es.pulsoft.spring_hospital.service;
import es.pulsoft.spring_hospital.model.HistoriaClinica;
import es.pulsoft.spring_hospital.model.Paciente;
import es.pulsoft.spring_hospital.repository.HistoriaClinicaRepository;
import es.pulsoft.spring_hospital.repository.PacienteRepository;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
import java.util.List;
@Service
public class PacienteService {
private final PacienteRepository pacienteRepository;
private final HistoriaClinicaRepository historiaClinicaRepository;
// Inyección de dependencias de repositorios
public PacienteService(PacienteRepository pacienteRepository,
HistoriaClinicaRepository historiaClinicaRepository) {
this.pacienteRepository = pacienteRepository;
this.historiaClinicaRepository = historiaClinicaRepository;
}
// Filtra pacientes por nombre, apellidos, DNI o teléfono
// Aplica filtros individuales según los parámetros recibidos
public List<Paciente> filtrar(String nombre,
String apellidos,
String dni,
String telefono) {
if (nombre != null && !nombre.isBlank()) {
return pacienteRepository.findByNombreContainingIgnoreCase(nombre);
}
if (apellidos != null && !apellidos.isBlank()) {
return pacienteRepository.findByApellidosContainingIgnoreCase(apellidos);
}
if (dni != null && !dni.isBlank()) {
return pacienteRepository.findByDniContainingIgnoreCase(dni);
}
if (telefono != null && !telefono.isBlank()) {
return pacienteRepository.findByTelefonoContainingIgnoreCase(telefono);
}
return pacienteRepository.findAll();
}
// Busca un paciente por su identificador
// Lanza excepción 404 si no existe
public Paciente buscarPorId(Long id) {
return pacienteRepository.findById(id).orElseThrow(() ->
new ResponseStatusException(HttpStatus.NOT_FOUND,
"Paciente no ecnontrado"));
}
// Crea un nuevo paciente aplicando validaciones de negocio
public Paciente crearPaciente(Paciente paciente) {
validaciones(paciente);
return pacienteRepository.save(paciente);
}
// Actualiza completamente un paciente existente
public ResponseEntity<Paciente> actualizarPaciente(Long id, Paciente paciente) {
Paciente p = buscarPorId(id);
validaciones(paciente);
p.setNombre(paciente.getNombre());
p.setApellidos(paciente.getApellidos());
p.setDni(paciente.getDni());
p.setTelefono(paciente.getTelefono());
return ResponseEntity.ok(pacienteRepository.save(p));
}
// Elimina un paciente por su identificador
public ResponseEntity<Void> borrarPaciente(Long id) {
Paciente p = buscarPorId(id);
pacienteRepository.delete(p);
return ResponseEntity.noContent().build();
}
// Validaciones de negocio para paciente
// Todos los campos básicos son obligatorios
private void validaciones(Paciente paciente) {
if (paciente.getNombre() == null || paciente.getNombre().trim().isEmpty()) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"El nombre del paciente no puede quedar sin cubrir");
}
if (paciente.getApellidos() == null || paciente.getApellidos().trim().isEmpty()) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"Los apellidos del paciente no puede quedar sin cubrir");
}
if (paciente.getDni() == null || paciente.getDni().trim().isEmpty()) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"El DNI del paciente no puede quedar sin cubrir");
}
if (paciente.getTelefono() == null || paciente.getTelefono().trim().isEmpty()) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"El telefono del paciente no puede quedar sin cubrir");
}
}
// Actualización parcial de paciente
// Solo modifica los campos enviados en la petición
public ResponseEntity<Paciente> actualizacionParcial(Long id, Paciente paciente) {
Paciente p = pacienteRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND,
"Paciente no encontrado"));
if (paciente.getNombre() != null) {
p.setNombre(paciente.getNombre());
}
if (paciente.getApellidos() != null) {
p.setApellidos(paciente.getApellidos());
}
if (paciente.getDni() != null) {
p.setDni(paciente.getDni());
}
if (paciente.getTelefono() != null) {
p.setTelefono(paciente.getTelefono());
}
if (paciente.getHistoriaClinica() != null) {
HistoriaClinica h = historiaClinicaRepository.findById(
paciente.getHistoriaClinica().getId()
).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND,
"Historia clinica no encontrada"));
p.setHistoriaClinica(paciente.getHistoriaClinica());
}
return ResponseEntity.ok(pacienteRepository.save(p));
}
}