From b8f156e6b918b15f8c2c38701925c04760a23436 Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Tue, 6 Aug 2024 21:22:40 +0900 Subject: [PATCH 1/6] =?UTF-8?q?Refactor:=20NullPointerException=20?= =?UTF-8?q?=EB=B0=A9=EC=A7=80=EC=9A=A9=20=EC=96=B4=EB=85=B8=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/cmc/suppin/event/events/domain/Event.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/cmc/suppin/event/events/domain/Event.java b/src/main/java/com/cmc/suppin/event/events/domain/Event.java index 3a53fc9..cf9d9a6 100644 --- a/src/main/java/com/cmc/suppin/event/events/domain/Event.java +++ b/src/main/java/com/cmc/suppin/event/events/domain/Event.java @@ -31,9 +31,11 @@ public class Event extends BaseDateTimeEntity { private Member member; @OneToMany(mappedBy = "event") + @Builder.Default private List surveyList = new ArrayList<>(); @OneToMany(mappedBy = "event") + @Builder.Default private List commentList = new ArrayList<>(); @Column(columnDefinition = "VARCHAR(100)", nullable = false) From 9c99cc3c104908bf33028bc15baefc4268e668b4 Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Tue, 6 Aug 2024 21:34:53 +0900 Subject: [PATCH 2/6] =?UTF-8?q?Refactor:=20=EC=9D=91=EB=8B=B5=20=EC=8B=9C?= =?UTF-8?q?=20eventId=20=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/events/controller/EventApi.java | 22 ++++++++++++------- .../events/converter/EventConverter.java | 8 ++++++- .../event/events/service/EventService.java | 8 +++---- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/cmc/suppin/event/events/controller/EventApi.java b/src/main/java/com/cmc/suppin/event/events/controller/EventApi.java index d8f88fa..962b9e8 100644 --- a/src/main/java/com/cmc/suppin/event/events/controller/EventApi.java +++ b/src/main/java/com/cmc/suppin/event/events/controller/EventApi.java @@ -2,6 +2,8 @@ import com.cmc.suppin.event.events.controller.dto.EventRequestDTO; import com.cmc.suppin.event.events.controller.dto.EventResponseDTO; +import com.cmc.suppin.event.events.converter.EventConverter; +import com.cmc.suppin.event.events.domain.Event; import com.cmc.suppin.event.events.service.EventService; import com.cmc.suppin.global.response.ApiResponse; import com.cmc.suppin.global.response.ResponseCode; @@ -38,19 +40,23 @@ public ResponseEntity>> getAllEv @PostMapping("/new/comment/crawling") @Operation(summary = "댓글 이벤트 생성 API", description = "Request : type(ENUM 타입으로, 'COMMENT와 SURVEY' 둘 중 하나를 입력해주시면 됩니다), " + - "title, description, url, startDate(yyyy-MM-dd), endDate(yyyy-MM-dd), announcementDate(yyyy-MM-dd)") - public ResponseEntity> createCommentEvent(@RequestBody @Valid EventRequestDTO.CommentEventCreateDTO request, @CurrentAccount Account account) { - eventService.createCommentEvent(request, account.userId()); - return ResponseEntity.ok(ApiResponse.of(ResponseCode.SUCCESS)); + "title, description, url, startDate(yyyy-MM-dd), endDate(yyyy-MM-dd), announcementDate(yyyy-MM-dd)

" + + "Response로 제공되는 eventId를 이용하여 타 API들을 호출해주시면 됩니다.") + public ResponseEntity> createCommentEvent(@RequestBody @Valid EventRequestDTO.CommentEventCreateDTO request, @CurrentAccount Account account) { + Event event = eventService.createCommentEvent(request, account.userId()); + EventResponseDTO.EventInfoDTO response = EventConverter.toEventInfoDTO(event); + return ResponseEntity.ok(ApiResponse.of(response)); } @PostMapping("/new/survey") @Operation(summary = "설문조사 이벤트 생성 API", description = "Request : type(ENUM 타입으로, 'COMMENT와 SURVEY' 둘 중 하나를 입력해주시면 됩니다), " + - "title, description, startDate(yyyy-MM-dd), endDate(yyyy-MM-dd), announcementDate(yyyy-MM-dd)") - public ResponseEntity> createSurveyEvent(@RequestBody @Valid EventRequestDTO.SurveyEventCreateDTO request, @CurrentAccount Account account) { - eventService.createSurveyEvent(request, account.userId()); - return ResponseEntity.ok(ApiResponse.of(ResponseCode.SUCCESS)); + "title, description, startDate(yyyy-MM-dd), endDate(yyyy-MM-dd), announcementDate(yyyy-MM-dd)

" + + "Response로 제공되는 eventId를 이용하여 타 API들을 호출해주시면 됩니다.") + public ResponseEntity> createSurveyEvent(@RequestBody @Valid EventRequestDTO.SurveyEventCreateDTO request, @CurrentAccount Account account) { + Event event = eventService.createSurveyEvent(request, account.userId()); + EventResponseDTO.EventInfoDTO response = EventConverter.toEventInfoDTO(event); + return ResponseEntity.ok(ApiResponse.of(response)); } @PutMapping("/{eventId}/update") diff --git a/src/main/java/com/cmc/suppin/event/events/converter/EventConverter.java b/src/main/java/com/cmc/suppin/event/events/converter/EventConverter.java index 9b828a1..3edb9d9 100644 --- a/src/main/java/com/cmc/suppin/event/events/converter/EventConverter.java +++ b/src/main/java/com/cmc/suppin/event/events/converter/EventConverter.java @@ -52,11 +52,17 @@ public static EventResponseDTO.CommentEventDetailDTO toEventDetailDTO(Event even public static EventResponseDTO.EventInfoDTO toEventInfoDTO(Event event) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + String url = null; + if (event.getType() == EventType.COMMENT) { + url = event.getUrl(); + } + return EventResponseDTO.EventInfoDTO.builder() .eventId(event.getId()) .type(event.getType()) .title(event.getTitle()) - .url(event.getUrl()) + .url(url) .startDate(event.getStartDate().format(formatter)) .endDate(event.getEndDate().format(formatter)) .announcementDate(event.getAnnouncementDate().format(formatter)) diff --git a/src/main/java/com/cmc/suppin/event/events/service/EventService.java b/src/main/java/com/cmc/suppin/event/events/service/EventService.java index d4ba61a..67988a4 100644 --- a/src/main/java/com/cmc/suppin/event/events/service/EventService.java +++ b/src/main/java/com/cmc/suppin/event/events/service/EventService.java @@ -49,24 +49,24 @@ public List getAllEvents(String userId) { .collect(Collectors.toList()); } - public void createCommentEvent(EventRequestDTO.CommentEventCreateDTO request, String userId) { + public Event createCommentEvent(EventRequestDTO.CommentEventCreateDTO request, String userId) { Member member = memberRepository.findByUserIdAndStatusNot(userId, UserStatus.DELETED) .orElseThrow(() -> new IllegalArgumentException("Member not found")); Event event = EventConverter.toCommentEventEntity(request, member); event.setMember(member); event.setStatus(EventStatus.PROCESSING); - eventRepository.save(event); + return eventRepository.save(event); } - public void createSurveyEvent(EventRequestDTO.SurveyEventCreateDTO request, String userId) { + public Event createSurveyEvent(EventRequestDTO.SurveyEventCreateDTO request, String userId) { Member member = memberRepository.findByUserIdAndStatusNot(userId, UserStatus.DELETED) .orElseThrow(() -> new IllegalArgumentException("Member not found")); Event event = EventConverter.toSurveyEventEntity(request, member); event.setMember(member); event.setStatus(EventStatus.PROCESSING); - eventRepository.save(event); + return eventRepository.save(event); } public void updateEvent(Long eventId, EventRequestDTO.EventUpdateDTO request, String userId) { From e32c3d07163027647f36eb437bcf6ed11a518e87 Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Tue, 6 Aug 2024 21:42:26 +0900 Subject: [PATCH 3/6] =?UTF-8?q?Refactor:=20url=20->=20Optional=20String=20?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/events/controller/dto/EventResponseDTO.java | 4 +++- .../suppin/event/events/converter/EventConverter.java | 9 +++++---- .../cmc/suppin/event/events/service/EventService.java | 9 +++++++++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/cmc/suppin/event/events/controller/dto/EventResponseDTO.java b/src/main/java/com/cmc/suppin/event/events/controller/dto/EventResponseDTO.java index 3645962..2bb0ca8 100644 --- a/src/main/java/com/cmc/suppin/event/events/controller/dto/EventResponseDTO.java +++ b/src/main/java/com/cmc/suppin/event/events/controller/dto/EventResponseDTO.java @@ -7,6 +7,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import java.util.Optional; + public class EventResponseDTO { @Builder @@ -17,7 +19,7 @@ public static class EventInfoDTO { private Long eventId; private EventType type; private String title; - private String url; + private Optional url; private String startDate; private String endDate; private String announcementDate; diff --git a/src/main/java/com/cmc/suppin/event/events/converter/EventConverter.java b/src/main/java/com/cmc/suppin/event/events/converter/EventConverter.java index 3edb9d9..ae76d93 100644 --- a/src/main/java/com/cmc/suppin/event/events/converter/EventConverter.java +++ b/src/main/java/com/cmc/suppin/event/events/converter/EventConverter.java @@ -8,13 +8,14 @@ import java.time.LocalDate; import java.time.format.DateTimeFormatter; +import java.util.Optional; public class EventConverter { public static Event toCommentEventEntity(EventRequestDTO.CommentEventCreateDTO request, Member member) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); return Event.builder() - .type(request.getType()) + .type(EventType.COMMENT) .title(request.getTitle()) .description(request.getDescription()) .url(request.getUrl()) @@ -28,7 +29,7 @@ public static Event toCommentEventEntity(EventRequestDTO.CommentEventCreateDTO r public static Event toSurveyEventEntity(EventRequestDTO.SurveyEventCreateDTO request, Member member) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); return Event.builder() - .type(request.getType()) + .type(EventType.SURVEY) .title(request.getTitle()) .description(request.getDescription()) .startDate(LocalDate.parse(request.getStartDate(), formatter).atStartOfDay()) @@ -53,9 +54,9 @@ public static EventResponseDTO.CommentEventDetailDTO toEventDetailDTO(Event even public static EventResponseDTO.EventInfoDTO toEventInfoDTO(Event event) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); - String url = null; + Optional url = Optional.empty(); if (event.getType() == EventType.COMMENT) { - url = event.getUrl(); + url = Optional.ofNullable(event.getUrl()); } return EventResponseDTO.EventInfoDTO.builder() diff --git a/src/main/java/com/cmc/suppin/event/events/service/EventService.java b/src/main/java/com/cmc/suppin/event/events/service/EventService.java index 67988a4..cefe85e 100644 --- a/src/main/java/com/cmc/suppin/event/events/service/EventService.java +++ b/src/main/java/com/cmc/suppin/event/events/service/EventService.java @@ -6,6 +6,7 @@ import com.cmc.suppin.event.events.domain.Event; import com.cmc.suppin.event.events.domain.repository.EventRepository; import com.cmc.suppin.global.enums.EventStatus; +import com.cmc.suppin.global.enums.EventType; import com.cmc.suppin.global.enums.UserStatus; import com.cmc.suppin.member.domain.Member; import com.cmc.suppin.member.domain.repository.MemberRepository; @@ -53,6 +54,10 @@ public Event createCommentEvent(EventRequestDTO.CommentEventCreateDTO request, S Member member = memberRepository.findByUserIdAndStatusNot(userId, UserStatus.DELETED) .orElseThrow(() -> new IllegalArgumentException("Member not found")); + if (request.getType() != EventType.COMMENT) { + throw new IllegalArgumentException("Event type must be COMMENT"); + } + Event event = EventConverter.toCommentEventEntity(request, member); event.setMember(member); event.setStatus(EventStatus.PROCESSING); @@ -63,6 +68,10 @@ public Event createSurveyEvent(EventRequestDTO.SurveyEventCreateDTO request, Str Member member = memberRepository.findByUserIdAndStatusNot(userId, UserStatus.DELETED) .orElseThrow(() -> new IllegalArgumentException("Member not found")); + if (request.getType() != EventType.SURVEY) { + throw new IllegalArgumentException("Event type must be SURVEY"); + } + Event event = EventConverter.toSurveyEventEntity(request, member); event.setMember(member); event.setStatus(EventStatus.PROCESSING); From 360bd44d3b23ee0a4a86d075f9daf0065dc7f509 Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Tue, 6 Aug 2024 23:46:47 +0900 Subject: [PATCH 4/6] =?UTF-8?q?Feat:=20=EC=9C=A0=ED=8A=9C=EB=B8=8C=20?= =?UTF-8?q?=EB=8C=93=EA=B8=80=20=ED=81=AC=EB=A1=A4=EB=9F=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/crawl/controller/CrawlApi.java | 12 ++++-- .../event/crawl/service/CrawlService.java | 40 +++++++++++-------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/cmc/suppin/event/crawl/controller/CrawlApi.java b/src/main/java/com/cmc/suppin/event/crawl/controller/CrawlApi.java index e520cb1..3ff2d6d 100644 --- a/src/main/java/com/cmc/suppin/event/crawl/controller/CrawlApi.java +++ b/src/main/java/com/cmc/suppin/event/crawl/controller/CrawlApi.java @@ -1,11 +1,11 @@ package com.cmc.suppin.event.crawl.controller; -import com.cmc.suppin.event.crawl.controller.dto.CrawlResponseDTO; import com.cmc.suppin.event.crawl.service.CrawlService; import com.cmc.suppin.global.response.ApiResponse; import com.cmc.suppin.global.response.ResponseCode; import com.cmc.suppin.global.security.reslover.Account; import com.cmc.suppin.global.security.reslover.CurrentAccount; +import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -26,9 +26,13 @@ public class CrawlApi { private final CrawlService crawlService; - @GetMapping("/crawling/youtube/comments") - public ResponseEntity> crawlYoutubeComments(@RequestParam String url, @CurrentAccount Account account) { - crawlService.crawlYoutubeComments(url, account.userId()); + // 유튜브 크롤링 + @GetMapping("/crawling/comments") + @Operation(summary = "유튜브 댓글 크롤링 API", description = "주어진 URL의 유튜브 댓글을 크롤링하고 DB에 저장합니다.") + public ResponseEntity> crawlYoutubeComments(@RequestParam String url, @RequestParam Long eventId, @CurrentAccount Account account) { + crawlService.crawlYoutubeComments(url, eventId, account.userId()); return ResponseEntity.ok(ApiResponse.of(ResponseCode.SUCCESS)); } + + // TODO: 인스타그램 게시글 크롤링 } diff --git a/src/main/java/com/cmc/suppin/event/crawl/service/CrawlService.java b/src/main/java/com/cmc/suppin/event/crawl/service/CrawlService.java index 164f089..07073c2 100644 --- a/src/main/java/com/cmc/suppin/event/crawl/service/CrawlService.java +++ b/src/main/java/com/cmc/suppin/event/crawl/service/CrawlService.java @@ -1,8 +1,13 @@ package com.cmc.suppin.event.crawl.service; -import com.cmc.suppin.event.crawl.controller.dto.CrawlResponseDTO; +import com.cmc.suppin.event.crawl.converter.CommentConverter; import com.cmc.suppin.event.crawl.domain.Comment; import com.cmc.suppin.event.crawl.domain.repository.CommentRepository; +import com.cmc.suppin.event.events.domain.Event; +import com.cmc.suppin.event.events.domain.repository.EventRepository; +import com.cmc.suppin.global.enums.UserStatus; +import com.cmc.suppin.member.domain.Member; +import com.cmc.suppin.member.domain.repository.MemberRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.jsoup.Jsoup; @@ -16,9 +21,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; import java.util.HashSet; -import java.util.List; import java.util.Set; @Service @@ -28,11 +31,26 @@ public class CrawlService { private final CommentRepository commentRepository; + private final EventRepository eventRepository; + private final MemberRepository memberRepository; + + public void crawlYoutubeComments(String url, Long eventId, String userId) { + log.info("Start crawling comments for URL: {} by user: {}", url, userId); + + Member member = memberRepository.findByUserIdAndStatusNot(userId, UserStatus.DELETED) + .orElseThrow(() -> new IllegalArgumentException("Member not found")); + + log.info("Member found: {}", member.getId()); + + Event event = eventRepository.findByIdAndMemberId(eventId, member.getId()) + .orElseThrow(() -> new IllegalArgumentException("Event not found")); + + log.info("Event found: {}", event.getId()); - public List crawlYoutubeComments(String url, String userId) { System.setProperty("webdriver.chrome.driver", "src/main/resources/drivers/chromedriver"); ChromeOptions options = new ChromeOptions(); + options.addArguments("--headless"); options.addArguments("--disable-gpu"); options.addArguments("--no-sandbox"); options.addArguments("--disable-dev-shm-usage"); @@ -41,7 +59,6 @@ public List crawlYoutubeComments(String url, St WebDriver driver = new ChromeDriver(options); driver.get(url); - List commentList = new ArrayList<>(); Set uniqueComments = new HashSet<>(); try { @@ -65,25 +82,16 @@ public List crawlYoutubeComments(String url, St if (!uniqueComments.contains(text)) { uniqueComments.add(text); - commentList.add(new CrawlResponseDTO.CrawlResultDTO(author, text, time)); // 엔티티 저장 - Comment comment = Comment.builder() - .author(author) - .commentText(text) - .commentDate(time) - .url(url) - .build(); + Comment comment = CommentConverter.toCommentEntity(author, text, time, url, event); commentRepository.save(comment); + log.info("Comment saved: {}", comment.getId()); } } } - - return commentList; - } catch (InterruptedException e) { e.printStackTrace(); - return new ArrayList<>(); } finally { driver.quit(); } From 46c05a4a6b67a0111e5aae03f113840eafe411a9 Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Wed, 7 Aug 2024 02:55:53 +0900 Subject: [PATCH 5/6] =?UTF-8?q?Fix:=20ngrok=20static=20domain,=20CORS=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cmc/suppin/global/security/config/WebMvcConfig.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/cmc/suppin/global/security/config/WebMvcConfig.java b/src/main/java/com/cmc/suppin/global/security/config/WebMvcConfig.java index 0ee074e..213fc00 100644 --- a/src/main/java/com/cmc/suppin/global/security/config/WebMvcConfig.java +++ b/src/main/java/com/cmc/suppin/global/security/config/WebMvcConfig.java @@ -35,8 +35,10 @@ private String[] getAllowOrigins() { "http://localhost:3000", "https://localhost:3000", "https://dev.suppin.store", + "https://api.suppin.store", "https://suppin.store", - "http://192.168.0.100:3000" // 모바일 디바이스의 IP 주소 + "http://192.168.200.120:3000", // 테스트 디바이스 IP 허용 + "https://coherent-midge-probably.ngrok-free.app" ).toArray(String[]::new); } } From 2dba59df18492e6beea1776f6dfbc01d01171ac1 Mon Sep 17 00:00:00 2001 From: yxhwxn Date: Wed, 7 Aug 2024 21:33:03 +0900 Subject: [PATCH 6/6] =?UTF-8?q?Feat:=20CommentConverter=20=EB=94=B0?= =?UTF-8?q?=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/crawl/converter/CommentConverter.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/com/cmc/suppin/event/crawl/converter/CommentConverter.java b/src/main/java/com/cmc/suppin/event/crawl/converter/CommentConverter.java index e66c6c0..2c50b9d 100644 --- a/src/main/java/com/cmc/suppin/event/crawl/converter/CommentConverter.java +++ b/src/main/java/com/cmc/suppin/event/crawl/converter/CommentConverter.java @@ -1,4 +1,17 @@ package com.cmc.suppin.event.crawl.converter; +import com.cmc.suppin.event.crawl.domain.Comment; +import com.cmc.suppin.event.events.domain.Event; + public class CommentConverter { + + public static Comment toCommentEntity(String author, String text, String time, String url, Event event) { + return Comment.builder() + .author(author) + .commentText(text) + .commentDate(time) + .url(url) + .event(event) + .build(); + } }