Skip to content
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,13 +1,11 @@
= 픽픽픽
= 픽픽픽 V1

include::pick-main.adoc[]
include::pick-main-v2.adoc[]
include::pick-detail.adoc[]
include::pick-register.adoc[]
include::pick-modify.adoc[]
include::pick-delete.adoc[]
include::pick-vote.adoc[]
include::pick-option-register-image.adoc[]
include::pick-option-delete-image.adoc[]
include::pick-similarity.adoc[]
include::pick-similarity-v2.adoc[]
include::pick-similarity.adoc[]
17 changes: 17 additions & 0 deletions src/docs/asciidoc/api/pick/v2/pick-search-v2.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[[pick-search-v2]]
== 픽픽픽 메인 검색 API(GET: /devdevdev/api/v2/picks/search)
* 픽픽픽 메인에서 검색 결과를 반환한다.
* 정확도순 정렬만 가능하다.

=== 정상 요청/응답
==== HTTP Request
include::{snippets}/pick-search-v2/http-request.adoc[]
==== HTTP Request Header Fields
include::{snippets}/pick-search-v2/request-headers.adoc[]
==== HTTP Request Query Parameters Fields
include::{snippets}/pick-search-v2/query-parameters.adoc[]

==== HTTP Response
include::{snippets}/pick-search-v2/http-response.adoc[]
==== HTTP Response Fields
include::{snippets}/pick-search-v2/response-fields.adoc[]
5 changes: 5 additions & 0 deletions src/docs/asciidoc/api/pick/v2/pick.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
= 픽픽픽 V2

include::pick-main-v2.adoc[]
include::pick-search-v2.adoc[]
include::pick-similarity-v2.adoc[]
3 changes: 2 additions & 1 deletion src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ include::api/token/token.adoc[]
include::api/member/member.adoc[]
include::api/mypage/mypage.adoc[]
include::api/common/common.adoc[]
include::api/pick/pick.adoc[]
include::api/pick/v1/pick.adoc[]
include::api/pick/v2/pick.adoc[]
include::api/pick-commnet/pick-comment.adoc[]
include::api/tech-article/tech-article.adoc[]
include::api/tech-article-comment/tech-article-comment.adoc[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(indexes = {
@Index(name = "idx__content_status", columnList = "contentStatus"),
@Index(name = "idx__member", columnList = "member_id")
@Index(name = "idx__member", columnList = "member_id"),
@Index(name = "idx_pick_01", columnList = "title")
})
public class Pick extends BasicTime {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ public interface PickRepository extends JpaRepository<Pick, Long>, PickRepositor
List<Pick> findTop1000ByContentStatusAndEmbeddingsIsNotNullOrderByCreatedAtDesc(ContentStatus contentStatus);

Long countByMember(Member member);

Long countByContentStatus(ContentStatus contentStatus);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.dreamypatisiel.devdevdev.domain.repository.pick;

import lombok.Getter;

@Getter
public class PickSearchDto {
private final Long pickId;
private final Double maxTotalScore;

public PickSearchDto(Long pickId, Double maxTotalScore) {
this.pickId = pickId;
this.maxTotalScore = maxTotalScore;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import com.dreamypatisiel.devdevdev.domain.entity.Member;
import com.dreamypatisiel.devdevdev.domain.entity.Pick;
import com.dreamypatisiel.devdevdev.domain.repository.pick.PickSort;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;

Expand All @@ -15,4 +17,6 @@ public interface PickRepositoryCustom {
Optional<Pick> findPickWithPickOptionByPickId(Long pickId);

Slice<Pick> findPicksByMemberAndCursor(Pageable pageable, Member member, Long pickId);

List<Pick> findPicksWithPickOptionWithMemberByIdIn(Set<Long> ids);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
Expand Down Expand Up @@ -66,6 +67,16 @@ public Slice<Pick> findPicksByMemberAndCursor(Pageable pageable, Member member,
return new SliceImpl<>(contents, pageable, hasNextPage(contents, pageable.getPageSize()));
}

@Override
public List<Pick> findPicksWithPickOptionWithMemberByIdIn(Set<Long> ids) {
return query.selectFrom(pick)
.leftJoin(pick.pickOptions, pickOption)
.leftJoin(pick.member, member).fetchJoin()
.where(pick.id.in(ids)
.and(pick.contentStatus.eq(ContentStatus.APPROVAL)))
.fetch();
}

@Override
public Optional<Pick> findPickWithPickOptionWithPickVoteWithMemberByPickId(Long pickId) {
Pick findPick = query.selectFrom(pick)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.dreamypatisiel.devdevdev.domain.repository.pick.mybatis;

import com.dreamypatisiel.devdevdev.domain.repository.pick.PickSearchDto;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface PickMapper {
List<PickSearchDto> findPickSearchDtoByKeywordAndCursor(@Param("cursorId") Long pickId,
@Param("keyword") String keyword,
@Param("cursorSearchScore") Double searchScore,
@Param("limit") int limit);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,49 @@
import com.dreamypatisiel.devdevdev.domain.entity.enums.ContentStatus;
import com.dreamypatisiel.devdevdev.domain.policy.PickBestCommentsPolicy;
import com.dreamypatisiel.devdevdev.domain.policy.PickPopularScorePolicy;
import com.dreamypatisiel.devdevdev.domain.repository.pick.*;
import com.dreamypatisiel.devdevdev.domain.repository.pick.PickCommentRecommendRepository;
import com.dreamypatisiel.devdevdev.domain.repository.pick.PickCommentRepository;
import com.dreamypatisiel.devdevdev.domain.repository.pick.PickRepository;
import com.dreamypatisiel.devdevdev.domain.repository.pick.PickSearchDto;
import com.dreamypatisiel.devdevdev.domain.repository.pick.PickSort;
import com.dreamypatisiel.devdevdev.domain.repository.pick.mybatis.PickMapper;
import com.dreamypatisiel.devdevdev.domain.service.member.AnonymousMemberService;
import com.dreamypatisiel.devdevdev.global.common.TimeProvider;
import com.dreamypatisiel.devdevdev.global.utils.AuthenticationMemberUtils;
import com.dreamypatisiel.devdevdev.openai.embeddings.EmbeddingsService;
import com.dreamypatisiel.devdevdev.web.dto.SliceCustom;
import com.dreamypatisiel.devdevdev.web.dto.response.pick.PickMainResponseV2;
import com.dreamypatisiel.devdevdev.web.dto.response.pick.PickMainSearchResponseV2;
import com.dreamypatisiel.devdevdev.web.dto.response.pick.SimilarPickResponseV2;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional(readOnly = true)
public class GuestPickServiceV2 extends PickCommonService implements PickServiceV2 {

private final AnonymousMemberService anonymousMemberService;
private final PickMapper pickMapper;

public GuestPickServiceV2(PickRepository pickRepository, EmbeddingsService embeddingsService,
PickBestCommentsPolicy pickBestCommentsPolicy,
PickCommentRepository pickCommentRepository,
PickCommentRecommendRepository pickCommentRecommendRepository,
PickPopularScorePolicy pickPopularScorePolicy,
TimeProvider timeProvider, AnonymousMemberService anonymousMemberService) {
TimeProvider timeProvider, AnonymousMemberService anonymousMemberService, PickMapper pickMapper) {
super(embeddingsService, pickBestCommentsPolicy, pickPopularScorePolicy, timeProvider, pickRepository,
pickCommentRepository, pickCommentRecommendRepository);
this.anonymousMemberService = anonymousMemberService;
this.pickMapper = pickMapper;
}

/**
Expand All @@ -44,7 +56,7 @@ public GuestPickServiceV2(PickRepository pickRepository, EmbeddingsService embed
@Transactional
@Override
public Slice<PickMainResponseV2> findPicksMain(Pageable pageable, Long pickId, PickSort pickSort,
String anonymousMemberId, Authentication authentication) {
String anonymousMemberId, Authentication authentication) {
// 익명 사용자 호출인지 확인
AuthenticationMemberUtils.validateAnonymousMethodCall(authentication);

Expand Down Expand Up @@ -72,4 +84,39 @@ public Slice<PickMainResponseV2> findPicksMain(Pageable pageable, Long pickId, P
public List<SimilarPickResponseV2> findTop3SimilarPicksV2(Long pickId) {
return super.findTop3SimilarPicksV2(pickId);
}

@Transactional
@Override
public Slice<PickMainSearchResponseV2> findPickMainSearch(Pageable pageable, Long pickId, Double searchScore,
String keyword, String anonymousMemberId,
Authentication authentication) {

// 익명 사용자 호출인지 확인
AuthenticationMemberUtils.validateAnonymousMethodCall(authentication);

// anonymousMemberId 검증
AnonymousMember anonymousMember = anonymousMemberService.findOrCreateAnonymousMember(anonymousMemberId);

// 픽픽픽 검색
List<PickSearchDto> pickSearchDtos = pickMapper.findPickSearchDtoByKeywordAndCursor(pickId,
keyword, searchScore, pageable.getPageSize());

Set<Long> pickIds = pickSearchDtos.stream()
.map(PickSearchDto::getPickId)
.collect(Collectors.toSet());

// 픽픽픽 조회
Map<Long, Pick> findPicks = pickRepository.findPicksWithPickOptionWithMemberByIdIn(pickIds).stream()
.collect(Collectors.toMap(Pick::getId, Function.identity()));

// 데이터 가공
List<PickMainSearchResponseV2> pickMainSearchResponse = pickSearchDtos.stream()
.flatMap(pickSearchDto -> Optional.ofNullable(findPicks.get(pickSearchDto.getPickId()))
.map(pick -> PickMainSearchResponseV2.of(pick, anonymousMember, pickSearchDto.getMaxTotalScore()))
.stream()
)
.toList();

return new SliceCustom<>(pickMainSearchResponse, pageable, (long) pickSearchDtos.size());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,53 @@
import com.dreamypatisiel.devdevdev.domain.repository.pick.PickCommentRecommendRepository;
import com.dreamypatisiel.devdevdev.domain.repository.pick.PickCommentRepository;
import com.dreamypatisiel.devdevdev.domain.repository.pick.PickRepository;
import com.dreamypatisiel.devdevdev.domain.repository.pick.PickSearchDto;
import com.dreamypatisiel.devdevdev.domain.repository.pick.PickSort;
import com.dreamypatisiel.devdevdev.domain.repository.pick.mybatis.PickMapper;
import com.dreamypatisiel.devdevdev.global.common.MemberProvider;
import com.dreamypatisiel.devdevdev.global.common.TimeProvider;
import com.dreamypatisiel.devdevdev.openai.embeddings.EmbeddingsService;
import com.dreamypatisiel.devdevdev.web.dto.SliceCustom;
import com.dreamypatisiel.devdevdev.web.dto.response.pick.PickMainResponseV2;
import com.dreamypatisiel.devdevdev.web.dto.response.pick.PickMainSearchResponseV2;
import com.dreamypatisiel.devdevdev.web.dto.response.pick.SimilarPickResponseV2;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional(readOnly = true)
public class MemberPickServiceV2 extends PickCommonService implements PickServiceV2 {

private final MemberProvider memberProvider;
private final PickMapper pickMapper;

public MemberPickServiceV2(EmbeddingsService embeddingsService, PickRepository pickRepository,
MemberProvider memberProvider,
PickCommentRepository pickCommentRepository,
PickCommentRecommendRepository pickCommentRecommendRepository,
PickPopularScorePolicy pickPopularScorePolicy,
PickBestCommentsPolicy pickBestCommentsPolicy, TimeProvider timeProvider) {
PickBestCommentsPolicy pickBestCommentsPolicy, TimeProvider timeProvider, PickMapper pickMapper) {
super(embeddingsService, pickBestCommentsPolicy, pickPopularScorePolicy, timeProvider, pickRepository,
pickCommentRepository,
pickCommentRecommendRepository);
pickCommentRepository, pickCommentRecommendRepository);
this.memberProvider = memberProvider;
this.pickMapper = pickMapper;
}

/**
* 픽픽픽 메인 조회 V2
*/
@Override
public Slice<PickMainResponseV2> findPicksMain(Pageable pageable, Long pickId, PickSort pickSort,
String anonymousMemberId, Authentication authentication) {
String anonymousMemberId, Authentication authentication) {
// 픽픽픽 조회
Slice<Pick> picks = pickRepository.findPicksByCursor(pageable, pickId, pickSort);

Expand All @@ -71,4 +79,38 @@ public Slice<PickMainResponseV2> findPicksMain(Pageable pageable, Long pickId, P
public List<SimilarPickResponseV2> findTop3SimilarPicksV2(Long pickId) {
return super.findTop3SimilarPicksV2(pickId);
}

/**
* @Author: 장세웅
* @Note: 픽픽픽 검색 조회
*/
@Override
public Slice<PickMainSearchResponseV2> findPickMainSearch(Pageable pageable, Long pickId, Double searchScore, String keyword,
String anonymousMemberId, Authentication authentication) {

// 회원 조회
Member findMember = memberProvider.getMemberByAuthentication(authentication);

// 픽픽픽 검색
List<PickSearchDto> pickSearchDtos = pickMapper.findPickSearchDtoByKeywordAndCursor(pickId,
keyword, searchScore, pageable.getPageSize());

Set<Long> pickIds = pickSearchDtos.stream()
.map(PickSearchDto::getPickId)
.collect(Collectors.toSet());

// 픽픽픽 조회
Map<Long, Pick> findPicks = pickRepository.findPicksWithPickOptionWithMemberByIdIn(pickIds).stream()
.collect(Collectors.toMap(Pick::getId, Function.identity()));

// 데이터 가공
List<PickMainSearchResponseV2> pickMainSearchResponse = pickSearchDtos.stream()
.flatMap(pickSearchDto -> Optional.ofNullable(findPicks.get(pickSearchDto.getPickId()))
.map(pick -> PickMainSearchResponseV2.of(pick, findMember, pickSearchDto.getMaxTotalScore()))
.stream()
)
.toList();

return new SliceCustom<>(pickMainSearchResponse, pageable, (long) pickSearchDtos.size());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@

import com.dreamypatisiel.devdevdev.domain.repository.pick.PickSort;
import com.dreamypatisiel.devdevdev.web.dto.response.pick.PickMainResponseV2;
import com.dreamypatisiel.devdevdev.web.dto.response.pick.PickMainSearchResponseV2;
import com.dreamypatisiel.devdevdev.web.dto.response.pick.SimilarPickResponseV2;
import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.security.core.Authentication;

import java.util.List;

public interface PickServiceV2 extends PickService {
Slice<PickMainResponseV2> findPicksMain(Pageable pageable, Long pickId, PickSort pickSort, String anonymousMemberId,
Authentication authentication);

List<SimilarPickResponseV2> findTop3SimilarPicksV2(Long pickId);

Slice<PickMainSearchResponseV2> findPickMainSearch(Pageable pageable, Long pickId, Double searchScore, String keyword,
String anonymousMemberId, Authentication authentication);
}
Loading