Skip to content

Commit

Permalink
feat: 아이템 구매 기능 구현 (#54)
Browse files Browse the repository at this point in the history
* feat: 벌레 내역 관련 Entity 생성

* feat: 아이템 구매 API 구현

* refactor: Bug -> Wallet 네이밍 수정

* refactor: Bug로 네이밍 재수정

* refactor: Entity 생성 로직 Mapper로 이동

* fix: isDefault nullable 하도록 수정

* fix: 레벨 1부터 시작하도록 수정

* test: 아이템 구매 Service 테스트

* test: 아이템 Entity 테스트

* test: 벌레 Entity 테스트

* test: 아이템 구매 Controller 테스트

* style: decrease로 메서드 네이밍 수정

* feat: 해당 벌레 타입의 개수 증가 메서드 추가

* chore: Table 어노테이션 추가

* test: 벌레 개수 증가 테스트
  • Loading branch information
kmebin authored Nov 9, 2023
1 parent 51ccfaa commit 0a25dd4
Show file tree
Hide file tree
Showing 21 changed files with 534 additions and 7 deletions.
40 changes: 40 additions & 0 deletions src/main/java/com/moabam/api/application/ItemService.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,21 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.moabam.api.domain.entity.Bug;
import com.moabam.api.domain.entity.Inventory;
import com.moabam.api.domain.entity.Item;
import com.moabam.api.domain.entity.Member;
import com.moabam.api.domain.entity.enums.ItemType;
import com.moabam.api.domain.repository.BugHistoryRepository;
import com.moabam.api.domain.repository.InventoryRepository;
import com.moabam.api.domain.repository.InventorySearchRepository;
import com.moabam.api.domain.repository.ItemRepository;
import com.moabam.api.domain.repository.ItemSearchRepository;
import com.moabam.api.dto.BugMapper;
import com.moabam.api.dto.ItemMapper;
import com.moabam.api.dto.ItemsResponse;
import com.moabam.api.dto.PurchaseItemRequest;
import com.moabam.global.error.exception.ConflictException;
import com.moabam.global.error.exception.NotFoundException;

import lombok.RequiredArgsConstructor;
Expand All @@ -23,8 +31,12 @@
@RequiredArgsConstructor
public class ItemService {

private final MemberService memberService;
private final ItemRepository itemRepository;
private final ItemSearchRepository itemSearchRepository;
private final InventoryRepository inventoryRepository;
private final InventorySearchRepository inventorySearchRepository;
private final BugHistoryRepository bugHistoryRepository;

public ItemsResponse getItems(Long memberId, ItemType type) {
List<Item> purchasedItems = inventorySearchRepository.findItems(memberId, type);
Expand All @@ -33,6 +45,22 @@ public ItemsResponse getItems(Long memberId, ItemType type) {
return ItemMapper.toItemsResponse(purchasedItems, notPurchasedItems);
}

@Transactional
public void purchaseItem(Long memberId, Long itemId, PurchaseItemRequest request) {
Item item = getItem(itemId);
Member member = memberService.getById(memberId);

validateAlreadyPurchased(memberId, itemId);
item.validatePurchasable(request.bugType(), member.getLevel());

Bug bug = member.getBug();
int price = item.getPrice(request.bugType());

bug.use(request.bugType(), price);
inventoryRepository.save(ItemMapper.toInventory(memberId, item));
bugHistoryRepository.save(BugMapper.toUseBugHistory(memberId, request.bugType(), price));
}

@Transactional
public void selectItem(Long memberId, Long itemId) {
Inventory inventory = getInventory(memberId, itemId);
Expand All @@ -42,8 +70,20 @@ public void selectItem(Long memberId, Long itemId) {
inventory.select();
}

private Item getItem(Long itemId) {
return itemRepository.findById(itemId)
.orElseThrow(() -> new NotFoundException(ITEM_NOT_FOUND));
}

private Inventory getInventory(Long memberId, Long itemId) {
return inventorySearchRepository.findOne(memberId, itemId)
.orElseThrow(() -> new NotFoundException(INVENTORY_NOT_FOUND));
}

private void validateAlreadyPurchased(Long memberId, Long itemId) {
inventorySearchRepository.findOne(memberId, itemId)
.ifPresent(inventory -> {
throw new ConflictException(INVENTORY_CONFLICT);
});
}
}
37 changes: 37 additions & 0 deletions src/main/java/com/moabam/api/domain/entity/Bug.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.BugType;
import com.moabam.global.error.exception.BadRequestException;

import jakarta.persistence.Column;
Expand Down Expand Up @@ -44,4 +45,40 @@ private int validateBugCount(int bug) {

return bug;
}

public void use(BugType bugType, int price) {
int currentBug = getBug(bugType);
validateEnoughBug(currentBug, price);
decreaseBug(bugType, price);
}

private int getBug(BugType bugType) {
return switch (bugType) {
case MORNING -> this.morningBug;
case NIGHT -> this.nightBug;
case GOLDEN -> this.goldenBug;
};
}

private void validateEnoughBug(int currentBug, int price) {
if (price > currentBug) {
throw new BadRequestException(BUG_NOT_ENOUGH);
}
}

private void decreaseBug(BugType bugType, int bug) {
switch (bugType) {
case MORNING -> this.morningBug -= bug;
case NIGHT -> this.nightBug -= bug;
case GOLDEN -> this.goldenBug -= bug;
}
}

public void increaseBug(BugType bugType, int bug) {
switch (bugType) {
case MORNING -> this.morningBug += bug;
case NIGHT -> this.nightBug += bug;
case GOLDEN -> this.goldenBug += bug;
}
}
}
64 changes: 64 additions & 0 deletions src/main/java/com/moabam/api/domain/entity/BugHistory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.moabam.api.domain.entity;

import static com.moabam.global.error.model.ErrorMessage.*;
import static java.util.Objects.*;

import com.moabam.api.domain.entity.enums.BugActionType;
import com.moabam.api.domain.entity.enums.BugType;
import com.moabam.global.common.entity.BaseTimeEntity;
import com.moabam.global.error.exception.BadRequestException;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@Table(name = "bug_history")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class BugHistory extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;

@Column(name = "member_id", updatable = false, nullable = false)
private Long memberId;

@Enumerated(value = EnumType.STRING)
@Column(name = "bug_type", nullable = false)
private BugType bugType;

@Enumerated(value = EnumType.STRING)
@Column(name = "action_type", nullable = false)
private BugActionType actionType;

@Column(name = "quantity", nullable = false)
private int quantity;

@Builder
private BugHistory(Long memberId, BugType bugType, BugActionType actionType, int quantity) {
this.memberId = requireNonNull(memberId);
this.bugType = requireNonNull(bugType);
this.actionType = requireNonNull(actionType);
this.quantity = validateQuantity(quantity);
}

private int validateQuantity(int quantity) {
if (quantity < 0) {
throw new BadRequestException(INVALID_QUANTITY);
}

return quantity;
}
}
22 changes: 22 additions & 0 deletions src/main/java/com/moabam/api/domain/entity/Item.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import org.hibernate.annotations.ColumnDefault;

import com.moabam.api.domain.entity.enums.BugType;
import com.moabam.api.domain.entity.enums.ItemCategory;
import com.moabam.api.domain.entity.enums.ItemType;
import com.moabam.global.common.entity.BaseTimeEntity;
Expand Down Expand Up @@ -87,4 +88,25 @@ private int validateLevel(int level) {

return level;
}

public void validatePurchasable(BugType bugType, int memberLevel) {
validateUnlocked(memberLevel);
validateBugTypeMatch(bugType);
}

private void validateUnlocked(int memberLevel) {
if (this.unlockLevel > memberLevel) {
throw new BadRequestException(ITEM_UNLOCK_LEVEL_HIGH);
}
}

private void validateBugTypeMatch(BugType bugType) {
if (!this.type.isPurchasableBy(bugType)) {
throw new BadRequestException(ITEM_NOT_PURCHASABLE_BY_BUG_TYPE);
}
}

public int getPrice(BugType bugType) {
return bugType.isGoldenBug() ? this.goldenBugPrice : this.bugPrice;
}
}
5 changes: 5 additions & 0 deletions src/main/java/com/moabam/api/domain/entity/Member.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.moabam.api.domain.entity;

import static com.moabam.global.common.util.GlobalConstant.*;
import static java.util.Objects.*;

import java.time.LocalDateTime;
Expand Down Expand Up @@ -107,4 +108,8 @@ public void exitNightRoom() {
currentNightCount--;
}
}

public int getLevel() {
return (int)(totalCertifyCount / LEVEL_DIVISOR) + 1;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.moabam.api.domain.entity.enums;

public enum BugActionType {

REWARD,
CHARGE,
USE,
REFUND,
COUPON;
}
12 changes: 12 additions & 0 deletions src/main/java/com/moabam/api/domain/entity/enums/BugType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.moabam.api.domain.entity.enums;

public enum BugType {

MORNING,
NIGHT,
GOLDEN;

public boolean isGoldenBug() {
return this == GOLDEN;
}
}
16 changes: 14 additions & 2 deletions src/main/java/com/moabam/api/domain/entity/enums/ItemType.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
package com.moabam.api.domain.entity.enums;

import java.util.List;

public enum ItemType {

MORNING,
NIGHT;
MORNING(List.of(BugType.MORNING, BugType.GOLDEN)),
NIGHT(List.of(BugType.NIGHT, BugType.GOLDEN));

private final List<BugType> purchasableBugTypes;

ItemType(List<BugType> purchasableBugTypes) {
this.purchasableBugTypes = purchasableBugTypes;
}

public boolean isPurchasableBy(BugType bugType) {
return this.purchasableBugTypes.contains(bugType);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.moabam.api.domain.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.moabam.api.domain.entity.BugHistory;

public interface BugHistoryRepository extends JpaRepository<BugHistory, Long> {

}
12 changes: 12 additions & 0 deletions src/main/java/com/moabam/api/dto/BugMapper.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.moabam.api.dto;

import com.moabam.api.domain.entity.Bug;
import com.moabam.api.domain.entity.BugHistory;
import com.moabam.api.domain.entity.enums.BugActionType;
import com.moabam.api.domain.entity.enums.BugType;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
Expand All @@ -15,4 +18,13 @@ public static BugResponse toBugResponse(Bug bug) {
.goldenBug(bug.getGoldenBug())
.build();
}

public static BugHistory toUseBugHistory(Long memberId, BugType bugType, int quantity) {
return BugHistory.builder()
.memberId(memberId)
.bugType(bugType)
.actionType(BugActionType.USE)
.quantity(quantity)
.build();
}
}
8 changes: 8 additions & 0 deletions src/main/java/com/moabam/api/dto/ItemMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.List;

import com.moabam.api.domain.entity.Inventory;
import com.moabam.api.domain.entity.Item;
import com.moabam.global.common.util.StreamUtils;

Expand Down Expand Up @@ -30,4 +31,11 @@ public static ItemsResponse toItemsResponse(List<Item> purchasedItems, List<Item
.notPurchasedItems(StreamUtils.map(notPurchasedItems, ItemMapper::toItemResponse))
.build();
}

public static Inventory toInventory(Long memberId, Item item) {
return Inventory.builder()
.memberId(memberId)
.item(item)
.build();
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/moabam/api/dto/PurchaseItemRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.moabam.api.dto;

import com.moabam.api.domain.entity.enums.BugType;

import jakarta.validation.constraints.NotNull;

public record PurchaseItemRequest(
@NotNull BugType bugType
) {

}
9 changes: 9 additions & 0 deletions src/main/java/com/moabam/api/presentation/ItemController.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
Expand All @@ -12,7 +13,9 @@
import com.moabam.api.application.ItemService;
import com.moabam.api.domain.entity.enums.ItemType;
import com.moabam.api.dto.ItemsResponse;
import com.moabam.api.dto.PurchaseItemRequest;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@RestController
Expand All @@ -28,6 +31,12 @@ public ItemsResponse getItems(@RequestParam ItemType type) {
return itemService.getItems(1L, type);
}

@PostMapping("/{itemId}/purchase")
@ResponseStatus(HttpStatus.OK)
public void purchaseItem(@PathVariable Long itemId, @Valid @RequestBody PurchaseItemRequest request) {
itemService.purchaseItem(1L, itemId, request);
}

@PostMapping("/{itemId}/select")
@ResponseStatus(HttpStatus.OK)
public void selectItem(@PathVariable Long itemId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ public class GlobalConstant {
public static final int HOURS_IN_A_DAY = 24;
public static final String KNOCK_KEY = "room_%s_member_%s_knocks_%s";
public static final String FIREBASE_PATH = "config/moabam-firebase.json";

public static final int LEVEL_DIVISOR = 10;
}
Loading

0 comments on commit 0a25dd4

Please sign in to comment.