diff --git a/.github/workflows/test-server-ci.yml b/.github/workflows/test-server-ci.yml index d9b7c694..153b0789 100644 --- a/.github/workflows/test-server-ci.yml +++ b/.github/workflows/test-server-ci.yml @@ -68,6 +68,7 @@ jobs: - name: Test with Gradle # 테스트 단계에서 RabbitMQ 연결을 위한 환경 변수 설정 env: + SPRING_PROFILES_ACTIVE: test OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} SPRING_RABBITMQ_HOST: localhost SPRING_RABBITMQ_USERNAME: guest diff --git a/src/main/java/org/tuna/zoopzoop/backend/domain/news/controller/ApiV1NewsController.java b/src/main/java/org/tuna/zoopzoop/backend/domain/news/controller/ApiV1NewsController.java index 078ad563..59d8fac8 100644 --- a/src/main/java/org/tuna/zoopzoop/backend/domain/news/controller/ApiV1NewsController.java +++ b/src/main/java/org/tuna/zoopzoop/backend/domain/news/controller/ApiV1NewsController.java @@ -26,6 +26,7 @@ public class ApiV1NewsController { private final NewsAPIService newsSearchService; private final NewsService newsService; + /** * 최신 뉴스 목록을 조회하는 API * 한번에 100개를 조회 합니다. @@ -98,4 +99,33 @@ public Mono>> searchNewsByRecommends( response ))); } + + /** + * 공유 아카이브의 폴더 내부의 자료를 기반으로 키워드를 추천해서 검색하는 뉴스 API + * HTTP METHOD: GET + * 한번에 100개를 조회 합니다. + * @param userDetails 로그인한 사용자 + * @param spaceId 대상 스페이스 id + * @param folderId 대상 폴더 id + */ + @GetMapping("/recommends/shared/{spaceId}/{folderId}") + @Operation(summary = "공유 아카이브 뉴스 추천") + public Mono>> searchNewsByRecommends( + @AuthenticationPrincipal CustomUserDetails userDetails, + @PathVariable Integer spaceId, + @PathVariable Integer folderId + ) { + Member member = userDetails.getMember(); + List frequency = newsService.getTagFrequencyFromFilesInSharing(spaceId, member.getId(), folderId); + String query = String.join(" ", frequency); + + return newsSearchService.searchNews(query, "sim") + .map(response -> ResponseEntity + .status(HttpStatus.OK) + .body(new RsData<>( + "200", + "키워드 기반 뉴스 목록을 조회했습니다.", + response + ))); + } } diff --git a/src/main/java/org/tuna/zoopzoop/backend/domain/news/service/NewsService.java b/src/main/java/org/tuna/zoopzoop/backend/domain/news/service/NewsService.java index b6b89221..6613898d 100644 --- a/src/main/java/org/tuna/zoopzoop/backend/domain/news/service/NewsService.java +++ b/src/main/java/org/tuna/zoopzoop/backend/domain/news/service/NewsService.java @@ -5,6 +5,9 @@ import org.tuna.zoopzoop.backend.domain.archive.folder.service.PersonalArchiveFolderService; import org.tuna.zoopzoop.backend.domain.datasource.dto.FileSummary; import org.tuna.zoopzoop.backend.domain.datasource.dto.FolderFilesDto; +import org.tuna.zoopzoop.backend.domain.member.entity.Member; +import org.tuna.zoopzoop.backend.domain.member.service.MemberService; +import org.tuna.zoopzoop.backend.domain.space.archive.service.SpaceArchiveFolderService; import java.util.Comparator; import java.util.List; @@ -15,6 +18,8 @@ @RequiredArgsConstructor public class NewsService { private final PersonalArchiveFolderService folderService; + private final SpaceArchiveFolderService spaceArchiveFolderService; + private final MemberService memberService; public List getTagFrequencyFromFiles(Integer memberId, Integer folderId) { FolderFilesDto folderFilesDto = folderService.getFilesInFolder(memberId, folderId); @@ -39,4 +44,29 @@ public List getTagFrequencyFromFiles(Integer memberId, Integer folderId) return frequency; } + + public List getTagFrequencyFromFilesInSharing(Integer spaceId, Integer memberId, Integer folderId) { + Member member = memberService.findById(memberId); + FolderFilesDto folderFilesDto = spaceArchiveFolderService.getFilesInFolder(spaceId, member, folderId); + + List files = folderFilesDto.files(); + + Map tags = files.stream() + .flatMap(file -> { + List ts = file.tags(); + return (ts == null ? List.of() : ts).stream(); + }) + .collect(Collectors.groupingBy( + tagName -> tagName, + Collectors.counting() + )); + + List frequency = tags.entrySet().stream() + .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) + .limit(3) + .map(Map.Entry::getKey) + .toList(); + + return frequency; + } } diff --git a/src/main/resources/application-secrets.yml.template b/src/main/resources/application-secrets.yml.template index 8f7e5116..97b5f5d3 100644 --- a/src/main/resources/application-secrets.yml.template +++ b/src/main/resources/application-secrets.yml.template @@ -1,55 +1,17 @@ -spring: - security: - oauth2: - client: - registration: - kakao: - client-id: {KAKAO_CLIENT_ID} - client-secret: "" - redirect-uri: {KAKAO_REDIRECT_URL} - scope: - - {SCOPE_LIST} - authorization-grant-type: {AUTHORIZATION_GRANT_TYPE} - google: - client-id: {GOOGLE_CLIENT_ID} - client-secret: {GOOGLE_SECRET} - redirect-uri: {GOOGLE_REDIRECT_URL} - scope: - - {SCOPE_LIST} - authorization-grant-type: {AUTHORIZATION_GRANT_TYPE} - provider: - kakao: - authorization-uri: https://kauth.kakao.com/oauth/authorize - token-uri: https://kauth.kakao.com/oauth/token - user-info-uri: https://kapi.kakao.com/v2/user/me - user-name-attribute: id - google: - authorization-uri: https://accounts.google.com/o/oauth2/v2/auth - token-uri: https://oauth2.googleapis.com/token - user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo - user-name-attribute: sub - cloud: - aws: - credentials: - access-key: {AWS_ACCESS_KEY} - secret-key: {AWS_SECRET_KEY} - region: - static: {AWS_REGION} - s3: - bucket: {AWS_S3_BUCKET_NAME} - stack: - auto: false +OPENAI_API_KEY: {YOUR_VALUE} +KAKAO_CLIENT_ID: {YOUR_VALUE} +GOOGLE_CLIENT_ID: {YOUR_VALUE} +GOOGLE_CLIENT_SECRET: {YOUR_VALUE} +AWS_ACCESS_KEY_ID: {YOUR_VALUE} +AWS_SECRET_ACCESS_KEY: {YOUR_VALUE} +AWS_S3_BUCKET_NAME: {YOUR_VALUE} +SENTRY_DSN: {YOUR_VALUE} +JWT_SECRET_KEY: {YOUR_VALUE} +JWT_ACCESS_TOKEN_VALIDITY: {YOUR_VALUE} +JWT_REFRESH_TOKEN_VALIDITY: {YOUR_VALUE} +NAVER_CLIENT_ID: {YOUR_VALUE} +NAVER_CLIENT_SECRET: {YOUR_VALUE} +LIVEBLOCKS_SECRET_KEY: {YOUR_VALUE} +REDIRECT_DOMAIN: {YOUR_VALUE} -naver: - client_id: {NAVER_CLIENT_ID} - client_secret: {NAVER_CLIENT_SECRET} - -jwt: - secret-key: {JWT_SECRET_KEY} - access-token-validity: {ACCESSTOKEN_VALIDITY} - refresh-token-validity: {REFRESHTOKEN_VALIDITY} - -OPENAI_API_KEY: {OPENAI_API_KEY} - -liveblocks: - secret-key: {LIVEBLOCKS_SECRET_KEY} \ No newline at end of file +#SENTRY_AUTH_TOKEN -> 따로 시스템 환경 변수로 설정. \ No newline at end of file