Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -6,19 +6,27 @@
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;

@Slf4j(topic = "Holiday Scheduler")
@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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -33,6 +38,9 @@ public class OperatingScheduler {
@EventListener(ApplicationReadyEvent.class)
public void updateOperatingTime() {

// 배포 서버에서만 실행
if(!env.equals("prod")) return;

try{
setState();
log.info("운영 시간 업데이트");
Expand All @@ -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, () -> {
Expand All @@ -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, () -> {
Expand All @@ -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, () -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
// 자연계 교직원 식당
Expand All @@ -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("------------------종료-------------------");

}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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<String> customOrder = Arrays.asList("천원의아침", "천원의아침(테이크아웃)", "중식(한식반상)", "중식(일품반상)");

private final CafeteriaMenuRepository cafeteriaMenuRepository;
private final PlaceRepository placeRepository;
Expand Down Expand Up @@ -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<String> 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<String, String> 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);
Expand Down Expand Up @@ -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
);

Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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);
}

}
}
}
Expand Down
Loading