Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/15: ngrok 설정 추가 및 크롤러 개발 #27

Merged
merged 6 commits into from
Aug 7, 2024
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
@@ -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;
Expand All @@ -26,9 +26,13 @@ public class CrawlApi {

private final CrawlService crawlService;

@GetMapping("/crawling/youtube/comments")
public ResponseEntity<ApiResponse<CrawlResponseDTO.CrawlResultDTO>> crawlYoutubeComments(@RequestParam String url, @CurrentAccount Account account) {
crawlService.crawlYoutubeComments(url, account.userId());
// 유튜브 크롤링
@GetMapping("/crawling/comments")
@Operation(summary = "유튜브 댓글 크롤링 API", description = "주어진 URL의 유튜브 댓글을 크롤링하고 DB에 저장합니다.")
public ResponseEntity<ApiResponse<Void>> crawlYoutubeComments(@RequestParam String url, @RequestParam Long eventId, @CurrentAccount Account account) {
crawlService.crawlYoutubeComments(url, eventId, account.userId());
return ResponseEntity.ok(ApiResponse.of(ResponseCode.SUCCESS));
}

// TODO: 인스타그램 게시글 크롤링
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
40 changes: 24 additions & 16 deletions src/main/java/com/cmc/suppin/event/crawl/service/CrawlService.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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
Expand All @@ -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<CrawlResponseDTO.CrawlResultDTO> 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");
Expand All @@ -41,7 +59,6 @@ public List<CrawlResponseDTO.CrawlResultDTO> crawlYoutubeComments(String url, St
WebDriver driver = new ChromeDriver(options);
driver.get(url);

List<CrawlResponseDTO.CrawlResultDTO> commentList = new ArrayList<>();
Set<String> uniqueComments = new HashSet<>();

try {
Expand All @@ -65,25 +82,16 @@ public List<CrawlResponseDTO.CrawlResultDTO> 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();
}
Expand Down
22 changes: 14 additions & 8 deletions src/main/java/com/cmc/suppin/event/events/controller/EventApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -38,19 +40,23 @@ public ResponseEntity<ApiResponse<List<EventResponseDTO.EventInfoDTO>>> 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<ApiResponse<Void>> 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)<br><br>" +
"Response로 제공되는 eventId를 이용하여 타 API들을 호출해주시면 됩니다.")
public ResponseEntity<ApiResponse<EventResponseDTO.EventInfoDTO>> 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<ApiResponse<Void>> 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)<br><br>" +
"Response로 제공되는 eventId를 이용하여 타 API들을 호출해주시면 됩니다.")
public ResponseEntity<ApiResponse<EventResponseDTO.EventInfoDTO>> 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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.Optional;

public class EventResponseDTO {

@Builder
Expand All @@ -17,7 +19,7 @@ public static class EventInfoDTO {
private Long eventId;
private EventType type;
private String title;
private String url;
private Optional<String> url;
private String startDate;
private String endDate;
private String announcementDate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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())
Expand All @@ -52,11 +53,17 @@ public static EventResponseDTO.CommentEventDetailDTO toEventDetailDTO(Event even

public static EventResponseDTO.EventInfoDTO toEventInfoDTO(Event event) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

Optional<String> url = Optional.empty();
if (event.getType() == EventType.COMMENT) {
url = Optional.ofNullable(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))
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/cmc/suppin/event/events/domain/Event.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ public class Event extends BaseDateTimeEntity {
private Member member;

@OneToMany(mappedBy = "event")
@Builder.Default
private List<Survey> surveyList = new ArrayList<>();

@OneToMany(mappedBy = "event")
@Builder.Default
private List<Comment> commentList = new ArrayList<>();

@Column(columnDefinition = "VARCHAR(100)", nullable = false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -49,24 +50,32 @@ public List<EventResponseDTO.EventInfoDTO> 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"));

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);
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"));

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);
eventRepository.save(event);
return eventRepository.save(event);
}

public void updateEvent(Long eventId, EventRequestDTO.EventUpdateDTO request, String userId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Loading