Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: 이미지 업로드 알림 로직 분리 #1124

Merged
merged 17 commits into from
Dec 15, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ ResponseEntity<Void> changeSoldOut(
);

@ApiResponses(

value = {
@ApiResponse(responseCode = "200"),
@ApiResponse(responseCode = "400", content = @Content(schema = @Schema(hidden = true))),
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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, String> {

DiningNotifyCache save(DiningNotifyCache diningNotifyCache);

boolean existsById(String diningNotifyId);

Optional<DiningNotifyCache> findById(String diningPlace);

default DiningNotifyCache getById(String diningPlace) {
return findById(diningPlace).orElseThrow(
() -> DiningCacheNotFoundException.withDetail("diningSoldOutCache: " + diningPlace));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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<String> placeFilters = Arrays.asList("A코너", "B코너", "C코너");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C
여기 접근 제어 안해줘도 되나요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 나이스입니다


private final int EXCEL_COLUMN_COUNT = 8;

Expand All @@ -91,15 +97,56 @@ 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());
}

public void sendDiningNotify() {
DiningType diningType = coopShopService.getDiningType();
LocalDate nowDate = LocalDate.now(clock);
List<Dining> dinings = diningRepository.findAllByDateAndType(nowDate, diningType);

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()));
if (dinings.isEmpty()) {
return;
}
dining.setImageUrl(imageRequest.imageUrl());

boolean allImageExist = diningRepository.allExistsByDateAndTypeAndPlacesAndImageUrlIsNotNull(
nowDate, diningType, placeFilters
);

boolean isOpened = coopShopService.getIsOpened(
LocalDateTime.now(clock), CoopShopType.CAFETERIA, diningType, true
);

String diningNotifyId = nowDate.toString() + diningType;

if (isOpened && allImageExist) {
if (alreadyNotify(diningNotifyId))
return;

if (!diningNotifyCacheRepository.existsById(diningNotifyId)) {
sendNotify(diningNotifyId, dinings);
}
}

if (LocalTime.now().isAfter(diningType.getStartTime().minusMinutes(10))
&& LocalTime.now().isBefore(diningType.getStartTime())
&& !diningNotifyCacheRepository.existsById(diningNotifyId)
&& diningRepository.existsByDateAndTypeAndImageUrlIsNotNull(nowDate, diningType)
) {
sendNotify(diningNotifyId, dinings);
}
}

private boolean alreadyNotify(String diningNotifyId) {
if (diningNotifyCacheRepository.existsById(diningNotifyId)) {
return true;
}
return false;
}

private void sendNotify(String diningNotifyId, List<Dining> dinings) {
diningNotifyCacheRepository.save(DiningNotifyCache.from(diningNotifyId));
eventPublisher.publishEvent(new DiningImageUploadEvent(dinings.get(0).getId(), dinings.get(0).getImageUrl()));
}

@Transactional
Expand All @@ -125,7 +172,6 @@ public ByteArrayInputStream generateDiningExcel(LocalDate startDate, LocalDate e
List<Dining> dinings;

if (isCafeteria) {
List<String> placeFilters = Arrays.asList("A코너", "B코너", "C코너");
dinings = diningRepository.findByDateBetweenAndPlaceIn(startDate, endDate, placeFilters);
} else {
dinings = diningRepository.findByDateBetween(startDate, endDate);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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 * * *")
@Scheduled(cron = "0 30/6 10-11 * * *")
@Scheduled(cron = "0 30/6 16-17 * * *")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A
한번에 여러 개 되는 지는 몰랐네요

public void notifyDiningImageUpload() {
try {
coopService.sendDiningNotify();
} catch (Exception e) {
log.warn("식단 이미지 알림 과정에서 오류가 발생했습니다.");
}
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -71,6 +74,23 @@ public boolean getIsOpened(LocalDateTime now, CoopShopType coopShopType, DiningT
}
}

public DiningType getDiningType(){
if(LocalTime.now(clock).isAfter(BREAKFAST.getStartTime().minusHours(1))
&& LocalTime.now(clock).isBefore(BREAKFAST.getEndTime())){
return BREAKFAST;
}
if(LocalTime.now(clock).isAfter(LUNCH.getStartTime().minusHours(1))
&& LocalTime.now(clock).isBefore(LUNCH.getEndTime())){
return LUNCH;
}
if(LocalTime.now(clock).isAfter(DINNER.getStartTime().minusHours(1))
&& LocalTime.now(clock).isBefore(DINNER.getEndTime())){
return DINNER;
}

throw DiningTypeNotFoundException.withDetail(LocalTime.now() + "");
}

@Transactional
public void updateSemester() {
CoopSemester currentSemester = coopSemesterRepository.getByIsApplied(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -36,4 +37,15 @@ default Dining getById(Integer id) {
List<Dining> findByDateBetween(LocalDate startDate, LocalDate endDate);

List<Dining> findByDateBetweenAndPlaceIn(LocalDate startDate, LocalDate endDate, List<String> placeFilters);

Optional<List<Dining>> findByDate(LocalDate now);

default List<Dining> 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<String> places);
}
Loading
Loading