diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/scheduler/HolidayScheduler.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/scheduler/HolidayScheduler.java index 009a68ab..ba46a4a6 100644 --- a/src/main/java/devkor/com/teamcback/domain/operatingtime/scheduler/HolidayScheduler.java +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/scheduler/HolidayScheduler.java @@ -6,6 +6,7 @@ import java.time.LocalDateTime; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -13,12 +14,19 @@ @Component @RequiredArgsConstructor public class HolidayScheduler { + + @Value("${metrics.environment}") + private String env; + private final HolidayService holidayService; private final RedisLockUtil redisLockUtil; // @Scheduled(cron = "0 * * * * *") // 테스트용 @Scheduled(cron = "0 0 0 1 * ?") // 매달 1일 자정마다 public void updateHoliday() { + // 배포 서버에서만 실행 + if(!env.equals("prod")) return; + redisLockUtil.executeWithLock("lock", 1, 300, () -> { LocalDateTime nowTime = LocalDateTime.now(); try { diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/scheduler/OperatingScheduler.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/scheduler/OperatingScheduler.java index 9f1e94b3..a86bbc05 100644 --- a/src/main/java/devkor/com/teamcback/domain/operatingtime/scheduler/OperatingScheduler.java +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/scheduler/OperatingScheduler.java @@ -10,6 +10,7 @@ import java.time.temporal.TemporalAdjusters; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Scheduled; @@ -19,6 +20,10 @@ @Component @RequiredArgsConstructor public class OperatingScheduler { + + @Value("${metrics.environment}") + private String env; + private final OperatingService operatingService; private final HolidayService holidayService; private final RedisLockUtil redisLockUtil; @@ -33,6 +38,9 @@ public class OperatingScheduler { @EventListener(ApplicationReadyEvent.class) public void updateOperatingTime() { + // 배포 서버에서만 실행 + if(!env.equals("prod")) return; + try{ setState(); log.info("운영 시간 업데이트"); @@ -50,6 +58,10 @@ public void updateOperatingTime() { // @EventListener(ApplicationReadyEvent.class) // 테스트용 @Scheduled(cron = "0 */10 9-18 * * *") // 10분마다 public void updateOperatingDuringPeakHour() { + + // 배포 서버에서만 실행 + if(!env.equals("prod")) return; + try{ log.info("운영 여부 업데이트"); redisLockUtil.executeWithLock("lock", 1, 300, () -> { @@ -63,6 +75,10 @@ public void updateOperatingDuringPeakHour() { @Scheduled(cron = "0 0,30 0-8,19-23 * * *") // 30분마다 public void updateOperating() { + + // 배포 서버에서만 실행 + if(!env.equals("prod")) return; + try{ log.info("운영 여부 업데이트"); redisLockUtil.executeWithLock("lock", 1, 300, () -> { @@ -77,6 +93,10 @@ public void updateOperating() { // 장소 운영 시간 저장 - 건물의 운영 시간에 변동이 있을 경우 1회 작동 @EventListener(ApplicationReadyEvent.class) public void updatePlaceOperatingTime() { + + // 배포 서버에서만 실행 + if(!env.equals("prod")) return; + try{ log.info("장소 운영 시간 업데이트"); redisLockUtil.executeWithLock("lock", 1, 300, () -> { diff --git a/src/main/java/devkor/com/teamcback/domain/place/scheduler/CafeteriaMenuScheduler.java b/src/main/java/devkor/com/teamcback/domain/place/scheduler/CafeteriaMenuScheduler.java index 3133f075..37bd80c6 100644 --- a/src/main/java/devkor/com/teamcback/domain/place/scheduler/CafeteriaMenuScheduler.java +++ b/src/main/java/devkor/com/teamcback/domain/place/scheduler/CafeteriaMenuScheduler.java @@ -4,6 +4,7 @@ import devkor.com.teamcback.global.redis.RedisLockUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Scheduled; @@ -14,18 +15,28 @@ @RequiredArgsConstructor public class CafeteriaMenuScheduler { + @Value("${metrics.environment}") + private String env; + private final CafeteriaMenuService cafeteriaMenuService; private final RedisLockUtil redisLockUtil; // @EventListener(ApplicationReadyEvent.class) @Scheduled(cron = "0 10 0 * * *") // 매일 자정 10분마다 public void updateMenus() { - redisLockUtil.executeWithLock("menu_lock", 1, 300, () -> { - System.out.println("--- 고려대학교 학식메뉴 스크래핑 시작 ---"); + System.out.println("--- 고려대학교 학식메뉴 스크래핑 시작 ---"); + + // 배포 서버에서만 실행 + if(!env.equals("prod")) { + System.out.println("--- 현재 서버: " + env + " ---"); + return; + } + + redisLockUtil.executeWithLock("menu_lock", 1, 300, () -> { // 수당삼양패컬티하우스 송림 - cafeteriaMenuService.scrapeMenu(503, 9757L); + cafeteriaMenuService.scrapeMenu(503, 9758L); // 자연계 학생식당 cafeteriaMenuService.scrapeMenu(504, 3103L); // 자연계 교직원 식당 @@ -37,12 +48,13 @@ public void updateMenus() { // 교우회관 학생식당 cafeteriaMenuService.scrapeMenu(507, 7705L); // 학생회관 학생식당 - cafeteriaMenuService.scrapeMenu(508, 9758L); + cafeteriaMenuService.scrapeMenu(508, 9757L); - System.out.println("------------------종료-------------------"); return null; }); + System.out.println("------------------종료-------------------"); + } } diff --git a/src/main/java/devkor/com/teamcback/domain/place/service/CafeteriaMenuService.java b/src/main/java/devkor/com/teamcback/domain/place/service/CafeteriaMenuService.java index 5f918a3b..f4a587f7 100644 --- a/src/main/java/devkor/com/teamcback/domain/place/service/CafeteriaMenuService.java +++ b/src/main/java/devkor/com/teamcback/domain/place/service/CafeteriaMenuService.java @@ -19,10 +19,7 @@ import java.io.IOException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -37,6 +34,8 @@ public class CafeteriaMenuService { private static final String TABLE_SELECTOR = ".table_1 table"; // 식단 메뉴가 없을 때 문구 private static final String NO_MENU_INFO = "등록된 식단내용이(가) 없습니다."; + // 학생식당 식단 종류 순서 지정 + private static final List customOrder = Arrays.asList("천원의아침", "천원의아침(테이크아웃)", "중식(한식반상)", "중식(일품반상)"); private final CafeteriaMenuRepository cafeteriaMenuRepository; private final PlaceRepository placeRepository; @@ -64,7 +63,35 @@ public GetCafeteriaMenuListRes getCafeteriaMenu(Long placeId, LocalDate startDat menuByKind.put(cafeteriaMenu.getKind(), cafeteriaMenu.getMenu()); } - menuMap.put(date, menuByKind); + if(placeId == 9757) { + // 식단 종류에 따라 정렬하기 + Comparator customComparator = (key1, key2) -> { + int index1 = customOrder.indexOf(key1); + int index2 = customOrder.indexOf(key2); + + // 맵에 정의되지 않은 키는 맨 뒤로 보냅니다 (큰 값 부여) + if (index1 == -1) index1 = customOrder.size(); + if (index2 == -1) index2 = customOrder.size(); + + // 순서 비교: index1이 작으면 먼저 옵니다. + int comparison = Integer.compare(index1, index2); + + // customOrder에 없는 키끼리의 순서는 원래 문자열 비교(알파벳순)를 따릅니다. + if (comparison == 0 && index1 >= customOrder.size()) { + return key1.compareTo(key2); + } + return comparison; + }; + + // 정렬된 맵 생성 + Map sortedMap = new TreeMap<>(customComparator); + sortedMap.putAll(menuByKind); + + menuMap.put(date, sortedMap); + } + else { + menuMap.put(date, menuByKind); + } }); GetCafeteriaMenuListRes res = new GetCafeteriaMenuListRes(placeId, place.getName(), address, place.getDetail(), place.getContact(), menuMap); @@ -140,7 +167,7 @@ public void scrapeMenu(int page, Long placeId) { // 6. 분리된 블록을 순회하며 식단 데이터 추출 // 패턴: (조식|중식|석식) (.+?) - 내용물은 하이픈(-) 기준으로 분리됩니다. Pattern menuItemsPattern = Pattern.compile( - "(조식|중식|석식|식사|요리|파스타/스테이크코스|천원의밥상)\\s*(.*?)(?=\\s*(조식|중식|석식|식사|요리|파스타/스테이크코스|천원의밥상)\\s*|$)", + "(조식|석식|식사|요리|파스타/스테이크 코스|천원의밥상|천원의아침\\(테이크아웃\\)|천원의아침|중식\\(한식반상\\)|중식\\(일품반상\\)|중식 A|중식 B|중식)\\s*(.*?)(?=\\s*(조식|석식|식사|요리|파스타/스테이크 코스|천원의밥상|천원의아침\\(테이크아웃\\)|천원의아침|중식\\(한식반상\\)|중식\\(일품반상\\)|중식 A|중식 B|중식)\\s*|$)", Pattern.DOTALL ); @@ -174,6 +201,27 @@ public void scrapeMenu(int page, Long placeId) { if (!content.isEmpty()) { + // 애기능 메뉴 정리 + if(placeId == 3103 || placeId == 2490) { + content = content.replaceAll("\\s*/\\s*", "/"); + content = content.replaceAll("(\\s+)의(\\s+)", "의"); + content = content.replaceAll("사이드메뉴: ", "사이드메뉴:"); + } + + // 안암학사 메뉴 정리 + if(placeId == 3654) { + if(content.contains("또는")) { + content = content.replaceAll(" ", ""); + content = content.replaceAll("또는", "/"); + } + content = content.replaceAll("/", " ").trim(); + } + + // 학생회관 메뉴 정리 + if(placeId == 9757) { + content = content.split("-제공되는 메뉴")[0].trim(); + } + // 애기능 - 학생식당 if(placeId == 3103) { if(!content.contains("[학생식당]")) content = NO_MENU_INFO; @@ -182,7 +230,7 @@ public void scrapeMenu(int page, Long placeId) { // 애기능 - 교직원식당 else if(placeId == 2490) { if(!content.contains("[교직원식당]")) content = NO_MENU_INFO; - else content = content.substring(content.lastIndexOf("[교직원식당]") + "[교직원식당]".length()).trim(); + else content = content.substring(content.lastIndexOf("[교직원식당]") + "[교직원식당]".length(), content.contains("[학생식당]") && content.lastIndexOf("[학생식당]") > content.lastIndexOf("[교직원식당]")? content.lastIndexOf("[학생식당]") : content.length()).trim(); } CafeteriaMenu savedMenu = cafeteriaMenuRepository.findByDateAndKindAndPlaceId(date, kind, placeId); @@ -201,15 +249,6 @@ else if(!savedMenu.getMenu().equals(content)) { savedMenu.setMenu(content); } - // 당일에 해당하는 경우 식당 설명 수정 - if(date.equals(LocalDate.now())) { - if(!updated) { - place.setDescription(kind + " - " + content); - updated = true; - } - else place.setDescription(place.getDescription() + "\n" + kind + " - " + content); - } - } } }