Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0110eb7
feat: facebook 게시물 응답 받기 위한 dto 추가 [PRBE-35]
Mar 7, 2025
f6a5196
feat: facebook 게시물 api 요청 로직 추가 [PRBE-35]
Mar 7, 2025
dfd7169
refactor: 외부 api 처리 dto에 맞게 위치 수정 [PRBE-35]
Mar 7, 2025
a4adba8
feat: 로그인 시, 사용자 정보 반환하기 위한 dto 추가 [PRBE-35]
Mar 7, 2025
457e64c
feat: 회원가입 시, 비동기로 게시물 처리 호출 및 로그인 시, 최근 게시물 이후 게시물 처리 로직 추가 [PRBE-35]
Mar 7, 2025
28fa92d
refactor: 로그인 api에 사용자 정보 추가된 dto 반영 [PRBE-35]
Mar 7, 2025
8054559
feat: DLP api 에서 탐지할 infoType 정의 [PRBE-35]
Mar 7, 2025
558d125
feat: DLP api 에서 신뢰도에 대한 정보 정의 [PRBE-35]
Mar 7, 2025
517883d
feat: 페이스북 게시물 저장 엔티티 추가 [PRBE-35]
Mar 7, 2025
b464597
feat: 페이스북 게시물 사진 저장 엔티티 추가 [PRBE-35]
Mar 7, 2025
63f59db
feat: 페이스북 게시물 메세지 개인정보 탐지 엔티티 추가 [PRBE-35]
Mar 7, 2025
0d87fff
feat: 페이스북 게시물 사진 개인정보 탐지 엔티티 추가 [PRBE-35]
Mar 7, 2025
20ed6af
feat: 페이스북 게시물 사진 개인정보 탐지 범위 엔티티 추가 [PRBE-35]
Mar 7, 2025
69744b9
feat: 가장 최근 게시물 가져오는 Jpa 메서드 추가 [PRBE-35]
Mar 7, 2025
53166a6
feat: post 도메인 관련 jpa 설정 추가 [PRBE-35]
Mar 7, 2025
527035e
feat: DLP api 응답 받기 위한 dto 추가 [PRBE-35]
Mar 7, 2025
82ef359
feat: DLP api 요청하기 위한 dto 추가 [PRBE-35]
Mar 7, 2025
351f715
feat: DLP api 이미지 분석 요청을 위한 url > file 화 util 메서드 추가 [PRBE-35]
Mar 7, 2025
4903b0b
feat: DLP api 텍스트 및 사진 분석 api 호출 로직 추가 [PRBE-35]
Mar 7, 2025
d731376
feat: facebook 게시물 api 및 DLP api 결과 처리를 통한 게시물 분석 정보 저장 로직 추가 [PRBE-35]
Mar 7, 2025
779ca5c
fix: N+1 문제 해결을 위한 batch 사이즈 추가 [PRBE-29]
Mar 7, 2025
0bd5a52
feat: 전체 게시물 응답 dto 추가 [PRBE-29]
Mar 7, 2025
067101c
feat: Query Dsl 설정 추가 [PRBE-29]
Mar 7, 2025
7761f32
feat: fbPost 게시물 전체 항목 조회 query dsl 추가 [PRBE-29]
Mar 7, 2025
2ba6b48
feat: fbPost 게시물 전체 항목 조회 로직 추가 [PRBE-29]
Mar 7, 2025
bc6cec4
feat: fbPost 게시물 전체 항목 조회 api 추가 [PRBE-29]
Mar 7, 2025
2847f73
docs: fbPost 게시물 전체 항목 조회 api 문서화 [PRBE-29]
Mar 7, 2025
d3dba90
chore: query dsl 의존성 추가 [PRBE-29]
Mar 7, 2025
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
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ dependencies {
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.2'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.2'

// Query Dsl
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

// MongoDB
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'

Expand Down
21 changes: 21 additions & 0 deletions src/main/java/com/olive/pribee/global/config/QueryDslConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.olive.pribee.global.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.querydsl.jpa.impl.JPAQueryFactory;


import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;

@Configuration
public class QueryDslConfig {

@PersistenceContext
private EntityManager entityManager;

@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
49 changes: 49 additions & 0 deletions src/main/java/com/olive/pribee/global/enums/DetectKeyword.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.olive.pribee.global.enums;

import java.util.Arrays;
import java.util.List;

import lombok.Getter;

@Getter
public enum DetectKeyword {
PERSON_NAME("PERSON_NAME", "이름", 10),
GENDER("GENDER", "성별", 10),
AGE("AGE", "나이", 10),
LOCATION("LOCATION", "주소", 10),
EMAIL_ADDRESS("EMAIL_ADDRESS", "이메일", 10),
PHONE_NUMBER("PHONE_NUMBER", "전화번호", 10),
IP_ADDRESS("IP_ADDRESS", "IP 주소", 5),
MAC_ADDRESS("MAC_ADDRESS", "MAC 주소", 5),
FINANCIAL_ACCOUNT_NUMBER("FINANCIAL_ACCOUNT_NUMBER", "계좌번호", 20),
CREDIT_CARD_NUMBER("CREDIT_CARD_NUMBER", "카드번호", 20),
IBAN_CODE("IBAN_CODE", "국제 계좌번호", 20),
MEDICAL_RECORD_NUMBER("MEDICAL_RECORD_NUMBER", "의료 기록 번호", 20),
KOREA_RRN("KOREA_RRN", "주민등록번호", 30),
KOREA_DRIVERS_LICENSE_NUMBER("KOREA_DRIVERS_LICENSE_NUMBER", "운전면허번호", 30),
KOREA_PASSPORT("KOREA_PASSPORT", "여권번호", 30);

private final String code;
private final String name;
private final int score;

private DetectKeyword(String code, String name, int score) {
this.code = code;
this.name = name;
this.score = score;
}

public static DetectKeyword of(String code) {
return Arrays.stream(DetectKeyword.values())
.filter(r -> r.getCode().equals(code))
.findAny()
.orElse(null);
}

public static List<String> getInfoTypes() {
return Arrays.stream(values())
.map(keyword -> keyword.code)
.toList();
}

}
29 changes: 29 additions & 0 deletions src/main/java/com/olive/pribee/global/enums/DetectLikelihood.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.olive.pribee.global.enums;

import java.util.Arrays;

import lombok.Getter;

@Getter
public enum DetectLikelihood {
VERY_UNLIKELY("VERY_UNLIKELY", "가능성 매우 낮음"),
UNLIKELY("UNLIKELY", "가능성 낮음"),
POSSIBLE("POSSIBLE", "가능성 있음"),
LIKELY("LIKELY", "가능성 높음"),
VERY_LIKELY("VERY_LIKELY", "가능성 매우 높음");

private final String code;
private final String name;

private DetectLikelihood(String code, String name) {
this.code = code;
this.name = name;
}

public static DetectLikelihood of(String code) {
return Arrays.stream(DetectLikelihood.values())
.filter(r -> r.getCode().equals(code))
.findAny()
.orElse(null);
}
}
26 changes: 26 additions & 0 deletions src/main/java/com/olive/pribee/global/util/fileUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.olive.pribee.global.util;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.Base64;

public class fileUtil {

public static String encodeImageToBase64(String imageUrl) {
try {
URL url = new URL(imageUrl);
InputStream inputStream = url.openStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
inputStream.close();
return Base64.getEncoder().encodeToString(outputStream.toByteArray());
} catch (Exception e) {
throw new RuntimeException("Failed to encode image", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
package com.olive.pribee.module.auth.service;
package com.olive.pribee.infra.api.facebook;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
Expand All @@ -9,19 +13,22 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.olive.pribee.global.error.GlobalErrorCode;
import com.olive.pribee.global.error.exception.AppException;
import com.olive.pribee.module.auth.dto.res.FacebookAuthRes;
import com.olive.pribee.module.auth.dto.res.FacebookTokenRes;
import com.olive.pribee.module.auth.dto.res.FacebookUserInfoRes;
import com.olive.pribee.infra.api.facebook.dto.res.auth.FacebookAuthRes;
import com.olive.pribee.infra.api.facebook.dto.res.auth.FacebookTokenRes;
import com.olive.pribee.infra.api.facebook.dto.res.auth.FacebookUserInfoRes;
import com.olive.pribee.infra.api.facebook.dto.res.post.FacebookPostListRes;
import com.olive.pribee.infra.api.facebook.dto.res.post.FacebookPostRes;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
@Slf4j
public class FacebookAuthService {
public class FacebookApiService {

private final String FB_EXCHANGE_TOKEN = "fb_exchange_token";

Expand Down Expand Up @@ -75,7 +82,7 @@ private Mono<String> exchangeCodeForAccessToken(String code) {
.build())
.retrieve()
.onStatus(status ->
status == HttpStatus.UNAUTHORIZED || status == HttpStatus.BAD_REQUEST, response ->{
status == HttpStatus.UNAUTHORIZED || status == HttpStatus.BAD_REQUEST, response -> {
log.error("[Facebook] Invalid Facebook Code: {}", code);
return Mono.error(new AppException(GlobalErrorCode.INVALID_FACEBOOK_CODE));
})
Expand Down Expand Up @@ -127,12 +134,12 @@ private Mono<String> fetchFacebookId(String accessToken) {
});
}

// (4-1) Facebook 사용자 정보 조회
// (4) Facebook 사용자 정보 조회
public Mono<FacebookUserInfoRes> fetchFacebookUserInfo(String accessToken) {
return getFacebookWebClient().get()
.uri(uriBuilder -> uriBuilder
.path("/me")
.queryParam("fields", "id,name,email,picture.width(1000).height(1000)")
.queryParam("fields", "id,name,email,picture.width(500).height(500){url}")
.queryParam("access_token", accessToken)
.build())
.retrieve()
Expand All @@ -142,4 +149,46 @@ public Mono<FacebookUserInfoRes> fetchFacebookUserInfo(String accessToken) {
return Mono.error(new AppException(GlobalErrorCode.INTERNAL_SERVER_ERROR));
});
}

// (5) Facebook 게시물 조회
public Mono<List<FacebookPostRes>> fetchFacebookUserPosts(String accessToken, LocalDateTime sinceTime) {
return getFacebookWebClient().get()
.uri(uriBuilder -> uriBuilder
.path("/me")
.queryParam("fields",
"feed.limit(100){id,updated_time,created_time,message,permalink_url,full_picture,place{name},attachments{subattachments{media{image{src}}},type}}")
.queryParam("access_token", accessToken)
.build())
.retrieve()
.bodyToMono(FacebookPostListRes.class) // 전체 피드를 받아옴
.flatMapMany(feed -> Flux.fromIterable(feed.getPosts().getPosts())) // 리스트로 변환하여 Flux로 반환
.filter(facebookPostRes -> isAfterSinceTime(facebookPostRes, sinceTime)) // 시간 필터링
.filter(this::isValidAttachment) // 유효한 첨부 필터링
.collectList()
.onErrorResume(Exception.class, ex -> {
log.error("[Facebook] 게시물 조회 실패: {}", ex.getMessage(), ex);
return Mono.error(new AppException(GlobalErrorCode.INTERNAL_SERVER_ERROR));
});
}

// 지정한 시간 이후의 게시물인지 확인
private boolean isAfterSinceTime(FacebookPostRes facebookPostRes, LocalDateTime sinceTime) {
if (sinceTime == null) {
return true;
}
LocalDateTime postCreatedTime = facebookPostRes.getCreatedTime().atZone(ZoneOffset.UTC).toLocalDateTime();
return postCreatedTime.isAfter(sinceTime);
}

// 유효한 첨부파일인지 확인 (null이거나, album/photo 타입만 허용)
private boolean isValidAttachment(FacebookPostRes facebookPostRes) {
if (facebookPostRes.getAttachments() == null) {
return true;
}
return facebookPostRes.getAttachments().getData().stream()
.findFirst()
.map(attachment -> "album".equals(attachment.getType()) || "photo".equals(attachment.getType()))
.orElse(false);
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.olive.pribee.module.auth.dto.res;
package com.olive.pribee.infra.api.facebook.dto.res.auth;

import com.fasterxml.jackson.annotation.JsonProperty;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.olive.pribee.module.auth.dto.res;
package com.olive.pribee.infra.api.facebook.dto.res.auth;

import com.fasterxml.jackson.annotation.JsonProperty;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.olive.pribee.module.auth.dto.res;
package com.olive.pribee.infra.api.facebook.dto.res.auth;

import com.fasterxml.jackson.annotation.JsonProperty;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.olive.pribee.module.auth.dto.res;
package com.olive.pribee.infra.api.facebook.dto.res.auth;

import com.fasterxml.jackson.annotation.JsonProperty;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.olive.pribee.infra.api.facebook.dto.res.post;

import com.fasterxml.jackson.annotation.JsonProperty;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class FacebookPostAttachmentDataRes {
private String type;
private FacebookPostSubattachmentsRes subattachments;

@Builder
public FacebookPostAttachmentDataRes(
@JsonProperty("type") String type,
@JsonProperty("subattachments") FacebookPostSubattachmentsRes subattachments
) {
this.type = type;
this.subattachments = subattachments;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.olive.pribee.infra.api.facebook.dto.res.post;

import java.util.List;

import com.fasterxml.jackson.annotation.JsonProperty;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class FacebookPostAttachmentsRes {
@JsonProperty("data")
private List<FacebookPostAttachmentDataRes> data;

@Builder
public FacebookPostAttachmentsRes(List<FacebookPostAttachmentDataRes> data) {
this.data = data;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.olive.pribee.infra.api.facebook.dto.res.post;

import java.util.List;

import com.fasterxml.jackson.annotation.JsonProperty;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class FacebookPostDataRes {
@JsonProperty("data")
private List<FacebookPostRes> posts;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.olive.pribee.infra.api.facebook.dto.res.post;


import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.*;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class FacebookPostImageDataRes {
private String src;

@Builder
public FacebookPostImageDataRes(@JsonProperty("src") String src) {
this.src = src;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.olive.pribee.infra.api.facebook.dto.res.post;


import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.*;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class FacebookPostImageRes {
private FacebookPostImageDataRes image;

@Builder
public FacebookPostImageRes(@JsonProperty("image") FacebookPostImageDataRes image) {
this.image = image;
}
}
Loading