From 71af6e42680822416276042ccfe946ab57590c98 Mon Sep 17 00:00:00 2001 From: mukeshs Date: Wed, 5 Nov 2025 15:27:49 +0530 Subject: [PATCH] Professor update --- .../backend/constant/FileConstant.java | 11 +- .../backend/domain/Professor.java | 13 +- .../backend/service/ProfessorServiceImpl.java | 162 ++++++++++-------- 3 files changed, 101 insertions(+), 85 deletions(-) diff --git a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/constant/FileConstant.java b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/constant/FileConstant.java index 06aabaf..2a174e5 100644 --- a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/constant/FileConstant.java +++ b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/constant/FileConstant.java @@ -4,7 +4,10 @@ public class FileConstant { public static final String USER_IMAGE_PATH = "/user/image/"; public static final String JPG_EXTENSION = "jpg"; - public static final String USER_FOLDER = System.getProperty("user.home") + "/supportportal/user/"; + + // ✅ CHANGED: From System.getProperty("user.home") to /app/uploads + public static final String USER_FOLDER = "/app/uploads/user/"; + public static final String DIRECTORY_CREATED = "Created directory for: "; public static final String DEFAULT_USER_IMAGE_URI_PATTERN = "/user/%s/profile-image"; public static final String USER_IMAGE_FILENAME = "avatar.jpg"; @@ -14,9 +17,9 @@ public class FileConstant { public static final String NOT_AN_IMAGE_FILE = " is not an image file. Please upload an image file"; public static final String TEMP_PROFILE_IMAGE_BASE_URL = "https://robohash.org/"; - // ✅ Professor-specific constants + // ✅ CHANGED: Professor-specific constants public static final String PROFESSOR_IMAGE_PATH = "/professor/image/"; - public static final String PROFESSOR_FOLDER = System.getProperty("user.home") + "/supportportal/professor/"; + public static final String PROFESSOR_FOLDER = "/app/uploads/professor/"; public static final String DEFAULT_PROFESSOR_IMAGE_URI_PATTERN = "/professor/%s/profile-image"; public static final String PROFESSOR_IMAGE_FILENAME = "avatar.jpg"; -} +} \ No newline at end of file diff --git a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/domain/Professor.java b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/domain/Professor.java index c574606..24f586e 100644 --- a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/domain/Professor.java +++ b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/domain/Professor.java @@ -68,17 +68,18 @@ public class Professor implements Serializable { @Column(name = "work_day") private List workDays; - // Use Set instead of List to avoid MultipleBagFetchException - // Sets can be eagerly loaded together without issues - @OneToMany(mappedBy = "professor", cascade = CascadeType.ALL, fetch = FetchType.EAGER) + // ✅ CRITICAL FIX: Added orphanRemoval = true + // This tells JPA to DELETE skills that are removed from the collection + @OneToMany(mappedBy = "professor", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true) private Set skills; - // Use Set instead of List to avoid MultipleBagFetchException - @OneToMany(mappedBy = "professor", cascade = CascadeType.ALL, fetch = FetchType.EAGER) + // ✅ CRITICAL FIX: Added orphanRemoval = true + // This tells JPA to DELETE awards that are removed from the collection + @OneToMany(mappedBy = "professor", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true) private Set awards; @ManyToMany(mappedBy = "professors") - @JsonIgnore // Keep this as @JsonIgnore to avoid circular references + @JsonIgnore private List posts; // Convenience method to get full name diff --git a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/ProfessorServiceImpl.java b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/ProfessorServiceImpl.java index d0fa4b1..566a61d 100644 --- a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/ProfessorServiceImpl.java +++ b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/ProfessorServiceImpl.java @@ -78,15 +78,12 @@ public class ProfessorServiceImpl implements ProfessorService { return addNewProfessor(professorDto); } - // ✅ FIXED: Changed from DEFAULT_USER_IMAGE_URI_PATTERN to DEFAULT_PROFESSOR_IMAGE_URI_PATTERN private String generateDefaultProfileImageUrl(UUID professorId) { return ServletUriComponentsBuilder.fromCurrentContextPath() .path(String.format(DEFAULT_PROFESSOR_IMAGE_URI_PATTERN, professorId)) .toUriString(); } - // ✅ FIXED: Changed from DEFAULT_USER_IMAGE_URI_PATTERN to DEFAULT_PROFESSOR_IMAGE_URI_PATTERN - // ✅ FIXED: Changed from USER_IMAGE_FILENAME to PROFESSOR_IMAGE_FILENAME private String generateProfileImageUrl(UUID professorId) { return ServletUriComponentsBuilder.fromCurrentContextPath() .path(String.format(DEFAULT_PROFESSOR_IMAGE_URI_PATTERN, professorId)) @@ -134,7 +131,6 @@ public class ProfessorServiceImpl implements ProfessorService { .orElseThrow(() -> new ProfessorNotFoundException(PROFESSOR_NOT_FOUND_MSG)); } - // ✅ FIXED: Changed from USER_IMAGE_FILENAME to PROFESSOR_IMAGE_FILENAME private void saveProfileImage(Professor professor, MultipartFile profileImage) { if (profileImage == null) return; @@ -214,82 +210,98 @@ public class ProfessorServiceImpl implements ProfessorService { } @Override -@Transactional -public Professor updateProfessor(UUID professorId, ProfessorDto professorDto) { - Professor professor = professorRepository.findByProfessorId(professorId) - .orElseThrow(() -> new RuntimeException("Professor not found with id: " + professorId)); + @Transactional + public Professor updateProfessor(UUID professorId, ProfessorDto professorDto) { + Professor professor = professorRepository.findByProfessorId(professorId) + .orElseThrow(() -> new RuntimeException("Professor not found with id: " + professorId)); - validateUpdateEmail(professorId, professorDto.getEmail()); + validateUpdateEmail(professorId, professorDto.getEmail()); - // Update basic fields - professor.setFirstName(professorDto.getFirstName()); - professor.setLastName(professorDto.getLastName()); - professor.setEmail(professorDto.getEmail()); - professor.setDepartment(professorDto.getDepartment()); - professor.setPosition(professorDto.getPosition()); - professor.setOfficeLocation(professorDto.getOfficeLocation()); - professor.setStatus(professorDto.getStatus()); - professor.setCategory(professorDto.getCategory()); - - // Update extended fields - professor.setPhone(professorDto.getPhone()); - professor.setSpecialty(professorDto.getSpecialty()); - professor.setCertification(professorDto.getCertification()); - professor.setTraining(professorDto.getTraining()); - professor.setExperience(professorDto.getExperience()); - professor.setDescription(professorDto.getDescription()); - professor.setDesignation(professorDto.getDesignation()); - professor.setWorkDays(professorDto.getWorkDays()); + // Update basic fields + professor.setFirstName(professorDto.getFirstName()); + professor.setLastName(professorDto.getLastName()); + professor.setEmail(professorDto.getEmail()); + professor.setDepartment(professorDto.getDepartment()); + professor.setPosition(professorDto.getPosition()); + professor.setOfficeLocation(professorDto.getOfficeLocation()); + professor.setStatus(professorDto.getStatus()); + professor.setCategory(professorDto.getCategory()); + + // Update extended fields + professor.setPhone(professorDto.getPhone()); + professor.setSpecialty(professorDto.getSpecialty()); + professor.setCertification(professorDto.getCertification()); + professor.setTraining(professorDto.getTraining()); + professor.setExperience(professorDto.getExperience()); + professor.setDescription(professorDto.getDescription()); + professor.setDesignation(professorDto.getDesignation()); + professor.setWorkDays(professorDto.getWorkDays()); - if (professorDto.getJoinDate() != null) { - professor.setJoinDate(professorDto.getJoinDate()); + if (professorDto.getJoinDate() != null) { + professor.setJoinDate(professorDto.getJoinDate()); + } + + // Create a final reference for lambda expressions + final Professor professorRef = professor; + + // ✅ CORRECT FIX: Update skills with orphanRemoval=true + // Initialize collection if null + if (professor.getSkills() == null) { + professor.setSkills(new HashSet<>()); + } + + // Clear existing skills (orphanRemoval will delete them from DB) + professor.getSkills().clear(); + + // Add new skills + if (professorDto.getSkills() != null && !professorDto.getSkills().isEmpty()) { + Set newSkills = professorDto.getSkills().stream() + .filter(skillDto -> skillDto.getName() != null && !skillDto.getName().trim().isEmpty()) + .map(skillDto -> ProfessorSkill.builder() + .name(skillDto.getName().trim()) + .level(skillDto.getLevel()) + .professor(professorRef) + .build()) + .collect(Collectors.toSet()); + + professor.getSkills().addAll(newSkills); + } + + // ✅ CORRECT FIX: Update awards with orphanRemoval=true + // Initialize collection if null + if (professor.getAwards() == null) { + professor.setAwards(new HashSet<>()); + } + + // Clear existing awards (orphanRemoval will delete them from DB) + professor.getAwards().clear(); + + // Add new awards + if (professorDto.getAwards() != null && !professorDto.getAwards().isEmpty()) { + Set newAwards = professorDto.getAwards().stream() + .filter(awardDto -> awardDto.getTitle() != null && !awardDto.getTitle().trim().isEmpty()) + .map(awardDto -> ProfessorAward.builder() + .title(awardDto.getTitle().trim()) + .year(awardDto.getYear()) + .description(awardDto.getDescription()) + .imageUrl(awardDto.getImageUrl()) + .professor(professorRef) + .build()) + .collect(Collectors.toSet()); + + professor.getAwards().addAll(newAwards); + } + + Professor savedProfessor = professorRepository.save(professor); + + // Handle profile image if provided + if (professorDto.getProfileImage() != null) { + saveProfileImage(savedProfessor, professorDto.getProfileImage()); + } + + return savedProfessor; } - // Create a final reference for lambda expressions - final Professor professorRef = professor; - - // Update skills - REPLACE the entire collection - if (professorDto.getSkills() != null && !professorDto.getSkills().isEmpty()) { - Set skills = professorDto.getSkills().stream() - .filter(skillDto -> skillDto.getName() != null && !skillDto.getName().trim().isEmpty()) - .map(skillDto -> ProfessorSkill.builder() - .name(skillDto.getName().trim()) - .level(skillDto.getLevel()) - .professor(professorRef) - .build()) - .collect(Collectors.toSet()); - professor.setSkills(skills); // ✅ REPLACE instead of add - } else { - professor.setSkills(new HashSet<>()); // Clear if empty - } - - // Update awards - REPLACE the entire collection - if (professorDto.getAwards() != null && !professorDto.getAwards().isEmpty()) { - Set awards = professorDto.getAwards().stream() - .filter(awardDto -> awardDto.getTitle() != null && !awardDto.getTitle().trim().isEmpty()) - .map(awardDto -> ProfessorAward.builder() - .title(awardDto.getTitle().trim()) - .year(awardDto.getYear()) - .description(awardDto.getDescription()) - .imageUrl(awardDto.getImageUrl()) - .professor(professorRef) - .build()) - .collect(Collectors.toSet()); - professor.setAwards(awards); // ✅ REPLACE instead of add - } else { - professor.setAwards(new HashSet<>()); // Clear if empty - } - - Professor savedProfessor = professorRepository.save(professor); - - // Handle profile image if provided - if (professorDto.getProfileImage() != null) { - saveProfileImage(savedProfessor, professorDto.getProfileImage()); - } - - return savedProfessor; -} - @Override public void deleteProfessor(UUID professorId) { Professor professorToBeDeleted = professorRepository