diff --git a/src/main/java/young/blaybus/domain/job_search/JobSearch.java b/src/main/java/young/blaybus/domain/job_search/JobSearch.java index 69cd20f..90541c4 100644 --- a/src/main/java/young/blaybus/domain/job_search/JobSearch.java +++ b/src/main/java/young/blaybus/domain/job_search/JobSearch.java @@ -17,9 +17,10 @@ import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import young.blaybus.domain.job_search.request.JobSearchAreaRequest; +import young.blaybus.domain.job_search.request.JobSearchTimeSlotRequest; import young.blaybus.domain.job_search.request.UpdateJobSearchRequest; import young.blaybus.domain.member.Member; -import young.blaybus.util.enums.DayOfWeek; + @Entity @Builder @@ -33,12 +34,6 @@ public class JobSearch { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Comment("시작 시간") - private LocalTime startTime; - - @Comment("종료 시간") - private LocalTime endTime; - @Comment("희망 급여") private Integer salary; @@ -59,16 +54,9 @@ public class JobSearch { @Builder.Default @OneToMany(mappedBy = "jobSearch", cascade = CascadeType.ALL, orphanRemoval = true) - private List dayList = new ArrayList<>(); + private List timeSlots = new ArrayList<>(); public void updateFromDto(UpdateJobSearchRequest request){ - if(request.startTime()!=null) { - this.startTime = LocalTime.parse(request.startTime()); - } - - if(request.endTime()!=null) { - this.endTime = LocalTime.parse(request.endTime()); - } if(request.salary()!=null) { this.salary = request.salary(); @@ -84,12 +72,14 @@ public void updateFromDto(UpdateJobSearchRequest request){ } } - if(!request.dayList().isEmpty()) { - this.dayList.clear(); - for (DayOfWeek dayOfWeek : request.dayList()) { - this.dayList.add(JobSearchDay.builder() + if (!request.timeSlots().isEmpty()) { + this.timeSlots.clear(); + for (JobSearchTimeSlotRequest timeSlot : request.timeSlots()) { + this.timeSlots.add(JobSearchTimeSlot.builder() .jobSearch(this) - .day(dayOfWeek) + .day(timeSlot.day()) + .startTime(LocalTime.parse(timeSlot.startTime())) + .endTime(LocalTime.parse(timeSlot.endTime())) .build()); } } diff --git a/src/main/java/young/blaybus/domain/job_search/JobSearchTimeSlot.java b/src/main/java/young/blaybus/domain/job_search/JobSearchTimeSlot.java new file mode 100644 index 0000000..12da48e --- /dev/null +++ b/src/main/java/young/blaybus/domain/job_search/JobSearchTimeSlot.java @@ -0,0 +1,33 @@ +package young.blaybus.domain.job_search; + +import jakarta.persistence.*; +import lombok.*; +import org.hibernate.annotations.Comment; +import young.blaybus.util.enums.DayOfWeek; + +import java.time.LocalTime; + +@Entity +@Builder +@Getter +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class JobSearchTimeSlot { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Enumerated(EnumType.STRING) + private DayOfWeek day; + + @Comment("시작 시간") + private LocalTime startTime; + + @Comment("종료 시간") + private LocalTime endTime; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "job_search_id") + private JobSearch jobSearch; +} \ No newline at end of file diff --git a/src/main/java/young/blaybus/domain/job_search/request/CreateJobSearchRequest.java b/src/main/java/young/blaybus/domain/job_search/request/CreateJobSearchRequest.java index e7ec9fe..42bfcd0 100644 --- a/src/main/java/young/blaybus/domain/job_search/request/CreateJobSearchRequest.java +++ b/src/main/java/young/blaybus/domain/job_search/request/CreateJobSearchRequest.java @@ -11,19 +11,13 @@ public record CreateJobSearchRequest( @Schema(description = "회원 ID") String memberId, - @Schema(description = "근무 시작 시간 (09:30)") - String startTime, - - @Schema(description = "근무 종료 시간(18:00)") - String endTime, - @Schema(description = "희망 시급") Integer salary, @Schema(description = "근무 가능 지역") List jobSearchAreas, - @Schema(description = "근무 가능 요일 목록") - List dayList + @Schema(description = "근무 가능 요일&시간대 목록") + List timeSlots ) { } diff --git a/src/main/java/young/blaybus/domain/job_search/request/JobSearchTimeSlotRequest.java b/src/main/java/young/blaybus/domain/job_search/request/JobSearchTimeSlotRequest.java new file mode 100644 index 0000000..9563492 --- /dev/null +++ b/src/main/java/young/blaybus/domain/job_search/request/JobSearchTimeSlotRequest.java @@ -0,0 +1,17 @@ +package young.blaybus.domain.job_search.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import young.blaybus.util.enums.DayOfWeek; + +@Schema(description = "근무 가능 시간대 요청 객체") +public record JobSearchTimeSlotRequest( + @Schema(description = "근무 요일") + DayOfWeek day, + + @Schema(description = "근무 시작 시간 (HH:mm)") + String startTime, + + @Schema(description = "근무 종료 시간 (HH:mm)") + String endTime +){ +} diff --git a/src/main/java/young/blaybus/domain/job_search/request/UpdateJobSearchRequest.java b/src/main/java/young/blaybus/domain/job_search/request/UpdateJobSearchRequest.java index 5667810..a12cc68 100644 --- a/src/main/java/young/blaybus/domain/job_search/request/UpdateJobSearchRequest.java +++ b/src/main/java/young/blaybus/domain/job_search/request/UpdateJobSearchRequest.java @@ -7,11 +7,6 @@ @Schema(description = "구직 정보 수정 객체") public record UpdateJobSearchRequest( - @Schema(description = "근무 시작 시간 (09:30)") - String startTime, - - @Schema(description = "근무 종료 시간(18:00)") - String endTime, @Schema(description = "희망 시급") Integer salary, @@ -20,6 +15,9 @@ public record UpdateJobSearchRequest( List jobSearchAreas, @Schema(description = "근무 가능 요일 목록") - List dayList + List dayList, + + @Schema(description = "근무 가능 요일&시간대 목록") + List timeSlots ) { } diff --git a/src/main/java/young/blaybus/domain/job_search/response/DetailJobSearchResponse.java b/src/main/java/young/blaybus/domain/job_search/response/DetailJobSearchResponse.java index 917c30e..9436e7c 100644 --- a/src/main/java/young/blaybus/domain/job_search/response/DetailJobSearchResponse.java +++ b/src/main/java/young/blaybus/domain/job_search/response/DetailJobSearchResponse.java @@ -16,11 +16,6 @@ public class DetailJobSearchResponse { @Schema(description = "구직 정보 ID") private Long jobSearchId; - @Schema(description = "근무 시작 시간") - private String startTime; - - @Schema(description = "근무 종료 시간") - private String endTime; @Schema(description = "희망 급여") private Integer salary; @@ -28,6 +23,6 @@ public class DetailJobSearchResponse { @Schema(description = "근무 가능 지역 목록") private List jobSearchAreas; - @Schema(description = "근무 가능 요일 목록") - private List dayList; + @Schema(description = "근무 가능 요일&시간대 목록") + private List timeSlots; } diff --git a/src/main/java/young/blaybus/domain/job_search/response/JobSearchTimeSlotResponse.java b/src/main/java/young/blaybus/domain/job_search/response/JobSearchTimeSlotResponse.java new file mode 100644 index 0000000..e00ef55 --- /dev/null +++ b/src/main/java/young/blaybus/domain/job_search/response/JobSearchTimeSlotResponse.java @@ -0,0 +1,24 @@ +package young.blaybus.domain.job_search.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import young.blaybus.util.enums.DayOfWeek; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Schema(description = "근무 가능 요일&시간대 응답 객체") +public class JobSearchTimeSlotResponse { + @Schema(description = "근무 요일") + DayOfWeek day; + + @Schema(description = "근무 시작 시간 (HH:mm)") + String startTime; + + @Schema(description = "근무 종료 시간 (HH:mm)") + String endTime; +} diff --git a/src/main/java/young/blaybus/domain/job_search/service/JobSearchService.java b/src/main/java/young/blaybus/domain/job_search/service/JobSearchService.java index 8ece5dc..7981ed6 100644 --- a/src/main/java/young/blaybus/domain/job_search/service/JobSearchService.java +++ b/src/main/java/young/blaybus/domain/job_search/service/JobSearchService.java @@ -6,15 +6,15 @@ import org.springframework.transaction.annotation.Transactional; import young.blaybus.domain.job_search.JobSearch; import young.blaybus.domain.job_search.JobSearchArea; -import young.blaybus.domain.job_search.JobSearchDay; +import young.blaybus.domain.job_search.JobSearchTimeSlot; import young.blaybus.domain.job_search.repository.JobSearchRepository; import young.blaybus.domain.job_search.request.CreateJobSearchRequest; import young.blaybus.domain.job_search.request.UpdateJobSearchRequest; import young.blaybus.domain.job_search.response.DetailJobSearchResponse; import young.blaybus.domain.job_search.response.JobSearchAreaResponse; +import young.blaybus.domain.job_search.response.JobSearchTimeSlotResponse; import young.blaybus.domain.member.Member; import young.blaybus.domain.member.repository.MemberRepository; -import young.blaybus.util.enums.DayOfWeek; import java.time.LocalDateTime; import java.time.LocalTime; @@ -35,28 +35,28 @@ public void createJobSearch(CreateJobSearchRequest jobSearchRequest) { JobSearch jobSearch = JobSearch.builder() .member(member) - .startTime(LocalTime.parse(jobSearchRequest.startTime())) - .endTime(LocalTime.parse(jobSearchRequest.endTime())) .salary(jobSearchRequest.salary()) .createdTime(LocalDateTime.now()) .build(); - List jobSearchAreas = jobSearchRequest.jobSearchAreas().stream() + List areas = jobSearchRequest.jobSearchAreas().stream() .map(areaRequest -> JobSearchArea.builder() .address(areaRequest.address()) .jobSearch(jobSearch) .build()) .toList(); - jobSearch.getJobSearchAreas().addAll(jobSearchAreas); + jobSearch.getJobSearchAreas().addAll(areas); - List jobSearchDays = jobSearchRequest.dayList().stream() - .map(day -> JobSearchDay.builder() - .day(day) + List timeSlots = jobSearchRequest.timeSlots().stream() + .map(slot -> JobSearchTimeSlot.builder() .jobSearch(jobSearch) + .day(slot.day()) + .startTime(LocalTime.parse(slot.startTime())) + .endTime(LocalTime.parse(slot.endTime())) .build()) .toList(); - jobSearch.getDayList().addAll(jobSearchDays); + jobSearch.getTimeSlots().addAll(timeSlots); jobSearchRepository.save(jobSearch); } @@ -76,22 +76,24 @@ public DetailJobSearchResponse getJobSearch(String memberId) { JobSearch jobSearch= jobSearchRepository.findByMemberId(memberId) .orElseThrow(()->new IllegalArgumentException("해당 회원의 구직 정보가 존재하지 않습니다.")); - List jobSearchAreaResponses = jobSearch.getJobSearchAreas().stream().map(jobSearchArea -> + List areaResponses = jobSearch.getJobSearchAreas().stream().map(jobSearchArea -> JobSearchAreaResponse.builder().address(jobSearchArea.getAddress()).build() ).toList(); - List dayList = jobSearch.getDayList().stream() - .map(JobSearchDay::getDay) - .map(DayOfWeek::toString) - .collect(Collectors.toList()); + List timeSlotResponses = jobSearch.getTimeSlots().stream() + .map(slot -> JobSearchTimeSlotResponse.builder() + .day(slot.getDay()) + .startTime(slot.getStartTime().toString()) + .endTime(slot.getEndTime().toString()) + .build()) + .toList(); return DetailJobSearchResponse.builder() .jobSearchId(jobSearch.getId()) - .startTime(jobSearch.getStartTime().toString()) - .endTime(jobSearch.getEndTime().toString()) .salary(jobSearch.getSalary()) - .jobSearchAreas(jobSearchAreaResponses) - .dayList(dayList) + .jobSearchAreas(areaResponses) + .timeSlots(timeSlotResponses) .build(); + } } diff --git a/src/main/java/young/blaybus/domain/senior/controller/response/ListRecommendDto.java b/src/main/java/young/blaybus/domain/senior/controller/response/ListRecommendDto.java index f1e3270..0341b96 100644 --- a/src/main/java/young/blaybus/domain/senior/controller/response/ListRecommendDto.java +++ b/src/main/java/young/blaybus/domain/senior/controller/response/ListRecommendDto.java @@ -8,6 +8,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import young.blaybus.domain.job_search.response.JobSearchTimeSlotResponse; import young.blaybus.util.enums.DayOfWeek; @Builder @@ -26,15 +27,8 @@ public class ListRecommendDto { @Schema(description = "프로필 사진") private String profileUrl; - @Setter - @Schema(description = "희망 요일 목록") - private List dayList; - - @Schema(description = "희망 시작 시간") - private LocalTime startTime; - - @Schema(description = "희망 종료 시간") - private LocalTime endTime; + @Schema(description = "근무 가능 요일&시간대 목록") + private List timeSlots; @Schema(description = "돌봄 스타일") private String careStyle; diff --git a/src/main/java/young/blaybus/domain/senior/controller/response/TimeSlotDto.java b/src/main/java/young/blaybus/domain/senior/controller/response/TimeSlotDto.java new file mode 100644 index 0000000..5a84544 --- /dev/null +++ b/src/main/java/young/blaybus/domain/senior/controller/response/TimeSlotDto.java @@ -0,0 +1,24 @@ +package young.blaybus.domain.senior.controller.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import young.blaybus.util.enums.DayOfWeek; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Schema(description = "근무 요일&시간대 DTO") +public class TimeSlotDto { + @Schema(description = "근무 요일") + DayOfWeek day; + + @Schema(description = "근무 시작 시간 (HH:mm)") + String startTime; + + @Schema(description = "근무 종료 시간 (HH:mm)") + String endTime; +} diff --git a/src/main/java/young/blaybus/domain/senior/service/RecommendService.java b/src/main/java/young/blaybus/domain/senior/service/RecommendService.java index b3b1019..c1b44a8 100644 --- a/src/main/java/young/blaybus/domain/senior/service/RecommendService.java +++ b/src/main/java/young/blaybus/domain/senior/service/RecommendService.java @@ -16,6 +16,7 @@ import young.blaybus.domain.certificate.repository.ListCertificateRepository; import young.blaybus.domain.job_search.JobSearch; import young.blaybus.domain.job_search.JobSearchDay; +import young.blaybus.domain.job_search.JobSearchTimeSlot; import young.blaybus.domain.job_search.repository.JobSearchRepository; import young.blaybus.domain.job_seek.JobSeek; import young.blaybus.domain.job_seek.repository.JobSeekRepository; @@ -25,6 +26,7 @@ import young.blaybus.domain.senior.SeniorDay; import young.blaybus.domain.senior.controller.response.ListRecommendDto; import young.blaybus.domain.senior.controller.response.ListRecommendResponse; +import young.blaybus.domain.senior.controller.response.TimeSlotDto; import young.blaybus.domain.senior.repository.ListRecommendRepository; import young.blaybus.domain.senior.repository.SeniorRepository; import young.blaybus.map.controller.response.geocoding.Coordinate; @@ -55,14 +57,21 @@ public ListRecommendResponse getRecommendList(Long seniorId) { JobSearch jobSearch = jobSearchRepository.findByMemberId(member.getId()).orElse(null); if (jobSearch == null) continue; + // timeSlots 기반으로 요일 및 시간대 가져오기 + List timeSlotDtos = jobSearch.getTimeSlots().stream() + .map(slot -> TimeSlotDto.builder() + .day(slot.getDay()) + .startTime(slot.getStartTime().toString()) // LocalTime → String 변환 + .endTime(slot.getEndTime().toString()) + .build()) + .toList(); + recommendList.add( ListRecommendDto.builder() .memberId(member.getId()) .name(member.getName()) .profileUrl(member.getProfileUrl()) - .dayList(jobSearch.getDayList().stream().map(JobSearchDay::getDay).toList()) - .startTime(jobSearch.getStartTime()) - .endTime(jobSearch.getEndTime()) + .timeSlots(timeSlotDtos) .careStyle(member.getCareStyle().getValue()) .fitness(calculateFitness(member, jobSearch, senior)) .build() @@ -81,17 +90,18 @@ private int calculateFitness(Member member, JobSearch jobSearch, Senior senior) JobSeek jobSeek = jobSeekRepository.findBySenior(senior); + // todo // 거리 : 0km 최고점, 350km 최하점 → 30점 만점 - fitness += 30; - Coordinate memberGeocoding = mapService.geocoding(member.getAddress().toString()); - Coordinate seniorGeocoding = mapService.geocoding(senior.getAddress()); - Double distance = mapService.getDistance(memberGeocoding, seniorGeocoding); - double maxDistance = 350_000; - - fitness -= Math.min(30, distance * 30 / maxDistance); +// fitness += 30; +// Coordinate memberGeocoding = mapService.geocoding(member.getAddress().toString()); +// Coordinate seniorGeocoding = mapService.geocoding(senior.getAddress()); +// Double distance = mapService.getDistance(memberGeocoding, seniorGeocoding); +// double maxDistance = 350_000; +// +// fitness -= Math.min(30, distance * 30 / maxDistance); // 요일 → (노인의 희망 요일이 보호사의 요일과 겹치는 개수) * 15 / (노인의 희망 요일 개수) 점 → 15점 만점 - List memberDayList = jobSearch.getDayList().stream().map(JobSearchDay::getDay).toList(); + List memberDayList = jobSearch.getTimeSlots().stream().map(JobSearchTimeSlot::getDay).toList(); List seniorDayList = senior.getDayList().stream().map(SeniorDay::getDay).toList(); int intersectCount = memberDayList.stream() .filter(seniorDayList::contains) @@ -99,16 +109,22 @@ private int calculateFitness(Member member, JobSearch jobSearch, Senior senior) fitness += (double) (15 * intersectCount) / seniorDayList.size(); // 시간 → 겹치는 시간의 비율 * 15 (15 만점) - LocalTime memberStart = jobSearch.getStartTime(); - LocalTime memberEnd = jobSearch.getEndTime(); - LocalTime seniorStart = senior.getStartTime(); - LocalTime seniorEnd = senior.getEndTime(); - LocalTime overlapStart = memberStart.isBefore(seniorStart) ? seniorStart : memberStart; - LocalTime overlapEnd = memberEnd.isBefore(seniorEnd) ? memberEnd : seniorEnd; - if (overlapStart.isBefore(overlapEnd)) { // 겹치면 - long overlapMinutes = Duration.between(overlapStart, overlapEnd).toMinutes(); - long memberTotalMinutes = Duration.between(memberStart, memberEnd).toMinutes(); - fitness += (double) 15 * overlapMinutes / memberTotalMinutes; + for (JobSearchTimeSlot slot : jobSearch.getTimeSlots()) { + LocalTime memberStart = slot.getStartTime(); + LocalTime memberEnd = slot.getEndTime(); + LocalTime seniorStart = senior.getStartTime(); + LocalTime seniorEnd = senior.getEndTime(); + + LocalTime overlapStart = memberStart.isBefore(seniorStart) ? seniorStart : memberStart; + LocalTime overlapEnd = memberEnd.isBefore(seniorEnd) ? memberEnd : seniorEnd; + + if (overlapStart.isBefore(overlapEnd)) { // 겹치는 경우 + long overlapMinutes = Duration.between(overlapStart, overlapEnd).toMinutes(); + long memberTotalMinutes = Duration.between(memberStart, memberEnd).toMinutes(); + if (memberTotalMinutes > 0) { + fitness += (double) 15 * overlapMinutes / memberTotalMinutes; + } + } } // 자격증 : 요양보호사 제외 개수당 3점 → 10점 만점, 사회복지사 자격증 번호 1로 시작(1급)하면 2점 추가 (10 만점)