Implementar controladores Estudiante, Materia y Empresarial

- Materia: entidad, repositorio, servicio y GET /materias (listado con filtro opcional por tipo) y GET /materias/{id} - Estudiante: entidad, repositorio, servicio y endpoints /estudiantes (listar, /me, crear y actualizar perfil del usuario autenticado, obtener por id) - Empresarial: entidades EmpresaVinculada y ResenaEmpresarial, repositorios, servicio y endpoints GET /empresarial (con filtros busqueda/calificacionMinima, contrato { empresas: [...] } esperado por el frontend), GET /empresarial/{id}/resenas y POST /empresarial/resenas Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

dantereach committed Sep 2, 2026 at 13:26 UTC f4703edd835cb42a24dc28e42a20fcea833ded65
21 files changed +796 -6
src/main/java/com/tecmilenio/mapsconect/controller/EmpresarialController.java
+42 -2
@@ -1,9 +1,49 @@
1 package com.tecmilenio.mapsconect.controller;
2
3 -import org.springframework.web.bind.annotation.RequestMapping;
4 -import org.springframework.web.bind.annotation.RestController;
3 +import com.tecmilenio.mapsconect.dto.ApiResponse;
4 +import com.tecmilenio.mapsconect.dto.EmpresaDTO;
5 +import com.tecmilenio.mapsconect.dto.ResenaDTO;
6 +import com.tecmilenio.mapsconect.dto.ResenaRequestDTO;
7 +import com.tecmilenio.mapsconect.service.EmpresarialService;
8 +import jakarta.validation.Valid;
9 +import org.springframework.beans.factory.annotation.Autowired;
10 +import org.springframework.http.HttpStatus;
11 +import org.springframework.http.ResponseEntity;
12 +import org.springframework.security.core.Authentication;
13 +import org.springframework.web.bind.annotation.*;
14 +
15 +import java.util.List;
16 +import java.util.Map;
17
18 @RestController
19 @RequestMapping("/empresarial")
20 public class EmpresarialController {
21 +
22 + @Autowired
23 + private EmpresarialService empresarialService;
24 +
25 + // Contrato consumido por frontend/js/pages/empresarial.js: { empresas: [...] }
26 + @GetMapping
27 + public ResponseEntity<Map<String, Object>> listar(
28 + @RequestParam(required = false) String busqueda,
29 + @RequestParam(required = false) Double calificacionMinima) {
30 + List<EmpresaDTO> empresas = empresarialService.listarEmpresas(busqueda, calificacionMinima);
31 + return ResponseEntity.ok(Map.of("empresas", empresas));
32 + }
33 +
34 + @GetMapping("/{id}/resenas")
35 + public ResponseEntity<ApiResponse<List<ResenaDTO>>> listarResenas(@PathVariable Integer id) {
36 + return ResponseEntity.ok(ApiResponse.success(empresarialService.listarResenasPorEmpresa(id)));
37 + }
38 +
39 + @PostMapping("/resenas")
40 + public ResponseEntity<ApiResponse<ResenaDTO>> crearResena(
41 + Authentication authentication,
42 + @Valid @RequestBody ResenaRequestDTO dto) {
43 + ResenaDTO resena = empresarialService.crearResena(authentication.getName(), dto);
44 + return ResponseEntity.status(HttpStatus.CREATED)
45 + .body(ApiResponse.success(resena, "Reseña publicada correctamente"));
46 + }
47 +
48 }
49 +
src/main/java/com/tecmilenio/mapsconect/controller/EstudianteController.java
+50 -2
@@ -1,9 +1,57 @@
1 package com.tecmilenio.mapsconect.controller;
2
3 -import org.springframework.web.bind.annotation.RequestMapping;
4 -import org.springframework.web.bind.annotation.RestController;
3 +import com.tecmilenio.mapsconect.dto.ApiResponse;
4 +import com.tecmilenio.mapsconect.dto.EstudianteDTO;
5 +import com.tecmilenio.mapsconect.dto.EstudianteRequestDTO;
6 +import com.tecmilenio.mapsconect.service.EstudianteService;
7 +import jakarta.validation.Valid;
8 +import org.springframework.beans.factory.annotation.Autowired;
9 +import org.springframework.http.HttpStatus;
10 +import org.springframework.http.ResponseEntity;
11 +import org.springframework.security.core.Authentication;
12 +import org.springframework.web.bind.annotation.*;
13 +
14 +import java.util.List;
15
16 @RestController
17 @RequestMapping("/estudiantes")
18 public class EstudianteController {
19 +
20 + @Autowired
21 + private EstudianteService estudianteService;
22 +
23 + @GetMapping
24 + public ResponseEntity<ApiResponse<List<EstudianteDTO>>> listar() {
25 + return ResponseEntity.ok(ApiResponse.success(estudianteService.listar()));
26 + }
27 +
28 + @GetMapping("/me")
29 + public ResponseEntity<ApiResponse<EstudianteDTO>> obtenerMiPerfil(Authentication authentication) {
30 + EstudianteDTO estudiante = estudianteService.obtenerPerfilActual(authentication.getName());
31 + return ResponseEntity.ok(ApiResponse.success(estudiante));
32 + }
33 +
34 + @GetMapping("/{id}")
35 + public ResponseEntity<ApiResponse<EstudianteDTO>> obtenerPorId(@PathVariable Integer id) {
36 + return ResponseEntity.ok(ApiResponse.success(estudianteService.obtenerPorId(id)));
37 + }
38 +
39 + @PostMapping
40 + public ResponseEntity<ApiResponse<EstudianteDTO>> crear(
41 + Authentication authentication,
42 + @Valid @RequestBody EstudianteRequestDTO dto) {
43 + EstudianteDTO estudiante = estudianteService.crearPerfil(authentication.getName(), dto);
44 + return ResponseEntity.status(HttpStatus.CREATED)
45 + .body(ApiResponse.success(estudiante, "Perfil de estudiante creado correctamente"));
46 + }
47 +
48 + @PutMapping("/me")
49 + public ResponseEntity<ApiResponse<EstudianteDTO>> actualizar(
50 + Authentication authentication,
51 + @Valid @RequestBody EstudianteRequestDTO dto) {
52 + EstudianteDTO estudiante = estudianteService.actualizarPerfil(authentication.getName(), dto);
53 + return ResponseEntity.ok(ApiResponse.success(estudiante, "Perfil actualizado correctamente"));
54 + }
55 +
56 }
57 +
src/main/java/com/tecmilenio/mapsconect/controller/MateriaController.java
+26 -2
@@ -1,9 +1,33 @@
1 package com.tecmilenio.mapsconect.controller;
2
3 -import org.springframework.web.bind.annotation.RequestMapping;
4 -import org.springframework.web.bind.annotation.RestController;
3 +import com.tecmilenio.mapsconect.dto.ApiResponse;
4 +import com.tecmilenio.mapsconect.dto.MateriaDTO;
5 +import com.tecmilenio.mapsconect.service.MateriaService;
6 +import org.springframework.beans.factory.annotation.Autowired;
7 +import org.springframework.http.ResponseEntity;
8 +import org.springframework.web.bind.annotation.*;
9 +
10 +import java.util.List;
11
12 @RestController
13 @RequestMapping("/materias")
14 public class MateriaController {
15 +
16 + @Autowired
17 + private MateriaService materiaService;
18 +
19 + @GetMapping
20 + public ResponseEntity<ApiResponse<List<MateriaDTO>>> listar(
21 + @RequestParam(required = false) String tipo) {
22 + List<MateriaDTO> materias = materiaService.listar(tipo);
23 + return ResponseEntity.ok(ApiResponse.success(materias));
24 + }
25 +
26 + @GetMapping("/{id}")
27 + public ResponseEntity<ApiResponse<MateriaDTO>> obtenerPorId(@PathVariable Integer id) {
28 + MateriaDTO materia = materiaService.obtenerPorId(id);
29 + return ResponseEntity.ok(ApiResponse.success(materia));
30 + }
31 +
32 }
33 +
src/main/java/com/tecmilenio/mapsconect/dto/EmpresaDTO.java new
+23
@@ -0,0 +1,23 @@
1 +package com.tecmilenio.mapsconect.dto;
2 +
3 +import lombok.AllArgsConstructor;
4 +import lombok.Builder;
5 +import lombok.Data;
6 +import lombok.NoArgsConstructor;
7 +
8 +@Data
9 +@NoArgsConstructor
10 +@AllArgsConstructor
11 +@Builder
12 +public class EmpresaDTO {
13 +
14 + private Integer id;
15 + private String nombre;
16 + private String sector;
17 + private String descripcion;
18 + private Boolean activa;
19 + private Double calificacion;
20 + private Long totalResenas;
21 + private ExperienciaDTO experienciaDestacada;
22 +
23 +}
src/main/java/com/tecmilenio/mapsconect/dto/EstudianteDTO.java new
+24
@@ -0,0 +1,24 @@
1 +package com.tecmilenio.mapsconect.dto;
2 +
3 +import lombok.AllArgsConstructor;
4 +import lombok.Builder;
5 +import lombok.Data;
6 +import lombok.NoArgsConstructor;
7 +
8 +@Data
9 +@NoArgsConstructor
10 +@AllArgsConstructor
11 +@Builder
12 +public class EstudianteDTO {
13 +
14 + private Integer id;
15 + private Integer idUsuario;
16 + private String nombreCompleto;
17 + private String email;
18 + private Integer idCarrera;
19 + private String matricula;
20 + private Integer semestreActual;
21 + private String propositoVida;
22 + private Integer puntosReputacion;
23 +
24 +}
src/main/java/com/tecmilenio/mapsconect/dto/EstudianteRequestDTO.java new
+31
@@ -0,0 +1,31 @@
1 +package com.tecmilenio.mapsconect.dto;
2 +
3 +import jakarta.validation.constraints.Max;
4 +import jakarta.validation.constraints.Min;
5 +import jakarta.validation.constraints.NotBlank;
6 +import jakarta.validation.constraints.NotNull;
7 +import lombok.AllArgsConstructor;
8 +import lombok.Builder;
9 +import lombok.Data;
10 +import lombok.NoArgsConstructor;
11 +
12 +@Data
13 +@NoArgsConstructor
14 +@AllArgsConstructor
15 +@Builder
16 +public class EstudianteRequestDTO {
17 +
18 + private Integer idCarrera;
19 +
20 + @NotBlank(message = "La matrícula es obligatoria")
21 + @jakarta.validation.constraints.Size(max = 20, message = "La matrícula no puede exceder 20 caracteres")
22 + private String matricula;
23 +
24 + @NotNull(message = "El semestre actual es obligatorio")
25 + @Min(value = 1, message = "El semestre debe estar entre 1 y 12")
26 + @Max(value = 12, message = "El semestre debe estar entre 1 y 12")
27 + private Integer semestreActual;
28 +
29 + private String propositoVida;
30 +
31 +}
src/main/java/com/tecmilenio/mapsconect/dto/ExperienciaDTO.java new
+18
@@ -0,0 +1,18 @@
1 +package com.tecmilenio.mapsconect.dto;
2 +
3 +import lombok.AllArgsConstructor;
4 +import lombok.Builder;
5 +import lombok.Data;
6 +import lombok.NoArgsConstructor;
7 +
8 +@Data
9 +@NoArgsConstructor
10 +@AllArgsConstructor
11 +@Builder
12 +public class ExperienciaDTO {
13 +
14 + private String autor;
15 + private Integer semestre;
16 + private String texto;
17 +
18 +}
src/main/java/com/tecmilenio/mapsconect/dto/MateriaDTO.java new
+20
@@ -0,0 +1,20 @@
1 +package com.tecmilenio.mapsconect.dto;
2 +
3 +import lombok.AllArgsConstructor;
4 +import lombok.Builder;
5 +import lombok.Data;
6 +import lombok.NoArgsConstructor;
7 +
8 +@Data
9 +@NoArgsConstructor
10 +@AllArgsConstructor
11 +@Builder
12 +public class MateriaDTO {
13 +
14 + private Integer id;
15 + private String clave;
16 + private String nombre;
17 + private Integer creditos;
18 + private String tipo;
19 +
20 +}
src/main/java/com/tecmilenio/mapsconect/dto/ResenaDTO.java new
+25
@@ -0,0 +1,25 @@
1 +package com.tecmilenio.mapsconect.dto;
2 +
3 +import lombok.AllArgsConstructor;
4 +import lombok.Builder;
5 +import lombok.Data;
6 +import lombok.NoArgsConstructor;
7 +
8 +import java.time.LocalDateTime;
9 +
10 +@Data
11 +@NoArgsConstructor
12 +@AllArgsConstructor
13 +@Builder
14 +public class ResenaDTO {
15 +
16 + private Integer id;
17 + private Integer idEmpresa;
18 + private Integer idEstudiante;
19 + private String nombreEstudiante;
20 + private Integer calificacion;
21 + private String proyectoRealizado;
22 + private String recomendaciones;
23 + private LocalDateTime fechaResena;
24 +
25 +}
src/main/java/com/tecmilenio/mapsconect/dto/ResenaRequestDTO.java new
+29
@@ -0,0 +1,29 @@
1 +package com.tecmilenio.mapsconect.dto;
2 +
3 +import jakarta.validation.constraints.Max;
4 +import jakarta.validation.constraints.Min;
5 +import jakarta.validation.constraints.NotNull;
6 +import lombok.AllArgsConstructor;
7 +import lombok.Builder;
8 +import lombok.Data;
9 +import lombok.NoArgsConstructor;
10 +
11 +@Data
12 +@NoArgsConstructor
13 +@AllArgsConstructor
14 +@Builder
15 +public class ResenaRequestDTO {
16 +
17 + @NotNull(message = "La empresa es obligatoria")
18 + private Integer idEmpresa;
19 +
20 + @NotNull(message = "La calificación es obligatoria")
21 + @Min(value = 1, message = "La calificación debe estar entre 1 y 5")
22 + @Max(value = 5, message = "La calificación debe estar entre 1 y 5")
23 + private Integer calificacion;
24 +
25 + private String proyectoRealizado;
26 +
27 + private String recomendaciones;
28 +
29 +}
src/main/java/com/tecmilenio/mapsconect/entity/EmpresaVinculada.java new
+41
@@ -0,0 +1,41 @@
1 +package com.tecmilenio.mapsconect.entity;
2 +
3 +import jakarta.persistence.*;
4 +import lombok.AllArgsConstructor;
5 +import lombok.Builder;
6 +import lombok.Data;
7 +import lombok.NoArgsConstructor;
8 +
9 +import java.time.LocalDateTime;
10 +
11 +@Entity
12 +@Table(name = "empresas_vinculadas")
13 +@Data
14 +@NoArgsConstructor
15 +@AllArgsConstructor
16 +@Builder
17 +public class EmpresaVinculada {
18 +
19 + @Id
20 + @GeneratedValue(strategy = GenerationType.IDENTITY)
21 + @Column(name = "id_empresa")
22 + private Integer id;
23 +
24 + @Column(nullable = false, length = 200)
25 + private String nombre;
26 +
27 + @Column(length = 100)
28 + private String sector;
29 +
30 + @Column(columnDefinition = "TEXT")
31 + private String descripcion;
32 +
33 + @Builder.Default
34 + @Column(nullable = false)
35 + private Boolean activa = true;
36 +
37 + @Builder.Default
38 + @Column(name = "fecha_creacion", nullable = false)
39 + private LocalDateTime fechaCreacion = LocalDateTime.now();
40 +
41 +}
src/main/java/com/tecmilenio/mapsconect/entity/Estudiante.java new
+42
@@ -0,0 +1,42 @@
1 +package com.tecmilenio.mapsconect.entity;
2 +
3 +import jakarta.persistence.*;
4 +import lombok.AllArgsConstructor;
5 +import lombok.Builder;
6 +import lombok.Data;
7 +import lombok.NoArgsConstructor;
8 +
9 +@Entity
10 +@Table(name = "estudiantes")
11 +@Data
12 +@NoArgsConstructor
13 +@AllArgsConstructor
14 +@Builder
15 +public class Estudiante {
16 +
17 + @Id
18 + @GeneratedValue(strategy = GenerationType.IDENTITY)
19 + @Column(name = "id_estudiante")
20 + private Integer id;
21 +
22 + @OneToOne
23 + @JoinColumn(name = "id_usuario", nullable = false, unique = true)
24 + private Usuario usuario;
25 +
26 + @Column(name = "id_carrera")
27 + private Integer idCarrera;
28 +
29 + @Column(nullable = false, length = 20)
30 + private String matricula;
31 +
32 + @Column(name = "semestre_actual", nullable = false)
33 + private Integer semestreActual;
34 +
35 + @Column(name = "proposito_vida", columnDefinition = "TEXT")
36 + private String propositoVida;
37 +
38 + @Builder.Default
39 + @Column(name = "puntos_reputacion", nullable = false)
40 + private Integer puntosReputacion = 0;
41 +
42 +}
src/main/java/com/tecmilenio/mapsconect/entity/Materia.java new
+46
@@ -0,0 +1,46 @@
1 +package com.tecmilenio.mapsconect.entity;
2 +
3 +import jakarta.persistence.*;
4 +import lombok.AllArgsConstructor;
5 +import lombok.Builder;
6 +import lombok.Data;
7 +import lombok.NoArgsConstructor;
8 +
9 +import java.time.LocalDateTime;
10 +
11 +@Entity
12 +@Table(name = "materias")
13 +@Data
14 +@NoArgsConstructor
15 +@AllArgsConstructor
16 +@Builder
17 +public class Materia {
18 +
19 + @Id
20 + @GeneratedValue(strategy = GenerationType.IDENTITY)
21 + @Column(name = "id_materia")
22 + private Integer id;
23 +
24 + @Column(nullable = false, length = 20)
25 + private String clave;
26 +
27 + @Column(nullable = false, length = 150)
28 + private String nombre;
29 +
30 + @Builder.Default
31 + @Column(nullable = false)
32 + private Integer creditos = 0;
33 +
34 + @Enumerated(EnumType.STRING)
35 + @Column(nullable = false)
36 + private Tipo tipo;
37 +
38 + @Builder.Default
39 + @Column(name = "fecha_creacion", nullable = false)
40 + private LocalDateTime fechaCreacion = LocalDateTime.now();
41 +
42 + public enum Tipo {
43 + TRONCO_COMUN, DISCIPLINAR, CERTIFICADO, BIENESTAR
44 + }
45 +
46 +}
src/main/java/com/tecmilenio/mapsconect/entity/ResenaEmpresarial.java new
+45
@@ -0,0 +1,45 @@
1 +package com.tecmilenio.mapsconect.entity;
2 +
3 +import jakarta.persistence.*;
4 +import lombok.AllArgsConstructor;
5 +import lombok.Builder;
6 +import lombok.Data;
7 +import lombok.NoArgsConstructor;
8 +
9 +import java.time.LocalDateTime;
10 +
11 +@Entity
12 +@Table(name = "resenas_empresarial")
13 +@Data
14 +@NoArgsConstructor
15 +@AllArgsConstructor
16 +@Builder
17 +public class ResenaEmpresarial {
18 +
19 + @Id
20 + @GeneratedValue(strategy = GenerationType.IDENTITY)
21 + @Column(name = "id_resena")
22 + private Integer id;
23 +
24 + @ManyToOne
25 + @JoinColumn(name = "id_estudiante", nullable = false)
26 + private Estudiante estudiante;
27 +
28 + @ManyToOne
29 + @JoinColumn(name = "id_empresa", nullable = false)
30 + private EmpresaVinculada empresa;
31 +
32 + @Column(nullable = false)
33 + private Integer calificacion;
34 +
35 + @Column(name = "proyecto_realizado", columnDefinition = "TEXT")
36 + private String proyectoRealizado;
37 +
38 + @Column(columnDefinition = "TEXT")
39 + private String recomendaciones;
40 +
41 + @Builder.Default
42 + @Column(name = "fecha_resena", nullable = false)
43 + private LocalDateTime fechaResena = LocalDateTime.now();
44 +
45 +}
src/main/java/com/tecmilenio/mapsconect/repository/EmpresaVinculadaRepository.java new
+16
@@ -0,0 +1,16 @@
1 +package com.tecmilenio.mapsconect.repository;
2 +
3 +import com.tecmilenio.mapsconect.entity.EmpresaVinculada;
4 +import org.springframework.data.jpa.repository.JpaRepository;
5 +import org.springframework.stereotype.Repository;
6 +
7 +import java.util.List;
8 +
9 +@Repository
10 +public interface EmpresaVinculadaRepository extends JpaRepository<EmpresaVinculada, Integer> {
11 +
12 + List<EmpresaVinculada> findByActivaTrueAndNombreContainingIgnoreCase(String nombre);
13 +
14 + List<EmpresaVinculada> findByActivaTrue();
15 +
16 +}
src/main/java/com/tecmilenio/mapsconect/repository/EstudianteRepository.java new
+16
@@ -0,0 +1,16 @@
1 +package com.tecmilenio.mapsconect.repository;
2 +
3 +import com.tecmilenio.mapsconect.entity.Estudiante;
4 +import org.springframework.data.jpa.repository.JpaRepository;
5 +import org.springframework.stereotype.Repository;
6 +
7 +import java.util.Optional;
8 +
9 +@Repository
10 +public interface EstudianteRepository extends JpaRepository<Estudiante, Integer> {
11 +
12 + Optional<Estudiante> findByUsuario_Id(Integer idUsuario);
13 +
14 + boolean existsByMatricula(String matricula);
15 +
16 +}
src/main/java/com/tecmilenio/mapsconect/repository/MateriaRepository.java new
+17
@@ -0,0 +1,17 @@
1 +package com.tecmilenio.mapsconect.repository;
2 +
3 +import com.tecmilenio.mapsconect.entity.Materia;
4 +import org.springframework.data.jpa.repository.JpaRepository;
5 +import org.springframework.stereotype.Repository;
6 +
7 +import java.util.List;
8 +import java.util.Optional;
9 +
10 +@Repository
11 +public interface MateriaRepository extends JpaRepository<Materia, Integer> {
12 +
13 + List<Materia> findByTipo(Materia.Tipo tipo);
14 +
15 + Optional<Materia> findByClave(String clave);
16 +
17 +}
src/main/java/com/tecmilenio/mapsconect/repository/ResenaEmpresarialRepository.java new
+14
@@ -0,0 +1,14 @@
1 +package com.tecmilenio.mapsconect.repository;
2 +
3 +import com.tecmilenio.mapsconect.entity.ResenaEmpresarial;
4 +import org.springframework.data.jpa.repository.JpaRepository;
5 +import org.springframework.stereotype.Repository;
6 +
7 +import java.util.List;
8 +
9 +@Repository
10 +public interface ResenaEmpresarialRepository extends JpaRepository<ResenaEmpresarial, Integer> {
11 +
12 + List<ResenaEmpresarial> findByEmpresa_Id(Integer idEmpresa);
13 +
14 +}
src/main/java/com/tecmilenio/mapsconect/service/EmpresarialService.java new
+126
@@ -0,0 +1,126 @@
1 +package com.tecmilenio.mapsconect.service;
2 +
3 +import com.tecmilenio.mapsconect.dto.EmpresaDTO;
4 +import com.tecmilenio.mapsconect.dto.ExperienciaDTO;
5 +import com.tecmilenio.mapsconect.dto.ResenaDTO;
6 +import com.tecmilenio.mapsconect.dto.ResenaRequestDTO;
7 +import com.tecmilenio.mapsconect.entity.EmpresaVinculada;
8 +import com.tecmilenio.mapsconect.entity.Estudiante;
9 +import com.tecmilenio.mapsconect.entity.ResenaEmpresarial;
10 +import com.tecmilenio.mapsconect.entity.Usuario;
11 +import com.tecmilenio.mapsconect.exception.ResourceNotFoundException;
12 +import com.tecmilenio.mapsconect.repository.EmpresaVinculadaRepository;
13 +import com.tecmilenio.mapsconect.repository.EstudianteRepository;
14 +import com.tecmilenio.mapsconect.repository.ResenaEmpresarialRepository;
15 +import com.tecmilenio.mapsconect.repository.UsuarioRepository;
16 +import org.springframework.beans.factory.annotation.Autowired;
17 +import org.springframework.stereotype.Service;
18 +
19 +import java.util.Comparator;
20 +import java.util.List;
21 +
22 +@Service
23 +public class EmpresarialService {
24 +
25 + @Autowired
26 + private EmpresaVinculadaRepository empresaRepository;
27 +
28 + @Autowired
29 + private ResenaEmpresarialRepository resenaRepository;
30 +
31 + @Autowired
32 + private EstudianteRepository estudianteRepository;
33 +
34 + @Autowired
35 + private UsuarioRepository usuarioRepository;
36 +
37 + public List<EmpresaDTO> listarEmpresas(String busqueda, Double calificacionMinima) {
38 + List<EmpresaVinculada> empresas = (busqueda != null && !busqueda.isBlank())
39 + ? empresaRepository.findByActivaTrueAndNombreContainingIgnoreCase(busqueda)
40 + : empresaRepository.findByActivaTrue();
41 +
42 + return empresas.stream()
43 + .map(this::mapearAEmpresaDTO)
44 + .filter(dto -> calificacionMinima == null
45 + || dto.getCalificacion() == null
46 + || dto.getCalificacion() >= calificacionMinima)
47 + .toList();
48 + }
49 +
50 + public List<ResenaDTO> listarResenasPorEmpresa(Integer idEmpresa) {
51 + if (!empresaRepository.existsById(idEmpresa)) {
52 + throw new ResourceNotFoundException("Empresa no encontrada");
53 + }
54 + return resenaRepository.findByEmpresa_Id(idEmpresa).stream().map(this::mapearAResenaDTO).toList();
55 + }
56 +
57 + public ResenaDTO crearResena(String email, ResenaRequestDTO dto) {
58 + Usuario usuario = usuarioRepository.findByEmail(email)
59 + .orElseThrow(() -> new ResourceNotFoundException("Usuario no encontrado"));
60 +
61 + Estudiante estudiante = estudianteRepository.findByUsuario_Id(usuario.getId())
62 + .orElseThrow(() -> new RuntimeException("Debes completar tu perfil de estudiante antes de dejar una reseña"));
63 +
64 + EmpresaVinculada empresa = empresaRepository.findById(dto.getIdEmpresa())
65 + .orElseThrow(() -> new ResourceNotFoundException("Empresa no encontrada"));
66 +
67 + ResenaEmpresarial resena = ResenaEmpresarial.builder()
68 + .estudiante(estudiante)
69 + .empresa(empresa)
70 + .calificacion(dto.getCalificacion())
71 + .proyectoRealizado(dto.getProyectoRealizado())
72 + .recomendaciones(dto.getRecomendaciones())
73 + .build();
74 +
75 + return mapearAResenaDTO(resenaRepository.save(resena));
76 + }
77 +
78 + private EmpresaDTO mapearAEmpresaDTO(EmpresaVinculada empresa) {
79 + List<ResenaEmpresarial> resenas = resenaRepository.findByEmpresa_Id(empresa.getId());
80 +
81 + Double promedio = resenas.isEmpty() ? null
82 + : resenas.stream().mapToInt(ResenaEmpresarial::getCalificacion).average().orElse(0);
83 +
84 + ResenaEmpresarial mejorResena = resenas.stream()
85 + .max(Comparator.comparingInt(ResenaEmpresarial::getCalificacion))
86 + .orElse(null);
87 +
88 + ExperienciaDTO experiencia = null;
89 + if (mejorResena != null) {
90 + Usuario autorUsuario = mejorResena.getEstudiante().getUsuario();
91 + experiencia = ExperienciaDTO.builder()
92 + .autor(autorUsuario != null ? autorUsuario.getNombre() : "Estudiante")
93 + .semestre(mejorResena.getEstudiante().getSemestreActual())
94 + .texto(mejorResena.getRecomendaciones() != null
95 + ? mejorResena.getRecomendaciones()
96 + : mejorResena.getProyectoRealizado())
97 + .build();
98 + }
99 +
100 + return EmpresaDTO.builder()
101 + .id(empresa.getId())
102 + .nombre(empresa.getNombre())
103 + .sector(empresa.getSector())
104 + .descripcion(empresa.getDescripcion())
105 + .activa(empresa.getActiva())
106 + .calificacion(promedio)
107 + .totalResenas((long) resenas.size())
108 + .experienciaDestacada(experiencia)
109 + .build();
110 + }
111 +
112 + private ResenaDTO mapearAResenaDTO(ResenaEmpresarial resena) {
113 + Usuario usuario = resena.getEstudiante().getUsuario();
114 + return ResenaDTO.builder()
115 + .id(resena.getId())
116 + .idEmpresa(resena.getEmpresa().getId())
117 + .idEstudiante(resena.getEstudiante().getId())
118 + .nombreEstudiante(usuario != null ? (usuario.getNombre() + " " + usuario.getApellido()) : null)
119 + .calificacion(resena.getCalificacion())
120 + .proyectoRealizado(resena.getProyectoRealizado())
121 + .recomendaciones(resena.getRecomendaciones())
122 + .fechaResena(resena.getFechaResena())
123 + .build();
124 + }
125 +
126 +}
src/main/java/com/tecmilenio/mapsconect/service/EstudianteService.java new
+95
@@ -0,0 +1,95 @@
1 +package com.tecmilenio.mapsconect.service;
2 +
3 +import com.tecmilenio.mapsconect.dto.EstudianteDTO;
4 +import com.tecmilenio.mapsconect.dto.EstudianteRequestDTO;
5 +import com.tecmilenio.mapsconect.entity.Estudiante;
6 +import com.tecmilenio.mapsconect.entity.Usuario;
7 +import com.tecmilenio.mapsconect.exception.ResourceNotFoundException;
8 +import com.tecmilenio.mapsconect.repository.EstudianteRepository;
9 +import com.tecmilenio.mapsconect.repository.UsuarioRepository;
10 +import org.springframework.beans.factory.annotation.Autowired;
11 +import org.springframework.stereotype.Service;
12 +
13 +import java.util.List;
14 +
15 +@Service
16 +public class EstudianteService {
17 +
18 + @Autowired
19 + private EstudianteRepository estudianteRepository;
20 +
21 + @Autowired
22 + private UsuarioRepository usuarioRepository;
23 +
24 + public List<EstudianteDTO> listar() {
25 + return estudianteRepository.findAll().stream().map(this::mapearADTO).toList();
26 + }
27 +
28 + public EstudianteDTO obtenerPorId(Integer id) {
29 + Estudiante estudiante = estudianteRepository.findById(id)
30 + .orElseThrow(() -> new ResourceNotFoundException("Estudiante no encontrado"));
31 + return mapearADTO(estudiante);
32 + }
33 +
34 + public EstudianteDTO obtenerPerfilActual(String email) {
35 + Usuario usuario = obtenerUsuario(email);
36 + Estudiante estudiante = estudianteRepository.findByUsuario_Id(usuario.getId())
37 + .orElseThrow(() -> new ResourceNotFoundException("Aún no has completado tu perfil de estudiante"));
38 + return mapearADTO(estudiante);
39 + }
40 +
41 + public EstudianteDTO crearPerfil(String email, EstudianteRequestDTO dto) {
42 + Usuario usuario = obtenerUsuario(email);
43 +
44 + if (estudianteRepository.findByUsuario_Id(usuario.getId()).isPresent()) {
45 + throw new RuntimeException("Ya existe un perfil de estudiante para este usuario");
46 + }
47 + if (estudianteRepository.existsByMatricula(dto.getMatricula())) {
48 + throw new RuntimeException("La matrícula ya está registrada");
49 + }
50 +
51 + Estudiante estudiante = Estudiante.builder()
52 + .usuario(usuario)
53 + .idCarrera(dto.getIdCarrera())
54 + .matricula(dto.getMatricula())
55 + .semestreActual(dto.getSemestreActual())
56 + .propositoVida(dto.getPropositoVida())
57 + .puntosReputacion(0)
58 + .build();
59 +
60 + return mapearADTO(estudianteRepository.save(estudiante));
61 + }
62 +
63 + public EstudianteDTO actualizarPerfil(String email, EstudianteRequestDTO dto) {
64 + Usuario usuario = obtenerUsuario(email);
65 + Estudiante estudiante = estudianteRepository.findByUsuario_Id(usuario.getId())
66 + .orElseThrow(() -> new ResourceNotFoundException("Aún no has completado tu perfil de estudiante"));
67 +
68 + estudiante.setIdCarrera(dto.getIdCarrera());
69 + estudiante.setSemestreActual(dto.getSemestreActual());
70 + estudiante.setPropositoVida(dto.getPropositoVida());
71 +
72 + return mapearADTO(estudianteRepository.save(estudiante));
73 + }
74 +
75 + private Usuario obtenerUsuario(String email) {
76 + return usuarioRepository.findByEmail(email)
77 + .orElseThrow(() -> new ResourceNotFoundException("Usuario no encontrado"));
78 + }
79 +
80 + private EstudianteDTO mapearADTO(Estudiante estudiante) {
81 + Usuario usuario = estudiante.getUsuario();
82 + return EstudianteDTO.builder()
83 + .id(estudiante.getId())
84 + .idUsuario(usuario != null ? usuario.getId() : null)
85 + .nombreCompleto(usuario != null ? (usuario.getNombre() + " " + usuario.getApellido()) : null)
86 + .email(usuario != null ? usuario.getEmail() : null)
87 + .idCarrera(estudiante.getIdCarrera())
88 + .matricula(estudiante.getMatricula())
89 + .semestreActual(estudiante.getSemestreActual())
90 + .propositoVida(estudiante.getPropositoVida())
91 + .puntosReputacion(estudiante.getPuntosReputacion())
92 + .build();
93 + }
94 +
95 +}
src/main/java/com/tecmilenio/mapsconect/service/MateriaService.java new
+50
@@ -0,0 +1,50 @@
1 +package com.tecmilenio.mapsconect.service;
2 +
3 +import com.tecmilenio.mapsconect.dto.MateriaDTO;
4 +import com.tecmilenio.mapsconect.entity.Materia;
5 +import com.tecmilenio.mapsconect.exception.ResourceNotFoundException;
6 +import com.tecmilenio.mapsconect.repository.MateriaRepository;
7 +import org.springframework.beans.factory.annotation.Autowired;
8 +import org.springframework.stereotype.Service;
9 +
10 +import java.util.List;
11 +
12 +@Service
13 +public class MateriaService {
14 +
15 + @Autowired
16 + private MateriaRepository materiaRepository;
17 +
18 + public List<MateriaDTO> listar(String tipo) {
19 + List<Materia> materias;
20 + if (tipo != null && !tipo.isBlank()) {
21 + Materia.Tipo tipoEnum;
22 + try {
23 + tipoEnum = Materia.Tipo.valueOf(tipo.toUpperCase());
24 + } catch (IllegalArgumentException ex) {
25 + throw new IllegalArgumentException("Tipo de materia inválido: " + tipo);
26 + }
27 + materias = materiaRepository.findByTipo(tipoEnum);
28 + } else {
29 + materias = materiaRepository.findAll();
30 + }
31 + return materias.stream().map(this::mapearADTO).toList();
32 + }
33 +
34 + public MateriaDTO obtenerPorId(Integer id) {
35 + Materia materia = materiaRepository.findById(id)
36 + .orElseThrow(() -> new ResourceNotFoundException("Materia no encontrada"));
37 + return mapearADTO(materia);
38 + }
39 +
40 + private MateriaDTO mapearADTO(Materia materia) {
41 + return MateriaDTO.builder()
42 + .id(materia.getId())
43 + .clave(materia.getClave())
44 + .nombre(materia.getNombre())
45 + .creditos(materia.getCreditos())
46 + .tipo(materia.getTipo() != null ? materia.getTipo().name() : null)
47 + .build();
48 + }
49 +
50 +}