Skip to content

Commit

Permalink
Merge pull request #104 from cvs-go/feature#103
Browse files Browse the repository at this point in the history
태그 매칭률 조회 기능 추가
  • Loading branch information
chaewss authored Nov 16, 2023
2 parents 22b75bd + bdf1d59 commit d0a287e
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 4 deletions.
20 changes: 18 additions & 2 deletions src/docs/asciidoc/api-doc.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,23 @@ include::{snippets}/user-controller-test/respond_200_when_delete_user_follow_suc
| `404 NOT FOUND` | `NOT_FOUND_USER_FOLLOW` | 해당하는 팔로우가 없는 경우
|===

=== 1-9. 특정 회원의 좋아요 상품 목록 조회
=== 1-9. 태그 매칭률 조회
==== Path Parameters
include::{snippets}/user-controller-test/respond_200_when_read_user_tag_match_percentage_successfully/path-parameters.adoc[]
==== Sample Request
include::{snippets}/user-controller-test/respond_200_when_read_user_tag_match_percentage_successfully/http-request.adoc[]
==== Response Fields
include::{snippets}/user-controller-test/respond_200_when_read_user_tag_match_percentage_successfully/response-fields.adoc[]
==== Sample Response
include::{snippets}/user-controller-test/respond_200_when_read_user_tag_match_percentage_successfully/http-response.adoc[]
==== Error Response
|===
| HTTP Status | Error Code | Detail

| `404 NOT FOUND` | `NOT_FOUND_USER` | 해당하는 유저가 없는 경우
|===

=== 1-10. 특정 회원의 좋아요 상품 목록 조회
==== Path Parameters
include::{snippets}/user-controller-test/respond_200_when_read_liked_product_list_successfully/path-parameters.adoc[]
==== Request Fields
Expand All @@ -120,7 +136,7 @@ include::{snippets}/user-controller-test/respond_200_when_read_liked_product_lis
==== Sample Response
include::{snippets}/user-controller-test/respond_200_when_read_liked_product_list_successfully/http-response.adoc[]

=== 1-10. 특정 회원의 북마크 상품 목록 조회
=== 1-11. 특정 회원의 북마크 상품 목록 조회
==== Path Parameters
include::{snippets}/user-controller-test/respond_200_when_read_bookmarked_product_list_successfully/path-parameters.adoc[]
==== Request Fields
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/com/cvsgo/controller/UserController.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ public SuccessResponse<Void> deleteUserFollow(@LoginUser User user, @PathVariabl
return SuccessResponse.create();
}

@GetMapping("/users/{userId}/tag-match-percentage")
public SuccessResponse<Integer> readUserTagMatchPercentage(
@LoginUser User user, @PathVariable Long userId) {
return SuccessResponse.from(userService.readUserTagMatchPercentage(user, userId));
}

@GetMapping("/users/{userId}/liked-products")
public SuccessResponse<Page<ReadProductResponseDto>> readLikedProductList(
@PathVariable Long userId, @ModelAttribute ReadUserProductRequestDto request,
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/cvsgo/repository/UserTagRepository.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.cvsgo.repository;

import com.cvsgo.entity.User;
import com.cvsgo.entity.UserTag;
import java.util.List;
import org.springframework.data.jpa.repository.EntityGraph;
Expand All @@ -10,4 +11,6 @@ public interface UserTagRepository extends JpaRepository<UserTag, Long> {
@EntityGraph(attributePaths = {"user", "tag"})
List<UserTag> findByUserIdIn(List<Long> userIds);

List<UserTag> findAllByUser(User user);

}
31 changes: 31 additions & 0 deletions src/main/java/com/cvsgo/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
import com.cvsgo.entity.Tag;
import com.cvsgo.entity.User;
import com.cvsgo.entity.UserFollow;
import com.cvsgo.entity.UserTag;
import com.cvsgo.exception.BadRequestException;
import com.cvsgo.exception.DuplicateException;
import com.cvsgo.exception.NotFoundException;
import com.cvsgo.repository.ReviewRepository;
import com.cvsgo.repository.TagRepository;
import com.cvsgo.repository.UserFollowRepository;
import com.cvsgo.repository.UserRepository;
import com.cvsgo.repository.UserTagRepository;
import jakarta.persistence.EntityManager;
import java.util.List;
import lombok.RequiredArgsConstructor;
Expand All @@ -36,6 +38,8 @@
@RequiredArgsConstructor
public class UserService {

private final UserTagRepository userTagRepository;

private final UserRepository userRepository;

private final TagRepository tagRepository;
Expand Down Expand Up @@ -177,4 +181,31 @@ public void deleteUserFollow(User user, Long userId) {
userFollowRepository.delete(userFollow);
}

/**
* 태그 매칭률을 조회한다.
*
* @param user 로그인한 사용자
* @param userId 태그 매칭률을 조회할 사용자 ID
* @return 태그 매칭률
* @throws NotFoundException 해당하는 아이디를 가진 사용자가 없는 경우
*/
@Transactional(readOnly = true)
public Integer readUserTagMatchPercentage(User user, Long userId) {
User targetUser = userRepository.findById(userId).orElseThrow(() -> NOT_FOUND_USER);

List<Tag> loginUserTag = userTagRepository.findAllByUser(user).stream().map(UserTag::getTag)
.toList();
List<Tag> targetUserTag = userTagRepository.findAllByUser(targetUser).stream().map(
UserTag::getTag).toList();

int matchingCount = 0;
for (Tag tag : loginUserTag) {
if (targetUserTag.contains(tag)) {
matchingCount++;
}
}

return (int) (((double) matchingCount / loginUserTag.size()) * 100);
}

}
24 changes: 23 additions & 1 deletion src/test/java/com/cvsgo/controller/UserControllerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@
import com.cvsgo.config.WebConfig;
import com.cvsgo.dto.product.ConvenienceStoreEventDto;
import com.cvsgo.dto.product.ProductSortBy;
import com.cvsgo.dto.product.ReadUserProductRequestDto;
import com.cvsgo.dto.product.ReadProductQueryDto;
import com.cvsgo.dto.product.ReadProductResponseDto;
import com.cvsgo.dto.product.ReadUserProductRequestDto;
import com.cvsgo.dto.user.SignUpRequestDto;
import com.cvsgo.dto.user.SignUpResponseDto;
import com.cvsgo.dto.user.UpdateUserRequestDto;
Expand Down Expand Up @@ -473,6 +473,28 @@ void respond_404_when_delete_user_follow_but_user_follow_does_not_exist() throws
.andDo(print());
}

@Test
@DisplayName("태그 매칭률을 정상적으로 조회하면 HTTP 200을 응답한다")
void respond_200_when_read_user_tag_match_percentage_successfully() throws Exception {
Integer percentage = 66;
given(userService.readUserTagMatchPercentage(any(), anyLong())).willReturn(percentage);

mockMvc.perform(get("/api/users/{userId}/tag-match-percentage", 1L)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(print())
.andDo(document(documentIdentifier,
getDocumentRequest(),
getDocumentResponse(),
pathParameters(
parameterWithName("userId").description("태그 매칭률을 조회할 회원 ID")
),
relaxedResponseFields(
fieldWithPath("data").type(JsonFieldType.NUMBER).description("태그 매칭률")
)
));
}

@Test
@DisplayName("특정 회원의 좋아요 상품 목록을 정상적으로 조회하면 HTTP 200을 응답한다")
void respond_200_when_read_liked_product_list_successfully() throws Exception {
Expand Down
72 changes: 72 additions & 0 deletions src/test/java/com/cvsgo/repository/UserTagRepositoryTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.cvsgo.repository;

import static org.assertj.core.api.Assertions.assertThat;

import com.cvsgo.config.TestConfig;
import com.cvsgo.entity.Tag;
import com.cvsgo.entity.User;
import com.cvsgo.entity.UserTag;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.Import;

@Import(TestConfig.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class UserTagRepositoryTest {

@Autowired
private UserTagRepository userTagRepository;

@Autowired
private UserRepository userRepository;

@Autowired
private TagRepository tagRepository;

User user;

@BeforeEach
void initData() {
tag1 = Tag.builder().name("맵찔이").group(1).build();
tag2 = Tag.builder().name("맵부심").group(1).build();
tag3 = Tag.builder().name("초코러버").group(2).build();
tag4 = Tag.builder().name("비건").group(3).build();
tag5 = Tag.builder().name("다이어터").group(4).build();
tag6 = Tag.builder().name("대식가").group(5).build();
tag7 = Tag.builder().name("소식가").group(5).build();
tagRepository.saveAll(List.of(tag1, tag2, tag3, tag4, tag5, tag6, tag7));

user1 = User.create("[email protected]", "password1!", "닉네임1", List.of(tag1, tag3, tag6));
user2 = User.create("[email protected]", "password1!", "닉네임2", List.of(tag2, tag4, tag6));
userRepository.saveAll(List.of(user1, user2));
}

@Test
@DisplayName("유저 태그를 조회한다")
void find_user_tags_by_user() {
// when
List<UserTag> userTags = userTagRepository.findAllByUser(user1);

// then
List<Tag> tags = userTags.stream().map(UserTag::getTag).toList();
assertThat(userTags).hasSize(3);
assertThat(tags).extracting("name").containsExactly("맵찔이", "초코러버", "대식가");
assertThat(tags).extracting("name").doesNotContain("맵부심", "비건", "다이어터", "소식가");
}

private Tag tag1;
private Tag tag2;
private Tag tag3;
private Tag tag4;
private Tag tag5;
private Tag tag6;
private Tag tag7;
private User user1;
private User user2;
}
45 changes: 44 additions & 1 deletion src/test/java/com/cvsgo/service/UserServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.times;

import com.cvsgo.dto.review.ReadProductReviewResponseDto;
import com.cvsgo.dto.user.SignUpRequestDto;
import com.cvsgo.dto.user.UpdateUserRequestDto;
import com.cvsgo.entity.Review;
Expand All @@ -26,6 +25,7 @@
import com.cvsgo.repository.TagRepository;
import com.cvsgo.repository.UserFollowRepository;
import com.cvsgo.repository.UserRepository;
import com.cvsgo.repository.UserTagRepository;
import jakarta.persistence.EntityManager;
import java.util.Arrays;
import java.util.List;
Expand All @@ -51,6 +51,9 @@ class UserServiceTest {
@Mock
private TagRepository tagRepository;

@Mock
private UserTagRepository userTagRepository;

@Mock
private UserFollowRepository userFollowRepository;

Expand Down Expand Up @@ -310,6 +313,30 @@ void should_throw_NotFoundException_when_delete_user_follow_but_user_follow_does
then(userFollowRepository).should(times(1)).findByUserAndFollower(any(), any());
}

@Test
@DisplayName("태그 매칭률을 조회한다")
void succeed_to_read_user_tag_match_percentage() {
given(userRepository.findById(anyLong())).willReturn(Optional.of(user2));
given(userTagRepository.findAllByUser(any())).willReturn(List.of(userTag1, userTag2));
given(userTagRepository.findAllByUser(any())).willReturn(List.of(userTag3, userTag4));

userService.readUserTagMatchPercentage(user, user2.getId());

then(userRepository).should(times(1)).findById(user2.getId());
then(userTagRepository).should(times(2)).findAllByUser(any());
}

@Test
@DisplayName("태그 매칭률 조회 시 해당하는 아이디를 가진 사용자가 없으면 NotFoundException이 발생한다")
void should_throw_NotFoundException_when_read_user_tag_match_percentage_but_user_does_not_exist() {
final Long userId = 10000L;

given(userRepository.findById(anyLong())).willReturn(Optional.empty());

assertThrows(NotFoundException.class, () -> userService.readUserTagMatchPercentage(user, userId));
then(userRepository).should(times(1)).findById(anyLong());
}

User user = User.builder()
.id(1L)
.userId("[email protected]")
Expand All @@ -336,16 +363,32 @@ void should_throw_NotFoundException_when_delete_user_follow_but_user_follow_does
.group(2)
.build();

Tag tag3 = Tag.builder()
.id(5L)
.name("다이어터")
.group(4)
.build();

UserTag userTag1 = UserTag.builder()
.user(user)
.tag(tag1)
.build();

UserTag userTag2 = UserTag.builder()
.user(user)
.tag(tag3)
.build();

UserTag userTag3 = UserTag.builder()
.user(user2)
.tag(tag2)
.build();

UserTag userTag4 = UserTag.builder()
.user(user2)
.tag(tag3)
.build();

Review review1 = Review.builder()
.user(user)
.build();
Expand Down

0 comments on commit d0a287e

Please sign in to comment.