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] 인벤토리 API #46

Merged
merged 13 commits into from
Oct 3, 2024
4 changes: 4 additions & 0 deletions src/main/java/ku/user/domain/character/domain/Character.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,8 @@ public Character update(Character character) {
public void setUserId(Long userId) {
this.userId = userId;
}

public void pay(int price) {
this.currentMoney -= price;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ku.user.domain.character.exception;

import ku.user.global.exception.CustomException;
import ku.user.global.exception.ErrorCode;

public class CurrentMoneyLeakException extends CustomException {
public CurrentMoneyLeakException() {
super("현재 재화가 부족합니다.", ErrorCode.INTERNAL_SERVER_ERROR);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import ku.user.domain.character.dao.CharacterRepository;
import ku.user.domain.character.domain.Character;
import ku.user.domain.character.exception.CharacterCreateException;
import ku.user.domain.character.exception.CurrentMoneyLeakException;
import ku.user.domain.user.infrastructure.entity.UserEntity;
import ku.user.domain.user.service.UserService;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -91,4 +92,13 @@ public Boolean checkCharacterExistByEmail(String email) {
}
}

@Transactional
public Character payPriceByEmail(String email, int price) {
Character character = findByEmail(email);
if (character.getCurrentMoney() < price) {
throw new CurrentMoneyLeakException();
}
character.pay(price);
return character;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package ku.user.domain.inventory.controller;

import ku.user.domain.inventory.domain.Inventory;
import ku.user.domain.inventory.domain.ItemType;
import ku.user.domain.inventory.dto.request.PostItemRequest;
import ku.user.domain.inventory.dto.response.GetInventoryResponse;
import ku.user.domain.inventory.dto.response.PostItemResponse;
import ku.user.domain.inventory.service.InventoryService;
import ku.user.global.response.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
public class InventoryController {

private final InventoryService inventoryService;

@GetMapping("/inventorys")
public ApiResponse<GetInventoryResponse> GetInventory(@RequestParam String email) {
Inventory inventory = inventoryService.getInventoryByEmail(email);
GetInventoryResponse getInventoryResponse = GetInventoryResponse.toDto(inventory);
return new ApiResponse<>(true,getInventoryResponse,null);
}

@PostMapping("/items")
public ApiResponse<PostItemResponse> PostItem(@RequestParam String email, @RequestBody PostItemRequest postItemRequest) {
Inventory inventory = inventoryService.buyItem(email, postItemRequest.getItemType());
PostItemResponse postItemResponse = PostItemResponse.toDto(inventory);
return new ApiResponse<>(true,postItemResponse,null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ku.user.domain.inventory.dao;

import ku.user.domain.inventory.domain.Inventory;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface InventoryRepository extends JpaRepository<Inventory, Long> {

Optional<Inventory> findByUserId(Long userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ku.user.domain.inventory.dao;

import ku.user.domain.inventory.domain.Item;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ItemRepository extends JpaRepository<Item, Long> {
}
35 changes: 35 additions & 0 deletions src/main/java/ku/user/domain/inventory/domain/Inventory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package ku.user.domain.inventory.domain;

import jakarta.persistence.*;
import lombok.*;

import java.util.ArrayList;
import java.util.List;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Table(name = "inventorys")
@Builder
@Getter
public class Inventory {

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

private Long userId;
Copy link
Member

Choose a reason for hiding this comment

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

PR 본문에도 궁금했던 내용인데 유저와 인벤토리가 연결되는 이유를 모르겠습니다. 인벤토리는 캐릭터와 연결되는게 맞는 그림이 아닌가 생각하는데 의도하신 이유가 궁금합니다.

Copy link
Member Author

Choose a reason for hiding this comment

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

잘못생각했습니다. 캐릭터와 관계를 가져야 하는 것이 맞다고 생각합니다


@OneToMany(mappedBy = "inventory")
private List<Item> itemList = new ArrayList<>();

public static Inventory from(Long userId) {
return Inventory.builder()
.userId(userId)
.build();
}

public void addItem(Item item) {
itemList.add(item);
}
}
30 changes: 30 additions & 0 deletions src/main/java/ku/user/domain/inventory/domain/Item.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package ku.user.domain.inventory.domain;

import jakarta.persistence.*;
import lombok.*;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Table(name = "items")
@Builder
@Getter
public class Item {

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

@ManyToOne
@JoinColumn(name = "item_id")
private Inventory inventory;

private ItemType itemType;

public static Item from(Inventory inventory, ItemType itemType){
return Item.builder()
.itemType(itemType)
.inventory(inventory)
.build();
}
}
21 changes: 21 additions & 0 deletions src/main/java/ku/user/domain/inventory/domain/ItemType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ku.user.domain.inventory.domain;

import lombok.Getter;

@Getter
public enum ItemType {

//오로라
AURORA(1L, 100),
//킥보드
KICK_BOARD(2L, 100),
//칭호
TITLE(3L, 100);
private Long itemNumber;
private int price;

ItemType(Long itemNumber, int price) {
this.itemNumber = itemNumber;
this.price = price;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ku.user.domain.inventory.dto.request;

import ku.user.domain.inventory.domain.ItemType;
import lombok.Getter;

@Getter
public class PostItemRequest {

private ItemType itemType;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package ku.user.domain.inventory.dto.response;

import ku.user.domain.inventory.domain.Inventory;
import ku.user.domain.inventory.domain.Item;
import lombok.Builder;

import java.util.List;

@Builder
public record GetInventoryResponse(Long id,
Long userId,
List<GetItemResponse> itemList) {

public static GetInventoryResponse toDto(Inventory inventory) {

List<GetItemResponse> getItemResponseList = inventory.getItemList().stream().map(GetItemResponse::toDto).toList();

return GetInventoryResponse.builder()
.id(inventory.getId())
.userId(inventory.getUserId())
.itemList(getItemResponseList)
.build();

}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package ku.user.domain.inventory.dto.response;

import ku.user.domain.inventory.domain.Item;
import ku.user.domain.inventory.domain.ItemType;
import lombok.Builder;

@Builder
public record GetItemResponse(Long id,
ItemType itemType,
Long itemNumber) {
public static GetItemResponse toDto(Item item) {
return GetItemResponse.builder()
.id(item.getId())
.itemType(item.getItemType())
.itemNumber(item.getItemType().getItemNumber())
.build();

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package ku.user.domain.inventory.dto.response;

import ku.user.domain.inventory.domain.Inventory;
import ku.user.domain.inventory.domain.Item;
import ku.user.domain.inventory.domain.ItemType;
import lombok.Builder;

import java.util.List;

/**
* 아이템 추가 API Response
* @param id
* @param userId
* @param itemList
* 아이템을 추가하지만 인밴토리를 반환한다.
*/
@Builder
public record PostItemResponse(Long id,
Long userId,
List<GetItemResponse> itemList) {

public static PostItemResponse toDto(Inventory inventory) {

List<GetItemResponse> getItemResponseList = inventory.getItemList().stream().map(GetItemResponse::toDto).toList();

return PostItemResponse.builder()
.id(inventory.getId())
.userId(inventory.getUserId())
.itemList(getItemResponseList)
.build();

}
}
101 changes: 101 additions & 0 deletions src/main/java/ku/user/domain/inventory/service/InventoryService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package ku.user.domain.inventory.service;

import ku.user.domain.character.domain.Character;
import ku.user.domain.character.service.CharacterService;
import ku.user.domain.inventory.dao.InventoryRepository;
import ku.user.domain.inventory.dao.ItemRepository;
import ku.user.domain.inventory.domain.Inventory;
import ku.user.domain.inventory.domain.Item;
import ku.user.domain.inventory.domain.ItemType;
import ku.user.domain.user.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

@Service
@RequiredArgsConstructor
public class InventoryService {

private final InventoryRepository inventoryRepository;
private final ItemRepository itemRepository;
private final UserService userService;
private final CharacterService characterService;


@Transactional
public Inventory findByUserId(Long userId){
Optional<Inventory> inventoryOptional = inventoryRepository.findByUserId(userId);
if(inventoryOptional.isEmpty())
throw new RuntimeException("해당하는 인벤토리가 없습니다. 회원가입된 아이디가 아닙니다.");
Copy link
Member

Choose a reason for hiding this comment

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

예외 내용에 두 가지가 들어가는거 같은데 이유가 있을까요?

Copy link
Member Author

Choose a reason for hiding this comment

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

현재 개발된 비즈니스 로직에 따르면 유저를 생성하면서 인벤토리를 생성하기 때문에 인벤토리가 없는경우가 직접삭제하거나 회원가입조차되지 않은 경우에 해당합니다
수정이 필요한 부분 같습니다. 관련된 내용으로 캐릭터가 생성될때 인벤토리가 같이 생성되도록 변경해야 합니다.
비즈니스로 로직상으로 회원가입이 된 이후에 해당 API를 호출한다는 점에서 "회원가입된 아이디가 아닙니다" 가 삭제되어야합니다

return inventoryOptional.get();
}

@Transactional
public Inventory save(Inventory inventory) {
return inventoryRepository.save(inventory);
}

@Transactional
public Item saveItem(Item item) {
return itemRepository.save(item);
}

/**
* 유저가 생성될때, 인벤토리 엔티티를 추가한다.
*
* @param userId 유저id
* @return 생성된 인벤토리
*/
@Transactional
public Inventory createInventory(Long userId) {
Inventory inventory = Inventory.from(userId);
return save(inventory);
}

/**
* 유저 이메일에 대한 인벤토리를 찾는다.
*
* @param email 유저 이메일
* @return 인벤토리
*/
@Transactional(readOnly = true)
public Inventory getInventoryByEmail(String email) {

Long userId = userService.getByEmail(email).getId();
Optional<Inventory> inventoryOptional = inventoryRepository.findByUserId(userId);

return inventoryOptional.get();
}

/**
* 이메일해당하는 유저가 아이템을 산다
* @param email
* @param itemType
* @return
*/
@Transactional
public Inventory buyItem(String email, ItemType itemType) {
Character character = characterService.payPriceByEmail(email, itemType.getPrice());
Long userId = character.getUserId();
Inventory findInventory = findByUserId(userId);
Inventory updatInventory = addItem(findInventory, itemType);
return updatInventory;
}

/**
* 인벤토리에 아이템을 저장하고 추가한다.
* @param findInventory 인벤토리
* @param itemType 추가할 아이템
* @return 인벤토리
* 트랙잭션 전파를 사용한다.
*/
private Inventory addItem(Inventory findInventory, ItemType itemType) {
Item item = Item.from(findInventory, itemType);
Item saveItem = saveItem(item);
findInventory.addItem(saveItem);
return findInventory;
}

}
Loading
Loading