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

feat: 아이템 적용 기능 구현 #45

Merged
merged 28 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2d0f4ef
refactor: ResponseStatus + DTO 방식으로 변경
kmebin Nov 1, 2023
c8d07a4
Merge branch 'develop' into feature/#23
kmebin Nov 3, 2023
8555fb8
feat: 아이템, 인벤토리 Entity 생성
kmebin Nov 4, 2023
b6703af
feat: 아이템 목록 조회 API 구현
kmebin Nov 4, 2023
fbfa925
test: containsExactly 검증으로 수정
kmebin Nov 4, 2023
6f939a5
test: 아이템 목록 조회 Service 테스트
kmebin Nov 4, 2023
f502897
test: 인벤토리 아이템 목록 조회 Repository 테스트
kmebin Nov 4, 2023
d4f60dc
feat: Stream 유틸 클래스 생성 및 적용
kmebin Nov 4, 2023
e77c284
fix: ItemFixture를 통한 아이템 생성 시 build() 추가
kmebin Nov 4, 2023
a0a7c3e
test: 구매하지 않은 아이템 목록 조회 Repository 테스트
kmebin Nov 4, 2023
2b88546
feat: MethodArgumentTypeMismatchException handler 추가
kmebin Nov 4, 2023
9e50a2b
test: 아이템 목록 조회 Controller 테스트
kmebin Nov 4, 2023
abbb40f
Merge branch 'develop' into feature/#23-get-items
kmebin Nov 4, 2023
c5000ad
feat: 아이템 적용 API 구현
kmebin Nov 5, 2023
e1cd9fd
test: 아이템 적용 Service 테스트
kmebin Nov 5, 2023
344d99e
test: Controller 테스트 @WebMvcTest로 변경
kmebin Nov 5, 2023
b1524f5
test: 아이템 적용 Controller 테스트
kmebin Nov 5, 2023
73bc05f
style: support 패키지 생성
kmebin Nov 5, 2023
2ba7aaf
test: RepositoryTest 어노테이션 생성 및 적용
kmebin Nov 5, 2023
2d008d8
test: 동일 메서드 테스트 Nested로 처리
kmebin Nov 5, 2023
42c13bb
feat: 현재 적용된 인벤토리 조회 시 아이템 타입 정보 추가
kmebin Nov 6, 2023
d4d5e71
test: 인벤토리 조회 Repository 테스트
kmebin Nov 6, 2023
7f5255e
Merge branch 'develop' into feature/#42-select-item
kmebin Nov 6, 2023
dcd87fd
fix: merge conflict 해결
kmebin Nov 6, 2023
e132a0b
Merge branch 'develop' into feature/#42-select-item
kmebin Nov 7, 2023
1f1f5b5
test: given-willReturn 으로 변경
kmebin Nov 7, 2023
a1362d7
refactor: 메서드 네이밍 수정
kmebin Nov 7, 2023
051f695
refactor: 어노테이션 네이밍 수정
kmebin Nov 7, 2023
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
18 changes: 18 additions & 0 deletions src/main/java/com/moabam/api/application/ItemService.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package com.moabam.api.application;

import static com.moabam.global.error.model.ErrorMessage.*;

import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.moabam.api.domain.entity.Inventory;
import com.moabam.api.domain.entity.Item;
import com.moabam.api.domain.entity.enums.ItemType;
import com.moabam.api.domain.repository.InventorySearchRepository;
import com.moabam.api.domain.repository.ItemSearchRepository;
import com.moabam.api.dto.ItemMapper;
import com.moabam.api.dto.ItemsResponse;
import com.moabam.global.error.exception.NotFoundException;

import lombok.RequiredArgsConstructor;

Expand All @@ -28,4 +32,18 @@ public ItemsResponse getItems(Long memberId, ItemType type) {

return ItemMapper.toItemsResponse(purchasedItems, notPurchasedItems);
}

@Transactional
public void selectItem(Long memberId, Long itemId) {
Inventory inventory = getInventory(memberId, itemId);

inventorySearchRepository.findDefault(memberId, inventory.getItemType())
.ifPresent(Inventory::unsetDefault);
inventory.setDefault();
}

private Inventory getInventory(Long memberId, Long itemId) {
return inventorySearchRepository.findOne(memberId, itemId)
.orElseThrow(() -> new NotFoundException(INVENTORY_NOT_FOUND));
}
}
13 changes: 13 additions & 0 deletions src/main/java/com/moabam/api/domain/entity/Inventory.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import org.hibernate.annotations.ColumnDefault;

import com.moabam.api.domain.entity.enums.ItemType;
import com.moabam.global.common.entity.BaseTimeEntity;

import jakarta.persistence.Column;
Expand Down Expand Up @@ -49,4 +50,16 @@ private Inventory(Long memberId, Item item, boolean isDefault) {
this.item = requireNonNull(item);
this.isDefault = isDefault;
}

public ItemType getItemType() {
return this.item.getType();
}

public void setDefault() {
this.isDefault = true;
}

public void unsetDefault() {
this.isDefault = false;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C: (김앵맹) unset은 공식 영어가 아님 히히

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아놔 ㅋㅋ 네이밍 추천해주고가

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(김앵맹) ㅋㅋ 수고

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import static com.moabam.api.domain.entity.QItem.*;

import java.util.List;
import java.util.Optional;

import org.springframework.stereotype.Repository;

import com.moabam.api.domain.entity.Inventory;
import com.moabam.api.domain.entity.Item;
import com.moabam.api.domain.entity.enums.ItemType;
import com.moabam.global.common.util.DynamicQuery;
Expand All @@ -20,6 +22,27 @@ public class InventorySearchRepository {

private final JPAQueryFactory jpaQueryFactory;

public Optional<Inventory> findOne(Long memberId, Long itemId) {
return Optional.ofNullable(
jpaQueryFactory.selectFrom(inventory)
.where(
DynamicQuery.generateEq(memberId, inventory.memberId::eq),
DynamicQuery.generateEq(itemId, inventory.item.id::eq))
.fetchOne()
);
}

public Optional<Inventory> findDefault(Long memberId, ItemType type) {
return Optional.ofNullable(
jpaQueryFactory.selectFrom(inventory)
.where(
DynamicQuery.generateEq(memberId, inventory.memberId::eq),
DynamicQuery.generateEq(type, inventory.item.type::eq),
inventory.isDefault.isTrue())
.fetchOne()
);
}

public List<Item> findItems(Long memberId, ItemType type) {
return jpaQueryFactory.selectFrom(inventory)
.join(inventory.item, item)
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/com/moabam/api/presentation/ItemController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
Expand All @@ -25,4 +27,10 @@ public class ItemController {
public ItemsResponse getItems(@RequestParam ItemType type) {
return itemService.getItems(1L, type);
}

@PostMapping("/{itemId}/select")
@ResponseStatus(HttpStatus.OK)
public void selectItem(@PathVariable Long itemId) {
itemService.selectItem(1L, itemId);
}
}
2 changes: 2 additions & 0 deletions src/main/java/com/moabam/global/error/model/ErrorMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public enum ErrorMessage {
MEMBER_NOT_FOUND("존재하지 않는 회원입니다."),
MEMBER_ROOM_EXCEED("참여할 수 있는 방의 개수가 모두 찼습니다."),

INVENTORY_NOT_FOUND("구매하지 않은 아이템은 적용할 수 없습니다."),

INVALID_BUG_COUNT("벌레 개수는 0 이상이어야 합니다."),
INVALID_PRICE("가격은 0 이상이어야 합니다."),
INVALID_QUANTITY("수량은 1 이상이어야 합니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@
import com.moabam.api.dto.AuthorizationTokenRequest;
import com.moabam.api.dto.AuthorizationTokenResponse;
import com.moabam.api.dto.OAuthMapper;
import com.moabam.fixture.AuthorizationResponseFixture;
import com.moabam.global.config.OAuthConfig;
import com.moabam.global.error.exception.BadRequestException;
import com.moabam.global.error.model.ErrorMessage;
import com.moabam.support.fixture.AuthorizationResponseFixture;

@ExtendWith(MockitoExtension.class)
class AuthenticationServiceTest {
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/com/moabam/api/application/BugServiceTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.moabam.api.application;

import static com.moabam.support.fixture.MemberFixture.*;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;

Expand All @@ -13,7 +14,6 @@
import com.moabam.api.domain.entity.Bug;
import com.moabam.api.domain.entity.Member;
import com.moabam.api.dto.BugResponse;
import com.moabam.fixture.MemberFixture;

@ExtendWith(MockitoExtension.class)
class BugServiceTest {
Expand All @@ -29,7 +29,7 @@ class BugServiceTest {
void get_bug_success() {
// given
Long memberId = 1L;
Member member = MemberFixture.member();
Member member = member();
given(memberService.getById(memberId)).willReturn(member);

// when
Expand Down
49 changes: 48 additions & 1 deletion src/test/java/com/moabam/api/application/ItemServiceTest.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
package com.moabam.api.application;

import static com.moabam.fixture.ItemFixture.*;
import static com.moabam.support.fixture.ItemFixture.*;
import static java.util.Collections.*;
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.BDDMockito.*;

import java.util.List;
import java.util.Optional;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import com.moabam.api.domain.entity.Inventory;
import com.moabam.api.domain.entity.Item;
import com.moabam.api.domain.entity.enums.ItemType;
import com.moabam.api.domain.repository.InventorySearchRepository;
import com.moabam.api.domain.repository.ItemSearchRepository;
import com.moabam.api.dto.ItemResponse;
import com.moabam.api.dto.ItemsResponse;
import com.moabam.global.common.util.StreamUtils;
import com.moabam.global.error.exception.NotFoundException;
import com.moabam.support.fixture.InventoryFixture;

@ExtendWith(MockitoExtension.class)
class ItemServiceTest {
Expand Down Expand Up @@ -54,4 +60,45 @@ void get_products_success() {
assertThat(purchasedItemNames).containsExactly(MORNING_SANTA_SKIN_NAME, MORNING_KILLER_SKIN_NAME);
assertThat(response.notPurchasedItems()).isEmpty();
}

@DisplayName("아이템을 적용한다.")
@Nested
class SelectItem {

@DisplayName("성공한다.")
@Test
Comment on lines +64 to +69
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C: 뭔가.. 뭔가... 쪼... 쪼끔만더 DisplayName을 신경써준다면,,,

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위에 Nested로 묶어서 괜찮지 않을까한..?

void success() {
// given
Long memberId = 1L;
Long itemId = 1L;
Inventory inventory = InventoryFixture.inventory(memberId, nightMageSkin());
Inventory defaultInventory = InventoryFixture.inventory(memberId, nightMageSkin());
ItemType itemType = inventory.getItemType();
when(inventorySearchRepository.findOne(memberId, itemId)).thenReturn(Optional.of(inventory));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given으로 바꾸는건 어떻게 생각하시나요?
가독성이 더 좋지 않을까 싶습니다

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

머야 왜 이거 이렇게 했지 ㅋㅋ 감사요

when(inventorySearchRepository.findDefault(memberId, itemType)).thenReturn(Optional.of(defaultInventory));

// when
itemService.selectItem(memberId, itemId);

// then
verify(inventorySearchRepository).findOne(memberId, itemId);
verify(inventorySearchRepository).findDefault(memberId, itemType);
assertFalse(defaultInventory.isDefault());
assertTrue(inventory.isDefault());
}

@DisplayName("인벤토리 아이템이 아니면 예외가 발생한다.")
@Test
void exception() {
// given
Long memberId = 1L;
Long itemId = 1L;
when(inventorySearchRepository.findOne(memberId, itemId)).thenReturn(Optional.empty());

// when, then
assertThatThrownBy(() -> itemService.selectItem(memberId, itemId))
.isInstanceOf(NotFoundException.class)
.hasMessage("구매하지 않은 아이템은 적용할 수 없습니다.");
Comment on lines +99 to +101
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

R: 텍스트를 그대로 박는 것보다 ErrorMessage.INVENTORY_NOT_FOUND.getMessage() 같은 방식은 어떠한가요 !?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

실제 값을 박아야 INVENTORY_NOT_FOUND에 잘못된 메세지가 적용되었을 경우에 에러를 잡을 수 있다고 생각했슴니다~

}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.moabam.api.application;

import static com.moabam.fixture.ProductFixture.*;
import static com.moabam.support.fixture.ProductFixture.*;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;

Expand Down
2 changes: 1 addition & 1 deletion src/test/java/com/moabam/api/domain/entity/MemberTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import org.junit.jupiter.api.Test;

import com.moabam.api.domain.entity.enums.Role;
import com.moabam.fixture.MemberFixture;
import com.moabam.global.common.util.BaseImageUrl;
import com.moabam.support.fixture.MemberFixture;

class MemberTest {

Expand Down
Loading