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 @@ -146,6 +146,7 @@ private String findPlaceOperatingTime(Place place, DayOfWeek dayOfWeek) {

// 장소의 운영 시간이 시간 형식이 아니면 건물의 운영 시간을 따라감
if(!isTimeRangePattern(operatingTime)) {
if(place.getBuilding() == null) return DEFAULT_OPERATING_TIME;
operatingTime = place.getBuilding().getOperatingTime();
}

Expand Down Expand Up @@ -327,10 +328,10 @@ public void updatePlaceOperatingTime() {
for(Place place : places) {
if(!placesWithCondition.contains(place)) {
// 조건이 없는 장소는 건물의 운영 여부 및 운영 시간과 동일하도록 세팅
place.setSundayOperatingTime(place.getBuilding().getSundayOperatingTime());
place.setSaturdayOperatingTime(place.getBuilding().getSaturdayOperatingTime());
place.setWeekdayOperatingTime(place.getBuilding().getWeekdayOperatingTime());
place.setOperating(place.getBuilding().isOperating());
place.setSundayOperatingTime(place.getBuilding() == null ? DEFAULT_OPERATING_TIME : place.getBuilding().getSundayOperatingTime());
place.setSaturdayOperatingTime(place.getBuilding() == null ? DEFAULT_OPERATING_TIME : place.getBuilding().getSaturdayOperatingTime());
place.setWeekdayOperatingTime(place.getBuilding() == null ? DEFAULT_OPERATING_TIME : place.getBuilding().getWeekdayOperatingTime());
place.setOperating(place.getBuilding() == null ? true : place.getBuilding().isOperating());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package devkor.com.teamcback.domain.place.controller;

import devkor.com.teamcback.domain.place.dto.response.GetCafeteriaMenuListRes;
import devkor.com.teamcback.domain.place.service.CafeteriaMenuService;
import devkor.com.teamcback.global.response.CommonResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDate;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/places/cafeterias")
public class CafeteriaMenuController {

private final CafeteriaMenuService cafeteriaMenuService;

@Operation(summary = "건물 id와 날짜로 학식 메뉴 검색",
description = "건물 id와 날짜로 학식 메뉴 검색")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
@ApiResponse(responseCode = "401", description = "권한이 없습니다.",
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
})
@GetMapping("/menus")
public CommonResponse<GetCafeteriaMenuListRes> getCafeteriaMenu(
@Parameter(name = "placeId", description = "장소 ID") @RequestParam Long placeId,
@Parameter(name = "startDate", description = "2025-12-25 형식의 요청 시작 날짜") @RequestParam LocalDate startDate,
@Parameter(name = "endDate", description = "2025-12-25 형식의 요청 마지막 날짜") @RequestParam LocalDate endDate) {

return CommonResponse.success(cafeteriaMenuService.getCafeteriaMenu(placeId, startDate, endDate));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package devkor.com.teamcback.domain.place.dto.response;

import lombok.Getter;

import java.time.LocalDate;
import java.util.HashMap;
import java.util.Map;

@Getter
public class GetCafeteriaMenuListRes {
private Long placeId;
private String placeName;
private String address;
private String operatingTime;
private String contact;
private Map<LocalDate, Map<String, String>> menus = new HashMap<>();

public GetCafeteriaMenuListRes(Long placeId, String name, String address, String operatingTime, String contact, Map<LocalDate, Map<String, String>> menus) {
this.placeId = placeId;
this.placeName = name;
this.address = address;
this.operatingTime = operatingTime;
this.contact = contact;
this.menus = menus;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ public class Place extends BaseEntity {
@Column(nullable = false)
private Integer starNum = 0;

@Column
private String contact; // 연락처 등

@ManyToOne
@JoinColumn(name = "building_id")
private Building building;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
import org.springframework.data.jpa.repository.JpaRepository;

import java.time.LocalDate;
import java.util.List;

public interface CafeteriaMenuRepository extends JpaRepository<CafeteriaMenu, Long> {
CafeteriaMenu findByDateAndKindAndPlaceId(LocalDate date, String kind, Long placeId);

List<CafeteriaMenu> findByDateAndPlaceId(LocalDate date, Long placeId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import devkor.com.teamcback.global.redis.RedisLockUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

Expand All @@ -15,15 +17,15 @@ public class CafeteriaMenuScheduler {
private final CafeteriaMenuService cafeteriaMenuService;
private final RedisLockUtil redisLockUtil;

// @EventListener(ApplicationReadyEvent.class)
// @EventListener(ApplicationReadyEvent.class)
@Scheduled(cron = "0 10 0 * * *") // 매일 자정 10분마다
public void updateMenus() {
redisLockUtil.executeWithLock("menu_lock", 1, 300, () -> {

System.out.println("--- 고려대학교 학식메뉴 스크래핑 시작 ---");

// 수당삼양패컬티하우스 송림
// cafeteriaMenuService.scrapeMenu(503, 3103L);
cafeteriaMenuService.scrapeMenu(503, 9757L);
// 자연계 학생식당
cafeteriaMenuService.scrapeMenu(504, 3103L);
// 자연계 교직원 식당
Expand All @@ -35,7 +37,7 @@ public void updateMenus() {
// 교우회관 학생식당
cafeteriaMenuService.scrapeMenu(507, 7705L);
// 학생회관 학생식당
// cafeteriaMenuService.scrapeMenu(508, 3103L);
cafeteriaMenuService.scrapeMenu(508, 9758L);

System.out.println("------------------종료-------------------");
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package devkor.com.teamcback.domain.place.service;

import devkor.com.teamcback.domain.building.entity.Building;
import devkor.com.teamcback.domain.building.repository.BuildingRepository;
import devkor.com.teamcback.domain.place.dto.response.GetCafeteriaMenuListRes;
import devkor.com.teamcback.domain.place.entity.CafeteriaMenu;
import devkor.com.teamcback.domain.place.entity.Place;
import devkor.com.teamcback.domain.place.repository.CafeteriaMenuRepository;
Expand All @@ -17,7 +20,9 @@
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.regex.Matcher;
import java.util.regex.Pattern;

Expand All @@ -36,6 +41,37 @@ public class CafeteriaMenuService {
private final CafeteriaMenuRepository cafeteriaMenuRepository;
private final PlaceRepository placeRepository;

/**
* 학생식당 메뉴 조회
*/
@Transactional(readOnly = true)
public GetCafeteriaMenuListRes getCafeteriaMenu(Long placeId, LocalDate startDate, LocalDate endDate) {
Place place = placeRepository.findById(placeId).orElseThrow(() -> new GlobalException(ResultCode.NOT_FOUND_PLACE));

String address = place.getBuilding() == null ? "" : place.getBuilding().getName() + " ";
address += place.getFloor() < 0 ? "B" : "";
address += (int)Math.abs(place.getFloor()) + "층";

Map<LocalDate, Map<String, String>> menuMap = new HashMap<>();

startDate
.datesUntil(endDate)
.forEach(date -> {
List<CafeteriaMenu> cafeteriaMenuList = cafeteriaMenuRepository.findByDateAndPlaceId(date, placeId);

Map<String, String> menuByKind = new HashMap<>();
for (CafeteriaMenu cafeteriaMenu : cafeteriaMenuList) {
menuByKind.put(cafeteriaMenu.getKind(), cafeteriaMenu.getMenu());
}

menuMap.put(date, menuByKind);
});

GetCafeteriaMenuListRes res = new GetCafeteriaMenuListRes(placeId, place.getName(), address, place.getDetail(), place.getContact(), menuMap);

return res;
}

/**
* 웹 페이지에서 식단 정보를 스크래핑하고 리스트로 반환합니다.
*/
Expand Down Expand Up @@ -78,7 +114,7 @@ public void scrapeMenu(int page, Long placeId) {

// 5. 데이터를 날짜-요일 패턴을 기준으로 분리하여 블록화
// 패턴: (YYYY.MM.DD (요일))
Pattern dateDayPattern = Pattern.compile("(\\d{4}\\.\\d{2}\\.\\d{2})\\s*\\(\\s*[월화수목금토일]\\s*\\)\\s*");
Pattern dateDayPattern = Pattern.compile("(\\d{4}\\.\\d{2}\\.\\d{2})\\s*");
Matcher dateDayMatcher = dateDayPattern.matcher(cleanedHtmlContent);

List<String> blocks = new ArrayList<>();
Expand Down