From 70ea17518cdedf2c33ba43bff6c549ff84fb1bd2 Mon Sep 17 00:00:00 2001 From: JungTae Kwon Date: Wed, 13 Nov 2024 17:22:19 +0900 Subject: [PATCH] =?UTF-8?q?REFACTOR:=20COGO=20=EC=8B=A0=EC=B2=AD=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD=20(#188)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * REFACTOR: COGO 신청 로직 변경 * FEAT: Add ApplicationCleanupAspect --- .../aspect/ApplicationCleanupAspect.java | 25 ++++ .../handler/ApplicationExceptionHandler.java | 2 +- .../dto/PossibleDateCreateGetResponseDto.java | 86 +++++------ .../CoffeeChat/entity/Application.java | 2 +- .../CoffeeChat/entity/PossibleDate.java | 89 +++++------ .../repository/ApplicationRepository.java | 25 +++- .../PossibleDate/PossibleDateRepository.java | 1 + .../PossibleDateRepositoryImpl.java | 37 ++--- .../service/ApplicationService.java | 30 ++-- .../service/PossibleDateService.java | 139 +++++++++--------- src/main/resources/application.yml | 2 +- 11 files changed, 240 insertions(+), 198 deletions(-) create mode 100644 src/main/java/com/soongsil/CoffeeChat/aspect/ApplicationCleanupAspect.java diff --git a/src/main/java/com/soongsil/CoffeeChat/aspect/ApplicationCleanupAspect.java b/src/main/java/com/soongsil/CoffeeChat/aspect/ApplicationCleanupAspect.java new file mode 100644 index 0000000..8b62392 --- /dev/null +++ b/src/main/java/com/soongsil/CoffeeChat/aspect/ApplicationCleanupAspect.java @@ -0,0 +1,25 @@ +package com.soongsil.CoffeeChat.aspect; + +import com.soongsil.CoffeeChat.repository.ApplicationRepository; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Component +@Aspect +public class ApplicationCleanupAspect { + + private final ApplicationRepository applicationRepository; + + public ApplicationCleanupAspect(ApplicationRepository applicationRepository) { + this.applicationRepository = applicationRepository; + } + + @Scheduled(cron = "0 0 0 * * ?") + @Transactional + public void deleteExpiredApplications() { + // 매일 자정에 현재 시간보다 이전인 COGO 삭제 + applicationRepository.deleteExpiredApplications(); + } +} diff --git a/src/main/java/com/soongsil/CoffeeChat/controller/handler/ApplicationExceptionHandler.java b/src/main/java/com/soongsil/CoffeeChat/controller/handler/ApplicationExceptionHandler.java index ca65344..deff6c2 100644 --- a/src/main/java/com/soongsil/CoffeeChat/controller/handler/ApplicationExceptionHandler.java +++ b/src/main/java/com/soongsil/CoffeeChat/controller/handler/ApplicationExceptionHandler.java @@ -21,7 +21,7 @@ public ResponseEntity handleResponseStatusException(ResponseStatusExcept @ExceptionHandler(Exception.class) public ResponseEntity handleException(Exception ex) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("An error occurred"); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.getMessage()); } @ExceptionHandler(CustomException.class) diff --git a/src/main/java/com/soongsil/CoffeeChat/dto/PossibleDateCreateGetResponseDto.java b/src/main/java/com/soongsil/CoffeeChat/dto/PossibleDateCreateGetResponseDto.java index 0c2b600..cd925f5 100644 --- a/src/main/java/com/soongsil/CoffeeChat/dto/PossibleDateCreateGetResponseDto.java +++ b/src/main/java/com/soongsil/CoffeeChat/dto/PossibleDateCreateGetResponseDto.java @@ -1,20 +1,15 @@ package com.soongsil.CoffeeChat.dto; -import java.time.LocalDate; -import java.time.LocalTime; - import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.querydsl.core.annotations.QueryProjection; import com.soongsil.CoffeeChat.entity.PossibleDate; - import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; + +import java.time.LocalDate; +import java.time.LocalTime; @Getter @Builder @@ -22,38 +17,43 @@ @NoArgsConstructor @Data public class PossibleDateCreateGetResponseDto { - @JsonProperty("possible_date_id") - private Long possibledateId; - - @JsonProperty("date") - @JsonFormat(pattern = "yyyy-MM-dd") - private LocalDate date; - - @JsonProperty("start_time") - @Schema(type = "string", pattern = "hh:mm:ss") - private LocalTime startTime; - - @JsonProperty("end_time") - @Schema(type = "string", pattern = "hh:mm:ss") - private LocalTime endTime; - - @JsonIgnore - - @QueryProjection - public PossibleDateCreateGetResponseDto(LocalDate date, LocalTime startTime, LocalTime endTime, - Long possibledateId) { - this.date = date; - this.startTime = startTime; - this.endTime = endTime; - this.possibledateId = possibledateId; - } - - public static PossibleDateCreateGetResponseDto from(PossibleDate possibleDate) { - return PossibleDateCreateGetResponseDto.builder() - .date(possibleDate.getDate()) - .startTime(possibleDate.getStartTime()) - .endTime(possibleDate.getEndTime()) - .possibledateId(possibleDate.getId()) - .build(); - } + @JsonProperty("possible_date_id") + private Long possibledateId; + + @JsonProperty("date") + @JsonFormat(pattern = "yyyy-MM-dd") + private LocalDate date; + + @JsonProperty("start_time") + @Schema(type = "string", pattern = "hh:mm:ss") + private LocalTime startTime; + + @JsonProperty("end_time") + @Schema(type = "string", pattern = "hh:mm:ss") + private LocalTime endTime; + + @JsonProperty("is_active") + private boolean isActive; + + @JsonIgnore + + @QueryProjection + public PossibleDateCreateGetResponseDto(LocalDate date, LocalTime startTime, LocalTime endTime, + Long possibledateId, boolean isActive) { + this.date = date; + this.startTime = startTime; + this.endTime = endTime; + this.possibledateId = possibledateId; + this.isActive = isActive; + } + + public static PossibleDateCreateGetResponseDto from(PossibleDate possibleDate) { + return PossibleDateCreateGetResponseDto.builder() + .date(possibleDate.getDate()) + .startTime(possibleDate.getStartTime()) + .endTime(possibleDate.getEndTime()) + .possibledateId(possibleDate.getId()) + .isActive(possibleDate.isActive()) + .build(); + } } diff --git a/src/main/java/com/soongsil/CoffeeChat/entity/Application.java b/src/main/java/com/soongsil/CoffeeChat/entity/Application.java index 1c49fb8..3a63507 100644 --- a/src/main/java/com/soongsil/CoffeeChat/entity/Application.java +++ b/src/main/java/com/soongsil/CoffeeChat/entity/Application.java @@ -52,7 +52,7 @@ public class Application { @Column(columnDefinition = "VARCHAR(255) DEFAULT 'UNMATCHED'") private ApplicationStatus accept; - @OneToOne + @ManyToOne @JoinColumn(name = "possible_date_id") private PossibleDate possibleDate; } diff --git a/src/main/java/com/soongsil/CoffeeChat/entity/PossibleDate.java b/src/main/java/com/soongsil/CoffeeChat/entity/PossibleDate.java index a63d5e3..4d7d466 100644 --- a/src/main/java/com/soongsil/CoffeeChat/entity/PossibleDate.java +++ b/src/main/java/com/soongsil/CoffeeChat/entity/PossibleDate.java @@ -1,25 +1,14 @@ package com.soongsil.CoffeeChat.entity; -import java.time.LocalDate; -import java.time.LocalTime; - import com.fasterxml.jackson.annotation.JsonFormat; import com.soongsil.CoffeeChat.dto.PossibleDateCreateRequestDto; +import jakarta.persistence.*; +import lombok.*; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.List; @Entity @Builder @@ -29,36 +18,38 @@ @Setter @ToString(of = {"id", "date", "startTime", "endTime", "isActive"}) public class PossibleDate { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "possible_date_id") - private Long id; - - @Setter - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "mentor_id") - private Mentor mentor; - - @JsonFormat(pattern = "yyyy-MM-dd") - LocalDate date; - - @JsonFormat(pattern = "HH:mm") //datetimeformat은 ss까지 전부 다 받아야 오류안남 - LocalTime startTime; - - @JsonFormat(pattern = "HH:mm") - LocalTime endTime; - - @Column - @Setter - @Builder.Default - private boolean isActive = true; - - public static PossibleDate from(PossibleDateCreateRequestDto dto) { - return PossibleDate.builder() - .date(dto.getDate()) - .startTime(dto.getStartTime()) - .endTime(dto.getEndTime()) - .isActive(true) - .build(); - } + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "possible_date_id") + private Long id; + + @Setter + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "mentor_id") + private Mentor mentor; + + @JsonFormat(pattern = "yyyy-MM-dd") + LocalDate date; + + @JsonFormat(pattern = "HH:mm") //datetimeformat은 ss까지 전부 다 받아야 오류안남 + LocalTime startTime; + + @JsonFormat(pattern = "HH:mm") + LocalTime endTime; + + @OneToMany(mappedBy = "possibleDate") + private List applications = new ArrayList<>(); + + @Column + @Setter + private boolean isActive = true; + + public static PossibleDate from(PossibleDateCreateRequestDto dto) { + return PossibleDate.builder() + .date(dto.getDate()) + .startTime(dto.getStartTime()) + .endTime(dto.getEndTime()) + .isActive(true) + .build(); + } } diff --git a/src/main/java/com/soongsil/CoffeeChat/repository/ApplicationRepository.java b/src/main/java/com/soongsil/CoffeeChat/repository/ApplicationRepository.java index a645096..6b6837d 100644 --- a/src/main/java/com/soongsil/CoffeeChat/repository/ApplicationRepository.java +++ b/src/main/java/com/soongsil/CoffeeChat/repository/ApplicationRepository.java @@ -1,14 +1,27 @@ package com.soongsil.CoffeeChat.repository; -import java.util.List; - +import com.soongsil.CoffeeChat.entity.Application; import com.soongsil.CoffeeChat.entity.Mentee; +import com.soongsil.CoffeeChat.entity.Mentor; +import com.soongsil.CoffeeChat.entity.PossibleDate; +import com.soongsil.CoffeeChat.enums.ApplicationStatus; +import jakarta.transaction.Transactional; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; -import com.soongsil.CoffeeChat.entity.Application; -import com.soongsil.CoffeeChat.entity.Mentor; +import java.util.List; public interface ApplicationRepository extends JpaRepository { - List findApplicationByMentor(Mentor mentor); - List findApplicationByMentee(Mentee mentee); + List findApplicationByMentor(Mentor mentor); + + List findApplicationByMentee(Mentee mentee); + + List findByPossibleDateAndAccept(PossibleDate possibleDate, ApplicationStatus accept); + + @Modifying + @Transactional + @Query("DELETE FROM Application a WHERE a.possibleDate.date < CURRENT_DATE " + + "OR (a.possibleDate.date = CURRENT_DATE AND a.possibleDate.startTime < CURRENT_TIME)") + void deleteExpiredApplications(); } diff --git a/src/main/java/com/soongsil/CoffeeChat/repository/PossibleDate/PossibleDateRepository.java b/src/main/java/com/soongsil/CoffeeChat/repository/PossibleDate/PossibleDateRepository.java index c086c7e..a082985 100644 --- a/src/main/java/com/soongsil/CoffeeChat/repository/PossibleDate/PossibleDateRepository.java +++ b/src/main/java/com/soongsil/CoffeeChat/repository/PossibleDate/PossibleDateRepository.java @@ -2,6 +2,7 @@ import java.util.List; +import com.soongsil.CoffeeChat.entity.Application; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; diff --git a/src/main/java/com/soongsil/CoffeeChat/repository/PossibleDate/PossibleDateRepositoryImpl.java b/src/main/java/com/soongsil/CoffeeChat/repository/PossibleDate/PossibleDateRepositoryImpl.java index 71dc271..8ef1e2f 100644 --- a/src/main/java/com/soongsil/CoffeeChat/repository/PossibleDate/PossibleDateRepositoryImpl.java +++ b/src/main/java/com/soongsil/CoffeeChat/repository/PossibleDate/PossibleDateRepositoryImpl.java @@ -14,23 +14,24 @@ @RequiredArgsConstructor public class PossibleDateRepositoryImpl implements PossibleDateRepositoryCustom { - private final JPAQueryFactory queryFactory; + private final JPAQueryFactory queryFactory; - @Override - public List getPossibleDatesByUsername(String username) { - return queryFactory. - select(new QPossibleDateCreateGetResponseDto( - possibleDate.date, - possibleDate.startTime, - possibleDate.endTime, - possibleDate.id.as("possibleDateId") - )) - .from(user) - .join(user.mentor, mentor) - .join(mentor.possibleDates, possibleDate) - .where(user.username.eq(username).and( - possibleDate.isActive.isTrue() - )) - .fetch(); - } + @Override + public List getPossibleDatesByUsername(String username) { + return queryFactory. + select(new QPossibleDateCreateGetResponseDto( + possibleDate.date, + possibleDate.startTime, + possibleDate.endTime, + possibleDate.id.as("possibleDateId"), + possibleDate.isActive + )) + .from(user) + .join(user.mentor, mentor) + .join(mentor.possibleDates, possibleDate) + .where(user.username.eq(username).and( + possibleDate.isActive.isTrue() + )) + .fetch(); + } } diff --git a/src/main/java/com/soongsil/CoffeeChat/service/ApplicationService.java b/src/main/java/com/soongsil/CoffeeChat/service/ApplicationService.java index 9a81a7b..55157ff 100644 --- a/src/main/java/com/soongsil/CoffeeChat/service/ApplicationService.java +++ b/src/main/java/com/soongsil/CoffeeChat/service/ApplicationService.java @@ -147,15 +147,6 @@ public ApplicationCreateResponseDto createApplication(ApplicationCreateRequestDt } log.info("[*] Found possibleDate is not preempted"); - // 가능시간 비활성화 - System.out.println("possibleDate.getId() = " + requestedPossibleDate.getId()); - requestedPossibleDate.setActive(false); - possibleDateRepository.save(requestedPossibleDate); - log.info( - "[*] PossibleDate(id:" + requestedPossibleDate.getId() + ") is just preempted: " - + requestedPossibleDate.isActive() - ); - // COGO 저장 User user = findUserByUsername(userName); Mentee findMentee = user.getMentee(); @@ -284,6 +275,16 @@ public ApplicationMatchResponseDto updateApplicationStatus(Long applicationId, S ApplicationMatchResponseDto responseDto = ApplicationMatchResponseDto.builder() .applicationId(applicationId) .build(); + PossibleDate matchedPossibleDate = findApplication.getPossibleDate(); + + // 가능시간 비활성화 + System.out.println("possibleDate.getId() = " + matchedPossibleDate.getId()); + matchedPossibleDate.setActive(false); + possibleDateRepository.save(matchedPossibleDate); + log.info( + "[*] PossibleDate(id:" + matchedPossibleDate.getId() + ") is just matched with: " + + findApplication.getId() + ); switch (decision) { case "reject" -> { @@ -294,6 +295,17 @@ public ApplicationMatchResponseDto updateApplicationStatus(Long applicationId, S case "accept" -> { findApplication.setAccept(MATCHED); responseDto.setStatus(MATCHED.name()); + + // 매치된 가능시간(PossibleDate)을 가진 다른 'UNMATCHED' 상태의 Application 엔티티 삭제 + List unmatchedApplications = applicationRepository + .findByPossibleDateAndAccept(matchedPossibleDate, ApplicationStatus.UNMATCHED); + + unmatchedApplications.forEach(application -> { +// application.setAccept(UNMATCHED); + applicationRepository.delete(application); + log.info("[*] Unmatched Application(id:" + application.getId() + ") with PossibleDate(id:" + + matchedPossibleDate.getId() + ") has been UNMATCHED"); + }); } default -> throw new CustomException( INVALID_MATCH_STATUS.getHttpStatusCode(), diff --git a/src/main/java/com/soongsil/CoffeeChat/service/PossibleDateService.java b/src/main/java/com/soongsil/CoffeeChat/service/PossibleDateService.java index ecbe601..40dde9f 100644 --- a/src/main/java/com/soongsil/CoffeeChat/service/PossibleDateService.java +++ b/src/main/java/com/soongsil/CoffeeChat/service/PossibleDateService.java @@ -1,14 +1,5 @@ package com.soongsil.CoffeeChat.service; -import static com.soongsil.CoffeeChat.controller.exception.enums.UserErrorCode.*; - -import java.time.LocalDate; -import java.util.List; -import java.util.stream.Collectors; - -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - import com.soongsil.CoffeeChat.controller.exception.CustomException; import com.soongsil.CoffeeChat.dto.PossibleDateCreateGetResponseDto; import com.soongsil.CoffeeChat.dto.PossibleDateCreateRequestDto; @@ -17,73 +8,81 @@ import com.soongsil.CoffeeChat.entity.User; import com.soongsil.CoffeeChat.repository.PossibleDate.PossibleDateRepository; import com.soongsil.CoffeeChat.repository.User.UserRepository; - import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.util.List; +import java.util.stream.Collectors; + +import static com.soongsil.CoffeeChat.controller.exception.enums.UserErrorCode.USER_NOT_FOUND; @Service @RequiredArgsConstructor @Slf4j @Transactional(readOnly = true) public class PossibleDateService { - private final PossibleDateRepository possibleDateRepository; - private final UserRepository userRepository; - - private User findUserByUsername(String username) { - return userRepository.findByUsername(username) - .orElseThrow(() -> new CustomException( - USER_NOT_FOUND.getHttpStatusCode(), - USER_NOT_FOUND.getErrorMessage()) - ); - } - - @Transactional - public List updatePossibleDate(List dtos, - String username) { - - User user = findUserByUsername(username); - Mentor mentor = user.getMentor(); - - // 가능 시간을 갱신하기 위해 모든 가능 시간을 삭제 후 새로운 값 삽입 - // possibleDateRepository.deleteAllByMentor(mentor); - // log.info("[*] 멘토(" + username + ")의 가능시간 모두 삭제(가능시간 갱신 API 일부)"); - - List possibleDates = dtos.stream() - .map(dto -> { - PossibleDate possibleDate = PossibleDate.from(dto); - possibleDate.setMentor(mentor); - mentor.addPossibleDate(possibleDate); - return possibleDate; - }) - .collect(Collectors.toList()); - - possibleDateRepository.saveAll(possibleDates); - - return possibleDates.stream() - .map(PossibleDateCreateGetResponseDto::from) - .collect(Collectors.toList()); - } - - public List findPossibleDateListByMentor(Long mentorId) { - // 2주로 설정 - LocalDate today = LocalDate.now(); - LocalDate twoWeeksLater = today.plusWeeks(2); - - return possibleDateRepository.getPossibleDatesByMentorId(mentorId) - .stream() - .filter(possibleDate -> !possibleDate.getDate().isBefore(today) && !possibleDate.getDate().isAfter(twoWeeksLater)) - .map(possibleDate -> PossibleDateCreateGetResponseDto.builder() - .date(possibleDate.getDate()) - .startTime(possibleDate.getStartTime()) - .endTime(possibleDate.getEndTime()) - .possibledateId(possibleDate.getId()) - .build()) - .collect(Collectors.toList()); - } - - public List findMentorPossibleDateListByUsername(String username) { - - User user = findUserByUsername(username); - return findPossibleDateListByMentor(user.getMentor().getId()); - } + private final PossibleDateRepository possibleDateRepository; + private final UserRepository userRepository; + + private User findUserByUsername(String username) { + return userRepository.findByUsername(username) + .orElseThrow(() -> new CustomException( + USER_NOT_FOUND.getHttpStatusCode(), + USER_NOT_FOUND.getErrorMessage()) + ); + } + + @Transactional + public List updatePossibleDate(List dtos, + String username) { + + User user = findUserByUsername(username); + Mentor mentor = user.getMentor(); + + // 가능 시간을 갱신하기 위해 모든 가능 시간을 삭제 후 새로운 값 삽입 + // possibleDateRepository.deleteAllByMentor(mentor); + // log.info("[*] 멘토(" + username + ")의 가능시간 모두 삭제(가능시간 갱신 API 일부)"); + + List possibleDates = dtos.stream() + .map(dto -> { + PossibleDate possibleDate = PossibleDate.from(dto); + possibleDate.setMentor(mentor); + mentor.addPossibleDate(possibleDate); + return possibleDate; + }) + .collect(Collectors.toList()); + + possibleDateRepository.saveAll(possibleDates); + + return possibleDates.stream() + .map(PossibleDateCreateGetResponseDto::from) + .collect(Collectors.toList()); + } + + public List findPossibleDateListByMentor(Long mentorId) { + // 2주로 설정 + LocalDate today = LocalDate.now(); + LocalDate twoWeeksLater = today.plusWeeks(2); + + return possibleDateRepository.getPossibleDatesByMentorId(mentorId) + .stream() + .filter(possibleDate -> !possibleDate.getDate().isBefore(today) && !possibleDate.getDate().isAfter(twoWeeksLater)) + .map(possibleDate -> PossibleDateCreateGetResponseDto.builder() + .date(possibleDate.getDate()) + .startTime(possibleDate.getStartTime()) + .endTime(possibleDate.getEndTime()) + .possibledateId(possibleDate.getId()) + .isActive(possibleDate.isActive()) + .build()) + .collect(Collectors.toList()); + } + + public List findMentorPossibleDateListByUsername(String username) { + + User user = findUserByUsername(username); + return findPossibleDateListByMentor(user.getMentor().getId()); + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 3eb7516..28bfaca 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -13,7 +13,7 @@ spring: password: ${DB_PW} jpa: hibernate: - ddl-auto: update + ddl-auto: none naming: physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl database: mysql