From 3ce38a96e84e2b23785bc967f68c1353397d8b1d Mon Sep 17 00:00:00 2001 From: Shin Hyeoncheol <65756225+moonn6pence@users.noreply.github.com> Date: Wed, 15 Nov 2023 16:05:12 +0900 Subject: [PATCH] =?UTF-8?q?feat=20:=20=EC=A0=90=EC=8B=AC=20=EC=88=98?= =?UTF-8?q?=EB=8F=99=20=ED=81=AC=EB=A1=A4=EB=A7=81=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat : 수동 스크래핑 서비스 로직 추가 * refactor : 점심 크롤링 리팩토링 - 스케줄러의 비즈니스 로직을 서비스단으로 분리 * feat : 수동 스크래핑 컨트롤러 추가 * refactor : 크롤링 관련 패키지 구조 변경 * refactor : 추상화 리팩토링 --- .../domain/auth/exception/AuthErrorInfo.java | 3 +- .../lunch/controller/LunchController.java | 9 ++ .../domain/FreshmealProperties.java | 2 +- .../{task => }/domain/WelstoryProperties.java | 2 +- .../{task => }/dto/GetFreshmealResDto.java | 28 ++++- .../lunch/{task => }/dto/GetScrapReqDto.java | 2 +- .../domain/lunch/dto/GetScrapResDto.java | 10 ++ .../{task => }/dto/GetWelstoryResDto.java | 27 ++++- .../lunch/service/FreshmealInfoProvider.java | 91 +++++++++++++++ .../lunch/service/LunchScrapService.java | 83 +++++++++++++ .../lunch/service/ScrapInfoProvider.java | 10 ++ .../ScrapInfoProviderFactory.java | 5 +- .../WelstoryInfoProvider.java | 69 ++++++----- .../domain/lunch/task/LunchScraper.java | 109 ++---------------- .../task/domain/FreshmealInfoProvider.java | 75 ------------ .../lunch/task/domain/ScrapInfoProvider.java | 8 -- .../domain/lunch/task/dto/GetScrapResDto.java | 4 - .../ssafsound/global/config/ScrapConfig.java | 4 +- 18 files changed, 320 insertions(+), 221 deletions(-) rename src/main/java/com/ssafy/ssafsound/domain/lunch/{task => }/domain/FreshmealProperties.java (90%) rename src/main/java/com/ssafy/ssafsound/domain/lunch/{task => }/domain/WelstoryProperties.java (93%) rename src/main/java/com/ssafy/ssafsound/domain/lunch/{task => }/dto/GetFreshmealResDto.java (67%) rename src/main/java/com/ssafy/ssafsound/domain/lunch/{task => }/dto/GetScrapReqDto.java (81%) create mode 100644 src/main/java/com/ssafy/ssafsound/domain/lunch/dto/GetScrapResDto.java rename src/main/java/com/ssafy/ssafsound/domain/lunch/{task => }/dto/GetWelstoryResDto.java (63%) create mode 100644 src/main/java/com/ssafy/ssafsound/domain/lunch/service/FreshmealInfoProvider.java create mode 100644 src/main/java/com/ssafy/ssafsound/domain/lunch/service/LunchScrapService.java create mode 100644 src/main/java/com/ssafy/ssafsound/domain/lunch/service/ScrapInfoProvider.java rename src/main/java/com/ssafy/ssafsound/domain/lunch/{task/domain => service}/ScrapInfoProviderFactory.java (71%) rename src/main/java/com/ssafy/ssafsound/domain/lunch/{task/domain => service}/WelstoryInfoProvider.java (56%) delete mode 100644 src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/FreshmealInfoProvider.java delete mode 100644 src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/ScrapInfoProvider.java delete mode 100644 src/main/java/com/ssafy/ssafsound/domain/lunch/task/dto/GetScrapResDto.java diff --git a/src/main/java/com/ssafy/ssafsound/domain/auth/exception/AuthErrorInfo.java b/src/main/java/com/ssafy/ssafsound/domain/auth/exception/AuthErrorInfo.java index 7cfade7db..c23f14ed5 100644 --- a/src/main/java/com/ssafy/ssafsound/domain/auth/exception/AuthErrorInfo.java +++ b/src/main/java/com/ssafy/ssafsound/domain/auth/exception/AuthErrorInfo.java @@ -9,7 +9,8 @@ public enum AuthErrorInfo { AUTH_SERVER_PARSING_ERROR("702", "서버에서 파싱하는데 문제가 발생했습니다."), AUTH_TOKEN_INVALID("705", "토큰이 유효하지 않습니다."), AUTH_TOKEN_EXPIRED("706", "토큰이 만료됐습니다."), - AUTH_TOKEN_SERVICE_ERROR("707", "서버에서 토큰을 처리하는 과정에서 문제가 발생했습니다."); + AUTH_TOKEN_SERVICE_ERROR("707", "서버에서 토큰을 처리하는 과정에서 문제가 발생했습니다."), + UNAUTHORIZED_ERROR("708", "권한이 없습니다."); private final String code; private final String message; diff --git a/src/main/java/com/ssafy/ssafsound/domain/lunch/controller/LunchController.java b/src/main/java/com/ssafy/ssafsound/domain/lunch/controller/LunchController.java index 7f736b801..25d2c090b 100644 --- a/src/main/java/com/ssafy/ssafsound/domain/lunch/controller/LunchController.java +++ b/src/main/java/com/ssafy/ssafsound/domain/lunch/controller/LunchController.java @@ -6,6 +6,7 @@ import com.ssafy.ssafsound.domain.lunch.dto.GetLunchListResDto; import com.ssafy.ssafsound.domain.lunch.dto.PostLunchPollResDto; import com.ssafy.ssafsound.domain.lunch.service.LunchPollService; +import com.ssafy.ssafsound.domain.lunch.service.LunchScrapService; import com.ssafy.ssafsound.domain.lunch.service.LunchService; import com.ssafy.ssafsound.global.common.response.EnvelopeResponse; import lombok.RequiredArgsConstructor; @@ -25,6 +26,7 @@ public class LunchController { private final LunchService lunchService; private final LunchPollService lunchPollService; + private final LunchScrapService lunchScrapService; @GetMapping public EnvelopeResponse getLunchesByCampusAndDate(@Authentication AuthenticatedMember member, @Valid GetLunchListReqDto getLunchListReqDto) { @@ -50,4 +52,11 @@ public EnvelopeResponse revertLunchPoll(@Authentication Aut .build(); } + @PostMapping("/manual-scrap") + public EnvelopeResponse scrapWelstoryLunchManually(@Authentication AuthenticatedMember member, @Valid GetLunchListReqDto getLunchListReqDto) { + + lunchScrapService.scrapLunchManually(member, getLunchListReqDto); + return EnvelopeResponse.builder().build(); + } + } diff --git a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/FreshmealProperties.java b/src/main/java/com/ssafy/ssafsound/domain/lunch/domain/FreshmealProperties.java similarity index 90% rename from src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/FreshmealProperties.java rename to src/main/java/com/ssafy/ssafsound/domain/lunch/domain/FreshmealProperties.java index fc8da5f23..dd031c55a 100644 --- a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/FreshmealProperties.java +++ b/src/main/java/com/ssafy/ssafsound/domain/lunch/domain/FreshmealProperties.java @@ -1,4 +1,4 @@ -package com.ssafy.ssafsound.domain.lunch.task.domain; +package com.ssafy.ssafsound.domain.lunch.domain; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/WelstoryProperties.java b/src/main/java/com/ssafy/ssafsound/domain/lunch/domain/WelstoryProperties.java similarity index 93% rename from src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/WelstoryProperties.java rename to src/main/java/com/ssafy/ssafsound/domain/lunch/domain/WelstoryProperties.java index 03d2e5b39..79240ea7f 100644 --- a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/WelstoryProperties.java +++ b/src/main/java/com/ssafy/ssafsound/domain/lunch/domain/WelstoryProperties.java @@ -1,4 +1,4 @@ -package com.ssafy.ssafsound.domain.lunch.task.domain; +package com.ssafy.ssafsound.domain.lunch.domain; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/dto/GetFreshmealResDto.java b/src/main/java/com/ssafy/ssafsound/domain/lunch/dto/GetFreshmealResDto.java similarity index 67% rename from src/main/java/com/ssafy/ssafsound/domain/lunch/task/dto/GetFreshmealResDto.java rename to src/main/java/com/ssafy/ssafsound/domain/lunch/dto/GetFreshmealResDto.java index 762749e5a..8d9fef68f 100644 --- a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/dto/GetFreshmealResDto.java +++ b/src/main/java/com/ssafy/ssafsound/domain/lunch/dto/GetFreshmealResDto.java @@ -1,8 +1,10 @@ -package com.ssafy.ssafsound.domain.lunch.task.dto; +package com.ssafy.ssafsound.domain.lunch.dto; import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonProperty; import com.ssafy.ssafsound.domain.lunch.domain.Lunch; +import com.ssafy.ssafsound.domain.lunch.exception.LunchErrorInfo; +import com.ssafy.ssafsound.domain.lunch.exception.LunchException; import com.ssafy.ssafsound.domain.meta.domain.MetaData; import lombok.Data; import lombok.NoArgsConstructor; @@ -10,6 +12,7 @@ import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -21,6 +24,29 @@ public class GetFreshmealResDto implements GetScrapResDto{ @JsonProperty("data") private FreshmealBodyData data; + @Override + public List getLunches(MetaData campus) { + List daysOfWeek = Arrays.asList("mo","tu","we","th","fr"); + List lunches = new ArrayList<>(); + + for (String day : daysOfWeek) { + this.data.getDailyMeal().get(day).getMeals().forEach( + meal -> { + try { + Lunch lunch = meal.toEntity(campus); + if (lunch != null) { + lunches.add(lunch); + } + } catch (Exception e) { + throw new LunchException(LunchErrorInfo.SCRAPING_ERROR); + } + } + ); + } + + return lunches; + } + @Data @NoArgsConstructor public static class FreshmealBodyData { diff --git a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/dto/GetScrapReqDto.java b/src/main/java/com/ssafy/ssafsound/domain/lunch/dto/GetScrapReqDto.java similarity index 81% rename from src/main/java/com/ssafy/ssafsound/domain/lunch/task/dto/GetScrapReqDto.java rename to src/main/java/com/ssafy/ssafsound/domain/lunch/dto/GetScrapReqDto.java index 1bc98e846..27436ade8 100644 --- a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/dto/GetScrapReqDto.java +++ b/src/main/java/com/ssafy/ssafsound/domain/lunch/dto/GetScrapReqDto.java @@ -1,4 +1,4 @@ -package com.ssafy.ssafsound.domain.lunch.task.dto; +package com.ssafy.ssafsound.domain.lunch.dto; import com.ssafy.ssafsound.domain.meta.domain.MetaData; import lombok.Builder; diff --git a/src/main/java/com/ssafy/ssafsound/domain/lunch/dto/GetScrapResDto.java b/src/main/java/com/ssafy/ssafsound/domain/lunch/dto/GetScrapResDto.java new file mode 100644 index 000000000..6b99c89da --- /dev/null +++ b/src/main/java/com/ssafy/ssafsound/domain/lunch/dto/GetScrapResDto.java @@ -0,0 +1,10 @@ +package com.ssafy.ssafsound.domain.lunch.dto; + +import com.ssafy.ssafsound.domain.lunch.domain.Lunch; +import com.ssafy.ssafsound.domain.meta.domain.MetaData; + +import java.util.List; + +public interface GetScrapResDto { + List getLunches(MetaData campus); +} diff --git a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/dto/GetWelstoryResDto.java b/src/main/java/com/ssafy/ssafsound/domain/lunch/dto/GetWelstoryResDto.java similarity index 63% rename from src/main/java/com/ssafy/ssafsound/domain/lunch/task/dto/GetWelstoryResDto.java rename to src/main/java/com/ssafy/ssafsound/domain/lunch/dto/GetWelstoryResDto.java index d2948e128..15a0dd61e 100644 --- a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/dto/GetWelstoryResDto.java +++ b/src/main/java/com/ssafy/ssafsound/domain/lunch/dto/GetWelstoryResDto.java @@ -1,20 +1,43 @@ -package com.ssafy.ssafsound.domain.lunch.task.dto; +package com.ssafy.ssafsound.domain.lunch.dto; import com.fasterxml.jackson.annotation.JsonProperty; import com.ssafy.ssafsound.domain.lunch.domain.Lunch; +import com.ssafy.ssafsound.domain.lunch.exception.LunchErrorInfo; +import com.ssafy.ssafsound.domain.lunch.exception.LunchException; import com.ssafy.ssafsound.domain.meta.domain.MetaData; import lombok.Data; import lombok.NoArgsConstructor; import java.time.LocalDate; import java.time.format.DateTimeFormatter; +import java.util.ArrayList; import java.util.List; @Data @NoArgsConstructor public class GetWelstoryResDto implements GetScrapResDto{ @JsonProperty("data") - private WelstoryBodyData welstoryBodyData; + private WelstoryBodyData data; + + public List getLunches(MetaData campus) { + List lunches = new ArrayList<>(); + + this.data.getMealList().forEach( + meal -> { + try { + Lunch lunch = meal.toEntity(campus); + + if (lunch != null) { + lunches.add(lunch); + } + } catch (Exception e) { + throw new LunchException(LunchErrorInfo.SCRAPING_ERROR); + } + } + ); + + return lunches; + } @Data @NoArgsConstructor diff --git a/src/main/java/com/ssafy/ssafsound/domain/lunch/service/FreshmealInfoProvider.java b/src/main/java/com/ssafy/ssafsound/domain/lunch/service/FreshmealInfoProvider.java new file mode 100644 index 000000000..de68c8144 --- /dev/null +++ b/src/main/java/com/ssafy/ssafsound/domain/lunch/service/FreshmealInfoProvider.java @@ -0,0 +1,91 @@ +package com.ssafy.ssafsound.domain.lunch.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.ssafy.ssafsound.domain.lunch.domain.FreshmealProperties; +import com.ssafy.ssafsound.domain.lunch.domain.Lunch; +import com.ssafy.ssafsound.domain.lunch.dto.GetFreshmealResDto; +import com.ssafy.ssafsound.domain.lunch.dto.GetScrapReqDto; +import com.ssafy.ssafsound.global.common.json.JsonParser; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Slf4j +@Component +@RequiredArgsConstructor +public class FreshmealInfoProvider implements ScrapInfoProvider { + + private final RestTemplate restTemplate; + + private final FreshmealProperties freshmealProperties; + + @Override + public List scrapLunchInfo(List getScrapReqDtos) { + + List lunches = new ArrayList<>(); + + for (GetScrapReqDto getScrapReqDto : getScrapReqDtos) { + Map parameters = makeScrapParameters(); + + String url = makeScrapUri(this.freshmealProperties.getUrl(), parameters); + + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.GET, + makeHeader(), + String.class + ); + + try { + lunches.addAll(JsonParser.getMapper() + .readValue(response.getBody(), GetFreshmealResDto.class) + .getLunches(getScrapReqDto.getCampus())); + } catch (JsonProcessingException e) { + log.error(e.getMessage()); + e.printStackTrace(); + + throw new RuntimeException(); + } + } + + return lunches; + } + + private HttpEntity makeHeader() { + HttpHeaders header = new HttpHeaders(); + header.add("Content-Type", "application/json"); + header.add("Accept", MediaType.APPLICATION_JSON_VALUE); + return new HttpEntity(header); + } + + private Map makeScrapParameters() { + Map parameters = new HashMap<>(); + + parameters.put("storeIdx", this.freshmealProperties.getStoreIdx()); + parameters.put("weekType", this.freshmealProperties.getWeekType()); + + return parameters; + } + + private String makeScrapUri(String baseUri, Map parameters) { + + UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); + for (Map.Entry entry : parameters.entrySet()) { + uri.queryParam(entry.getKey(), entry.getValue()); + } + + return uri.toUriString(); + } +} diff --git a/src/main/java/com/ssafy/ssafsound/domain/lunch/service/LunchScrapService.java b/src/main/java/com/ssafy/ssafsound/domain/lunch/service/LunchScrapService.java new file mode 100644 index 000000000..72b82498c --- /dev/null +++ b/src/main/java/com/ssafy/ssafsound/domain/lunch/service/LunchScrapService.java @@ -0,0 +1,83 @@ +package com.ssafy.ssafsound.domain.lunch.service; + +import com.ssafy.ssafsound.domain.auth.dto.AuthenticatedMember; +import com.ssafy.ssafsound.domain.auth.exception.AuthErrorInfo; +import com.ssafy.ssafsound.domain.auth.exception.AuthException; +import com.ssafy.ssafsound.domain.lunch.dto.GetLunchListReqDto; +import com.ssafy.ssafsound.domain.lunch.dto.GetScrapReqDto; +import com.ssafy.ssafsound.domain.lunch.exception.LunchErrorInfo; +import com.ssafy.ssafsound.domain.lunch.exception.LunchException; +import com.ssafy.ssafsound.domain.lunch.repository.LunchRepository; +import com.ssafy.ssafsound.domain.member.exception.MemberErrorInfo; +import com.ssafy.ssafsound.domain.meta.domain.Campus; +import com.ssafy.ssafsound.domain.meta.domain.MetaData; +import com.ssafy.ssafsound.domain.meta.service.MetaDataConsumer; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class LunchScrapService { + + private final LunchRepository lunchRepository; + private final MetaDataConsumer metaDataConsumer; + private final ScrapInfoProviderFactory scrapInfoProviderFactory; + + @Transactional(readOnly = false) + public void scrapLunchManually(AuthenticatedMember authenticatedMember, GetLunchListReqDto getLunchListReqDto) { + + String memberRole = Objects.requireNonNull(authenticatedMember.getMemberRole(), MemberErrorInfo.MEMBER_ROLE_TYPE_NOT_FOUND.getMessage()); + + if (!memberRole.equals("admin")) { + throw new AuthException(AuthErrorInfo.UNAUTHORIZED_ERROR); + } + + MetaData campus = metaDataConsumer.getMetaData("CAMPUS", getLunchListReqDto.getCampus()); + + if (getLunchListReqDto.getCampus().equals(Campus.DAEJEON.getName())) { + throw new LunchException(LunchErrorInfo.SCRAPING_ERROR); + } + + if (getLunchListReqDto.getCampus().equals(Campus.GWANGJU.getName())) { + scrapFreshmealLunch(campus); + } else { + scrapWelstoryLunch(getLunchListReqDto.getDate(), campus); + } + } + + @Transactional(readOnly = false) + public void scrapWelstoryLunch(LocalDate date, MetaData... campuses) { + + ScrapInfoProvider scraper = scrapInfoProviderFactory.getProviderFrom("welstory"); + + List requests = Arrays.stream(campuses) + .map(campus -> GetScrapReqDto.builder() + .campus(campus) + .menuDt(date) + .build()) + .collect(Collectors.toList()); + + lunchRepository.saveAll(scraper.scrapLunchInfo(requests)); + } + + @Transactional(readOnly = false) + public void scrapFreshmealLunch(MetaData... campuses) { + + ScrapInfoProvider scraper = scrapInfoProviderFactory.getProviderFrom("freshmeal"); + + List requests = Arrays.stream(campuses) + .map(campus -> GetScrapReqDto.builder() + .campus(campus) + .build()) + .collect(Collectors.toList()); + + lunchRepository.saveAll(scraper.scrapLunchInfo(requests)); + } +} diff --git a/src/main/java/com/ssafy/ssafsound/domain/lunch/service/ScrapInfoProvider.java b/src/main/java/com/ssafy/ssafsound/domain/lunch/service/ScrapInfoProvider.java new file mode 100644 index 000000000..db38b04a4 --- /dev/null +++ b/src/main/java/com/ssafy/ssafsound/domain/lunch/service/ScrapInfoProvider.java @@ -0,0 +1,10 @@ +package com.ssafy.ssafsound.domain.lunch.service; + +import com.ssafy.ssafsound.domain.lunch.domain.Lunch; +import com.ssafy.ssafsound.domain.lunch.dto.GetScrapReqDto; + +import java.util.List; + +public interface ScrapInfoProvider { + List scrapLunchInfo(List getScrapReqDtos); +} diff --git a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/ScrapInfoProviderFactory.java b/src/main/java/com/ssafy/ssafsound/domain/lunch/service/ScrapInfoProviderFactory.java similarity index 71% rename from src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/ScrapInfoProviderFactory.java rename to src/main/java/com/ssafy/ssafsound/domain/lunch/service/ScrapInfoProviderFactory.java index 3b76e8b45..230f4e713 100644 --- a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/ScrapInfoProviderFactory.java +++ b/src/main/java/com/ssafy/ssafsound/domain/lunch/service/ScrapInfoProviderFactory.java @@ -1,5 +1,8 @@ -package com.ssafy.ssafsound.domain.lunch.task.domain; +package com.ssafy.ssafsound.domain.lunch.service; +import com.ssafy.ssafsound.domain.lunch.service.FreshmealInfoProvider; +import com.ssafy.ssafsound.domain.lunch.service.ScrapInfoProvider; +import com.ssafy.ssafsound.domain.lunch.service.WelstoryInfoProvider; import org.springframework.stereotype.Component; import java.util.HashMap; diff --git a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/WelstoryInfoProvider.java b/src/main/java/com/ssafy/ssafsound/domain/lunch/service/WelstoryInfoProvider.java similarity index 56% rename from src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/WelstoryInfoProvider.java rename to src/main/java/com/ssafy/ssafsound/domain/lunch/service/WelstoryInfoProvider.java index 5c1cd9920..84cfcfb67 100644 --- a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/WelstoryInfoProvider.java +++ b/src/main/java/com/ssafy/ssafsound/domain/lunch/service/WelstoryInfoProvider.java @@ -1,25 +1,32 @@ -package com.ssafy.ssafsound.domain.lunch.task.domain; +package com.ssafy.ssafsound.domain.lunch.service; import com.fasterxml.jackson.core.JsonProcessingException; -import com.ssafy.ssafsound.domain.lunch.task.dto.GetScrapReqDto; -import com.ssafy.ssafsound.domain.lunch.task.dto.GetScrapResDto; -import com.ssafy.ssafsound.domain.lunch.task.dto.GetWelstoryResDto; +import com.ssafy.ssafsound.domain.lunch.domain.Lunch; +import com.ssafy.ssafsound.domain.lunch.domain.WelstoryProperties; +import com.ssafy.ssafsound.domain.lunch.dto.GetScrapReqDto; +import com.ssafy.ssafsound.domain.lunch.dto.GetWelstoryResDto; +import com.ssafy.ssafsound.domain.lunch.exception.LunchErrorInfo; +import com.ssafy.ssafsound.domain.lunch.exception.LunchException; import com.ssafy.ssafsound.global.common.json.JsonParser; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.*; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; import java.time.format.DateTimeFormatter; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; -@Slf4j @Component @RequiredArgsConstructor -public class WelstoryInfoProvider implements ScrapInfoProvider{ +public class WelstoryInfoProvider implements ScrapInfoProvider { private final RestTemplate restTemplate; private final WelstoryProperties welstoryProperties; @@ -27,38 +34,46 @@ public class WelstoryInfoProvider implements ScrapInfoProvider{ private HttpEntity header; @Override - public GetScrapResDto scrapLunchInfo(GetScrapReqDto getScrapReqDto) { + public List scrapLunchInfo(List getScrapReqDtos) { - Map parameters = makeScrapParameters(getScrapReqDto); - String url = makeUri(this.welstoryProperties.getInfo().get("url"), parameters); + this.setSessionHeader(); - ResponseEntity response = restTemplate.exchange( - url, - HttpMethod.GET, - header, - String.class); + List lunches = new ArrayList<>(); + + for (GetScrapReqDto getScrapReqDto : getScrapReqDtos) { + Map parameters = makeScrapParameters(getScrapReqDto); + String url = makeUri(this.welstoryProperties.getInfo().get("url"), parameters); - try { - return JsonParser.getMapper().readValue(response.getBody(), GetWelstoryResDto.class); - } catch (JsonProcessingException e) { - log.error(e.getMessage()); - e.printStackTrace(); + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.GET, + header, + String.class); - throw new RuntimeException(); + try { + lunches.addAll(JsonParser.getMapper() + .readValue(response.getBody(), GetWelstoryResDto.class) + .getLunches(getScrapReqDto.getCampus())); + } catch (JsonProcessingException e) { + throw new LunchException(LunchErrorInfo.SCRAPING_ERROR); + } } + + return lunches; + } - public void setSessionHeader() { + private void setSessionHeader() { HttpHeaders header = new HttpHeaders(); - header.add("Cookie",getJSESSIONID()); - header.add("Content-Type","application/json"); + header.add("Cookie", getJSESSIONID()); + header.add("Content-Type", "application/json"); header.add("Accept", MediaType.APPLICATION_JSON_VALUE); this.header = new HttpEntity(header); } - private String getJSESSIONID(){ + private String getJSESSIONID() { Map parameters = makeLoginParameters(); String url = makeUri(this.welstoryProperties.getCredentials().getUrl(), parameters); @@ -93,7 +108,7 @@ private Map makeScrapParameters(GetScrapReqDto getScrapReqDto) { return parameters; } - private String makeUri(String baseUri, Map parameters){ + private String makeUri(String baseUri, Map parameters) { UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); for (Map.Entry entry : parameters.entrySet()) { diff --git a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/LunchScraper.java b/src/main/java/com/ssafy/ssafsound/domain/lunch/task/LunchScraper.java index 54a706ad0..2bb3bd6a9 100644 --- a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/LunchScraper.java +++ b/src/main/java/com/ssafy/ssafsound/domain/lunch/task/LunchScraper.java @@ -1,15 +1,7 @@ package com.ssafy.ssafsound.domain.lunch.task; -import com.ssafy.ssafsound.domain.lunch.domain.Lunch; -import com.ssafy.ssafsound.domain.lunch.repository.LunchRepository; -import com.ssafy.ssafsound.domain.lunch.task.domain.ScrapInfoProvider; -import com.ssafy.ssafsound.domain.lunch.task.domain.ScrapInfoProviderFactory; -import com.ssafy.ssafsound.domain.lunch.task.domain.WelstoryInfoProvider; -import com.ssafy.ssafsound.domain.lunch.task.dto.GetFreshmealResDto; -import com.ssafy.ssafsound.domain.lunch.task.dto.GetScrapReqDto; -import com.ssafy.ssafsound.domain.lunch.task.dto.GetWelstoryResDto; -import com.ssafy.ssafsound.domain.meta.domain.MetaData; +import com.ssafy.ssafsound.domain.lunch.service.LunchScrapService; import com.ssafy.ssafsound.domain.meta.service.MetaDataConsumer; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -17,113 +9,36 @@ import org.springframework.stereotype.Component; import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; @Slf4j @Component @RequiredArgsConstructor public class LunchScraper { - private final LunchRepository lunchRepository; + private final LunchScrapService lunchScrapService; private final MetaDataConsumer metaDataConsumer; - private final ScrapInfoProviderFactory scrapInfoProviderFactory; // 토 ~ 수요일 동안 03:00에 이틀 뒤의 웰스토리 메뉴를 스크래핑 @Scheduled(cron = "0 0 3 ? * SAT,SUN,MON,TUE,WED") - public void scrapWelstory(){ - - WelstoryInfoProvider welstoryInfoProvider = (WelstoryInfoProvider) scrapInfoProviderFactory.getProviderFrom("welstory"); - - List lunches = new ArrayList<>(); - - List campuses = new ArrayList<>(); - campuses.add(metaDataConsumer.getMetaData("CAMPUS","서울")); - campuses.add(metaDataConsumer.getMetaData("CAMPUS","부울경")); - campuses.add(metaDataConsumer.getMetaData("CAMPUS","구미")); + public void scrapWelstory() { // 이틀 뒤 메뉴를 가져오기 위한 일자 설정 - LocalDate menuDt = LocalDate.now().plusDays(2); - - - // 로그인 - welstoryInfoProvider.setSessionHeader(); - - for (MetaData campus : campuses) { - try { - // 캠퍼스 + 날짜로 스크랩 요청을 위한 파라미터 dto 생성 - GetScrapReqDto request = GetScrapReqDto.builder() - .campus(campus) - .menuDt(menuDt) - .build(); - - // 스크랩 해오기 - GetWelstoryResDto meals = (GetWelstoryResDto) welstoryInfoProvider.scrapLunchInfo(request); - - // 결과 저장 - meals.getWelstoryBodyData().getMealList().forEach( - meal -> { - Lunch lunch = meal.toEntity(campus); - - if (lunch != null) { - lunches.add(lunch); - } - } - ); + LocalDate date = LocalDate.now().plusDays(2); - } catch (Exception e) { - log.error(e.getMessage()); - e.printStackTrace(); + lunchScrapService.scrapWelstoryLunch(date, + metaDataConsumer.getMetaData("CAMPUS", "서울"), + metaDataConsumer.getMetaData("CAMPUS", "부울경"), + metaDataConsumer.getMetaData("CAMPUS", "구미") + ); - throw new RuntimeException(e); - } - } - - // 일괄 저장 - lunchRepository.saveAll(lunches); } // 매주 토요일 03:02 프레시밀 스크래핑 @Scheduled(cron = "0 2 3 ? * SAT") - public void scrapFreshmeal(){ - ScrapInfoProvider freshmealInfoProvider = scrapInfoProviderFactory.getProviderFrom("freshmeal"); - - List lunches = new ArrayList<>(); - // 일주일의 데이터를 한 번에 응답의 요일별 프로퍼티로 받는다. - // 요일별 Key - List daysOfWeek = Arrays.asList("mo","tu","we","th","fr"); - - MetaData campus = metaDataConsumer.getMetaData("CAMPUS","광주"); - - for (String day : daysOfWeek) { + public void scrapFreshmeal() { - // 캠퍼스 정보로 요청 dto 생성 - GetScrapReqDto request = GetScrapReqDto.builder() - .campus(campus) - .build(); - - // 스크래핑 요청 - GetFreshmealResDto meals = (GetFreshmealResDto) freshmealInfoProvider.scrapLunchInfo(request); - - // json 형식에 따른 dto를 파싱하고, 리스트를 반복하면서 Lunch 객체를 얻어온다. - meals.getData().getDailyMeal().get(day).getMeals().forEach( - meal -> { - try { - Lunch lunch = meal.toEntity(campus); - if (lunch != null) { - lunches.add(lunch); - } - } catch (Exception e) { - e.printStackTrace(); + lunchScrapService.scrapFreshmealLunch(metaDataConsumer.getMetaData("CAMPUS", "광주")); + } - throw new RuntimeException(e.getMessage()); - } - } - ); - } - // 일괄 저장 - lunchRepository.saveAll(lunches); - } } diff --git a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/FreshmealInfoProvider.java b/src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/FreshmealInfoProvider.java deleted file mode 100644 index 83257cfbb..000000000 --- a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/FreshmealInfoProvider.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.ssafy.ssafsound.domain.lunch.task.domain; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.ssafy.ssafsound.domain.lunch.task.dto.GetFreshmealResDto; -import com.ssafy.ssafsound.domain.lunch.task.dto.GetScrapReqDto; -import com.ssafy.ssafsound.domain.lunch.task.dto.GetScrapResDto; -import com.ssafy.ssafsound.global.common.json.JsonParser; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.*; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; - -import java.util.HashMap; -import java.util.Map; - -@Slf4j -@Component -@RequiredArgsConstructor -public class FreshmealInfoProvider implements ScrapInfoProvider{ - - private final RestTemplate restTemplate; - - private final FreshmealProperties freshmealProperties; - - @Override - public GetScrapResDto scrapLunchInfo(GetScrapReqDto getScrapReqDto) { - Map parameters = makeScrapParameters(); - - String url = makeScrapUri(this.freshmealProperties.getUrl(), parameters); - - ResponseEntity response = restTemplate.exchange( - url, - HttpMethod.GET, - makeHeader(), - String.class - ); - - try { - return JsonParser.getMapper().readValue(response.getBody(), GetFreshmealResDto.class); - } catch (JsonProcessingException e) { - log.error(e.getMessage()); - e.printStackTrace(); - - throw new RuntimeException(); - } - } - - private HttpEntity makeHeader() { - HttpHeaders header = new HttpHeaders(); - header.add("Content-Type","application/json"); - header.add("Accept", MediaType.APPLICATION_JSON_VALUE); - return new HttpEntity(header); - } - - private Map makeScrapParameters() { - Map parameters = new HashMap<>(); - - parameters.put("storeIdx", this.freshmealProperties.getStoreIdx()); - parameters.put("weekType", this.freshmealProperties.getWeekType()); - - return parameters; - } - - private String makeScrapUri(String baseUri, Map parameters){ - - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - for (Map.Entry entry : parameters.entrySet()) { - uri.queryParam(entry.getKey(), entry.getValue()); - } - - return uri.toUriString(); - } -} diff --git a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/ScrapInfoProvider.java b/src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/ScrapInfoProvider.java deleted file mode 100644 index 09b5ce376..000000000 --- a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/domain/ScrapInfoProvider.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.ssafy.ssafsound.domain.lunch.task.domain; - -import com.ssafy.ssafsound.domain.lunch.task.dto.GetScrapReqDto; -import com.ssafy.ssafsound.domain.lunch.task.dto.GetScrapResDto; - -public interface ScrapInfoProvider { - GetScrapResDto scrapLunchInfo(GetScrapReqDto getScrapReqDto); -} diff --git a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/dto/GetScrapResDto.java b/src/main/java/com/ssafy/ssafsound/domain/lunch/task/dto/GetScrapResDto.java deleted file mode 100644 index 0d0aa9b93..000000000 --- a/src/main/java/com/ssafy/ssafsound/domain/lunch/task/dto/GetScrapResDto.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.ssafy.ssafsound.domain.lunch.task.dto; - -public interface GetScrapResDto { -} diff --git a/src/main/java/com/ssafy/ssafsound/global/config/ScrapConfig.java b/src/main/java/com/ssafy/ssafsound/global/config/ScrapConfig.java index f4f91f0d0..5067f0f07 100644 --- a/src/main/java/com/ssafy/ssafsound/global/config/ScrapConfig.java +++ b/src/main/java/com/ssafy/ssafsound/global/config/ScrapConfig.java @@ -1,7 +1,7 @@ package com.ssafy.ssafsound.global.config; -import com.ssafy.ssafsound.domain.lunch.task.domain.FreshmealProperties; -import com.ssafy.ssafsound.domain.lunch.task.domain.WelstoryProperties; +import com.ssafy.ssafsound.domain.lunch.domain.FreshmealProperties; +import com.ssafy.ssafsound.domain.lunch.domain.WelstoryProperties; import lombok.RequiredArgsConstructor; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration;