From 5ae6187e5cfc766ca061b87ead6fd66bb0752953 Mon Sep 17 00:00:00 2001 From: dradnats1012 Date: Mon, 2 Dec 2024 01:09:27 +0900 Subject: [PATCH 01/14] =?UTF-8?q?feat:=20DiningTypeNotFoundException=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DiningTypeNotFoundException.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/in/koreatech/koin/domain/coopshop/exception/DiningTypeNotFoundException.java diff --git a/src/main/java/in/koreatech/koin/domain/coopshop/exception/DiningTypeNotFoundException.java b/src/main/java/in/koreatech/koin/domain/coopshop/exception/DiningTypeNotFoundException.java new file mode 100644 index 000000000..0a29a4ec1 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/coopshop/exception/DiningTypeNotFoundException.java @@ -0,0 +1,20 @@ +package in.koreatech.koin.domain.coopshop.exception; + +import in.koreatech.koin.global.exception.DataNotFoundException; + +public class DiningTypeNotFoundException extends DataNotFoundException { + + private static final String DEFAULT_MESSAGE = "해당하는 식단 타입이 존재하지 않습니다."; + + public DiningTypeNotFoundException(String message) { + super(message); + } + + public DiningTypeNotFoundException(String message, String detail) { + super(message, detail); + } + + public static DiningTypeNotFoundException withDetail(String detail) { + return new DiningTypeNotFoundException(DEFAULT_MESSAGE, detail); + } +} From 791f90d999adf9f87c14c7437b7f7d9098386e43 Mon Sep 17 00:00:00 2001 From: dradnats1012 Date: Mon, 2 Dec 2024 01:10:34 +0900 Subject: [PATCH 02/14] =?UTF-8?q?feat:=20CoopShopService=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20diningType=20=EC=B0=BE=EB=8A=94=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coopshop/service/CoopShopService.java | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/java/in/koreatech/koin/domain/coopshop/service/CoopShopService.java b/src/main/java/in/koreatech/koin/domain/coopshop/service/CoopShopService.java index 6e3414569..6f5303f75 100644 --- a/src/main/java/in/koreatech/koin/domain/coopshop/service/CoopShopService.java +++ b/src/main/java/in/koreatech/koin/domain/coopshop/service/CoopShopService.java @@ -1,5 +1,7 @@ package in.koreatech.koin.domain.coopshop.service; +import static in.koreatech.koin.domain.dining.model.DiningType.*; + import java.time.Clock; import java.time.DayOfWeek; import java.time.LocalDate; @@ -14,14 +16,15 @@ import in.koreatech.koin.domain.coopshop.dto.CoopShopResponse; import in.koreatech.koin.domain.coopshop.dto.CoopShopsResponse; import in.koreatech.koin.domain.coopshop.exception.CoopSemesterNotFoundException; +import in.koreatech.koin.domain.coopshop.exception.DiningTypeNotFoundException; import in.koreatech.koin.domain.coopshop.model.CoopOpen; import in.koreatech.koin.domain.coopshop.model.CoopSemester; import in.koreatech.koin.domain.coopshop.model.CoopShop; import in.koreatech.koin.domain.coopshop.model.CoopShopType; import in.koreatech.koin.domain.coopshop.model.DayType; import in.koreatech.koin.domain.coopshop.repository.CoopOpenRepository; -import in.koreatech.koin.domain.coopshop.repository.CoopShopRepository; import in.koreatech.koin.domain.coopshop.repository.CoopSemesterRepository; +import in.koreatech.koin.domain.coopshop.repository.CoopShopRepository; import in.koreatech.koin.domain.dining.model.DiningType; import lombok.RequiredArgsConstructor; @@ -71,6 +74,23 @@ public boolean getIsOpened(LocalDateTime now, CoopShopType coopShopType, DiningT } } + public DiningType getDiningType(){ + if(LocalTime.now().isAfter(BREAKFAST.getStartTime().minusHours(1)) + && LocalTime.now().isBefore(BREAKFAST.getEndTime())){ + return BREAKFAST; + } + if(LocalTime.now().isAfter(LUNCH.getStartTime().minusHours(1)) + && LocalTime.now().isBefore(LUNCH.getEndTime())){ + return LUNCH; + } + if(LocalTime.now().isAfter(DINNER.getStartTime().minusHours(3)) + && LocalTime.now().isBefore(DINNER.getEndTime())){ + return DINNER; + } + + throw DiningTypeNotFoundException.withDetail(""); + } + @Transactional public void updateSemester() { CoopSemester currentSemester = coopSemesterRepository.getByIsApplied(true); From 55d73aa8721e747b459d60b7d5f0b640c20b6e6f Mon Sep 17 00:00:00 2001 From: dradnats1012 Date: Mon, 2 Dec 2024 01:10:59 +0900 Subject: [PATCH 03/14] =?UTF-8?q?feat:=20DiningNotifyCache=20Redis=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/coop/model/DiningNotifyCache.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/main/java/in/koreatech/koin/domain/coop/model/DiningNotifyCache.java diff --git a/src/main/java/in/koreatech/koin/domain/coop/model/DiningNotifyCache.java b/src/main/java/in/koreatech/koin/domain/coop/model/DiningNotifyCache.java new file mode 100644 index 000000000..7f365f557 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/coop/model/DiningNotifyCache.java @@ -0,0 +1,35 @@ +package in.koreatech.koin.domain.coop.model; + +import java.util.concurrent.TimeUnit; + +import org.springframework.data.redis.core.RedisHash; +import org.springframework.data.redis.core.TimeToLive; + +import jakarta.persistence.Id; +import lombok.Builder; +import lombok.Getter; + +@Getter +@RedisHash("DiningNotify") +public class DiningNotifyCache { + + private static final long CACHE_EXPIRE_HOUR_BY_COOP = 3L; + + @Id + private String id; + + @TimeToLive(unit = TimeUnit.HOURS) + private final Long expiration; + + @Builder + private DiningNotifyCache(String id, Long expiration){ + this.id = id; + this.expiration = CACHE_EXPIRE_HOUR_BY_COOP; + } + + public static DiningNotifyCache from(String diningId){ + return DiningNotifyCache.builder() + .id(diningId) + .build(); + } +} From 9792acba5b04d151870893e51c163c95e568a8f5 Mon Sep 17 00:00:00 2001 From: dradnats1012 Date: Mon, 2 Dec 2024 01:11:19 +0900 Subject: [PATCH 04/14] =?UTF-8?q?feat:=20DiningNotifyCacheRepository=20Red?= =?UTF-8?q?is=EC=9A=A9=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DiningNotifyCacheRepository.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/main/java/in/koreatech/koin/domain/coop/repository/DiningNotifyCacheRepository.java diff --git a/src/main/java/in/koreatech/koin/domain/coop/repository/DiningNotifyCacheRepository.java b/src/main/java/in/koreatech/koin/domain/coop/repository/DiningNotifyCacheRepository.java new file mode 100644 index 000000000..e89db50e2 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/coop/repository/DiningNotifyCacheRepository.java @@ -0,0 +1,22 @@ +package in.koreatech.koin.domain.coop.repository; + +import java.util.Optional; + +import org.springframework.data.repository.Repository; + +import in.koreatech.koin.domain.coop.exception.DiningCacheNotFoundException; +import in.koreatech.koin.domain.coop.model.DiningNotifyCache; + +public interface DiningNotifyCacheRepository extends Repository { + + DiningNotifyCache save(DiningNotifyCache diningNotifyCache); + + boolean existsById(String diningNotifyId); + + Optional findById(String diningPlace); + + default DiningNotifyCache getById(String diningPlace) { + return findById(diningPlace).orElseThrow( + () -> DiningCacheNotFoundException.withDetail("diningSoldOutCache: " + diningPlace)); + } +} From c659c0d503da33b01c9faf9bb686a6a8fe732924 Mon Sep 17 00:00:00 2001 From: dradnats1012 Date: Mon, 2 Dec 2024 01:12:03 +0900 Subject: [PATCH 05/14] =?UTF-8?q?feat:=20DiningRepository=20=EB=82=A0?= =?UTF-8?q?=EC=A7=9C,=20=ED=83=80=EC=9E=85,=20=EC=9E=A5=EC=86=8C=20?= =?UTF-8?q?=EA=B8=B0=EC=A4=80=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EC=97=AC=EB=B6=80=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/dining/repository/DiningRepository.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/in/koreatech/koin/domain/dining/repository/DiningRepository.java b/src/main/java/in/koreatech/koin/domain/dining/repository/DiningRepository.java index c43547c07..e525a3d33 100644 --- a/src/main/java/in/koreatech/koin/domain/dining/repository/DiningRepository.java +++ b/src/main/java/in/koreatech/koin/domain/dining/repository/DiningRepository.java @@ -6,6 +6,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.Repository; import in.koreatech.koin.domain.coop.exception.MenuNotFoundException; @@ -36,4 +37,15 @@ default Dining getById(Integer id) { List findByDateBetween(LocalDate startDate, LocalDate endDate); List findByDateBetweenAndPlaceIn(LocalDate startDate, LocalDate endDate, List placeFilters); + + Optional> findByDate(LocalDate now); + + default List getByDate(LocalDate now){ + return findByDate(now) + .orElseThrow(()-> MenuNotFoundException.withDetail("menuId: " + now)); + } + + @Query("SELECT COUNT(d) = (SELECT COUNT(d2) FROM Dining d2 WHERE d2.date = :date AND d2.type = :type AND d2.place IN :places) " + + "FROM Dining d WHERE d.date = :date AND d.type = :type AND d.place IN :places AND d.imageUrl IS NOT NULL") + boolean allExistsByDateAndTypeAndPlacesAndImageUrlIsNotNull(LocalDate date, DiningType type, List places); } From 3b2708ac7be8a6a6f1f5c6b40e62c483c433b938 Mon Sep 17 00:00:00 2001 From: dradnats1012 Date: Mon, 2 Dec 2024 01:12:39 +0900 Subject: [PATCH 06/14] =?UTF-8?q?feat:=20=EC=8B=9D=EB=8B=A8=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=EC=A0=80=EC=9E=A5=EC=8B=9C=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EB=B0=9C=EC=86=A1=20=EC=82=AD=EC=A0=9C,=20?= =?UTF-8?q?=EC=8A=A4=EC=BC=80=EC=A4=84=EB=9F=AC=EC=9A=A9=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EB=B0=9C=EC=86=A1=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../koin/domain/coop/service/CoopService.java | 56 ++++++++++++++++--- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java b/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java index ee3daac70..702869756 100644 --- a/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java +++ b/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java @@ -6,6 +6,7 @@ import java.time.Clock; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -40,12 +41,15 @@ import in.koreatech.koin.domain.coop.exception.StartDateAfterEndDateException; import in.koreatech.koin.domain.coop.model.Coop; import in.koreatech.koin.domain.coop.model.DiningImageUploadEvent; +import in.koreatech.koin.domain.coop.model.DiningNotifyCache; import in.koreatech.koin.domain.coop.model.DiningSoldOutEvent; import in.koreatech.koin.domain.coop.repository.CoopRepository; +import in.koreatech.koin.domain.coop.repository.DiningNotifyCacheRepository; import in.koreatech.koin.domain.coop.repository.DiningSoldOutCacheRepository; import in.koreatech.koin.domain.coopshop.model.CoopShopType; import in.koreatech.koin.domain.coopshop.service.CoopShopService; import in.koreatech.koin.domain.dining.model.Dining; +import in.koreatech.koin.domain.dining.model.DiningType; import in.koreatech.koin.domain.dining.repository.DiningRepository; import in.koreatech.koin.domain.user.model.User; import in.koreatech.koin.domain.user.model.UserToken; @@ -63,11 +67,13 @@ public class CoopService { private final ApplicationEventPublisher eventPublisher; private final DiningRepository diningRepository; private final DiningSoldOutCacheRepository diningSoldOutCacheRepository; + private final DiningNotifyCacheRepository diningNotifyCacheRepository; private final CoopRepository coopRepository; private final UserTokenRepository userTokenRepository; private final CoopShopService coopShopService; private final PasswordEncoder passwordEncoder; private final JwtProvider jwtProvider; + List placeFilters = Arrays.asList("A코너", "B코너", "C코너"); private final int EXCEL_COLUMN_COUNT = 8; @@ -91,15 +97,50 @@ public void changeSoldOut(SoldOutRequest soldOutRequest) { @Transactional public void saveDiningImage(DiningImageRequest imageRequest) { Dining dining = diningRepository.getById(imageRequest.menuId()); - boolean isImageExist = diningRepository.existsByDateAndTypeAndImageUrlIsNotNull(dining.getDate(), - dining.getType()); + dining.setImageUrl(imageRequest.imageUrl()); + } - LocalDateTime now = LocalDateTime.now(clock); - boolean isOpened = coopShopService.getIsOpened(now, CoopShopType.CAFETERIA, dining.getType(), true); - if (isOpened && !isImageExist) { - eventPublisher.publishEvent(new DiningImageUploadEvent(dining.getId(), dining.getImageUrl())); + public void sendDiningNotify() { + DiningType diningType = coopShopService.getDiningType(); + LocalDate now = LocalDate.now(); + List dinings = diningRepository.findAllByDateAndType(now, diningType); + + if (dinings.isEmpty()) { + return; } - dining.setImageUrl(imageRequest.imageUrl()); + + boolean allImageExist = diningRepository.allExistsByDateAndTypeAndPlacesAndImageUrlIsNotNull( + now, diningType, placeFilters + ); + boolean isOpened = coopShopService.getIsOpened(LocalDateTime.now(), CoopShopType.CAFETERIA, diningType, true); + String diningNotifyId = now.toString() + diningType; + + if (isOpened && allImageExist) { + if (alreadyNotify(diningNotifyId)) + return; + + if (!diningNotifyCacheRepository.existsById(diningNotifyId)) { + sendDiningNotify(diningNotifyId, dinings); + } + + if (LocalTime.now().isAfter(diningType.getStartTime().minusMinutes(10)) + && !diningNotifyCacheRepository.existsById(diningNotifyId) + ) { + sendDiningNotify(diningNotifyId, dinings); + } + } + } + + private boolean alreadyNotify(String diningNotifyId) { + if (diningNotifyCacheRepository.existsById(diningNotifyId)) { + return true; + } + return false; + } + + private void sendDiningNotify(String diningNotifyId, List dinings) { + diningNotifyCacheRepository.save(DiningNotifyCache.from(diningNotifyId)); + eventPublisher.publishEvent(new DiningImageUploadEvent(dinings.get(0).getId(), dinings.get(0).getImageUrl())); } @Transactional @@ -125,7 +166,6 @@ public ByteArrayInputStream generateDiningExcel(LocalDate startDate, LocalDate e List dinings; if (isCafeteria) { - List placeFilters = Arrays.asList("A코너", "B코너", "C코너"); dinings = diningRepository.findByDateBetweenAndPlaceIn(startDate, endDate, placeFilters); } else { dinings = diningRepository.findByDateBetween(startDate, endDate); From 9f353bc788a5fb72bdbd1f861189a09e6c72a795 Mon Sep 17 00:00:00 2001 From: dradnats1012 Date: Mon, 2 Dec 2024 01:13:05 +0900 Subject: [PATCH 07/14] =?UTF-8?q?feat:=20CoopScheduler=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../koin/domain/coop/util/CoopScheduler.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/main/java/in/koreatech/koin/domain/coop/util/CoopScheduler.java diff --git a/src/main/java/in/koreatech/koin/domain/coop/util/CoopScheduler.java b/src/main/java/in/koreatech/koin/domain/coop/util/CoopScheduler.java new file mode 100644 index 000000000..633259010 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/coop/util/CoopScheduler.java @@ -0,0 +1,30 @@ +package in.koreatech.koin.domain.coop.util; + +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import in.koreatech.koin.domain.coop.service.CoopService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +@RequiredArgsConstructor +public class CoopScheduler { + + private final CoopService coopService; + + @Scheduled(cron = "0 0/6 7-8 * * *") + @Scheduled(cron = "0 30-59/6 10 * * *") + @Scheduled(cron = "0 0/6 11-12 * * *") + @Scheduled(cron = "0 0-30/6 13 * * *") + @Scheduled(cron = "0 0/6 16-17 * * *") + @Scheduled(cron = "0 0-30/6 18 * * *") + public void notifyDiningImageUpload() { + try { + coopService.sendDiningNotify(); + } catch (Exception e) { + log.warn("식단 이미지 알림 과정에서 오류가 발생했습니다."); + } + } +} From e0a1706f0ebb66784cedb07601f4f7dc2ba21189 Mon Sep 17 00:00:00 2001 From: dradnats1012 Date: Fri, 6 Dec 2024 15:37:57 +0900 Subject: [PATCH 08/14] =?UTF-8?q?feat:=20LocalTime=20clock=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../koin/domain/coop/service/CoopService.java | 10 +++++----- .../domain/coopshop/service/CoopShopService.java | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java b/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java index 702869756..367a6fac2 100644 --- a/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java +++ b/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java @@ -102,7 +102,7 @@ public void saveDiningImage(DiningImageRequest imageRequest) { public void sendDiningNotify() { DiningType diningType = coopShopService.getDiningType(); - LocalDate now = LocalDate.now(); + LocalDate now = LocalDate.now(clock); List dinings = diningRepository.findAllByDateAndType(now, diningType); if (dinings.isEmpty()) { @@ -112,7 +112,7 @@ public void sendDiningNotify() { boolean allImageExist = diningRepository.allExistsByDateAndTypeAndPlacesAndImageUrlIsNotNull( now, diningType, placeFilters ); - boolean isOpened = coopShopService.getIsOpened(LocalDateTime.now(), CoopShopType.CAFETERIA, diningType, true); + boolean isOpened = coopShopService.getIsOpened(LocalDateTime.now(clock), CoopShopType.CAFETERIA, diningType, true); String diningNotifyId = now.toString() + diningType; if (isOpened && allImageExist) { @@ -120,13 +120,13 @@ public void sendDiningNotify() { return; if (!diningNotifyCacheRepository.existsById(diningNotifyId)) { - sendDiningNotify(diningNotifyId, dinings); + sendNotify(diningNotifyId, dinings); } if (LocalTime.now().isAfter(diningType.getStartTime().minusMinutes(10)) && !diningNotifyCacheRepository.existsById(diningNotifyId) ) { - sendDiningNotify(diningNotifyId, dinings); + sendNotify(diningNotifyId, dinings); } } } @@ -138,7 +138,7 @@ private boolean alreadyNotify(String diningNotifyId) { return false; } - private void sendDiningNotify(String diningNotifyId, List dinings) { + private void sendNotify(String diningNotifyId, List dinings) { diningNotifyCacheRepository.save(DiningNotifyCache.from(diningNotifyId)); eventPublisher.publishEvent(new DiningImageUploadEvent(dinings.get(0).getId(), dinings.get(0).getImageUrl())); } diff --git a/src/main/java/in/koreatech/koin/domain/coopshop/service/CoopShopService.java b/src/main/java/in/koreatech/koin/domain/coopshop/service/CoopShopService.java index 6f5303f75..55858ccff 100644 --- a/src/main/java/in/koreatech/koin/domain/coopshop/service/CoopShopService.java +++ b/src/main/java/in/koreatech/koin/domain/coopshop/service/CoopShopService.java @@ -75,20 +75,20 @@ public boolean getIsOpened(LocalDateTime now, CoopShopType coopShopType, DiningT } public DiningType getDiningType(){ - if(LocalTime.now().isAfter(BREAKFAST.getStartTime().minusHours(1)) - && LocalTime.now().isBefore(BREAKFAST.getEndTime())){ + if(LocalTime.now(clock).isAfter(BREAKFAST.getStartTime().minusHours(1)) + && LocalTime.now(clock).isBefore(BREAKFAST.getEndTime())){ return BREAKFAST; } - if(LocalTime.now().isAfter(LUNCH.getStartTime().minusHours(1)) - && LocalTime.now().isBefore(LUNCH.getEndTime())){ + if(LocalTime.now(clock).isAfter(LUNCH.getStartTime().minusHours(1)) + && LocalTime.now(clock).isBefore(LUNCH.getEndTime())){ return LUNCH; } - if(LocalTime.now().isAfter(DINNER.getStartTime().minusHours(3)) - && LocalTime.now().isBefore(DINNER.getEndTime())){ + if(LocalTime.now(clock).isAfter(DINNER.getStartTime().minusHours(1)) + && LocalTime.now(clock).isBefore(DINNER.getEndTime())){ return DINNER; } - throw DiningTypeNotFoundException.withDetail(""); + throw DiningTypeNotFoundException.withDetail(LocalTime.now() + ""); } @Transactional From 8aee7d2b7431d2a8bdf7e40930c14438cfd8a97e Mon Sep 17 00:00:00 2001 From: dradnats1012 Date: Sat, 7 Dec 2024 21:37:30 +0900 Subject: [PATCH 09/14] =?UTF-8?q?feat:=20test=20=EC=99=84=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../koin/domain/coop/controller/CoopApi.java | 1 - .../koin/domain/coop/util/CoopScheduler.java | 9 +- .../koin/acceptance/DiningApiTest.java | 194 +++++++++++++----- .../koreatech/koin/fixture/DiningFixture.java | 1 + 4 files changed, 152 insertions(+), 53 deletions(-) diff --git a/src/main/java/in/koreatech/koin/domain/coop/controller/CoopApi.java b/src/main/java/in/koreatech/koin/domain/coop/controller/CoopApi.java index a21a27627..455b71023 100644 --- a/src/main/java/in/koreatech/koin/domain/coop/controller/CoopApi.java +++ b/src/main/java/in/koreatech/koin/domain/coop/controller/CoopApi.java @@ -48,7 +48,6 @@ ResponseEntity changeSoldOut( ); @ApiResponses( - value = { @ApiResponse(responseCode = "200"), @ApiResponse(responseCode = "400", content = @Content(schema = @Schema(hidden = true))), diff --git a/src/main/java/in/koreatech/koin/domain/coop/util/CoopScheduler.java b/src/main/java/in/koreatech/koin/domain/coop/util/CoopScheduler.java index 633259010..c37965b35 100644 --- a/src/main/java/in/koreatech/koin/domain/coop/util/CoopScheduler.java +++ b/src/main/java/in/koreatech/koin/domain/coop/util/CoopScheduler.java @@ -14,12 +14,9 @@ public class CoopScheduler { private final CoopService coopService; - @Scheduled(cron = "0 0/6 7-8 * * *") - @Scheduled(cron = "0 30-59/6 10 * * *") - @Scheduled(cron = "0 0/6 11-12 * * *") - @Scheduled(cron = "0 0-30/6 13 * * *") - @Scheduled(cron = "0 0/6 16-17 * * *") - @Scheduled(cron = "0 0-30/6 18 * * *") + @Scheduled(cron = "0 0/6 7 * * *") + @Scheduled(cron = "0 30/6 10-11 * * *") + @Scheduled(cron = "0 30/6 16-17 * * *") public void notifyDiningImageUpload() { try { coopService.sendDiningNotify(); diff --git a/src/test/java/in/koreatech/koin/acceptance/DiningApiTest.java b/src/test/java/in/koreatech/koin/acceptance/DiningApiTest.java index acf081641..f185ba869 100644 --- a/src/test/java/in/koreatech/koin/acceptance/DiningApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/DiningApiTest.java @@ -1,5 +1,6 @@ package in.koreatech.koin.acceptance; +import static in.koreatech.koin.domain.dining.model.DiningType.LUNCH; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; @@ -19,8 +20,11 @@ import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.AcceptanceTest; +import in.koreatech.koin.domain.coop.model.DiningNotifyCache; import in.koreatech.koin.domain.coop.model.DiningSoldOutCache; +import in.koreatech.koin.domain.coop.repository.DiningNotifyCacheRepository; import in.koreatech.koin.domain.coop.repository.DiningSoldOutCacheRepository; +import in.koreatech.koin.domain.coop.service.CoopService; import in.koreatech.koin.domain.dining.model.Dining; import in.koreatech.koin.domain.dining.repository.DiningRepository; import in.koreatech.koin.domain.user.model.User; @@ -48,7 +52,14 @@ class DiningApiTest extends AcceptanceTest { @Autowired private CoopShopFixture coopShopFixture; + @Autowired + private CoopService coopService; + + @Autowired + private DiningNotifyCacheRepository diningNotifyCacheRepository; + private Dining A코너_점심; + private Dining B코너_점심; private User coop_준기; private String token_준기; private User owner_현수; @@ -63,6 +74,7 @@ void setUp() { owner_현수 = userFixture.현수_사장님().getUser(); token_현수 = userFixture.getToken(owner_현수); A코너_점심 = diningFixture.A코너_점심(LocalDate.parse("2024-01-15")); + B코너_점심 = diningFixture.B코너_점심(LocalDate.parse("2024-01-15")); } @Test @@ -88,6 +100,28 @@ void setUp() { "땡초부추전", "누룽지탕" ], + "image_url": "https://stage.koreatech.in/image.jpg", + "created_at": "2024-01-15 12:00:00", + "updated_at": "2024-01-15 12:00:00", + "soldout_at": null, + "changed_at": null, + "likes": 0, + "is_liked" : false + }, + { + "id": 2, + "date": "2024-01-15", + "type": "LUNCH", + "place": "B코너", + "price_card": 6000, + "price_cash": 6000, + "kcal": 881, + "menu": [ + "병아리", + "소고기", + "땡초", + "탕" + ], "image_url": null, "created_at": "2024-01-15 12:00:00", "updated_at": "2024-01-15 12:00:00", @@ -118,30 +152,52 @@ void setUp() { ) .andExpect(status().isOk()) .andExpect(content().json(""" - [ - { - "id": 1, - "date": "2024-01-15", - "type": "LUNCH", - "place": "A코너", - "price_card": 6000, - "price_cash": 6000, - "kcal": 881, - "menu": [ - "병아리콩밥", - "(탕)소고기육개장", - "땡초부추전", - "누룽지탕" - ], - "image_url": null, - "created_at": "2024-01-15 12:00:00", - "updated_at": "2024-01-15 12:00:00", - "soldout_at": null, - "changed_at": null, - "likes": 0, - "is_liked" : false - } - ] + [ + { + "id": 1, + "date": "2024-01-15", + "type": "LUNCH", + "place": "A코너", + "price_card": 6000, + "price_cash": 6000, + "kcal": 881, + "menu": [ + "병아리콩밥", + "(탕)소고기육개장", + "땡초부추전", + "누룽지탕" + ], + "image_url": "https://stage.koreatech.in/image.jpg", + "created_at": "2024-01-15 12:00:00", + "updated_at": "2024-01-15 12:00:00", + "soldout_at": null, + "changed_at": null, + "likes": 0, + "is_liked" : false + }, + { + "id": 2, + "date": "2024-01-15", + "type": "LUNCH", + "place": "B코너", + "price_card": 6000, + "price_cash": 6000, + "kcal": 881, + "menu": [ + "병아리", + "소고기", + "땡초", + "탕" + ], + "image_url": null, + "created_at": "2024-01-15 12:00:00", + "updated_at": "2024-01-15 12:00:00", + "soldout_at": null, + "changed_at": null, + "likes": 0, + "is_liked" : false + } + ] """)); } @@ -345,13 +401,35 @@ void setUp() { "땡초부추전", "누룽지탕" ], - "image_url": null, + "image_url": "https://stage.koreatech.in/image.jpg", "created_at": "2024-01-15 12:00:00", "updated_at": "2024-01-15 12:00:00", "soldout_at": null, "changed_at": null, "likes": 1, "is_liked" : true + }, + { + "id": 2, + "date": "2024-01-15", + "type": "LUNCH", + "place": "B코너", + "price_card": 6000, + "price_cash": 6000, + "kcal": 881, + "menu": [ + "병아리", + "소고기", + "땡초", + "탕" + ], + "image_url": null, + "created_at": "2024-01-15 12:00:00", + "updated_at": "2024-01-15 12:00:00", + "soldout_at": null, + "changed_at": null, + "likes": 0, + "is_liked" : false } ] """)) @@ -383,6 +461,28 @@ void setUp() { "땡초부추전", "누룽지탕" ], + "image_url": "https://stage.koreatech.in/image.jpg", + "created_at": "2024-01-15 12:00:00", + "updated_at": "2024-01-15 12:00:00", + "soldout_at": null, + "changed_at": null, + "likes": 0, + "is_liked" : false + }, + { + "id": 2, + "date": "2024-01-15", + "type": "LUNCH", + "place": "B코너", + "price_card": 6000, + "price_cash": 6000, + "kcal": 881, + "menu": [ + "병아리", + "소고기", + "땡초", + "탕" + ], "image_url": null, "created_at": "2024-01-15 12:00:00", "updated_at": "2024-01-15 12:00:00", @@ -397,7 +497,8 @@ void setUp() { } @Test - void 이미지_업로드를_한다_품절_알림이_발송된다() throws Exception { + void 식단_이미지를_업로드_한다() throws Exception { + Dining A코너_저녁 = diningFixture.A코너_저녁(LocalDate.parse("2024-01-15")); String imageUrl = "https://stage.koreatech.in/image.jpg"; mockMvc.perform( patch("/coop/dining/image") @@ -407,38 +508,39 @@ void setUp() { "menu_id": "%s", "image_url": "%s" } - """, A코너_점심.getId(), imageUrl)) + """, A코너_저녁.getId(), imageUrl)) .param("diningId", String.valueOf(1)) .contentType(MediaType.APPLICATION_JSON) ) .andExpect(status().isOk()); - forceVerify(() -> verify(coopEventListener).onDiningImageUploadRequest(any())); clear(); setUp(); } @Test - void 해당_식사시간_외에_이미지_업로드를_한다_품절_알림이_발송되지_않는다() throws Exception { - Dining A코너_저녁 = diningFixture.A코너_저녁(LocalDate.parse("2024-01-15")); - String imageUrl = "https://stage.koreatech.in/image.jpg"; - mockMvc.perform( - patch("/coop/dining/image") - .header("Authorization", "Bearer " + token_준기) - .content(String.format(""" - { - "menu_id": "%s", - "image_url": "%s" - } - """, A코너_저녁.getId(), imageUrl)) - .param("diningId", String.valueOf(1)) - .contentType(MediaType.APPLICATION_JSON) - ) - .andExpect(status().isOk()); + void 이미지가_모두_존재하고_오픈시간이고_Redis에_키가_있으면_알림이_발송되지_않는다() throws Exception { + String diningNotifyId = LocalDate.now(clock).toString() + LUNCH; + diningNotifyCacheRepository.save(DiningNotifyCache.from(diningNotifyId)); + B코너_점심.setImageUrl("https://stage.koreatech.in/image.jpg"); + diningRepository.save(B코너_점심); + coopService.sendDiningNotify(); + forceVerify(() -> verify(coopEventListener, never()).onDiningImageUploadRequest(any())); clear(); setUp(); } + @Test + void 이미지가_모두_존재하고_오픈시간이고_Redis에_키가_없으면_알림이_발송된다() throws Exception { + B코너_점심.setImageUrl("https://stage.koreatech.in/image.jpg"); + diningRepository.save(B코너_점심); + coopService.sendDiningNotify(); + + forceVerify(() -> verify(coopEventListener).onDiningImageUploadRequest(any())); + clear(); + setUp(); + } + @Test void 특정_메뉴_특정_코너의_식단을_검색한다() throws Exception { mockMvc.perform( @@ -466,7 +568,7 @@ void setUp() { "땡초부추전", "누룽지탕" ], - "image_url": null, + "image_url": "https://stage.koreatech.in/image.jpg", "created_at": "2024-01-15 12:00:00", "soldout_at": "2024-01-15 12:00:00", "changed_at": "2024-01-15 12:00:00", @@ -529,7 +631,7 @@ void setUp() { "땡초부추전", "누룽지탕" ], - "image_url": null, + "image_url": "https://stage.koreatech.in/image.jpg", "created_at": "2024-01-15 12:00:00", "soldout_at": "2024-01-15 12:00:00", "changed_at": "2024-01-15 12:00:00", diff --git a/src/test/java/in/koreatech/koin/fixture/DiningFixture.java b/src/test/java/in/koreatech/koin/fixture/DiningFixture.java index 9889e8668..c89829a4e 100644 --- a/src/test/java/in/koreatech/koin/fixture/DiningFixture.java +++ b/src/test/java/in/koreatech/koin/fixture/DiningFixture.java @@ -63,6 +63,7 @@ public DiningFixture(DiningRepository diningRepository) { .kcal(881) .menu(""" ["병아리콩밥", "(탕)소고기육개장", "땡초부추전", "누룽지탕"]""") + .imageUrl("https://stage.koreatech.in/image.jpg") .likes(0) .build() ); From fdd46d2c6e37f43efba02f41e158e94f7c9bee4e Mon Sep 17 00:00:00 2001 From: dradnats1012 Date: Sat, 7 Dec 2024 22:06:23 +0900 Subject: [PATCH 10/14] =?UTF-8?q?fix:=2010=EB=B6=84=EC=A0=84=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../koin/domain/coop/service/CoopService.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java b/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java index 367a6fac2..77aeb7f83 100644 --- a/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java +++ b/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java @@ -102,18 +102,22 @@ public void saveDiningImage(DiningImageRequest imageRequest) { public void sendDiningNotify() { DiningType diningType = coopShopService.getDiningType(); - LocalDate now = LocalDate.now(clock); - List dinings = diningRepository.findAllByDateAndType(now, diningType); + LocalDate nowDate = LocalDate.now(clock); + List dinings = diningRepository.findAllByDateAndType(nowDate, diningType); if (dinings.isEmpty()) { return; } boolean allImageExist = diningRepository.allExistsByDateAndTypeAndPlacesAndImageUrlIsNotNull( - now, diningType, placeFilters + nowDate, diningType, placeFilters ); - boolean isOpened = coopShopService.getIsOpened(LocalDateTime.now(clock), CoopShopType.CAFETERIA, diningType, true); - String diningNotifyId = now.toString() + diningType; + + boolean isOpened = coopShopService.getIsOpened( + LocalDateTime.of(nowDate, LocalTime.of(11,25))/*LocalDateTime.now(clock)*/, CoopShopType.CAFETERIA, diningType, true + ); + + String diningNotifyId = nowDate.toString() + diningType; if (isOpened && allImageExist) { if (alreadyNotify(diningNotifyId)) @@ -122,12 +126,13 @@ public void sendDiningNotify() { if (!diningNotifyCacheRepository.existsById(diningNotifyId)) { sendNotify(diningNotifyId, dinings); } + } - if (LocalTime.now().isAfter(diningType.getStartTime().minusMinutes(10)) - && !diningNotifyCacheRepository.existsById(diningNotifyId) - ) { - sendNotify(diningNotifyId, dinings); - } + if (LocalTime.now().isAfter(diningType.getStartTime().minusMinutes(10)) + && !diningNotifyCacheRepository.existsById(diningNotifyId) + && diningRepository.existsByDateAndTypeAndImageUrlIsNotNull(nowDate, diningType) + ) { + sendNotify(diningNotifyId, dinings); } } From f2bf73cefcdca4c0f9fc86a5cf1000fb8bfefc5b Mon Sep 17 00:00:00 2001 From: dradnats1012 Date: Sat, 7 Dec 2024 22:12:28 +0900 Subject: [PATCH 11/14] =?UTF-8?q?fix:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=9A=A9=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/in/koreatech/koin/domain/coop/service/CoopService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java b/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java index 77aeb7f83..311d09ef6 100644 --- a/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java +++ b/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java @@ -114,7 +114,7 @@ public void sendDiningNotify() { ); boolean isOpened = coopShopService.getIsOpened( - LocalDateTime.of(nowDate, LocalTime.of(11,25))/*LocalDateTime.now(clock)*/, CoopShopType.CAFETERIA, diningType, true + LocalDateTime.now(clock), CoopShopType.CAFETERIA, diningType, true ); String diningNotifyId = nowDate.toString() + diningType; From d8b99642d0942b2b03e8d007602fa4d82f04afa1 Mon Sep 17 00:00:00 2001 From: dradnats1012 Date: Sat, 7 Dec 2024 23:58:46 +0900 Subject: [PATCH 12/14] =?UTF-8?q?fix:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../koin/domain/coop/service/CoopService.java | 1 + .../in/koreatech/koin/acceptance/DiningApiTest.java | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java b/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java index 311d09ef6..f23a73da9 100644 --- a/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java +++ b/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java @@ -129,6 +129,7 @@ public void sendDiningNotify() { } if (LocalTime.now().isAfter(diningType.getStartTime().minusMinutes(10)) + && LocalTime.now().isBefore(diningType.getStartTime()) && !diningNotifyCacheRepository.existsById(diningNotifyId) && diningRepository.existsByDateAndTypeAndImageUrlIsNotNull(nowDate, diningType) ) { diff --git a/src/test/java/in/koreatech/koin/acceptance/DiningApiTest.java b/src/test/java/in/koreatech/koin/acceptance/DiningApiTest.java index f185ba869..626250b09 100644 --- a/src/test/java/in/koreatech/koin/acceptance/DiningApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/DiningApiTest.java @@ -517,12 +517,19 @@ void setUp() { setUp(); } + @Test + void 이미지가_모두_존재하지_않으면_알림이_발송되지_않는다() throws Exception { + coopService.sendDiningNotify(); + + forceVerify(() -> verify(coopEventListener, never()).onDiningImageUploadRequest(any())); + clear(); + setUp(); + } + @Test void 이미지가_모두_존재하고_오픈시간이고_Redis에_키가_있으면_알림이_발송되지_않는다() throws Exception { String diningNotifyId = LocalDate.now(clock).toString() + LUNCH; diningNotifyCacheRepository.save(DiningNotifyCache.from(diningNotifyId)); - B코너_점심.setImageUrl("https://stage.koreatech.in/image.jpg"); - diningRepository.save(B코너_점심); coopService.sendDiningNotify(); forceVerify(() -> verify(coopEventListener, never()).onDiningImageUploadRequest(any())); From 48f04c38b5c959ab250f272e8d11f433d48a270c Mon Sep 17 00:00:00 2001 From: dradnats1012 Date: Sun, 15 Dec 2024 12:32:48 +0900 Subject: [PATCH 13/14] =?UTF-8?q?fix:=20conflict=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../koin/domain/coop/service/CoopService.java | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java b/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java index 1d42738d6..bed2fb7b7 100644 --- a/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java +++ b/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java @@ -281,24 +281,4 @@ private ByteArrayInputStream writeWorkbookToStream(SXSSFWorkbook workbook) throw return new ByteArrayInputStream(out.toByteArray()); } } - - private static CellStyle makeCommonStyle(Workbook workbook) { - CellStyle commonStyle = workbook.createCellStyle(); - commonStyle.setAlignment(HorizontalAlignment.CENTER); - commonStyle.setVerticalAlignment(VerticalAlignment.CENTER); - commonStyle.setWrapText(true); - return commonStyle; - } - - private static CellStyle makeHeaderStyle(Workbook workbook) { - CellStyle headerStyle = workbook.createCellStyle(); - Font font = workbook.createFont(); - font.setBold(true); - font.setColor(IndexedColors.WHITE.getIndex()); - headerStyle.setFont(font); - headerStyle.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex()); - headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); - headerStyle.setAlignment(HorizontalAlignment.CENTER); - return headerStyle; - } } From c505ea6a6d1bdc9fcf0b034e7c00df1fb87672bd Mon Sep 17 00:00:00 2001 From: dradnats1012 Date: Sun, 15 Dec 2024 12:33:54 +0900 Subject: [PATCH 14/14] =?UTF-8?q?fix:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/in/koreatech/koin/domain/coop/service/CoopService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java b/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java index bed2fb7b7..a1182e3c1 100644 --- a/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java +++ b/src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java @@ -71,7 +71,7 @@ public class CoopService { private final CoopShopService coopShopService; private final PasswordEncoder passwordEncoder; private final JwtProvider jwtProvider; - List placeFilters = Arrays.asList("A코너", "B코너", "C코너"); + private final List placeFilters = Arrays.asList("A코너", "B코너", "C코너"); public static final LocalDate LIMIT_DATE = LocalDate.of(2022, 11, 29); private final int EXCEL_COLUMN_COUNT = 8;