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/33: FCM 푸시알림 구현-2 #46

Merged
merged 39 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
49de632
Refactor: 크롤러 소요시간을 고려한 Nginx 타임아웃 설정 추가
yxhwxn Aug 11, 2024
710d025
Refactor: 전체 댓글이 로드된 경우, 크롤링을 조기에 종료하도록 로직을 조정
yxhwxn Aug 11, 2024
a99f6bf
Chore: 배포 서버와 selenium 버전 동기화
yxhwxn Aug 11, 2024
d593e34
Chore: Timeout 방지 설정 추가
yxhwxn Aug 11, 2024
db3407e
Refactor: 유튜브 댓글 크롤링 API response 값 수정(시간, 총 댓글 수 추가)
yxhwxn Aug 12, 2024
98b7a19
Refactor: 설문지 조회 시, URL을 통합 접근 허용하도록 수정
yxhwxn Aug 15, 2024
7851d35
Refactor: 전체 이벤트 조회 시, 설문이 있는 경우 uuid값 응답 필드 추가
yxhwxn Aug 15, 2024
80eda63
Refactor: API 요청마다 crawlTime 갱신 오류 수정
yxhwxn Aug 15, 2024
cb53a7c
Chore: CORS origin 경로 추가
yxhwxn Aug 15, 2024
4e815ee
Refactor: NullPointerException 방지용 어노테이션 추가
yxhwxn Aug 15, 2024
b27d6be
Feat: 설문 생성 시, 개인정보 수집 동의서(consentForm) 필드 추가
yxhwxn Aug 15, 2024
aa8bd11
Feat: 설문 조회 시, 개인정보 수집 동의서(consentForm) 필드 추가
yxhwxn Aug 15, 2024
cf27f7c
Fix: 크롤링 중복 검증 시 날짜 포맷 수정
yxhwxn Aug 15, 2024
fe5041c
Refactor: 랜덤 추첨 API 호출 시, 당첨자 리스트 추가(isWinner 업데이트)
yxhwxn Aug 15, 2024
09bf769
Feat: 댓글/설문 이벤트 당첨자 리스트 간편 조회 API 구현
yxhwxn Aug 15, 2024
5b7c4f1
Feat: 스케쥴러 어노테이션 추가
yxhwxn Aug 16, 2024
6f1606c
Fix: 주소 필드 세분화 및 설문지 조회 API 응답값 수정
yxhwxn Aug 16, 2024
dddfeec
Chore: CORS 프론트 배포 도메인 추가
yxhwxn Aug 16, 2024
d9885b8
Chore: gitignore 추가
yxhwxn Aug 16, 2024
7d779a5
Fix: Nginx CORS 중복 헤더 제거
yxhwxn Aug 16, 2024
e4eba3b
Refactor: chromedriver 옵션 추가(페이지 및 이미지 로딩 설정)
yxhwxn Aug 17, 2024
8ce2eca
Fix: Email 인증 디자인 및 로고 수정
yxhwxn Aug 18, 2024
648947a
Fix: questionOptionId 식별값 응답 DTO 추가
yxhwxn Aug 18, 2024
9fb1a3c
Chore: Delete drivers folder in dev
yxhwxn Aug 18, 2024
0dd64b5
Refactor: Queue 자료구조를 활용한 스크롤 방식 개선
yxhwxn Aug 18, 2024
a1d37fc
Fix: yyyy-MM-dd HH:mm 포맷으로 변경
yxhwxn Aug 18, 2024
16a2239
Feat: 이벤트 당첨자 선별 조건 관련 컬럼 추가
yxhwxn Aug 18, 2024
7d78b1b
Refactor: 설문 이벤트 당첨자 조회 시, 해당 선별 조건값을 포함하여 조회되도록 수정
yxhwxn Aug 18, 2024
fd01795
Docs: API 명세 수정
yxhwxn Aug 18, 2024
a94666e
Feat: 댓글 이벤트 당첨자 재추첨 API 구현
yxhwxn Aug 18, 2024
06f6a89
Chore: 로컬 chromedriver gitignore 추가
yxhwxn Aug 18, 2024
c85fd6b
Fix: crawlTime 관련 Timezone 이슈 해결
yxhwxn Aug 18, 2024
6d038bc
Feat: NullPointerException 방지를 위한 SelectionCriteria 관련 필드 기본값 설정
yxhwxn Aug 18, 2024
5ced1f3
Feat: 선별 조건 리스트도 함께 응답하도록 댓글 이벤트 당첨자 조회 API 수정
yxhwxn Aug 18, 2024
848eaef
Chore: gitignore 파일 경로 수정
yxhwxn Aug 18, 2024
8cfdaad
Feat: FCM 디바이스 토큰 등록 API 구현
yxhwxn Aug 19, 2024
93878a1
Feat: 디바이스 토큰 저장을 위한 엔티티 추가
yxhwxn Aug 19, 2024
85e8898
Feat: 이벤트 종료일 및 당첨자 발표일 알림 전송 로직 구현
yxhwxn Aug 19, 2024
0f8330a
Feat: 푸시알림 스케쥴러 추가
yxhwxn Aug 19, 2024
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ build/
!**/src/test/**/build/
/src/main/resources/firebase/
/src/main/resources/firebase/suppin-a5657-firebase-adminsdk-s75m9-d65cc88029.json
/src/main/resources/drivers/


### STS ###
.apt_generated
Expand Down Expand Up @@ -37,3 +39,5 @@ out/

### VS Code ###
.vscode/

app.log
102 changes: 53 additions & 49 deletions .platform/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -11,53 +11,57 @@ events {
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;


log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

include conf.d/*.conf;

map $http_upgrade $connection_upgrade {
default "upgrade";
}

upstream springboot {
server 127.0.0.1:8080;
keepalive 1024;
}

server {
listen 80 default_server;
listen [::]:80 default_server;

location / {
proxy_pass http://springboot;
# CORS 관련 헤더 추가
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type';
proxy_http_version 1.1;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Upgrade $http_upgrade;

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

access_log /var/log/nginx/access.log main;

client_header_timeout 60;
client_body_timeout 60;
keepalive_timeout 60;
gzip off;
gzip_comp_level 4;

# Include the Elastic Beanstalk generated locations
include conf.d/elasticbeanstalk/healthd.conf;
}
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

include conf.d/*.conf;

map $http_upgrade $connection_upgrade {
default "upgrade";
}

upstream springboot {
server 127.0.0.1:8080;
keepalive 1024;
}

server {
listen 80 default_server;
listen [::]:80 default_server;

location / {
proxy_pass http://springboot;
# CORS 관련 헤더 추가
# add_header 'Access-Control-Allow-Origin' '*';
# add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
# add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type';
proxy_http_version 1.1;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Upgrade $http_upgrade;

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# 타임아웃 설정 추가
proxy_read_timeout 900s; # 백엔드 서버로부터의 응답을 기다리는 시간
proxy_connect_timeout 900s; # 백엔드 서버에 연결을 시도하는 시간
proxy_send_timeout 900s; # Nginx가 백엔드 서버로 요청을 전송하는 시간
}

access_log /var/log/nginx/access.log main;

client_header_timeout 60;
client_body_timeout 60;
keepalive_timeout 60;
gzip off;
gzip_comp_level 4;

# Include the Elastic Beanstalk generated locations
# include conf.d/elasticbeanstalk/healthd.conf;
}
}
5 changes: 2 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,9 @@ dependencies {
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.2'

//selenium
implementation 'org.seleniumhq.selenium:selenium-java:4.1.4'
implementation 'io.github.bonigarcia:webdrivermanager:5.0.3'
implementation 'org.seleniumhq.selenium:selenium-java:4.22.0'
implementation 'io.github.bonigarcia:webdrivermanager:5.4.0'
implementation 'org.jsoup:jsoup:1.13.1'
testImplementation 'org.seleniumhq.selenium:selenium-java:4.22.0'

//Google Firebase
implementation 'com.google.firebase:firebase-admin:9.2.0'
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/cmc/suppin/SuppinApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableJpaAuditing
@EnableScheduling
public class SuppinApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.cmc.suppin.event.crawl.controller.dto.CommentResponseDTO;
import com.cmc.suppin.event.crawl.service.CommentService;
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;
Expand Down Expand Up @@ -44,7 +45,7 @@ public ResponseEntity<ApiResponse<CommentResponseDTO.CrawledCommentListDTO>> get
}

@PostMapping("/draft-winners")
@Operation(summary = "조건별 당첨자 추첨 API(댓글 이벤트)", description = "주어진 조건에 따라 이벤트의 당첨자를 추첨합니다.")
@Operation(summary = "당첨자 랜덤 추첨 결과 조회 API(댓글 이벤트)", description = "주어진 조건에 따라 이벤트의 당첨자를 추첨합니다.")
public ResponseEntity<ApiResponse<CommentResponseDTO.WinnerResponseDTO>> drawWinners(
@RequestBody @Valid CommentRequestDTO.WinnerRequestDTO request,
@CurrentAccount Account account) {
Expand All @@ -61,4 +62,11 @@ public ResponseEntity<ApiResponse<List<CommentResponseDTO.CommentDetailDTO>>> ge
List<CommentResponseDTO.CommentDetailDTO> filteredWinners = commentService.getCommentsByKeyword(eventId, keyword, account.userId());
return ResponseEntity.ok(ApiResponse.of(filteredWinners));
}

@DeleteMapping("/")
@Operation(summary = "댓글 이벤트 당첨자 리스트 삭제 API(당첨자 재추첨 시, 기존 당첨자 리스트를 삭제한 후 진행해야 합니다.", description = "모든 당첨자들의 isWinner 값을 false로 변경합니다.")
public ResponseEntity<ApiResponse<Void>> deleteWinners(@RequestParam("eventId") Long eventId, @CurrentAccount Account account) {
commentService.deleteWinners(eventId);
return ResponseEntity.ok(ApiResponse.of(ResponseCode.SUCCESS));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
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;
Expand Down Expand Up @@ -51,9 +52,9 @@ public ResponseEntity<ApiResponse<String>> checkExistingComments(@RequestParam("
"크롤링하려는 URL이 중복되지 않았을 때의 요청이기 때문에, 새로운 댓글을 크롤링합니다. <br>" +
"- DB에 기존 댓글이 존재하는 경우: 크롤링을 중지하고 예외를 던집니다. <br>" +
"- DB에 기존 댓글이 존재하지 않는 경우: 새로운 댓글을 크롤링하고 이를 DB에 저장합니다.")
public ResponseEntity<ApiResponse<String>> crawlYoutubeComments(@RequestParam("url") String url, @RequestParam("eventId") Long eventId, @RequestParam("forceUpdate") boolean forceUpdate, @CurrentAccount Account account) {
crawlService.crawlYoutubeComments(url, eventId, account.userId(), forceUpdate);
return ResponseEntity.ok(ApiResponse.of(ResponseCode.SUCCESS, "댓글 수집이 완료되었습니다."));
public ResponseEntity<ApiResponse<CrawlResponseDTO.CrawlResultDTO>> crawlYoutubeComments(@RequestParam("url") String url, @RequestParam("eventId") Long eventId, @RequestParam("forceUpdate") boolean forceUpdate, @CurrentAccount Account account) {
CrawlResponseDTO.CrawlResultDTO crawlResultDTO = crawlService.crawlYoutubeComments(url, eventId, account.userId(), forceUpdate);
return ResponseEntity.ok(ApiResponse.of(ResponseCode.SUCCESS, crawlResultDTO));
}

// @GetMapping("/count")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.cmc.suppin.event.crawl.controller.dto;

import jakarta.validation.constraints.Pattern;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -27,14 +26,10 @@ public static class CommentListRequestDTO {
@Builder
public static class WinnerRequestDTO {
private Long eventId;
private int winnerCount;
private int minLength;

@Pattern(regexp = "\\d{4}\\. \\d{2}\\. \\d{2} \\d{2}:\\d{2}", message = "날짜 형식은 yyyy. MM. dd HH:mm 이어야 합니다.")
private Integer winnerCount;
private Integer minLength;
private String startDate;
@Pattern(regexp = "\\d{4}\\. \\d{2}\\. \\d{2} \\d{2}:\\d{2}", message = "날짜 형식은 yyyy. MM. dd HH:mm 이어야 합니다.")
private String endDate;

private List<String> keywords;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,27 @@ public static class WinnerResponseDTO {
private String endDate;
private List<CommentDetailDTO> winners;
}

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class CommentEventWinnersWithCriteria {
private Integer winnerCount;
private Integer minLength;
private String startDate;
private String endDate;
private List<String> keywords;
private List<CommentEventWinners> winners;
}

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class CommentEventWinners {
private String author;
private String commentText;
private String commentDate;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ public class CrawlResponseDTO {
@NoArgsConstructor
@AllArgsConstructor
public static class CrawlResultDTO {
private String author;
private String commentText;
private String date;
private String crawlTime;
private int totalCommentCount;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.cmc.suppin.event.crawl.controller.dto.CommentRequestDTO;
import com.cmc.suppin.event.crawl.controller.dto.CommentResponseDTO;
import com.cmc.suppin.event.crawl.controller.dto.CrawlResponseDTO;
import com.cmc.suppin.event.crawl.domain.Comment;
import com.cmc.suppin.event.events.domain.Event;

Expand Down Expand Up @@ -56,5 +57,20 @@ public static CommentResponseDTO.WinnerResponseDTO toWinnerResponseDTO(List<Comm
.winners(winnerDetails)
.build();
}

public static CrawlResponseDTO.CrawlResultDTO toCrawlResultDTO(LocalDateTime crawlingDate, int totalCommentCount) {
return CrawlResponseDTO.CrawlResultDTO.builder()
.crawlTime(crawlingDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")))
.totalCommentCount(totalCommentCount)
.build();
}

public static CommentResponseDTO.CommentEventWinners toCommentEventWinners(Comment comment) {
return CommentResponseDTO.CommentEventWinners.builder()
.author(comment.getAuthor())
.commentText(comment.getCommentText())
.commentDate(comment.getCommentDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")))
.build();
}
}

15 changes: 15 additions & 0 deletions src/main/java/com/cmc/suppin/event/crawl/domain/Comment.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,19 @@ public class Comment extends BaseDateTimeEntity {
@Column(nullable = false)
private LocalDateTime commentDate;

@Column(nullable = false)
private LocalDateTime crawlTime;

@Builder.Default
@Column(nullable = false)
private boolean isWinner = false;

public void setCrawlTime(LocalDateTime crawlTime) {
this.crawlTime = crawlTime;
}

public void setIsWinner(boolean isWinner) {
this.isWinner = isWinner;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,9 @@ public interface CommentRepository extends JpaRepository<Comment, Long> {
List<Comment> findByEventIdAndCommentDateBetween(Long eventId, LocalDateTime start, LocalDateTime end);

List<Comment> findByEventIdAndCommentTextContaining(Long eventId, String keyword);

List<Comment> findByEventIdAndIsWinnerTrue(Long eventId);


boolean existsByUrlAndEventId(String url, Long eventId);
}
Loading
Loading