diff --git a/src/main/java/until/the/eternity/item/application/service/ItemService.java b/src/main/java/until/the/eternity/item/application/service/ItemService.java deleted file mode 100644 index 49a8dee5..00000000 --- a/src/main/java/until/the/eternity/item/application/service/ItemService.java +++ /dev/null @@ -1,18 +0,0 @@ -package until.the.eternity.item.application.service; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import until.the.eternity.item.interfaces.rest.dto.ItemCategoryResponse; - -import java.util.List; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class ItemService { - - public List findItemCategories() { - return ItemCategoryResponse.from(); - } -} diff --git a/src/main/java/until/the/eternity/item/interfaces/rest/controller/ItemController.java b/src/main/java/until/the/eternity/item/interfaces/rest/controller/ItemController.java deleted file mode 100644 index 2d134108..00000000 --- a/src/main/java/until/the/eternity/item/interfaces/rest/controller/ItemController.java +++ /dev/null @@ -1,30 +0,0 @@ -package until.the.eternity.item.interfaces.rest.controller; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import until.the.eternity.common.response.ApiResponse; -import until.the.eternity.item.application.service.ItemService; -import until.the.eternity.item.interfaces.rest.dto.ItemCategoryResponse; - -import java.util.List; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/items") -@Tag(name = "아이템 정보 API", description = "아이템 정보 API") -public class ItemController { - - private final ItemService itemService; - - @GetMapping("/categories") - @Operation(summary = "카테고리 정보", description = "아이템 상위 카테고리, 하위 카테고리 정보 조회") - public ResponseEntity>> findItemCategories() { - List itemCategories = itemService.findItemCategories(); - return ResponseEntity.ok(ApiResponse.success(itemCategories)); - } -} diff --git a/src/main/java/until/the/eternity/iteminfo/application/service/ItemInfoService.java b/src/main/java/until/the/eternity/iteminfo/application/service/ItemInfoService.java new file mode 100644 index 00000000..333a39a1 --- /dev/null +++ b/src/main/java/until/the/eternity/iteminfo/application/service/ItemInfoService.java @@ -0,0 +1,38 @@ +package until.the.eternity.iteminfo.application.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import until.the.eternity.iteminfo.domain.entity.ItemInfo; +import until.the.eternity.iteminfo.domain.repository.ItemInfoRepositoryPort; +import until.the.eternity.iteminfo.interfaces.rest.dto.response.ItemCategoryResponse; +import until.the.eternity.iteminfo.interfaces.rest.dto.response.ItemInfoResponse; + +import java.util.List; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class ItemInfoService { + + private final ItemInfoRepositoryPort itemInfoRepository; + + public List findItemCategories() { + return ItemCategoryResponse.from(); + } + + public List findAll() { + List itemInfos = itemInfoRepository.findAll(); + return ItemInfoResponse.from(itemInfos); + } + + public List findByTopCategory(String topCategory) { + List itemInfos = itemInfoRepository.findByTopCategory(topCategory); + return ItemInfoResponse.from(itemInfos); + } + + public List findBySubCategory(String subCategory) { + List itemInfos = itemInfoRepository.findBySubCategory(subCategory); + return ItemInfoResponse.from(itemInfos); + } +} diff --git a/src/main/java/until/the/eternity/iteminfo/domain/entity/ItemInfo.java b/src/main/java/until/the/eternity/iteminfo/domain/entity/ItemInfo.java new file mode 100644 index 00000000..a351b9e0 --- /dev/null +++ b/src/main/java/until/the/eternity/iteminfo/domain/entity/ItemInfo.java @@ -0,0 +1,56 @@ +package until.the.eternity.iteminfo.domain.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Table(name = "item_info") +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class ItemInfo { + + @Id + @Column(name = "name", nullable = false) + private String name; + + @Column(name = "sub_category", nullable = false) + private String subCategory; + + @Column(name = "top_category", nullable = false) + private String topCategory; + + @Column(name = "description") + private String description; + + @Column(name = "inventory_width") + private Byte inventoryWidth; + + @Column(name = "inventory_height") + private Byte inventoryHeight; + + @Column(name = "inventory_max_bundle_count") + private Integer inventoryMaxBundleCount; + + @Column(name = "history", length = 3000) + private String history; + + @Column(name = "acquisition_method", length = 3000) + private String acquisitionMethod; + + @Column(name = "store_sales_price", length = 3000) + private String storeSalesPrice; + + @Column(name = "weapon_type") + private String weaponType; + + @Column(name = "repair") + private String repair; + + @Column(name = "max_alteration_count") + private Byte maxAlterationCount; +} diff --git a/src/main/java/until/the/eternity/iteminfo/domain/repository/ItemInfoRepositoryPort.java b/src/main/java/until/the/eternity/iteminfo/domain/repository/ItemInfoRepositoryPort.java new file mode 100644 index 00000000..395897d3 --- /dev/null +++ b/src/main/java/until/the/eternity/iteminfo/domain/repository/ItemInfoRepositoryPort.java @@ -0,0 +1,13 @@ +package until.the.eternity.iteminfo.domain.repository; + +import until.the.eternity.iteminfo.domain.entity.ItemInfo; + +import java.util.List; + +public interface ItemInfoRepositoryPort { + List findAll(); + + List findByTopCategory(String topCategory); + + List findBySubCategory(String subCategory); +} diff --git a/src/main/java/until/the/eternity/iteminfo/infrastructure/persistence/ItemInfoJpaRepository.java b/src/main/java/until/the/eternity/iteminfo/infrastructure/persistence/ItemInfoJpaRepository.java new file mode 100644 index 00000000..b41e6ad4 --- /dev/null +++ b/src/main/java/until/the/eternity/iteminfo/infrastructure/persistence/ItemInfoJpaRepository.java @@ -0,0 +1,16 @@ +package until.the.eternity.iteminfo.infrastructure.persistence; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import until.the.eternity.iteminfo.domain.entity.ItemInfo; + +import java.util.List; + +public interface ItemInfoJpaRepository + extends JpaRepository, JpaSpecificationExecutor { + + + List findByTopCategory(String topCategory); + + List findBySubCategory(String subCategory); +} diff --git a/src/main/java/until/the/eternity/iteminfo/infrastructure/persistence/ItemInfoRepositoryPortImpl.java b/src/main/java/until/the/eternity/iteminfo/infrastructure/persistence/ItemInfoRepositoryPortImpl.java new file mode 100644 index 00000000..80c735f5 --- /dev/null +++ b/src/main/java/until/the/eternity/iteminfo/infrastructure/persistence/ItemInfoRepositoryPortImpl.java @@ -0,0 +1,29 @@ +package until.the.eternity.iteminfo.infrastructure.persistence; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; +import until.the.eternity.iteminfo.domain.entity.ItemInfo; +import until.the.eternity.iteminfo.domain.repository.ItemInfoRepositoryPort; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class ItemInfoRepositoryPortImpl implements ItemInfoRepositoryPort { + private final ItemInfoJpaRepository jpaRepository; + + @Override + public List findAll() { + return jpaRepository.findAll(); + } + + @Override + public List findByTopCategory(String topCategory) { + return jpaRepository.findByTopCategory(topCategory); + } + + @Override + public List findBySubCategory(String subCategory) { + return jpaRepository.findBySubCategory(subCategory); + } +} diff --git a/src/main/java/until/the/eternity/iteminfo/interfaces/rest/controller/ItemInfoController.java b/src/main/java/until/the/eternity/iteminfo/interfaces/rest/controller/ItemInfoController.java new file mode 100644 index 00000000..bac6bce3 --- /dev/null +++ b/src/main/java/until/the/eternity/iteminfo/interfaces/rest/controller/ItemInfoController.java @@ -0,0 +1,55 @@ +package until.the.eternity.iteminfo.interfaces.rest.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import until.the.eternity.common.response.ApiResponse; +import until.the.eternity.iteminfo.application.service.ItemInfoService; +import until.the.eternity.iteminfo.interfaces.rest.dto.response.ItemCategoryResponse; +import until.the.eternity.iteminfo.interfaces.rest.dto.response.ItemInfoResponse; + +import java.util.List; + +@RestController +@RequestMapping("/api/item-infos") +@RequiredArgsConstructor +@Tag(name = "Item Info", description = "아이템 정보 조회 API") +public class ItemInfoController { + + private final ItemInfoService itemInfoService; + + @GetMapping("/categories") + @Operation(summary = "카테고리 정보", description = "아이템 상위 카테고리, 하위 카테고리 정보 조회") + public ResponseEntity>> findItemCategories() { + List itemCategories = itemInfoService.findItemCategories(); + return ResponseEntity.ok(ApiResponse.success(itemCategories)); + } + + @Operation(summary = "모든 아이템 정보 조회", description = "시스템에 저장된 모든 아이템 정보를 조회합니다.") + @GetMapping + public List getAllItemInfos() { + return itemInfoService.findAll(); + } + + @Operation(summary = "상위 카테고리로 검색", description = "상위 카테고리 이름으로 아이템 정보를 검색합니다.") + @GetMapping("/search/top-category") + public List searchItemInfosByTopCategory( + @Parameter(description = "검색할 상위 카테고리", required = true, example = "무기") @RequestParam + String topCategory) { + return itemInfoService.findByTopCategory(topCategory); + } + + @Operation(summary = "하위 카테고리로 검색", description = "하위 카테고리 이름으로 아이템 정보를 검색합니다.") + @GetMapping("/search/sub-category") + public List searchItemInfosBySubCategory( + @Parameter(description = "검색할 하위 카테고리", required = true, example = "한손검") @RequestParam + String subCategory) { + return itemInfoService.findBySubCategory(subCategory); + } +} diff --git a/src/main/java/until/the/eternity/item/interfaces/rest/dto/ItemCategoryResponse.java b/src/main/java/until/the/eternity/iteminfo/interfaces/rest/dto/response/ItemCategoryResponse.java similarity index 92% rename from src/main/java/until/the/eternity/item/interfaces/rest/dto/ItemCategoryResponse.java rename to src/main/java/until/the/eternity/iteminfo/interfaces/rest/dto/response/ItemCategoryResponse.java index d8a57ca2..0b1e6817 100644 --- a/src/main/java/until/the/eternity/item/interfaces/rest/dto/ItemCategoryResponse.java +++ b/src/main/java/until/the/eternity/iteminfo/interfaces/rest/dto/response/ItemCategoryResponse.java @@ -1,4 +1,4 @@ -package until.the.eternity.item.interfaces.rest.dto; +package until.the.eternity.iteminfo.interfaces.rest.dto.response; import lombok.Builder; import lombok.Getter; diff --git a/src/main/java/until/the/eternity/iteminfo/interfaces/rest/dto/response/ItemInfoResponse.java b/src/main/java/until/the/eternity/iteminfo/interfaces/rest/dto/response/ItemInfoResponse.java new file mode 100644 index 00000000..70897793 --- /dev/null +++ b/src/main/java/until/the/eternity/iteminfo/interfaces/rest/dto/response/ItemInfoResponse.java @@ -0,0 +1,47 @@ +package until.the.eternity.iteminfo.interfaces.rest.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import until.the.eternity.iteminfo.domain.entity.ItemInfo; + +import java.util.List; +import java.util.stream.Collectors; + +@Builder +@Schema(description = "아이템 정보 응답 DTO") +public record ItemInfoResponse( + @Schema(description = "아이템 이름", example = "나뭇가지") String name, + @Schema(description = "상위 카테고리", example = "무기") String topCategory, + @Schema(description = "하위 카테고리", example = "한손검") String subCategory, + @Schema(description = "아이템 설명", example = "흔한 나뭇가지이다.") String description, + @Schema(description = "인벤토리 가로 크기", example = "1") Byte inventoryWidth, + @Schema(description = "인벤토리 세로 크기", example = "2") Byte inventoryHeight, + @Schema(description = "최대 번들 가능 개수", example = "100") Integer inventoryMaxBundleCount, + @Schema(description = "아이템 역사") String history, + @Schema(description = "입수 방법") String acquisitionMethod, + @Schema(description = "1개 상점 판매가") String storeSalesPrice, + @Schema(description = "공격 속도 및 무기 타입") String weaponType, + @Schema(description = "수리 정보") String repair, + @Schema(description = "최대 개조 횟수") Byte maxAlterationCount) { + public static ItemInfoResponse from(ItemInfo itemInfo) { + return ItemInfoResponse.builder() + .name(itemInfo.getName()) + .topCategory(itemInfo.getTopCategory()) + .subCategory(itemInfo.getSubCategory()) + .description(itemInfo.getDescription()) + .inventoryWidth(itemInfo.getInventoryWidth()) + .inventoryHeight(itemInfo.getInventoryHeight()) + .inventoryMaxBundleCount(itemInfo.getInventoryMaxBundleCount()) + .history(itemInfo.getHistory()) + .acquisitionMethod(itemInfo.getAcquisitionMethod()) + .storeSalesPrice(itemInfo.getStoreSalesPrice()) + .weaponType(itemInfo.getWeaponType()) + .repair(itemInfo.getRepair()) + .maxAlterationCount(itemInfo.getMaxAlterationCount()) + .build(); + } + + public static List from(List itemInfos) { + return itemInfos.stream().map(ItemInfoResponse::from).collect(Collectors.toList()); + } +} diff --git a/src/main/resources/db/migration/V6__create_item_info.sql b/src/main/resources/db/migration/V6__create_item_info.sql new file mode 100644 index 00000000..19ffaf05 --- /dev/null +++ b/src/main/resources/db/migration/V6__create_item_info.sql @@ -0,0 +1,19 @@ +CREATE TABLE item_info ( + name VARCHAR(255) NOT NULL PRIMARY KEY COMMENT '아이템의 이름', + sub_category VARCHAR(25) NOT NULL COMMENT '아이템 하위 카테고리', + top_category VARCHAR(25) NOT NULL COMMENT '아이템 상위 카테고리', + description VARCHAR(255) COMMENT '아이템의 설명', + inventory_width TINYINT COMMENT '인벤토리 가로 크기', + inventory_height TINYINT COMMENT '인벤토리 세로 크기', + inventory_max_bundle_count INT COMMENT '최대 번들 가능 개수', + history VARCHAR(3000) COMMENT '아이템의 역사', + acquisition_method VARCHAR(3000) COMMENT '입수 방법', + store_sales_price VARCHAR(3000) COMMENT '1개 상점 판매가', + weapon_type VARCHAR(25) COMMENT '공격 속도 및 무기 타입', + repair VARCHAR(25) COMMENT '수리', + max_alteration_count TINYINT COMMENT '최대 개조 횟수' + +) COMMENT='아이템 정보 테이블'; + +-- 초기 데이터 적재 쿼리 +-- INSERT INTO item_info (name, top_category, sub_category) select distinct item_name, item_top_category, item_sub_category from auction_history; \ No newline at end of file