diff --git a/src/main/java/in/koreatech/koin/domain/coop/exception/DuplicateExcelRequestException.java b/src/main/java/in/koreatech/koin/domain/coop/exception/DuplicateExcelRequestException.java new file mode 100644 index 000000000..2fd98dd62 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/coop/exception/DuplicateExcelRequestException.java @@ -0,0 +1,23 @@ +package in.koreatech.koin.domain.coop.exception; + +import java.time.LocalDate; + +import in.koreatech.koin.global.exception.DuplicationException; + +public class DuplicateExcelRequestException extends DuplicationException { + + private static final String DEFAULT_MESSAGE = "동일한 요청을 30초 안에 다시 보낼 수 없습니다!"; + + public DuplicateExcelRequestException(String message) { + super(message); + } + + public DuplicateExcelRequestException(String message, String detail) { + super(message, detail); + } + + public static DuplicateExcelRequestException withDetail(LocalDate startDate, LocalDate endDate) { + return new DuplicateExcelRequestException(DEFAULT_MESSAGE, + "startDate: '" + startDate + "'" + "endDate: " + endDate); + } +} \ No newline at end of file diff --git a/src/main/java/in/koreatech/koin/domain/coop/model/ExcelDownloadCache.java b/src/main/java/in/koreatech/koin/domain/coop/model/ExcelDownloadCache.java new file mode 100644 index 000000000..c37d131d4 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/coop/model/ExcelDownloadCache.java @@ -0,0 +1,37 @@ +package in.koreatech.koin.domain.coop.model; + +import java.time.LocalDate; +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(value = "excelDownload") +public class ExcelDownloadCache { + + private static final long CACHE_EXPIRE_SECONDS = 30L; + + @Id + private String id; + + @TimeToLive(unit = TimeUnit.SECONDS) + private final Long expiration; + + @Builder + private ExcelDownloadCache(String id, Long expiration) { + this.id = id; + this.expiration = expiration; + } + + public static ExcelDownloadCache from(LocalDate startDate, LocalDate endDate) { + return ExcelDownloadCache.builder() + .id(startDate.toString() + endDate.toString()) + .expiration(CACHE_EXPIRE_SECONDS) + .build(); + } +} diff --git a/src/main/java/in/koreatech/koin/domain/coop/repository/ExcelDownloadCacheRepository.java b/src/main/java/in/koreatech/koin/domain/coop/repository/ExcelDownloadCacheRepository.java new file mode 100644 index 000000000..29500b882 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/coop/repository/ExcelDownloadCacheRepository.java @@ -0,0 +1,16 @@ +package in.koreatech.koin.domain.coop.repository; + +import java.util.Optional; + +import org.springframework.data.repository.Repository; + +import in.koreatech.koin.domain.coop.model.ExcelDownloadCache; + +public interface ExcelDownloadCacheRepository extends Repository { + + ExcelDownloadCache save(ExcelDownloadCache excelDownloadCache); + + Optional findById(String id); + + boolean existsById(String id); +} 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 a1182e3c1..692c6aeda 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 @@ -36,14 +36,17 @@ import in.koreatech.koin.domain.coop.dto.SoldOutRequest; import in.koreatech.koin.domain.coop.exception.DiningLimitDateException; import in.koreatech.koin.domain.coop.exception.DiningNowDateException; +import in.koreatech.koin.domain.coop.exception.DuplicateExcelRequestException; 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.model.ExcelDownloadCache; 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.coop.repository.ExcelDownloadCacheRepository; import in.koreatech.koin.domain.coopshop.model.CoopShopType; import in.koreatech.koin.domain.coopshop.service.CoopShopService; import in.koreatech.koin.domain.dining.model.Dining; @@ -65,6 +68,7 @@ public class CoopService { private final ApplicationEventPublisher eventPublisher; private final DiningRepository diningRepository; private final DiningSoldOutCacheRepository diningSoldOutCacheRepository; + private final ExcelDownloadCacheRepository excelDownloadCacheRepository; private final DiningNotifyCacheRepository diningNotifyCacheRepository; private final CoopRepository coopRepository; private final UserTokenRepository userTokenRepository; @@ -166,6 +170,7 @@ public CoopLoginResponse coopLogin(CoopLoginRequest request) { } public ByteArrayInputStream generateDiningExcel(LocalDate startDate, LocalDate endDate, Boolean isCafeteria) { + checkDuplicateExcelRequest(startDate, endDate); validateDates(startDate, endDate); List dinings = fetchDiningData(startDate, endDate, isCafeteria); @@ -192,7 +197,6 @@ private void validateDates(LocalDate startDate, LocalDate endDate) { if (startDate.isAfter(today) || endDate.isAfter(today)) { throw new DiningNowDateException("오늘 날짜 이후 기간은 설정할 수 없어요."); } - if (startDate.isAfter(endDate)) { throw new StartDateAfterEndDateException("시작일은 종료일 이전으로 설정해주세요."); } @@ -265,10 +269,10 @@ private void fillDiningRow(Dining dining, Row row, CellStyle commonStyle) { row.createCell(6).setCellValue(Optional.ofNullable(dining.getSoldOut()).map(Object::toString).orElse("")); row.createCell(7).setCellValue(Optional.ofNullable(dining.getIsChanged()).map(Object::toString).orElse("")); - for (int i = 0; i < EXCEL_COLUMN_COUNT; i++) { - row.getCell(i).setCellStyle(commonStyle); - } + for (int i = 0; i < EXCEL_COLUMN_COUNT; i++) { + row.getCell(i).setCellStyle(commonStyle); } + } private String formatMenu(List menu) { return String.join("\n", menu); @@ -281,4 +285,13 @@ private ByteArrayInputStream writeWorkbookToStream(SXSSFWorkbook workbook) throw return new ByteArrayInputStream(out.toByteArray()); } } + + private void checkDuplicateExcelRequest(LocalDate startDate, LocalDate endDate) { + boolean isCacheExist = excelDownloadCacheRepository.existsById(startDate.toString() + endDate.toString()); + + if (isCacheExist) { + throw DuplicateExcelRequestException.withDetail(startDate, endDate); + } + excelDownloadCacheRepository.save(ExcelDownloadCache.from(startDate, endDate)); + } }