diff --git a/src/main/java/com/balybus/galaxy/careAssistant/controller/HelperController.java b/src/main/java/com/balybus/galaxy/careAssistant/controller/HelperController.java index 6048dbe2..d921d695 100644 --- a/src/main/java/com/balybus/galaxy/careAssistant/controller/HelperController.java +++ b/src/main/java/com/balybus/galaxy/careAssistant/controller/HelperController.java @@ -89,6 +89,32 @@ public ResponseEntity> updateProfile(@RequestBody HelperProf return ResponseEntity.ok(result); } + /** + * 요양 보호사 통합 프로필 업데이트 + * @param helperCompleteProfileDTO + * @param userDetails + * @return + */ + @Operation(summary = "요양 보호사 통합 프로필 업데이트", description = "현재 로그인한 요양 보호사의 모든 정보를 한 번에 업데이트") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "프로필이 성공적으로 업데이트되었습니다.", + content = @Content(schema = @Schema(implementation = HelperCompleteProfileResponse.class))), + @ApiResponse(responseCode = "3000", description = "요양 보호사 테이블을 찾을 수 없습니다."), + @ApiResponse(responseCode = "3010", description = "로그인한 회원을 찾을 수 없습니다."), + @ApiResponse(responseCode = "3008", description = "잘못된 주소 목록"), + @ApiResponse(responseCode = "3009", description = "중복된 근무 가능 시간이 존재합니다."), + @ApiResponse(responseCode = "3012", description = "자격증 이름이 유효하지 않습니다."), + @ApiResponse(responseCode = "3013", description = "자격증 번호가 유효하지 않습니다."), + @ApiResponse(responseCode = "3014", description = "자격증 번호 형식이 올바르지 않습니다.") + }) + @PutMapping("/helper/complete-profile") + public ResponseEntity updateCompleteProfile( + @RequestBody HelperCompleteProfileDTO helperCompleteProfileDTO, + @AuthenticationPrincipal UserDetails userDetails) { + HelperCompleteProfileResponse response = helperService.updateCompleteProfile(userDetails, helperCompleteProfileDTO); + return ResponseEntity.ok(response); + } + /////////////////////////////////////// @@ -204,7 +230,7 @@ public ResponseEntity> certiVerify( ////////////// - @Operation(summary = "요양 보호사 정보 모두보기", description = "개인 요양보호사 구분자로 요양보호사 상세 조회") + @Operation(summary = "요양 보호사 정보 조회", description = "개인 요양보호사 구분자로 요양보호사 상세 조회") @ApiResponses({ @ApiResponse(responseCode = "200", description = "요양 보호사 정보를 성공적으로 불러왔습니다.", content = @Content(schema = @Schema(implementation = HelperResponse.class)) diff --git a/src/main/java/com/balybus/galaxy/careAssistant/domain/TblHelperWorkTime.java b/src/main/java/com/balybus/galaxy/careAssistant/domain/TblHelperWorkTime.java index 39a89bfe..ac960628 100644 --- a/src/main/java/com/balybus/galaxy/careAssistant/domain/TblHelperWorkTime.java +++ b/src/main/java/com/balybus/galaxy/careAssistant/domain/TblHelperWorkTime.java @@ -49,7 +49,6 @@ public class TblHelperWorkTime extends BaseEntity { private Boolean negotiation; @Column(name = "hwt_work_term") - @Convert(converter = StringListConverter.class) @Comment("근무 가능 기간") - private List workTerm = new ArrayList<>(); + private String workTerm; } diff --git a/src/main/java/com/balybus/galaxy/careAssistant/dto/request/HelperCompleteProfileDTO.java b/src/main/java/com/balybus/galaxy/careAssistant/dto/request/HelperCompleteProfileDTO.java new file mode 100644 index 00000000..33de29df --- /dev/null +++ b/src/main/java/com/balybus/galaxy/careAssistant/dto/request/HelperCompleteProfileDTO.java @@ -0,0 +1,54 @@ +package com.balybus.galaxy.careAssistant.dto.request; + +import com.balybus.galaxy.login.classic.dto.request.HelperCertDTO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class HelperCompleteProfileDTO { + // 프로필 정보 + private String introduce; + private Boolean careExperience; + + // 자격증 정보 + private List certificates; + private Boolean verifyQnet; // Q-net 검증 여부 (기본값: false) + + // 급여 정보 + private Integer wage; + private Integer wageState; + private Boolean wageNegotiation; + + // 근무 희망 지역 + private List addressFirstIds; + private List addressSecondIds; + private List addressThirdIds; + + // 근무 가능 시간 + private List workTimes; + private Boolean negotiation; // 시간 협의 가능 여부 + private Integer workTerm; // 근무 기간 + + // 경력 정보 + private HelperExperienceDTO experience; + + // 돌봄 서비스 정보 + private Integer careLevel; + private Integer inmateState; + private Integer workType; + private Integer careGender; + private Integer serviceMeal; + private Integer serviceMobility; + private Integer serviceDaily; + + // 기타 정보 + private Boolean carOwnYn; + private Boolean eduYn; +} \ No newline at end of file diff --git a/src/main/java/com/balybus/galaxy/careAssistant/dto/response/HelperCompleteProfileResponse.java b/src/main/java/com/balybus/galaxy/careAssistant/dto/response/HelperCompleteProfileResponse.java new file mode 100644 index 00000000..1ba2a1fb --- /dev/null +++ b/src/main/java/com/balybus/galaxy/careAssistant/dto/response/HelperCompleteProfileResponse.java @@ -0,0 +1,43 @@ +package com.balybus.galaxy.careAssistant.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.Map; + +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class HelperCompleteProfileResponse { + private boolean success; + private String message; + + // 각 섹션별 처리 결과 + private SectionResult profileResult; + private SectionResult certificateResult; + private SectionResult wageResult; + private SectionResult locationResult; + private SectionResult workTimeResult; + private SectionResult experienceResult; + private SectionResult careServiceResult; + + // 자격증 검증 결과 (Q-net 검증한 경우) + private Map certificateVerificationResults; + + // 전체 업데이트된 프로필 정보 + private HelperResponse updatedProfile; + + @Getter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class SectionResult { + private boolean updated; + private boolean success; + private String message; + private String error; + } +} \ No newline at end of file diff --git a/src/main/java/com/balybus/galaxy/careAssistant/dto/response/HelperResponse.java b/src/main/java/com/balybus/galaxy/careAssistant/dto/response/HelperResponse.java index fb949cea..1cf75695 100644 --- a/src/main/java/com/balybus/galaxy/careAssistant/dto/response/HelperResponse.java +++ b/src/main/java/com/balybus/galaxy/careAssistant/dto/response/HelperResponse.java @@ -1,6 +1,12 @@ package com.balybus.galaxy.careAssistant.dto.response; +import com.balybus.galaxy.careAssistant.domain.TblHelperWorkLocation; +import com.balybus.galaxy.careAssistant.domain.TblHelperWorkTime; +import com.balybus.galaxy.global.domain.tblAddressFirst.TblAddressFirst; +import com.balybus.galaxy.global.domain.tblAddressSecond.TblAddressSecond; +import com.balybus.galaxy.global.domain.tblAddressThird.TblAddressThird; import com.balybus.galaxy.login.classic.dto.request.HelperCertDTO; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -18,11 +24,24 @@ public class HelperResponse { private String name; private String phone; private String addressDetail; - private List certificates; - private boolean carOwnYn; - private boolean eduYn; - private Integer wage; - private Integer wageState; - private String introduce; - private Boolean careExperience; + private ImageDto img; // 프로필 사진 + private List helperWorkLocation; // 선호 지역 + private List helperWorkTime; // 근무 가능일정 + + private int careLevel; // 장기 요양 등급 + private int inmateState; // 동거인 여부 + private int workType; // 근무 종류 + private int careGender; // 성별 + private int serviceMeal; // 식사 보조 + private int serviceMobility; // 이동 보조 + private int serviceDaily; // 일상 생활 + + private List certificates; // 자격증 번호 + private boolean carOwnYn; // 요양보호사 차량 소유 여부 + private boolean eduYn; // 치매 교육 이수 여부 + private Integer wage; // 급여 + private Integer wageState; // 시급, 일급, 주급 구분 + private String introduce; // 자기소개 + private Boolean careExperience; // 간병 경력 + private Boolean wageNegotiation; // 급여 협의 가능 여부 } diff --git a/src/main/java/com/balybus/galaxy/careAssistant/dto/response/HelperWorkLocationDto.java b/src/main/java/com/balybus/galaxy/careAssistant/dto/response/HelperWorkLocationDto.java new file mode 100644 index 00000000..442e9eac --- /dev/null +++ b/src/main/java/com/balybus/galaxy/careAssistant/dto/response/HelperWorkLocationDto.java @@ -0,0 +1,16 @@ +package com.balybus.galaxy.careAssistant.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HelperWorkLocationDto { + private Long afSeq; // 시.도 구분자 + private Long asSeq; // 시.군.구 구분자 + private Long atSeq; // 읍.면.동 구분자 +} \ No newline at end of file diff --git a/src/main/java/com/balybus/galaxy/careAssistant/dto/response/HelperWorkTimeDto.java b/src/main/java/com/balybus/galaxy/careAssistant/dto/response/HelperWorkTimeDto.java new file mode 100644 index 00000000..7f45b529 --- /dev/null +++ b/src/main/java/com/balybus/galaxy/careAssistant/dto/response/HelperWorkTimeDto.java @@ -0,0 +1,21 @@ +package com.balybus.galaxy.careAssistant.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HelperWorkTimeDto { + private Long id; + private Integer date; // 근무 가능 요일 + private String startTime; // 시작시간 + private String endTime; // 종료시간 + private Boolean negotiation; // 근무 협의 여부 + private String workTerm; // 근무 가능 기간 +} \ No newline at end of file diff --git a/src/main/java/com/balybus/galaxy/careAssistant/dto/response/ImageDto.java b/src/main/java/com/balybus/galaxy/careAssistant/dto/response/ImageDto.java new file mode 100644 index 00000000..9c7c28eb --- /dev/null +++ b/src/main/java/com/balybus/galaxy/careAssistant/dto/response/ImageDto.java @@ -0,0 +1,16 @@ +package com.balybus.galaxy.careAssistant.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ImageDto { + private Long id; + private String imgUuid; + private String imgOriginName; +} \ No newline at end of file diff --git a/src/main/java/com/balybus/galaxy/careAssistant/repository/HelperWorkLocationRepository.java b/src/main/java/com/balybus/galaxy/careAssistant/repository/HelperWorkLocationRepository.java index 77d965c5..66551df2 100644 --- a/src/main/java/com/balybus/galaxy/careAssistant/repository/HelperWorkLocationRepository.java +++ b/src/main/java/com/balybus/galaxy/careAssistant/repository/HelperWorkLocationRepository.java @@ -4,6 +4,9 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface HelperWorkLocationRepository extends JpaRepository { + List findByHelper_Id(Long helperId); } diff --git a/src/main/java/com/balybus/galaxy/careAssistant/service/HelperService.java b/src/main/java/com/balybus/galaxy/careAssistant/service/HelperService.java index 8c7e0fa5..31961809 100644 --- a/src/main/java/com/balybus/galaxy/careAssistant/service/HelperService.java +++ b/src/main/java/com/balybus/galaxy/careAssistant/service/HelperService.java @@ -19,4 +19,5 @@ public interface HelperService { Map saveCertificateByQNet(List helperCertDTO, UserDetails userDetails); String checkCertificate(String name, String birth, String certNo, String issueDate, String insideNo); HelperResponse getHelperDetail(HelperDetailDTO helperDetailDTO); + HelperCompleteProfileResponse updateCompleteProfile(UserDetails userDetails, HelperCompleteProfileDTO dto); } diff --git a/src/main/java/com/balybus/galaxy/careAssistant/service/HelperServiceImpl.java b/src/main/java/com/balybus/galaxy/careAssistant/service/HelperServiceImpl.java index 517a8eeb..ef54f440 100644 --- a/src/main/java/com/balybus/galaxy/careAssistant/service/HelperServiceImpl.java +++ b/src/main/java/com/balybus/galaxy/careAssistant/service/HelperServiceImpl.java @@ -11,7 +11,10 @@ import com.balybus.galaxy.careAssistant.domain.*; import com.balybus.galaxy.careAssistant.dto.request.*; import com.balybus.galaxy.careAssistant.dto.response.*; +import com.balybus.galaxy.careAssistant.dto.response.HelperCompleteProfileResponse.SectionResult; import com.balybus.galaxy.careAssistant.repository.*; +import com.balybus.galaxy.careAssistant.dto.response.HelperWorkLocationDto; +import com.balybus.galaxy.careAssistant.dto.response.ImageDto; import com.balybus.galaxy.login.classic.dto.request.HelperCertDTO; import com.balybus.galaxy.member.domain.TblUser; import com.balybus.galaxy.member.repository.MemberRepository; @@ -84,12 +87,49 @@ public HelperResponse getAllHelperInfo(UserDetails userDetails) { .build()); } + // 선호 지역 + List workLocations = helperWorkLocationRepository.findByHelper_Id(tblHelper.getId()); + List helperWorkLocationDtos = workLocations.stream() + .map(wl -> HelperWorkLocationDto.builder() + .afSeq(wl.getTblAddressFirst().getId()) + .asSeq(wl.getTblAddressSecond().getId()) + .atSeq(wl.getTblAddressThird().getId()) + .build()) + .toList(); + + // 근무 가능 일정 + List workTimes = helperWorkTimeRepository.findByHelper(tblHelper); + List helperWorkTimeDtos = workTimes.stream() + .map(wt -> HelperWorkTimeDto.builder() + .id(wt.getId()) + .date(wt.getDate()) + .startTime(wt.getStartTime()) + .endTime(wt.getEndTime()) + .negotiation(wt.getNegotiation()) + .workTerm(wt.getWorkTerm()) + .build()) + .toList(); + return HelperResponse.builder() .id(tblHelper.getId()) .userEmail(tblUser.getEmail()) .name(tblHelper.getName()) .phone(tblHelper.getPhone()) .addressDetail(tblHelper.getAddressDetail()) + .img(tblHelper.getImg() != null ? ImageDto.builder() + .id(tblHelper.getImg().getId()) + .imgUuid(tblHelper.getImg().getImgUuid()) + .imgOriginName(tblHelper.getImg().getImgOriginName()) + .build() : null) + .helperWorkLocation(helperWorkLocationDtos) + .helperWorkTime(helperWorkTimeDtos) + .careLevel(tblHelper.getCareLevel()) + .inmateState(tblHelper.getInmateState()) + .workType(tblHelper.getWorkType()) + .careGender(tblHelper.getCareGender()) + .serviceMeal(tblHelper.getServiceMeal()) + .serviceMobility(tblHelper.getServiceMobility()) + .serviceDaily(tblHelper.getServiceDaily()) .certificates(certDTOList) .carOwnYn(tblHelper.isCarOwnYn()) .eduYn(tblHelper.isEduYn()) @@ -97,11 +137,11 @@ public HelperResponse getAllHelperInfo(UserDetails userDetails) { .wageState(tblHelper.getWageState()) .introduce(tblHelper.getIntroduce()) .careExperience(tblHelper.getIs_experienced()) + .wageNegotiation(tblHelper.getWageNegotiation()) .build(); } @Override - @Transactional public Map updateProfile(UserDetails userDetails, HelperProfileDTO helperProfileDTO) { String username = userDetails.getUsername(); @@ -125,56 +165,9 @@ public Map updateProfile(UserDetails userDetails, HelperProfileD profileUpdated = true; } - // 자격증 정보가 있는 경우에만 업데이트 + // 자격증 정보가 있는 경우에만 업데이트 (Q-net 검증 없이) if (helperProfileDTO.getCertificates() != null && !helperProfileDTO.getCertificates().isEmpty()) { - List certificates = helperCertRepository.findByTblHelperId(tblHelper.getId()); - - for (HelperCertDTO helperCertDTO : helperProfileDTO.getCertificates()) { - // 자격증 형식 검증 - validateCertificateFormat(helperCertDTO); - - // Q-net 인증 시도 - String qnetResult = checkCertificate( - tblHelper.getName(), - tblHelper.getBirthday(), - helperCertDTO.getCertNum(), - String.valueOf(helperCertDTO.getCertDateIssue()), - String.valueOf(helperCertDTO.getCertSerialNum()) - ); - - certificateResults.put(helperCertDTO.getCertName(), qnetResult); - - // 기존 자격증 찾아서 업데이트 - TblHelperCert existingCert = certificates.stream() - .filter(cert -> helperCertDTO.getCertName().equals(cert.getCertName())) - .findFirst() - .orElse(null); - - if (existingCert != null) { - // 기존 자격증 업데이트 - existingCert.setCertNum(helperCertDTO.getCertNum()); - existingCert.setCertDateIssue(helperCertDTO.getCertDateIssue()); - existingCert.setCertSerialNum(helperCertDTO.getCertSerialNum()); - } else { - // 새로운 자격증 추가 - TblHelperCert newCert = TblHelperCert.builder() - .tblHelper(tblHelper) - .certName(helperCertDTO.getCertName()) - .certNum(helperCertDTO.getCertNum()) - .certDateIssue(helperCertDTO.getCertDateIssue()) - .certSerialNum(helperCertDTO.getCertSerialNum()) - .build(); - certificates.add(newCert); - } - - // Q-net 인증 실패 시 로그 기록 (fallback으로 DB에는 저장) - if (!"VALID".equals(qnetResult)) { - log.warn("Q-net 자격증 인증 실패 - 자격증명: {}, 번호: {}, 결과: {}", - helperCertDTO.getCertName(), helperCertDTO.getCertNum(), qnetResult); - } - } - - helperCertRepository.saveAll(certificates); + certificateResults = processCertificates(helperProfileDTO.getCertificates(), tblHelper, false); } helperRepository.save(tblHelper); @@ -272,7 +265,7 @@ public HelperWorkTimeResponse workTimeSignUp(HelperWorkTimeRequestDTO helperWork .startTime(dto.getStartTime()) .endTime(dto.getEndTime()) .negotiation(helperWorkTimeRequestDTO.getNegotiation()) - .workTerm(helperWorkTimeRequestDTO.getWorkTerm()) + .workTerm(helperWorkTimeRequestDTO.getWorkTerm().toString()) .build()) .collect(Collectors.toList()); @@ -421,31 +414,14 @@ else if(totalYears < 1) { List workTimes = helperWorkTimeRepository.findByHelper(helper); if (!workTimes.isEmpty() && !helperSearchDTO.getTerms().isEmpty()) { - long size = workTimes.size(); - long maxWorkTermValue = 0; - for(int i=0; i saveCertificateByQNet(List helperCertDTO, UserDetails userDetails) { TblUser tblUser = memberRepository.findByEmail(userDetails.getUsername()) .orElseThrow(() -> new BadRequestException(MEMBER_NOT_FOUND)); @@ -481,59 +456,8 @@ public Map saveCertificateByQNet(List helperCertD TblHelper tblHelper = helperRepository.findByUserId(tblUser.getId()) .orElseThrow(() -> new BadRequestException(NOT_FOUND_HELPER)); - List certificates = helperCertRepository.findByTblHelperId(tblHelper.getId()); - Map answers = new HashMap<>(); - - for(HelperCertDTO helperCert : helperCertDTO) { - // 자격증 형식 검증 - validateCertificateFormat(helperCert); - - // Q-net 인증 시도 - String qnetResult = checkCertificate( - tblHelper.getName(), - tblHelper.getBirthday(), - helperCert.getCertNum(), - String.valueOf(helperCert.getCertDateIssue()), - String.valueOf(helperCert.getCertSerialNum()) - ); - - answers.put(helperCert.getCertName(), qnetResult); - - // Q-net 인증 결과와 상관없이 DB에 저장 (fallback 로직) - // 기존 자격증 찾기 - TblHelperCert existingCert = certificates.stream() - .filter(cert -> helperCert.getCertName().equals(cert.getCertName())) - .findFirst() - .orElse(null); - - if (existingCert != null) { - // 기존 자격증 업데이트 - existingCert.setCertNum(helperCert.getCertNum()); - existingCert.setCertDateIssue(helperCert.getCertDateIssue()); - existingCert.setCertSerialNum(helperCert.getCertSerialNum()); - } else { - // 새로운 자격증 추가 - TblHelperCert newCert = TblHelperCert.builder() - .tblHelper(tblHelper) - .certName(helperCert.getCertName()) - .certNum(helperCert.getCertNum()) - .certDateIssue(helperCert.getCertDateIssue()) - .certSerialNum(helperCert.getCertSerialNum()) - .build(); - certificates.add(newCert); - } - - // Q-net 인증 실패 시 로그 기록 - if (!"VALID".equals(qnetResult)) { - log.warn("Q-net 자격증 인증 실패하였으나 DB에 저장 - 자격증명: {}, 번호: {}, 결과: {}", - helperCert.getCertName(), helperCert.getCertNum(), qnetResult); - } - } - - // 자격증 정보 DB에 저장 (Q-net 결과와 무관하게) - helperCertRepository.saveAll(certificates); - - return answers; + // processCertificates 메서드를 사용하여 처리 (Q-net 검증 포함) + return processCertificates(helperCertDTO, tblHelper, true); } @@ -640,6 +564,387 @@ private void validateCertificateFormat(HelperCertDTO helperCertDTO) { } } + /** + * 통합 프로필 업데이트 메서드 + * @param userDetails 현재 로그인한 사용자 정보 + * @param dto 통합 프로필 업데이트 DTO + * @return 업데이트 결과 + */ + @Transactional + public HelperCompleteProfileResponse updateCompleteProfile(UserDetails userDetails, HelperCompleteProfileDTO dto) { + String username = userDetails.getUsername(); + + TblUser tblUser = memberRepository.findByEmail(username) + .orElseThrow(() -> new BadRequestException(MEMBER_NOT_FOUND)); + + TblHelper tblHelper = helperRepository.findByUserId(tblUser.getId()) + .orElseThrow(() -> new BadRequestException(NOT_FOUND_HELPER)); + + HelperCompleteProfileResponse.HelperCompleteProfileResponseBuilder responseBuilder = + HelperCompleteProfileResponse.builder() + .success(true) + .message("프로필이 성공적으로 업데이트되었습니다."); + + // 1. 기본 프로필 정보 업데이트 + SectionResult profileResult = updateBasicProfile(tblHelper, dto); + responseBuilder.profileResult(profileResult); + + // 2. 자격증 정보 업데이트 + if (dto.getCertificates() != null && !dto.getCertificates().isEmpty()) { + boolean verifyQnet = dto.getVerifyQnet() != null ? dto.getVerifyQnet() : false; + Map certResults = processCertificates(dto.getCertificates(), tblHelper, verifyQnet); + + SectionResult certResult = SectionResult.builder() + .updated(true) + .success(true) + .message("자격증 정보가 업데이트되었습니다.") + .build(); + + responseBuilder.certificateResult(certResult) + .certificateVerificationResults(certResults); + } + + // 3. 급여 정보 업데이트 + if (dto.getWage() != null || dto.getWageState() != null || dto.getWageNegotiation() != null) { + SectionResult wageResult = updateWageInfo(tblHelper, dto); + responseBuilder.wageResult(wageResult); + } + + // 4. 근무 희망 지역 업데이트 + if (dto.getAddressFirstIds() != null && dto.getAddressSecondIds() != null && dto.getAddressThirdIds() != null) { + SectionResult locationResult = updateWorkLocation(tblHelper, dto); + responseBuilder.locationResult(locationResult); + } + + // 5. 근무 가능 시간 업데이트 + if (dto.getWorkTimes() != null && !dto.getWorkTimes().isEmpty()) { + SectionResult workTimeResult = updateWorkTime(tblHelper, dto); + responseBuilder.workTimeResult(workTimeResult); + } + + // 6. 경력 정보 업데이트 + if (dto.getExperience() != null) { + SectionResult experienceResult = updateExperience(tblHelper, dto.getExperience()); + responseBuilder.experienceResult(experienceResult); + } + + // 7. 돌봄 서비스 정보 업데이트 + SectionResult careServiceResult = updateCareService(tblHelper, dto); + responseBuilder.careServiceResult(careServiceResult); + + // Helper 엔티티 저장 + helperRepository.save(tblHelper); + + // 업데이트된 전체 프로필 정보 조회 + HelperResponse updatedProfile = getAllHelperInfo(userDetails); + responseBuilder.updatedProfile(updatedProfile); + + return responseBuilder.build(); + } + + private SectionResult updateBasicProfile(TblHelper tblHelper, HelperCompleteProfileDTO dto) { + boolean updated = false; + + if (dto.getIntroduce() != null) { + tblHelper.setIntroduce(dto.getIntroduce()); + updated = true; + } + + if (dto.getCareExperience() != null) { + tblHelper.setIs_experienced(dto.getCareExperience()); + updated = true; + } + + if (dto.getCarOwnYn() != null) { + tblHelper.setCarOwnYn(dto.getCarOwnYn()); + updated = true; + } + + if (dto.getEduYn() != null) { + tblHelper.setEduYn(dto.getEduYn()); + updated = true; + } + + return SectionResult.builder() + .updated(updated) + .success(true) + .message(updated ? "기본 프로필이 업데이트되었습니다." : "업데이트할 프로필 정보가 없습니다.") + .build(); + } + + private SectionResult updateWageInfo(TblHelper tblHelper, HelperCompleteProfileDTO dto) { + boolean updated = false; + + if (dto.getWage() != null) { + tblHelper.setWage(dto.getWage()); + updated = true; + } + + if (dto.getWageState() != null) { + tblHelper.setWageState(dto.getWageState()); + updated = true; + } + + if (dto.getWageNegotiation() != null) { + tblHelper.setWageNegotiation(dto.getWageNegotiation()); + updated = true; + } + + return SectionResult.builder() + .updated(updated) + .success(true) + .message(updated ? "급여 정보가 업데이트되었습니다." : "업데이트할 급여 정보가 없습니다.") + .build(); + } + + private SectionResult updateWorkLocation(TblHelper tblHelper, HelperCompleteProfileDTO dto) { + try { + // 기존 근무 희망 지역 삭제 + List existingLocations = helperWorkLocationRepository.findByHelper_Id(tblHelper.getId()); + helperWorkLocationRepository.deleteAll(existingLocations); + + // 새로운 근무 희망 지역 추가 + List addressFirstList = tblAddressFirstRepository.findAllById(dto.getAddressFirstIds()); + List addressSecondList = tblAddressSecondRepository.findAllById(dto.getAddressSecondIds()); + List addressThirdList = tblAddressThirdRepository.findAllById(dto.getAddressThirdIds()); + + if (addressFirstList.isEmpty() || addressSecondList.isEmpty() || addressThirdList.isEmpty()) { + return SectionResult.builder() + .updated(false) + .success(false) + .error("유효하지 않은 주소 정보입니다.") + .build(); + } + + List workLocations = new ArrayList<>(); + for (int i = 0; i < Math.min(addressFirstList.size(), Math.min(addressSecondList.size(), addressThirdList.size())); i++) { + TblHelperWorkLocation location = TblHelperWorkLocation.builder() + .helper(tblHelper) + .tblAddressFirst(addressFirstList.get(i)) + .tblAddressSecond(addressSecondList.get(i)) + .tblAddressThird(addressThirdList.get(i)) + .build(); + workLocations.add(location); + } + + helperWorkLocationRepository.saveAll(workLocations); + + return SectionResult.builder() + .updated(true) + .success(true) + .message("근무 희망 지역이 업데이트되었습니다.") + .build(); + } catch (Exception e) { + return SectionResult.builder() + .updated(false) + .success(false) + .error("근무 희망 지역 업데이트 중 오류가 발생했습니다: " + e.getMessage()) + .build(); + } + } + + private SectionResult updateWorkTime(TblHelper tblHelper, HelperCompleteProfileDTO dto) { + try { + List newWorkTimes = dto.getWorkTimes().stream() + .filter(wt -> !helperWorkTimeRepository.existsByHelperAndDateAndStartTimeAndEndTime( + tblHelper, wt.getDay(), wt.getStartTime(), wt.getEndTime())) + .map(wt -> TblHelperWorkTime.builder() + .helper(tblHelper) + .date(wt.getDay()) + .startTime(wt.getStartTime()) + .endTime(wt.getEndTime()) + .negotiation(dto.getNegotiation()) + .workTerm(dto.getWorkTerm() != null ? dto.getWorkTerm().toString() : null) + .build()) + .collect(Collectors.toList()); + + if (!newWorkTimes.isEmpty()) { + helperWorkTimeRepository.saveAll(newWorkTimes); + return SectionResult.builder() + .updated(true) + .success(true) + .message("근무 가능 시간이 업데이트되었습니다.") + .build(); + } + + return SectionResult.builder() + .updated(false) + .success(true) + .message("추가할 새로운 근무 시간이 없습니다.") + .build(); + } catch (Exception e) { + return SectionResult.builder() + .updated(false) + .success(false) + .error("근무 가능 시간 업데이트 중 오류가 발생했습니다: " + e.getMessage()) + .build(); + } + } + + private SectionResult updateExperience(TblHelper tblHelper, HelperExperienceDTO experienceDTO) { + try { + if (HelperExperienceDTO.hasNullHelperExperienceRequestDTO(experienceDTO)) { + return SectionResult.builder() + .updated(false) + .success(false) + .error("경력 정보가 불완전합니다.") + .build(); + } + + int countExperience = helperExperienceRepository.countByHelperAndFieldAndHeStartDateAndHeEndDate( + tblHelper, + experienceDTO.getField(), + experienceDTO.getHeStartDate(), + experienceDTO.getHeEndDate() + ); + + if (countExperience > 0) { + return SectionResult.builder() + .updated(false) + .success(false) + .error("이미 등록된 경력입니다.") + .build(); + } + + TblHelperExperience experience = TblHelperExperience.builder() + .helper(tblHelper) + .field(experienceDTO.getField()) + .heStartDate(experienceDTO.getHeStartDate()) + .heEndDate(experienceDTO.getHeEndDate()) + .build(); + + helperExperienceRepository.save(experience); + tblHelper.setIs_experienced(true); + + return SectionResult.builder() + .updated(true) + .success(true) + .message("경력 정보가 업데이트되었습니다.") + .build(); + } catch (Exception e) { + return SectionResult.builder() + .updated(false) + .success(false) + .error("경력 정보 업데이트 중 오류가 발생했습니다: " + e.getMessage()) + .build(); + } + } + + private SectionResult updateCareService(TblHelper tblHelper, HelperCompleteProfileDTO dto) { + boolean updated = false; + + if (dto.getCareLevel() != null) { + tblHelper.setCareLevel(dto.getCareLevel()); + updated = true; + } + + if (dto.getInmateState() != null) { + tblHelper.setInmateState(dto.getInmateState()); + updated = true; + } + + if (dto.getWorkType() != null) { + tblHelper.setWorkType(dto.getWorkType()); + updated = true; + } + + if (dto.getCareGender() != null) { + tblHelper.setCareGender(dto.getCareGender()); + updated = true; + } + + if (dto.getServiceMeal() != null) { + tblHelper.setServiceMeal(dto.getServiceMeal()); + updated = true; + } + + if (dto.getServiceMobility() != null) { + tblHelper.setServiceMobility(dto.getServiceMobility()); + updated = true; + } + + if (dto.getServiceDaily() != null) { + tblHelper.setServiceDaily(dto.getServiceDaily()); + updated = true; + } + + return SectionResult.builder() + .updated(updated) + .success(true) + .message(updated ? "돌봄 서비스 정보가 업데이트되었습니다." : "업데이트할 돌봄 서비스 정보가 없습니다.") + .build(); + } + + /** + * 자격증 처리를 위한 private 메서드 + * @param certificates 처리할 자격증 리스트 + * @param tblHelper 요양보호사 엔티티 + * @param verifyQnet Q-net 검증 여부 + * @return 처리 결과 Map + */ + private Map processCertificates(List certificates, TblHelper tblHelper, boolean verifyQnet) { + List existingCerts = helperCertRepository.findByTblHelperId(tblHelper.getId()); + Map results = new HashMap<>(); + + for (HelperCertDTO certDTO : certificates) { + try { + // 자격증 형식 검증 + validateCertificateFormat(certDTO); + + String verificationResult = "NOT_VERIFIED"; + + // Q-net 검증이 요청된 경우에만 수행 + if (verifyQnet) { + verificationResult = checkCertificate( + tblHelper.getName(), + tblHelper.getBirthday(), + certDTO.getCertNum(), + String.valueOf(certDTO.getCertDateIssue()), + String.valueOf(certDTO.getCertSerialNum()) + ); + } + + results.put(certDTO.getCertName(), verificationResult); + + // 기존 자격증 찾기 + TblHelperCert existingCert = existingCerts.stream() + .filter(cert -> certDTO.getCertName().equals(cert.getCertName())) + .findFirst() + .orElse(null); + + if (existingCert != null) { + // 기존 자격증 업데이트 + existingCert.setCertNum(certDTO.getCertNum()); + existingCert.setCertDateIssue(certDTO.getCertDateIssue()); + existingCert.setCertSerialNum(certDTO.getCertSerialNum()); + } else { + // 새로운 자격증 추가 + TblHelperCert newCert = TblHelperCert.builder() + .tblHelper(tblHelper) + .certName(certDTO.getCertName()) + .certNum(certDTO.getCertNum()) + .certDateIssue(certDTO.getCertDateIssue()) + .certSerialNum(certDTO.getCertSerialNum()) + .build(); + existingCerts.add(newCert); + } + + if (verifyQnet && !"VALID".equals(verificationResult)) { + log.warn("Q-net 자격증 인증 실패 - 자격증명: {}, 번호: {}, 결과: {}", + certDTO.getCertName(), certDTO.getCertNum(), verificationResult); + } + } catch (BadRequestException e) { + results.put(certDTO.getCertName(), "VALIDATION_FAILED: " + e.getMessage()); + log.error("자격증 검증 실패 - 자격증명: {}, 오류: {}", certDTO.getCertName(), e.getMessage()); + } + } + + // 자격증 정보 DB에 저장 + helperCertRepository.saveAll(existingCerts); + + return results; + } + @Override public HelperResponse getHelperDetail(HelperDetailDTO helperDetailDTO) { TblHelper helper = helperRepository.findById(helperDetailDTO.getHelperSeq()) @@ -659,12 +964,49 @@ public HelperResponse getHelperDetail(HelperDetailDTO helperDetailDTO) { .build()); } + // 선호 지역 + List workLocations = helperWorkLocationRepository.findByHelper_Id(helper.getId()); + List helperWorkLocationDtos = workLocations.stream() + .map(wl -> HelperWorkLocationDto.builder() + .afSeq(wl.getTblAddressFirst().getId()) + .asSeq(wl.getTblAddressSecond().getId()) + .atSeq(wl.getTblAddressThird().getId()) + .build()) + .toList(); + + // 근무 가능 일정 + List workTimes = helperWorkTimeRepository.findByHelper(helper); + List helperWorkTimeDtos = workTimes.stream() + .map(wt -> HelperWorkTimeDto.builder() + .id(wt.getId()) + .date(wt.getDate()) + .startTime(wt.getStartTime()) + .endTime(wt.getEndTime()) + .negotiation(wt.getNegotiation()) + .workTerm(wt.getWorkTerm()) + .build()) + .toList(); + return HelperResponse.builder() .id(helper.getId()) .userEmail(user.getEmail()) .name(helper.getName()) .phone(helper.getPhone()) .addressDetail(helper.getAddressDetail()) + .img(helper.getImg() != null ? ImageDto.builder() + .id(helper.getImg().getId()) + .imgUuid(helper.getImg().getImgUuid()) + .imgOriginName(helper.getImg().getImgOriginName()) + .build() : null) + .helperWorkLocation(helperWorkLocationDtos) + .helperWorkTime(helperWorkTimeDtos) + .careLevel(helper.getCareLevel()) + .inmateState(helper.getInmateState()) + .workType(helper.getWorkType()) + .careGender(helper.getCareGender()) + .serviceMeal(helper.getServiceMeal()) + .serviceMobility(helper.getServiceMobility()) + .serviceDaily(helper.getServiceDaily()) .certificates(certDTOList) .carOwnYn(helper.isCarOwnYn()) .eduYn(helper.isEduYn()) @@ -672,8 +1014,7 @@ public HelperResponse getHelperDetail(HelperDetailDTO helperDetailDTO) { .wageState(helper.getWageState()) .introduce(helper.getIntroduce()) .careExperience(helper.getIs_experienced()) + .wageNegotiation(helper.getWageNegotiation()) .build(); } - - } \ No newline at end of file diff --git a/src/main/java/com/balybus/galaxy/global/config/SecurityConfig.java b/src/main/java/com/balybus/galaxy/global/config/SecurityConfig.java index fd96f783..29ba6ac6 100644 --- a/src/main/java/com/balybus/galaxy/global/config/SecurityConfig.java +++ b/src/main/java/com/balybus/galaxy/global/config/SecurityConfig.java @@ -41,7 +41,9 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .requestMatchers("/page/**").permitAll() .requestMatchers("/api/update-profile").permitAll() .requestMatchers("/api/sign/**","/api/sign-up/**").permitAll() - .requestMatchers("/api/token/**", "/api/oauth/kakao-signup").permitAll() + .requestMatchers("/api/token/**").permitAll() + .requestMatchers("/login/oauth2/code/**").permitAll() // OAuth callback 엔드포인트 허용 + .requestMatchers("/api/oauth/**").permitAll() // OAuth API 엔드포인트 허용 .requestMatchers("/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**").permitAll() .requestMatchers("/img/**", "/css/**", "/static/js/**", "/docs/**").permitAll() .requestMatchers("/api/**").authenticated() diff --git a/src/main/java/com/balybus/galaxy/global/config/jwt/JwtAuthenticationFilter.java b/src/main/java/com/balybus/galaxy/global/config/jwt/JwtAuthenticationFilter.java index 5610bfe2..8bd5eb16 100644 --- a/src/main/java/com/balybus/galaxy/global/config/jwt/JwtAuthenticationFilter.java +++ b/src/main/java/com/balybus/galaxy/global/config/jwt/JwtAuthenticationFilter.java @@ -48,6 +48,11 @@ private boolean checkUri(String requestUri) { return false; } + // 1. 엑세스, 리프레시 토큰 발급 + // 2. 로그인 회원가입 로직 + // 3. 권한 ROLE -> enum + // 4. + // 쿠키 사용 @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { diff --git a/src/main/java/com/balybus/galaxy/global/domain/tblAddressSecond/TblAddressSecond.java b/src/main/java/com/balybus/galaxy/global/domain/tblAddressSecond/TblAddressSecond.java index 2f2b8a88..7d641792 100644 --- a/src/main/java/com/balybus/galaxy/global/domain/tblAddressSecond/TblAddressSecond.java +++ b/src/main/java/com/balybus/galaxy/global/domain/tblAddressSecond/TblAddressSecond.java @@ -1,6 +1,7 @@ package com.balybus.galaxy.global.domain.tblAddressSecond; import com.balybus.galaxy.global.domain.tblAddressFirst.TblAddressFirst; +import com.fasterxml.jackson.annotation.JsonBackReference; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; @@ -23,6 +24,7 @@ public class TblAddressSecond { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "af_seq", nullable = false) @Comment("시.도 구분자") + @JsonBackReference private TblAddressFirst addressFirst; @Column(name = "as_name", nullable = false, length = 50) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 234ff52b..6bb6d740 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,7 +1,7 @@ spring: profiles: active: dev - include: db, oauth, jwt, mail, aws, oauth + include: db, oauth, jwt, mail, aws springdoc: swagger-ui: